blob: 05e9927bc6e5b9102fb5b96cf57f701470775081 [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"
23#include "src/gpu/GrContextPriv.h"
24#include "src/gpu/GrGpu.h"
25#include "src/gpu/GrPersistentCacheUtils.h"
Chris Dalton77912982019-12-16 11:18:13 -070026#include "src/gpu/GrShaderUtils.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050027#include "src/gpu/ccpr/GrCoverageCountingPathRenderer.h"
28#include "src/utils/SkJSONWriter.h"
29#include "src/utils/SkOSPath.h"
30#include "tools/Resources.h"
31#include "tools/ToolUtils.h"
32#include "tools/flags/CommandLineFlags.h"
33#include "tools/flags/CommonFlags.h"
34#include "tools/trace/EventTracingPriv.h"
35#include "tools/viewer/BisectSlide.h"
36#include "tools/viewer/GMSlide.h"
37#include "tools/viewer/ImageSlide.h"
38#include "tools/viewer/ParticlesSlide.h"
39#include "tools/viewer/SKPSlide.h"
40#include "tools/viewer/SampleSlide.h"
Brian Osmand927bd22019-12-18 11:23:12 -050041#include "tools/viewer/SkSLSlide.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050042#include "tools/viewer/SlideDir.h"
43#include "tools/viewer/SvgSlide.h"
44#include "tools/viewer/Viewer.h"
csmartdalton578f0642017-02-24 16:04:47 -070045
Hal Canaryc640d0d2018-06-13 09:59:02 -040046#include <stdlib.h>
47#include <map>
48
Hal Canary8a001442018-09-19 11:31:27 -040049#include "imgui.h"
Brian Osman0b8bb882019-04-12 11:47:19 -040050#include "misc/cpp/imgui_stdlib.h" // For ImGui support of std::string
Florin Malita3b526b02018-05-25 12:43:51 -040051
Florin Malita87ccf332018-05-04 12:23:24 -040052#if defined(SK_ENABLE_SKOTTIE)
Mike Kleinc0bd9f92019-04-23 12:05:21 -050053 #include "tools/viewer/SkottieSlide.h"
Florin Malita87ccf332018-05-04 12:23:24 -040054#endif
55
Brian Osman5e7fbfd2019-05-03 13:13:35 -040056class CapturingShaderErrorHandler : public GrContextOptions::ShaderErrorHandler {
57public:
58 void compileError(const char* shader, const char* errors) override {
59 fShaders.push_back(SkString(shader));
60 fErrors.push_back(SkString(errors));
61 }
62
63 void reset() {
64 fShaders.reset();
65 fErrors.reset();
66 }
67
68 SkTArray<SkString> fShaders;
69 SkTArray<SkString> fErrors;
70};
71
72static CapturingShaderErrorHandler gShaderErrorHandler;
73
jvanverth34524262016-05-04 13:49:13 -070074using namespace sk_app;
75
csmartdalton61cd31a2017-02-27 17:00:53 -070076static std::map<GpuPathRenderers, std::string> gPathRendererNames;
77
jvanverth9f372462016-04-06 06:08:59 -070078Application* Application::Create(int argc, char** argv, void* platformData) {
jvanverth34524262016-05-04 13:49:13 -070079 return new Viewer(argc, argv, platformData);
jvanverth9f372462016-04-06 06:08:59 -070080}
81
Chris Dalton7a0ebfc2017-10-13 12:35:50 -060082static DEFINE_string(slide, "", "Start on this sample.");
83static DEFINE_bool(list, false, "List samples?");
Jim Van Verth6f449692017-02-14 15:16:46 -050084
Stephen Whitea800ec92019-08-02 15:04:52 -040085#if defined(SK_VULKAN)
jvanverthb8794cc2016-07-27 14:29:18 -070086# define BACKENDS_STR "\"sw\", \"gl\", and \"vk\""
Jim Van Verthbe39f712019-02-08 15:36:14 -050087#elif defined(SK_METAL) && defined(SK_BUILD_FOR_MAC)
88# define BACKENDS_STR "\"sw\", \"gl\", and \"mtl\""
Stephen Whitea800ec92019-08-02 15:04:52 -040089#elif defined(SK_DAWN)
90# define BACKENDS_STR "\"sw\", \"gl\", and \"dawn\""
bsalomon6c471f72016-07-26 12:56:32 -070091#else
92# define BACKENDS_STR "\"sw\" and \"gl\""
93#endif
94
Brian Osman2dd96932016-10-18 15:33:53 -040095static DEFINE_string2(backend, b, "sw", "Backend to use. Allowed values are " BACKENDS_STR ".");
bsalomon6c471f72016-07-26 12:56:32 -070096
Mike Klein5b3f3432019-03-21 11:42:21 -050097static DEFINE_int(msaa, 1, "Number of subpixel samples. 0 for no HW antialiasing.");
csmartdalton008b9d82017-02-22 12:00:42 -070098
Mike Klein84836b72019-03-21 11:31:36 -050099static DEFINE_string(bisect, "", "Path to a .skp or .svg file to bisect.");
Chris Dalton2d18f412018-02-20 13:23:32 -0700100
Mike Klein84836b72019-03-21 11:31:36 -0500101static DEFINE_string2(file, f, "", "Open a single file for viewing.");
Florin Malita38792ce2018-05-08 10:36:18 -0400102
Mike Kleinc6142d82019-03-25 10:54:59 -0500103static DEFINE_string2(match, m, nullptr,
104 "[~][^]substring[$] [...] of name to run.\n"
105 "Multiple matches may be separated by spaces.\n"
106 "~ causes a matching name to always be skipped\n"
107 "^ requires the start of the name to match\n"
108 "$ requires the end of the name to match\n"
109 "^ and $ requires an exact match\n"
110 "If a name does not match any list entry,\n"
111 "it is skipped unless some list entry starts with ~");
112
Mike Klein19fb3972019-03-21 13:08:08 -0500113#if defined(SK_BUILD_FOR_ANDROID)
114 static DEFINE_string(jpgs, "/data/local/tmp/resources", "Directory to read jpgs from.");
Mike Kleinc6142d82019-03-25 10:54:59 -0500115 static DEFINE_string(skps, "/data/local/tmp/skps", "Directory to read skps from.");
116 static DEFINE_string(lotties, "/data/local/tmp/lotties",
117 "Directory to read (Bodymovin) jsons from.");
Mike Klein19fb3972019-03-21 13:08:08 -0500118#else
119 static DEFINE_string(jpgs, "jpgs", "Directory to read jpgs from.");
Mike Kleinc6142d82019-03-25 10:54:59 -0500120 static DEFINE_string(skps, "skps", "Directory to read skps from.");
121 static DEFINE_string(lotties, "lotties", "Directory to read (Bodymovin) jsons from.");
Mike Klein19fb3972019-03-21 13:08:08 -0500122#endif
123
Mike Kleinc6142d82019-03-25 10:54:59 -0500124static DEFINE_string(svgs, "", "Directory to read SVGs from, or a single SVG file.");
125
126static DEFINE_int_2(threads, j, -1,
127 "Run threadsafe tests on a threadpool with this many extra threads, "
128 "defaulting to one extra thread per core.");
129
Jim Van Verth7b558182019-11-14 16:47:01 -0500130static DEFINE_bool(redraw, false, "Toggle continuous redraw.");
131
Chris Daltonc8877332020-01-06 09:48:30 -0700132static DEFINE_bool(offscreen, false, "Force rendering to an offscreen surface.");
Mike Kleinc6142d82019-03-25 10:54:59 -0500133
Brian Salomon194db172017-08-17 14:37:06 -0400134const char* kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = {
csmartdalton578f0642017-02-24 16:04:47 -0700135 "OpenGL",
Brian Salomon194db172017-08-17 14:37:06 -0400136#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
137 "ANGLE",
138#endif
Stephen Whitea800ec92019-08-02 15:04:52 -0400139#ifdef SK_DAWN
140 "Dawn",
141#endif
jvanverth063ece72016-06-17 09:29:14 -0700142#ifdef SK_VULKAN
csmartdalton578f0642017-02-24 16:04:47 -0700143 "Vulkan",
jvanverth063ece72016-06-17 09:29:14 -0700144#endif
Jim Van Verthe58d5322019-09-03 09:42:57 -0400145#ifdef SK_METAL
Jim Van Verthbe39f712019-02-08 15:36:14 -0500146 "Metal",
147#endif
csmartdalton578f0642017-02-24 16:04:47 -0700148 "Raster"
jvanverthaf236b52016-05-20 06:01:06 -0700149};
150
bsalomon6c471f72016-07-26 12:56:32 -0700151static sk_app::Window::BackendType get_backend_type(const char* str) {
Stephen Whitea800ec92019-08-02 15:04:52 -0400152#ifdef SK_DAWN
153 if (0 == strcmp(str, "dawn")) {
154 return sk_app::Window::kDawn_BackendType;
155 } else
156#endif
bsalomon6c471f72016-07-26 12:56:32 -0700157#ifdef SK_VULKAN
158 if (0 == strcmp(str, "vk")) {
159 return sk_app::Window::kVulkan_BackendType;
160 } else
161#endif
Brian Salomon194db172017-08-17 14:37:06 -0400162#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
163 if (0 == strcmp(str, "angle")) {
164 return sk_app::Window::kANGLE_BackendType;
165 } else
166#endif
Jim Van Verthe58d5322019-09-03 09:42:57 -0400167#ifdef SK_METAL
168 if (0 == strcmp(str, "mtl")) {
169 return sk_app::Window::kMetal_BackendType;
170 } else
Jim Van Verthbe39f712019-02-08 15:36:14 -0500171#endif
bsalomon6c471f72016-07-26 12:56:32 -0700172 if (0 == strcmp(str, "gl")) {
173 return sk_app::Window::kNativeGL_BackendType;
174 } else if (0 == strcmp(str, "sw")) {
175 return sk_app::Window::kRaster_BackendType;
176 } else {
177 SkDebugf("Unknown backend type, %s, defaulting to sw.", str);
178 return sk_app::Window::kRaster_BackendType;
179 }
180}
181
Brian Osmana109e392017-02-24 09:49:14 -0500182static SkColorSpacePrimaries gSrgbPrimaries = {
183 0.64f, 0.33f,
184 0.30f, 0.60f,
185 0.15f, 0.06f,
186 0.3127f, 0.3290f };
187
188static SkColorSpacePrimaries gAdobePrimaries = {
189 0.64f, 0.33f,
190 0.21f, 0.71f,
191 0.15f, 0.06f,
192 0.3127f, 0.3290f };
193
194static SkColorSpacePrimaries gP3Primaries = {
195 0.680f, 0.320f,
196 0.265f, 0.690f,
197 0.150f, 0.060f,
198 0.3127f, 0.3290f };
199
200static SkColorSpacePrimaries gRec2020Primaries = {
201 0.708f, 0.292f,
202 0.170f, 0.797f,
203 0.131f, 0.046f,
204 0.3127f, 0.3290f };
205
206struct NamedPrimaries {
207 const char* fName;
208 SkColorSpacePrimaries* fPrimaries;
209} gNamedPrimaries[] = {
210 { "sRGB", &gSrgbPrimaries },
211 { "AdobeRGB", &gAdobePrimaries },
212 { "P3", &gP3Primaries },
213 { "Rec. 2020", &gRec2020Primaries },
214};
215
216static bool primaries_equal(const SkColorSpacePrimaries& a, const SkColorSpacePrimaries& b) {
217 return memcmp(&a, &b, sizeof(SkColorSpacePrimaries)) == 0;
218}
219
Brian Osman70d2f432017-11-08 09:54:10 -0500220static Window::BackendType backend_type_for_window(Window::BackendType backendType) {
221 // In raster mode, we still use GL for the window.
222 // This lets us render the GUI faster (and correct).
223 return Window::kRaster_BackendType == backendType ? Window::kNativeGL_BackendType : backendType;
224}
225
Jim Van Verth74826c82019-03-01 14:37:30 -0500226class NullSlide : public Slide {
227 SkISize getDimensions() const override {
228 return SkISize::Make(640, 480);
229 }
230
231 void draw(SkCanvas* canvas) override {
232 canvas->clear(0xffff11ff);
233 }
234};
235
liyuqiane5a6cd92016-05-27 08:52:52 -0700236const char* kName = "name";
237const char* kValue = "value";
238const char* kOptions = "options";
239const char* kSlideStateName = "Slide";
240const char* kBackendStateName = "Backend";
csmartdalton578f0642017-02-24 16:04:47 -0700241const char* kMSAAStateName = "MSAA";
csmartdalton61cd31a2017-02-27 17:00:53 -0700242const char* kPathRendererStateName = "Path renderer";
liyuqianb73c24b2016-06-03 08:47:23 -0700243const char* kSoftkeyStateName = "Softkey";
244const char* kSoftkeyHint = "Please select a softkey";
liyuqian1f508fd2016-06-07 06:57:40 -0700245const char* kFpsStateName = "FPS";
liyuqian6f163d22016-06-13 12:26:45 -0700246const char* kON = "ON";
247const char* kOFF = "OFF";
liyuqian2edb0f42016-07-06 14:11:32 -0700248const char* kRefreshStateName = "Refresh";
liyuqiane5a6cd92016-05-27 08:52:52 -0700249
jvanverth34524262016-05-04 13:49:13 -0700250Viewer::Viewer(int argc, char** argv, void* platformData)
Florin Malitaab99c342018-01-16 16:23:03 -0500251 : fCurrentSlide(-1)
252 , fRefresh(false)
Brian Osman3ac99cf2017-12-01 11:23:53 -0500253 , fSaveToSKP(false)
Mike Reed376d8122019-03-14 11:39:02 -0400254 , fShowSlideDimensions(false)
Brian Osman79086b92017-02-10 13:36:16 -0500255 , fShowImGuiDebugWindow(false)
Brian Osmanfce09c52017-11-14 15:32:20 -0500256 , fShowSlidePicker(false)
Brian Osman79086b92017-02-10 13:36:16 -0500257 , fShowImGuiTestWindow(false)
Brian Osmanf6877092017-02-13 09:39:57 -0500258 , fShowZoomWindow(false)
Ben Wagner3627d2e2018-06-26 14:23:20 -0400259 , fZoomWindowFixed(false)
260 , fZoomWindowLocation{0.0f, 0.0f}
Brian Osmanf6877092017-02-13 09:39:57 -0500261 , fLastImage(nullptr)
Brian Osmanb63f6002018-07-24 18:01:53 -0400262 , fZoomUI(false)
jvanverth063ece72016-06-17 09:29:14 -0700263 , fBackendType(sk_app::Window::kNativeGL_BackendType)
Brian Osman92004802017-03-06 11:47:26 -0500264 , fColorMode(ColorMode::kLegacy)
Brian Osmana109e392017-02-24 09:49:14 -0500265 , fColorSpacePrimaries(gSrgbPrimaries)
Brian Osmanfdab5762017-11-09 10:27:55 -0500266 // Our UI can only tweak gamma (currently), so start out gamma-only
Brian Osman82ebe042019-01-04 17:03:00 -0500267 , fColorSpaceTransferFn(SkNamedTransferFn::k2Dot2)
egdaniel2a0bb0a2016-04-11 08:30:40 -0700268 , fZoomLevel(0.0f)
Ben Wagnerd02a74d2018-04-23 12:55:06 -0400269 , fRotation(0.0f)
Ben Wagner897dfa22018-08-09 15:18:46 -0400270 , fOffset{0.5f, 0.5f}
Brian Osmanb53f48c2017-06-07 10:00:30 -0400271 , fGestureDevice(GestureDevice::kNone)
Brian Osmane9ed0f02018-11-26 14:50:05 -0500272 , fTiled(false)
273 , fDrawTileBoundaries(false)
274 , fTileScale{0.25f, 0.25f}
Brian Osman805a7272018-05-02 15:40:20 -0400275 , fPerspectiveMode(kPerspective_Off)
jvanverthc265a922016-04-08 12:51:45 -0700276{
Greg Daniel285db442016-10-14 09:12:53 -0400277 SkGraphics::Init();
csmartdalton61cd31a2017-02-27 17:00:53 -0700278
Chris Dalton37ae4b02019-12-28 14:51:11 -0700279 gPathRendererNames[GpuPathRenderers::kDefault] = "Default Path Renderers";
Brian Osmanf09e35e2017-12-15 14:48:09 -0500280 gPathRendererNames[GpuPathRenderers::kStencilAndCover] = "NV_path_rendering";
Brian Osmanf09e35e2017-12-15 14:48:09 -0500281 gPathRendererNames[GpuPathRenderers::kSmall] = "Small paths (cached sdf or alpha masks)";
Chris Daltonc3318f02019-07-19 14:20:53 -0600282 gPathRendererNames[GpuPathRenderers::kCoverageCounting] = "CCPR";
Brian Osmanf09e35e2017-12-15 14:48:09 -0500283 gPathRendererNames[GpuPathRenderers::kTessellating] = "Tessellating";
284 gPathRendererNames[GpuPathRenderers::kNone] = "Software masks";
csmartdalton61cd31a2017-02-27 17:00:53 -0700285
jvanverth2bb3b6d2016-04-08 07:24:09 -0700286 SkDebugf("Command line arguments: ");
287 for (int i = 1; i < argc; ++i) {
288 SkDebugf("%s ", argv[i]);
289 }
290 SkDebugf("\n");
291
Mike Klein88544fb2019-03-20 10:50:33 -0500292 CommandLineFlags::Parse(argc, argv);
Greg Daniel9fcc7432016-11-29 16:35:19 -0500293#ifdef SK_BUILD_FOR_ANDROID
Brian Salomon96789b32017-05-26 12:06:21 -0400294 SetResourcePath("/data/local/tmp/resources");
Greg Daniel9fcc7432016-11-29 16:35:19 -0500295#endif
jvanverth2bb3b6d2016-04-08 07:24:09 -0700296
Mike Klein19cc0f62019-03-22 15:30:07 -0500297 ToolUtils::SetDefaultFontMgr();
Ben Wagner483c7722018-02-20 17:06:07 -0500298
Brian Osmanbc8150f2017-07-24 11:38:01 -0400299 initializeEventTracingForTools();
Brian Osman53136aa2017-07-20 15:43:35 -0400300 static SkTaskGroup::Enabler kTaskGroupEnabler(FLAGS_threads);
Greg Daniel285db442016-10-14 09:12:53 -0400301
bsalomon6c471f72016-07-26 12:56:32 -0700302 fBackendType = get_backend_type(FLAGS_backend[0]);
jvanverth9f372462016-04-06 06:08:59 -0700303 fWindow = Window::CreateNativeWindow(platformData);
jvanverth9f372462016-04-06 06:08:59 -0700304
csmartdalton578f0642017-02-24 16:04:47 -0700305 DisplayParams displayParams;
306 displayParams.fMSAASampleCount = FLAGS_msaa;
Chris Dalton040238b2017-12-18 14:22:34 -0700307 SetCtxOptionsFromCommonFlags(&displayParams.fGrContextOptions);
Brian Osman0b8bb882019-04-12 11:47:19 -0400308 displayParams.fGrContextOptions.fPersistentCache = &fPersistentCache;
Brian Osmana66081d2019-09-03 14:59:26 -0400309 displayParams.fGrContextOptions.fShaderCacheStrategy =
310 GrContextOptions::ShaderCacheStrategy::kBackendSource;
Brian Osman5e7fbfd2019-05-03 13:13:35 -0400311 displayParams.fGrContextOptions.fShaderErrorHandler = &gShaderErrorHandler;
312 displayParams.fGrContextOptions.fSuppressPrints = true;
csmartdalton578f0642017-02-24 16:04:47 -0700313 fWindow->setRequestedDisplayParams(displayParams);
Jim Van Verth7b558182019-11-14 16:47:01 -0500314 fRefresh = FLAGS_redraw;
csmartdalton578f0642017-02-24 16:04:47 -0700315
Brian Osman56a24812017-12-19 11:15:16 -0500316 // Configure timers
317 fStatsLayer.setActive(false);
318 fAnimateTimer = fStatsLayer.addTimer("Animate", SK_ColorMAGENTA, 0xffff66ff);
319 fPaintTimer = fStatsLayer.addTimer("Paint", SK_ColorGREEN);
320 fFlushTimer = fStatsLayer.addTimer("Flush", SK_ColorRED, 0xffff6666);
321
jvanverth9f372462016-04-06 06:08:59 -0700322 // register callbacks
brianosman622c8d52016-05-10 06:50:49 -0700323 fCommands.attach(fWindow);
Brian Osman80fc07e2017-12-08 16:45:43 -0500324 fWindow->pushLayer(this);
Brian Osman56a24812017-12-19 11:15:16 -0500325 fWindow->pushLayer(&fStatsLayer);
Brian Osmand67e5182017-12-08 16:46:09 -0500326 fWindow->pushLayer(&fImGuiLayer);
jvanverth9f372462016-04-06 06:08:59 -0700327
brianosman622c8d52016-05-10 06:50:49 -0700328 // add key-bindings
Brian Osman79086b92017-02-10 13:36:16 -0500329 fCommands.addCommand(' ', "GUI", "Toggle Debug GUI", [this]() {
330 this->fShowImGuiDebugWindow = !this->fShowImGuiDebugWindow;
331 fWindow->inval();
332 });
Brian Osmanfce09c52017-11-14 15:32:20 -0500333 // Command to jump directly to the slide picker and give it focus
334 fCommands.addCommand('/', "GUI", "Jump to slide picker", [this]() {
335 this->fShowImGuiDebugWindow = true;
336 this->fShowSlidePicker = true;
337 fWindow->inval();
338 });
339 // Alias that to Backspace, to match SampleApp
Hal Canaryb1f411a2019-08-29 10:39:22 -0400340 fCommands.addCommand(skui::Key::kBack, "Backspace", "GUI", "Jump to slide picker", [this]() {
Brian Osmanfce09c52017-11-14 15:32:20 -0500341 this->fShowImGuiDebugWindow = true;
342 this->fShowSlidePicker = true;
343 fWindow->inval();
344 });
Brian Osman79086b92017-02-10 13:36:16 -0500345 fCommands.addCommand('g', "GUI", "Toggle GUI Demo", [this]() {
346 this->fShowImGuiTestWindow = !this->fShowImGuiTestWindow;
347 fWindow->inval();
348 });
Brian Osmanf6877092017-02-13 09:39:57 -0500349 fCommands.addCommand('z', "GUI", "Toggle zoom window", [this]() {
350 this->fShowZoomWindow = !this->fShowZoomWindow;
351 fWindow->inval();
352 });
Ben Wagner3627d2e2018-06-26 14:23:20 -0400353 fCommands.addCommand('Z', "GUI", "Toggle zoom window state", [this]() {
354 this->fZoomWindowFixed = !this->fZoomWindowFixed;
355 fWindow->inval();
356 });
Greg Danield0794cc2019-03-27 16:23:26 -0400357 fCommands.addCommand('v', "VSync", "Toggle vsync on/off", [this]() {
358 DisplayParams params = fWindow->getRequestedDisplayParams();
359 params.fDisableVsync = !params.fDisableVsync;
360 fWindow->setRequestedDisplayParams(params);
361 this->updateTitle();
362 fWindow->inval();
363 });
Mike Reedf702ed42019-07-22 17:00:49 -0400364 fCommands.addCommand('r', "Redraw", "Toggle redraw", [this]() {
365 fRefresh = !fRefresh;
366 fWindow->inval();
367 });
brianosman622c8d52016-05-10 06:50:49 -0700368 fCommands.addCommand('s', "Overlays", "Toggle stats display", [this]() {
Brian Osman56a24812017-12-19 11:15:16 -0500369 fStatsLayer.setActive(!fStatsLayer.getActive());
brianosman622c8d52016-05-10 06:50:49 -0700370 fWindow->inval();
371 });
Jim Van Verth90dcce52017-11-03 13:36:07 -0400372 fCommands.addCommand('0', "Overlays", "Reset stats", [this]() {
Brian Osman56a24812017-12-19 11:15:16 -0500373 fStatsLayer.resetMeasurements();
Jim Van Verth90dcce52017-11-03 13:36:07 -0400374 this->updateTitle();
375 fWindow->inval();
376 });
Brian Osmanf750fbc2017-02-08 10:47:28 -0500377 fCommands.addCommand('c', "Modes", "Cycle color mode", [this]() {
Brian Osman92004802017-03-06 11:47:26 -0500378 switch (fColorMode) {
379 case ColorMode::kLegacy:
Brian Osman03115dc2018-11-26 13:55:19 -0500380 this->setColorMode(ColorMode::kColorManaged8888);
Brian Osman92004802017-03-06 11:47:26 -0500381 break;
Brian Osman03115dc2018-11-26 13:55:19 -0500382 case ColorMode::kColorManaged8888:
383 this->setColorMode(ColorMode::kColorManagedF16);
Brian Osman92004802017-03-06 11:47:26 -0500384 break;
Brian Osman03115dc2018-11-26 13:55:19 -0500385 case ColorMode::kColorManagedF16:
Brian Salomon8391bac2019-09-18 11:22:44 -0400386 this->setColorMode(ColorMode::kColorManagedF16Norm);
387 break;
388 case ColorMode::kColorManagedF16Norm:
Brian Osman92004802017-03-06 11:47:26 -0500389 this->setColorMode(ColorMode::kLegacy);
390 break;
Brian Osmanf750fbc2017-02-08 10:47:28 -0500391 }
brianosman622c8d52016-05-10 06:50:49 -0700392 });
Chris Dalton1215cda2019-12-17 21:44:04 -0700393 fCommands.addCommand('w', "Modes", "Toggle wireframe", [this]() {
394 DisplayParams params = fWindow->getRequestedDisplayParams();
395 params.fGrContextOptions.fWireframeMode = !params.fGrContextOptions.fWireframeMode;
396 fWindow->setRequestedDisplayParams(params);
397 fWindow->inval();
398 });
Hal Canaryb1f411a2019-08-29 10:39:22 -0400399 fCommands.addCommand(skui::Key::kRight, "Right", "Navigation", "Next slide", [this]() {
Florin Malitaab99c342018-01-16 16:23:03 -0500400 this->setCurrentSlide(fCurrentSlide < fSlides.count() - 1 ? fCurrentSlide + 1 : 0);
brianosman622c8d52016-05-10 06:50:49 -0700401 });
Hal Canaryb1f411a2019-08-29 10:39:22 -0400402 fCommands.addCommand(skui::Key::kLeft, "Left", "Navigation", "Previous slide", [this]() {
Florin Malitaab99c342018-01-16 16:23:03 -0500403 this->setCurrentSlide(fCurrentSlide > 0 ? fCurrentSlide - 1 : fSlides.count() - 1);
brianosman622c8d52016-05-10 06:50:49 -0700404 });
Hal Canaryb1f411a2019-08-29 10:39:22 -0400405 fCommands.addCommand(skui::Key::kUp, "Up", "Transform", "Zoom in", [this]() {
brianosman622c8d52016-05-10 06:50:49 -0700406 this->changeZoomLevel(1.f / 32.f);
407 fWindow->inval();
408 });
Hal Canaryb1f411a2019-08-29 10:39:22 -0400409 fCommands.addCommand(skui::Key::kDown, "Down", "Transform", "Zoom out", [this]() {
brianosman622c8d52016-05-10 06:50:49 -0700410 this->changeZoomLevel(-1.f / 32.f);
411 fWindow->inval();
412 });
jvanverthaf236b52016-05-20 06:01:06 -0700413 fCommands.addCommand('d', "Modes", "Change rendering backend", [this]() {
Brian Salomon194db172017-08-17 14:37:06 -0400414 sk_app::Window::BackendType newBackend = (sk_app::Window::BackendType)(
415 (fBackendType + 1) % sk_app::Window::kBackendTypeCount);
Jim Van Verthd63c1022017-01-05 13:50:49 -0500416 // Switching to and from Vulkan is problematic on Linux so disabled for now
Brian Salomon194db172017-08-17 14:37:06 -0400417#if defined(SK_BUILD_FOR_UNIX) && defined(SK_VULKAN)
418 if (newBackend == sk_app::Window::kVulkan_BackendType) {
419 newBackend = (sk_app::Window::BackendType)((newBackend + 1) %
420 sk_app::Window::kBackendTypeCount);
421 } else if (fBackendType == sk_app::Window::kVulkan_BackendType) {
422 newBackend = sk_app::Window::kVulkan_BackendType;
Jim Van Verthd63c1022017-01-05 13:50:49 -0500423 }
424#endif
Brian Osman621491e2017-02-28 15:45:01 -0500425 this->setBackend(newBackend);
jvanverthaf236b52016-05-20 06:01:06 -0700426 });
Brian Osman3ac99cf2017-12-01 11:23:53 -0500427 fCommands.addCommand('K', "IO", "Save slide to SKP", [this]() {
428 fSaveToSKP = true;
429 fWindow->inval();
430 });
Mike Reed376d8122019-03-14 11:39:02 -0400431 fCommands.addCommand('&', "Overlays", "Show slide dimensios", [this]() {
432 fShowSlideDimensions = !fShowSlideDimensions;
433 fWindow->inval();
434 });
Ben Wagner37c54032018-04-13 14:30:23 -0400435 fCommands.addCommand('G', "Modes", "Geometry", [this]() {
436 DisplayParams params = fWindow->getRequestedDisplayParams();
437 uint32_t flags = params.fSurfaceProps.flags();
438 if (!fPixelGeometryOverrides) {
439 fPixelGeometryOverrides = true;
440 params.fSurfaceProps = SkSurfaceProps(flags, kUnknown_SkPixelGeometry);
441 } else {
442 switch (params.fSurfaceProps.pixelGeometry()) {
443 case kUnknown_SkPixelGeometry:
444 params.fSurfaceProps = SkSurfaceProps(flags, kRGB_H_SkPixelGeometry);
445 break;
446 case kRGB_H_SkPixelGeometry:
447 params.fSurfaceProps = SkSurfaceProps(flags, kBGR_H_SkPixelGeometry);
448 break;
449 case kBGR_H_SkPixelGeometry:
450 params.fSurfaceProps = SkSurfaceProps(flags, kRGB_V_SkPixelGeometry);
451 break;
452 case kRGB_V_SkPixelGeometry:
453 params.fSurfaceProps = SkSurfaceProps(flags, kBGR_V_SkPixelGeometry);
454 break;
455 case kBGR_V_SkPixelGeometry:
456 params.fSurfaceProps = SkSurfaceProps(flags, SkSurfaceProps::kLegacyFontHost_InitType);
457 fPixelGeometryOverrides = false;
458 break;
459 }
460 }
461 fWindow->setRequestedDisplayParams(params);
462 this->updateTitle();
463 fWindow->inval();
464 });
Ben Wagner9613e452019-01-23 10:34:59 -0500465 fCommands.addCommand('H', "Font", "Hinting mode", [this]() {
Mike Reed3ae47332019-01-04 10:11:46 -0500466 if (!fFontOverrides.fHinting) {
467 fFontOverrides.fHinting = true;
Ben Wagner5785e4a2019-05-07 16:50:29 -0400468 fFont.setHinting(SkFontHinting::kNone);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500469 } else {
Mike Reed3ae47332019-01-04 10:11:46 -0500470 switch (fFont.getHinting()) {
Ben Wagner5785e4a2019-05-07 16:50:29 -0400471 case SkFontHinting::kNone:
472 fFont.setHinting(SkFontHinting::kSlight);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500473 break;
Ben Wagner5785e4a2019-05-07 16:50:29 -0400474 case SkFontHinting::kSlight:
475 fFont.setHinting(SkFontHinting::kNormal);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500476 break;
Ben Wagner5785e4a2019-05-07 16:50:29 -0400477 case SkFontHinting::kNormal:
478 fFont.setHinting(SkFontHinting::kFull);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500479 break;
Ben Wagner5785e4a2019-05-07 16:50:29 -0400480 case SkFontHinting::kFull:
481 fFont.setHinting(SkFontHinting::kNone);
Mike Reed3ae47332019-01-04 10:11:46 -0500482 fFontOverrides.fHinting = false;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500483 break;
484 }
485 }
486 this->updateTitle();
487 fWindow->inval();
488 });
489 fCommands.addCommand('A', "Paint", "Antialias Mode", [this]() {
Ben Wagner9613e452019-01-23 10:34:59 -0500490 if (!fPaintOverrides.fAntiAlias) {
491 fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Alias;
492 fPaintOverrides.fAntiAlias = true;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500493 fPaint.setAntiAlias(false);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500494 gSkUseAnalyticAA = gSkForceAnalyticAA = false;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500495 } else {
496 fPaint.setAntiAlias(true);
Ben Wagner9613e452019-01-23 10:34:59 -0500497 switch (fPaintOverrides.fAntiAliasState) {
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500498 case SkPaintFields::AntiAliasState::Alias:
Ben Wagner9613e452019-01-23 10:34:59 -0500499 fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Normal;
Ben Wagnera580fb32018-04-17 11:16:32 -0400500 gSkUseAnalyticAA = gSkForceAnalyticAA = false;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500501 break;
502 case SkPaintFields::AntiAliasState::Normal:
Ben Wagner9613e452019-01-23 10:34:59 -0500503 fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::AnalyticAAEnabled;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500504 gSkUseAnalyticAA = true;
Ben Wagnera580fb32018-04-17 11:16:32 -0400505 gSkForceAnalyticAA = false;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500506 break;
507 case SkPaintFields::AntiAliasState::AnalyticAAEnabled:
Ben Wagner9613e452019-01-23 10:34:59 -0500508 fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::AnalyticAAForced;
Ben Wagnera580fb32018-04-17 11:16:32 -0400509 gSkUseAnalyticAA = gSkForceAnalyticAA = true;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500510 break;
511 case SkPaintFields::AntiAliasState::AnalyticAAForced:
Ben Wagner9613e452019-01-23 10:34:59 -0500512 fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Alias;
513 fPaintOverrides.fAntiAlias = false;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500514 gSkUseAnalyticAA = fPaintOverrides.fOriginalSkUseAnalyticAA;
515 gSkForceAnalyticAA = fPaintOverrides.fOriginalSkForceAnalyticAA;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500516 break;
517 }
518 }
519 this->updateTitle();
520 fWindow->inval();
521 });
Ben Wagner37c54032018-04-13 14:30:23 -0400522 fCommands.addCommand('D', "Modes", "DFT", [this]() {
523 DisplayParams params = fWindow->getRequestedDisplayParams();
524 uint32_t flags = params.fSurfaceProps.flags();
525 flags ^= SkSurfaceProps::kUseDeviceIndependentFonts_Flag;
526 params.fSurfaceProps = SkSurfaceProps(flags, params.fSurfaceProps.pixelGeometry());
527 fWindow->setRequestedDisplayParams(params);
528 this->updateTitle();
529 fWindow->inval();
530 });
Ben Wagner9613e452019-01-23 10:34:59 -0500531 fCommands.addCommand('L', "Font", "Subpixel Antialias Mode", [this]() {
Mike Reede5f9cfa2019-01-10 13:55:35 -0500532 if (!fFontOverrides.fEdging) {
533 fFontOverrides.fEdging = true;
534 fFont.setEdging(SkFont::Edging::kAlias);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500535 } else {
Mike Reede5f9cfa2019-01-10 13:55:35 -0500536 switch (fFont.getEdging()) {
537 case SkFont::Edging::kAlias:
538 fFont.setEdging(SkFont::Edging::kAntiAlias);
539 break;
540 case SkFont::Edging::kAntiAlias:
541 fFont.setEdging(SkFont::Edging::kSubpixelAntiAlias);
542 break;
543 case SkFont::Edging::kSubpixelAntiAlias:
544 fFont.setEdging(SkFont::Edging::kAlias);
545 fFontOverrides.fEdging = false;
546 break;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500547 }
548 }
549 this->updateTitle();
550 fWindow->inval();
551 });
Ben Wagner9613e452019-01-23 10:34:59 -0500552 fCommands.addCommand('S', "Font", "Subpixel Position Mode", [this]() {
Mike Reede5f9cfa2019-01-10 13:55:35 -0500553 if (!fFontOverrides.fSubpixel) {
554 fFontOverrides.fSubpixel = true;
555 fFont.setSubpixel(false);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500556 } else {
Mike Reede5f9cfa2019-01-10 13:55:35 -0500557 if (!fFont.isSubpixel()) {
558 fFont.setSubpixel(true);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500559 } else {
Mike Reede5f9cfa2019-01-10 13:55:35 -0500560 fFontOverrides.fSubpixel = false;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500561 }
562 }
563 this->updateTitle();
564 fWindow->inval();
565 });
Ben Wagner54aa8842019-08-27 16:20:39 -0400566 fCommands.addCommand('B', "Font", "Baseline Snapping", [this]() {
567 if (!fFontOverrides.fBaselineSnap) {
568 fFontOverrides.fBaselineSnap = true;
569 fFont.setBaselineSnap(false);
570 } else {
571 if (!fFont.isBaselineSnap()) {
572 fFont.setBaselineSnap(true);
573 } else {
574 fFontOverrides.fBaselineSnap = false;
575 }
576 }
577 this->updateTitle();
578 fWindow->inval();
579 });
Brian Osman805a7272018-05-02 15:40:20 -0400580 fCommands.addCommand('p', "Transform", "Toggle Perspective Mode", [this]() {
581 fPerspectiveMode = (kPerspective_Real == fPerspectiveMode) ? kPerspective_Fake
582 : kPerspective_Real;
583 this->updateTitle();
584 fWindow->inval();
585 });
586 fCommands.addCommand('P', "Transform", "Toggle Perspective", [this]() {
587 fPerspectiveMode = (kPerspective_Off == fPerspectiveMode) ? kPerspective_Real
588 : kPerspective_Off;
589 this->updateTitle();
590 fWindow->inval();
591 });
Brian Osman207d4102019-01-10 09:40:58 -0500592 fCommands.addCommand('a', "Transform", "Toggle Animation", [this]() {
593 fAnimTimer.togglePauseResume();
594 });
Brian Osmanb63f6002018-07-24 18:01:53 -0400595 fCommands.addCommand('u', "GUI", "Zoom UI", [this]() {
596 fZoomUI = !fZoomUI;
597 fStatsLayer.setDisplayScale(fZoomUI ? 2.0f : 1.0f);
598 fWindow->inval();
599 });
Yuqian Lib2ba6642017-11-22 12:07:41 -0500600
jvanverth2bb3b6d2016-04-08 07:24:09 -0700601 // set up slides
602 this->initSlides();
Jim Van Verth6f449692017-02-14 15:16:46 -0500603 if (FLAGS_list) {
604 this->listNames();
605 }
jvanverth2bb3b6d2016-04-08 07:24:09 -0700606
Brian Osman9bb47cf2018-04-26 15:55:00 -0400607 fPerspectivePoints[0].set(0, 0);
608 fPerspectivePoints[1].set(1, 0);
609 fPerspectivePoints[2].set(0, 1);
610 fPerspectivePoints[3].set(1, 1);
djsollen12d62a72016-04-21 07:59:44 -0700611 fAnimTimer.run();
612
Hal Canaryc465d132017-12-08 10:21:31 -0500613 auto gamutImage = GetResourceAsImage("images/gamut.png");
Brian Osmana109e392017-02-24 09:49:14 -0500614 if (gamutImage) {
Mike Reed0acd7952017-04-28 11:12:19 -0400615 fImGuiGamutPaint.setShader(gamutImage->makeShader());
Brian Osmana109e392017-02-24 09:49:14 -0500616 }
617 fImGuiGamutPaint.setColor(SK_ColorWHITE);
618 fImGuiGamutPaint.setFilterQuality(kLow_SkFilterQuality);
619
jongdeok.kim804f17e2019-02-26 14:39:23 +0900620 fWindow->attach(backend_type_for_window(fBackendType));
Jim Van Verth74826c82019-03-01 14:37:30 -0500621 this->setCurrentSlide(this->startupSlide());
jvanverth9f372462016-04-06 06:08:59 -0700622}
623
jvanverth34524262016-05-04 13:49:13 -0700624void Viewer::initSlides() {
Florin Malita0ffa3222018-04-05 14:34:45 -0400625 using SlideFactory = sk_sp<Slide>(*)(const SkString& name, const SkString& path);
626 static const struct {
627 const char* fExtension;
628 const char* fDirName;
Mike Klein88544fb2019-03-20 10:50:33 -0500629 const CommandLineFlags::StringArray& fFlags;
Florin Malita0ffa3222018-04-05 14:34:45 -0400630 const SlideFactory fFactory;
631 } gExternalSlidesInfo[] = {
632 { ".skp", "skp-dir", FLAGS_skps,
633 [](const SkString& name, const SkString& path) -> sk_sp<Slide> {
634 return sk_make_sp<SKPSlide>(name, path);}
635 },
636 { ".jpg", "jpg-dir", FLAGS_jpgs,
637 [](const SkString& name, const SkString& path) -> sk_sp<Slide> {
638 return sk_make_sp<ImageSlide>(name, path);}
639 },
Florin Malita87ccf332018-05-04 12:23:24 -0400640#if defined(SK_ENABLE_SKOTTIE)
Eric Boren8c172ba2018-07-19 13:27:49 -0400641 { ".json", "skottie-dir", FLAGS_lotties,
Florin Malita0ffa3222018-04-05 14:34:45 -0400642 [](const SkString& name, const SkString& path) -> sk_sp<Slide> {
643 return sk_make_sp<SkottieSlide>(name, path);}
644 },
Florin Malita87ccf332018-05-04 12:23:24 -0400645#endif
Florin Malita5d3ff432018-07-31 16:38:43 -0400646#if defined(SK_XML)
Florin Malita0ffa3222018-04-05 14:34:45 -0400647 { ".svg", "svg-dir", FLAGS_svgs,
648 [](const SkString& name, const SkString& path) -> sk_sp<Slide> {
649 return sk_make_sp<SvgSlide>(name, path);}
650 },
Florin Malita5d3ff432018-07-31 16:38:43 -0400651#endif
Florin Malita0ffa3222018-04-05 14:34:45 -0400652 };
jvanverthc265a922016-04-08 12:51:45 -0700653
Brian Salomon343553a2018-09-05 15:41:23 -0400654 SkTArray<sk_sp<Slide>> dirSlides;
jvanverthc265a922016-04-08 12:51:45 -0700655
Mike Klein88544fb2019-03-20 10:50:33 -0500656 const auto addSlide =
657 [&](const SkString& name, const SkString& path, const SlideFactory& fact) {
658 if (CommandLineFlags::ShouldSkip(FLAGS_match, name.c_str())) {
659 return;
660 }
liyuqian6f163d22016-06-13 12:26:45 -0700661
Mike Klein88544fb2019-03-20 10:50:33 -0500662 if (auto slide = fact(name, path)) {
663 dirSlides.push_back(slide);
664 fSlides.push_back(std::move(slide));
665 }
666 };
Florin Malita76a076b2018-02-15 18:40:48 -0500667
Florin Malita38792ce2018-05-08 10:36:18 -0400668 if (!FLAGS_file.isEmpty()) {
669 // single file mode
670 const SkString file(FLAGS_file[0]);
671
672 if (sk_exists(file.c_str(), kRead_SkFILE_Flag)) {
673 for (const auto& sinfo : gExternalSlidesInfo) {
674 if (file.endsWith(sinfo.fExtension)) {
675 addSlide(SkOSPath::Basename(file.c_str()), file, sinfo.fFactory);
676 return;
677 }
678 }
679
680 fprintf(stderr, "Unsupported file type \"%s\"\n", file.c_str());
681 } else {
682 fprintf(stderr, "Cannot read \"%s\"\n", file.c_str());
683 }
684
685 return;
686 }
687
688 // Bisect slide.
689 if (!FLAGS_bisect.isEmpty()) {
690 sk_sp<BisectSlide> bisect = BisectSlide::Create(FLAGS_bisect[0]);
Mike Klein88544fb2019-03-20 10:50:33 -0500691 if (bisect && !CommandLineFlags::ShouldSkip(FLAGS_match, bisect->getName().c_str())) {
Florin Malita38792ce2018-05-08 10:36:18 -0400692 if (FLAGS_bisect.count() >= 2) {
693 for (const char* ch = FLAGS_bisect[1]; *ch; ++ch) {
694 bisect->onChar(*ch);
695 }
696 }
697 fSlides.push_back(std::move(bisect));
698 }
699 }
700
701 // GMs
702 int firstGM = fSlides.count();
Hal Canary972eba32018-07-30 17:07:07 -0400703 for (skiagm::GMFactory gmFactory : skiagm::GMRegistry::Range()) {
Ben Wagner406ff502019-08-12 16:39:24 -0400704 std::unique_ptr<skiagm::GM> gm = gmFactory();
Mike Klein88544fb2019-03-20 10:50:33 -0500705 if (!CommandLineFlags::ShouldSkip(FLAGS_match, gm->getName())) {
Ben Wagner406ff502019-08-12 16:39:24 -0400706 sk_sp<Slide> slide(new GMSlide(std::move(gm)));
Florin Malita38792ce2018-05-08 10:36:18 -0400707 fSlides.push_back(std::move(slide));
708 }
Florin Malita38792ce2018-05-08 10:36:18 -0400709 }
710 // reverse gms
711 int numGMs = fSlides.count() - firstGM;
712 for (int i = 0; i < numGMs/2; ++i) {
713 std::swap(fSlides[firstGM + i], fSlides[fSlides.count() - i - 1]);
714 }
715
716 // samples
Ben Wagnerb2c4ea62018-08-08 11:36:17 -0400717 for (const SampleFactory factory : SampleRegistry::Range()) {
718 sk_sp<Slide> slide(new SampleSlide(factory));
Mike Klein88544fb2019-03-20 10:50:33 -0500719 if (!CommandLineFlags::ShouldSkip(FLAGS_match, slide->getName().c_str())) {
Florin Malita38792ce2018-05-08 10:36:18 -0400720 fSlides.push_back(slide);
721 }
Florin Malita38792ce2018-05-08 10:36:18 -0400722 }
723
Brian Osman7c979f52019-02-12 13:27:51 -0500724 // Particle demo
725 {
726 // TODO: Convert this to a sample
727 sk_sp<Slide> slide(new ParticlesSlide());
Mike Klein88544fb2019-03-20 10:50:33 -0500728 if (!CommandLineFlags::ShouldSkip(FLAGS_match, slide->getName().c_str())) {
Brian Osman7c979f52019-02-12 13:27:51 -0500729 fSlides.push_back(std::move(slide));
730 }
731 }
732
Brian Osmand927bd22019-12-18 11:23:12 -0500733 // Runtime shader editor
734 {
735 sk_sp<Slide> slide(new SkSLSlide());
736 if (!CommandLineFlags::ShouldSkip(FLAGS_match, slide->getName().c_str())) {
737 fSlides.push_back(std::move(slide));
738 }
739 }
740
Florin Malita0ffa3222018-04-05 14:34:45 -0400741 for (const auto& info : gExternalSlidesInfo) {
742 for (const auto& flag : info.fFlags) {
743 if (SkStrEndsWith(flag.c_str(), info.fExtension)) {
744 // single file
745 addSlide(SkOSPath::Basename(flag.c_str()), flag, info.fFactory);
746 } else {
747 // directory
748 SkOSFile::Iter it(flag.c_str(), info.fExtension);
749 SkString name;
750 while (it.next(&name)) {
751 addSlide(name, SkOSPath::Join(flag.c_str(), name.c_str()), info.fFactory);
752 }
Florin Malitac659c2c2018-04-05 11:57:21 -0400753 }
Florin Malita0ffa3222018-04-05 14:34:45 -0400754 if (!dirSlides.empty()) {
755 fSlides.push_back(
756 sk_make_sp<SlideDir>(SkStringPrintf("%s[%s]", info.fDirName, flag.c_str()),
757 std::move(dirSlides)));
Mike Klein16885072018-12-11 09:54:31 -0500758 dirSlides.reset(); // NOLINT(bugprone-use-after-move)
Florin Malita0ffa3222018-04-05 14:34:45 -0400759 }
Florin Malitac659c2c2018-04-05 11:57:21 -0400760 }
761 }
Jim Van Verth74826c82019-03-01 14:37:30 -0500762
763 if (!fSlides.count()) {
764 sk_sp<Slide> slide(new NullSlide());
765 fSlides.push_back(std::move(slide));
766 }
jvanverth2bb3b6d2016-04-08 07:24:09 -0700767}
768
769
jvanverth34524262016-05-04 13:49:13 -0700770Viewer::~Viewer() {
jvanverth9f372462016-04-06 06:08:59 -0700771 fWindow->detach();
772 delete fWindow;
773}
774
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500775struct SkPaintTitleUpdater {
776 SkPaintTitleUpdater(SkString* title) : fTitle(title), fCount(0) {}
777 void append(const char* s) {
778 if (fCount == 0) {
779 fTitle->append(" {");
780 } else {
781 fTitle->append(", ");
782 }
783 fTitle->append(s);
784 ++fCount;
785 }
786 void done() {
787 if (fCount > 0) {
788 fTitle->append("}");
789 }
790 }
791 SkString* fTitle;
792 int fCount;
793};
794
brianosman05de2162016-05-06 13:28:57 -0700795void Viewer::updateTitle() {
csmartdalton578f0642017-02-24 16:04:47 -0700796 if (!fWindow) {
797 return;
798 }
Brian Salomonbdecacf2018-02-02 20:32:49 -0500799 if (fWindow->sampleCount() < 1) {
csmartdalton578f0642017-02-24 16:04:47 -0700800 return; // Surface hasn't been created yet.
801 }
802
jvanverth34524262016-05-04 13:49:13 -0700803 SkString title("Viewer: ");
jvanverthc265a922016-04-08 12:51:45 -0700804 title.append(fSlides[fCurrentSlide]->getName());
brianosmanb109b8c2016-06-16 13:03:24 -0700805
Mike Kleine5acd752019-03-22 09:57:16 -0500806 if (gSkUseAnalyticAA) {
Yuqian Li399b3c22017-08-03 11:08:15 -0400807 if (gSkForceAnalyticAA) {
808 title.append(" <FAAA>");
809 } else {
810 title.append(" <AAA>");
811 }
812 }
813
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500814 SkPaintTitleUpdater paintTitle(&title);
Ben Wagner9613e452019-01-23 10:34:59 -0500815 auto paintFlag = [this, &paintTitle](bool SkPaintFields::* flag,
816 bool (SkPaint::* isFlag)() const,
Ben Wagner99a78dc2018-05-09 18:23:51 -0400817 const char* on, const char* off)
818 {
Ben Wagner9613e452019-01-23 10:34:59 -0500819 if (fPaintOverrides.*flag) {
Ben Wagner99a78dc2018-05-09 18:23:51 -0400820 paintTitle.append((fPaint.*isFlag)() ? on : off);
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500821 }
Ben Wagner99a78dc2018-05-09 18:23:51 -0400822 };
823
Ben Wagner9613e452019-01-23 10:34:59 -0500824 auto fontFlag = [this, &paintTitle](bool SkFontFields::* flag, bool (SkFont::* isFlag)() const,
825 const char* on, const char* off)
826 {
827 if (fFontOverrides.*flag) {
828 paintTitle.append((fFont.*isFlag)() ? on : off);
829 }
830 };
831
832 paintFlag(&SkPaintFields::fAntiAlias, &SkPaint::isAntiAlias, "Antialias", "Alias");
833 paintFlag(&SkPaintFields::fDither, &SkPaint::isDither, "DITHER", "No Dither");
Ben Wagnerd10a78f2019-03-07 13:14:26 -0500834 if (fPaintOverrides.fFilterQuality) {
835 switch (fPaint.getFilterQuality()) {
836 case kNone_SkFilterQuality:
837 paintTitle.append("NoFilter");
838 break;
839 case kLow_SkFilterQuality:
840 paintTitle.append("LowFilter");
841 break;
842 case kMedium_SkFilterQuality:
843 paintTitle.append("MediumFilter");
844 break;
845 case kHigh_SkFilterQuality:
846 paintTitle.append("HighFilter");
847 break;
848 }
849 }
Ben Wagner9613e452019-01-23 10:34:59 -0500850
851 fontFlag(&SkFontFields::fForceAutoHinting, &SkFont::isForceAutoHinting,
852 "Force Autohint", "No Force Autohint");
853 fontFlag(&SkFontFields::fEmbolden, &SkFont::isEmbolden, "Fake Bold", "No Fake Bold");
Ben Wagnerc17de1d2019-08-26 16:59:09 -0400854 fontFlag(&SkFontFields::fBaselineSnap, &SkFont::isBaselineSnap, "BaseSnap", "No BaseSnap");
Ben Wagner9613e452019-01-23 10:34:59 -0500855 fontFlag(&SkFontFields::fLinearMetrics, &SkFont::isLinearMetrics,
856 "Linear Metrics", "Non-Linear Metrics");
857 fontFlag(&SkFontFields::fEmbeddedBitmaps, &SkFont::isEmbeddedBitmaps,
858 "Bitmap Text", "No Bitmap Text");
859 fontFlag(&SkFontFields::fSubpixel, &SkFont::isSubpixel, "Subpixel Text", "Pixel Text");
860
861 if (fFontOverrides.fEdging) {
862 switch (fFont.getEdging()) {
863 case SkFont::Edging::kAlias:
864 paintTitle.append("Alias Text");
865 break;
866 case SkFont::Edging::kAntiAlias:
867 paintTitle.append("Antialias Text");
868 break;
869 case SkFont::Edging::kSubpixelAntiAlias:
870 paintTitle.append("Subpixel Antialias Text");
871 break;
872 }
873 }
Ben Wagner99a78dc2018-05-09 18:23:51 -0400874
Mike Reed3ae47332019-01-04 10:11:46 -0500875 if (fFontOverrides.fHinting) {
876 switch (fFont.getHinting()) {
Ben Wagner5785e4a2019-05-07 16:50:29 -0400877 case SkFontHinting::kNone:
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500878 paintTitle.append("No Hinting");
879 break;
Ben Wagner5785e4a2019-05-07 16:50:29 -0400880 case SkFontHinting::kSlight:
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500881 paintTitle.append("Slight Hinting");
882 break;
Ben Wagner5785e4a2019-05-07 16:50:29 -0400883 case SkFontHinting::kNormal:
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500884 paintTitle.append("Normal Hinting");
885 break;
Ben Wagner5785e4a2019-05-07 16:50:29 -0400886 case SkFontHinting::kFull:
Ben Wagnerabdcc5f2018-02-12 16:37:28 -0500887 paintTitle.append("Full Hinting");
888 break;
889 }
890 }
891 paintTitle.done();
892
Brian Osman92004802017-03-06 11:47:26 -0500893 switch (fColorMode) {
894 case ColorMode::kLegacy:
895 title.append(" Legacy 8888");
896 break;
Brian Osman03115dc2018-11-26 13:55:19 -0500897 case ColorMode::kColorManaged8888:
Brian Osman92004802017-03-06 11:47:26 -0500898 title.append(" ColorManaged 8888");
899 break;
Brian Osman03115dc2018-11-26 13:55:19 -0500900 case ColorMode::kColorManagedF16:
Brian Osman92004802017-03-06 11:47:26 -0500901 title.append(" ColorManaged F16");
902 break;
Brian Salomon8391bac2019-09-18 11:22:44 -0400903 case ColorMode::kColorManagedF16Norm:
904 title.append(" ColorManaged F16 Norm");
905 break;
Brian Osman92004802017-03-06 11:47:26 -0500906 }
Brian Osmanf750fbc2017-02-08 10:47:28 -0500907
Brian Osman92004802017-03-06 11:47:26 -0500908 if (ColorMode::kLegacy != fColorMode) {
Brian Osmana109e392017-02-24 09:49:14 -0500909 int curPrimaries = -1;
910 for (size_t i = 0; i < SK_ARRAY_COUNT(gNamedPrimaries); ++i) {
911 if (primaries_equal(*gNamedPrimaries[i].fPrimaries, fColorSpacePrimaries)) {
912 curPrimaries = i;
913 break;
914 }
915 }
Brian Osman03115dc2018-11-26 13:55:19 -0500916 title.appendf(" %s Gamma %f",
917 curPrimaries >= 0 ? gNamedPrimaries[curPrimaries].fName : "Custom",
Brian Osman82ebe042019-01-04 17:03:00 -0500918 fColorSpaceTransferFn.g);
brianosman05de2162016-05-06 13:28:57 -0700919 }
Brian Osmanf750fbc2017-02-08 10:47:28 -0500920
Ben Wagner37c54032018-04-13 14:30:23 -0400921 const DisplayParams& params = fWindow->getRequestedDisplayParams();
922 if (fPixelGeometryOverrides) {
923 switch (params.fSurfaceProps.pixelGeometry()) {
924 case kUnknown_SkPixelGeometry:
925 title.append( " Flat");
926 break;
927 case kRGB_H_SkPixelGeometry:
928 title.append( " RGB");
929 break;
930 case kBGR_H_SkPixelGeometry:
931 title.append( " BGR");
932 break;
933 case kRGB_V_SkPixelGeometry:
934 title.append( " RGBV");
935 break;
936 case kBGR_V_SkPixelGeometry:
937 title.append( " BGRV");
938 break;
939 }
940 }
941
942 if (params.fSurfaceProps.isUseDeviceIndependentFonts()) {
943 title.append(" DFT");
944 }
945
csmartdalton578f0642017-02-24 16:04:47 -0700946 title.append(" [");
jvanverthaf236b52016-05-20 06:01:06 -0700947 title.append(kBackendTypeStrings[fBackendType]);
Brian Salomonbdecacf2018-02-02 20:32:49 -0500948 int msaa = fWindow->sampleCount();
949 if (msaa > 1) {
csmartdalton578f0642017-02-24 16:04:47 -0700950 title.appendf(" MSAA: %i", msaa);
951 }
952 title.append("]");
csmartdalton61cd31a2017-02-27 17:00:53 -0700953
954 GpuPathRenderers pr = fWindow->getRequestedDisplayParams().fGrContextOptions.fGpuPathRenderers;
Chris Dalton37ae4b02019-12-28 14:51:11 -0700955 if (GpuPathRenderers::kDefault != pr) {
csmartdalton61cd31a2017-02-27 17:00:53 -0700956 title.appendf(" [Path renderer: %s]", gPathRendererNames[pr].c_str());
957 }
958
Brian Osman805a7272018-05-02 15:40:20 -0400959 if (kPerspective_Real == fPerspectiveMode) {
960 title.append(" Perpsective (Real)");
961 } else if (kPerspective_Fake == fPerspectiveMode) {
962 title.append(" Perspective (Fake)");
963 }
964
brianosman05de2162016-05-06 13:28:57 -0700965 fWindow->setTitle(title.c_str());
966}
967
Florin Malitaab99c342018-01-16 16:23:03 -0500968int Viewer::startupSlide() const {
Jim Van Verth6f449692017-02-14 15:16:46 -0500969
970 if (!FLAGS_slide.isEmpty()) {
971 int count = fSlides.count();
972 for (int i = 0; i < count; i++) {
973 if (fSlides[i]->getName().equals(FLAGS_slide[0])) {
Florin Malitaab99c342018-01-16 16:23:03 -0500974 return i;
Jim Van Verth6f449692017-02-14 15:16:46 -0500975 }
976 }
977
978 fprintf(stderr, "Unknown slide \"%s\"\n", FLAGS_slide[0]);
979 this->listNames();
980 }
981
Florin Malitaab99c342018-01-16 16:23:03 -0500982 return 0;
Jim Van Verth6f449692017-02-14 15:16:46 -0500983}
984
Florin Malitaab99c342018-01-16 16:23:03 -0500985void Viewer::listNames() const {
Jim Van Verth6f449692017-02-14 15:16:46 -0500986 SkDebugf("All Slides:\n");
Florin Malitaab99c342018-01-16 16:23:03 -0500987 for (const auto& slide : fSlides) {
988 SkDebugf(" %s\n", slide->getName().c_str());
Jim Van Verth6f449692017-02-14 15:16:46 -0500989 }
990}
991
Florin Malitaab99c342018-01-16 16:23:03 -0500992void Viewer::setCurrentSlide(int slide) {
993 SkASSERT(slide >= 0 && slide < fSlides.count());
liyuqian6f163d22016-06-13 12:26:45 -0700994
Florin Malitaab99c342018-01-16 16:23:03 -0500995 if (slide == fCurrentSlide) {
996 return;
997 }
998
999 if (fCurrentSlide >= 0) {
1000 fSlides[fCurrentSlide]->unload();
1001 }
1002
1003 fSlides[slide]->load(SkIntToScalar(fWindow->width()),
1004 SkIntToScalar(fWindow->height()));
1005 fCurrentSlide = slide;
1006 this->setupCurrentSlide();
1007}
1008
1009void Viewer::setupCurrentSlide() {
Jim Van Verth0848fb02018-01-22 13:39:30 -05001010 if (fCurrentSlide >= 0) {
1011 // prepare dimensions for image slides
1012 fGesture.resetTouchState();
1013 fDefaultMatrix.reset();
liyuqiane46e4f02016-05-20 07:32:19 -07001014
Jim Van Verth0848fb02018-01-22 13:39:30 -05001015 const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
1016 const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
1017 const SkRect windowRect = SkRect::MakeIWH(fWindow->width(), fWindow->height());
Brian Osman42bb6ac2017-06-05 08:46:04 -04001018
Jim Van Verth0848fb02018-01-22 13:39:30 -05001019 // Start with a matrix that scales the slide to the available screen space
1020 if (fWindow->scaleContentToFit()) {
1021 if (windowRect.width() > 0 && windowRect.height() > 0) {
1022 fDefaultMatrix.setRectToRect(slideBounds, windowRect, SkMatrix::kStart_ScaleToFit);
1023 }
liyuqiane46e4f02016-05-20 07:32:19 -07001024 }
Jim Van Verth0848fb02018-01-22 13:39:30 -05001025
1026 // Prevent the user from dragging content so far outside the window they can't find it again
Yuqian Li755778c2018-03-28 16:23:31 -04001027 fGesture.setTransLimit(slideBounds, windowRect, this->computePreTouchMatrix());
Jim Van Verth0848fb02018-01-22 13:39:30 -05001028
1029 this->updateTitle();
1030 this->updateUIState();
1031
1032 fStatsLayer.resetMeasurements();
1033
1034 fWindow->inval();
liyuqiane46e4f02016-05-20 07:32:19 -07001035 }
jvanverthc265a922016-04-08 12:51:45 -07001036}
1037
1038#define MAX_ZOOM_LEVEL 8
1039#define MIN_ZOOM_LEVEL -8
1040
jvanverth34524262016-05-04 13:49:13 -07001041void Viewer::changeZoomLevel(float delta) {
jvanverthc265a922016-04-08 12:51:45 -07001042 fZoomLevel += delta;
Brian Osman42bb6ac2017-06-05 08:46:04 -04001043 fZoomLevel = SkScalarPin(fZoomLevel, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL);
Ben Wagnerd02a74d2018-04-23 12:55:06 -04001044 this->preTouchMatrixChanged();
1045}
Yuqian Li755778c2018-03-28 16:23:31 -04001046
Ben Wagnerd02a74d2018-04-23 12:55:06 -04001047void Viewer::preTouchMatrixChanged() {
1048 // Update the trans limit as the transform changes.
Yuqian Li755778c2018-03-28 16:23:31 -04001049 const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
1050 const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
1051 const SkRect windowRect = SkRect::MakeIWH(fWindow->width(), fWindow->height());
1052 fGesture.setTransLimit(slideBounds, windowRect, this->computePreTouchMatrix());
1053}
1054
Brian Osman805a7272018-05-02 15:40:20 -04001055SkMatrix Viewer::computePerspectiveMatrix() {
1056 SkScalar w = fWindow->width(), h = fWindow->height();
1057 SkPoint orthoPts[4] = { { 0, 0 }, { w, 0 }, { 0, h }, { w, h } };
1058 SkPoint perspPts[4] = {
1059 { fPerspectivePoints[0].fX * w, fPerspectivePoints[0].fY * h },
1060 { fPerspectivePoints[1].fX * w, fPerspectivePoints[1].fY * h },
1061 { fPerspectivePoints[2].fX * w, fPerspectivePoints[2].fY * h },
1062 { fPerspectivePoints[3].fX * w, fPerspectivePoints[3].fY * h }
1063 };
1064 SkMatrix m;
1065 m.setPolyToPoly(orthoPts, perspPts, 4);
1066 return m;
1067}
1068
Yuqian Li755778c2018-03-28 16:23:31 -04001069SkMatrix Viewer::computePreTouchMatrix() {
1070 SkMatrix m = fDefaultMatrix;
Ben Wagnercc8eb862019-03-21 16:50:22 -04001071
1072 SkScalar zoomScale = exp(fZoomLevel);
Ben Wagner897dfa22018-08-09 15:18:46 -04001073 m.preTranslate((fOffset.x() - 0.5f) * 2.0f, (fOffset.y() - 0.5f) * 2.0f);
Yuqian Li755778c2018-03-28 16:23:31 -04001074 m.preScale(zoomScale, zoomScale);
Brian Osmanbdaf97b2018-04-26 16:22:42 -04001075
1076 const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
1077 m.preRotate(fRotation, slideSize.width() * 0.5f, slideSize.height() * 0.5f);
Brian Osman9bb47cf2018-04-26 15:55:00 -04001078
Brian Osman805a7272018-05-02 15:40:20 -04001079 if (kPerspective_Real == fPerspectiveMode) {
1080 SkMatrix persp = this->computePerspectiveMatrix();
Brian Osmanbdaf97b2018-04-26 16:22:42 -04001081 m.postConcat(persp);
Brian Osman9bb47cf2018-04-26 15:55:00 -04001082 }
1083
Yuqian Li755778c2018-03-28 16:23:31 -04001084 return m;
jvanverthc265a922016-04-08 12:51:45 -07001085}
1086
liyuqiand3cdbca2016-05-17 12:44:20 -07001087SkMatrix Viewer::computeMatrix() {
Yuqian Li755778c2018-03-28 16:23:31 -04001088 SkMatrix m = fGesture.localM();
liyuqiand3cdbca2016-05-17 12:44:20 -07001089 m.preConcat(fGesture.globalM());
Yuqian Li755778c2018-03-28 16:23:31 -04001090 m.preConcat(this->computePreTouchMatrix());
liyuqiand3cdbca2016-05-17 12:44:20 -07001091 return m;
jvanverthc265a922016-04-08 12:51:45 -07001092}
1093
Brian Osman621491e2017-02-28 15:45:01 -05001094void Viewer::setBackend(sk_app::Window::BackendType backendType) {
Brian Osman5bee3902019-05-07 09:55:45 -04001095 fPersistentCache.reset();
1096 fCachedGLSL.reset();
Brian Osman621491e2017-02-28 15:45:01 -05001097 fBackendType = backendType;
1098
1099 fWindow->detach();
1100
Brian Osman70d2f432017-11-08 09:54:10 -05001101#if defined(SK_BUILD_FOR_WIN)
Brian Salomon194db172017-08-17 14:37:06 -04001102 // Switching between OpenGL, Vulkan, and ANGLE in the same window is problematic at this point
1103 // on Windows, so we just delete the window and recreate it.
Brian Osman70d2f432017-11-08 09:54:10 -05001104 DisplayParams params = fWindow->getRequestedDisplayParams();
1105 delete fWindow;
1106 fWindow = Window::CreateNativeWindow(nullptr);
Brian Osman621491e2017-02-28 15:45:01 -05001107
Brian Osman70d2f432017-11-08 09:54:10 -05001108 // re-register callbacks
1109 fCommands.attach(fWindow);
Brian Osman80fc07e2017-12-08 16:45:43 -05001110 fWindow->pushLayer(this);
Brian Osman56a24812017-12-19 11:15:16 -05001111 fWindow->pushLayer(&fStatsLayer);
Brian Osmand67e5182017-12-08 16:46:09 -05001112 fWindow->pushLayer(&fImGuiLayer);
1113
Brian Osman70d2f432017-11-08 09:54:10 -05001114 // Don't allow the window to re-attach. If we're in MSAA mode, the params we grabbed above
1115 // will still include our correct sample count. But the re-created fWindow will lose that
1116 // information. On Windows, we need to re-create the window when changing sample count,
1117 // so we'll incorrectly detect that situation, then re-initialize the window in GL mode,
1118 // rendering this tear-down step pointless (and causing the Vulkan window context to fail
1119 // as if we had never changed windows at all).
1120 fWindow->setRequestedDisplayParams(params, false);
Brian Osman621491e2017-02-28 15:45:01 -05001121#endif
1122
Brian Osman70d2f432017-11-08 09:54:10 -05001123 fWindow->attach(backend_type_for_window(fBackendType));
Brian Osman621491e2017-02-28 15:45:01 -05001124}
1125
Brian Osman92004802017-03-06 11:47:26 -05001126void Viewer::setColorMode(ColorMode colorMode) {
1127 fColorMode = colorMode;
Brian Osmanf750fbc2017-02-08 10:47:28 -05001128 this->updateTitle();
1129 fWindow->inval();
1130}
1131
Ben Wagnerabdcc5f2018-02-12 16:37:28 -05001132class OveridePaintFilterCanvas : public SkPaintFilterCanvas {
1133public:
Mike Reed3ae47332019-01-04 10:11:46 -05001134 OveridePaintFilterCanvas(SkCanvas* canvas, SkPaint* paint, Viewer::SkPaintFields* pfields,
1135 SkFont* font, Viewer::SkFontFields* ffields)
1136 : SkPaintFilterCanvas(canvas), fPaint(paint), fPaintOverrides(pfields), fFont(font), fFontOverrides(ffields)
Ben Wagnerabdcc5f2018-02-12 16:37:28 -05001137 { }
Ben Wagner41e40472018-09-24 13:01:54 -04001138 const SkTextBlob* filterTextBlob(const SkPaint& paint, const SkTextBlob* blob,
1139 sk_sp<SkTextBlob>* cache) {
1140 bool blobWillChange = false;
1141 for (SkTextBlobRunIterator it(blob); !it.done(); it.next()) {
Mike Reed3ae47332019-01-04 10:11:46 -05001142 SkTCopyOnFirstWrite<SkFont> filteredFont(it.font());
1143 bool shouldDraw = this->filterFont(&filteredFont);
1144 if (it.font() != *filteredFont || !shouldDraw) {
Ben Wagner41e40472018-09-24 13:01:54 -04001145 blobWillChange = true;
1146 break;
1147 }
1148 }
1149 if (!blobWillChange) {
1150 return blob;
1151 }
1152
1153 SkTextBlobBuilder builder;
1154 for (SkTextBlobRunIterator it(blob); !it.done(); it.next()) {
Mike Reed3ae47332019-01-04 10:11:46 -05001155 SkTCopyOnFirstWrite<SkFont> filteredFont(it.font());
1156 bool shouldDraw = this->filterFont(&filteredFont);
Ben Wagner41e40472018-09-24 13:01:54 -04001157 if (!shouldDraw) {
1158 continue;
1159 }
1160
Mike Reed3ae47332019-01-04 10:11:46 -05001161 SkFont font = *filteredFont;
Mike Reed6d595682018-12-05 17:28:14 -05001162
Ben Wagner41e40472018-09-24 13:01:54 -04001163 const SkTextBlobBuilder::RunBuffer& runBuffer
1164 = it.positioning() == SkTextBlobRunIterator::kDefault_Positioning
Mike Reed6d595682018-12-05 17:28:14 -05001165 ? SkTextBlobBuilderPriv::AllocRunText(&builder, font,
Ben Wagner0bb2db12019-03-06 18:19:08 -05001166 it.glyphCount(), it.offset().x(),it.offset().y(), it.textSize(), SkString())
Ben Wagner41e40472018-09-24 13:01:54 -04001167 : it.positioning() == SkTextBlobRunIterator::kHorizontal_Positioning
Mike Reed6d595682018-12-05 17:28:14 -05001168 ? SkTextBlobBuilderPriv::AllocRunTextPosH(&builder, font,
Ben Wagner0bb2db12019-03-06 18:19:08 -05001169 it.glyphCount(), it.offset().y(), it.textSize(), SkString())
Ben Wagner41e40472018-09-24 13:01:54 -04001170 : it.positioning() == SkTextBlobRunIterator::kFull_Positioning
Mike Reed6d595682018-12-05 17:28:14 -05001171 ? SkTextBlobBuilderPriv::AllocRunTextPos(&builder, font,
Ben Wagner41e40472018-09-24 13:01:54 -04001172 it.glyphCount(), it.textSize(), SkString())
1173 : (SkASSERT_RELEASE(false), SkTextBlobBuilder::RunBuffer());
1174 uint32_t glyphCount = it.glyphCount();
1175 if (it.glyphs()) {
1176 size_t glyphSize = sizeof(decltype(*it.glyphs()));
1177 memcpy(runBuffer.glyphs, it.glyphs(), glyphCount * glyphSize);
1178 }
1179 if (it.pos()) {
1180 size_t posSize = sizeof(decltype(*it.pos()));
1181 uint8_t positioning = it.positioning();
1182 memcpy(runBuffer.pos, it.pos(), glyphCount * positioning * posSize);
1183 }
1184 if (it.text()) {
1185 size_t textSize = sizeof(decltype(*it.text()));
1186 uint32_t textCount = it.textSize();
1187 memcpy(runBuffer.utf8text, it.text(), textCount * textSize);
1188 }
1189 if (it.clusters()) {
1190 size_t clusterSize = sizeof(decltype(*it.clusters()));
1191 memcpy(runBuffer.clusters, it.clusters(), glyphCount * clusterSize);
1192 }
1193 }
1194 *cache = builder.make();
1195 return cache->get();
1196 }
1197 void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
1198 const SkPaint& paint) override {
1199 sk_sp<SkTextBlob> cache;
1200 this->SkPaintFilterCanvas::onDrawTextBlob(
1201 this->filterTextBlob(paint, blob, &cache), x, y, paint);
1202 }
Mike Reed3ae47332019-01-04 10:11:46 -05001203 bool filterFont(SkTCopyOnFirstWrite<SkFont>* font) const {
Ben Wagner15a8d572019-03-21 13:35:44 -04001204 if (fFontOverrides->fSize) {
Mike Reed3ae47332019-01-04 10:11:46 -05001205 font->writable()->setSize(fFont->getSize());
1206 }
Ben Wagner15a8d572019-03-21 13:35:44 -04001207 if (fFontOverrides->fScaleX) {
1208 font->writable()->setScaleX(fFont->getScaleX());
1209 }
1210 if (fFontOverrides->fSkewX) {
1211 font->writable()->setSkewX(fFont->getSkewX());
1212 }
Mike Reed3ae47332019-01-04 10:11:46 -05001213 if (fFontOverrides->fHinting) {
1214 font->writable()->setHinting(fFont->getHinting());
1215 }
Ben Wagner9613e452019-01-23 10:34:59 -05001216 if (fFontOverrides->fEdging) {
1217 font->writable()->setEdging(fFont->getEdging());
Hal Canary02738a82019-01-21 18:51:32 +00001218 }
Ben Wagner9613e452019-01-23 10:34:59 -05001219 if (fFontOverrides->fEmbolden) {
1220 font->writable()->setEmbolden(fFont->isEmbolden());
Hal Canary02738a82019-01-21 18:51:32 +00001221 }
Ben Wagnerc17de1d2019-08-26 16:59:09 -04001222 if (fFontOverrides->fBaselineSnap) {
1223 font->writable()->setBaselineSnap(fFont->isBaselineSnap());
1224 }
Ben Wagner9613e452019-01-23 10:34:59 -05001225 if (fFontOverrides->fLinearMetrics) {
1226 font->writable()->setLinearMetrics(fFont->isLinearMetrics());
Hal Canary02738a82019-01-21 18:51:32 +00001227 }
Ben Wagner9613e452019-01-23 10:34:59 -05001228 if (fFontOverrides->fSubpixel) {
1229 font->writable()->setSubpixel(fFont->isSubpixel());
Hal Canary02738a82019-01-21 18:51:32 +00001230 }
Ben Wagner9613e452019-01-23 10:34:59 -05001231 if (fFontOverrides->fEmbeddedBitmaps) {
1232 font->writable()->setEmbeddedBitmaps(fFont->isEmbeddedBitmaps());
Hal Canary02738a82019-01-21 18:51:32 +00001233 }
Ben Wagner9613e452019-01-23 10:34:59 -05001234 if (fFontOverrides->fForceAutoHinting) {
1235 font->writable()->setForceAutoHinting(fFont->isForceAutoHinting());
Hal Canary02738a82019-01-21 18:51:32 +00001236 }
Ben Wagner9613e452019-01-23 10:34:59 -05001237
Mike Reed3ae47332019-01-04 10:11:46 -05001238 return true;
1239 }
Ben Wagnerf55fa0d2018-08-27 18:11:57 -04001240 bool onFilter(SkPaint& paint) const override {
Ben Wagner9613e452019-01-23 10:34:59 -05001241 if (fPaintOverrides->fAntiAlias) {
Ben Wagnerf55fa0d2018-08-27 18:11:57 -04001242 paint.setAntiAlias(fPaint->isAntiAlias());
Ben Wagnerabdcc5f2018-02-12 16:37:28 -05001243 }
Ben Wagner9613e452019-01-23 10:34:59 -05001244 if (fPaintOverrides->fDither) {
Ben Wagnerf55fa0d2018-08-27 18:11:57 -04001245 paint.setDither(fPaint->isDither());
Ben Wagner99a78dc2018-05-09 18:23:51 -04001246 }
Ben Wagnerd10a78f2019-03-07 13:14:26 -05001247 if (fPaintOverrides->fFilterQuality) {
Ben Wagnerf55fa0d2018-08-27 18:11:57 -04001248 paint.setFilterQuality(fPaint->getFilterQuality());
Ben Wagnerd10a78f2019-03-07 13:14:26 -05001249 }
Ben Wagnerabdcc5f2018-02-12 16:37:28 -05001250 return true;
1251 }
1252 SkPaint* fPaint;
1253 Viewer::SkPaintFields* fPaintOverrides;
Mike Reed3ae47332019-01-04 10:11:46 -05001254 SkFont* fFont;
1255 Viewer::SkFontFields* fFontOverrides;
Ben Wagnerabdcc5f2018-02-12 16:37:28 -05001256};
1257
Robert Phillips9882dae2019-03-04 11:00:10 -05001258void Viewer::drawSlide(SkSurface* surface) {
Jim Van Verth74826c82019-03-01 14:37:30 -05001259 if (fCurrentSlide < 0) {
1260 return;
1261 }
1262
Robert Phillips9882dae2019-03-04 11:00:10 -05001263 SkAutoCanvasRestore autorestore(surface->getCanvas(), false);
Brian Salomonbf52e3d2017-02-22 15:21:11 -05001264
Brian Osmanf750fbc2017-02-08 10:47:28 -05001265 // By default, we render directly into the window's surface/canvas
Robert Phillips9882dae2019-03-04 11:00:10 -05001266 SkSurface* slideSurface = surface;
1267 SkCanvas* slideCanvas = surface->getCanvas();
Brian Osmanf6877092017-02-13 09:39:57 -05001268 fLastImage.reset();
jvanverth3d6ed3a2016-04-07 11:09:51 -07001269
Brian Osmane0d4fba2017-03-15 10:24:55 -04001270 // 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 -05001271 sk_sp<SkColorSpace> colorSpace = nullptr;
Brian Osmane0d4fba2017-03-15 10:24:55 -04001272 if (ColorMode::kLegacy != fColorMode) {
Brian Osman82ebe042019-01-04 17:03:00 -05001273 skcms_Matrix3x3 toXYZ;
Brian Osmane0d4fba2017-03-15 10:24:55 -04001274 SkAssertResult(fColorSpacePrimaries.toXYZD50(&toXYZ));
Brian Osman03115dc2018-11-26 13:55:19 -05001275 colorSpace = SkColorSpace::MakeRGB(fColorSpaceTransferFn, toXYZ);
Brian Osmane0d4fba2017-03-15 10:24:55 -04001276 }
1277
Brian Osman3ac99cf2017-12-01 11:23:53 -05001278 if (fSaveToSKP) {
1279 SkPictureRecorder recorder;
1280 SkCanvas* recorderCanvas = recorder.beginRecording(
1281 SkRect::Make(fSlides[fCurrentSlide]->getDimensions()));
Brian Osman3ac99cf2017-12-01 11:23:53 -05001282 fSlides[fCurrentSlide]->draw(recorderCanvas);
1283 sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
1284 SkFILEWStream stream("sample_app.skp");
1285 picture->serialize(&stream);
1286 fSaveToSKP = false;
1287 }
1288
Brian Osmane9ed0f02018-11-26 14:50:05 -05001289 // Grab some things we'll need to make surfaces (for tiling or general offscreen rendering)
Brian Salomon8391bac2019-09-18 11:22:44 -04001290 SkColorType colorType;
1291 switch (fColorMode) {
1292 case ColorMode::kLegacy:
1293 case ColorMode::kColorManaged8888:
1294 colorType = kN32_SkColorType;
1295 break;
1296 case ColorMode::kColorManagedF16:
1297 colorType = kRGBA_F16_SkColorType;
1298 break;
1299 case ColorMode::kColorManagedF16Norm:
1300 colorType = kRGBA_F16Norm_SkColorType;
1301 break;
1302 }
Brian Osmane9ed0f02018-11-26 14:50:05 -05001303
1304 auto make_surface = [=](int w, int h) {
Robert Phillips9882dae2019-03-04 11:00:10 -05001305 SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
1306 slideCanvas->getProps(&props);
1307
Brian Osmane9ed0f02018-11-26 14:50:05 -05001308 SkImageInfo info = SkImageInfo::Make(w, h, colorType, kPremul_SkAlphaType, colorSpace);
1309 return Window::kRaster_BackendType == this->fBackendType
1310 ? SkSurface::MakeRaster(info, &props)
Robert Phillips9882dae2019-03-04 11:00:10 -05001311 : slideCanvas->makeSurface(info, &props);
Brian Osmane9ed0f02018-11-26 14:50:05 -05001312 };
1313
Brian Osman03115dc2018-11-26 13:55:19 -05001314 // We need to render offscreen if we're...
1315 // ... in fake perspective or zooming (so we have a snapped copy of the results)
1316 // ... in any raster mode, because the window surface is actually GL
1317 // ... in any color managed mode, because we always make the window surface with no color space
Chris Daltonc8877332020-01-06 09:48:30 -07001318 // ... or if the user explicitly requested offscreen rendering
Brian Osmanf750fbc2017-02-08 10:47:28 -05001319 sk_sp<SkSurface> offscreenSurface = nullptr;
Brian Osman03115dc2018-11-26 13:55:19 -05001320 if (kPerspective_Fake == fPerspectiveMode ||
Brian Osman92004802017-03-06 11:47:26 -05001321 fShowZoomWindow ||
Brian Osman03115dc2018-11-26 13:55:19 -05001322 Window::kRaster_BackendType == fBackendType ||
Chris Daltonc8877332020-01-06 09:48:30 -07001323 colorSpace != nullptr ||
1324 FLAGS_offscreen) {
Brian Osmane0d4fba2017-03-15 10:24:55 -04001325
Brian Osmane9ed0f02018-11-26 14:50:05 -05001326 offscreenSurface = make_surface(fWindow->width(), fWindow->height());
Robert Phillips9882dae2019-03-04 11:00:10 -05001327 slideSurface = offscreenSurface.get();
Mike Klein48b64902018-07-25 13:28:44 -04001328 slideCanvas = offscreenSurface->getCanvas();
Brian Osmanf750fbc2017-02-08 10:47:28 -05001329 }
1330
Brian Salomonbf52e3d2017-02-22 15:21:11 -05001331 int count = slideCanvas->save();
Brian Osmanf750fbc2017-02-08 10:47:28 -05001332 slideCanvas->clear(SK_ColorWHITE);
Brian Osman1df161a2017-02-09 12:10:20 -05001333 // Time the painting logic of the slide
Brian Osman56a24812017-12-19 11:15:16 -05001334 fStatsLayer.beginTiming(fPaintTimer);
Brian Osmane9ed0f02018-11-26 14:50:05 -05001335 if (fTiled) {
1336 int tileW = SkScalarCeilToInt(fWindow->width() * fTileScale.width());
1337 int tileH = SkScalarCeilToInt(fWindow->height() * fTileScale.height());
1338 sk_sp<SkSurface> tileSurface = make_surface(tileW, tileH);
1339 SkCanvas* tileCanvas = tileSurface->getCanvas();
1340 SkMatrix m = this->computeMatrix();
1341 for (int y = 0; y < fWindow->height(); y += tileH) {
1342 for (int x = 0; x < fWindow->width(); x += tileW) {
1343 SkAutoCanvasRestore acr(tileCanvas, true);
1344 tileCanvas->translate(-x, -y);
1345 tileCanvas->clear(SK_ColorTRANSPARENT);
1346 tileCanvas->concat(m);
Mike Reed3ae47332019-01-04 10:11:46 -05001347 OveridePaintFilterCanvas filterCanvas(tileCanvas, &fPaint, &fPaintOverrides,
1348 &fFont, &fFontOverrides);
Brian Osmane9ed0f02018-11-26 14:50:05 -05001349 fSlides[fCurrentSlide]->draw(&filterCanvas);
1350 tileSurface->draw(slideCanvas, x, y, nullptr);
1351 }
1352 }
1353
1354 // Draw borders between tiles
1355 if (fDrawTileBoundaries) {
1356 SkPaint border;
1357 border.setColor(0x60FF00FF);
1358 border.setStyle(SkPaint::kStroke_Style);
1359 for (int y = 0; y < fWindow->height(); y += tileH) {
1360 for (int x = 0; x < fWindow->width(); x += tileW) {
1361 slideCanvas->drawRect(SkRect::MakeXYWH(x, y, tileW, tileH), border);
1362 }
1363 }
1364 }
1365 } else {
1366 slideCanvas->concat(this->computeMatrix());
1367 if (kPerspective_Real == fPerspectiveMode) {
1368 slideCanvas->clipRect(SkRect::MakeWH(fWindow->width(), fWindow->height()));
1369 }
Mike Reed3ae47332019-01-04 10:11:46 -05001370 OveridePaintFilterCanvas filterCanvas(slideCanvas, &fPaint, &fPaintOverrides, &fFont, &fFontOverrides);
Brian Osmane9ed0f02018-11-26 14:50:05 -05001371 fSlides[fCurrentSlide]->draw(&filterCanvas);
1372 }
Brian Osman56a24812017-12-19 11:15:16 -05001373 fStatsLayer.endTiming(fPaintTimer);
Brian Salomonbf52e3d2017-02-22 15:21:11 -05001374 slideCanvas->restoreToCount(count);
Brian Osman1df161a2017-02-09 12:10:20 -05001375
1376 // Force a flush so we can time that, too
Brian Osman56a24812017-12-19 11:15:16 -05001377 fStatsLayer.beginTiming(fFlushTimer);
Robert Phillips9882dae2019-03-04 11:00:10 -05001378 slideSurface->flush();
Brian Osman56a24812017-12-19 11:15:16 -05001379 fStatsLayer.endTiming(fFlushTimer);
Brian Osmanf750fbc2017-02-08 10:47:28 -05001380
1381 // If we rendered offscreen, snap an image and push the results to the window's canvas
1382 if (offscreenSurface) {
Brian Osmanf6877092017-02-13 09:39:57 -05001383 fLastImage = offscreenSurface->makeImageSnapshot();
Brian Osmanf750fbc2017-02-08 10:47:28 -05001384
Robert Phillips9882dae2019-03-04 11:00:10 -05001385 SkCanvas* canvas = surface->getCanvas();
Brian Salomonbf52e3d2017-02-22 15:21:11 -05001386 SkPaint paint;
1387 paint.setBlendMode(SkBlendMode::kSrc);
Brian Osman805a7272018-05-02 15:40:20 -04001388 int prePerspectiveCount = canvas->save();
1389 if (kPerspective_Fake == fPerspectiveMode) {
1390 paint.setFilterQuality(kHigh_SkFilterQuality);
1391 canvas->clear(SK_ColorWHITE);
1392 canvas->concat(this->computePerspectiveMatrix());
1393 }
Brian Osman03115dc2018-11-26 13:55:19 -05001394 canvas->drawImage(fLastImage, 0, 0, &paint);
Brian Osman805a7272018-05-02 15:40:20 -04001395 canvas->restoreToCount(prePerspectiveCount);
liyuqian74959a12016-06-16 14:10:34 -07001396 }
Mike Reed376d8122019-03-14 11:39:02 -04001397
1398 if (fShowSlideDimensions) {
1399 SkRect r = SkRect::Make(fSlides[fCurrentSlide]->getDimensions());
1400 SkPaint paint;
1401 paint.setColor(0x40FFFF00);
1402 surface->getCanvas()->drawRect(r, paint);
1403 }
liyuqian6f163d22016-06-13 12:26:45 -07001404}
1405
Christopher Dalton443ec1b2017-02-24 13:22:53 -07001406void Viewer::onBackendCreated() {
Florin Malitaab99c342018-01-16 16:23:03 -05001407 this->setupCurrentSlide();
Christopher Dalton443ec1b2017-02-24 13:22:53 -07001408 fWindow->show();
Christopher Dalton443ec1b2017-02-24 13:22:53 -07001409}
Jim Van Verth6f449692017-02-14 15:16:46 -05001410
Robert Phillips9882dae2019-03-04 11:00:10 -05001411void Viewer::onPaint(SkSurface* surface) {
1412 this->drawSlide(surface);
jvanverthc265a922016-04-08 12:51:45 -07001413
Robert Phillips9882dae2019-03-04 11:00:10 -05001414 fCommands.drawHelp(surface->getCanvas());
liyuqian2edb0f42016-07-06 14:11:32 -07001415
Brian Osmand67e5182017-12-08 16:46:09 -05001416 this->drawImGui();
Chris Dalton89305752018-11-01 10:52:34 -06001417
1418 if (GrContext* ctx = fWindow->getGrContext()) {
1419 // Clean out cache items that haven't been used in more than 10 seconds.
1420 ctx->performDeferredCleanup(std::chrono::seconds(10));
1421 }
jvanverth3d6ed3a2016-04-07 11:09:51 -07001422}
1423
Ben Wagnera1915972018-08-09 15:06:19 -04001424void Viewer::onResize(int width, int height) {
Jim Van Verthb35c6552018-08-13 10:42:17 -04001425 if (fCurrentSlide >= 0) {
1426 fSlides[fCurrentSlide]->resize(width, height);
1427 }
Ben Wagnera1915972018-08-09 15:06:19 -04001428}
1429
Florin Malitacefc1b92018-02-19 21:43:47 -05001430SkPoint Viewer::mapEvent(float x, float y) {
1431 const auto m = this->computeMatrix();
1432 SkMatrix inv;
1433
1434 SkAssertResult(m.invert(&inv));
1435
1436 return inv.mapXY(x, y);
1437}
1438
Hal Canaryb1f411a2019-08-29 10:39:22 -04001439bool Viewer::onTouch(intptr_t owner, skui::InputState state, float x, float y) {
Brian Osmanb53f48c2017-06-07 10:00:30 -04001440 if (GestureDevice::kMouse == fGestureDevice) {
1441 return false;
1442 }
Florin Malitacefc1b92018-02-19 21:43:47 -05001443
1444 const auto slidePt = this->mapEvent(x, y);
Hal Canaryb1f411a2019-08-29 10:39:22 -04001445 if (fSlides[fCurrentSlide]->onMouse(slidePt.x(), slidePt.y(), state, skui::ModifierKey::kNone)) {
Florin Malitacefc1b92018-02-19 21:43:47 -05001446 fWindow->inval();
1447 return true;
1448 }
1449
liyuqiand3cdbca2016-05-17 12:44:20 -07001450 void* castedOwner = reinterpret_cast<void*>(owner);
1451 switch (state) {
Hal Canaryb1f411a2019-08-29 10:39:22 -04001452 case skui::InputState::kUp: {
liyuqiand3cdbca2016-05-17 12:44:20 -07001453 fGesture.touchEnd(castedOwner);
Jim Van Verth234e5a22018-07-23 13:46:01 -04001454#if defined(SK_BUILD_FOR_IOS)
1455 // TODO: move IOS swipe detection higher up into the platform code
1456 SkPoint dir;
1457 if (fGesture.isFling(&dir)) {
1458 // swiping left or right
1459 if (SkTAbs(dir.fX) > SkTAbs(dir.fY)) {
1460 if (dir.fX < 0) {
1461 this->setCurrentSlide(fCurrentSlide < fSlides.count() - 1 ?
1462 fCurrentSlide + 1 : 0);
1463 } else {
1464 this->setCurrentSlide(fCurrentSlide > 0 ?
1465 fCurrentSlide - 1 : fSlides.count() - 1);
1466 }
1467 }
1468 fGesture.reset();
1469 }
1470#endif
liyuqiand3cdbca2016-05-17 12:44:20 -07001471 break;
1472 }
Hal Canaryb1f411a2019-08-29 10:39:22 -04001473 case skui::InputState::kDown: {
Brian Osman42bb6ac2017-06-05 08:46:04 -04001474 fGesture.touchBegin(castedOwner, x, y);
liyuqiand3cdbca2016-05-17 12:44:20 -07001475 break;
1476 }
Hal Canaryb1f411a2019-08-29 10:39:22 -04001477 case skui::InputState::kMove: {
Brian Osman42bb6ac2017-06-05 08:46:04 -04001478 fGesture.touchMoved(castedOwner, x, y);
liyuqiand3cdbca2016-05-17 12:44:20 -07001479 break;
1480 }
Jim Van Verthd0cf5da2019-09-09 16:53:39 -04001481 default: {
1482 // kLeft and kRight are only for swipes
1483 SkASSERT(false);
1484 break;
1485 }
liyuqiand3cdbca2016-05-17 12:44:20 -07001486 }
Brian Osmanb53f48c2017-06-07 10:00:30 -04001487 fGestureDevice = fGesture.isBeingTouched() ? GestureDevice::kTouch : GestureDevice::kNone;
liyuqiand3cdbca2016-05-17 12:44:20 -07001488 fWindow->inval();
1489 return true;
1490}
1491
Hal Canaryb1f411a2019-08-29 10:39:22 -04001492bool Viewer::onMouse(int x, int y, skui::InputState state, skui::ModifierKey modifiers) {
Brian Osman16c81a12017-12-20 11:58:34 -05001493 if (GestureDevice::kTouch == fGestureDevice) {
1494 return false;
Brian Osman80fc07e2017-12-08 16:45:43 -05001495 }
Brian Osman16c81a12017-12-20 11:58:34 -05001496
Florin Malitacefc1b92018-02-19 21:43:47 -05001497 const auto slidePt = this->mapEvent(x, y);
1498 if (fSlides[fCurrentSlide]->onMouse(slidePt.x(), slidePt.y(), state, modifiers)) {
1499 fWindow->inval();
1500 return true;
Brian Osman16c81a12017-12-20 11:58:34 -05001501 }
1502
1503 switch (state) {
Hal Canaryb1f411a2019-08-29 10:39:22 -04001504 case skui::InputState::kUp: {
Brian Osman16c81a12017-12-20 11:58:34 -05001505 fGesture.touchEnd(nullptr);
1506 break;
1507 }
Hal Canaryb1f411a2019-08-29 10:39:22 -04001508 case skui::InputState::kDown: {
Brian Osman16c81a12017-12-20 11:58:34 -05001509 fGesture.touchBegin(nullptr, x, y);
1510 break;
1511 }
Hal Canaryb1f411a2019-08-29 10:39:22 -04001512 case skui::InputState::kMove: {
Brian Osman16c81a12017-12-20 11:58:34 -05001513 fGesture.touchMoved(nullptr, x, y);
1514 break;
1515 }
Jim Van Verthd0cf5da2019-09-09 16:53:39 -04001516 default: {
1517 SkASSERT(false); // shouldn't see kRight or kLeft here
1518 break;
1519 }
Brian Osman16c81a12017-12-20 11:58:34 -05001520 }
1521 fGestureDevice = fGesture.isBeingTouched() ? GestureDevice::kMouse : GestureDevice::kNone;
1522
Hal Canaryb1f411a2019-08-29 10:39:22 -04001523 if (state != skui::InputState::kMove || fGesture.isBeingTouched()) {
Brian Osman16c81a12017-12-20 11:58:34 -05001524 fWindow->inval();
1525 }
Jim Van Verthe7705782017-05-04 14:00:59 -04001526 return true;
1527}
1528
Jim Van Verthd0cf5da2019-09-09 16:53:39 -04001529bool Viewer::onFling(skui::InputState state) {
1530 if (skui::InputState::kRight == state) {
1531 this->setCurrentSlide(fCurrentSlide > 0 ? fCurrentSlide - 1 : fSlides.count() - 1);
1532 return true;
1533 } else if (skui::InputState::kLeft == state) {
1534 this->setCurrentSlide(fCurrentSlide < fSlides.count() - 1 ? fCurrentSlide + 1 : 0);
1535 return true;
1536 }
1537 return false;
1538}
1539
1540bool Viewer::onPinch(skui::InputState state, float scale, float x, float y) {
1541 switch (state) {
1542 case skui::InputState::kDown:
1543 fGesture.startZoom();
1544 return true;
1545 break;
1546 case skui::InputState::kMove:
1547 fGesture.updateZoom(scale, x, y, x, y);
1548 return true;
1549 break;
1550 case skui::InputState::kUp:
1551 fGesture.endZoom();
1552 return true;
1553 break;
1554 default:
1555 SkASSERT(false);
1556 break;
1557 }
1558
1559 return false;
1560}
1561
Brian Osmana109e392017-02-24 09:49:14 -05001562static void ImGui_Primaries(SkColorSpacePrimaries* primaries, SkPaint* gamutPaint) {
Brian Osman535c5e32019-02-09 16:32:58 -05001563 // The gamut image covers a (0.8 x 0.9) shaped region
1564 ImGui::DragCanvas dc(primaries, { 0.0f, 0.9f }, { 0.8f, 0.0f });
Brian Osmana109e392017-02-24 09:49:14 -05001565
1566 // Background image. Only draw a subset of the image, to avoid the regions less than zero.
1567 // Simplifes re-mapping math, clipping behavior, and increases resolution in the useful area.
1568 // Magic numbers are pixel locations of the origin and upper-right corner.
Brian Osman535c5e32019-02-09 16:32:58 -05001569 dc.fDrawList->AddImage(gamutPaint, dc.fPos,
1570 ImVec2(dc.fPos.x + dc.fSize.x, dc.fPos.y + dc.fSize.y),
1571 ImVec2(242, 61), ImVec2(1897, 1922));
Brian Osmana109e392017-02-24 09:49:14 -05001572
Brian Osman535c5e32019-02-09 16:32:58 -05001573 dc.dragPoint((SkPoint*)(&primaries->fRX), true, 0xFF000040);
1574 dc.dragPoint((SkPoint*)(&primaries->fGX), true, 0xFF004000);
1575 dc.dragPoint((SkPoint*)(&primaries->fBX), true, 0xFF400000);
1576 dc.dragPoint((SkPoint*)(&primaries->fWX), true);
1577 dc.fDrawList->AddPolyline(dc.fScreenPoints.begin(), 3, 0xFFFFFFFF, true, 1.5f);
Brian Osman9bb47cf2018-04-26 15:55:00 -04001578}
1579
Ben Wagner3627d2e2018-06-26 14:23:20 -04001580static bool ImGui_DragLocation(SkPoint* pt) {
Brian Osman535c5e32019-02-09 16:32:58 -05001581 ImGui::DragCanvas dc(pt);
1582 dc.fillColor(IM_COL32(0, 0, 0, 128));
1583 dc.dragPoint(pt);
1584 return dc.fDragging;
Ben Wagner3627d2e2018-06-26 14:23:20 -04001585}
1586
Brian Osman9bb47cf2018-04-26 15:55:00 -04001587static bool ImGui_DragQuad(SkPoint* pts) {
Brian Osman535c5e32019-02-09 16:32:58 -05001588 ImGui::DragCanvas dc(pts);
1589 dc.fillColor(IM_COL32(0, 0, 0, 128));
Brian Osman9bb47cf2018-04-26 15:55:00 -04001590
Brian Osman535c5e32019-02-09 16:32:58 -05001591 for (int i = 0; i < 4; ++i) {
1592 dc.dragPoint(pts + i);
1593 }
Brian Osman9bb47cf2018-04-26 15:55:00 -04001594
Brian Osman535c5e32019-02-09 16:32:58 -05001595 dc.fDrawList->AddLine(dc.fScreenPoints[0], dc.fScreenPoints[1], 0xFFFFFFFF);
1596 dc.fDrawList->AddLine(dc.fScreenPoints[1], dc.fScreenPoints[3], 0xFFFFFFFF);
1597 dc.fDrawList->AddLine(dc.fScreenPoints[3], dc.fScreenPoints[2], 0xFFFFFFFF);
1598 dc.fDrawList->AddLine(dc.fScreenPoints[2], dc.fScreenPoints[0], 0xFFFFFFFF);
Brian Osman9bb47cf2018-04-26 15:55:00 -04001599
Brian Osman535c5e32019-02-09 16:32:58 -05001600 return dc.fDragging;
Brian Osmana109e392017-02-24 09:49:14 -05001601}
1602
Brian Osmand67e5182017-12-08 16:46:09 -05001603void Viewer::drawImGui() {
Brian Osman79086b92017-02-10 13:36:16 -05001604 // Support drawing the ImGui demo window. Superfluous, but gives a good idea of what's possible
1605 if (fShowImGuiTestWindow) {
Brian Osman7197e052018-06-29 14:30:48 -04001606 ImGui::ShowDemoWindow(&fShowImGuiTestWindow);
Brian Osman79086b92017-02-10 13:36:16 -05001607 }
1608
1609 if (fShowImGuiDebugWindow) {
Brian Osmana109e392017-02-24 09:49:14 -05001610 // We have some dynamic content that sizes to fill available size. If the scroll bar isn't
1611 // always visible, we can end up in a layout feedback loop.
Brian Osman7197e052018-06-29 14:30:48 -04001612 ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
Brian Salomon99a33902017-03-07 15:16:34 -05001613 DisplayParams params = fWindow->getRequestedDisplayParams();
1614 bool paramsChanged = false;
Brian Osman0b8bb882019-04-12 11:47:19 -04001615 const GrContext* ctx = fWindow->getGrContext();
1616
Brian Osmana109e392017-02-24 09:49:14 -05001617 if (ImGui::Begin("Tools", &fShowImGuiDebugWindow,
1618 ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
Brian Osman621491e2017-02-28 15:45:01 -05001619 if (ImGui::CollapsingHeader("Backend")) {
1620 int newBackend = static_cast<int>(fBackendType);
1621 ImGui::RadioButton("Raster", &newBackend, sk_app::Window::kRaster_BackendType);
1622 ImGui::SameLine();
1623 ImGui::RadioButton("OpenGL", &newBackend, sk_app::Window::kNativeGL_BackendType);
Brian Salomon194db172017-08-17 14:37:06 -04001624#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
1625 ImGui::SameLine();
1626 ImGui::RadioButton("ANGLE", &newBackend, sk_app::Window::kANGLE_BackendType);
1627#endif
Stephen Whitea800ec92019-08-02 15:04:52 -04001628#if defined(SK_DAWN)
1629 ImGui::SameLine();
1630 ImGui::RadioButton("Dawn", &newBackend, sk_app::Window::kDawn_BackendType);
1631#endif
Brian Osman621491e2017-02-28 15:45:01 -05001632#if defined(SK_VULKAN)
1633 ImGui::SameLine();
1634 ImGui::RadioButton("Vulkan", &newBackend, sk_app::Window::kVulkan_BackendType);
1635#endif
Jim Van Verthe58d5322019-09-03 09:42:57 -04001636#if defined(SK_METAL)
Jim Van Verthbe39f712019-02-08 15:36:14 -05001637 ImGui::SameLine();
1638 ImGui::RadioButton("Metal", &newBackend, sk_app::Window::kMetal_BackendType);
1639#endif
Brian Osman621491e2017-02-28 15:45:01 -05001640 if (newBackend != fBackendType) {
1641 fDeferredActions.push_back([=]() {
1642 this->setBackend(static_cast<sk_app::Window::BackendType>(newBackend));
1643 });
1644 }
Brian Osman8a9de3d2017-03-01 14:59:05 -05001645
Jim Van Verthfbdc0802017-05-02 16:15:53 -04001646 bool* wire = &params.fGrContextOptions.fWireframeMode;
1647 if (ctx && ImGui::Checkbox("Wireframe Mode", wire)) {
1648 paramsChanged = true;
1649 }
Brian Salomon99a33902017-03-07 15:16:34 -05001650
Brian Osman28b12522017-03-08 17:10:24 -05001651 if (ctx) {
1652 int sampleCount = fWindow->sampleCount();
1653 ImGui::Text("MSAA: "); ImGui::SameLine();
Brian Salomonbdecacf2018-02-02 20:32:49 -05001654 ImGui::RadioButton("1", &sampleCount, 1); ImGui::SameLine();
Brian Osman28b12522017-03-08 17:10:24 -05001655 ImGui::RadioButton("4", &sampleCount, 4); ImGui::SameLine();
1656 ImGui::RadioButton("8", &sampleCount, 8); ImGui::SameLine();
1657 ImGui::RadioButton("16", &sampleCount, 16);
1658
1659 if (sampleCount != params.fMSAASampleCount) {
1660 params.fMSAASampleCount = sampleCount;
1661 paramsChanged = true;
1662 }
1663 }
1664
Ben Wagner37c54032018-04-13 14:30:23 -04001665 int pixelGeometryIdx = 0;
1666 if (fPixelGeometryOverrides) {
1667 pixelGeometryIdx = params.fSurfaceProps.pixelGeometry() + 1;
1668 }
1669 if (ImGui::Combo("Pixel Geometry", &pixelGeometryIdx,
1670 "Default\0Flat\0RGB\0BGR\0RGBV\0BGRV\0\0"))
1671 {
1672 uint32_t flags = params.fSurfaceProps.flags();
1673 if (pixelGeometryIdx == 0) {
1674 fPixelGeometryOverrides = false;
1675 params.fSurfaceProps = SkSurfaceProps(flags, SkSurfaceProps::kLegacyFontHost_InitType);
1676 } else {
1677 fPixelGeometryOverrides = true;
1678 SkPixelGeometry pixelGeometry = SkTo<SkPixelGeometry>(pixelGeometryIdx - 1);
1679 params.fSurfaceProps = SkSurfaceProps(flags, pixelGeometry);
1680 }
1681 paramsChanged = true;
1682 }
1683
1684 bool useDFT = params.fSurfaceProps.isUseDeviceIndependentFonts();
1685 if (ImGui::Checkbox("DFT", &useDFT)) {
1686 uint32_t flags = params.fSurfaceProps.flags();
1687 if (useDFT) {
1688 flags |= SkSurfaceProps::kUseDeviceIndependentFonts_Flag;
1689 } else {
1690 flags &= ~SkSurfaceProps::kUseDeviceIndependentFonts_Flag;
1691 }
1692 SkPixelGeometry pixelGeometry = params.fSurfaceProps.pixelGeometry();
1693 params.fSurfaceProps = SkSurfaceProps(flags, pixelGeometry);
1694 paramsChanged = true;
1695 }
1696
Brian Osman8a9de3d2017-03-01 14:59:05 -05001697 if (ImGui::TreeNode("Path Renderers")) {
Brian Osman8a9de3d2017-03-01 14:59:05 -05001698 GpuPathRenderers prevPr = params.fGrContextOptions.fGpuPathRenderers;
Brian Osman8a9de3d2017-03-01 14:59:05 -05001699 auto prButton = [&](GpuPathRenderers x) {
1700 if (ImGui::RadioButton(gPathRendererNames[x].c_str(), prevPr == x)) {
Brian Salomon99a33902017-03-07 15:16:34 -05001701 if (x != params.fGrContextOptions.fGpuPathRenderers) {
1702 params.fGrContextOptions.fGpuPathRenderers = x;
1703 paramsChanged = true;
1704 }
Brian Osman8a9de3d2017-03-01 14:59:05 -05001705 }
1706 };
1707
1708 if (!ctx) {
1709 ImGui::RadioButton("Software", true);
Brian Osman8a9de3d2017-03-01 14:59:05 -05001710 } else {
Chris Dalton37ae4b02019-12-28 14:51:11 -07001711 const auto* caps = ctx->priv().caps();
1712 prButton(GpuPathRenderers::kDefault);
1713 if (fWindow->sampleCount() > 1 || caps->mixedSamplesSupport()) {
1714 if (caps->shaderCaps()->pathRenderingSupport()) {
1715 prButton(GpuPathRenderers::kStencilAndCover);
1716 }
Chris Dalton1a325d22017-07-14 15:17:41 -06001717 }
Chris Dalton37ae4b02019-12-28 14:51:11 -07001718 if (1 == fWindow->sampleCount()) {
1719 if (GrCoverageCountingPathRenderer::IsSupported(*caps)) {
1720 prButton(GpuPathRenderers::kCoverageCounting);
1721 }
1722 prButton(GpuPathRenderers::kSmall);
1723 }
Brian Osman8a9de3d2017-03-01 14:59:05 -05001724 prButton(GpuPathRenderers::kTessellating);
1725 prButton(GpuPathRenderers::kNone);
1726 }
Brian Osman8a9de3d2017-03-01 14:59:05 -05001727 ImGui::TreePop();
1728 }
Brian Osman621491e2017-02-28 15:45:01 -05001729 }
1730
Ben Wagner964571d2019-03-08 12:35:06 -05001731 if (ImGui::CollapsingHeader("Tiling")) {
1732 ImGui::Checkbox("Enable", &fTiled);
1733 ImGui::Checkbox("Draw Boundaries", &fDrawTileBoundaries);
1734 ImGui::SliderFloat("Horizontal", &fTileScale.fWidth, 0.1f, 1.0f);
1735 ImGui::SliderFloat("Vertical", &fTileScale.fHeight, 0.1f, 1.0f);
1736 }
1737
Ben Wagnerd02a74d2018-04-23 12:55:06 -04001738 if (ImGui::CollapsingHeader("Transform")) {
1739 float zoom = fZoomLevel;
1740 if (ImGui::SliderFloat("Zoom", &zoom, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL)) {
1741 fZoomLevel = zoom;
1742 this->preTouchMatrixChanged();
1743 paramsChanged = true;
1744 }
1745 float deg = fRotation;
Ben Wagnercb139352018-05-04 10:33:04 -04001746 if (ImGui::SliderFloat("Rotate", &deg, -30, 360, "%.3f deg")) {
Ben Wagnerd02a74d2018-04-23 12:55:06 -04001747 fRotation = deg;
1748 this->preTouchMatrixChanged();
1749 paramsChanged = true;
1750 }
Ben Wagner3627d2e2018-06-26 14:23:20 -04001751 if (ImGui::CollapsingHeader("Subpixel offset", ImGuiTreeNodeFlags_NoTreePushOnOpen)) {
1752 if (ImGui_DragLocation(&fOffset)) {
1753 this->preTouchMatrixChanged();
1754 paramsChanged = true;
1755 }
Ben Wagner897dfa22018-08-09 15:18:46 -04001756 } else if (fOffset != SkVector{0.5f, 0.5f}) {
1757 this->preTouchMatrixChanged();
1758 paramsChanged = true;
1759 fOffset = {0.5f, 0.5f};
Ben Wagner3627d2e2018-06-26 14:23:20 -04001760 }
Brian Osman805a7272018-05-02 15:40:20 -04001761 int perspectiveMode = static_cast<int>(fPerspectiveMode);
1762 if (ImGui::Combo("Perspective", &perspectiveMode, "Off\0Real\0Fake\0\0")) {
1763 fPerspectiveMode = static_cast<PerspectiveMode>(perspectiveMode);
Brian Osman9bb47cf2018-04-26 15:55:00 -04001764 this->preTouchMatrixChanged();
Ben Wagner3627d2e2018-06-26 14:23:20 -04001765 paramsChanged = true;
Brian Osman9bb47cf2018-04-26 15:55:00 -04001766 }
Ben Wagner3627d2e2018-06-26 14:23:20 -04001767 if (perspectiveMode != kPerspective_Off && ImGui_DragQuad(fPerspectivePoints)) {
Brian Osman9bb47cf2018-04-26 15:55:00 -04001768 this->preTouchMatrixChanged();
Ben Wagner3627d2e2018-06-26 14:23:20 -04001769 paramsChanged = true;
Brian Osman9bb47cf2018-04-26 15:55:00 -04001770 }
Ben Wagnerd02a74d2018-04-23 12:55:06 -04001771 }
1772
Ben Wagnera580fb32018-04-17 11:16:32 -04001773 if (ImGui::CollapsingHeader("Paint")) {
Ben Wagnera580fb32018-04-17 11:16:32 -04001774 int aliasIdx = 0;
Ben Wagner9613e452019-01-23 10:34:59 -05001775 if (fPaintOverrides.fAntiAlias) {
1776 aliasIdx = SkTo<int>(fPaintOverrides.fAntiAliasState) + 1;
Ben Wagnera580fb32018-04-17 11:16:32 -04001777 }
1778 if (ImGui::Combo("Anti-Alias", &aliasIdx,
Mike Kleine5acd752019-03-22 09:57:16 -05001779 "Default\0Alias\0Normal\0AnalyticAAEnabled\0AnalyticAAForced\0\0"))
Ben Wagnera580fb32018-04-17 11:16:32 -04001780 {
1781 gSkUseAnalyticAA = fPaintOverrides.fOriginalSkUseAnalyticAA;
1782 gSkForceAnalyticAA = fPaintOverrides.fOriginalSkForceAnalyticAA;
Ben Wagnera580fb32018-04-17 11:16:32 -04001783 if (aliasIdx == 0) {
Ben Wagner9613e452019-01-23 10:34:59 -05001784 fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Alias;
1785 fPaintOverrides.fAntiAlias = false;
Ben Wagnera580fb32018-04-17 11:16:32 -04001786 } else {
Ben Wagner9613e452019-01-23 10:34:59 -05001787 fPaintOverrides.fAntiAlias = true;
1788 fPaintOverrides.fAntiAliasState = SkTo<SkPaintFields::AntiAliasState>(aliasIdx-1);
Ben Wagnera580fb32018-04-17 11:16:32 -04001789 fPaint.setAntiAlias(aliasIdx > 1);
Ben Wagner9613e452019-01-23 10:34:59 -05001790 switch (fPaintOverrides.fAntiAliasState) {
Ben Wagnera580fb32018-04-17 11:16:32 -04001791 case SkPaintFields::AntiAliasState::Alias:
1792 break;
1793 case SkPaintFields::AntiAliasState::Normal:
1794 break;
1795 case SkPaintFields::AntiAliasState::AnalyticAAEnabled:
1796 gSkUseAnalyticAA = true;
1797 gSkForceAnalyticAA = false;
Ben Wagnera580fb32018-04-17 11:16:32 -04001798 break;
1799 case SkPaintFields::AntiAliasState::AnalyticAAForced:
1800 gSkUseAnalyticAA = gSkForceAnalyticAA = true;
Ben Wagnera580fb32018-04-17 11:16:32 -04001801 break;
1802 }
1803 }
1804 paramsChanged = true;
1805 }
1806
Ben Wagner99a78dc2018-05-09 18:23:51 -04001807 auto paintFlag = [this, &paramsChanged](const char* label, const char* items,
Ben Wagner9613e452019-01-23 10:34:59 -05001808 bool SkPaintFields::* flag,
Ben Wagner99a78dc2018-05-09 18:23:51 -04001809 bool (SkPaint::* isFlag)() const,
1810 void (SkPaint::* setFlag)(bool) )
Ben Wagnera580fb32018-04-17 11:16:32 -04001811 {
Ben Wagner99a78dc2018-05-09 18:23:51 -04001812 int itemIndex = 0;
Ben Wagner9613e452019-01-23 10:34:59 -05001813 if (fPaintOverrides.*flag) {
Ben Wagner99a78dc2018-05-09 18:23:51 -04001814 itemIndex = (fPaint.*isFlag)() ? 2 : 1;
Ben Wagnera580fb32018-04-17 11:16:32 -04001815 }
Ben Wagner99a78dc2018-05-09 18:23:51 -04001816 if (ImGui::Combo(label, &itemIndex, items)) {
1817 if (itemIndex == 0) {
Ben Wagner9613e452019-01-23 10:34:59 -05001818 fPaintOverrides.*flag = false;
Ben Wagner99a78dc2018-05-09 18:23:51 -04001819 } else {
Ben Wagner9613e452019-01-23 10:34:59 -05001820 fPaintOverrides.*flag = true;
Ben Wagner99a78dc2018-05-09 18:23:51 -04001821 (fPaint.*setFlag)(itemIndex == 2);
1822 }
1823 paramsChanged = true;
1824 }
1825 };
Ben Wagnera580fb32018-04-17 11:16:32 -04001826
Ben Wagner99a78dc2018-05-09 18:23:51 -04001827 paintFlag("Dither",
1828 "Default\0No Dither\0Dither\0\0",
Ben Wagner9613e452019-01-23 10:34:59 -05001829 &SkPaintFields::fDither,
Ben Wagner99a78dc2018-05-09 18:23:51 -04001830 &SkPaint::isDither, &SkPaint::setDither);
Ben Wagnerd10a78f2019-03-07 13:14:26 -05001831
1832 int filterQualityIdx = 0;
1833 if (fPaintOverrides.fFilterQuality) {
1834 filterQualityIdx = SkTo<int>(fPaint.getFilterQuality()) + 1;
1835 }
1836 if (ImGui::Combo("Filter Quality", &filterQualityIdx,
1837 "Default\0None\0Low\0Medium\0High\0\0"))
1838 {
1839 if (filterQualityIdx == 0) {
1840 fPaintOverrides.fFilterQuality = false;
1841 fPaint.setFilterQuality(kNone_SkFilterQuality);
1842 } else {
1843 fPaint.setFilterQuality(SkTo<SkFilterQuality>(filterQualityIdx - 1));
1844 fPaintOverrides.fFilterQuality = true;
1845 }
1846 paramsChanged = true;
1847 }
Ben Wagner9613e452019-01-23 10:34:59 -05001848 }
Hal Canary02738a82019-01-21 18:51:32 +00001849
Ben Wagner9613e452019-01-23 10:34:59 -05001850 if (ImGui::CollapsingHeader("Font")) {
1851 int hintingIdx = 0;
1852 if (fFontOverrides.fHinting) {
1853 hintingIdx = SkTo<int>(fFont.getHinting()) + 1;
1854 }
1855 if (ImGui::Combo("Hinting", &hintingIdx,
1856 "Default\0None\0Slight\0Normal\0Full\0\0"))
1857 {
1858 if (hintingIdx == 0) {
1859 fFontOverrides.fHinting = false;
Ben Wagner5785e4a2019-05-07 16:50:29 -04001860 fFont.setHinting(SkFontHinting::kNone);
Ben Wagner9613e452019-01-23 10:34:59 -05001861 } else {
1862 fFont.setHinting(SkTo<SkFontHinting>(hintingIdx - 1));
1863 fFontOverrides.fHinting = true;
1864 }
1865 paramsChanged = true;
1866 }
Hal Canary02738a82019-01-21 18:51:32 +00001867
Ben Wagner9613e452019-01-23 10:34:59 -05001868 auto fontFlag = [this, &paramsChanged](const char* label, const char* items,
1869 bool SkFontFields::* flag,
1870 bool (SkFont::* isFlag)() const,
1871 void (SkFont::* setFlag)(bool) )
1872 {
1873 int itemIndex = 0;
1874 if (fFontOverrides.*flag) {
1875 itemIndex = (fFont.*isFlag)() ? 2 : 1;
1876 }
1877 if (ImGui::Combo(label, &itemIndex, items)) {
1878 if (itemIndex == 0) {
1879 fFontOverrides.*flag = false;
1880 } else {
1881 fFontOverrides.*flag = true;
1882 (fFont.*setFlag)(itemIndex == 2);
1883 }
1884 paramsChanged = true;
1885 }
1886 };
Hal Canary02738a82019-01-21 18:51:32 +00001887
Ben Wagner9613e452019-01-23 10:34:59 -05001888 fontFlag("Fake Bold Glyphs",
1889 "Default\0No Fake Bold\0Fake Bold\0\0",
1890 &SkFontFields::fEmbolden,
1891 &SkFont::isEmbolden, &SkFont::setEmbolden);
Hal Canary02738a82019-01-21 18:51:32 +00001892
Ben Wagnerc17de1d2019-08-26 16:59:09 -04001893 fontFlag("Baseline Snapping",
1894 "Default\0No Baseline Snapping\0Baseline Snapping\0\0",
1895 &SkFontFields::fBaselineSnap,
1896 &SkFont::isBaselineSnap, &SkFont::setBaselineSnap);
1897
Ben Wagner9613e452019-01-23 10:34:59 -05001898 fontFlag("Linear Text",
1899 "Default\0No Linear Text\0Linear Text\0\0",
1900 &SkFontFields::fLinearMetrics,
1901 &SkFont::isLinearMetrics, &SkFont::setLinearMetrics);
Hal Canary02738a82019-01-21 18:51:32 +00001902
Ben Wagner9613e452019-01-23 10:34:59 -05001903 fontFlag("Subpixel Position Glyphs",
1904 "Default\0Pixel Text\0Subpixel Text\0\0",
1905 &SkFontFields::fSubpixel,
1906 &SkFont::isSubpixel, &SkFont::setSubpixel);
1907
1908 fontFlag("Embedded Bitmap Text",
1909 "Default\0No Embedded Bitmaps\0Embedded Bitmaps\0\0",
1910 &SkFontFields::fEmbeddedBitmaps,
1911 &SkFont::isEmbeddedBitmaps, &SkFont::setEmbeddedBitmaps);
1912
1913 fontFlag("Force Auto-Hinting",
1914 "Default\0No Force Auto-Hinting\0Force Auto-Hinting\0\0",
1915 &SkFontFields::fForceAutoHinting,
1916 &SkFont::isForceAutoHinting, &SkFont::setForceAutoHinting);
1917
1918 int edgingIdx = 0;
1919 if (fFontOverrides.fEdging) {
1920 edgingIdx = SkTo<int>(fFont.getEdging()) + 1;
1921 }
1922 if (ImGui::Combo("Edging", &edgingIdx,
1923 "Default\0Alias\0Antialias\0Subpixel Antialias\0\0"))
1924 {
1925 if (edgingIdx == 0) {
1926 fFontOverrides.fEdging = false;
1927 fFont.setEdging(SkFont::Edging::kAlias);
1928 } else {
1929 fFont.setEdging(SkTo<SkFont::Edging>(edgingIdx-1));
1930 fFontOverrides.fEdging = true;
1931 }
1932 paramsChanged = true;
1933 }
1934
Ben Wagner15a8d572019-03-21 13:35:44 -04001935 ImGui::Checkbox("Override Size", &fFontOverrides.fSize);
1936 if (fFontOverrides.fSize) {
1937 ImGui::DragFloat2("TextRange", fFontOverrides.fSizeRange,
Ben Wagnerd2ae4df2018-06-07 17:54:07 -04001938 0.001f, -10.0f, 300.0f, "%.6f", 2.0f);
Mike Reed3ae47332019-01-04 10:11:46 -05001939 float textSize = fFont.getSize();
Ben Wagnerd2ae4df2018-06-07 17:54:07 -04001940 if (ImGui::DragFloat("TextSize", &textSize, 0.001f,
Ben Wagner15a8d572019-03-21 13:35:44 -04001941 fFontOverrides.fSizeRange[0],
1942 fFontOverrides.fSizeRange[1],
Ben Wagnerd2ae4df2018-06-07 17:54:07 -04001943 "%.6f", 2.0f))
1944 {
Mike Reed3ae47332019-01-04 10:11:46 -05001945 fFont.setSize(textSize);
Ben Wagner15a8d572019-03-21 13:35:44 -04001946 paramsChanged = true;
1947 }
1948 }
1949
1950 ImGui::Checkbox("Override ScaleX", &fFontOverrides.fScaleX);
1951 if (fFontOverrides.fScaleX) {
1952 float scaleX = fFont.getScaleX();
1953 if (ImGui::SliderFloat("ScaleX", &scaleX, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL)) {
1954 fFont.setScaleX(scaleX);
1955 paramsChanged = true;
1956 }
1957 }
1958
1959 ImGui::Checkbox("Override SkewX", &fFontOverrides.fSkewX);
1960 if (fFontOverrides.fSkewX) {
1961 float skewX = fFont.getSkewX();
1962 if (ImGui::SliderFloat("SkewX", &skewX, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL)) {
1963 fFont.setSkewX(skewX);
Ben Wagnerd2ae4df2018-06-07 17:54:07 -04001964 paramsChanged = true;
1965 }
1966 }
Ben Wagnera580fb32018-04-17 11:16:32 -04001967 }
1968
Mike Reed81f60ec2018-05-15 10:09:52 -04001969 {
1970 SkMetaData controls;
1971 if (fSlides[fCurrentSlide]->onGetControls(&controls)) {
1972 if (ImGui::CollapsingHeader("Current Slide")) {
1973 SkMetaData::Iter iter(controls);
1974 const char* name;
1975 SkMetaData::Type type;
1976 int count;
Brian Osman61fb4bb2018-08-03 11:14:02 -04001977 while ((name = iter.next(&type, &count)) != nullptr) {
Mike Reed81f60ec2018-05-15 10:09:52 -04001978 if (type == SkMetaData::kScalar_Type) {
1979 float val[3];
1980 SkASSERT(count == 3);
1981 controls.findScalars(name, &count, val);
1982 if (ImGui::SliderFloat(name, &val[0], val[1], val[2])) {
1983 controls.setScalars(name, 3, val);
Mike Reed81f60ec2018-05-15 10:09:52 -04001984 }
Ben Wagner110c7032019-03-22 17:03:59 -04001985 } else if (type == SkMetaData::kBool_Type) {
1986 bool val;
1987 SkASSERT(count == 1);
1988 controls.findBool(name, &val);
1989 if (ImGui::Checkbox(name, &val)) {
1990 controls.setBool(name, val);
1991 }
Mike Reed81f60ec2018-05-15 10:09:52 -04001992 }
1993 }
Brian Osman61fb4bb2018-08-03 11:14:02 -04001994 fSlides[fCurrentSlide]->onSetControls(controls);
Mike Reed81f60ec2018-05-15 10:09:52 -04001995 }
1996 }
1997 }
1998
Ben Wagner7a3c6742018-04-23 10:01:07 -04001999 if (fShowSlidePicker) {
2000 ImGui::SetNextTreeNodeOpen(true);
2001 }
Brian Osman79086b92017-02-10 13:36:16 -05002002 if (ImGui::CollapsingHeader("Slide")) {
2003 static ImGuiTextFilter filter;
Brian Osmanf479e422017-11-08 13:11:36 -05002004 static ImVector<const char*> filteredSlideNames;
2005 static ImVector<int> filteredSlideIndices;
2006
Brian Osmanfce09c52017-11-14 15:32:20 -05002007 if (fShowSlidePicker) {
2008 ImGui::SetKeyboardFocusHere();
2009 fShowSlidePicker = false;
2010 }
2011
Brian Osman79086b92017-02-10 13:36:16 -05002012 filter.Draw();
Brian Osmanf479e422017-11-08 13:11:36 -05002013 filteredSlideNames.clear();
2014 filteredSlideIndices.clear();
2015 int filteredIndex = 0;
2016 for (int i = 0; i < fSlides.count(); ++i) {
2017 const char* slideName = fSlides[i]->getName().c_str();
2018 if (filter.PassFilter(slideName) || i == fCurrentSlide) {
2019 if (i == fCurrentSlide) {
2020 filteredIndex = filteredSlideIndices.size();
Brian Osman79086b92017-02-10 13:36:16 -05002021 }
Brian Osmanf479e422017-11-08 13:11:36 -05002022 filteredSlideNames.push_back(slideName);
2023 filteredSlideIndices.push_back(i);
Brian Osman79086b92017-02-10 13:36:16 -05002024 }
Brian Osman79086b92017-02-10 13:36:16 -05002025 }
Brian Osmanf479e422017-11-08 13:11:36 -05002026
Brian Osmanf479e422017-11-08 13:11:36 -05002027 if (ImGui::ListBox("", &filteredIndex, filteredSlideNames.begin(),
2028 filteredSlideNames.size(), 20)) {
Florin Malitaab99c342018-01-16 16:23:03 -05002029 this->setCurrentSlide(filteredSlideIndices[filteredIndex]);
Brian Osman79086b92017-02-10 13:36:16 -05002030 }
2031 }
Brian Osmana109e392017-02-24 09:49:14 -05002032
2033 if (ImGui::CollapsingHeader("Color Mode")) {
Brian Osman92004802017-03-06 11:47:26 -05002034 ColorMode newMode = fColorMode;
2035 auto cmButton = [&](ColorMode mode, const char* label) {
2036 if (ImGui::RadioButton(label, mode == fColorMode)) {
2037 newMode = mode;
2038 }
2039 };
2040
2041 cmButton(ColorMode::kLegacy, "Legacy 8888");
Brian Osman03115dc2018-11-26 13:55:19 -05002042 cmButton(ColorMode::kColorManaged8888, "Color Managed 8888");
2043 cmButton(ColorMode::kColorManagedF16, "Color Managed F16");
Brian Salomon8391bac2019-09-18 11:22:44 -04002044 cmButton(ColorMode::kColorManagedF16Norm, "Color Managed F16 Norm");
Brian Osman92004802017-03-06 11:47:26 -05002045
2046 if (newMode != fColorMode) {
Brian Osman03115dc2018-11-26 13:55:19 -05002047 this->setColorMode(newMode);
Brian Osmana109e392017-02-24 09:49:14 -05002048 }
2049
2050 // Pick from common gamuts:
2051 int primariesIdx = 4; // Default: Custom
2052 for (size_t i = 0; i < SK_ARRAY_COUNT(gNamedPrimaries); ++i) {
2053 if (primaries_equal(*gNamedPrimaries[i].fPrimaries, fColorSpacePrimaries)) {
2054 primariesIdx = i;
2055 break;
2056 }
2057 }
2058
Brian Osman03115dc2018-11-26 13:55:19 -05002059 // Let user adjust the gamma
Brian Osman82ebe042019-01-04 17:03:00 -05002060 ImGui::SliderFloat("Gamma", &fColorSpaceTransferFn.g, 0.5f, 3.5f);
Brian Osmanfdab5762017-11-09 10:27:55 -05002061
Brian Osmana109e392017-02-24 09:49:14 -05002062 if (ImGui::Combo("Primaries", &primariesIdx,
2063 "sRGB\0AdobeRGB\0P3\0Rec. 2020\0Custom\0\0")) {
2064 if (primariesIdx >= 0 && primariesIdx <= 3) {
2065 fColorSpacePrimaries = *gNamedPrimaries[primariesIdx].fPrimaries;
2066 }
2067 }
2068
2069 // Allow direct editing of gamut
2070 ImGui_Primaries(&fColorSpacePrimaries, &fImGuiGamutPaint);
2071 }
Brian Osman207d4102019-01-10 09:40:58 -05002072
2073 if (ImGui::CollapsingHeader("Animation")) {
Hal Canary41248072019-07-11 16:32:53 -04002074 bool isPaused = AnimTimer::kPaused_State == fAnimTimer.state();
Brian Osman207d4102019-01-10 09:40:58 -05002075 if (ImGui::Checkbox("Pause", &isPaused)) {
2076 fAnimTimer.togglePauseResume();
2077 }
Brian Osman707d2022019-01-10 11:27:34 -05002078
2079 float speed = fAnimTimer.getSpeed();
2080 if (ImGui::DragFloat("Speed", &speed, 0.1f)) {
2081 fAnimTimer.setSpeed(speed);
2082 }
Brian Osman207d4102019-01-10 09:40:58 -05002083 }
Brian Osman0b8bb882019-04-12 11:47:19 -04002084
Brian Osmanfd7657c2019-04-25 11:34:07 -04002085 bool backendIsGL = Window::kNativeGL_BackendType == fBackendType
2086#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
2087 || Window::kANGLE_BackendType == fBackendType
2088#endif
2089 ;
2090
2091 // HACK: If we get here when SKSL caching isn't enabled, and we're on a backend other
2092 // than GL, we need to force it on. Just do that on the first frame after the backend
2093 // switch, then resume normal operation.
Brian Osmana66081d2019-09-03 14:59:26 -04002094 if (!backendIsGL &&
2095 params.fGrContextOptions.fShaderCacheStrategy !=
2096 GrContextOptions::ShaderCacheStrategy::kSkSL) {
2097 params.fGrContextOptions.fShaderCacheStrategy =
2098 GrContextOptions::ShaderCacheStrategy::kSkSL;
Brian Osmanfd7657c2019-04-25 11:34:07 -04002099 paramsChanged = true;
2100 fPersistentCache.reset();
2101 } else if (ImGui::CollapsingHeader("Shaders")) {
Brian Osman0b8bb882019-04-12 11:47:19 -04002102 // To re-load shaders from the currently active programs, we flush all caches on one
2103 // frame, then set a flag to poll the cache on the next frame.
2104 static bool gLoadPending = false;
2105 if (gLoadPending) {
2106 auto collectShaders = [this](sk_sp<const SkData> key, sk_sp<SkData> data,
2107 int hitCount) {
2108 CachedGLSL& entry(fCachedGLSL.push_back());
2109 entry.fKey = key;
2110 SkMD5 hash;
2111 hash.write(key->bytes(), key->size());
2112 SkMD5::Digest digest = hash.finish();
2113 for (int i = 0; i < 16; ++i) {
2114 entry.fKeyString.appendf("%02x", digest.data[i]);
2115 }
2116
Brian Osmana66081d2019-09-03 14:59:26 -04002117 SkReader32 reader(data->data(), data->size());
2118 entry.fShaderType = reader.readU32();
2119 GrPersistentCacheUtils::UnpackCachedShaders(&reader, entry.fShader,
2120 entry.fInputs,
2121 kGrShaderTypeCount);
Brian Osman0b8bb882019-04-12 11:47:19 -04002122 };
2123 fCachedGLSL.reset();
2124 fPersistentCache.foreach(collectShaders);
2125 gLoadPending = false;
2126 }
2127
2128 // Defer actually doing the load/save logic so that we can trigger a save when we
2129 // start or finish hovering on a tree node in the list below:
2130 bool doLoad = ImGui::Button("Load"); ImGui::SameLine();
Brian Osmanfd7657c2019-04-25 11:34:07 -04002131 bool doSave = ImGui::Button("Save");
2132 if (backendIsGL) {
2133 ImGui::SameLine();
Brian Osmana66081d2019-09-03 14:59:26 -04002134 bool sksl = params.fGrContextOptions.fShaderCacheStrategy ==
2135 GrContextOptions::ShaderCacheStrategy::kSkSL;
2136 if (ImGui::Checkbox("SkSL", &sksl)) {
2137 params.fGrContextOptions.fShaderCacheStrategy = sksl
2138 ? GrContextOptions::ShaderCacheStrategy::kSkSL
2139 : GrContextOptions::ShaderCacheStrategy::kBackendSource;
Brian Osmanfd7657c2019-04-25 11:34:07 -04002140 paramsChanged = true;
2141 doLoad = true;
2142 fDeferredActions.push_back([=]() { fPersistentCache.reset(); });
2143 }
Brian Osmancbc33b82019-04-19 14:16:19 -04002144 }
Brian Osman0b8bb882019-04-12 11:47:19 -04002145
2146 ImGui::BeginChild("##ScrollingRegion");
2147 for (auto& entry : fCachedGLSL) {
2148 bool inTreeNode = ImGui::TreeNode(entry.fKeyString.c_str());
2149 bool hovered = ImGui::IsItemHovered();
2150 if (hovered != entry.fHovered) {
2151 // Force a save to patch the highlight shader in/out
2152 entry.fHovered = hovered;
2153 doSave = true;
2154 }
2155 if (inTreeNode) {
2156 // Full width, and a reasonable amount of space for each shader.
2157 ImVec2 boxSize(-1.0f, ImGui::GetTextLineHeight() * 20.0f);
2158 ImGui::InputTextMultiline("##VP", &entry.fShader[kVertex_GrShaderType],
2159 boxSize);
2160 ImGui::InputTextMultiline("##FP", &entry.fShader[kFragment_GrShaderType],
2161 boxSize);
2162 ImGui::TreePop();
2163 }
2164 }
2165 ImGui::EndChild();
2166
2167 if (doLoad) {
2168 fPersistentCache.reset();
2169 fWindow->getGrContext()->priv().getGpu()->resetShaderCacheForTesting();
2170 gLoadPending = true;
2171 }
2172 if (doSave) {
2173 // The hovered item (if any) gets a special shader to make it identifiable
Brian Osman5bee3902019-05-07 09:55:45 -04002174 auto shaderCaps = ctx->priv().caps()->shaderCaps();
Brian Osmana66081d2019-09-03 14:59:26 -04002175 bool sksl = params.fGrContextOptions.fShaderCacheStrategy ==
2176 GrContextOptions::ShaderCacheStrategy::kSkSL;
Brian Osman5bee3902019-05-07 09:55:45 -04002177
Brian Osman072e6fc2019-06-12 11:35:41 -04002178 SkSL::String highlight;
2179 if (!sksl) {
2180 highlight = shaderCaps->versionDeclString();
2181 if (shaderCaps->usesPrecisionModifiers()) {
2182 highlight.append("precision mediump float;\n");
2183 }
Brian Osman5bee3902019-05-07 09:55:45 -04002184 }
2185 const char* f4Type = sksl ? "half4" : "vec4";
Brian Osmancbc33b82019-04-19 14:16:19 -04002186 highlight.appendf("out %s sk_FragColor;\n"
2187 "void main() { sk_FragColor = %s(1, 0, 1, 0.5); }",
2188 f4Type, f4Type);
Brian Osman0b8bb882019-04-12 11:47:19 -04002189
2190 fPersistentCache.reset();
2191 fWindow->getGrContext()->priv().getGpu()->resetShaderCacheForTesting();
2192 for (auto& entry : fCachedGLSL) {
2193 SkSL::String backup = entry.fShader[kFragment_GrShaderType];
2194 if (entry.fHovered) {
2195 entry.fShader[kFragment_GrShaderType] = highlight;
2196 }
2197
Brian Osmana085a412019-04-25 09:44:43 -04002198 auto data = GrPersistentCacheUtils::PackCachedShaders(entry.fShaderType,
2199 entry.fShader,
2200 entry.fInputs,
Brian Osman4524e842019-09-24 16:03:41 -04002201 kGrShaderTypeCount);
Brian Osman0b8bb882019-04-12 11:47:19 -04002202 fPersistentCache.store(*entry.fKey, *data);
2203
2204 entry.fShader[kFragment_GrShaderType] = backup;
2205 }
2206 }
2207 }
Brian Osman79086b92017-02-10 13:36:16 -05002208 }
Brian Salomon99a33902017-03-07 15:16:34 -05002209 if (paramsChanged) {
2210 fDeferredActions.push_back([=]() {
2211 fWindow->setRequestedDisplayParams(params);
2212 fWindow->inval();
2213 this->updateTitle();
2214 });
2215 }
Brian Osman79086b92017-02-10 13:36:16 -05002216 ImGui::End();
2217 }
2218
Brian Osman5e7fbfd2019-05-03 13:13:35 -04002219 if (gShaderErrorHandler.fErrors.count()) {
2220 ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
2221 ImGui::Begin("Shader Errors");
2222 for (int i = 0; i < gShaderErrorHandler.fErrors.count(); ++i) {
2223 ImGui::TextWrapped("%s", gShaderErrorHandler.fErrors[i].c_str());
Chris Dalton77912982019-12-16 11:18:13 -07002224 SkSL::String sksl(gShaderErrorHandler.fShaders[i].c_str());
2225 GrShaderUtils::VisitLineByLine(sksl, [](int lineNumber, const char* lineText) {
2226 ImGui::TextWrapped("%4i\t%s\n", lineNumber, lineText);
2227 });
Brian Osman5e7fbfd2019-05-03 13:13:35 -04002228 }
2229 ImGui::End();
2230 gShaderErrorHandler.reset();
2231 }
2232
Brian Osmanf6877092017-02-13 09:39:57 -05002233 if (fShowZoomWindow && fLastImage) {
Brian Osman7197e052018-06-29 14:30:48 -04002234 ImGui::SetNextWindowSize(ImVec2(200, 200), ImGuiCond_FirstUseEver);
2235 if (ImGui::Begin("Zoom", &fShowZoomWindow)) {
Brian Osmanead517d2017-11-13 15:36:36 -05002236 static int zoomFactor = 8;
2237 if (ImGui::Button("<<")) {
2238 zoomFactor = SkTMax(zoomFactor / 2, 4);
2239 }
2240 ImGui::SameLine(); ImGui::Text("%2d", zoomFactor); ImGui::SameLine();
2241 if (ImGui::Button(">>")) {
2242 zoomFactor = SkTMin(zoomFactor * 2, 32);
2243 }
Brian Osmanf6877092017-02-13 09:39:57 -05002244
Ben Wagner3627d2e2018-06-26 14:23:20 -04002245 if (!fZoomWindowFixed) {
2246 ImVec2 mousePos = ImGui::GetMousePos();
2247 fZoomWindowLocation = SkPoint::Make(mousePos.x, mousePos.y);
2248 }
2249 SkScalar x = fZoomWindowLocation.x();
2250 SkScalar y = fZoomWindowLocation.y();
2251 int xInt = SkScalarRoundToInt(x);
2252 int yInt = SkScalarRoundToInt(y);
Brian Osmanf6877092017-02-13 09:39:57 -05002253 ImVec2 avail = ImGui::GetContentRegionAvail();
2254
Brian Osmanead517d2017-11-13 15:36:36 -05002255 uint32_t pixel = 0;
2256 SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
Ben Wagner3627d2e2018-06-26 14:23:20 -04002257 if (fLastImage->readPixels(info, &pixel, info.minRowBytes(), xInt, yInt)) {
Brian Osmanead517d2017-11-13 15:36:36 -05002258 ImGui::SameLine();
Brian Osman22eeb3c2019-02-20 10:13:06 -05002259 ImGui::Text("(X, Y): %d, %d RGBA: %X %X %X %X",
Ben Wagner3627d2e2018-06-26 14:23:20 -04002260 xInt, yInt,
Brian Osman07b56b22017-11-21 14:59:31 -05002261 SkGetPackedR32(pixel), SkGetPackedG32(pixel),
Brian Osmanead517d2017-11-13 15:36:36 -05002262 SkGetPackedB32(pixel), SkGetPackedA32(pixel));
2263 }
2264
Brian Osmand67e5182017-12-08 16:46:09 -05002265 fImGuiLayer.skiaWidget(avail, [=](SkCanvas* c) {
Brian Osmanead517d2017-11-13 15:36:36 -05002266 // Translate so the region of the image that's under the mouse cursor is centered
2267 // in the zoom canvas:
2268 c->scale(zoomFactor, zoomFactor);
Ben Wagner3627d2e2018-06-26 14:23:20 -04002269 c->translate(avail.x * 0.5f / zoomFactor - x - 0.5f,
2270 avail.y * 0.5f / zoomFactor - y - 0.5f);
Brian Osmanead517d2017-11-13 15:36:36 -05002271 c->drawImage(this->fLastImage, 0, 0);
2272
2273 SkPaint outline;
2274 outline.setStyle(SkPaint::kStroke_Style);
Ben Wagner3627d2e2018-06-26 14:23:20 -04002275 c->drawRect(SkRect::MakeXYWH(x, y, 1, 1), outline);
Brian Osmanead517d2017-11-13 15:36:36 -05002276 });
Brian Osmanf6877092017-02-13 09:39:57 -05002277 }
2278
2279 ImGui::End();
2280 }
Brian Osman79086b92017-02-10 13:36:16 -05002281}
2282
liyuqian2edb0f42016-07-06 14:11:32 -07002283void Viewer::onIdle() {
Brian Osmanfd8f4d52017-02-24 11:57:23 -05002284 for (int i = 0; i < fDeferredActions.count(); ++i) {
2285 fDeferredActions[i]();
2286 }
2287 fDeferredActions.reset();
2288
Brian Osman56a24812017-12-19 11:15:16 -05002289 fStatsLayer.beginTiming(fAnimateTimer);
jvanverthc265a922016-04-08 12:51:45 -07002290 fAnimTimer.updateTime();
Hal Canary41248072019-07-11 16:32:53 -04002291 bool animateWantsInval = fSlides[fCurrentSlide]->animate(fAnimTimer.nanos());
Brian Osman56a24812017-12-19 11:15:16 -05002292 fStatsLayer.endTiming(fAnimateTimer);
Brian Osman1df161a2017-02-09 12:10:20 -05002293
Brian Osman79086b92017-02-10 13:36:16 -05002294 ImGuiIO& io = ImGui::GetIO();
Brian Osmanffee60f2018-08-03 13:03:19 -04002295 // ImGui always has at least one "active" window, which is the default "Debug" window. It may
2296 // not be visible, though. So we need to redraw if there is at least one visible window, or
2297 // more than one active window. Newly created windows are active but not visible for one frame
2298 // while they determine their layout and sizing.
2299 if (animateWantsInval || fStatsLayer.getActive() || fRefresh ||
2300 io.MetricsActiveWindows > 1 || io.MetricsRenderWindows > 0) {
jvanverthc265a922016-04-08 12:51:45 -07002301 fWindow->inval();
2302 }
jvanverth9f372462016-04-06 06:08:59 -07002303}
liyuqiane5a6cd92016-05-27 08:52:52 -07002304
Florin Malitab632df72018-06-18 21:23:06 -04002305template <typename OptionsFunc>
2306static void WriteStateObject(SkJSONWriter& writer, const char* name, const char* value,
2307 OptionsFunc&& optionsFunc) {
2308 writer.beginObject();
2309 {
2310 writer.appendString(kName , name);
2311 writer.appendString(kValue, value);
2312
2313 writer.beginArray(kOptions);
2314 {
2315 optionsFunc(writer);
2316 }
2317 writer.endArray();
2318 }
2319 writer.endObject();
2320}
2321
2322
liyuqiane5a6cd92016-05-27 08:52:52 -07002323void Viewer::updateUIState() {
csmartdalton578f0642017-02-24 16:04:47 -07002324 if (!fWindow) {
2325 return;
2326 }
Brian Salomonbdecacf2018-02-02 20:32:49 -05002327 if (fWindow->sampleCount() < 1) {
csmartdalton578f0642017-02-24 16:04:47 -07002328 return; // Surface hasn't been created yet.
2329 }
2330
Florin Malitab632df72018-06-18 21:23:06 -04002331 SkDynamicMemoryWStream memStream;
2332 SkJSONWriter writer(&memStream);
2333 writer.beginArray();
2334
liyuqianb73c24b2016-06-03 08:47:23 -07002335 // Slide state
Florin Malitab632df72018-06-18 21:23:06 -04002336 WriteStateObject(writer, kSlideStateName, fSlides[fCurrentSlide]->getName().c_str(),
2337 [this](SkJSONWriter& writer) {
2338 for(const auto& slide : fSlides) {
2339 writer.appendString(slide->getName().c_str());
2340 }
2341 });
liyuqiane5a6cd92016-05-27 08:52:52 -07002342
liyuqianb73c24b2016-06-03 08:47:23 -07002343 // Backend state
Florin Malitab632df72018-06-18 21:23:06 -04002344 WriteStateObject(writer, kBackendStateName, kBackendTypeStrings[fBackendType],
2345 [](SkJSONWriter& writer) {
2346 for (const auto& str : kBackendTypeStrings) {
2347 writer.appendString(str);
2348 }
2349 });
liyuqiane5a6cd92016-05-27 08:52:52 -07002350
csmartdalton578f0642017-02-24 16:04:47 -07002351 // MSAA state
Florin Malitab632df72018-06-18 21:23:06 -04002352 const auto countString = SkStringPrintf("%d", fWindow->sampleCount());
2353 WriteStateObject(writer, kMSAAStateName, countString.c_str(),
2354 [this](SkJSONWriter& writer) {
2355 writer.appendS32(0);
2356
2357 if (sk_app::Window::kRaster_BackendType == fBackendType) {
2358 return;
2359 }
2360
2361 for (int msaa : {4, 8, 16}) {
2362 writer.appendS32(msaa);
2363 }
2364 });
csmartdalton578f0642017-02-24 16:04:47 -07002365
csmartdalton61cd31a2017-02-27 17:00:53 -07002366 // Path renderer state
2367 GpuPathRenderers pr = fWindow->getRequestedDisplayParams().fGrContextOptions.fGpuPathRenderers;
Florin Malitab632df72018-06-18 21:23:06 -04002368 WriteStateObject(writer, kPathRendererStateName, gPathRendererNames[pr].c_str(),
2369 [this](SkJSONWriter& writer) {
2370 const GrContext* ctx = fWindow->getGrContext();
2371 if (!ctx) {
2372 writer.appendString("Software");
2373 } else {
Robert Phillips9da87e02019-02-04 13:26:26 -05002374 const auto* caps = ctx->priv().caps();
Chris Dalton37ae4b02019-12-28 14:51:11 -07002375 writer.appendString(gPathRendererNames[GpuPathRenderers::kDefault].c_str());
2376 if (fWindow->sampleCount() > 1 || caps->mixedSamplesSupport()) {
Florin Malitab632df72018-06-18 21:23:06 -04002377 if (caps->shaderCaps()->pathRenderingSupport()) {
2378 writer.appendString(
Chris Dalton37ae4b02019-12-28 14:51:11 -07002379 gPathRendererNames[GpuPathRenderers::kStencilAndCover].c_str());
Florin Malitab632df72018-06-18 21:23:06 -04002380 }
Chris Dalton37ae4b02019-12-28 14:51:11 -07002381 }
2382 if (1 == fWindow->sampleCount()) {
Florin Malitab632df72018-06-18 21:23:06 -04002383 if(GrCoverageCountingPathRenderer::IsSupported(*caps)) {
2384 writer.appendString(
2385 gPathRendererNames[GpuPathRenderers::kCoverageCounting].c_str());
2386 }
2387 writer.appendString(gPathRendererNames[GpuPathRenderers::kSmall].c_str());
2388 }
Chris Dalton37ae4b02019-12-28 14:51:11 -07002389 writer.appendString(gPathRendererNames[GpuPathRenderers::kTessellating].c_str());
2390 writer.appendString(gPathRendererNames[GpuPathRenderers::kNone].c_str());
Florin Malitab632df72018-06-18 21:23:06 -04002391 }
2392 });
csmartdalton61cd31a2017-02-27 17:00:53 -07002393
liyuqianb73c24b2016-06-03 08:47:23 -07002394 // Softkey state
Florin Malitab632df72018-06-18 21:23:06 -04002395 WriteStateObject(writer, kSoftkeyStateName, kSoftkeyHint,
2396 [this](SkJSONWriter& writer) {
2397 writer.appendString(kSoftkeyHint);
2398 for (const auto& softkey : fCommands.getCommandsAsSoftkeys()) {
2399 writer.appendString(softkey.c_str());
2400 }
2401 });
liyuqianb73c24b2016-06-03 08:47:23 -07002402
Florin Malitab632df72018-06-18 21:23:06 -04002403 writer.endArray();
2404 writer.flush();
liyuqiane5a6cd92016-05-27 08:52:52 -07002405
Florin Malitab632df72018-06-18 21:23:06 -04002406 auto data = memStream.detachAsData();
2407
2408 // TODO: would be cool to avoid this copy
2409 const SkString cstring(static_cast<const char*>(data->data()), data->size());
2410
2411 fWindow->setUIState(cstring.c_str());
liyuqiane5a6cd92016-05-27 08:52:52 -07002412}
2413
2414void Viewer::onUIStateChanged(const SkString& stateName, const SkString& stateValue) {
liyuqian6cb70252016-06-02 12:16:25 -07002415 // For those who will add more features to handle the state change in this function:
2416 // After the change, please call updateUIState no notify the frontend (e.g., Android app).
2417 // For example, after slide change, updateUIState is called inside setupCurrentSlide;
2418 // after backend change, updateUIState is called in this function.
liyuqiane5a6cd92016-05-27 08:52:52 -07002419 if (stateName.equals(kSlideStateName)) {
Florin Malitaab99c342018-01-16 16:23:03 -05002420 for (int i = 0; i < fSlides.count(); ++i) {
2421 if (fSlides[i]->getName().equals(stateValue)) {
2422 this->setCurrentSlide(i);
2423 return;
liyuqiane5a6cd92016-05-27 08:52:52 -07002424 }
liyuqiane5a6cd92016-05-27 08:52:52 -07002425 }
Florin Malitaab99c342018-01-16 16:23:03 -05002426
2427 SkDebugf("Slide not found: %s", stateValue.c_str());
liyuqian6cb70252016-06-02 12:16:25 -07002428 } else if (stateName.equals(kBackendStateName)) {
2429 for (int i = 0; i < sk_app::Window::kBackendTypeCount; i++) {
2430 if (stateValue.equals(kBackendTypeStrings[i])) {
2431 if (fBackendType != i) {
2432 fBackendType = (sk_app::Window::BackendType)i;
2433 fWindow->detach();
Brian Osman70d2f432017-11-08 09:54:10 -05002434 fWindow->attach(backend_type_for_window(fBackendType));
liyuqian6cb70252016-06-02 12:16:25 -07002435 }
2436 break;
2437 }
2438 }
csmartdalton578f0642017-02-24 16:04:47 -07002439 } else if (stateName.equals(kMSAAStateName)) {
2440 DisplayParams params = fWindow->getRequestedDisplayParams();
2441 int sampleCount = atoi(stateValue.c_str());
2442 if (sampleCount != params.fMSAASampleCount) {
2443 params.fMSAASampleCount = sampleCount;
2444 fWindow->setRequestedDisplayParams(params);
2445 fWindow->inval();
Brian Salomon99a33902017-03-07 15:16:34 -05002446 this->updateTitle();
2447 this->updateUIState();
csmartdalton61cd31a2017-02-27 17:00:53 -07002448 }
2449 } else if (stateName.equals(kPathRendererStateName)) {
2450 DisplayParams params = fWindow->getRequestedDisplayParams();
2451 for (const auto& pair : gPathRendererNames) {
2452 if (pair.second == stateValue.c_str()) {
2453 if (params.fGrContextOptions.fGpuPathRenderers != pair.first) {
2454 params.fGrContextOptions.fGpuPathRenderers = pair.first;
2455 fWindow->setRequestedDisplayParams(params);
2456 fWindow->inval();
Brian Salomon99a33902017-03-07 15:16:34 -05002457 this->updateTitle();
2458 this->updateUIState();
csmartdalton61cd31a2017-02-27 17:00:53 -07002459 }
2460 break;
2461 }
csmartdalton578f0642017-02-24 16:04:47 -07002462 }
liyuqianb73c24b2016-06-03 08:47:23 -07002463 } else if (stateName.equals(kSoftkeyStateName)) {
2464 if (!stateValue.equals(kSoftkeyHint)) {
2465 fCommands.onSoftkey(stateValue);
Brian Salomon99a33902017-03-07 15:16:34 -05002466 this->updateUIState(); // This is still needed to reset the value to kSoftkeyHint
liyuqianb73c24b2016-06-03 08:47:23 -07002467 }
liyuqian2edb0f42016-07-06 14:11:32 -07002468 } else if (stateName.equals(kRefreshStateName)) {
2469 // This state is actually NOT in the UI state.
2470 // We use this to allow Android to quickly set bool fRefresh.
2471 fRefresh = stateValue.equals(kON);
liyuqiane5a6cd92016-05-27 08:52:52 -07002472 } else {
2473 SkDebugf("Unknown stateName: %s", stateName.c_str());
2474 }
2475}
Brian Osman79086b92017-02-10 13:36:16 -05002476
Hal Canaryb1f411a2019-08-29 10:39:22 -04002477bool Viewer::onKey(skui::Key key, skui::InputState state, skui::ModifierKey modifiers) {
Brian Osmand67e5182017-12-08 16:46:09 -05002478 return fCommands.onKey(key, state, modifiers);
Brian Osman79086b92017-02-10 13:36:16 -05002479}
2480
Hal Canaryb1f411a2019-08-29 10:39:22 -04002481bool Viewer::onChar(SkUnichar c, skui::ModifierKey modifiers) {
Brian Osmand67e5182017-12-08 16:46:09 -05002482 if (fSlides[fCurrentSlide]->onChar(c)) {
Jim Van Verth6f449692017-02-14 15:16:46 -05002483 fWindow->inval();
2484 return true;
Brian Osman80fc07e2017-12-08 16:45:43 -05002485 } else {
2486 return fCommands.onChar(c, modifiers);
Jim Van Verth6f449692017-02-14 15:16:46 -05002487 }
Brian Osman79086b92017-02-10 13:36:16 -05002488}