xref: /third_party/skia/tools/viewer/Viewer.cpp (revision cb93a386)
1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci* Copyright 2016 Google Inc.
3cb93a386Sopenharmony_ci*
4cb93a386Sopenharmony_ci* Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci* found in the LICENSE file.
6cb93a386Sopenharmony_ci*/
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "tools/viewer/Viewer.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h"
11cb93a386Sopenharmony_ci#include "include/core/SkData.h"
12cb93a386Sopenharmony_ci#include "include/core/SkGraphics.h"
13cb93a386Sopenharmony_ci#include "include/core/SkPictureRecorder.h"
14cb93a386Sopenharmony_ci#include "include/core/SkStream.h"
15cb93a386Sopenharmony_ci#include "include/core/SkSurface.h"
16cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h"
17cb93a386Sopenharmony_ci#include "include/private/SkTPin.h"
18cb93a386Sopenharmony_ci#include "include/private/SkTo.h"
19cb93a386Sopenharmony_ci#include "include/utils/SkPaintFilterCanvas.h"
20cb93a386Sopenharmony_ci#include "src/core/SkColorSpacePriv.h"
21cb93a386Sopenharmony_ci#include "src/core/SkImagePriv.h"
22cb93a386Sopenharmony_ci#include "src/core/SkMD5.h"
23cb93a386Sopenharmony_ci#include "src/core/SkOSFile.h"
24cb93a386Sopenharmony_ci#include "src/core/SkReadBuffer.h"
25cb93a386Sopenharmony_ci#include "src/core/SkScan.h"
26cb93a386Sopenharmony_ci#include "src/core/SkSurfacePriv.h"
27cb93a386Sopenharmony_ci#include "src/core/SkTSort.h"
28cb93a386Sopenharmony_ci#include "src/core/SkTaskGroup.h"
29cb93a386Sopenharmony_ci#include "src/core/SkTextBlobPriv.h"
30cb93a386Sopenharmony_ci#include "src/core/SkVMBlitter.h"
31cb93a386Sopenharmony_ci#include "src/gpu/GrDirectContextPriv.h"
32cb93a386Sopenharmony_ci#include "src/gpu/GrGpu.h"
33cb93a386Sopenharmony_ci#include "src/gpu/GrPersistentCacheUtils.h"
34cb93a386Sopenharmony_ci#include "src/gpu/GrShaderUtils.h"
35cb93a386Sopenharmony_ci#include "src/image/SkImage_Base.h"
36cb93a386Sopenharmony_ci#include "src/sksl/SkSLCompiler.h"
37cb93a386Sopenharmony_ci#include "src/utils/SkJSONWriter.h"
38cb93a386Sopenharmony_ci#include "src/utils/SkOSPath.h"
39cb93a386Sopenharmony_ci#include "tools/Resources.h"
40cb93a386Sopenharmony_ci#include "tools/RuntimeBlendUtils.h"
41cb93a386Sopenharmony_ci#include "tools/ToolUtils.h"
42cb93a386Sopenharmony_ci#include "tools/flags/CommandLineFlags.h"
43cb93a386Sopenharmony_ci#include "tools/flags/CommonFlags.h"
44cb93a386Sopenharmony_ci#include "tools/trace/EventTracingPriv.h"
45cb93a386Sopenharmony_ci#include "tools/viewer/BisectSlide.h"
46cb93a386Sopenharmony_ci#include "tools/viewer/GMSlide.h"
47cb93a386Sopenharmony_ci#include "tools/viewer/ImageSlide.h"
48cb93a386Sopenharmony_ci#include "tools/viewer/MSKPSlide.h"
49cb93a386Sopenharmony_ci#include "tools/viewer/ParticlesSlide.h"
50cb93a386Sopenharmony_ci#include "tools/viewer/SKPSlide.h"
51cb93a386Sopenharmony_ci#include "tools/viewer/SampleSlide.h"
52cb93a386Sopenharmony_ci#include "tools/viewer/SkSLSlide.h"
53cb93a386Sopenharmony_ci#include "tools/viewer/SlideDir.h"
54cb93a386Sopenharmony_ci#include "tools/viewer/SvgSlide.h"
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ci#if SK_GPU_V1
57cb93a386Sopenharmony_ci#include "src/gpu/ops/AtlasPathRenderer.h"
58cb93a386Sopenharmony_ci#include "src/gpu/ops/TessellationPathRenderer.h"
59cb93a386Sopenharmony_ci#endif
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_ci#include <cstdlib>
62cb93a386Sopenharmony_ci#include <map>
63cb93a386Sopenharmony_ci
64cb93a386Sopenharmony_ci#include "imgui.h"
65cb93a386Sopenharmony_ci#include "misc/cpp/imgui_stdlib.h"  // For ImGui support of std::string
66cb93a386Sopenharmony_ci
67cb93a386Sopenharmony_ci#ifdef SK_VULKAN
68cb93a386Sopenharmony_ci#include "spirv-tools/libspirv.hpp"
69cb93a386Sopenharmony_ci#endif
70cb93a386Sopenharmony_ci
71cb93a386Sopenharmony_ci#if defined(SK_ENABLE_SKOTTIE)
72cb93a386Sopenharmony_ci    #include "tools/viewer/SkottieSlide.h"
73cb93a386Sopenharmony_ci#endif
74cb93a386Sopenharmony_ci#if defined(SK_ENABLE_SKRIVE)
75cb93a386Sopenharmony_ci    #include "tools/viewer/SkRiveSlide.h"
76cb93a386Sopenharmony_ci#endif
77cb93a386Sopenharmony_ci
78cb93a386Sopenharmony_ciclass CapturingShaderErrorHandler : public GrContextOptions::ShaderErrorHandler {
79cb93a386Sopenharmony_cipublic:
80cb93a386Sopenharmony_ci    void compileError(const char* shader, const char* errors) override {
81cb93a386Sopenharmony_ci        fShaders.push_back(SkString(shader));
82cb93a386Sopenharmony_ci        fErrors.push_back(SkString(errors));
83cb93a386Sopenharmony_ci    }
84cb93a386Sopenharmony_ci
85cb93a386Sopenharmony_ci    void reset() {
86cb93a386Sopenharmony_ci        fShaders.reset();
87cb93a386Sopenharmony_ci        fErrors.reset();
88cb93a386Sopenharmony_ci    }
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_ci    SkTArray<SkString> fShaders;
91cb93a386Sopenharmony_ci    SkTArray<SkString> fErrors;
92cb93a386Sopenharmony_ci};
93cb93a386Sopenharmony_ci
94cb93a386Sopenharmony_cistatic CapturingShaderErrorHandler gShaderErrorHandler;
95cb93a386Sopenharmony_ci
96cb93a386Sopenharmony_ciGrContextOptions::ShaderErrorHandler* Viewer::ShaderErrorHandler() { return &gShaderErrorHandler; }
97cb93a386Sopenharmony_ci
98cb93a386Sopenharmony_ciusing namespace sk_app;
99cb93a386Sopenharmony_ciusing SkSL::Compiler;
100cb93a386Sopenharmony_ciusing OverrideFlag = SkSL::Compiler::OverrideFlag;
101cb93a386Sopenharmony_ci
102cb93a386Sopenharmony_cistatic std::map<GpuPathRenderers, std::string> gPathRendererNames;
103cb93a386Sopenharmony_ci
104cb93a386Sopenharmony_ciApplication* Application::Create(int argc, char** argv, void* platformData) {
105cb93a386Sopenharmony_ci    return new Viewer(argc, argv, platformData);
106cb93a386Sopenharmony_ci}
107cb93a386Sopenharmony_ci
108cb93a386Sopenharmony_cistatic DEFINE_string(slide, "", "Start on this sample.");
109cb93a386Sopenharmony_cistatic DEFINE_bool(list, false, "List samples?");
110cb93a386Sopenharmony_ci
111cb93a386Sopenharmony_ci#ifdef SK_GL
112cb93a386Sopenharmony_ci#define GL_BACKEND_STR ", \"gl\""
113cb93a386Sopenharmony_ci#else
114cb93a386Sopenharmony_ci#define GL_BACKEND_STR
115cb93a386Sopenharmony_ci#endif
116cb93a386Sopenharmony_ci#ifdef SK_VULKAN
117cb93a386Sopenharmony_ci#define VK_BACKEND_STR ", \"vk\""
118cb93a386Sopenharmony_ci#else
119cb93a386Sopenharmony_ci#define VK_BACKEND_STR
120cb93a386Sopenharmony_ci#endif
121cb93a386Sopenharmony_ci#ifdef SK_METAL
122cb93a386Sopenharmony_ci#define MTL_BACKEND_STR ", \"mtl\""
123cb93a386Sopenharmony_ci#else
124cb93a386Sopenharmony_ci#define MTL_BACKEND_STR
125cb93a386Sopenharmony_ci#endif
126cb93a386Sopenharmony_ci#ifdef SK_DIRECT3D
127cb93a386Sopenharmony_ci#define D3D_BACKEND_STR ", \"d3d\""
128cb93a386Sopenharmony_ci#else
129cb93a386Sopenharmony_ci#define D3D_BACKEND_STR
130cb93a386Sopenharmony_ci#endif
131cb93a386Sopenharmony_ci#ifdef SK_DAWN
132cb93a386Sopenharmony_ci#define DAWN_BACKEND_STR ", \"dawn\""
133cb93a386Sopenharmony_ci#else
134cb93a386Sopenharmony_ci#define DAWN_BACKEND_STR
135cb93a386Sopenharmony_ci#endif
136cb93a386Sopenharmony_ci#define BACKENDS_STR_EVALUATOR(sw, gl, vk, mtl, d3d, dawn) sw gl vk mtl d3d dawn
137cb93a386Sopenharmony_ci#define BACKENDS_STR BACKENDS_STR_EVALUATOR( \
138cb93a386Sopenharmony_ci    "\"sw\"", GL_BACKEND_STR, VK_BACKEND_STR, MTL_BACKEND_STR, D3D_BACKEND_STR, DAWN_BACKEND_STR)
139cb93a386Sopenharmony_ci
140cb93a386Sopenharmony_cistatic DEFINE_string2(backend, b, "sw", "Backend to use. Allowed values are " BACKENDS_STR ".");
141cb93a386Sopenharmony_ci
142cb93a386Sopenharmony_cistatic DEFINE_int(msaa, 1, "Number of subpixel samples. 0 for no HW antialiasing.");
143cb93a386Sopenharmony_cistatic DEFINE_bool(dmsaa, false, "Use internal MSAA to render to non-MSAA surfaces?");
144cb93a386Sopenharmony_ci
145cb93a386Sopenharmony_cistatic DEFINE_string(bisect, "", "Path to a .skp or .svg file to bisect.");
146cb93a386Sopenharmony_ci
147cb93a386Sopenharmony_cistatic DEFINE_string2(file, f, "", "Open a single file for viewing.");
148cb93a386Sopenharmony_ci
149cb93a386Sopenharmony_cistatic DEFINE_string2(match, m, nullptr,
150cb93a386Sopenharmony_ci               "[~][^]substring[$] [...] of name to run.\n"
151cb93a386Sopenharmony_ci               "Multiple matches may be separated by spaces.\n"
152cb93a386Sopenharmony_ci               "~ causes a matching name to always be skipped\n"
153cb93a386Sopenharmony_ci               "^ requires the start of the name to match\n"
154cb93a386Sopenharmony_ci               "$ requires the end of the name to match\n"
155cb93a386Sopenharmony_ci               "^ and $ requires an exact match\n"
156cb93a386Sopenharmony_ci               "If a name does not match any list entry,\n"
157cb93a386Sopenharmony_ci               "it is skipped unless some list entry starts with ~");
158cb93a386Sopenharmony_ci
159cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_ANDROID)
160cb93a386Sopenharmony_ci#   define PATH_PREFIX "/data/local/tmp/"
161cb93a386Sopenharmony_ci#else
162cb93a386Sopenharmony_ci#   define PATH_PREFIX ""
163cb93a386Sopenharmony_ci#endif
164cb93a386Sopenharmony_ci
165cb93a386Sopenharmony_cistatic DEFINE_string(jpgs   , PATH_PREFIX "jpgs"   , "Directory to read jpgs from.");
166cb93a386Sopenharmony_cistatic DEFINE_string(skps   , PATH_PREFIX "skps"   , "Directory to read skps from.");
167cb93a386Sopenharmony_cistatic DEFINE_string(mskps  , PATH_PREFIX "mskps"  , "Directory to read mskps from.");
168cb93a386Sopenharmony_cistatic DEFINE_string(lotties, PATH_PREFIX "lotties", "Directory to read (Bodymovin) jsons from.");
169cb93a386Sopenharmony_cistatic DEFINE_string(rives  , PATH_PREFIX "rives"  , "Directory to read Rive (Flare) files from.");
170cb93a386Sopenharmony_ci#undef PATH_PREFIX
171cb93a386Sopenharmony_ci
172cb93a386Sopenharmony_cistatic DEFINE_string(svgs, "", "Directory to read SVGs from, or a single SVG file.");
173cb93a386Sopenharmony_ci
174cb93a386Sopenharmony_cistatic DEFINE_int_2(threads, j, -1,
175cb93a386Sopenharmony_ci               "Run threadsafe tests on a threadpool with this many extra threads, "
176cb93a386Sopenharmony_ci               "defaulting to one extra thread per core.");
177cb93a386Sopenharmony_ci
178cb93a386Sopenharmony_cistatic DEFINE_bool(redraw, false, "Toggle continuous redraw.");
179cb93a386Sopenharmony_ci
180cb93a386Sopenharmony_cistatic DEFINE_bool(offscreen, false, "Force rendering to an offscreen surface.");
181cb93a386Sopenharmony_cistatic DEFINE_bool(skvm, false, "Force skvm blitters for raster.");
182cb93a386Sopenharmony_cistatic DEFINE_bool(jit, true, "JIT SkVM?");
183cb93a386Sopenharmony_cistatic DEFINE_bool(dylib, false, "JIT via dylib (much slower compile but easier to debug/profile)");
184cb93a386Sopenharmony_cistatic DEFINE_bool(stats, false, "Display stats overlay on startup.");
185cb93a386Sopenharmony_cistatic DEFINE_bool(binaryarchive, false, "Enable MTLBinaryArchive use (if available).");
186cb93a386Sopenharmony_ci
187cb93a386Sopenharmony_ci#ifndef SK_GL
188cb93a386Sopenharmony_cistatic_assert(false, "viewer requires GL backend for raster.")
189cb93a386Sopenharmony_ci#endif
190cb93a386Sopenharmony_ci
191cb93a386Sopenharmony_ciconst char* kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = {
192cb93a386Sopenharmony_ci    "OpenGL",
193cb93a386Sopenharmony_ci#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
194cb93a386Sopenharmony_ci    "ANGLE",
195cb93a386Sopenharmony_ci#endif
196cb93a386Sopenharmony_ci#ifdef SK_DAWN
197cb93a386Sopenharmony_ci    "Dawn",
198cb93a386Sopenharmony_ci#endif
199cb93a386Sopenharmony_ci#ifdef SK_VULKAN
200cb93a386Sopenharmony_ci    "Vulkan",
201cb93a386Sopenharmony_ci#endif
202cb93a386Sopenharmony_ci#ifdef SK_METAL
203cb93a386Sopenharmony_ci    "Metal",
204cb93a386Sopenharmony_ci#ifdef SK_GRAPHITE_ENABLED
205cb93a386Sopenharmony_ci    "Metal (Graphite)",
206cb93a386Sopenharmony_ci#endif
207cb93a386Sopenharmony_ci#endif
208cb93a386Sopenharmony_ci#ifdef SK_DIRECT3D
209cb93a386Sopenharmony_ci    "Direct3D",
210cb93a386Sopenharmony_ci#endif
211cb93a386Sopenharmony_ci    "Raster"
212cb93a386Sopenharmony_ci};
213cb93a386Sopenharmony_ci
214cb93a386Sopenharmony_cistatic sk_app::Window::BackendType get_backend_type(const char* str) {
215cb93a386Sopenharmony_ci#ifdef SK_DAWN
216cb93a386Sopenharmony_ci    if (0 == strcmp(str, "dawn")) {
217cb93a386Sopenharmony_ci        return sk_app::Window::kDawn_BackendType;
218cb93a386Sopenharmony_ci    } else
219cb93a386Sopenharmony_ci#endif
220cb93a386Sopenharmony_ci#ifdef SK_VULKAN
221cb93a386Sopenharmony_ci    if (0 == strcmp(str, "vk")) {
222cb93a386Sopenharmony_ci        return sk_app::Window::kVulkan_BackendType;
223cb93a386Sopenharmony_ci    } else
224cb93a386Sopenharmony_ci#endif
225cb93a386Sopenharmony_ci#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
226cb93a386Sopenharmony_ci    if (0 == strcmp(str, "angle")) {
227cb93a386Sopenharmony_ci        return sk_app::Window::kANGLE_BackendType;
228cb93a386Sopenharmony_ci    } else
229cb93a386Sopenharmony_ci#endif
230cb93a386Sopenharmony_ci#ifdef SK_METAL
231cb93a386Sopenharmony_ci    if (0 == strcmp(str, "mtl")) {
232cb93a386Sopenharmony_ci        return sk_app::Window::kMetal_BackendType;
233cb93a386Sopenharmony_ci    } else
234cb93a386Sopenharmony_ci#ifdef SK_GRAPHITE_ENABLED
235cb93a386Sopenharmony_ci    if (0 == strcmp(str, "grmtl")) {
236cb93a386Sopenharmony_ci        return sk_app::Window::kGraphiteMetal_BackendType;
237cb93a386Sopenharmony_ci    } else
238cb93a386Sopenharmony_ci#endif
239cb93a386Sopenharmony_ci#endif
240cb93a386Sopenharmony_ci#ifdef SK_DIRECT3D
241cb93a386Sopenharmony_ci    if (0 == strcmp(str, "d3d")) {
242cb93a386Sopenharmony_ci        return sk_app::Window::kDirect3D_BackendType;
243cb93a386Sopenharmony_ci    } else
244cb93a386Sopenharmony_ci#endif
245cb93a386Sopenharmony_ci
246cb93a386Sopenharmony_ci    if (0 == strcmp(str, "gl")) {
247cb93a386Sopenharmony_ci        return sk_app::Window::kNativeGL_BackendType;
248cb93a386Sopenharmony_ci    } else if (0 == strcmp(str, "sw")) {
249cb93a386Sopenharmony_ci        return sk_app::Window::kRaster_BackendType;
250cb93a386Sopenharmony_ci    } else {
251cb93a386Sopenharmony_ci        SkDebugf("Unknown backend type, %s, defaulting to sw.", str);
252cb93a386Sopenharmony_ci        return sk_app::Window::kRaster_BackendType;
253cb93a386Sopenharmony_ci    }
254cb93a386Sopenharmony_ci}
255cb93a386Sopenharmony_ci
256cb93a386Sopenharmony_cistatic SkColorSpacePrimaries gSrgbPrimaries = {
257cb93a386Sopenharmony_ci    0.64f, 0.33f,
258cb93a386Sopenharmony_ci    0.30f, 0.60f,
259cb93a386Sopenharmony_ci    0.15f, 0.06f,
260cb93a386Sopenharmony_ci    0.3127f, 0.3290f };
261cb93a386Sopenharmony_ci
262cb93a386Sopenharmony_cistatic SkColorSpacePrimaries gAdobePrimaries = {
263cb93a386Sopenharmony_ci    0.64f, 0.33f,
264cb93a386Sopenharmony_ci    0.21f, 0.71f,
265cb93a386Sopenharmony_ci    0.15f, 0.06f,
266cb93a386Sopenharmony_ci    0.3127f, 0.3290f };
267cb93a386Sopenharmony_ci
268cb93a386Sopenharmony_cistatic SkColorSpacePrimaries gP3Primaries = {
269cb93a386Sopenharmony_ci    0.680f, 0.320f,
270cb93a386Sopenharmony_ci    0.265f, 0.690f,
271cb93a386Sopenharmony_ci    0.150f, 0.060f,
272cb93a386Sopenharmony_ci    0.3127f, 0.3290f };
273cb93a386Sopenharmony_ci
274cb93a386Sopenharmony_cistatic SkColorSpacePrimaries gRec2020Primaries = {
275cb93a386Sopenharmony_ci    0.708f, 0.292f,
276cb93a386Sopenharmony_ci    0.170f, 0.797f,
277cb93a386Sopenharmony_ci    0.131f, 0.046f,
278cb93a386Sopenharmony_ci    0.3127f, 0.3290f };
279cb93a386Sopenharmony_ci
280cb93a386Sopenharmony_cistruct NamedPrimaries {
281cb93a386Sopenharmony_ci    const char* fName;
282cb93a386Sopenharmony_ci    SkColorSpacePrimaries* fPrimaries;
283cb93a386Sopenharmony_ci} gNamedPrimaries[] = {
284cb93a386Sopenharmony_ci    { "sRGB", &gSrgbPrimaries },
285cb93a386Sopenharmony_ci    { "AdobeRGB", &gAdobePrimaries },
286cb93a386Sopenharmony_ci    { "P3", &gP3Primaries },
287cb93a386Sopenharmony_ci    { "Rec. 2020", &gRec2020Primaries },
288cb93a386Sopenharmony_ci};
289cb93a386Sopenharmony_ci
290cb93a386Sopenharmony_cistatic bool primaries_equal(const SkColorSpacePrimaries& a, const SkColorSpacePrimaries& b) {
291cb93a386Sopenharmony_ci    return memcmp(&a, &b, sizeof(SkColorSpacePrimaries)) == 0;
292cb93a386Sopenharmony_ci}
293cb93a386Sopenharmony_ci
294cb93a386Sopenharmony_cistatic Window::BackendType backend_type_for_window(Window::BackendType backendType) {
295cb93a386Sopenharmony_ci    // In raster mode, we still use GL for the window.
296cb93a386Sopenharmony_ci    // This lets us render the GUI faster (and correct).
297cb93a386Sopenharmony_ci    return Window::kRaster_BackendType == backendType ? Window::kNativeGL_BackendType : backendType;
298cb93a386Sopenharmony_ci}
299cb93a386Sopenharmony_ci
300cb93a386Sopenharmony_ciclass NullSlide : public Slide {
301cb93a386Sopenharmony_ci    SkISize getDimensions() const override {
302cb93a386Sopenharmony_ci        return SkISize::Make(640, 480);
303cb93a386Sopenharmony_ci    }
304cb93a386Sopenharmony_ci
305cb93a386Sopenharmony_ci    void draw(SkCanvas* canvas) override {
306cb93a386Sopenharmony_ci        canvas->clear(0xffff11ff);
307cb93a386Sopenharmony_ci    }
308cb93a386Sopenharmony_ci};
309cb93a386Sopenharmony_ci
310cb93a386Sopenharmony_cistatic const char kName[] = "name";
311cb93a386Sopenharmony_cistatic const char kValue[] = "value";
312cb93a386Sopenharmony_cistatic const char kOptions[] = "options";
313cb93a386Sopenharmony_cistatic const char kSlideStateName[] = "Slide";
314cb93a386Sopenharmony_cistatic const char kBackendStateName[] = "Backend";
315cb93a386Sopenharmony_cistatic const char kMSAAStateName[] = "MSAA";
316cb93a386Sopenharmony_cistatic const char kPathRendererStateName[] = "Path renderer";
317cb93a386Sopenharmony_cistatic const char kSoftkeyStateName[] = "Softkey";
318cb93a386Sopenharmony_cistatic const char kSoftkeyHint[] = "Please select a softkey";
319cb93a386Sopenharmony_cistatic const char kON[] = "ON";
320cb93a386Sopenharmony_cistatic const char kRefreshStateName[] = "Refresh";
321cb93a386Sopenharmony_ci
322cb93a386Sopenharmony_ciextern bool gUseSkVMBlitter;
323cb93a386Sopenharmony_ciextern bool gSkVMAllowJIT;
324cb93a386Sopenharmony_ciextern bool gSkVMJITViaDylib;
325cb93a386Sopenharmony_ci
326cb93a386Sopenharmony_ciViewer::Viewer(int argc, char** argv, void* platformData)
327cb93a386Sopenharmony_ci    : fCurrentSlide(-1)
328cb93a386Sopenharmony_ci    , fRefresh(false)
329cb93a386Sopenharmony_ci    , fSaveToSKP(false)
330cb93a386Sopenharmony_ci    , fShowSlideDimensions(false)
331cb93a386Sopenharmony_ci    , fShowImGuiDebugWindow(false)
332cb93a386Sopenharmony_ci    , fShowSlidePicker(false)
333cb93a386Sopenharmony_ci    , fShowImGuiTestWindow(false)
334cb93a386Sopenharmony_ci    , fShowZoomWindow(false)
335cb93a386Sopenharmony_ci    , fZoomWindowFixed(false)
336cb93a386Sopenharmony_ci    , fZoomWindowLocation{0.0f, 0.0f}
337cb93a386Sopenharmony_ci    , fLastImage(nullptr)
338cb93a386Sopenharmony_ci    , fZoomUI(false)
339cb93a386Sopenharmony_ci    , fBackendType(sk_app::Window::kNativeGL_BackendType)
340cb93a386Sopenharmony_ci    , fColorMode(ColorMode::kLegacy)
341cb93a386Sopenharmony_ci    , fColorSpacePrimaries(gSrgbPrimaries)
342cb93a386Sopenharmony_ci    // Our UI can only tweak gamma (currently), so start out gamma-only
343cb93a386Sopenharmony_ci    , fColorSpaceTransferFn(SkNamedTransferFn::k2Dot2)
344cb93a386Sopenharmony_ci    , fApplyBackingScale(true)
345cb93a386Sopenharmony_ci    , fZoomLevel(0.0f)
346cb93a386Sopenharmony_ci    , fRotation(0.0f)
347cb93a386Sopenharmony_ci    , fOffset{0.5f, 0.5f}
348cb93a386Sopenharmony_ci    , fGestureDevice(GestureDevice::kNone)
349cb93a386Sopenharmony_ci    , fTiled(false)
350cb93a386Sopenharmony_ci    , fDrawTileBoundaries(false)
351cb93a386Sopenharmony_ci    , fTileScale{0.25f, 0.25f}
352cb93a386Sopenharmony_ci    , fPerspectiveMode(kPerspective_Off)
353cb93a386Sopenharmony_ci{
354cb93a386Sopenharmony_ci    SkGraphics::Init();
355cb93a386Sopenharmony_ci
356cb93a386Sopenharmony_ci    gPathRendererNames[GpuPathRenderers::kDefault] = "Default Path Renderers";
357cb93a386Sopenharmony_ci    gPathRendererNames[GpuPathRenderers::kAtlas] = "Atlas (tessellation)";
358cb93a386Sopenharmony_ci    gPathRendererNames[GpuPathRenderers::kTessellation] = "Tessellation";
359cb93a386Sopenharmony_ci    gPathRendererNames[GpuPathRenderers::kSmall] = "Small paths (cached sdf or alpha masks)";
360cb93a386Sopenharmony_ci    gPathRendererNames[GpuPathRenderers::kTriangulating] = "Triangulating";
361cb93a386Sopenharmony_ci    gPathRendererNames[GpuPathRenderers::kNone] = "Software masks";
362cb93a386Sopenharmony_ci
363cb93a386Sopenharmony_ci    SkDebugf("Command line arguments: ");
364cb93a386Sopenharmony_ci    for (int i = 1; i < argc; ++i) {
365cb93a386Sopenharmony_ci        SkDebugf("%s ", argv[i]);
366cb93a386Sopenharmony_ci    }
367cb93a386Sopenharmony_ci    SkDebugf("\n");
368cb93a386Sopenharmony_ci
369cb93a386Sopenharmony_ci    CommandLineFlags::Parse(argc, argv);
370cb93a386Sopenharmony_ci#ifdef SK_BUILD_FOR_ANDROID
371cb93a386Sopenharmony_ci    SetResourcePath("/data/local/tmp/resources");
372cb93a386Sopenharmony_ci#endif
373cb93a386Sopenharmony_ci
374cb93a386Sopenharmony_ci    gUseSkVMBlitter = FLAGS_skvm;
375cb93a386Sopenharmony_ci    gSkVMAllowJIT = FLAGS_jit;
376cb93a386Sopenharmony_ci    gSkVMJITViaDylib = FLAGS_dylib;
377cb93a386Sopenharmony_ci
378cb93a386Sopenharmony_ci    CommonFlags::SetDefaultFontMgr();
379cb93a386Sopenharmony_ci
380cb93a386Sopenharmony_ci    initializeEventTracingForTools();
381cb93a386Sopenharmony_ci    static SkTaskGroup::Enabler kTaskGroupEnabler(FLAGS_threads);
382cb93a386Sopenharmony_ci
383cb93a386Sopenharmony_ci    fBackendType = get_backend_type(FLAGS_backend[0]);
384cb93a386Sopenharmony_ci    fWindow = Window::CreateNativeWindow(platformData);
385cb93a386Sopenharmony_ci
386cb93a386Sopenharmony_ci    DisplayParams displayParams;
387cb93a386Sopenharmony_ci    displayParams.fMSAASampleCount = FLAGS_msaa;
388cb93a386Sopenharmony_ci    displayParams.fEnableBinaryArchive = FLAGS_binaryarchive;
389cb93a386Sopenharmony_ci    CommonFlags::SetCtxOptions(&displayParams.fGrContextOptions);
390cb93a386Sopenharmony_ci    displayParams.fGrContextOptions.fPersistentCache = &fPersistentCache;
391cb93a386Sopenharmony_ci    displayParams.fGrContextOptions.fShaderCacheStrategy =
392cb93a386Sopenharmony_ci            GrContextOptions::ShaderCacheStrategy::kSkSL;
393cb93a386Sopenharmony_ci    displayParams.fGrContextOptions.fShaderErrorHandler = &gShaderErrorHandler;
394cb93a386Sopenharmony_ci    displayParams.fGrContextOptions.fSuppressPrints = true;
395cb93a386Sopenharmony_ci    if (FLAGS_dmsaa) {
396cb93a386Sopenharmony_ci        displayParams.fSurfaceProps = SkSurfaceProps(
397cb93a386Sopenharmony_ci                displayParams.fSurfaceProps.flags() | SkSurfaceProps::kDynamicMSAA_Flag,
398cb93a386Sopenharmony_ci                displayParams.fSurfaceProps.pixelGeometry());
399cb93a386Sopenharmony_ci    }
400cb93a386Sopenharmony_ci    fWindow->setRequestedDisplayParams(displayParams);
401cb93a386Sopenharmony_ci    fDisplay = fWindow->getRequestedDisplayParams();
402cb93a386Sopenharmony_ci    fRefresh = FLAGS_redraw;
403cb93a386Sopenharmony_ci
404cb93a386Sopenharmony_ci    fImGuiLayer.setScaleFactor(fWindow->scaleFactor());
405cb93a386Sopenharmony_ci    fStatsLayer.setDisplayScale((fZoomUI ? 2.0f : 1.0f) * fWindow->scaleFactor());
406cb93a386Sopenharmony_ci
407cb93a386Sopenharmony_ci    // Configure timers
408cb93a386Sopenharmony_ci    fStatsLayer.setActive(FLAGS_stats);
409cb93a386Sopenharmony_ci    fAnimateTimer = fStatsLayer.addTimer("Animate", SK_ColorMAGENTA, 0xffff66ff);
410cb93a386Sopenharmony_ci    fPaintTimer = fStatsLayer.addTimer("Paint", SK_ColorGREEN);
411cb93a386Sopenharmony_ci    fFlushTimer = fStatsLayer.addTimer("Flush", SK_ColorRED, 0xffff6666);
412cb93a386Sopenharmony_ci
413cb93a386Sopenharmony_ci    // register callbacks
414cb93a386Sopenharmony_ci    fCommands.attach(fWindow);
415cb93a386Sopenharmony_ci    fWindow->pushLayer(this);
416cb93a386Sopenharmony_ci    fWindow->pushLayer(&fStatsLayer);
417cb93a386Sopenharmony_ci    fWindow->pushLayer(&fImGuiLayer);
418cb93a386Sopenharmony_ci
419cb93a386Sopenharmony_ci    // add key-bindings
420cb93a386Sopenharmony_ci    fCommands.addCommand(' ', "GUI", "Toggle Debug GUI", [this]() {
421cb93a386Sopenharmony_ci        this->fShowImGuiDebugWindow = !this->fShowImGuiDebugWindow;
422cb93a386Sopenharmony_ci        fWindow->inval();
423cb93a386Sopenharmony_ci    });
424cb93a386Sopenharmony_ci    // Command to jump directly to the slide picker and give it focus
425cb93a386Sopenharmony_ci    fCommands.addCommand('/', "GUI", "Jump to slide picker", [this]() {
426cb93a386Sopenharmony_ci        this->fShowImGuiDebugWindow = true;
427cb93a386Sopenharmony_ci        this->fShowSlidePicker = true;
428cb93a386Sopenharmony_ci        fWindow->inval();
429cb93a386Sopenharmony_ci    });
430cb93a386Sopenharmony_ci    // Alias that to Backspace, to match SampleApp
431cb93a386Sopenharmony_ci    fCommands.addCommand(skui::Key::kBack, "Backspace", "GUI", "Jump to slide picker", [this]() {
432cb93a386Sopenharmony_ci        this->fShowImGuiDebugWindow = true;
433cb93a386Sopenharmony_ci        this->fShowSlidePicker = true;
434cb93a386Sopenharmony_ci        fWindow->inval();
435cb93a386Sopenharmony_ci    });
436cb93a386Sopenharmony_ci    fCommands.addCommand('g', "GUI", "Toggle GUI Demo", [this]() {
437cb93a386Sopenharmony_ci        this->fShowImGuiTestWindow = !this->fShowImGuiTestWindow;
438cb93a386Sopenharmony_ci        fWindow->inval();
439cb93a386Sopenharmony_ci    });
440cb93a386Sopenharmony_ci    fCommands.addCommand('z', "GUI", "Toggle zoom window", [this]() {
441cb93a386Sopenharmony_ci        this->fShowZoomWindow = !this->fShowZoomWindow;
442cb93a386Sopenharmony_ci        fWindow->inval();
443cb93a386Sopenharmony_ci    });
444cb93a386Sopenharmony_ci    fCommands.addCommand('Z', "GUI", "Toggle zoom window state", [this]() {
445cb93a386Sopenharmony_ci        this->fZoomWindowFixed = !this->fZoomWindowFixed;
446cb93a386Sopenharmony_ci        fWindow->inval();
447cb93a386Sopenharmony_ci    });
448cb93a386Sopenharmony_ci    fCommands.addCommand('v', "Swapchain", "Toggle vsync on/off", [this]() {
449cb93a386Sopenharmony_ci        DisplayParams params = fWindow->getRequestedDisplayParams();
450cb93a386Sopenharmony_ci        params.fDisableVsync = !params.fDisableVsync;
451cb93a386Sopenharmony_ci        fWindow->setRequestedDisplayParams(params);
452cb93a386Sopenharmony_ci        this->updateTitle();
453cb93a386Sopenharmony_ci        fWindow->inval();
454cb93a386Sopenharmony_ci    });
455cb93a386Sopenharmony_ci    fCommands.addCommand('V', "Swapchain", "Toggle delayed acquire on/off (Metal only)", [this]() {
456cb93a386Sopenharmony_ci        DisplayParams params = fWindow->getRequestedDisplayParams();
457cb93a386Sopenharmony_ci        params.fDelayDrawableAcquisition = !params.fDelayDrawableAcquisition;
458cb93a386Sopenharmony_ci        fWindow->setRequestedDisplayParams(params);
459cb93a386Sopenharmony_ci        this->updateTitle();
460cb93a386Sopenharmony_ci        fWindow->inval();
461cb93a386Sopenharmony_ci    });
462cb93a386Sopenharmony_ci    fCommands.addCommand('r', "Redraw", "Toggle redraw", [this]() {
463cb93a386Sopenharmony_ci        fRefresh = !fRefresh;
464cb93a386Sopenharmony_ci        fWindow->inval();
465cb93a386Sopenharmony_ci    });
466cb93a386Sopenharmony_ci    fCommands.addCommand('s', "Overlays", "Toggle stats display", [this]() {
467cb93a386Sopenharmony_ci        fStatsLayer.setActive(!fStatsLayer.getActive());
468cb93a386Sopenharmony_ci        fWindow->inval();
469cb93a386Sopenharmony_ci    });
470cb93a386Sopenharmony_ci    fCommands.addCommand('0', "Overlays", "Reset stats", [this]() {
471cb93a386Sopenharmony_ci        fStatsLayer.resetMeasurements();
472cb93a386Sopenharmony_ci        this->updateTitle();
473cb93a386Sopenharmony_ci        fWindow->inval();
474cb93a386Sopenharmony_ci    });
475cb93a386Sopenharmony_ci    fCommands.addCommand('c', "Modes", "Cycle color mode", [this]() {
476cb93a386Sopenharmony_ci        switch (fColorMode) {
477cb93a386Sopenharmony_ci            case ColorMode::kLegacy:
478cb93a386Sopenharmony_ci                this->setColorMode(ColorMode::kColorManaged8888);
479cb93a386Sopenharmony_ci                break;
480cb93a386Sopenharmony_ci            case ColorMode::kColorManaged8888:
481cb93a386Sopenharmony_ci                this->setColorMode(ColorMode::kColorManagedF16);
482cb93a386Sopenharmony_ci                break;
483cb93a386Sopenharmony_ci            case ColorMode::kColorManagedF16:
484cb93a386Sopenharmony_ci                this->setColorMode(ColorMode::kColorManagedF16Norm);
485cb93a386Sopenharmony_ci                break;
486cb93a386Sopenharmony_ci            case ColorMode::kColorManagedF16Norm:
487cb93a386Sopenharmony_ci                this->setColorMode(ColorMode::kLegacy);
488cb93a386Sopenharmony_ci                break;
489cb93a386Sopenharmony_ci        }
490cb93a386Sopenharmony_ci    });
491cb93a386Sopenharmony_ci    fCommands.addCommand('w', "Modes", "Toggle wireframe", [this]() {
492cb93a386Sopenharmony_ci        DisplayParams params = fWindow->getRequestedDisplayParams();
493cb93a386Sopenharmony_ci        params.fGrContextOptions.fWireframeMode = !params.fGrContextOptions.fWireframeMode;
494cb93a386Sopenharmony_ci        fWindow->setRequestedDisplayParams(params);
495cb93a386Sopenharmony_ci        fWindow->inval();
496cb93a386Sopenharmony_ci    });
497cb93a386Sopenharmony_ci    fCommands.addCommand('w', "Modes", "Toggle reduced shaders", [this]() {
498cb93a386Sopenharmony_ci      DisplayParams params = fWindow->getRequestedDisplayParams();
499cb93a386Sopenharmony_ci      params.fGrContextOptions.fReducedShaderVariations =
500cb93a386Sopenharmony_ci              !params.fGrContextOptions.fReducedShaderVariations;
501cb93a386Sopenharmony_ci      fWindow->setRequestedDisplayParams(params);
502cb93a386Sopenharmony_ci      fWindow->inval();
503cb93a386Sopenharmony_ci    });
504cb93a386Sopenharmony_ci    fCommands.addCommand(skui::Key::kRight, "Right", "Navigation", "Next slide", [this]() {
505cb93a386Sopenharmony_ci        this->setCurrentSlide(fCurrentSlide < fSlides.count() - 1 ? fCurrentSlide + 1 : 0);
506cb93a386Sopenharmony_ci    });
507cb93a386Sopenharmony_ci    fCommands.addCommand(skui::Key::kLeft, "Left", "Navigation", "Previous slide", [this]() {
508cb93a386Sopenharmony_ci        this->setCurrentSlide(fCurrentSlide > 0 ? fCurrentSlide - 1 : fSlides.count() - 1);
509cb93a386Sopenharmony_ci    });
510cb93a386Sopenharmony_ci    fCommands.addCommand(skui::Key::kUp, "Up", "Transform", "Zoom in", [this]() {
511cb93a386Sopenharmony_ci        this->changeZoomLevel(1.f / 32.f);
512cb93a386Sopenharmony_ci        fWindow->inval();
513cb93a386Sopenharmony_ci    });
514cb93a386Sopenharmony_ci    fCommands.addCommand(skui::Key::kDown, "Down", "Transform", "Zoom out", [this]() {
515cb93a386Sopenharmony_ci        this->changeZoomLevel(-1.f / 32.f);
516cb93a386Sopenharmony_ci        fWindow->inval();
517cb93a386Sopenharmony_ci    });
518cb93a386Sopenharmony_ci    fCommands.addCommand('d', "Modes", "Change rendering backend", [this]() {
519cb93a386Sopenharmony_ci        sk_app::Window::BackendType newBackend = (sk_app::Window::BackendType)(
520cb93a386Sopenharmony_ci                (fBackendType + 1) % sk_app::Window::kBackendTypeCount);
521cb93a386Sopenharmony_ci        // Switching to and from Vulkan is problematic on Linux so disabled for now
522cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_UNIX) && defined(SK_VULKAN)
523cb93a386Sopenharmony_ci        if (newBackend == sk_app::Window::kVulkan_BackendType) {
524cb93a386Sopenharmony_ci            newBackend = (sk_app::Window::BackendType)((newBackend + 1) %
525cb93a386Sopenharmony_ci                                                       sk_app::Window::kBackendTypeCount);
526cb93a386Sopenharmony_ci        } else if (fBackendType == sk_app::Window::kVulkan_BackendType) {
527cb93a386Sopenharmony_ci            newBackend = sk_app::Window::kVulkan_BackendType;
528cb93a386Sopenharmony_ci        }
529cb93a386Sopenharmony_ci#endif
530cb93a386Sopenharmony_ci        this->setBackend(newBackend);
531cb93a386Sopenharmony_ci    });
532cb93a386Sopenharmony_ci    fCommands.addCommand('K', "IO", "Save slide to SKP", [this]() {
533cb93a386Sopenharmony_ci        fSaveToSKP = true;
534cb93a386Sopenharmony_ci        fWindow->inval();
535cb93a386Sopenharmony_ci    });
536cb93a386Sopenharmony_ci    fCommands.addCommand('&', "Overlays", "Show slide dimensios", [this]() {
537cb93a386Sopenharmony_ci        fShowSlideDimensions = !fShowSlideDimensions;
538cb93a386Sopenharmony_ci        fWindow->inval();
539cb93a386Sopenharmony_ci    });
540cb93a386Sopenharmony_ci    fCommands.addCommand('G', "Modes", "Geometry", [this]() {
541cb93a386Sopenharmony_ci        DisplayParams params = fWindow->getRequestedDisplayParams();
542cb93a386Sopenharmony_ci        uint32_t flags = params.fSurfaceProps.flags();
543cb93a386Sopenharmony_ci        SkPixelGeometry defaultPixelGeometry = fDisplay.fSurfaceProps.pixelGeometry();
544cb93a386Sopenharmony_ci        if (!fDisplayOverrides.fSurfaceProps.fPixelGeometry) {
545cb93a386Sopenharmony_ci            fDisplayOverrides.fSurfaceProps.fPixelGeometry = true;
546cb93a386Sopenharmony_ci            params.fSurfaceProps = SkSurfaceProps(flags, kUnknown_SkPixelGeometry);
547cb93a386Sopenharmony_ci        } else {
548cb93a386Sopenharmony_ci            switch (params.fSurfaceProps.pixelGeometry()) {
549cb93a386Sopenharmony_ci                case kUnknown_SkPixelGeometry:
550cb93a386Sopenharmony_ci                    params.fSurfaceProps = SkSurfaceProps(flags, kRGB_H_SkPixelGeometry);
551cb93a386Sopenharmony_ci                    break;
552cb93a386Sopenharmony_ci                case kRGB_H_SkPixelGeometry:
553cb93a386Sopenharmony_ci                    params.fSurfaceProps = SkSurfaceProps(flags, kBGR_H_SkPixelGeometry);
554cb93a386Sopenharmony_ci                    break;
555cb93a386Sopenharmony_ci                case kBGR_H_SkPixelGeometry:
556cb93a386Sopenharmony_ci                    params.fSurfaceProps = SkSurfaceProps(flags, kRGB_V_SkPixelGeometry);
557cb93a386Sopenharmony_ci                    break;
558cb93a386Sopenharmony_ci                case kRGB_V_SkPixelGeometry:
559cb93a386Sopenharmony_ci                    params.fSurfaceProps = SkSurfaceProps(flags, kBGR_V_SkPixelGeometry);
560cb93a386Sopenharmony_ci                    break;
561cb93a386Sopenharmony_ci                case kBGR_V_SkPixelGeometry:
562cb93a386Sopenharmony_ci                    params.fSurfaceProps = SkSurfaceProps(flags, defaultPixelGeometry);
563cb93a386Sopenharmony_ci                    fDisplayOverrides.fSurfaceProps.fPixelGeometry = false;
564cb93a386Sopenharmony_ci                    break;
565cb93a386Sopenharmony_ci            }
566cb93a386Sopenharmony_ci        }
567cb93a386Sopenharmony_ci        fWindow->setRequestedDisplayParams(params);
568cb93a386Sopenharmony_ci        this->updateTitle();
569cb93a386Sopenharmony_ci        fWindow->inval();
570cb93a386Sopenharmony_ci    });
571cb93a386Sopenharmony_ci    fCommands.addCommand('H', "Font", "Hinting mode", [this]() {
572cb93a386Sopenharmony_ci        if (!fFontOverrides.fHinting) {
573cb93a386Sopenharmony_ci            fFontOverrides.fHinting = true;
574cb93a386Sopenharmony_ci            fFont.setHinting(SkFontHinting::kNone);
575cb93a386Sopenharmony_ci        } else {
576cb93a386Sopenharmony_ci            switch (fFont.getHinting()) {
577cb93a386Sopenharmony_ci                case SkFontHinting::kNone:
578cb93a386Sopenharmony_ci                    fFont.setHinting(SkFontHinting::kSlight);
579cb93a386Sopenharmony_ci                    break;
580cb93a386Sopenharmony_ci                case SkFontHinting::kSlight:
581cb93a386Sopenharmony_ci                    fFont.setHinting(SkFontHinting::kNormal);
582cb93a386Sopenharmony_ci                    break;
583cb93a386Sopenharmony_ci                case SkFontHinting::kNormal:
584cb93a386Sopenharmony_ci                    fFont.setHinting(SkFontHinting::kFull);
585cb93a386Sopenharmony_ci                    break;
586cb93a386Sopenharmony_ci                case SkFontHinting::kFull:
587cb93a386Sopenharmony_ci                    fFont.setHinting(SkFontHinting::kNone);
588cb93a386Sopenharmony_ci                    fFontOverrides.fHinting = false;
589cb93a386Sopenharmony_ci                    break;
590cb93a386Sopenharmony_ci            }
591cb93a386Sopenharmony_ci        }
592cb93a386Sopenharmony_ci        this->updateTitle();
593cb93a386Sopenharmony_ci        fWindow->inval();
594cb93a386Sopenharmony_ci    });
595cb93a386Sopenharmony_ci    fCommands.addCommand('A', "Paint", "Antialias Mode", [this]() {
596cb93a386Sopenharmony_ci        if (!fPaintOverrides.fAntiAlias) {
597cb93a386Sopenharmony_ci            fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Alias;
598cb93a386Sopenharmony_ci            fPaintOverrides.fAntiAlias = true;
599cb93a386Sopenharmony_ci            fPaint.setAntiAlias(false);
600cb93a386Sopenharmony_ci            gSkUseAnalyticAA = gSkForceAnalyticAA = false;
601cb93a386Sopenharmony_ci        } else {
602cb93a386Sopenharmony_ci            fPaint.setAntiAlias(true);
603cb93a386Sopenharmony_ci            switch (fPaintOverrides.fAntiAliasState) {
604cb93a386Sopenharmony_ci                case SkPaintFields::AntiAliasState::Alias:
605cb93a386Sopenharmony_ci                    fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Normal;
606cb93a386Sopenharmony_ci                    gSkUseAnalyticAA = gSkForceAnalyticAA = false;
607cb93a386Sopenharmony_ci                    break;
608cb93a386Sopenharmony_ci                case SkPaintFields::AntiAliasState::Normal:
609cb93a386Sopenharmony_ci                    fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::AnalyticAAEnabled;
610cb93a386Sopenharmony_ci                    gSkUseAnalyticAA = true;
611cb93a386Sopenharmony_ci                    gSkForceAnalyticAA = false;
612cb93a386Sopenharmony_ci                    break;
613cb93a386Sopenharmony_ci                case SkPaintFields::AntiAliasState::AnalyticAAEnabled:
614cb93a386Sopenharmony_ci                    fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::AnalyticAAForced;
615cb93a386Sopenharmony_ci                    gSkUseAnalyticAA = gSkForceAnalyticAA = true;
616cb93a386Sopenharmony_ci                    break;
617cb93a386Sopenharmony_ci                case SkPaintFields::AntiAliasState::AnalyticAAForced:
618cb93a386Sopenharmony_ci                    fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Alias;
619cb93a386Sopenharmony_ci                    fPaintOverrides.fAntiAlias = false;
620cb93a386Sopenharmony_ci                    gSkUseAnalyticAA = fPaintOverrides.fOriginalSkUseAnalyticAA;
621cb93a386Sopenharmony_ci                    gSkForceAnalyticAA = fPaintOverrides.fOriginalSkForceAnalyticAA;
622cb93a386Sopenharmony_ci                    break;
623cb93a386Sopenharmony_ci            }
624cb93a386Sopenharmony_ci        }
625cb93a386Sopenharmony_ci        this->updateTitle();
626cb93a386Sopenharmony_ci        fWindow->inval();
627cb93a386Sopenharmony_ci    });
628cb93a386Sopenharmony_ci    fCommands.addCommand('D', "Modes", "DFT", [this]() {
629cb93a386Sopenharmony_ci        DisplayParams params = fWindow->getRequestedDisplayParams();
630cb93a386Sopenharmony_ci        uint32_t flags = params.fSurfaceProps.flags();
631cb93a386Sopenharmony_ci        flags ^= SkSurfaceProps::kUseDeviceIndependentFonts_Flag;
632cb93a386Sopenharmony_ci        params.fSurfaceProps = SkSurfaceProps(flags, params.fSurfaceProps.pixelGeometry());
633cb93a386Sopenharmony_ci        fWindow->setRequestedDisplayParams(params);
634cb93a386Sopenharmony_ci        this->updateTitle();
635cb93a386Sopenharmony_ci        fWindow->inval();
636cb93a386Sopenharmony_ci    });
637cb93a386Sopenharmony_ci    fCommands.addCommand('L', "Font", "Subpixel Antialias Mode", [this]() {
638cb93a386Sopenharmony_ci        if (!fFontOverrides.fEdging) {
639cb93a386Sopenharmony_ci            fFontOverrides.fEdging = true;
640cb93a386Sopenharmony_ci            fFont.setEdging(SkFont::Edging::kAlias);
641cb93a386Sopenharmony_ci        } else {
642cb93a386Sopenharmony_ci            switch (fFont.getEdging()) {
643cb93a386Sopenharmony_ci                case SkFont::Edging::kAlias:
644cb93a386Sopenharmony_ci                    fFont.setEdging(SkFont::Edging::kAntiAlias);
645cb93a386Sopenharmony_ci                    break;
646cb93a386Sopenharmony_ci                case SkFont::Edging::kAntiAlias:
647cb93a386Sopenharmony_ci                    fFont.setEdging(SkFont::Edging::kSubpixelAntiAlias);
648cb93a386Sopenharmony_ci                    break;
649cb93a386Sopenharmony_ci                case SkFont::Edging::kSubpixelAntiAlias:
650cb93a386Sopenharmony_ci                    fFont.setEdging(SkFont::Edging::kAlias);
651cb93a386Sopenharmony_ci                    fFontOverrides.fEdging = false;
652cb93a386Sopenharmony_ci                    break;
653cb93a386Sopenharmony_ci            }
654cb93a386Sopenharmony_ci        }
655cb93a386Sopenharmony_ci        this->updateTitle();
656cb93a386Sopenharmony_ci        fWindow->inval();
657cb93a386Sopenharmony_ci    });
658cb93a386Sopenharmony_ci    fCommands.addCommand('S', "Font", "Subpixel Position Mode", [this]() {
659cb93a386Sopenharmony_ci        if (!fFontOverrides.fSubpixel) {
660cb93a386Sopenharmony_ci            fFontOverrides.fSubpixel = true;
661cb93a386Sopenharmony_ci            fFont.setSubpixel(false);
662cb93a386Sopenharmony_ci        } else {
663cb93a386Sopenharmony_ci            if (!fFont.isSubpixel()) {
664cb93a386Sopenharmony_ci                fFont.setSubpixel(true);
665cb93a386Sopenharmony_ci            } else {
666cb93a386Sopenharmony_ci                fFontOverrides.fSubpixel = false;
667cb93a386Sopenharmony_ci            }
668cb93a386Sopenharmony_ci        }
669cb93a386Sopenharmony_ci        this->updateTitle();
670cb93a386Sopenharmony_ci        fWindow->inval();
671cb93a386Sopenharmony_ci    });
672cb93a386Sopenharmony_ci    fCommands.addCommand('B', "Font", "Baseline Snapping", [this]() {
673cb93a386Sopenharmony_ci        if (!fFontOverrides.fBaselineSnap) {
674cb93a386Sopenharmony_ci            fFontOverrides.fBaselineSnap = true;
675cb93a386Sopenharmony_ci            fFont.setBaselineSnap(false);
676cb93a386Sopenharmony_ci        } else {
677cb93a386Sopenharmony_ci            if (!fFont.isBaselineSnap()) {
678cb93a386Sopenharmony_ci                fFont.setBaselineSnap(true);
679cb93a386Sopenharmony_ci            } else {
680cb93a386Sopenharmony_ci                fFontOverrides.fBaselineSnap = false;
681cb93a386Sopenharmony_ci            }
682cb93a386Sopenharmony_ci        }
683cb93a386Sopenharmony_ci        this->updateTitle();
684cb93a386Sopenharmony_ci        fWindow->inval();
685cb93a386Sopenharmony_ci    });
686cb93a386Sopenharmony_ci    fCommands.addCommand('p', "Transform", "Toggle Perspective Mode", [this]() {
687cb93a386Sopenharmony_ci        fPerspectiveMode = (kPerspective_Real == fPerspectiveMode) ? kPerspective_Fake
688cb93a386Sopenharmony_ci                                                                   : kPerspective_Real;
689cb93a386Sopenharmony_ci        this->updateTitle();
690cb93a386Sopenharmony_ci        fWindow->inval();
691cb93a386Sopenharmony_ci    });
692cb93a386Sopenharmony_ci    fCommands.addCommand('P', "Transform", "Toggle Perspective", [this]() {
693cb93a386Sopenharmony_ci        fPerspectiveMode = (kPerspective_Off == fPerspectiveMode) ? kPerspective_Real
694cb93a386Sopenharmony_ci                                                                  : kPerspective_Off;
695cb93a386Sopenharmony_ci        this->updateTitle();
696cb93a386Sopenharmony_ci        fWindow->inval();
697cb93a386Sopenharmony_ci    });
698cb93a386Sopenharmony_ci    fCommands.addCommand('a', "Transform", "Toggle Animation", [this]() {
699cb93a386Sopenharmony_ci        fAnimTimer.togglePauseResume();
700cb93a386Sopenharmony_ci    });
701cb93a386Sopenharmony_ci    fCommands.addCommand('u', "GUI", "Zoom UI", [this]() {
702cb93a386Sopenharmony_ci        fZoomUI = !fZoomUI;
703cb93a386Sopenharmony_ci        fStatsLayer.setDisplayScale((fZoomUI ? 2.0f : 1.0f) * fWindow->scaleFactor());
704cb93a386Sopenharmony_ci        fWindow->inval();
705cb93a386Sopenharmony_ci    });
706cb93a386Sopenharmony_ci    fCommands.addCommand('$', "ViaSerialize", "Toggle ViaSerialize", [this]() {
707cb93a386Sopenharmony_ci        fDrawViaSerialize = !fDrawViaSerialize;
708cb93a386Sopenharmony_ci        this->updateTitle();
709cb93a386Sopenharmony_ci        fWindow->inval();
710cb93a386Sopenharmony_ci    });
711cb93a386Sopenharmony_ci    fCommands.addCommand('!', "SkVM", "Toggle SkVM blitter", [this]() {
712cb93a386Sopenharmony_ci        gUseSkVMBlitter = !gUseSkVMBlitter;
713cb93a386Sopenharmony_ci        this->updateTitle();
714cb93a386Sopenharmony_ci        fWindow->inval();
715cb93a386Sopenharmony_ci    });
716cb93a386Sopenharmony_ci    fCommands.addCommand('@', "SkVM", "Toggle SkVM JIT", [this]() {
717cb93a386Sopenharmony_ci        gSkVMAllowJIT = !gSkVMAllowJIT;
718cb93a386Sopenharmony_ci        this->updateTitle();
719cb93a386Sopenharmony_ci        fWindow->inval();
720cb93a386Sopenharmony_ci    });
721cb93a386Sopenharmony_ci
722cb93a386Sopenharmony_ci    // set up slides
723cb93a386Sopenharmony_ci    this->initSlides();
724cb93a386Sopenharmony_ci    if (FLAGS_list) {
725cb93a386Sopenharmony_ci        this->listNames();
726cb93a386Sopenharmony_ci    }
727cb93a386Sopenharmony_ci
728cb93a386Sopenharmony_ci    fPerspectivePoints[0].set(0, 0);
729cb93a386Sopenharmony_ci    fPerspectivePoints[1].set(1, 0);
730cb93a386Sopenharmony_ci    fPerspectivePoints[2].set(0, 1);
731cb93a386Sopenharmony_ci    fPerspectivePoints[3].set(1, 1);
732cb93a386Sopenharmony_ci    fAnimTimer.run();
733cb93a386Sopenharmony_ci
734cb93a386Sopenharmony_ci    auto gamutImage = GetResourceAsImage("images/gamut.png");
735cb93a386Sopenharmony_ci    if (gamutImage) {
736cb93a386Sopenharmony_ci        fImGuiGamutPaint.setShader(gamutImage->makeShader(SkSamplingOptions(SkFilterMode::kLinear)));
737cb93a386Sopenharmony_ci    }
738cb93a386Sopenharmony_ci    fImGuiGamutPaint.setColor(SK_ColorWHITE);
739cb93a386Sopenharmony_ci
740cb93a386Sopenharmony_ci    fWindow->attach(backend_type_for_window(fBackendType));
741cb93a386Sopenharmony_ci    this->setCurrentSlide(this->startupSlide());
742cb93a386Sopenharmony_ci}
743cb93a386Sopenharmony_ci
744cb93a386Sopenharmony_civoid Viewer::initSlides() {
745cb93a386Sopenharmony_ci    using SlideFactory = sk_sp<Slide>(*)(const SkString& name, const SkString& path);
746cb93a386Sopenharmony_ci    static const struct {
747cb93a386Sopenharmony_ci        const char*                            fExtension;
748cb93a386Sopenharmony_ci        const char*                            fDirName;
749cb93a386Sopenharmony_ci        const CommandLineFlags::StringArray&   fFlags;
750cb93a386Sopenharmony_ci        const SlideFactory                     fFactory;
751cb93a386Sopenharmony_ci    } gExternalSlidesInfo[] = {
752cb93a386Sopenharmony_ci        { ".mskp", "mskp-dir", FLAGS_mskps,
753cb93a386Sopenharmony_ci          [](const SkString& name, const SkString& path) -> sk_sp<Slide> {
754cb93a386Sopenharmony_ci            return sk_make_sp<MSKPSlide>(name, path);}
755cb93a386Sopenharmony_ci        },
756cb93a386Sopenharmony_ci        { ".skp", "skp-dir", FLAGS_skps,
757cb93a386Sopenharmony_ci            [](const SkString& name, const SkString& path) -> sk_sp<Slide> {
758cb93a386Sopenharmony_ci                return sk_make_sp<SKPSlide>(name, path);}
759cb93a386Sopenharmony_ci        },
760cb93a386Sopenharmony_ci        { ".jpg", "jpg-dir", FLAGS_jpgs,
761cb93a386Sopenharmony_ci            [](const SkString& name, const SkString& path) -> sk_sp<Slide> {
762cb93a386Sopenharmony_ci                return sk_make_sp<ImageSlide>(name, path);}
763cb93a386Sopenharmony_ci        },
764cb93a386Sopenharmony_ci#if defined(SK_ENABLE_SKOTTIE)
765cb93a386Sopenharmony_ci        { ".json", "skottie-dir", FLAGS_lotties,
766cb93a386Sopenharmony_ci            [](const SkString& name, const SkString& path) -> sk_sp<Slide> {
767cb93a386Sopenharmony_ci                return sk_make_sp<SkottieSlide>(name, path);}
768cb93a386Sopenharmony_ci        },
769cb93a386Sopenharmony_ci#endif
770cb93a386Sopenharmony_ci    #if defined(SK_ENABLE_SKRIVE)
771cb93a386Sopenharmony_ci            { ".flr", "skrive-dir", FLAGS_rives,
772cb93a386Sopenharmony_ci                [](const SkString& name, const SkString& path) -> sk_sp<Slide> {
773cb93a386Sopenharmony_ci                    return sk_make_sp<SkRiveSlide>(name, path);}
774cb93a386Sopenharmony_ci            },
775cb93a386Sopenharmony_ci    #endif
776cb93a386Sopenharmony_ci#if defined(SK_ENABLE_SVG)
777cb93a386Sopenharmony_ci        { ".svg", "svg-dir", FLAGS_svgs,
778cb93a386Sopenharmony_ci            [](const SkString& name, const SkString& path) -> sk_sp<Slide> {
779cb93a386Sopenharmony_ci                return sk_make_sp<SvgSlide>(name, path);}
780cb93a386Sopenharmony_ci        },
781cb93a386Sopenharmony_ci#endif
782cb93a386Sopenharmony_ci    };
783cb93a386Sopenharmony_ci
784cb93a386Sopenharmony_ci    SkTArray<sk_sp<Slide>> dirSlides;
785cb93a386Sopenharmony_ci
786cb93a386Sopenharmony_ci    const auto addSlide =
787cb93a386Sopenharmony_ci            [&](const SkString& name, const SkString& path, const SlideFactory& fact) {
788cb93a386Sopenharmony_ci                if (CommandLineFlags::ShouldSkip(FLAGS_match, name.c_str())) {
789cb93a386Sopenharmony_ci                    return;
790cb93a386Sopenharmony_ci                }
791cb93a386Sopenharmony_ci
792cb93a386Sopenharmony_ci                if (auto slide = fact(name, path)) {
793cb93a386Sopenharmony_ci                    dirSlides.push_back(slide);
794cb93a386Sopenharmony_ci                    fSlides.push_back(std::move(slide));
795cb93a386Sopenharmony_ci                }
796cb93a386Sopenharmony_ci            };
797cb93a386Sopenharmony_ci
798cb93a386Sopenharmony_ci    if (!FLAGS_file.isEmpty()) {
799cb93a386Sopenharmony_ci        // single file mode
800cb93a386Sopenharmony_ci        const SkString file(FLAGS_file[0]);
801cb93a386Sopenharmony_ci
802cb93a386Sopenharmony_ci        if (sk_exists(file.c_str(), kRead_SkFILE_Flag)) {
803cb93a386Sopenharmony_ci            for (const auto& sinfo : gExternalSlidesInfo) {
804cb93a386Sopenharmony_ci                if (file.endsWith(sinfo.fExtension)) {
805cb93a386Sopenharmony_ci                    addSlide(SkOSPath::Basename(file.c_str()), file, sinfo.fFactory);
806cb93a386Sopenharmony_ci                    return;
807cb93a386Sopenharmony_ci                }
808cb93a386Sopenharmony_ci            }
809cb93a386Sopenharmony_ci
810cb93a386Sopenharmony_ci            fprintf(stderr, "Unsupported file type \"%s\"\n", file.c_str());
811cb93a386Sopenharmony_ci        } else {
812cb93a386Sopenharmony_ci            fprintf(stderr, "Cannot read \"%s\"\n", file.c_str());
813cb93a386Sopenharmony_ci        }
814cb93a386Sopenharmony_ci
815cb93a386Sopenharmony_ci        return;
816cb93a386Sopenharmony_ci    }
817cb93a386Sopenharmony_ci
818cb93a386Sopenharmony_ci    // Bisect slide.
819cb93a386Sopenharmony_ci    if (!FLAGS_bisect.isEmpty()) {
820cb93a386Sopenharmony_ci        sk_sp<BisectSlide> bisect = BisectSlide::Create(FLAGS_bisect[0]);
821cb93a386Sopenharmony_ci        if (bisect && !CommandLineFlags::ShouldSkip(FLAGS_match, bisect->getName().c_str())) {
822cb93a386Sopenharmony_ci            if (FLAGS_bisect.count() >= 2) {
823cb93a386Sopenharmony_ci                for (const char* ch = FLAGS_bisect[1]; *ch; ++ch) {
824cb93a386Sopenharmony_ci                    bisect->onChar(*ch);
825cb93a386Sopenharmony_ci                }
826cb93a386Sopenharmony_ci            }
827cb93a386Sopenharmony_ci            fSlides.push_back(std::move(bisect));
828cb93a386Sopenharmony_ci        }
829cb93a386Sopenharmony_ci    }
830cb93a386Sopenharmony_ci
831cb93a386Sopenharmony_ci    // GMs
832cb93a386Sopenharmony_ci    int firstGM = fSlides.count();
833cb93a386Sopenharmony_ci    for (skiagm::GMFactory gmFactory : skiagm::GMRegistry::Range()) {
834cb93a386Sopenharmony_ci        std::unique_ptr<skiagm::GM> gm = gmFactory();
835cb93a386Sopenharmony_ci        if (!CommandLineFlags::ShouldSkip(FLAGS_match, gm->getName())) {
836cb93a386Sopenharmony_ci            sk_sp<Slide> slide(new GMSlide(std::move(gm)));
837cb93a386Sopenharmony_ci            fSlides.push_back(std::move(slide));
838cb93a386Sopenharmony_ci        }
839cb93a386Sopenharmony_ci    }
840cb93a386Sopenharmony_ci    // reverse gms
841cb93a386Sopenharmony_ci    int numGMs = fSlides.count() - firstGM;
842cb93a386Sopenharmony_ci    for (int i = 0; i < numGMs/2; ++i) {
843cb93a386Sopenharmony_ci        std::swap(fSlides[firstGM + i], fSlides[fSlides.count() - i - 1]);
844cb93a386Sopenharmony_ci    }
845cb93a386Sopenharmony_ci
846cb93a386Sopenharmony_ci    // samples
847cb93a386Sopenharmony_ci    for (const SampleFactory factory : SampleRegistry::Range()) {
848cb93a386Sopenharmony_ci        sk_sp<Slide> slide(new SampleSlide(factory));
849cb93a386Sopenharmony_ci        if (!CommandLineFlags::ShouldSkip(FLAGS_match, slide->getName().c_str())) {
850cb93a386Sopenharmony_ci            fSlides.push_back(slide);
851cb93a386Sopenharmony_ci        }
852cb93a386Sopenharmony_ci    }
853cb93a386Sopenharmony_ci
854cb93a386Sopenharmony_ci    // Particle demo
855cb93a386Sopenharmony_ci    {
856cb93a386Sopenharmony_ci        // TODO: Convert this to a sample
857cb93a386Sopenharmony_ci        sk_sp<Slide> slide(new ParticlesSlide());
858cb93a386Sopenharmony_ci        if (!CommandLineFlags::ShouldSkip(FLAGS_match, slide->getName().c_str())) {
859cb93a386Sopenharmony_ci            fSlides.push_back(std::move(slide));
860cb93a386Sopenharmony_ci        }
861cb93a386Sopenharmony_ci    }
862cb93a386Sopenharmony_ci
863cb93a386Sopenharmony_ci    // Runtime shader editor
864cb93a386Sopenharmony_ci    {
865cb93a386Sopenharmony_ci        sk_sp<Slide> slide(new SkSLSlide());
866cb93a386Sopenharmony_ci        if (!CommandLineFlags::ShouldSkip(FLAGS_match, slide->getName().c_str())) {
867cb93a386Sopenharmony_ci            fSlides.push_back(std::move(slide));
868cb93a386Sopenharmony_ci        }
869cb93a386Sopenharmony_ci    }
870cb93a386Sopenharmony_ci
871cb93a386Sopenharmony_ci    for (const auto& info : gExternalSlidesInfo) {
872cb93a386Sopenharmony_ci        for (const auto& flag : info.fFlags) {
873cb93a386Sopenharmony_ci            if (SkStrEndsWith(flag.c_str(), info.fExtension)) {
874cb93a386Sopenharmony_ci                // single file
875cb93a386Sopenharmony_ci                addSlide(SkOSPath::Basename(flag.c_str()), flag, info.fFactory);
876cb93a386Sopenharmony_ci            } else {
877cb93a386Sopenharmony_ci                // directory
878cb93a386Sopenharmony_ci                SkString name;
879cb93a386Sopenharmony_ci                SkTArray<SkString> sortedFilenames;
880cb93a386Sopenharmony_ci                SkOSFile::Iter it(flag.c_str(), info.fExtension);
881cb93a386Sopenharmony_ci                while (it.next(&name)) {
882cb93a386Sopenharmony_ci                    sortedFilenames.push_back(name);
883cb93a386Sopenharmony_ci                }
884cb93a386Sopenharmony_ci                if (sortedFilenames.count()) {
885cb93a386Sopenharmony_ci                    SkTQSort(sortedFilenames.begin(), sortedFilenames.end(),
886cb93a386Sopenharmony_ci                             [](const SkString& a, const SkString& b) {
887cb93a386Sopenharmony_ci                                 return strcmp(a.c_str(), b.c_str()) < 0;
888cb93a386Sopenharmony_ci                             });
889cb93a386Sopenharmony_ci                }
890cb93a386Sopenharmony_ci                for (const SkString& filename : sortedFilenames) {
891cb93a386Sopenharmony_ci                    addSlide(filename, SkOSPath::Join(flag.c_str(), filename.c_str()),
892cb93a386Sopenharmony_ci                             info.fFactory);
893cb93a386Sopenharmony_ci                }
894cb93a386Sopenharmony_ci            }
895cb93a386Sopenharmony_ci            if (!dirSlides.empty()) {
896cb93a386Sopenharmony_ci                fSlides.push_back(
897cb93a386Sopenharmony_ci                    sk_make_sp<SlideDir>(SkStringPrintf("%s[%s]", info.fDirName, flag.c_str()),
898cb93a386Sopenharmony_ci                                         std::move(dirSlides)));
899cb93a386Sopenharmony_ci                dirSlides.reset();  // NOLINT(bugprone-use-after-move)
900cb93a386Sopenharmony_ci            }
901cb93a386Sopenharmony_ci        }
902cb93a386Sopenharmony_ci    }
903cb93a386Sopenharmony_ci
904cb93a386Sopenharmony_ci    if (!fSlides.count()) {
905cb93a386Sopenharmony_ci        sk_sp<Slide> slide(new NullSlide());
906cb93a386Sopenharmony_ci        fSlides.push_back(std::move(slide));
907cb93a386Sopenharmony_ci    }
908cb93a386Sopenharmony_ci}
909cb93a386Sopenharmony_ci
910cb93a386Sopenharmony_ci
911cb93a386Sopenharmony_ciViewer::~Viewer() {
912cb93a386Sopenharmony_ci    for(auto& slide : fSlides) {
913cb93a386Sopenharmony_ci        slide->gpuTeardown();
914cb93a386Sopenharmony_ci    }
915cb93a386Sopenharmony_ci
916cb93a386Sopenharmony_ci    fWindow->detach();
917cb93a386Sopenharmony_ci    delete fWindow;
918cb93a386Sopenharmony_ci}
919cb93a386Sopenharmony_ci
920cb93a386Sopenharmony_cistruct SkPaintTitleUpdater {
921cb93a386Sopenharmony_ci    SkPaintTitleUpdater(SkString* title) : fTitle(title), fCount(0) {}
922cb93a386Sopenharmony_ci    void append(const char* s) {
923cb93a386Sopenharmony_ci        if (fCount == 0) {
924cb93a386Sopenharmony_ci            fTitle->append(" {");
925cb93a386Sopenharmony_ci        } else {
926cb93a386Sopenharmony_ci            fTitle->append(", ");
927cb93a386Sopenharmony_ci        }
928cb93a386Sopenharmony_ci        fTitle->append(s);
929cb93a386Sopenharmony_ci        ++fCount;
930cb93a386Sopenharmony_ci    }
931cb93a386Sopenharmony_ci    void done() {
932cb93a386Sopenharmony_ci        if (fCount > 0) {
933cb93a386Sopenharmony_ci            fTitle->append("}");
934cb93a386Sopenharmony_ci        }
935cb93a386Sopenharmony_ci    }
936cb93a386Sopenharmony_ci    SkString* fTitle;
937cb93a386Sopenharmony_ci    int fCount;
938cb93a386Sopenharmony_ci};
939cb93a386Sopenharmony_ci
940cb93a386Sopenharmony_civoid Viewer::updateTitle() {
941cb93a386Sopenharmony_ci    if (!fWindow) {
942cb93a386Sopenharmony_ci        return;
943cb93a386Sopenharmony_ci    }
944cb93a386Sopenharmony_ci    if (fWindow->sampleCount() < 1) {
945cb93a386Sopenharmony_ci        return; // Surface hasn't been created yet.
946cb93a386Sopenharmony_ci    }
947cb93a386Sopenharmony_ci
948cb93a386Sopenharmony_ci    SkString title("Viewer: ");
949cb93a386Sopenharmony_ci    title.append(fSlides[fCurrentSlide]->getName());
950cb93a386Sopenharmony_ci
951cb93a386Sopenharmony_ci    if (gSkUseAnalyticAA) {
952cb93a386Sopenharmony_ci        if (gSkForceAnalyticAA) {
953cb93a386Sopenharmony_ci            title.append(" <FAAA>");
954cb93a386Sopenharmony_ci        } else {
955cb93a386Sopenharmony_ci            title.append(" <AAA>");
956cb93a386Sopenharmony_ci        }
957cb93a386Sopenharmony_ci    }
958cb93a386Sopenharmony_ci    if (fDrawViaSerialize) {
959cb93a386Sopenharmony_ci        title.append(" <serialize>");
960cb93a386Sopenharmony_ci    }
961cb93a386Sopenharmony_ci    if (gUseSkVMBlitter) {
962cb93a386Sopenharmony_ci        title.append(" <SkVMBlitter>");
963cb93a386Sopenharmony_ci    }
964cb93a386Sopenharmony_ci    if (!gSkVMAllowJIT) {
965cb93a386Sopenharmony_ci        title.append(" <SkVM interpreter>");
966cb93a386Sopenharmony_ci    }
967cb93a386Sopenharmony_ci
968cb93a386Sopenharmony_ci    SkPaintTitleUpdater paintTitle(&title);
969cb93a386Sopenharmony_ci    auto paintFlag = [this, &paintTitle](bool SkPaintFields::* flag,
970cb93a386Sopenharmony_ci                                         bool (SkPaint::* isFlag)() const,
971cb93a386Sopenharmony_ci                                         const char* on, const char* off)
972cb93a386Sopenharmony_ci    {
973cb93a386Sopenharmony_ci        if (fPaintOverrides.*flag) {
974cb93a386Sopenharmony_ci            paintTitle.append((fPaint.*isFlag)() ? on : off);
975cb93a386Sopenharmony_ci        }
976cb93a386Sopenharmony_ci    };
977cb93a386Sopenharmony_ci
978cb93a386Sopenharmony_ci    auto fontFlag = [this, &paintTitle](bool SkFontFields::* flag, bool (SkFont::* isFlag)() const,
979cb93a386Sopenharmony_ci                                        const char* on, const char* off)
980cb93a386Sopenharmony_ci    {
981cb93a386Sopenharmony_ci        if (fFontOverrides.*flag) {
982cb93a386Sopenharmony_ci            paintTitle.append((fFont.*isFlag)() ? on : off);
983cb93a386Sopenharmony_ci        }
984cb93a386Sopenharmony_ci    };
985cb93a386Sopenharmony_ci
986cb93a386Sopenharmony_ci    paintFlag(&SkPaintFields::fAntiAlias, &SkPaint::isAntiAlias, "Antialias", "Alias");
987cb93a386Sopenharmony_ci    paintFlag(&SkPaintFields::fDither, &SkPaint::isDither, "DITHER", "No Dither");
988cb93a386Sopenharmony_ci
989cb93a386Sopenharmony_ci    fontFlag(&SkFontFields::fForceAutoHinting, &SkFont::isForceAutoHinting,
990cb93a386Sopenharmony_ci             "Force Autohint", "No Force Autohint");
991cb93a386Sopenharmony_ci    fontFlag(&SkFontFields::fEmbolden, &SkFont::isEmbolden, "Fake Bold", "No Fake Bold");
992cb93a386Sopenharmony_ci    fontFlag(&SkFontFields::fBaselineSnap, &SkFont::isBaselineSnap, "BaseSnap", "No BaseSnap");
993cb93a386Sopenharmony_ci    fontFlag(&SkFontFields::fLinearMetrics, &SkFont::isLinearMetrics,
994cb93a386Sopenharmony_ci             "Linear Metrics", "Non-Linear Metrics");
995cb93a386Sopenharmony_ci    fontFlag(&SkFontFields::fEmbeddedBitmaps, &SkFont::isEmbeddedBitmaps,
996cb93a386Sopenharmony_ci             "Bitmap Text", "No Bitmap Text");
997cb93a386Sopenharmony_ci    fontFlag(&SkFontFields::fSubpixel, &SkFont::isSubpixel, "Subpixel Text", "Pixel Text");
998cb93a386Sopenharmony_ci
999cb93a386Sopenharmony_ci    if (fFontOverrides.fEdging) {
1000cb93a386Sopenharmony_ci        switch (fFont.getEdging()) {
1001cb93a386Sopenharmony_ci            case SkFont::Edging::kAlias:
1002cb93a386Sopenharmony_ci                paintTitle.append("Alias Text");
1003cb93a386Sopenharmony_ci                break;
1004cb93a386Sopenharmony_ci            case SkFont::Edging::kAntiAlias:
1005cb93a386Sopenharmony_ci                paintTitle.append("Antialias Text");
1006cb93a386Sopenharmony_ci                break;
1007cb93a386Sopenharmony_ci            case SkFont::Edging::kSubpixelAntiAlias:
1008cb93a386Sopenharmony_ci                paintTitle.append("Subpixel Antialias Text");
1009cb93a386Sopenharmony_ci                break;
1010cb93a386Sopenharmony_ci        }
1011cb93a386Sopenharmony_ci    }
1012cb93a386Sopenharmony_ci
1013cb93a386Sopenharmony_ci    if (fFontOverrides.fHinting) {
1014cb93a386Sopenharmony_ci        switch (fFont.getHinting()) {
1015cb93a386Sopenharmony_ci            case SkFontHinting::kNone:
1016cb93a386Sopenharmony_ci                paintTitle.append("No Hinting");
1017cb93a386Sopenharmony_ci                break;
1018cb93a386Sopenharmony_ci            case SkFontHinting::kSlight:
1019cb93a386Sopenharmony_ci                paintTitle.append("Slight Hinting");
1020cb93a386Sopenharmony_ci                break;
1021cb93a386Sopenharmony_ci            case SkFontHinting::kNormal:
1022cb93a386Sopenharmony_ci                paintTitle.append("Normal Hinting");
1023cb93a386Sopenharmony_ci                break;
1024cb93a386Sopenharmony_ci            case SkFontHinting::kFull:
1025cb93a386Sopenharmony_ci                paintTitle.append("Full Hinting");
1026cb93a386Sopenharmony_ci                break;
1027cb93a386Sopenharmony_ci        }
1028cb93a386Sopenharmony_ci    }
1029cb93a386Sopenharmony_ci    paintTitle.done();
1030cb93a386Sopenharmony_ci
1031cb93a386Sopenharmony_ci    switch (fColorMode) {
1032cb93a386Sopenharmony_ci        case ColorMode::kLegacy:
1033cb93a386Sopenharmony_ci            title.append(" Legacy 8888");
1034cb93a386Sopenharmony_ci            break;
1035cb93a386Sopenharmony_ci        case ColorMode::kColorManaged8888:
1036cb93a386Sopenharmony_ci            title.append(" ColorManaged 8888");
1037cb93a386Sopenharmony_ci            break;
1038cb93a386Sopenharmony_ci        case ColorMode::kColorManagedF16:
1039cb93a386Sopenharmony_ci            title.append(" ColorManaged F16");
1040cb93a386Sopenharmony_ci            break;
1041cb93a386Sopenharmony_ci        case ColorMode::kColorManagedF16Norm:
1042cb93a386Sopenharmony_ci            title.append(" ColorManaged F16 Norm");
1043cb93a386Sopenharmony_ci            break;
1044cb93a386Sopenharmony_ci    }
1045cb93a386Sopenharmony_ci
1046cb93a386Sopenharmony_ci    if (ColorMode::kLegacy != fColorMode) {
1047cb93a386Sopenharmony_ci        int curPrimaries = -1;
1048cb93a386Sopenharmony_ci        for (size_t i = 0; i < SK_ARRAY_COUNT(gNamedPrimaries); ++i) {
1049cb93a386Sopenharmony_ci            if (primaries_equal(*gNamedPrimaries[i].fPrimaries, fColorSpacePrimaries)) {
1050cb93a386Sopenharmony_ci                curPrimaries = i;
1051cb93a386Sopenharmony_ci                break;
1052cb93a386Sopenharmony_ci            }
1053cb93a386Sopenharmony_ci        }
1054cb93a386Sopenharmony_ci        title.appendf(" %s Gamma %f",
1055cb93a386Sopenharmony_ci                      curPrimaries >= 0 ? gNamedPrimaries[curPrimaries].fName : "Custom",
1056cb93a386Sopenharmony_ci                      fColorSpaceTransferFn.g);
1057cb93a386Sopenharmony_ci    }
1058cb93a386Sopenharmony_ci
1059cb93a386Sopenharmony_ci    const DisplayParams& params = fWindow->getRequestedDisplayParams();
1060cb93a386Sopenharmony_ci    if (fDisplayOverrides.fSurfaceProps.fPixelGeometry) {
1061cb93a386Sopenharmony_ci        switch (params.fSurfaceProps.pixelGeometry()) {
1062cb93a386Sopenharmony_ci            case kUnknown_SkPixelGeometry:
1063cb93a386Sopenharmony_ci                title.append( " Flat");
1064cb93a386Sopenharmony_ci                break;
1065cb93a386Sopenharmony_ci            case kRGB_H_SkPixelGeometry:
1066cb93a386Sopenharmony_ci                title.append( " RGB");
1067cb93a386Sopenharmony_ci                break;
1068cb93a386Sopenharmony_ci            case kBGR_H_SkPixelGeometry:
1069cb93a386Sopenharmony_ci                title.append( " BGR");
1070cb93a386Sopenharmony_ci                break;
1071cb93a386Sopenharmony_ci            case kRGB_V_SkPixelGeometry:
1072cb93a386Sopenharmony_ci                title.append( " RGBV");
1073cb93a386Sopenharmony_ci                break;
1074cb93a386Sopenharmony_ci            case kBGR_V_SkPixelGeometry:
1075cb93a386Sopenharmony_ci                title.append( " BGRV");
1076cb93a386Sopenharmony_ci                break;
1077cb93a386Sopenharmony_ci        }
1078cb93a386Sopenharmony_ci    }
1079cb93a386Sopenharmony_ci
1080cb93a386Sopenharmony_ci    if (params.fSurfaceProps.isUseDeviceIndependentFonts()) {
1081cb93a386Sopenharmony_ci        title.append(" DFT");
1082cb93a386Sopenharmony_ci    }
1083cb93a386Sopenharmony_ci
1084cb93a386Sopenharmony_ci    title.append(" [");
1085cb93a386Sopenharmony_ci    title.append(kBackendTypeStrings[fBackendType]);
1086cb93a386Sopenharmony_ci    int msaa = fWindow->sampleCount();
1087cb93a386Sopenharmony_ci    if (msaa > 1) {
1088cb93a386Sopenharmony_ci        title.appendf(" MSAA: %i", msaa);
1089cb93a386Sopenharmony_ci    }
1090cb93a386Sopenharmony_ci    title.append("]");
1091cb93a386Sopenharmony_ci
1092cb93a386Sopenharmony_ci    GpuPathRenderers pr = fWindow->getRequestedDisplayParams().fGrContextOptions.fGpuPathRenderers;
1093cb93a386Sopenharmony_ci    if (GpuPathRenderers::kDefault != pr) {
1094cb93a386Sopenharmony_ci        title.appendf(" [Path renderer: %s]", gPathRendererNames[pr].c_str());
1095cb93a386Sopenharmony_ci    }
1096cb93a386Sopenharmony_ci
1097cb93a386Sopenharmony_ci    if (kPerspective_Real == fPerspectiveMode) {
1098cb93a386Sopenharmony_ci        title.append(" Perpsective (Real)");
1099cb93a386Sopenharmony_ci    } else if (kPerspective_Fake == fPerspectiveMode) {
1100cb93a386Sopenharmony_ci        title.append(" Perspective (Fake)");
1101cb93a386Sopenharmony_ci    }
1102cb93a386Sopenharmony_ci
1103cb93a386Sopenharmony_ci    fWindow->setTitle(title.c_str());
1104cb93a386Sopenharmony_ci}
1105cb93a386Sopenharmony_ci
1106cb93a386Sopenharmony_ciint Viewer::startupSlide() const {
1107cb93a386Sopenharmony_ci
1108cb93a386Sopenharmony_ci    if (!FLAGS_slide.isEmpty()) {
1109cb93a386Sopenharmony_ci        int count = fSlides.count();
1110cb93a386Sopenharmony_ci        for (int i = 0; i < count; i++) {
1111cb93a386Sopenharmony_ci            if (fSlides[i]->getName().equals(FLAGS_slide[0])) {
1112cb93a386Sopenharmony_ci                return i;
1113cb93a386Sopenharmony_ci            }
1114cb93a386Sopenharmony_ci        }
1115cb93a386Sopenharmony_ci
1116cb93a386Sopenharmony_ci        fprintf(stderr, "Unknown slide \"%s\"\n", FLAGS_slide[0]);
1117cb93a386Sopenharmony_ci        this->listNames();
1118cb93a386Sopenharmony_ci    }
1119cb93a386Sopenharmony_ci
1120cb93a386Sopenharmony_ci    return 0;
1121cb93a386Sopenharmony_ci}
1122cb93a386Sopenharmony_ci
1123cb93a386Sopenharmony_civoid Viewer::listNames() const {
1124cb93a386Sopenharmony_ci    SkDebugf("All Slides:\n");
1125cb93a386Sopenharmony_ci    for (const auto& slide : fSlides) {
1126cb93a386Sopenharmony_ci        SkDebugf("    %s\n", slide->getName().c_str());
1127cb93a386Sopenharmony_ci    }
1128cb93a386Sopenharmony_ci}
1129cb93a386Sopenharmony_ci
1130cb93a386Sopenharmony_civoid Viewer::setCurrentSlide(int slide) {
1131cb93a386Sopenharmony_ci    SkASSERT(slide >= 0 && slide < fSlides.count());
1132cb93a386Sopenharmony_ci
1133cb93a386Sopenharmony_ci    if (slide == fCurrentSlide) {
1134cb93a386Sopenharmony_ci        return;
1135cb93a386Sopenharmony_ci    }
1136cb93a386Sopenharmony_ci
1137cb93a386Sopenharmony_ci    if (fCurrentSlide >= 0) {
1138cb93a386Sopenharmony_ci        fSlides[fCurrentSlide]->unload();
1139cb93a386Sopenharmony_ci    }
1140cb93a386Sopenharmony_ci
1141cb93a386Sopenharmony_ci    SkScalar scaleFactor = 1.0;
1142cb93a386Sopenharmony_ci    if (fApplyBackingScale) {
1143cb93a386Sopenharmony_ci        scaleFactor = fWindow->scaleFactor();
1144cb93a386Sopenharmony_ci    }
1145cb93a386Sopenharmony_ci    fSlides[slide]->load(SkIntToScalar(fWindow->width()) / scaleFactor,
1146cb93a386Sopenharmony_ci                         SkIntToScalar(fWindow->height()) / scaleFactor);
1147cb93a386Sopenharmony_ci    fCurrentSlide = slide;
1148cb93a386Sopenharmony_ci    this->setupCurrentSlide();
1149cb93a386Sopenharmony_ci}
1150cb93a386Sopenharmony_ci
1151cb93a386Sopenharmony_civoid Viewer::setupCurrentSlide() {
1152cb93a386Sopenharmony_ci    if (fCurrentSlide >= 0) {
1153cb93a386Sopenharmony_ci        // prepare dimensions for image slides
1154cb93a386Sopenharmony_ci        fGesture.resetTouchState();
1155cb93a386Sopenharmony_ci        fDefaultMatrix.reset();
1156cb93a386Sopenharmony_ci
1157cb93a386Sopenharmony_ci        const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
1158cb93a386Sopenharmony_ci        const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
1159cb93a386Sopenharmony_ci        const SkRect windowRect = SkRect::MakeIWH(fWindow->width(), fWindow->height());
1160cb93a386Sopenharmony_ci
1161cb93a386Sopenharmony_ci        // Start with a matrix that scales the slide to the available screen space
1162cb93a386Sopenharmony_ci        if (fWindow->scaleContentToFit()) {
1163cb93a386Sopenharmony_ci            if (windowRect.width() > 0 && windowRect.height() > 0) {
1164cb93a386Sopenharmony_ci                fDefaultMatrix = SkMatrix::RectToRect(slideBounds, windowRect,
1165cb93a386Sopenharmony_ci                                                      SkMatrix::kStart_ScaleToFit);
1166cb93a386Sopenharmony_ci            }
1167cb93a386Sopenharmony_ci        }
1168cb93a386Sopenharmony_ci
1169cb93a386Sopenharmony_ci        // Prevent the user from dragging content so far outside the window they can't find it again
1170cb93a386Sopenharmony_ci        fGesture.setTransLimit(slideBounds, windowRect, this->computePreTouchMatrix());
1171cb93a386Sopenharmony_ci
1172cb93a386Sopenharmony_ci        this->updateTitle();
1173cb93a386Sopenharmony_ci        this->updateUIState();
1174cb93a386Sopenharmony_ci
1175cb93a386Sopenharmony_ci        fStatsLayer.resetMeasurements();
1176cb93a386Sopenharmony_ci
1177cb93a386Sopenharmony_ci        fWindow->inval();
1178cb93a386Sopenharmony_ci    }
1179cb93a386Sopenharmony_ci}
1180cb93a386Sopenharmony_ci
1181cb93a386Sopenharmony_ci#define MAX_ZOOM_LEVEL  8.0f
1182cb93a386Sopenharmony_ci#define MIN_ZOOM_LEVEL  -8.0f
1183cb93a386Sopenharmony_ci
1184cb93a386Sopenharmony_civoid Viewer::changeZoomLevel(float delta) {
1185cb93a386Sopenharmony_ci    fZoomLevel += delta;
1186cb93a386Sopenharmony_ci    fZoomLevel = SkTPin(fZoomLevel, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL);
1187cb93a386Sopenharmony_ci    this->preTouchMatrixChanged();
1188cb93a386Sopenharmony_ci}
1189cb93a386Sopenharmony_ci
1190cb93a386Sopenharmony_civoid Viewer::preTouchMatrixChanged() {
1191cb93a386Sopenharmony_ci    // Update the trans limit as the transform changes.
1192cb93a386Sopenharmony_ci    const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
1193cb93a386Sopenharmony_ci    const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
1194cb93a386Sopenharmony_ci    const SkRect windowRect = SkRect::MakeIWH(fWindow->width(), fWindow->height());
1195cb93a386Sopenharmony_ci    fGesture.setTransLimit(slideBounds, windowRect, this->computePreTouchMatrix());
1196cb93a386Sopenharmony_ci}
1197cb93a386Sopenharmony_ci
1198cb93a386Sopenharmony_ciSkMatrix Viewer::computePerspectiveMatrix() {
1199cb93a386Sopenharmony_ci    SkScalar w = fWindow->width(), h = fWindow->height();
1200cb93a386Sopenharmony_ci    SkPoint orthoPts[4] = { { 0, 0 }, { w, 0 }, { 0, h }, { w, h } };
1201cb93a386Sopenharmony_ci    SkPoint perspPts[4] = {
1202cb93a386Sopenharmony_ci        { fPerspectivePoints[0].fX * w, fPerspectivePoints[0].fY * h },
1203cb93a386Sopenharmony_ci        { fPerspectivePoints[1].fX * w, fPerspectivePoints[1].fY * h },
1204cb93a386Sopenharmony_ci        { fPerspectivePoints[2].fX * w, fPerspectivePoints[2].fY * h },
1205cb93a386Sopenharmony_ci        { fPerspectivePoints[3].fX * w, fPerspectivePoints[3].fY * h }
1206cb93a386Sopenharmony_ci    };
1207cb93a386Sopenharmony_ci    SkMatrix m;
1208cb93a386Sopenharmony_ci    m.setPolyToPoly(orthoPts, perspPts, 4);
1209cb93a386Sopenharmony_ci    return m;
1210cb93a386Sopenharmony_ci}
1211cb93a386Sopenharmony_ci
1212cb93a386Sopenharmony_ciSkMatrix Viewer::computePreTouchMatrix() {
1213cb93a386Sopenharmony_ci    SkMatrix m = fDefaultMatrix;
1214cb93a386Sopenharmony_ci
1215cb93a386Sopenharmony_ci    SkScalar zoomScale = exp(fZoomLevel);
1216cb93a386Sopenharmony_ci    if (fApplyBackingScale) {
1217cb93a386Sopenharmony_ci        zoomScale *= fWindow->scaleFactor();
1218cb93a386Sopenharmony_ci    }
1219cb93a386Sopenharmony_ci    m.preTranslate((fOffset.x() - 0.5f) * 2.0f, (fOffset.y() - 0.5f) * 2.0f);
1220cb93a386Sopenharmony_ci    m.preScale(zoomScale, zoomScale);
1221cb93a386Sopenharmony_ci
1222cb93a386Sopenharmony_ci    const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
1223cb93a386Sopenharmony_ci    m.preRotate(fRotation, slideSize.width() * 0.5f, slideSize.height() * 0.5f);
1224cb93a386Sopenharmony_ci
1225cb93a386Sopenharmony_ci    if (kPerspective_Real == fPerspectiveMode) {
1226cb93a386Sopenharmony_ci        SkMatrix persp = this->computePerspectiveMatrix();
1227cb93a386Sopenharmony_ci        m.postConcat(persp);
1228cb93a386Sopenharmony_ci    }
1229cb93a386Sopenharmony_ci
1230cb93a386Sopenharmony_ci    return m;
1231cb93a386Sopenharmony_ci}
1232cb93a386Sopenharmony_ci
1233cb93a386Sopenharmony_ciSkMatrix Viewer::computeMatrix() {
1234cb93a386Sopenharmony_ci    SkMatrix m = fGesture.localM();
1235cb93a386Sopenharmony_ci    m.preConcat(fGesture.globalM());
1236cb93a386Sopenharmony_ci    m.preConcat(this->computePreTouchMatrix());
1237cb93a386Sopenharmony_ci    return m;
1238cb93a386Sopenharmony_ci}
1239cb93a386Sopenharmony_ci
1240cb93a386Sopenharmony_civoid Viewer::setBackend(sk_app::Window::BackendType backendType) {
1241cb93a386Sopenharmony_ci    fPersistentCache.reset();
1242cb93a386Sopenharmony_ci    fCachedShaders.reset();
1243cb93a386Sopenharmony_ci    fBackendType = backendType;
1244cb93a386Sopenharmony_ci
1245cb93a386Sopenharmony_ci    // The active context is going away in 'detach'
1246cb93a386Sopenharmony_ci    for(auto& slide : fSlides) {
1247cb93a386Sopenharmony_ci        slide->gpuTeardown();
1248cb93a386Sopenharmony_ci    }
1249cb93a386Sopenharmony_ci
1250cb93a386Sopenharmony_ci    fWindow->detach();
1251cb93a386Sopenharmony_ci
1252cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_WIN)
1253cb93a386Sopenharmony_ci    // Switching between OpenGL, Vulkan, and ANGLE in the same window is problematic at this point
1254cb93a386Sopenharmony_ci    // on Windows, so we just delete the window and recreate it.
1255cb93a386Sopenharmony_ci    DisplayParams params = fWindow->getRequestedDisplayParams();
1256cb93a386Sopenharmony_ci    delete fWindow;
1257cb93a386Sopenharmony_ci    fWindow = Window::CreateNativeWindow(nullptr);
1258cb93a386Sopenharmony_ci
1259cb93a386Sopenharmony_ci    // re-register callbacks
1260cb93a386Sopenharmony_ci    fCommands.attach(fWindow);
1261cb93a386Sopenharmony_ci    fWindow->pushLayer(this);
1262cb93a386Sopenharmony_ci    fWindow->pushLayer(&fStatsLayer);
1263cb93a386Sopenharmony_ci    fWindow->pushLayer(&fImGuiLayer);
1264cb93a386Sopenharmony_ci
1265cb93a386Sopenharmony_ci    // Don't allow the window to re-attach. If we're in MSAA mode, the params we grabbed above
1266cb93a386Sopenharmony_ci    // will still include our correct sample count. But the re-created fWindow will lose that
1267cb93a386Sopenharmony_ci    // information. On Windows, we need to re-create the window when changing sample count,
1268cb93a386Sopenharmony_ci    // so we'll incorrectly detect that situation, then re-initialize the window in GL mode,
1269cb93a386Sopenharmony_ci    // rendering this tear-down step pointless (and causing the Vulkan window context to fail
1270cb93a386Sopenharmony_ci    // as if we had never changed windows at all).
1271cb93a386Sopenharmony_ci    fWindow->setRequestedDisplayParams(params, false);
1272cb93a386Sopenharmony_ci#endif
1273cb93a386Sopenharmony_ci
1274cb93a386Sopenharmony_ci    fWindow->attach(backend_type_for_window(fBackendType));
1275cb93a386Sopenharmony_ci}
1276cb93a386Sopenharmony_ci
1277cb93a386Sopenharmony_civoid Viewer::setColorMode(ColorMode colorMode) {
1278cb93a386Sopenharmony_ci    fColorMode = colorMode;
1279cb93a386Sopenharmony_ci    this->updateTitle();
1280cb93a386Sopenharmony_ci    fWindow->inval();
1281cb93a386Sopenharmony_ci}
1282cb93a386Sopenharmony_ci
1283cb93a386Sopenharmony_ciclass OveridePaintFilterCanvas : public SkPaintFilterCanvas {
1284cb93a386Sopenharmony_cipublic:
1285cb93a386Sopenharmony_ci    OveridePaintFilterCanvas(SkCanvas* canvas,
1286cb93a386Sopenharmony_ci                             SkPaint* paint, Viewer::SkPaintFields* pfields,
1287cb93a386Sopenharmony_ci                             SkFont* font, Viewer::SkFontFields* ffields)
1288cb93a386Sopenharmony_ci        : SkPaintFilterCanvas(canvas)
1289cb93a386Sopenharmony_ci        , fPaint(paint)
1290cb93a386Sopenharmony_ci        , fPaintOverrides(pfields)
1291cb93a386Sopenharmony_ci        , fFont(font)
1292cb93a386Sopenharmony_ci        , fFontOverrides(ffields) {
1293cb93a386Sopenharmony_ci    }
1294cb93a386Sopenharmony_ci
1295cb93a386Sopenharmony_ci    const SkTextBlob* filterTextBlob(const SkPaint& paint,
1296cb93a386Sopenharmony_ci                                     const SkTextBlob* blob,
1297cb93a386Sopenharmony_ci                                     sk_sp<SkTextBlob>* cache) {
1298cb93a386Sopenharmony_ci        bool blobWillChange = false;
1299cb93a386Sopenharmony_ci        for (SkTextBlobRunIterator it(blob); !it.done(); it.next()) {
1300cb93a386Sopenharmony_ci            SkTCopyOnFirstWrite<SkFont> filteredFont(it.font());
1301cb93a386Sopenharmony_ci            bool shouldDraw = this->filterFont(&filteredFont);
1302cb93a386Sopenharmony_ci            if (it.font() != *filteredFont || !shouldDraw) {
1303cb93a386Sopenharmony_ci                blobWillChange = true;
1304cb93a386Sopenharmony_ci                break;
1305cb93a386Sopenharmony_ci            }
1306cb93a386Sopenharmony_ci        }
1307cb93a386Sopenharmony_ci        if (!blobWillChange) {
1308cb93a386Sopenharmony_ci            return blob;
1309cb93a386Sopenharmony_ci        }
1310cb93a386Sopenharmony_ci
1311cb93a386Sopenharmony_ci        SkTextBlobBuilder builder;
1312cb93a386Sopenharmony_ci        for (SkTextBlobRunIterator it(blob); !it.done(); it.next()) {
1313cb93a386Sopenharmony_ci            SkTCopyOnFirstWrite<SkFont> filteredFont(it.font());
1314cb93a386Sopenharmony_ci            bool shouldDraw = this->filterFont(&filteredFont);
1315cb93a386Sopenharmony_ci            if (!shouldDraw) {
1316cb93a386Sopenharmony_ci                continue;
1317cb93a386Sopenharmony_ci            }
1318cb93a386Sopenharmony_ci
1319cb93a386Sopenharmony_ci            SkFont font = *filteredFont;
1320cb93a386Sopenharmony_ci
1321cb93a386Sopenharmony_ci            const SkTextBlobBuilder::RunBuffer& runBuffer
1322cb93a386Sopenharmony_ci                = it.positioning() == SkTextBlobRunIterator::kDefault_Positioning
1323cb93a386Sopenharmony_ci                    ? builder.allocRunText(font, it.glyphCount(), it.offset().x(),it.offset().y(),
1324cb93a386Sopenharmony_ci                                           it.textSize())
1325cb93a386Sopenharmony_ci                : it.positioning() == SkTextBlobRunIterator::kHorizontal_Positioning
1326cb93a386Sopenharmony_ci                    ? builder.allocRunTextPosH(font, it.glyphCount(), it.offset().y(),
1327cb93a386Sopenharmony_ci                                               it.textSize())
1328cb93a386Sopenharmony_ci                : it.positioning() == SkTextBlobRunIterator::kFull_Positioning
1329cb93a386Sopenharmony_ci                    ? builder.allocRunTextPos(font, it.glyphCount(), it.textSize())
1330cb93a386Sopenharmony_ci                : it.positioning() == SkTextBlobRunIterator::kRSXform_Positioning
1331cb93a386Sopenharmony_ci                    ? builder.allocRunTextRSXform(font, it.glyphCount(), it.textSize())
1332cb93a386Sopenharmony_ci                : (SkASSERT_RELEASE(false), SkTextBlobBuilder::RunBuffer());
1333cb93a386Sopenharmony_ci            uint32_t glyphCount = it.glyphCount();
1334cb93a386Sopenharmony_ci            if (it.glyphs()) {
1335cb93a386Sopenharmony_ci                size_t glyphSize = sizeof(decltype(*it.glyphs()));
1336cb93a386Sopenharmony_ci                memcpy(runBuffer.glyphs, it.glyphs(), glyphCount * glyphSize);
1337cb93a386Sopenharmony_ci            }
1338cb93a386Sopenharmony_ci            if (it.pos()) {
1339cb93a386Sopenharmony_ci                size_t posSize = sizeof(decltype(*it.pos()));
1340cb93a386Sopenharmony_ci                unsigned posPerGlyph = it.scalarsPerGlyph();
1341cb93a386Sopenharmony_ci                memcpy(runBuffer.pos, it.pos(), glyphCount * posPerGlyph * posSize);
1342cb93a386Sopenharmony_ci            }
1343cb93a386Sopenharmony_ci            if (it.text()) {
1344cb93a386Sopenharmony_ci                size_t textSize = sizeof(decltype(*it.text()));
1345cb93a386Sopenharmony_ci                uint32_t textCount = it.textSize();
1346cb93a386Sopenharmony_ci                memcpy(runBuffer.utf8text, it.text(), textCount * textSize);
1347cb93a386Sopenharmony_ci            }
1348cb93a386Sopenharmony_ci            if (it.clusters()) {
1349cb93a386Sopenharmony_ci                size_t clusterSize = sizeof(decltype(*it.clusters()));
1350cb93a386Sopenharmony_ci                memcpy(runBuffer.clusters, it.clusters(), glyphCount * clusterSize);
1351cb93a386Sopenharmony_ci            }
1352cb93a386Sopenharmony_ci        }
1353cb93a386Sopenharmony_ci        *cache = builder.make();
1354cb93a386Sopenharmony_ci        return cache->get();
1355cb93a386Sopenharmony_ci    }
1356cb93a386Sopenharmony_ci    void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
1357cb93a386Sopenharmony_ci                        const SkPaint& paint) override {
1358cb93a386Sopenharmony_ci        sk_sp<SkTextBlob> cache;
1359cb93a386Sopenharmony_ci        this->SkPaintFilterCanvas::onDrawTextBlob(
1360cb93a386Sopenharmony_ci            this->filterTextBlob(paint, blob, &cache), x, y, paint);
1361cb93a386Sopenharmony_ci    }
1362cb93a386Sopenharmony_ci    bool filterFont(SkTCopyOnFirstWrite<SkFont>* font) const {
1363cb93a386Sopenharmony_ci        if (fFontOverrides->fTypeface) {
1364cb93a386Sopenharmony_ci            font->writable()->setTypeface(fFont->refTypeface());
1365cb93a386Sopenharmony_ci        }
1366cb93a386Sopenharmony_ci        if (fFontOverrides->fSize) {
1367cb93a386Sopenharmony_ci            font->writable()->setSize(fFont->getSize());
1368cb93a386Sopenharmony_ci        }
1369cb93a386Sopenharmony_ci        if (fFontOverrides->fScaleX) {
1370cb93a386Sopenharmony_ci            font->writable()->setScaleX(fFont->getScaleX());
1371cb93a386Sopenharmony_ci        }
1372cb93a386Sopenharmony_ci        if (fFontOverrides->fSkewX) {
1373cb93a386Sopenharmony_ci            font->writable()->setSkewX(fFont->getSkewX());
1374cb93a386Sopenharmony_ci        }
1375cb93a386Sopenharmony_ci        if (fFontOverrides->fHinting) {
1376cb93a386Sopenharmony_ci            font->writable()->setHinting(fFont->getHinting());
1377cb93a386Sopenharmony_ci        }
1378cb93a386Sopenharmony_ci        if (fFontOverrides->fEdging) {
1379cb93a386Sopenharmony_ci            font->writable()->setEdging(fFont->getEdging());
1380cb93a386Sopenharmony_ci        }
1381cb93a386Sopenharmony_ci        if (fFontOverrides->fSubpixel) {
1382cb93a386Sopenharmony_ci            font->writable()->setSubpixel(fFont->isSubpixel());
1383cb93a386Sopenharmony_ci        }
1384cb93a386Sopenharmony_ci        if (fFontOverrides->fForceAutoHinting) {
1385cb93a386Sopenharmony_ci            font->writable()->setForceAutoHinting(fFont->isForceAutoHinting());
1386cb93a386Sopenharmony_ci        }
1387cb93a386Sopenharmony_ci        if (fFontOverrides->fEmbeddedBitmaps) {
1388cb93a386Sopenharmony_ci            font->writable()->setEmbeddedBitmaps(fFont->isEmbeddedBitmaps());
1389cb93a386Sopenharmony_ci        }
1390cb93a386Sopenharmony_ci        if (fFontOverrides->fLinearMetrics) {
1391cb93a386Sopenharmony_ci            font->writable()->setLinearMetrics(fFont->isLinearMetrics());
1392cb93a386Sopenharmony_ci        }
1393cb93a386Sopenharmony_ci        if (fFontOverrides->fEmbolden) {
1394cb93a386Sopenharmony_ci            font->writable()->setEmbolden(fFont->isEmbolden());
1395cb93a386Sopenharmony_ci        }
1396cb93a386Sopenharmony_ci        if (fFontOverrides->fBaselineSnap) {
1397cb93a386Sopenharmony_ci            font->writable()->setBaselineSnap(fFont->isBaselineSnap());
1398cb93a386Sopenharmony_ci        }
1399cb93a386Sopenharmony_ci
1400cb93a386Sopenharmony_ci        return true; // we, currently, never elide a draw
1401cb93a386Sopenharmony_ci    }
1402cb93a386Sopenharmony_ci
1403cb93a386Sopenharmony_ci    bool onFilter(SkPaint& paint) const override {
1404cb93a386Sopenharmony_ci        if (fPaintOverrides->fPathEffect) {
1405cb93a386Sopenharmony_ci            paint.setPathEffect(fPaint->refPathEffect());
1406cb93a386Sopenharmony_ci        }
1407cb93a386Sopenharmony_ci        if (fPaintOverrides->fShader) {
1408cb93a386Sopenharmony_ci            paint.setShader(fPaint->refShader());
1409cb93a386Sopenharmony_ci        }
1410cb93a386Sopenharmony_ci        if (fPaintOverrides->fMaskFilter) {
1411cb93a386Sopenharmony_ci            paint.setMaskFilter(fPaint->refMaskFilter());
1412cb93a386Sopenharmony_ci        }
1413cb93a386Sopenharmony_ci        if (fPaintOverrides->fColorFilter) {
1414cb93a386Sopenharmony_ci            paint.setColorFilter(fPaint->refColorFilter());
1415cb93a386Sopenharmony_ci        }
1416cb93a386Sopenharmony_ci        if (fPaintOverrides->fImageFilter) {
1417cb93a386Sopenharmony_ci            paint.setImageFilter(fPaint->refImageFilter());
1418cb93a386Sopenharmony_ci        }
1419cb93a386Sopenharmony_ci        if (fPaintOverrides->fColor) {
1420cb93a386Sopenharmony_ci            paint.setColor4f(fPaint->getColor4f());
1421cb93a386Sopenharmony_ci        }
1422cb93a386Sopenharmony_ci        if (fPaintOverrides->fStrokeWidth) {
1423cb93a386Sopenharmony_ci            paint.setStrokeWidth(fPaint->getStrokeWidth());
1424cb93a386Sopenharmony_ci        }
1425cb93a386Sopenharmony_ci        if (fPaintOverrides->fMiterLimit) {
1426cb93a386Sopenharmony_ci            paint.setStrokeMiter(fPaint->getStrokeMiter());
1427cb93a386Sopenharmony_ci        }
1428cb93a386Sopenharmony_ci        if (fPaintOverrides->fBlendMode) {
1429cb93a386Sopenharmony_ci            paint.setBlendMode(fPaint->getBlendMode_or(SkBlendMode::kSrc));
1430cb93a386Sopenharmony_ci        }
1431cb93a386Sopenharmony_ci        if (fPaintOverrides->fAntiAlias) {
1432cb93a386Sopenharmony_ci            paint.setAntiAlias(fPaint->isAntiAlias());
1433cb93a386Sopenharmony_ci        }
1434cb93a386Sopenharmony_ci        if (fPaintOverrides->fDither) {
1435cb93a386Sopenharmony_ci            paint.setDither(fPaint->isDither());
1436cb93a386Sopenharmony_ci        }
1437cb93a386Sopenharmony_ci        if (fPaintOverrides->fForceRuntimeBlend) {
1438cb93a386Sopenharmony_ci            if (skstd::optional<SkBlendMode> mode = paint.asBlendMode()) {
1439cb93a386Sopenharmony_ci                paint.setBlender(GetRuntimeBlendForBlendMode(*mode));
1440cb93a386Sopenharmony_ci            }
1441cb93a386Sopenharmony_ci        }
1442cb93a386Sopenharmony_ci        if (fPaintOverrides->fCapType) {
1443cb93a386Sopenharmony_ci            paint.setStrokeCap(fPaint->getStrokeCap());
1444cb93a386Sopenharmony_ci        }
1445cb93a386Sopenharmony_ci        if (fPaintOverrides->fJoinType) {
1446cb93a386Sopenharmony_ci            paint.setStrokeJoin(fPaint->getStrokeJoin());
1447cb93a386Sopenharmony_ci        }
1448cb93a386Sopenharmony_ci        if (fPaintOverrides->fStyle) {
1449cb93a386Sopenharmony_ci            paint.setStyle(fPaint->getStyle());
1450cb93a386Sopenharmony_ci        }
1451cb93a386Sopenharmony_ci        return true; // we, currently, never elide a draw
1452cb93a386Sopenharmony_ci    }
1453cb93a386Sopenharmony_ci    SkPaint* fPaint;
1454cb93a386Sopenharmony_ci    Viewer::SkPaintFields* fPaintOverrides;
1455cb93a386Sopenharmony_ci    SkFont* fFont;
1456cb93a386Sopenharmony_ci    Viewer::SkFontFields* fFontOverrides;
1457cb93a386Sopenharmony_ci};
1458cb93a386Sopenharmony_ci
1459cb93a386Sopenharmony_civoid Viewer::drawSlide(SkSurface* surface) {
1460cb93a386Sopenharmony_ci    if (fCurrentSlide < 0) {
1461cb93a386Sopenharmony_ci        return;
1462cb93a386Sopenharmony_ci    }
1463cb93a386Sopenharmony_ci
1464cb93a386Sopenharmony_ci    SkAutoCanvasRestore autorestore(surface->getCanvas(), false);
1465cb93a386Sopenharmony_ci
1466cb93a386Sopenharmony_ci    // By default, we render directly into the window's surface/canvas
1467cb93a386Sopenharmony_ci    SkSurface* slideSurface = surface;
1468cb93a386Sopenharmony_ci    SkCanvas* slideCanvas = surface->getCanvas();
1469cb93a386Sopenharmony_ci    fLastImage.reset();
1470cb93a386Sopenharmony_ci
1471cb93a386Sopenharmony_ci    // If we're in any of the color managed modes, construct the color space we're going to use
1472cb93a386Sopenharmony_ci    sk_sp<SkColorSpace> colorSpace = nullptr;
1473cb93a386Sopenharmony_ci    if (ColorMode::kLegacy != fColorMode) {
1474cb93a386Sopenharmony_ci        skcms_Matrix3x3 toXYZ;
1475cb93a386Sopenharmony_ci        SkAssertResult(fColorSpacePrimaries.toXYZD50(&toXYZ));
1476cb93a386Sopenharmony_ci        colorSpace = SkColorSpace::MakeRGB(fColorSpaceTransferFn, toXYZ);
1477cb93a386Sopenharmony_ci    }
1478cb93a386Sopenharmony_ci
1479cb93a386Sopenharmony_ci    if (fSaveToSKP) {
1480cb93a386Sopenharmony_ci        SkPictureRecorder recorder;
1481cb93a386Sopenharmony_ci        SkCanvas* recorderCanvas = recorder.beginRecording(
1482cb93a386Sopenharmony_ci                SkRect::Make(fSlides[fCurrentSlide]->getDimensions()));
1483cb93a386Sopenharmony_ci        fSlides[fCurrentSlide]->draw(recorderCanvas);
1484cb93a386Sopenharmony_ci        sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
1485cb93a386Sopenharmony_ci        SkFILEWStream stream("sample_app.skp");
1486cb93a386Sopenharmony_ci        picture->serialize(&stream);
1487cb93a386Sopenharmony_ci        fSaveToSKP = false;
1488cb93a386Sopenharmony_ci    }
1489cb93a386Sopenharmony_ci
1490cb93a386Sopenharmony_ci    // Grab some things we'll need to make surfaces (for tiling or general offscreen rendering)
1491cb93a386Sopenharmony_ci    SkColorType colorType;
1492cb93a386Sopenharmony_ci    switch (fColorMode) {
1493cb93a386Sopenharmony_ci        case ColorMode::kLegacy:
1494cb93a386Sopenharmony_ci        case ColorMode::kColorManaged8888:
1495cb93a386Sopenharmony_ci            colorType = kN32_SkColorType;
1496cb93a386Sopenharmony_ci            break;
1497cb93a386Sopenharmony_ci        case ColorMode::kColorManagedF16:
1498cb93a386Sopenharmony_ci            colorType = kRGBA_F16_SkColorType;
1499cb93a386Sopenharmony_ci            break;
1500cb93a386Sopenharmony_ci        case ColorMode::kColorManagedF16Norm:
1501cb93a386Sopenharmony_ci            colorType = kRGBA_F16Norm_SkColorType;
1502cb93a386Sopenharmony_ci            break;
1503cb93a386Sopenharmony_ci    }
1504cb93a386Sopenharmony_ci
1505cb93a386Sopenharmony_ci    auto make_surface = [=](int w, int h) {
1506cb93a386Sopenharmony_ci        SkSurfaceProps props(fWindow->getRequestedDisplayParams().fSurfaceProps);
1507cb93a386Sopenharmony_ci        slideCanvas->getProps(&props);
1508cb93a386Sopenharmony_ci
1509cb93a386Sopenharmony_ci        SkImageInfo info = SkImageInfo::Make(w, h, colorType, kPremul_SkAlphaType, colorSpace);
1510cb93a386Sopenharmony_ci        return Window::kRaster_BackendType == this->fBackendType
1511cb93a386Sopenharmony_ci                ? SkSurface::MakeRaster(info, &props)
1512cb93a386Sopenharmony_ci                : slideCanvas->makeSurface(info, &props);
1513cb93a386Sopenharmony_ci    };
1514cb93a386Sopenharmony_ci
1515cb93a386Sopenharmony_ci    // We need to render offscreen if we're...
1516cb93a386Sopenharmony_ci    // ... in fake perspective or zooming (so we have a snapped copy of the results)
1517cb93a386Sopenharmony_ci    // ... in any raster mode, because the window surface is actually GL
1518cb93a386Sopenharmony_ci    // ... in any color managed mode, because we always make the window surface with no color space
1519cb93a386Sopenharmony_ci    // ... or if the user explicitly requested offscreen rendering
1520cb93a386Sopenharmony_ci    sk_sp<SkSurface> offscreenSurface = nullptr;
1521cb93a386Sopenharmony_ci    if (kPerspective_Fake == fPerspectiveMode ||
1522cb93a386Sopenharmony_ci        fShowZoomWindow ||
1523cb93a386Sopenharmony_ci        Window::kRaster_BackendType == fBackendType ||
1524cb93a386Sopenharmony_ci        colorSpace != nullptr ||
1525cb93a386Sopenharmony_ci        FLAGS_offscreen) {
1526cb93a386Sopenharmony_ci
1527cb93a386Sopenharmony_ci        offscreenSurface = make_surface(fWindow->width(), fWindow->height());
1528cb93a386Sopenharmony_ci        slideSurface = offscreenSurface.get();
1529cb93a386Sopenharmony_ci        slideCanvas = offscreenSurface->getCanvas();
1530cb93a386Sopenharmony_ci    }
1531cb93a386Sopenharmony_ci
1532cb93a386Sopenharmony_ci    SkPictureRecorder recorder;
1533cb93a386Sopenharmony_ci    SkCanvas* recorderRestoreCanvas = nullptr;
1534cb93a386Sopenharmony_ci    if (fDrawViaSerialize) {
1535cb93a386Sopenharmony_ci        recorderRestoreCanvas = slideCanvas;
1536cb93a386Sopenharmony_ci        slideCanvas = recorder.beginRecording(
1537cb93a386Sopenharmony_ci                SkRect::Make(fSlides[fCurrentSlide]->getDimensions()));
1538cb93a386Sopenharmony_ci    }
1539cb93a386Sopenharmony_ci
1540cb93a386Sopenharmony_ci    int count = slideCanvas->save();
1541cb93a386Sopenharmony_ci    slideCanvas->clear(SK_ColorWHITE);
1542cb93a386Sopenharmony_ci    // Time the painting logic of the slide
1543cb93a386Sopenharmony_ci    fStatsLayer.beginTiming(fPaintTimer);
1544cb93a386Sopenharmony_ci    if (fTiled) {
1545cb93a386Sopenharmony_ci        int tileW = SkScalarCeilToInt(fWindow->width() * fTileScale.width());
1546cb93a386Sopenharmony_ci        int tileH = SkScalarCeilToInt(fWindow->height() * fTileScale.height());
1547cb93a386Sopenharmony_ci        for (int y = 0; y < fWindow->height(); y += tileH) {
1548cb93a386Sopenharmony_ci            for (int x = 0; x < fWindow->width(); x += tileW) {
1549cb93a386Sopenharmony_ci                SkAutoCanvasRestore acr(slideCanvas, true);
1550cb93a386Sopenharmony_ci                slideCanvas->clipRect(SkRect::MakeXYWH(x, y, tileW, tileH));
1551cb93a386Sopenharmony_ci                fSlides[fCurrentSlide]->draw(slideCanvas);
1552cb93a386Sopenharmony_ci            }
1553cb93a386Sopenharmony_ci        }
1554cb93a386Sopenharmony_ci
1555cb93a386Sopenharmony_ci        // Draw borders between tiles
1556cb93a386Sopenharmony_ci        if (fDrawTileBoundaries) {
1557cb93a386Sopenharmony_ci            SkPaint border;
1558cb93a386Sopenharmony_ci            border.setColor(0x60FF00FF);
1559cb93a386Sopenharmony_ci            border.setStyle(SkPaint::kStroke_Style);
1560cb93a386Sopenharmony_ci            for (int y = 0; y < fWindow->height(); y += tileH) {
1561cb93a386Sopenharmony_ci                for (int x = 0; x < fWindow->width(); x += tileW) {
1562cb93a386Sopenharmony_ci                    slideCanvas->drawRect(SkRect::MakeXYWH(x, y, tileW, tileH), border);
1563cb93a386Sopenharmony_ci                }
1564cb93a386Sopenharmony_ci            }
1565cb93a386Sopenharmony_ci        }
1566cb93a386Sopenharmony_ci    } else {
1567cb93a386Sopenharmony_ci        slideCanvas->concat(this->computeMatrix());
1568cb93a386Sopenharmony_ci        if (kPerspective_Real == fPerspectiveMode) {
1569cb93a386Sopenharmony_ci            slideCanvas->clipRect(SkRect::MakeWH(fWindow->width(), fWindow->height()));
1570cb93a386Sopenharmony_ci        }
1571cb93a386Sopenharmony_ci        if (fPaintOverrides.overridesSomething() || fFontOverrides.overridesSomething()) {
1572cb93a386Sopenharmony_ci            OveridePaintFilterCanvas filterCanvas(slideCanvas,
1573cb93a386Sopenharmony_ci                                                  &fPaint, &fPaintOverrides,
1574cb93a386Sopenharmony_ci                                                  &fFont, &fFontOverrides);
1575cb93a386Sopenharmony_ci            fSlides[fCurrentSlide]->draw(&filterCanvas);
1576cb93a386Sopenharmony_ci        } else {
1577cb93a386Sopenharmony_ci            fSlides[fCurrentSlide]->draw(slideCanvas);
1578cb93a386Sopenharmony_ci        }
1579cb93a386Sopenharmony_ci    }
1580cb93a386Sopenharmony_ci    fStatsLayer.endTiming(fPaintTimer);
1581cb93a386Sopenharmony_ci    slideCanvas->restoreToCount(count);
1582cb93a386Sopenharmony_ci
1583cb93a386Sopenharmony_ci    if (recorderRestoreCanvas) {
1584cb93a386Sopenharmony_ci        sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
1585cb93a386Sopenharmony_ci        auto data = picture->serialize();
1586cb93a386Sopenharmony_ci        slideCanvas = recorderRestoreCanvas;
1587cb93a386Sopenharmony_ci        slideCanvas->drawPicture(SkPicture::MakeFromData(data.get()));
1588cb93a386Sopenharmony_ci    }
1589cb93a386Sopenharmony_ci
1590cb93a386Sopenharmony_ci    // Force a flush so we can time that, too
1591cb93a386Sopenharmony_ci    fStatsLayer.beginTiming(fFlushTimer);
1592cb93a386Sopenharmony_ci    slideSurface->flushAndSubmit();
1593cb93a386Sopenharmony_ci    fStatsLayer.endTiming(fFlushTimer);
1594cb93a386Sopenharmony_ci
1595cb93a386Sopenharmony_ci    // If we rendered offscreen, snap an image and push the results to the window's canvas
1596cb93a386Sopenharmony_ci    if (offscreenSurface) {
1597cb93a386Sopenharmony_ci        fLastImage = offscreenSurface->makeImageSnapshot();
1598cb93a386Sopenharmony_ci
1599cb93a386Sopenharmony_ci        SkCanvas* canvas = surface->getCanvas();
1600cb93a386Sopenharmony_ci        SkPaint paint;
1601cb93a386Sopenharmony_ci        paint.setBlendMode(SkBlendMode::kSrc);
1602cb93a386Sopenharmony_ci        SkSamplingOptions sampling;
1603cb93a386Sopenharmony_ci        int prePerspectiveCount = canvas->save();
1604cb93a386Sopenharmony_ci        if (kPerspective_Fake == fPerspectiveMode) {
1605cb93a386Sopenharmony_ci            sampling = SkSamplingOptions({1.0f/3, 1.0f/3});
1606cb93a386Sopenharmony_ci            canvas->clear(SK_ColorWHITE);
1607cb93a386Sopenharmony_ci            canvas->concat(this->computePerspectiveMatrix());
1608cb93a386Sopenharmony_ci        }
1609cb93a386Sopenharmony_ci        canvas->drawImage(fLastImage, 0, 0, sampling, &paint);
1610cb93a386Sopenharmony_ci        canvas->restoreToCount(prePerspectiveCount);
1611cb93a386Sopenharmony_ci    }
1612cb93a386Sopenharmony_ci
1613cb93a386Sopenharmony_ci    if (fShowSlideDimensions) {
1614cb93a386Sopenharmony_ci        SkCanvas* canvas = surface->getCanvas();
1615cb93a386Sopenharmony_ci        SkAutoCanvasRestore acr(canvas, true);
1616cb93a386Sopenharmony_ci        canvas->concat(this->computeMatrix());
1617cb93a386Sopenharmony_ci        SkRect r = SkRect::Make(fSlides[fCurrentSlide]->getDimensions());
1618cb93a386Sopenharmony_ci        SkPaint paint;
1619cb93a386Sopenharmony_ci        paint.setColor(0x40FFFF00);
1620cb93a386Sopenharmony_ci        canvas->drawRect(r, paint);
1621cb93a386Sopenharmony_ci    }
1622cb93a386Sopenharmony_ci}
1623cb93a386Sopenharmony_ci
1624cb93a386Sopenharmony_civoid Viewer::onBackendCreated() {
1625cb93a386Sopenharmony_ci    this->setupCurrentSlide();
1626cb93a386Sopenharmony_ci    fWindow->show();
1627cb93a386Sopenharmony_ci}
1628cb93a386Sopenharmony_ci
1629cb93a386Sopenharmony_civoid Viewer::onPaint(SkSurface* surface) {
1630cb93a386Sopenharmony_ci    this->drawSlide(surface);
1631cb93a386Sopenharmony_ci
1632cb93a386Sopenharmony_ci    fCommands.drawHelp(surface->getCanvas());
1633cb93a386Sopenharmony_ci
1634cb93a386Sopenharmony_ci    this->drawImGui();
1635cb93a386Sopenharmony_ci
1636cb93a386Sopenharmony_ci    fLastImage.reset();
1637cb93a386Sopenharmony_ci
1638cb93a386Sopenharmony_ci    if (auto direct = fWindow->directContext()) {
1639cb93a386Sopenharmony_ci        // Clean out cache items that haven't been used in more than 10 seconds.
1640cb93a386Sopenharmony_ci        direct->performDeferredCleanup(std::chrono::seconds(10));
1641cb93a386Sopenharmony_ci    }
1642cb93a386Sopenharmony_ci}
1643cb93a386Sopenharmony_ci
1644cb93a386Sopenharmony_civoid Viewer::onResize(int width, int height) {
1645cb93a386Sopenharmony_ci    if (fCurrentSlide >= 0) {
1646cb93a386Sopenharmony_ci        SkScalar scaleFactor = 1.0;
1647cb93a386Sopenharmony_ci        if (fApplyBackingScale) {
1648cb93a386Sopenharmony_ci            scaleFactor = fWindow->scaleFactor();
1649cb93a386Sopenharmony_ci        }
1650cb93a386Sopenharmony_ci        fSlides[fCurrentSlide]->resize(width / scaleFactor, height / scaleFactor);
1651cb93a386Sopenharmony_ci    }
1652cb93a386Sopenharmony_ci}
1653cb93a386Sopenharmony_ci
1654cb93a386Sopenharmony_ciSkPoint Viewer::mapEvent(float x, float y) {
1655cb93a386Sopenharmony_ci    const auto m = this->computeMatrix();
1656cb93a386Sopenharmony_ci    SkMatrix inv;
1657cb93a386Sopenharmony_ci
1658cb93a386Sopenharmony_ci    SkAssertResult(m.invert(&inv));
1659cb93a386Sopenharmony_ci
1660cb93a386Sopenharmony_ci    return inv.mapXY(x, y);
1661cb93a386Sopenharmony_ci}
1662cb93a386Sopenharmony_ci
1663cb93a386Sopenharmony_cibool Viewer::onTouch(intptr_t owner, skui::InputState state, float x, float y) {
1664cb93a386Sopenharmony_ci    if (GestureDevice::kMouse == fGestureDevice) {
1665cb93a386Sopenharmony_ci        return false;
1666cb93a386Sopenharmony_ci    }
1667cb93a386Sopenharmony_ci
1668cb93a386Sopenharmony_ci    const auto slidePt = this->mapEvent(x, y);
1669cb93a386Sopenharmony_ci    if (fSlides[fCurrentSlide]->onMouse(slidePt.x(), slidePt.y(), state, skui::ModifierKey::kNone)) {
1670cb93a386Sopenharmony_ci        fWindow->inval();
1671cb93a386Sopenharmony_ci        return true;
1672cb93a386Sopenharmony_ci    }
1673cb93a386Sopenharmony_ci
1674cb93a386Sopenharmony_ci    void* castedOwner = reinterpret_cast<void*>(owner);
1675cb93a386Sopenharmony_ci    switch (state) {
1676cb93a386Sopenharmony_ci        case skui::InputState::kUp: {
1677cb93a386Sopenharmony_ci            fGesture.touchEnd(castedOwner);
1678cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_IOS)
1679cb93a386Sopenharmony_ci            // TODO: move IOS swipe detection higher up into the platform code
1680cb93a386Sopenharmony_ci            SkPoint dir;
1681cb93a386Sopenharmony_ci            if (fGesture.isFling(&dir)) {
1682cb93a386Sopenharmony_ci                // swiping left or right
1683cb93a386Sopenharmony_ci                if (SkTAbs(dir.fX) > SkTAbs(dir.fY)) {
1684cb93a386Sopenharmony_ci                    if (dir.fX < 0) {
1685cb93a386Sopenharmony_ci                        this->setCurrentSlide(fCurrentSlide < fSlides.count() - 1 ?
1686cb93a386Sopenharmony_ci                                              fCurrentSlide + 1 : 0);
1687cb93a386Sopenharmony_ci                    } else {
1688cb93a386Sopenharmony_ci                        this->setCurrentSlide(fCurrentSlide > 0 ?
1689cb93a386Sopenharmony_ci                                              fCurrentSlide - 1 : fSlides.count() - 1);
1690cb93a386Sopenharmony_ci                    }
1691cb93a386Sopenharmony_ci                }
1692cb93a386Sopenharmony_ci                fGesture.reset();
1693cb93a386Sopenharmony_ci            }
1694cb93a386Sopenharmony_ci#endif
1695cb93a386Sopenharmony_ci            break;
1696cb93a386Sopenharmony_ci        }
1697cb93a386Sopenharmony_ci        case skui::InputState::kDown: {
1698cb93a386Sopenharmony_ci            fGesture.touchBegin(castedOwner, x, y);
1699cb93a386Sopenharmony_ci            break;
1700cb93a386Sopenharmony_ci        }
1701cb93a386Sopenharmony_ci        case skui::InputState::kMove: {
1702cb93a386Sopenharmony_ci            fGesture.touchMoved(castedOwner, x, y);
1703cb93a386Sopenharmony_ci            break;
1704cb93a386Sopenharmony_ci        }
1705cb93a386Sopenharmony_ci        default: {
1706cb93a386Sopenharmony_ci            // kLeft and kRight are only for swipes
1707cb93a386Sopenharmony_ci            SkASSERT(false);
1708cb93a386Sopenharmony_ci            break;
1709cb93a386Sopenharmony_ci        }
1710cb93a386Sopenharmony_ci    }
1711cb93a386Sopenharmony_ci    fGestureDevice = fGesture.isBeingTouched() ? GestureDevice::kTouch : GestureDevice::kNone;
1712cb93a386Sopenharmony_ci    fWindow->inval();
1713cb93a386Sopenharmony_ci    return true;
1714cb93a386Sopenharmony_ci}
1715cb93a386Sopenharmony_ci
1716cb93a386Sopenharmony_cibool Viewer::onMouse(int x, int y, skui::InputState state, skui::ModifierKey modifiers) {
1717cb93a386Sopenharmony_ci    if (GestureDevice::kTouch == fGestureDevice) {
1718cb93a386Sopenharmony_ci        return false;
1719cb93a386Sopenharmony_ci    }
1720cb93a386Sopenharmony_ci
1721cb93a386Sopenharmony_ci    const auto slidePt = this->mapEvent(x, y);
1722cb93a386Sopenharmony_ci    if (fSlides[fCurrentSlide]->onMouse(slidePt.x(), slidePt.y(), state, modifiers)) {
1723cb93a386Sopenharmony_ci        fWindow->inval();
1724cb93a386Sopenharmony_ci        return true;
1725cb93a386Sopenharmony_ci    }
1726cb93a386Sopenharmony_ci
1727cb93a386Sopenharmony_ci    switch (state) {
1728cb93a386Sopenharmony_ci        case skui::InputState::kUp: {
1729cb93a386Sopenharmony_ci            fGesture.touchEnd(nullptr);
1730cb93a386Sopenharmony_ci            break;
1731cb93a386Sopenharmony_ci        }
1732cb93a386Sopenharmony_ci        case skui::InputState::kDown: {
1733cb93a386Sopenharmony_ci            fGesture.touchBegin(nullptr, x, y);
1734cb93a386Sopenharmony_ci            break;
1735cb93a386Sopenharmony_ci        }
1736cb93a386Sopenharmony_ci        case skui::InputState::kMove: {
1737cb93a386Sopenharmony_ci            fGesture.touchMoved(nullptr, x, y);
1738cb93a386Sopenharmony_ci            break;
1739cb93a386Sopenharmony_ci        }
1740cb93a386Sopenharmony_ci        default: {
1741cb93a386Sopenharmony_ci            SkASSERT(false); // shouldn't see kRight or kLeft here
1742cb93a386Sopenharmony_ci            break;
1743cb93a386Sopenharmony_ci        }
1744cb93a386Sopenharmony_ci    }
1745cb93a386Sopenharmony_ci    fGestureDevice = fGesture.isBeingTouched() ? GestureDevice::kMouse : GestureDevice::kNone;
1746cb93a386Sopenharmony_ci
1747cb93a386Sopenharmony_ci    if (state != skui::InputState::kMove || fGesture.isBeingTouched()) {
1748cb93a386Sopenharmony_ci        fWindow->inval();
1749cb93a386Sopenharmony_ci    }
1750cb93a386Sopenharmony_ci    return true;
1751cb93a386Sopenharmony_ci}
1752cb93a386Sopenharmony_ci
1753cb93a386Sopenharmony_cibool Viewer::onFling(skui::InputState state) {
1754cb93a386Sopenharmony_ci    if (skui::InputState::kRight == state) {
1755cb93a386Sopenharmony_ci        this->setCurrentSlide(fCurrentSlide > 0 ? fCurrentSlide - 1 : fSlides.count() - 1);
1756cb93a386Sopenharmony_ci        return true;
1757cb93a386Sopenharmony_ci    } else if (skui::InputState::kLeft == state) {
1758cb93a386Sopenharmony_ci        this->setCurrentSlide(fCurrentSlide < fSlides.count() - 1 ? fCurrentSlide + 1 : 0);
1759cb93a386Sopenharmony_ci        return true;
1760cb93a386Sopenharmony_ci    }
1761cb93a386Sopenharmony_ci    return false;
1762cb93a386Sopenharmony_ci}
1763cb93a386Sopenharmony_ci
1764cb93a386Sopenharmony_cibool Viewer::onPinch(skui::InputState state, float scale, float x, float y) {
1765cb93a386Sopenharmony_ci    switch (state) {
1766cb93a386Sopenharmony_ci        case skui::InputState::kDown:
1767cb93a386Sopenharmony_ci            fGesture.startZoom();
1768cb93a386Sopenharmony_ci            return true;
1769cb93a386Sopenharmony_ci            break;
1770cb93a386Sopenharmony_ci        case skui::InputState::kMove:
1771cb93a386Sopenharmony_ci            fGesture.updateZoom(scale, x, y, x, y);
1772cb93a386Sopenharmony_ci            return true;
1773cb93a386Sopenharmony_ci            break;
1774cb93a386Sopenharmony_ci        case skui::InputState::kUp:
1775cb93a386Sopenharmony_ci            fGesture.endZoom();
1776cb93a386Sopenharmony_ci            return true;
1777cb93a386Sopenharmony_ci            break;
1778cb93a386Sopenharmony_ci        default:
1779cb93a386Sopenharmony_ci            SkASSERT(false);
1780cb93a386Sopenharmony_ci            break;
1781cb93a386Sopenharmony_ci    }
1782cb93a386Sopenharmony_ci
1783cb93a386Sopenharmony_ci    return false;
1784cb93a386Sopenharmony_ci}
1785cb93a386Sopenharmony_ci
1786cb93a386Sopenharmony_cistatic void ImGui_Primaries(SkColorSpacePrimaries* primaries, SkPaint* gamutPaint) {
1787cb93a386Sopenharmony_ci    // The gamut image covers a (0.8 x 0.9) shaped region
1788cb93a386Sopenharmony_ci    ImGui::DragCanvas dc(primaries, { 0.0f, 0.9f }, { 0.8f, 0.0f });
1789cb93a386Sopenharmony_ci
1790cb93a386Sopenharmony_ci    // Background image. Only draw a subset of the image, to avoid the regions less than zero.
1791cb93a386Sopenharmony_ci    // Simplifes re-mapping math, clipping behavior, and increases resolution in the useful area.
1792cb93a386Sopenharmony_ci    // Magic numbers are pixel locations of the origin and upper-right corner.
1793cb93a386Sopenharmony_ci    dc.fDrawList->AddImage(gamutPaint, dc.fPos,
1794cb93a386Sopenharmony_ci                           ImVec2(dc.fPos.x + dc.fSize.x, dc.fPos.y + dc.fSize.y),
1795cb93a386Sopenharmony_ci                           ImVec2(242, 61), ImVec2(1897, 1922));
1796cb93a386Sopenharmony_ci
1797cb93a386Sopenharmony_ci    dc.dragPoint((SkPoint*)(&primaries->fRX), true, 0xFF000040);
1798cb93a386Sopenharmony_ci    dc.dragPoint((SkPoint*)(&primaries->fGX), true, 0xFF004000);
1799cb93a386Sopenharmony_ci    dc.dragPoint((SkPoint*)(&primaries->fBX), true, 0xFF400000);
1800cb93a386Sopenharmony_ci    dc.dragPoint((SkPoint*)(&primaries->fWX), true);
1801cb93a386Sopenharmony_ci    dc.fDrawList->AddPolyline(dc.fScreenPoints.begin(), 3, 0xFFFFFFFF, true, 1.5f);
1802cb93a386Sopenharmony_ci}
1803cb93a386Sopenharmony_ci
1804cb93a386Sopenharmony_cistatic bool ImGui_DragLocation(SkPoint* pt) {
1805cb93a386Sopenharmony_ci    ImGui::DragCanvas dc(pt);
1806cb93a386Sopenharmony_ci    dc.fillColor(IM_COL32(0, 0, 0, 128));
1807cb93a386Sopenharmony_ci    dc.dragPoint(pt);
1808cb93a386Sopenharmony_ci    return dc.fDragging;
1809cb93a386Sopenharmony_ci}
1810cb93a386Sopenharmony_ci
1811cb93a386Sopenharmony_cistatic bool ImGui_DragQuad(SkPoint* pts) {
1812cb93a386Sopenharmony_ci    ImGui::DragCanvas dc(pts);
1813cb93a386Sopenharmony_ci    dc.fillColor(IM_COL32(0, 0, 0, 128));
1814cb93a386Sopenharmony_ci
1815cb93a386Sopenharmony_ci    for (int i = 0; i < 4; ++i) {
1816cb93a386Sopenharmony_ci        dc.dragPoint(pts + i);
1817cb93a386Sopenharmony_ci    }
1818cb93a386Sopenharmony_ci
1819cb93a386Sopenharmony_ci    dc.fDrawList->AddLine(dc.fScreenPoints[0], dc.fScreenPoints[1], 0xFFFFFFFF);
1820cb93a386Sopenharmony_ci    dc.fDrawList->AddLine(dc.fScreenPoints[1], dc.fScreenPoints[3], 0xFFFFFFFF);
1821cb93a386Sopenharmony_ci    dc.fDrawList->AddLine(dc.fScreenPoints[3], dc.fScreenPoints[2], 0xFFFFFFFF);
1822cb93a386Sopenharmony_ci    dc.fDrawList->AddLine(dc.fScreenPoints[2], dc.fScreenPoints[0], 0xFFFFFFFF);
1823cb93a386Sopenharmony_ci
1824cb93a386Sopenharmony_ci    return dc.fDragging;
1825cb93a386Sopenharmony_ci}
1826cb93a386Sopenharmony_ci
1827cb93a386Sopenharmony_cistatic SkSL::String build_sksl_highlight_shader() {
1828cb93a386Sopenharmony_ci    return SkSL::String("out half4 sk_FragColor;\n"
1829cb93a386Sopenharmony_ci                        "void main() { sk_FragColor = half4(1, 0, 1, 0.5); }");
1830cb93a386Sopenharmony_ci}
1831cb93a386Sopenharmony_ci
1832cb93a386Sopenharmony_cistatic SkSL::String build_metal_highlight_shader(const SkSL::String& inShader) {
1833cb93a386Sopenharmony_ci    // Metal fragment shaders need a lot of non-trivial boilerplate that we don't want to recompute
1834cb93a386Sopenharmony_ci    // here. So keep all shader code, but right before `return *_out;`, swap out the sk_FragColor.
1835cb93a386Sopenharmony_ci    size_t pos = inShader.rfind("return *_out;\n");
1836cb93a386Sopenharmony_ci    if (pos == std::string::npos) {
1837cb93a386Sopenharmony_ci        return inShader;
1838cb93a386Sopenharmony_ci    }
1839cb93a386Sopenharmony_ci
1840cb93a386Sopenharmony_ci    SkSL::String replacementShader = inShader;
1841cb93a386Sopenharmony_ci    replacementShader.insert(pos, "_out->sk_FragColor = float4(1.0, 0.0, 1.0, 0.5); ");
1842cb93a386Sopenharmony_ci    return replacementShader;
1843cb93a386Sopenharmony_ci}
1844cb93a386Sopenharmony_ci
1845cb93a386Sopenharmony_cistatic SkSL::String build_glsl_highlight_shader(const GrShaderCaps& shaderCaps) {
1846cb93a386Sopenharmony_ci    const char* versionDecl = shaderCaps.versionDeclString();
1847cb93a386Sopenharmony_ci    SkSL::String highlight = versionDecl ? versionDecl : "";
1848cb93a386Sopenharmony_ci    if (shaderCaps.usesPrecisionModifiers()) {
1849cb93a386Sopenharmony_ci        highlight.append("precision mediump float;\n");
1850cb93a386Sopenharmony_ci    }
1851cb93a386Sopenharmony_ci    highlight.appendf("out vec4 sk_FragColor;\n"
1852cb93a386Sopenharmony_ci                      "void main() { sk_FragColor = vec4(1, 0, 1, 0.5); }");
1853cb93a386Sopenharmony_ci    return highlight;
1854cb93a386Sopenharmony_ci}
1855cb93a386Sopenharmony_ci
1856cb93a386Sopenharmony_cistatic skvm::Program build_skvm_highlight_program(SkColorType ct, int nargs) {
1857cb93a386Sopenharmony_ci    // Code here is heavily tied to (and inspired by) SkVMBlitter::BuildProgram
1858cb93a386Sopenharmony_ci    skvm::Builder b;
1859cb93a386Sopenharmony_ci
1860cb93a386Sopenharmony_ci    // All VM blitters start with two arguments (uniforms, dst surface)
1861cb93a386Sopenharmony_ci    SkASSERT(nargs >= 2);
1862cb93a386Sopenharmony_ci    (void)b.uniform();
1863cb93a386Sopenharmony_ci    skvm::Ptr dst_ptr = b.varying(SkColorTypeBytesPerPixel(ct));
1864cb93a386Sopenharmony_ci
1865cb93a386Sopenharmony_ci    // Depending on coverage and shader, there can be additional arguments.
1866cb93a386Sopenharmony_ci    // Make sure that we append the right number, so that we don't assert when
1867cb93a386Sopenharmony_ci    // the CPU backend tries to run this program.
1868cb93a386Sopenharmony_ci    for (int i = 2; i < nargs; ++i) {
1869cb93a386Sopenharmony_ci        (void)b.uniform();
1870cb93a386Sopenharmony_ci    }
1871cb93a386Sopenharmony_ci
1872cb93a386Sopenharmony_ci    skvm::Color magenta = {b.splat(1.0f), b.splat(0.0f), b.splat(1.0f), b.splat(0.5f)};
1873cb93a386Sopenharmony_ci    skvm::PixelFormat dstFormat = skvm::SkColorType_to_PixelFormat(ct);
1874cb93a386Sopenharmony_ci    store(dstFormat, dst_ptr, magenta);
1875cb93a386Sopenharmony_ci
1876cb93a386Sopenharmony_ci    return b.done();
1877cb93a386Sopenharmony_ci}
1878cb93a386Sopenharmony_ci
1879cb93a386Sopenharmony_civoid Viewer::drawImGui() {
1880cb93a386Sopenharmony_ci    // Support drawing the ImGui demo window. Superfluous, but gives a good idea of what's possible
1881cb93a386Sopenharmony_ci    if (fShowImGuiTestWindow) {
1882cb93a386Sopenharmony_ci        ImGui::ShowDemoWindow(&fShowImGuiTestWindow);
1883cb93a386Sopenharmony_ci    }
1884cb93a386Sopenharmony_ci
1885cb93a386Sopenharmony_ci    if (fShowImGuiDebugWindow) {
1886cb93a386Sopenharmony_ci        // We have some dynamic content that sizes to fill available size. If the scroll bar isn't
1887cb93a386Sopenharmony_ci        // always visible, we can end up in a layout feedback loop.
1888cb93a386Sopenharmony_ci        ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
1889cb93a386Sopenharmony_ci        DisplayParams params = fWindow->getRequestedDisplayParams();
1890cb93a386Sopenharmony_ci        bool displayParamsChanged = false; // heavy-weight, might recreate entire context
1891cb93a386Sopenharmony_ci        bool uiParamsChanged = false;      // light weight, just triggers window invalidation
1892cb93a386Sopenharmony_ci        auto ctx = fWindow->directContext();
1893cb93a386Sopenharmony_ci
1894cb93a386Sopenharmony_ci        if (ImGui::Begin("Tools", &fShowImGuiDebugWindow,
1895cb93a386Sopenharmony_ci                         ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
1896cb93a386Sopenharmony_ci            if (ImGui::CollapsingHeader("Backend")) {
1897cb93a386Sopenharmony_ci                int newBackend = static_cast<int>(fBackendType);
1898cb93a386Sopenharmony_ci                ImGui::RadioButton("Raster", &newBackend, sk_app::Window::kRaster_BackendType);
1899cb93a386Sopenharmony_ci                ImGui::SameLine();
1900cb93a386Sopenharmony_ci                ImGui::RadioButton("OpenGL", &newBackend, sk_app::Window::kNativeGL_BackendType);
1901cb93a386Sopenharmony_ci#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
1902cb93a386Sopenharmony_ci                ImGui::SameLine();
1903cb93a386Sopenharmony_ci                ImGui::RadioButton("ANGLE", &newBackend, sk_app::Window::kANGLE_BackendType);
1904cb93a386Sopenharmony_ci#endif
1905cb93a386Sopenharmony_ci#if defined(SK_DAWN)
1906cb93a386Sopenharmony_ci                ImGui::SameLine();
1907cb93a386Sopenharmony_ci                ImGui::RadioButton("Dawn", &newBackend, sk_app::Window::kDawn_BackendType);
1908cb93a386Sopenharmony_ci#endif
1909cb93a386Sopenharmony_ci#if defined(SK_VULKAN) && !defined(SK_BUILD_FOR_MAC)
1910cb93a386Sopenharmony_ci                ImGui::SameLine();
1911cb93a386Sopenharmony_ci                ImGui::RadioButton("Vulkan", &newBackend, sk_app::Window::kVulkan_BackendType);
1912cb93a386Sopenharmony_ci#endif
1913cb93a386Sopenharmony_ci#if defined(SK_METAL)
1914cb93a386Sopenharmony_ci                ImGui::SameLine();
1915cb93a386Sopenharmony_ci                ImGui::RadioButton("Metal", &newBackend, sk_app::Window::kMetal_BackendType);
1916cb93a386Sopenharmony_ci#if defined(SK_GRAPHITE_ENABLED)
1917cb93a386Sopenharmony_ci                ImGui::SameLine();
1918cb93a386Sopenharmony_ci                ImGui::RadioButton("Metal (Graphite)", &newBackend,
1919cb93a386Sopenharmony_ci                                   sk_app::Window::kGraphiteMetal_BackendType);
1920cb93a386Sopenharmony_ci#endif
1921cb93a386Sopenharmony_ci#endif
1922cb93a386Sopenharmony_ci#if defined(SK_DIRECT3D)
1923cb93a386Sopenharmony_ci                ImGui::SameLine();
1924cb93a386Sopenharmony_ci                ImGui::RadioButton("Direct3D", &newBackend, sk_app::Window::kDirect3D_BackendType);
1925cb93a386Sopenharmony_ci#endif
1926cb93a386Sopenharmony_ci                if (newBackend != fBackendType) {
1927cb93a386Sopenharmony_ci                    fDeferredActions.push_back([=]() {
1928cb93a386Sopenharmony_ci                        this->setBackend(static_cast<sk_app::Window::BackendType>(newBackend));
1929cb93a386Sopenharmony_ci                    });
1930cb93a386Sopenharmony_ci                }
1931cb93a386Sopenharmony_ci
1932cb93a386Sopenharmony_ci                bool* wire = &params.fGrContextOptions.fWireframeMode;
1933cb93a386Sopenharmony_ci                if (ctx && ImGui::Checkbox("Wireframe Mode", wire)) {
1934cb93a386Sopenharmony_ci                    displayParamsChanged = true;
1935cb93a386Sopenharmony_ci                }
1936cb93a386Sopenharmony_ci
1937cb93a386Sopenharmony_ci                bool* reducedShaders = &params.fGrContextOptions.fReducedShaderVariations;
1938cb93a386Sopenharmony_ci                if (ctx && ImGui::Checkbox("Reduced shaders", reducedShaders)) {
1939cb93a386Sopenharmony_ci                    displayParamsChanged = true;
1940cb93a386Sopenharmony_ci                }
1941cb93a386Sopenharmony_ci
1942cb93a386Sopenharmony_ci                if (ctx) {
1943cb93a386Sopenharmony_ci                    // Determine the context's max sample count for MSAA radio buttons.
1944cb93a386Sopenharmony_ci                    int sampleCount = fWindow->sampleCount();
1945cb93a386Sopenharmony_ci                    int maxMSAA = (fBackendType != sk_app::Window::kRaster_BackendType) ?
1946cb93a386Sopenharmony_ci                            ctx->maxSurfaceSampleCountForColorType(kRGBA_8888_SkColorType) :
1947cb93a386Sopenharmony_ci                            1;
1948cb93a386Sopenharmony_ci
1949cb93a386Sopenharmony_ci                    // Only display the MSAA radio buttons when there are options above 1x MSAA.
1950cb93a386Sopenharmony_ci                    if (maxMSAA >= 4) {
1951cb93a386Sopenharmony_ci                        ImGui::Text("MSAA: ");
1952cb93a386Sopenharmony_ci
1953cb93a386Sopenharmony_ci                        for (int curMSAA = 1; curMSAA <= maxMSAA; curMSAA *= 2) {
1954cb93a386Sopenharmony_ci                            // 2x MSAA works, but doesn't offer much of a visual improvement, so we
1955cb93a386Sopenharmony_ci                            // don't show it in the list.
1956cb93a386Sopenharmony_ci                            if (curMSAA == 2) {
1957cb93a386Sopenharmony_ci                                continue;
1958cb93a386Sopenharmony_ci                            }
1959cb93a386Sopenharmony_ci                            ImGui::SameLine();
1960cb93a386Sopenharmony_ci                            ImGui::RadioButton(SkStringPrintf("%d", curMSAA).c_str(),
1961cb93a386Sopenharmony_ci                                               &sampleCount, curMSAA);
1962cb93a386Sopenharmony_ci                        }
1963cb93a386Sopenharmony_ci                    }
1964cb93a386Sopenharmony_ci
1965cb93a386Sopenharmony_ci                    if (sampleCount != params.fMSAASampleCount) {
1966cb93a386Sopenharmony_ci                        params.fMSAASampleCount = sampleCount;
1967cb93a386Sopenharmony_ci                        displayParamsChanged = true;
1968cb93a386Sopenharmony_ci                    }
1969cb93a386Sopenharmony_ci                }
1970cb93a386Sopenharmony_ci
1971cb93a386Sopenharmony_ci                int pixelGeometryIdx = 0;
1972cb93a386Sopenharmony_ci                if (fDisplayOverrides.fSurfaceProps.fPixelGeometry) {
1973cb93a386Sopenharmony_ci                    pixelGeometryIdx = params.fSurfaceProps.pixelGeometry() + 1;
1974cb93a386Sopenharmony_ci                }
1975cb93a386Sopenharmony_ci                if (ImGui::Combo("Pixel Geometry", &pixelGeometryIdx,
1976cb93a386Sopenharmony_ci                                 "Default\0Flat\0RGB\0BGR\0RGBV\0BGRV\0\0"))
1977cb93a386Sopenharmony_ci                {
1978cb93a386Sopenharmony_ci                    uint32_t flags = params.fSurfaceProps.flags();
1979cb93a386Sopenharmony_ci                    if (pixelGeometryIdx == 0) {
1980cb93a386Sopenharmony_ci                        fDisplayOverrides.fSurfaceProps.fPixelGeometry = false;
1981cb93a386Sopenharmony_ci                        SkPixelGeometry pixelGeometry = fDisplay.fSurfaceProps.pixelGeometry();
1982cb93a386Sopenharmony_ci                        params.fSurfaceProps = SkSurfaceProps(flags, pixelGeometry);
1983cb93a386Sopenharmony_ci                    } else {
1984cb93a386Sopenharmony_ci                        fDisplayOverrides.fSurfaceProps.fPixelGeometry = true;
1985cb93a386Sopenharmony_ci                        SkPixelGeometry pixelGeometry = SkTo<SkPixelGeometry>(pixelGeometryIdx - 1);
1986cb93a386Sopenharmony_ci                        params.fSurfaceProps = SkSurfaceProps(flags, pixelGeometry);
1987cb93a386Sopenharmony_ci                    }
1988cb93a386Sopenharmony_ci                    displayParamsChanged = true;
1989cb93a386Sopenharmony_ci                }
1990cb93a386Sopenharmony_ci
1991cb93a386Sopenharmony_ci                bool useDFT = params.fSurfaceProps.isUseDeviceIndependentFonts();
1992cb93a386Sopenharmony_ci                if (ImGui::Checkbox("DFT", &useDFT)) {
1993cb93a386Sopenharmony_ci                    uint32_t flags = params.fSurfaceProps.flags();
1994cb93a386Sopenharmony_ci                    if (useDFT) {
1995cb93a386Sopenharmony_ci                        flags |= SkSurfaceProps::kUseDeviceIndependentFonts_Flag;
1996cb93a386Sopenharmony_ci                    } else {
1997cb93a386Sopenharmony_ci                        flags &= ~SkSurfaceProps::kUseDeviceIndependentFonts_Flag;
1998cb93a386Sopenharmony_ci                    }
1999cb93a386Sopenharmony_ci                    SkPixelGeometry pixelGeometry = params.fSurfaceProps.pixelGeometry();
2000cb93a386Sopenharmony_ci                    params.fSurfaceProps = SkSurfaceProps(flags, pixelGeometry);
2001cb93a386Sopenharmony_ci                    displayParamsChanged = true;
2002cb93a386Sopenharmony_ci                }
2003cb93a386Sopenharmony_ci
2004cb93a386Sopenharmony_ci                if (ImGui::TreeNode("Path Renderers")) {
2005cb93a386Sopenharmony_ci                    GpuPathRenderers prevPr = params.fGrContextOptions.fGpuPathRenderers;
2006cb93a386Sopenharmony_ci                    auto prButton = [&](GpuPathRenderers x) {
2007cb93a386Sopenharmony_ci                        if (ImGui::RadioButton(gPathRendererNames[x].c_str(), prevPr == x)) {
2008cb93a386Sopenharmony_ci                            if (x != params.fGrContextOptions.fGpuPathRenderers) {
2009cb93a386Sopenharmony_ci                                params.fGrContextOptions.fGpuPathRenderers = x;
2010cb93a386Sopenharmony_ci                                displayParamsChanged = true;
2011cb93a386Sopenharmony_ci                            }
2012cb93a386Sopenharmony_ci                        }
2013cb93a386Sopenharmony_ci                    };
2014cb93a386Sopenharmony_ci
2015cb93a386Sopenharmony_ci                    if (!ctx) {
2016cb93a386Sopenharmony_ci                        ImGui::RadioButton("Software", true);
2017cb93a386Sopenharmony_ci                    } else {
2018cb93a386Sopenharmony_ci                        prButton(GpuPathRenderers::kDefault);
2019cb93a386Sopenharmony_ci#if SK_GPU_V1
2020cb93a386Sopenharmony_ci                        if (fWindow->sampleCount() > 1 || FLAGS_dmsaa) {
2021cb93a386Sopenharmony_ci                            const auto* caps = ctx->priv().caps();
2022cb93a386Sopenharmony_ci                            if (skgpu::v1::AtlasPathRenderer::IsSupported(ctx)) {
2023cb93a386Sopenharmony_ci                                prButton(GpuPathRenderers::kAtlas);
2024cb93a386Sopenharmony_ci                            }
2025cb93a386Sopenharmony_ci                            if (skgpu::v1::TessellationPathRenderer::IsSupported(*caps)) {
2026cb93a386Sopenharmony_ci                                prButton(GpuPathRenderers::kTessellation);
2027cb93a386Sopenharmony_ci                            }
2028cb93a386Sopenharmony_ci                        }
2029cb93a386Sopenharmony_ci#endif
2030cb93a386Sopenharmony_ci                        if (1 == fWindow->sampleCount()) {
2031cb93a386Sopenharmony_ci                            prButton(GpuPathRenderers::kSmall);
2032cb93a386Sopenharmony_ci                        }
2033cb93a386Sopenharmony_ci                        prButton(GpuPathRenderers::kTriangulating);
2034cb93a386Sopenharmony_ci                        prButton(GpuPathRenderers::kNone);
2035cb93a386Sopenharmony_ci                    }
2036cb93a386Sopenharmony_ci                    ImGui::TreePop();
2037cb93a386Sopenharmony_ci                }
2038cb93a386Sopenharmony_ci            }
2039cb93a386Sopenharmony_ci
2040cb93a386Sopenharmony_ci            if (ImGui::CollapsingHeader("Tiling")) {
2041cb93a386Sopenharmony_ci                ImGui::Checkbox("Enable", &fTiled);
2042cb93a386Sopenharmony_ci                ImGui::Checkbox("Draw Boundaries", &fDrawTileBoundaries);
2043cb93a386Sopenharmony_ci                ImGui::SliderFloat("Horizontal", &fTileScale.fWidth, 0.1f, 1.0f);
2044cb93a386Sopenharmony_ci                ImGui::SliderFloat("Vertical", &fTileScale.fHeight, 0.1f, 1.0f);
2045cb93a386Sopenharmony_ci            }
2046cb93a386Sopenharmony_ci
2047cb93a386Sopenharmony_ci            if (ImGui::CollapsingHeader("Transform")) {
2048cb93a386Sopenharmony_ci                if (ImGui::Checkbox("Apply Backing Scale", &fApplyBackingScale)) {
2049cb93a386Sopenharmony_ci                    this->preTouchMatrixChanged();
2050cb93a386Sopenharmony_ci                    this->onResize(fWindow->width(), fWindow->height());
2051cb93a386Sopenharmony_ci                    // This changes how we manipulate the canvas transform, it's not changing the
2052cb93a386Sopenharmony_ci                    // window's actual parameters.
2053cb93a386Sopenharmony_ci                    uiParamsChanged = true;
2054cb93a386Sopenharmony_ci                }
2055cb93a386Sopenharmony_ci
2056cb93a386Sopenharmony_ci                float zoom = fZoomLevel;
2057cb93a386Sopenharmony_ci                if (ImGui::SliderFloat("Zoom", &zoom, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL)) {
2058cb93a386Sopenharmony_ci                    fZoomLevel = zoom;
2059cb93a386Sopenharmony_ci                    this->preTouchMatrixChanged();
2060cb93a386Sopenharmony_ci                    uiParamsChanged = true;
2061cb93a386Sopenharmony_ci                }
2062cb93a386Sopenharmony_ci                float deg = fRotation;
2063cb93a386Sopenharmony_ci                if (ImGui::SliderFloat("Rotate", &deg, -30, 360, "%.3f deg")) {
2064cb93a386Sopenharmony_ci                    fRotation = deg;
2065cb93a386Sopenharmony_ci                    this->preTouchMatrixChanged();
2066cb93a386Sopenharmony_ci                    uiParamsChanged = true;
2067cb93a386Sopenharmony_ci                }
2068cb93a386Sopenharmony_ci                if (ImGui::CollapsingHeader("Subpixel offset", ImGuiTreeNodeFlags_NoTreePushOnOpen)) {
2069cb93a386Sopenharmony_ci                    if (ImGui_DragLocation(&fOffset)) {
2070cb93a386Sopenharmony_ci                        this->preTouchMatrixChanged();
2071cb93a386Sopenharmony_ci                        uiParamsChanged = true;
2072cb93a386Sopenharmony_ci                    }
2073cb93a386Sopenharmony_ci                } else if (fOffset != SkVector{0.5f, 0.5f}) {
2074cb93a386Sopenharmony_ci                    this->preTouchMatrixChanged();
2075cb93a386Sopenharmony_ci                    uiParamsChanged = true;
2076cb93a386Sopenharmony_ci                    fOffset = {0.5f, 0.5f};
2077cb93a386Sopenharmony_ci                }
2078cb93a386Sopenharmony_ci                int perspectiveMode = static_cast<int>(fPerspectiveMode);
2079cb93a386Sopenharmony_ci                if (ImGui::Combo("Perspective", &perspectiveMode, "Off\0Real\0Fake\0\0")) {
2080cb93a386Sopenharmony_ci                    fPerspectiveMode = static_cast<PerspectiveMode>(perspectiveMode);
2081cb93a386Sopenharmony_ci                    this->preTouchMatrixChanged();
2082cb93a386Sopenharmony_ci                    uiParamsChanged = true;
2083cb93a386Sopenharmony_ci                }
2084cb93a386Sopenharmony_ci                if (perspectiveMode != kPerspective_Off && ImGui_DragQuad(fPerspectivePoints)) {
2085cb93a386Sopenharmony_ci                    this->preTouchMatrixChanged();
2086cb93a386Sopenharmony_ci                    uiParamsChanged = true;
2087cb93a386Sopenharmony_ci                }
2088cb93a386Sopenharmony_ci            }
2089cb93a386Sopenharmony_ci
2090cb93a386Sopenharmony_ci            if (ImGui::CollapsingHeader("Paint")) {
2091cb93a386Sopenharmony_ci                int aliasIdx = 0;
2092cb93a386Sopenharmony_ci                if (fPaintOverrides.fAntiAlias) {
2093cb93a386Sopenharmony_ci                    aliasIdx = SkTo<int>(fPaintOverrides.fAntiAliasState) + 1;
2094cb93a386Sopenharmony_ci                }
2095cb93a386Sopenharmony_ci                if (ImGui::Combo("Anti-Alias", &aliasIdx,
2096cb93a386Sopenharmony_ci                                 "Default\0Alias\0Normal\0AnalyticAAEnabled\0AnalyticAAForced\0\0"))
2097cb93a386Sopenharmony_ci                {
2098cb93a386Sopenharmony_ci                    gSkUseAnalyticAA = fPaintOverrides.fOriginalSkUseAnalyticAA;
2099cb93a386Sopenharmony_ci                    gSkForceAnalyticAA = fPaintOverrides.fOriginalSkForceAnalyticAA;
2100cb93a386Sopenharmony_ci                    if (aliasIdx == 0) {
2101cb93a386Sopenharmony_ci                        fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Alias;
2102cb93a386Sopenharmony_ci                        fPaintOverrides.fAntiAlias = false;
2103cb93a386Sopenharmony_ci                    } else {
2104cb93a386Sopenharmony_ci                        fPaintOverrides.fAntiAlias = true;
2105cb93a386Sopenharmony_ci                        fPaintOverrides.fAntiAliasState = SkTo<SkPaintFields::AntiAliasState>(aliasIdx-1);
2106cb93a386Sopenharmony_ci                        fPaint.setAntiAlias(aliasIdx > 1);
2107cb93a386Sopenharmony_ci                        switch (fPaintOverrides.fAntiAliasState) {
2108cb93a386Sopenharmony_ci                            case SkPaintFields::AntiAliasState::Alias:
2109cb93a386Sopenharmony_ci                                break;
2110cb93a386Sopenharmony_ci                            case SkPaintFields::AntiAliasState::Normal:
2111cb93a386Sopenharmony_ci                                break;
2112cb93a386Sopenharmony_ci                            case SkPaintFields::AntiAliasState::AnalyticAAEnabled:
2113cb93a386Sopenharmony_ci                                gSkUseAnalyticAA = true;
2114cb93a386Sopenharmony_ci                                gSkForceAnalyticAA = false;
2115cb93a386Sopenharmony_ci                                break;
2116cb93a386Sopenharmony_ci                            case SkPaintFields::AntiAliasState::AnalyticAAForced:
2117cb93a386Sopenharmony_ci                                gSkUseAnalyticAA = gSkForceAnalyticAA = true;
2118cb93a386Sopenharmony_ci                                break;
2119cb93a386Sopenharmony_ci                        }
2120cb93a386Sopenharmony_ci                    }
2121cb93a386Sopenharmony_ci                    uiParamsChanged = true;
2122cb93a386Sopenharmony_ci                }
2123cb93a386Sopenharmony_ci
2124cb93a386Sopenharmony_ci                auto paintFlag = [this, &uiParamsChanged](const char* label, const char* items,
2125cb93a386Sopenharmony_ci                                                          bool SkPaintFields::* flag,
2126cb93a386Sopenharmony_ci                                                          bool (SkPaint::* isFlag)() const,
2127cb93a386Sopenharmony_ci                                                          void (SkPaint::* setFlag)(bool) )
2128cb93a386Sopenharmony_ci                {
2129cb93a386Sopenharmony_ci                    int itemIndex = 0;
2130cb93a386Sopenharmony_ci                    if (fPaintOverrides.*flag) {
2131cb93a386Sopenharmony_ci                        itemIndex = (fPaint.*isFlag)() ? 2 : 1;
2132cb93a386Sopenharmony_ci                    }
2133cb93a386Sopenharmony_ci                    if (ImGui::Combo(label, &itemIndex, items)) {
2134cb93a386Sopenharmony_ci                        if (itemIndex == 0) {
2135cb93a386Sopenharmony_ci                            fPaintOverrides.*flag = false;
2136cb93a386Sopenharmony_ci                        } else {
2137cb93a386Sopenharmony_ci                            fPaintOverrides.*flag = true;
2138cb93a386Sopenharmony_ci                            (fPaint.*setFlag)(itemIndex == 2);
2139cb93a386Sopenharmony_ci                        }
2140cb93a386Sopenharmony_ci                        uiParamsChanged = true;
2141cb93a386Sopenharmony_ci                    }
2142cb93a386Sopenharmony_ci                };
2143cb93a386Sopenharmony_ci
2144cb93a386Sopenharmony_ci                paintFlag("Dither",
2145cb93a386Sopenharmony_ci                          "Default\0No Dither\0Dither\0\0",
2146cb93a386Sopenharmony_ci                          &SkPaintFields::fDither,
2147cb93a386Sopenharmony_ci                          &SkPaint::isDither, &SkPaint::setDither);
2148cb93a386Sopenharmony_ci
2149cb93a386Sopenharmony_ci                int styleIdx = 0;
2150cb93a386Sopenharmony_ci                if (fPaintOverrides.fStyle) {
2151cb93a386Sopenharmony_ci                    styleIdx = SkTo<int>(fPaint.getStyle()) + 1;
2152cb93a386Sopenharmony_ci                }
2153cb93a386Sopenharmony_ci                if (ImGui::Combo("Style", &styleIdx,
2154cb93a386Sopenharmony_ci                                 "Default\0Fill\0Stroke\0Stroke and Fill\0\0"))
2155cb93a386Sopenharmony_ci                {
2156cb93a386Sopenharmony_ci                    if (styleIdx == 0) {
2157cb93a386Sopenharmony_ci                        fPaintOverrides.fStyle = false;
2158cb93a386Sopenharmony_ci                        fPaint.setStyle(SkPaint::kFill_Style);
2159cb93a386Sopenharmony_ci                    } else {
2160cb93a386Sopenharmony_ci                        fPaint.setStyle(SkTo<SkPaint::Style>(styleIdx - 1));
2161cb93a386Sopenharmony_ci                        fPaintOverrides.fStyle = true;
2162cb93a386Sopenharmony_ci                    }
2163cb93a386Sopenharmony_ci                    uiParamsChanged = true;
2164cb93a386Sopenharmony_ci                }
2165cb93a386Sopenharmony_ci
2166cb93a386Sopenharmony_ci                ImGui::Checkbox("Force Runtime Blends", &fPaintOverrides.fForceRuntimeBlend);
2167cb93a386Sopenharmony_ci
2168cb93a386Sopenharmony_ci                ImGui::Checkbox("Override Stroke Width", &fPaintOverrides.fStrokeWidth);
2169cb93a386Sopenharmony_ci                if (fPaintOverrides.fStrokeWidth) {
2170cb93a386Sopenharmony_ci                    float width = fPaint.getStrokeWidth();
2171cb93a386Sopenharmony_ci                    if (ImGui::SliderFloat("Stroke Width", &width, 0, 20)) {
2172cb93a386Sopenharmony_ci                        fPaint.setStrokeWidth(width);
2173cb93a386Sopenharmony_ci                        uiParamsChanged = true;
2174cb93a386Sopenharmony_ci                    }
2175cb93a386Sopenharmony_ci                }
2176cb93a386Sopenharmony_ci
2177cb93a386Sopenharmony_ci                ImGui::Checkbox("Override Miter Limit", &fPaintOverrides.fMiterLimit);
2178cb93a386Sopenharmony_ci                if (fPaintOverrides.fMiterLimit) {
2179cb93a386Sopenharmony_ci                    float miterLimit = fPaint.getStrokeMiter();
2180cb93a386Sopenharmony_ci                    if (ImGui::SliderFloat("Miter Limit", &miterLimit, 0, 20)) {
2181cb93a386Sopenharmony_ci                        fPaint.setStrokeMiter(miterLimit);
2182cb93a386Sopenharmony_ci                        uiParamsChanged = true;
2183cb93a386Sopenharmony_ci                    }
2184cb93a386Sopenharmony_ci                }
2185cb93a386Sopenharmony_ci
2186cb93a386Sopenharmony_ci                int capIdx = 0;
2187cb93a386Sopenharmony_ci                if (fPaintOverrides.fCapType) {
2188cb93a386Sopenharmony_ci                    capIdx = SkTo<int>(fPaint.getStrokeCap()) + 1;
2189cb93a386Sopenharmony_ci                }
2190cb93a386Sopenharmony_ci                if (ImGui::Combo("Cap Type", &capIdx,
2191cb93a386Sopenharmony_ci                                 "Default\0Butt\0Round\0Square\0\0"))
2192cb93a386Sopenharmony_ci                {
2193cb93a386Sopenharmony_ci                    if (capIdx == 0) {
2194cb93a386Sopenharmony_ci                        fPaintOverrides.fCapType = false;
2195cb93a386Sopenharmony_ci                        fPaint.setStrokeCap(SkPaint::kDefault_Cap);
2196cb93a386Sopenharmony_ci                    } else {
2197cb93a386Sopenharmony_ci                        fPaint.setStrokeCap(SkTo<SkPaint::Cap>(capIdx - 1));
2198cb93a386Sopenharmony_ci                        fPaintOverrides.fCapType = true;
2199cb93a386Sopenharmony_ci                    }
2200cb93a386Sopenharmony_ci                    uiParamsChanged = true;
2201cb93a386Sopenharmony_ci                }
2202cb93a386Sopenharmony_ci
2203cb93a386Sopenharmony_ci                int joinIdx = 0;
2204cb93a386Sopenharmony_ci                if (fPaintOverrides.fJoinType) {
2205cb93a386Sopenharmony_ci                    joinIdx = SkTo<int>(fPaint.getStrokeJoin()) + 1;
2206cb93a386Sopenharmony_ci                }
2207cb93a386Sopenharmony_ci                if (ImGui::Combo("Join Type", &joinIdx,
2208cb93a386Sopenharmony_ci                                 "Default\0Miter\0Round\0Bevel\0\0"))
2209cb93a386Sopenharmony_ci                {
2210cb93a386Sopenharmony_ci                    if (joinIdx == 0) {
2211cb93a386Sopenharmony_ci                        fPaintOverrides.fJoinType = false;
2212cb93a386Sopenharmony_ci                        fPaint.setStrokeJoin(SkPaint::kDefault_Join);
2213cb93a386Sopenharmony_ci                    } else {
2214cb93a386Sopenharmony_ci                        fPaint.setStrokeJoin(SkTo<SkPaint::Join>(joinIdx - 1));
2215cb93a386Sopenharmony_ci                        fPaintOverrides.fJoinType = true;
2216cb93a386Sopenharmony_ci                    }
2217cb93a386Sopenharmony_ci                    uiParamsChanged = true;
2218cb93a386Sopenharmony_ci                }
2219cb93a386Sopenharmony_ci            }
2220cb93a386Sopenharmony_ci
2221cb93a386Sopenharmony_ci            if (ImGui::CollapsingHeader("Font")) {
2222cb93a386Sopenharmony_ci                int hintingIdx = 0;
2223cb93a386Sopenharmony_ci                if (fFontOverrides.fHinting) {
2224cb93a386Sopenharmony_ci                    hintingIdx = SkTo<int>(fFont.getHinting()) + 1;
2225cb93a386Sopenharmony_ci                }
2226cb93a386Sopenharmony_ci                if (ImGui::Combo("Hinting", &hintingIdx,
2227cb93a386Sopenharmony_ci                                 "Default\0None\0Slight\0Normal\0Full\0\0"))
2228cb93a386Sopenharmony_ci                {
2229cb93a386Sopenharmony_ci                    if (hintingIdx == 0) {
2230cb93a386Sopenharmony_ci                        fFontOverrides.fHinting = false;
2231cb93a386Sopenharmony_ci                        fFont.setHinting(SkFontHinting::kNone);
2232cb93a386Sopenharmony_ci                    } else {
2233cb93a386Sopenharmony_ci                        fFont.setHinting(SkTo<SkFontHinting>(hintingIdx - 1));
2234cb93a386Sopenharmony_ci                        fFontOverrides.fHinting = true;
2235cb93a386Sopenharmony_ci                    }
2236cb93a386Sopenharmony_ci                    uiParamsChanged = true;
2237cb93a386Sopenharmony_ci                }
2238cb93a386Sopenharmony_ci
2239cb93a386Sopenharmony_ci                auto fontFlag = [this, &uiParamsChanged](const char* label, const char* items,
2240cb93a386Sopenharmony_ci                                                        bool SkFontFields::* flag,
2241cb93a386Sopenharmony_ci                                                        bool (SkFont::* isFlag)() const,
2242cb93a386Sopenharmony_ci                                                        void (SkFont::* setFlag)(bool) )
2243cb93a386Sopenharmony_ci                {
2244cb93a386Sopenharmony_ci                    int itemIndex = 0;
2245cb93a386Sopenharmony_ci                    if (fFontOverrides.*flag) {
2246cb93a386Sopenharmony_ci                        itemIndex = (fFont.*isFlag)() ? 2 : 1;
2247cb93a386Sopenharmony_ci                    }
2248cb93a386Sopenharmony_ci                    if (ImGui::Combo(label, &itemIndex, items)) {
2249cb93a386Sopenharmony_ci                        if (itemIndex == 0) {
2250cb93a386Sopenharmony_ci                            fFontOverrides.*flag = false;
2251cb93a386Sopenharmony_ci                        } else {
2252cb93a386Sopenharmony_ci                            fFontOverrides.*flag = true;
2253cb93a386Sopenharmony_ci                            (fFont.*setFlag)(itemIndex == 2);
2254cb93a386Sopenharmony_ci                        }
2255cb93a386Sopenharmony_ci                        uiParamsChanged = true;
2256cb93a386Sopenharmony_ci                    }
2257cb93a386Sopenharmony_ci                };
2258cb93a386Sopenharmony_ci
2259cb93a386Sopenharmony_ci                fontFlag("Fake Bold Glyphs",
2260cb93a386Sopenharmony_ci                         "Default\0No Fake Bold\0Fake Bold\0\0",
2261cb93a386Sopenharmony_ci                         &SkFontFields::fEmbolden,
2262cb93a386Sopenharmony_ci                         &SkFont::isEmbolden, &SkFont::setEmbolden);
2263cb93a386Sopenharmony_ci
2264cb93a386Sopenharmony_ci                fontFlag("Baseline Snapping",
2265cb93a386Sopenharmony_ci                         "Default\0No Baseline Snapping\0Baseline Snapping\0\0",
2266cb93a386Sopenharmony_ci                         &SkFontFields::fBaselineSnap,
2267cb93a386Sopenharmony_ci                         &SkFont::isBaselineSnap, &SkFont::setBaselineSnap);
2268cb93a386Sopenharmony_ci
2269cb93a386Sopenharmony_ci                fontFlag("Linear Text",
2270cb93a386Sopenharmony_ci                         "Default\0No Linear Text\0Linear Text\0\0",
2271cb93a386Sopenharmony_ci                         &SkFontFields::fLinearMetrics,
2272cb93a386Sopenharmony_ci                         &SkFont::isLinearMetrics, &SkFont::setLinearMetrics);
2273cb93a386Sopenharmony_ci
2274cb93a386Sopenharmony_ci                fontFlag("Subpixel Position Glyphs",
2275cb93a386Sopenharmony_ci                         "Default\0Pixel Text\0Subpixel Text\0\0",
2276cb93a386Sopenharmony_ci                         &SkFontFields::fSubpixel,
2277cb93a386Sopenharmony_ci                         &SkFont::isSubpixel, &SkFont::setSubpixel);
2278cb93a386Sopenharmony_ci
2279cb93a386Sopenharmony_ci                fontFlag("Embedded Bitmap Text",
2280cb93a386Sopenharmony_ci                         "Default\0No Embedded Bitmaps\0Embedded Bitmaps\0\0",
2281cb93a386Sopenharmony_ci                         &SkFontFields::fEmbeddedBitmaps,
2282cb93a386Sopenharmony_ci                         &SkFont::isEmbeddedBitmaps, &SkFont::setEmbeddedBitmaps);
2283cb93a386Sopenharmony_ci
2284cb93a386Sopenharmony_ci                fontFlag("Force Auto-Hinting",
2285cb93a386Sopenharmony_ci                         "Default\0No Force Auto-Hinting\0Force Auto-Hinting\0\0",
2286cb93a386Sopenharmony_ci                         &SkFontFields::fForceAutoHinting,
2287cb93a386Sopenharmony_ci                         &SkFont::isForceAutoHinting, &SkFont::setForceAutoHinting);
2288cb93a386Sopenharmony_ci
2289cb93a386Sopenharmony_ci                int edgingIdx = 0;
2290cb93a386Sopenharmony_ci                if (fFontOverrides.fEdging) {
2291cb93a386Sopenharmony_ci                    edgingIdx = SkTo<int>(fFont.getEdging()) + 1;
2292cb93a386Sopenharmony_ci                }
2293cb93a386Sopenharmony_ci                if (ImGui::Combo("Edging", &edgingIdx,
2294cb93a386Sopenharmony_ci                                 "Default\0Alias\0Antialias\0Subpixel Antialias\0\0"))
2295cb93a386Sopenharmony_ci                {
2296cb93a386Sopenharmony_ci                    if (edgingIdx == 0) {
2297cb93a386Sopenharmony_ci                        fFontOverrides.fEdging = false;
2298cb93a386Sopenharmony_ci                        fFont.setEdging(SkFont::Edging::kAlias);
2299cb93a386Sopenharmony_ci                    } else {
2300cb93a386Sopenharmony_ci                        fFont.setEdging(SkTo<SkFont::Edging>(edgingIdx-1));
2301cb93a386Sopenharmony_ci                        fFontOverrides.fEdging = true;
2302cb93a386Sopenharmony_ci                    }
2303cb93a386Sopenharmony_ci                    uiParamsChanged = true;
2304cb93a386Sopenharmony_ci                }
2305cb93a386Sopenharmony_ci
2306cb93a386Sopenharmony_ci                ImGui::Checkbox("Override Size", &fFontOverrides.fSize);
2307cb93a386Sopenharmony_ci                if (fFontOverrides.fSize) {
2308cb93a386Sopenharmony_ci                    ImGui::DragFloat2("TextRange", fFontOverrides.fSizeRange,
2309cb93a386Sopenharmony_ci                                      0.001f, -10.0f, 300.0f, "%.6f", 2.0f);
2310cb93a386Sopenharmony_ci                    float textSize = fFont.getSize();
2311cb93a386Sopenharmony_ci                    if (ImGui::DragFloat("TextSize", &textSize, 0.001f,
2312cb93a386Sopenharmony_ci                                         fFontOverrides.fSizeRange[0],
2313cb93a386Sopenharmony_ci                                         fFontOverrides.fSizeRange[1],
2314cb93a386Sopenharmony_ci                                         "%.6f", 2.0f))
2315cb93a386Sopenharmony_ci                    {
2316cb93a386Sopenharmony_ci                        fFont.setSize(textSize);
2317cb93a386Sopenharmony_ci                        uiParamsChanged = true;
2318cb93a386Sopenharmony_ci                    }
2319cb93a386Sopenharmony_ci                }
2320cb93a386Sopenharmony_ci
2321cb93a386Sopenharmony_ci                ImGui::Checkbox("Override ScaleX", &fFontOverrides.fScaleX);
2322cb93a386Sopenharmony_ci                if (fFontOverrides.fScaleX) {
2323cb93a386Sopenharmony_ci                    float scaleX = fFont.getScaleX();
2324cb93a386Sopenharmony_ci                    if (ImGui::SliderFloat("ScaleX", &scaleX, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL)) {
2325cb93a386Sopenharmony_ci                        fFont.setScaleX(scaleX);
2326cb93a386Sopenharmony_ci                        uiParamsChanged = true;
2327cb93a386Sopenharmony_ci                    }
2328cb93a386Sopenharmony_ci                }
2329cb93a386Sopenharmony_ci
2330cb93a386Sopenharmony_ci                ImGui::Checkbox("Override SkewX", &fFontOverrides.fSkewX);
2331cb93a386Sopenharmony_ci                if (fFontOverrides.fSkewX) {
2332cb93a386Sopenharmony_ci                    float skewX = fFont.getSkewX();
2333cb93a386Sopenharmony_ci                    if (ImGui::SliderFloat("SkewX", &skewX, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL)) {
2334cb93a386Sopenharmony_ci                        fFont.setSkewX(skewX);
2335cb93a386Sopenharmony_ci                        uiParamsChanged = true;
2336cb93a386Sopenharmony_ci                    }
2337cb93a386Sopenharmony_ci                }
2338cb93a386Sopenharmony_ci            }
2339cb93a386Sopenharmony_ci
2340cb93a386Sopenharmony_ci            {
2341cb93a386Sopenharmony_ci                SkMetaData controls;
2342cb93a386Sopenharmony_ci                if (fSlides[fCurrentSlide]->onGetControls(&controls)) {
2343cb93a386Sopenharmony_ci                    if (ImGui::CollapsingHeader("Current Slide")) {
2344cb93a386Sopenharmony_ci                        SkMetaData::Iter iter(controls);
2345cb93a386Sopenharmony_ci                        const char* name;
2346cb93a386Sopenharmony_ci                        SkMetaData::Type type;
2347cb93a386Sopenharmony_ci                        int count;
2348cb93a386Sopenharmony_ci                        while ((name = iter.next(&type, &count)) != nullptr) {
2349cb93a386Sopenharmony_ci                            if (type == SkMetaData::kScalar_Type) {
2350cb93a386Sopenharmony_ci                                float val[3];
2351cb93a386Sopenharmony_ci                                SkASSERT(count == 3);
2352cb93a386Sopenharmony_ci                                controls.findScalars(name, &count, val);
2353cb93a386Sopenharmony_ci                                if (ImGui::SliderFloat(name, &val[0], val[1], val[2])) {
2354cb93a386Sopenharmony_ci                                    controls.setScalars(name, 3, val);
2355cb93a386Sopenharmony_ci                                }
2356cb93a386Sopenharmony_ci                            } else if (type == SkMetaData::kBool_Type) {
2357cb93a386Sopenharmony_ci                                bool val;
2358cb93a386Sopenharmony_ci                                SkASSERT(count == 1);
2359cb93a386Sopenharmony_ci                                controls.findBool(name, &val);
2360cb93a386Sopenharmony_ci                                if (ImGui::Checkbox(name, &val)) {
2361cb93a386Sopenharmony_ci                                    controls.setBool(name, val);
2362cb93a386Sopenharmony_ci                                }
2363cb93a386Sopenharmony_ci                            }
2364cb93a386Sopenharmony_ci                        }
2365cb93a386Sopenharmony_ci                        fSlides[fCurrentSlide]->onSetControls(controls);
2366cb93a386Sopenharmony_ci                    }
2367cb93a386Sopenharmony_ci                }
2368cb93a386Sopenharmony_ci            }
2369cb93a386Sopenharmony_ci
2370cb93a386Sopenharmony_ci            if (fShowSlidePicker) {
2371cb93a386Sopenharmony_ci                ImGui::SetNextTreeNodeOpen(true);
2372cb93a386Sopenharmony_ci            }
2373cb93a386Sopenharmony_ci            if (ImGui::CollapsingHeader("Slide")) {
2374cb93a386Sopenharmony_ci                static ImGuiTextFilter filter;
2375cb93a386Sopenharmony_ci                static ImVector<const char*> filteredSlideNames;
2376cb93a386Sopenharmony_ci                static ImVector<int> filteredSlideIndices;
2377cb93a386Sopenharmony_ci
2378cb93a386Sopenharmony_ci                if (fShowSlidePicker) {
2379cb93a386Sopenharmony_ci                    ImGui::SetKeyboardFocusHere();
2380cb93a386Sopenharmony_ci                    fShowSlidePicker = false;
2381cb93a386Sopenharmony_ci                }
2382cb93a386Sopenharmony_ci
2383cb93a386Sopenharmony_ci                filter.Draw();
2384cb93a386Sopenharmony_ci                filteredSlideNames.clear();
2385cb93a386Sopenharmony_ci                filteredSlideIndices.clear();
2386cb93a386Sopenharmony_ci                int filteredIndex = 0;
2387cb93a386Sopenharmony_ci                for (int i = 0; i < fSlides.count(); ++i) {
2388cb93a386Sopenharmony_ci                    const char* slideName = fSlides[i]->getName().c_str();
2389cb93a386Sopenharmony_ci                    if (filter.PassFilter(slideName) || i == fCurrentSlide) {
2390cb93a386Sopenharmony_ci                        if (i == fCurrentSlide) {
2391cb93a386Sopenharmony_ci                            filteredIndex = filteredSlideIndices.size();
2392cb93a386Sopenharmony_ci                        }
2393cb93a386Sopenharmony_ci                        filteredSlideNames.push_back(slideName);
2394cb93a386Sopenharmony_ci                        filteredSlideIndices.push_back(i);
2395cb93a386Sopenharmony_ci                    }
2396cb93a386Sopenharmony_ci                }
2397cb93a386Sopenharmony_ci
2398cb93a386Sopenharmony_ci                if (ImGui::ListBox("", &filteredIndex, filteredSlideNames.begin(),
2399cb93a386Sopenharmony_ci                                   filteredSlideNames.size(), 20)) {
2400cb93a386Sopenharmony_ci                    this->setCurrentSlide(filteredSlideIndices[filteredIndex]);
2401cb93a386Sopenharmony_ci                }
2402cb93a386Sopenharmony_ci            }
2403cb93a386Sopenharmony_ci
2404cb93a386Sopenharmony_ci            if (ImGui::CollapsingHeader("Color Mode")) {
2405cb93a386Sopenharmony_ci                ColorMode newMode = fColorMode;
2406cb93a386Sopenharmony_ci                auto cmButton = [&](ColorMode mode, const char* label) {
2407cb93a386Sopenharmony_ci                    if (ImGui::RadioButton(label, mode == fColorMode)) {
2408cb93a386Sopenharmony_ci                        newMode = mode;
2409cb93a386Sopenharmony_ci                    }
2410cb93a386Sopenharmony_ci                };
2411cb93a386Sopenharmony_ci
2412cb93a386Sopenharmony_ci                cmButton(ColorMode::kLegacy, "Legacy 8888");
2413cb93a386Sopenharmony_ci                cmButton(ColorMode::kColorManaged8888, "Color Managed 8888");
2414cb93a386Sopenharmony_ci                cmButton(ColorMode::kColorManagedF16, "Color Managed F16");
2415cb93a386Sopenharmony_ci                cmButton(ColorMode::kColorManagedF16Norm, "Color Managed F16 Norm");
2416cb93a386Sopenharmony_ci
2417cb93a386Sopenharmony_ci                if (newMode != fColorMode) {
2418cb93a386Sopenharmony_ci                    this->setColorMode(newMode);
2419cb93a386Sopenharmony_ci                }
2420cb93a386Sopenharmony_ci
2421cb93a386Sopenharmony_ci                // Pick from common gamuts:
2422cb93a386Sopenharmony_ci                int primariesIdx = 4; // Default: Custom
2423cb93a386Sopenharmony_ci                for (size_t i = 0; i < SK_ARRAY_COUNT(gNamedPrimaries); ++i) {
2424cb93a386Sopenharmony_ci                    if (primaries_equal(*gNamedPrimaries[i].fPrimaries, fColorSpacePrimaries)) {
2425cb93a386Sopenharmony_ci                        primariesIdx = i;
2426cb93a386Sopenharmony_ci                        break;
2427cb93a386Sopenharmony_ci                    }
2428cb93a386Sopenharmony_ci                }
2429cb93a386Sopenharmony_ci
2430cb93a386Sopenharmony_ci                // Let user adjust the gamma
2431cb93a386Sopenharmony_ci                ImGui::SliderFloat("Gamma", &fColorSpaceTransferFn.g, 0.5f, 3.5f);
2432cb93a386Sopenharmony_ci
2433cb93a386Sopenharmony_ci                if (ImGui::Combo("Primaries", &primariesIdx,
2434cb93a386Sopenharmony_ci                                 "sRGB\0AdobeRGB\0P3\0Rec. 2020\0Custom\0\0")) {
2435cb93a386Sopenharmony_ci                    if (primariesIdx >= 0 && primariesIdx <= 3) {
2436cb93a386Sopenharmony_ci                        fColorSpacePrimaries = *gNamedPrimaries[primariesIdx].fPrimaries;
2437cb93a386Sopenharmony_ci                    }
2438cb93a386Sopenharmony_ci                }
2439cb93a386Sopenharmony_ci
2440cb93a386Sopenharmony_ci                if (ImGui::Button("Spin")) {
2441cb93a386Sopenharmony_ci                    float rx = fColorSpacePrimaries.fRX,
2442cb93a386Sopenharmony_ci                          ry = fColorSpacePrimaries.fRY;
2443cb93a386Sopenharmony_ci                    fColorSpacePrimaries.fRX = fColorSpacePrimaries.fGX;
2444cb93a386Sopenharmony_ci                    fColorSpacePrimaries.fRY = fColorSpacePrimaries.fGY;
2445cb93a386Sopenharmony_ci                    fColorSpacePrimaries.fGX = fColorSpacePrimaries.fBX;
2446cb93a386Sopenharmony_ci                    fColorSpacePrimaries.fGY = fColorSpacePrimaries.fBY;
2447cb93a386Sopenharmony_ci                    fColorSpacePrimaries.fBX = rx;
2448cb93a386Sopenharmony_ci                    fColorSpacePrimaries.fBY = ry;
2449cb93a386Sopenharmony_ci                }
2450cb93a386Sopenharmony_ci
2451cb93a386Sopenharmony_ci                // Allow direct editing of gamut
2452cb93a386Sopenharmony_ci                ImGui_Primaries(&fColorSpacePrimaries, &fImGuiGamutPaint);
2453cb93a386Sopenharmony_ci            }
2454cb93a386Sopenharmony_ci
2455cb93a386Sopenharmony_ci            if (ImGui::CollapsingHeader("Animation")) {
2456cb93a386Sopenharmony_ci                bool isPaused = AnimTimer::kPaused_State == fAnimTimer.state();
2457cb93a386Sopenharmony_ci                if (ImGui::Checkbox("Pause", &isPaused)) {
2458cb93a386Sopenharmony_ci                    fAnimTimer.togglePauseResume();
2459cb93a386Sopenharmony_ci                }
2460cb93a386Sopenharmony_ci
2461cb93a386Sopenharmony_ci                float speed = fAnimTimer.getSpeed();
2462cb93a386Sopenharmony_ci                if (ImGui::DragFloat("Speed", &speed, 0.1f)) {
2463cb93a386Sopenharmony_ci                    fAnimTimer.setSpeed(speed);
2464cb93a386Sopenharmony_ci                }
2465cb93a386Sopenharmony_ci            }
2466cb93a386Sopenharmony_ci
2467cb93a386Sopenharmony_ci            if (ImGui::CollapsingHeader("Shaders")) {
2468cb93a386Sopenharmony_ci                bool sksl = params.fGrContextOptions.fShaderCacheStrategy ==
2469cb93a386Sopenharmony_ci                            GrContextOptions::ShaderCacheStrategy::kSkSL;
2470cb93a386Sopenharmony_ci
2471cb93a386Sopenharmony_ci#if defined(SK_VULKAN)
2472cb93a386Sopenharmony_ci                const bool isVulkan = fBackendType == sk_app::Window::kVulkan_BackendType;
2473cb93a386Sopenharmony_ci#else
2474cb93a386Sopenharmony_ci                const bool isVulkan = false;
2475cb93a386Sopenharmony_ci#endif
2476cb93a386Sopenharmony_ci
2477cb93a386Sopenharmony_ci                // To re-load shaders from the currently active programs, we flush all
2478cb93a386Sopenharmony_ci                // caches on one frame, then set a flag to poll the cache on the next frame.
2479cb93a386Sopenharmony_ci                static bool gLoadPending = false;
2480cb93a386Sopenharmony_ci                if (gLoadPending) {
2481cb93a386Sopenharmony_ci                    auto collectShaders = [this](sk_sp<const SkData> key, sk_sp<SkData> data,
2482cb93a386Sopenharmony_ci                                                 const SkString& description, int hitCount) {
2483cb93a386Sopenharmony_ci                        CachedShader& entry(fCachedShaders.push_back());
2484cb93a386Sopenharmony_ci                        entry.fKey = key;
2485cb93a386Sopenharmony_ci                        SkMD5 hash;
2486cb93a386Sopenharmony_ci                        hash.write(key->bytes(), key->size());
2487cb93a386Sopenharmony_ci                        SkMD5::Digest digest = hash.finish();
2488cb93a386Sopenharmony_ci                        for (int i = 0; i < 16; ++i) {
2489cb93a386Sopenharmony_ci                            entry.fKeyString.appendf("%02x", digest.data[i]);
2490cb93a386Sopenharmony_ci                        }
2491cb93a386Sopenharmony_ci                        entry.fKeyDescription = description;
2492cb93a386Sopenharmony_ci
2493cb93a386Sopenharmony_ci                        SkReadBuffer reader(data->data(), data->size());
2494cb93a386Sopenharmony_ci                        entry.fShaderType = GrPersistentCacheUtils::GetType(&reader);
2495cb93a386Sopenharmony_ci                        GrPersistentCacheUtils::UnpackCachedShaders(&reader, entry.fShader,
2496cb93a386Sopenharmony_ci                                                                    entry.fInputs,
2497cb93a386Sopenharmony_ci                                                                    kGrShaderTypeCount);
2498cb93a386Sopenharmony_ci                    };
2499cb93a386Sopenharmony_ci                    fCachedShaders.reset();
2500cb93a386Sopenharmony_ci                    fPersistentCache.foreach(collectShaders);
2501cb93a386Sopenharmony_ci                    gLoadPending = false;
2502cb93a386Sopenharmony_ci
2503cb93a386Sopenharmony_ci#if defined(SK_VULKAN)
2504cb93a386Sopenharmony_ci                    if (isVulkan && !sksl) {
2505cb93a386Sopenharmony_ci                        spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0);
2506cb93a386Sopenharmony_ci                        for (auto& entry : fCachedShaders) {
2507cb93a386Sopenharmony_ci                            for (int i = 0; i < kGrShaderTypeCount; ++i) {
2508cb93a386Sopenharmony_ci                                const SkSL::String& spirv(entry.fShader[i]);
2509cb93a386Sopenharmony_ci                                std::string disasm;
2510cb93a386Sopenharmony_ci                                tools.Disassemble((const uint32_t*)spirv.c_str(), spirv.size() / 4,
2511cb93a386Sopenharmony_ci                                                  &disasm);
2512cb93a386Sopenharmony_ci                                entry.fShader[i].assign(disasm);
2513cb93a386Sopenharmony_ci                            }
2514cb93a386Sopenharmony_ci                        }
2515cb93a386Sopenharmony_ci                    }
2516cb93a386Sopenharmony_ci#endif
2517cb93a386Sopenharmony_ci                }
2518cb93a386Sopenharmony_ci
2519cb93a386Sopenharmony_ci                // Defer actually doing the View/Apply logic so that we can trigger an Apply when we
2520cb93a386Sopenharmony_ci                // start or finish hovering on a tree node in the list below:
2521cb93a386Sopenharmony_ci                bool doView      = ImGui::Button("View"); ImGui::SameLine();
2522cb93a386Sopenharmony_ci                bool doApply     = ImGui::Button("Apply Changes"); ImGui::SameLine();
2523cb93a386Sopenharmony_ci                bool doDump      = ImGui::Button("Dump SkSL to resources/sksl/");
2524cb93a386Sopenharmony_ci
2525cb93a386Sopenharmony_ci                int newOptLevel = fOptLevel;
2526cb93a386Sopenharmony_ci                ImGui::RadioButton("SkSL", &newOptLevel, kShaderOptLevel_Source);
2527cb93a386Sopenharmony_ci                ImGui::SameLine();
2528cb93a386Sopenharmony_ci                ImGui::RadioButton("Compile", &newOptLevel, kShaderOptLevel_Compile);
2529cb93a386Sopenharmony_ci                ImGui::SameLine();
2530cb93a386Sopenharmony_ci                ImGui::RadioButton("Optimize", &newOptLevel, kShaderOptLevel_Optimize);
2531cb93a386Sopenharmony_ci                ImGui::SameLine();
2532cb93a386Sopenharmony_ci                ImGui::RadioButton("Inline", &newOptLevel, kShaderOptLevel_Inline);
2533cb93a386Sopenharmony_ci
2534cb93a386Sopenharmony_ci                // If we are changing the compile mode, we want to reset the cache and redo
2535cb93a386Sopenharmony_ci                // everything.
2536cb93a386Sopenharmony_ci                if (doDump || newOptLevel != fOptLevel) {
2537cb93a386Sopenharmony_ci                    sksl = doDump || (newOptLevel == kShaderOptLevel_Source);
2538cb93a386Sopenharmony_ci                    fOptLevel = (ShaderOptLevel)newOptLevel;
2539cb93a386Sopenharmony_ci                    switch (fOptLevel) {
2540cb93a386Sopenharmony_ci                        case kShaderOptLevel_Source:
2541cb93a386Sopenharmony_ci                            Compiler::EnableOptimizer(OverrideFlag::kDefault);
2542cb93a386Sopenharmony_ci                            Compiler::EnableInliner(OverrideFlag::kDefault);
2543cb93a386Sopenharmony_ci                            break;
2544cb93a386Sopenharmony_ci                        case kShaderOptLevel_Compile:
2545cb93a386Sopenharmony_ci                            Compiler::EnableOptimizer(OverrideFlag::kOff);
2546cb93a386Sopenharmony_ci                            Compiler::EnableInliner(OverrideFlag::kOff);
2547cb93a386Sopenharmony_ci                            break;
2548cb93a386Sopenharmony_ci                        case kShaderOptLevel_Optimize:
2549cb93a386Sopenharmony_ci                            Compiler::EnableOptimizer(OverrideFlag::kOn);
2550cb93a386Sopenharmony_ci                            Compiler::EnableInliner(OverrideFlag::kOff);
2551cb93a386Sopenharmony_ci                            break;
2552cb93a386Sopenharmony_ci                        case kShaderOptLevel_Inline:
2553cb93a386Sopenharmony_ci                            Compiler::EnableOptimizer(OverrideFlag::kOn);
2554cb93a386Sopenharmony_ci                            Compiler::EnableInliner(OverrideFlag::kOn);
2555cb93a386Sopenharmony_ci                            break;
2556cb93a386Sopenharmony_ci                    }
2557cb93a386Sopenharmony_ci
2558cb93a386Sopenharmony_ci                    params.fGrContextOptions.fShaderCacheStrategy =
2559cb93a386Sopenharmony_ci                            sksl ? GrContextOptions::ShaderCacheStrategy::kSkSL
2560cb93a386Sopenharmony_ci                                 : GrContextOptions::ShaderCacheStrategy::kBackendSource;
2561cb93a386Sopenharmony_ci                    displayParamsChanged = true;
2562cb93a386Sopenharmony_ci                    doView = true;
2563cb93a386Sopenharmony_ci
2564cb93a386Sopenharmony_ci                    fDeferredActions.push_back([=]() {
2565cb93a386Sopenharmony_ci                        // Reset the cache.
2566cb93a386Sopenharmony_ci                        fPersistentCache.reset();
2567cb93a386Sopenharmony_ci                        // Dump the cache once we have drawn a frame with it.
2568cb93a386Sopenharmony_ci                        if (doDump) {
2569cb93a386Sopenharmony_ci                            fDeferredActions.push_back([this]() {
2570cb93a386Sopenharmony_ci                                this->dumpShadersToResources();
2571cb93a386Sopenharmony_ci                            });
2572cb93a386Sopenharmony_ci                        }
2573cb93a386Sopenharmony_ci                    });
2574cb93a386Sopenharmony_ci                }
2575cb93a386Sopenharmony_ci
2576cb93a386Sopenharmony_ci                ImGui::BeginChild("##ScrollingRegion");
2577cb93a386Sopenharmony_ci                for (auto& entry : fCachedShaders) {
2578cb93a386Sopenharmony_ci                    bool inTreeNode = ImGui::TreeNode(entry.fKeyString.c_str());
2579cb93a386Sopenharmony_ci                    bool hovered = ImGui::IsItemHovered();
2580cb93a386Sopenharmony_ci                    if (hovered != entry.fHovered) {
2581cb93a386Sopenharmony_ci                        // Force an Apply to patch the highlight shader in/out
2582cb93a386Sopenharmony_ci                        entry.fHovered = hovered;
2583cb93a386Sopenharmony_ci                        doApply = true;
2584cb93a386Sopenharmony_ci                    }
2585cb93a386Sopenharmony_ci                    if (inTreeNode) {
2586cb93a386Sopenharmony_ci                        auto stringBox = [](const char* label, std::string* str) {
2587cb93a386Sopenharmony_ci                            // Full width, and not too much space for each shader
2588cb93a386Sopenharmony_ci                            int lines = std::count(str->begin(), str->end(), '\n') + 2;
2589cb93a386Sopenharmony_ci                            ImVec2 boxSize(-1.0f, ImGui::GetTextLineHeight() * std::min(lines, 30));
2590cb93a386Sopenharmony_ci                            ImGui::InputTextMultiline(label, str, boxSize);
2591cb93a386Sopenharmony_ci                        };
2592cb93a386Sopenharmony_ci                        if (ImGui::TreeNode("Key")) {
2593cb93a386Sopenharmony_ci                            ImGui::TextWrapped("%s", entry.fKeyDescription.c_str());
2594cb93a386Sopenharmony_ci                            ImGui::TreePop();
2595cb93a386Sopenharmony_ci                        }
2596cb93a386Sopenharmony_ci                        stringBox("##VP", &entry.fShader[kVertex_GrShaderType]);
2597cb93a386Sopenharmony_ci                        stringBox("##FP", &entry.fShader[kFragment_GrShaderType]);
2598cb93a386Sopenharmony_ci                        ImGui::TreePop();
2599cb93a386Sopenharmony_ci                    }
2600cb93a386Sopenharmony_ci                }
2601cb93a386Sopenharmony_ci                ImGui::EndChild();
2602cb93a386Sopenharmony_ci
2603cb93a386Sopenharmony_ci                if (doView) {
2604cb93a386Sopenharmony_ci                    fPersistentCache.reset();
2605cb93a386Sopenharmony_ci                    ctx->priv().getGpu()->resetShaderCacheForTesting();
2606cb93a386Sopenharmony_ci                    gLoadPending = true;
2607cb93a386Sopenharmony_ci                }
2608cb93a386Sopenharmony_ci
2609cb93a386Sopenharmony_ci                // We don't support updating SPIRV shaders. We could re-assemble them (with edits),
2610cb93a386Sopenharmony_ci                // but I'm not sure anyone wants to do that.
2611cb93a386Sopenharmony_ci                if (isVulkan && !sksl) {
2612cb93a386Sopenharmony_ci                    doApply = false;
2613cb93a386Sopenharmony_ci                }
2614cb93a386Sopenharmony_ci                if (doApply) {
2615cb93a386Sopenharmony_ci                    fPersistentCache.reset();
2616cb93a386Sopenharmony_ci                    ctx->priv().getGpu()->resetShaderCacheForTesting();
2617cb93a386Sopenharmony_ci                    for (auto& entry : fCachedShaders) {
2618cb93a386Sopenharmony_ci                        SkSL::String backup = entry.fShader[kFragment_GrShaderType];
2619cb93a386Sopenharmony_ci                        if (entry.fHovered) {
2620cb93a386Sopenharmony_ci                            // The hovered item (if any) gets a special shader to make it
2621cb93a386Sopenharmony_ci                            // identifiable.
2622cb93a386Sopenharmony_ci                            SkSL::String& fragShader = entry.fShader[kFragment_GrShaderType];
2623cb93a386Sopenharmony_ci                            switch (entry.fShaderType) {
2624cb93a386Sopenharmony_ci                                case SkSetFourByteTag('S', 'K', 'S', 'L'): {
2625cb93a386Sopenharmony_ci                                    fragShader = build_sksl_highlight_shader();
2626cb93a386Sopenharmony_ci                                    break;
2627cb93a386Sopenharmony_ci                                }
2628cb93a386Sopenharmony_ci                                case SkSetFourByteTag('G', 'L', 'S', 'L'): {
2629cb93a386Sopenharmony_ci                                    fragShader = build_glsl_highlight_shader(
2630cb93a386Sopenharmony_ci                                        *ctx->priv().caps()->shaderCaps());
2631cb93a386Sopenharmony_ci                                    break;
2632cb93a386Sopenharmony_ci                                }
2633cb93a386Sopenharmony_ci                                case SkSetFourByteTag('M', 'S', 'L', ' '): {
2634cb93a386Sopenharmony_ci                                    fragShader = build_metal_highlight_shader(fragShader);
2635cb93a386Sopenharmony_ci                                    break;
2636cb93a386Sopenharmony_ci                                }
2637cb93a386Sopenharmony_ci                            }
2638cb93a386Sopenharmony_ci                        }
2639cb93a386Sopenharmony_ci
2640cb93a386Sopenharmony_ci                        auto data = GrPersistentCacheUtils::PackCachedShaders(entry.fShaderType,
2641cb93a386Sopenharmony_ci                                                                              entry.fShader,
2642cb93a386Sopenharmony_ci                                                                              entry.fInputs,
2643cb93a386Sopenharmony_ci                                                                              kGrShaderTypeCount);
2644cb93a386Sopenharmony_ci                        fPersistentCache.store(*entry.fKey, *data, entry.fKeyDescription);
2645cb93a386Sopenharmony_ci
2646cb93a386Sopenharmony_ci                        entry.fShader[kFragment_GrShaderType] = backup;
2647cb93a386Sopenharmony_ci                    }
2648cb93a386Sopenharmony_ci                }
2649cb93a386Sopenharmony_ci            }
2650cb93a386Sopenharmony_ci
2651cb93a386Sopenharmony_ci            if (ImGui::CollapsingHeader("SkVM")) {
2652cb93a386Sopenharmony_ci                auto* cache = SkVMBlitter::TryAcquireProgramCache();
2653cb93a386Sopenharmony_ci                SkASSERT(cache);
2654cb93a386Sopenharmony_ci
2655cb93a386Sopenharmony_ci                if (ImGui::Button("Clear")) {
2656cb93a386Sopenharmony_ci                    cache->reset();
2657cb93a386Sopenharmony_ci                    fDisassemblyCache.reset();
2658cb93a386Sopenharmony_ci                }
2659cb93a386Sopenharmony_ci
2660cb93a386Sopenharmony_ci                // First, go through the cache and restore the original program if we were hovering
2661cb93a386Sopenharmony_ci                if (!fHoveredProgram.empty()) {
2662cb93a386Sopenharmony_ci                    auto restoreHoveredProgram = [this](const SkVMBlitter::Key* key,
2663cb93a386Sopenharmony_ci                                                        skvm::Program* program) {
2664cb93a386Sopenharmony_ci                        if (*key == fHoveredKey) {
2665cb93a386Sopenharmony_ci                            *program = std::move(fHoveredProgram);
2666cb93a386Sopenharmony_ci                            fHoveredProgram = {};
2667cb93a386Sopenharmony_ci                        }
2668cb93a386Sopenharmony_ci                    };
2669cb93a386Sopenharmony_ci                    cache->foreach(restoreHoveredProgram);
2670cb93a386Sopenharmony_ci                }
2671cb93a386Sopenharmony_ci
2672cb93a386Sopenharmony_ci                // Now iterate again, and dump any expanded program. If any program is hovered,
2673cb93a386Sopenharmony_ci                // patch it, and remember the original (so it can be restored next frame).
2674cb93a386Sopenharmony_ci                auto showVMEntry = [this](const SkVMBlitter::Key* key, skvm::Program* program) {
2675cb93a386Sopenharmony_ci                    SkString keyString = SkVMBlitter::DebugName(*key);
2676cb93a386Sopenharmony_ci                    bool inTreeNode = ImGui::TreeNode(keyString.c_str());
2677cb93a386Sopenharmony_ci                    bool hovered = ImGui::IsItemHovered();
2678cb93a386Sopenharmony_ci
2679cb93a386Sopenharmony_ci                    if (inTreeNode) {
2680cb93a386Sopenharmony_ci                        auto stringBox = [](const char* label, std::string* str) {
2681cb93a386Sopenharmony_ci                            int lines = std::count(str->begin(), str->end(), '\n') + 2;
2682cb93a386Sopenharmony_ci                            ImVec2 boxSize(-1.0f, ImGui::GetTextLineHeight() * std::min(lines, 30));
2683cb93a386Sopenharmony_ci                            ImGui::InputTextMultiline(label, str, boxSize);
2684cb93a386Sopenharmony_ci                        };
2685cb93a386Sopenharmony_ci
2686cb93a386Sopenharmony_ci                        SkDynamicMemoryWStream stream;
2687cb93a386Sopenharmony_ci                        program->dump(&stream);
2688cb93a386Sopenharmony_ci                        auto dumpData = stream.detachAsData();
2689cb93a386Sopenharmony_ci                        std::string dumpString((const char*)dumpData->data(), dumpData->size());
2690cb93a386Sopenharmony_ci                        stringBox("##VM", &dumpString);
2691cb93a386Sopenharmony_ci
2692cb93a386Sopenharmony_ci#if defined(SKVM_JIT)
2693cb93a386Sopenharmony_ci                        std::string* asmString = fDisassemblyCache.find(*key);
2694cb93a386Sopenharmony_ci                        if (!asmString) {
2695cb93a386Sopenharmony_ci                            program->disassemble(&stream);
2696cb93a386Sopenharmony_ci                            auto asmData = stream.detachAsData();
2697cb93a386Sopenharmony_ci                            asmString = fDisassemblyCache.set(
2698cb93a386Sopenharmony_ci                                    *key,
2699cb93a386Sopenharmony_ci                                    std::string((const char*)asmData->data(), asmData->size()));
2700cb93a386Sopenharmony_ci                        }
2701cb93a386Sopenharmony_ci                        stringBox("##ASM", asmString);
2702cb93a386Sopenharmony_ci#endif
2703cb93a386Sopenharmony_ci
2704cb93a386Sopenharmony_ci                        ImGui::TreePop();
2705cb93a386Sopenharmony_ci                    }
2706cb93a386Sopenharmony_ci                    if (hovered) {
2707cb93a386Sopenharmony_ci                        // Generate a new blitter that just draws magenta
2708cb93a386Sopenharmony_ci                        skvm::Program highlightProgram = build_skvm_highlight_program(
2709cb93a386Sopenharmony_ci                                static_cast<SkColorType>(key->colorType), program->nargs());
2710cb93a386Sopenharmony_ci
2711cb93a386Sopenharmony_ci                        fHoveredKey = *key;
2712cb93a386Sopenharmony_ci                        fHoveredProgram = std::move(*program);
2713cb93a386Sopenharmony_ci                        *program = std::move(highlightProgram);
2714cb93a386Sopenharmony_ci                    }
2715cb93a386Sopenharmony_ci                };
2716cb93a386Sopenharmony_ci                cache->foreach(showVMEntry);
2717cb93a386Sopenharmony_ci
2718cb93a386Sopenharmony_ci                SkVMBlitter::ReleaseProgramCache();
2719cb93a386Sopenharmony_ci            }
2720cb93a386Sopenharmony_ci        }
2721cb93a386Sopenharmony_ci        if (displayParamsChanged || uiParamsChanged) {
2722cb93a386Sopenharmony_ci            fDeferredActions.push_back([=]() {
2723cb93a386Sopenharmony_ci                if (displayParamsChanged) {
2724cb93a386Sopenharmony_ci                    fWindow->setRequestedDisplayParams(params);
2725cb93a386Sopenharmony_ci                }
2726cb93a386Sopenharmony_ci                fWindow->inval();
2727cb93a386Sopenharmony_ci                this->updateTitle();
2728cb93a386Sopenharmony_ci            });
2729cb93a386Sopenharmony_ci        }
2730cb93a386Sopenharmony_ci        ImGui::End();
2731cb93a386Sopenharmony_ci    }
2732cb93a386Sopenharmony_ci
2733cb93a386Sopenharmony_ci    if (gShaderErrorHandler.fErrors.count()) {
2734cb93a386Sopenharmony_ci        ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
2735cb93a386Sopenharmony_ci        ImGui::Begin("Shader Errors", nullptr, ImGuiWindowFlags_NoFocusOnAppearing);
2736cb93a386Sopenharmony_ci        for (int i = 0; i < gShaderErrorHandler.fErrors.count(); ++i) {
2737cb93a386Sopenharmony_ci            ImGui::TextWrapped("%s", gShaderErrorHandler.fErrors[i].c_str());
2738cb93a386Sopenharmony_ci            SkSL::String sksl(gShaderErrorHandler.fShaders[i].c_str());
2739cb93a386Sopenharmony_ci            GrShaderUtils::VisitLineByLine(sksl, [](int lineNumber, const char* lineText) {
2740cb93a386Sopenharmony_ci                ImGui::TextWrapped("%4i\t%s\n", lineNumber, lineText);
2741cb93a386Sopenharmony_ci            });
2742cb93a386Sopenharmony_ci        }
2743cb93a386Sopenharmony_ci        ImGui::End();
2744cb93a386Sopenharmony_ci        gShaderErrorHandler.reset();
2745cb93a386Sopenharmony_ci    }
2746cb93a386Sopenharmony_ci
2747cb93a386Sopenharmony_ci    if (fShowZoomWindow && fLastImage) {
2748cb93a386Sopenharmony_ci        ImGui::SetNextWindowSize(ImVec2(200, 200), ImGuiCond_FirstUseEver);
2749cb93a386Sopenharmony_ci        if (ImGui::Begin("Zoom", &fShowZoomWindow)) {
2750cb93a386Sopenharmony_ci            static int zoomFactor = 8;
2751cb93a386Sopenharmony_ci            if (ImGui::Button("<<")) {
2752cb93a386Sopenharmony_ci                zoomFactor = std::max(zoomFactor / 2, 4);
2753cb93a386Sopenharmony_ci            }
2754cb93a386Sopenharmony_ci            ImGui::SameLine(); ImGui::Text("%2d", zoomFactor); ImGui::SameLine();
2755cb93a386Sopenharmony_ci            if (ImGui::Button(">>")) {
2756cb93a386Sopenharmony_ci                zoomFactor = std::min(zoomFactor * 2, 32);
2757cb93a386Sopenharmony_ci            }
2758cb93a386Sopenharmony_ci
2759cb93a386Sopenharmony_ci            if (!fZoomWindowFixed) {
2760cb93a386Sopenharmony_ci                ImVec2 mousePos = ImGui::GetMousePos();
2761cb93a386Sopenharmony_ci                fZoomWindowLocation = SkPoint::Make(mousePos.x, mousePos.y);
2762cb93a386Sopenharmony_ci            }
2763cb93a386Sopenharmony_ci            SkScalar x = fZoomWindowLocation.x();
2764cb93a386Sopenharmony_ci            SkScalar y = fZoomWindowLocation.y();
2765cb93a386Sopenharmony_ci            int xInt = SkScalarRoundToInt(x);
2766cb93a386Sopenharmony_ci            int yInt = SkScalarRoundToInt(y);
2767cb93a386Sopenharmony_ci            ImVec2 avail = ImGui::GetContentRegionAvail();
2768cb93a386Sopenharmony_ci
2769cb93a386Sopenharmony_ci            uint32_t pixel = 0;
2770cb93a386Sopenharmony_ci            SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
2771cb93a386Sopenharmony_ci            auto dContext = fWindow->directContext();
2772cb93a386Sopenharmony_ci            if (fLastImage->readPixels(dContext, info, &pixel, info.minRowBytes(), xInt, yInt)) {
2773cb93a386Sopenharmony_ci                ImGui::SameLine();
2774cb93a386Sopenharmony_ci                ImGui::Text("(X, Y): %d, %d RGBA: %X %X %X %X",
2775cb93a386Sopenharmony_ci                            xInt, yInt,
2776cb93a386Sopenharmony_ci                            SkGetPackedR32(pixel), SkGetPackedG32(pixel),
2777cb93a386Sopenharmony_ci                            SkGetPackedB32(pixel), SkGetPackedA32(pixel));
2778cb93a386Sopenharmony_ci            }
2779cb93a386Sopenharmony_ci
2780cb93a386Sopenharmony_ci            fImGuiLayer.skiaWidget(avail, [=, lastImage = fLastImage](SkCanvas* c) {
2781cb93a386Sopenharmony_ci                // Translate so the region of the image that's under the mouse cursor is centered
2782cb93a386Sopenharmony_ci                // in the zoom canvas:
2783cb93a386Sopenharmony_ci                c->scale(zoomFactor, zoomFactor);
2784cb93a386Sopenharmony_ci                c->translate(avail.x * 0.5f / zoomFactor - x - 0.5f,
2785cb93a386Sopenharmony_ci                             avail.y * 0.5f / zoomFactor - y - 0.5f);
2786cb93a386Sopenharmony_ci                c->drawImage(lastImage, 0, 0);
2787cb93a386Sopenharmony_ci
2788cb93a386Sopenharmony_ci                SkPaint outline;
2789cb93a386Sopenharmony_ci                outline.setStyle(SkPaint::kStroke_Style);
2790cb93a386Sopenharmony_ci                c->drawRect(SkRect::MakeXYWH(x, y, 1, 1), outline);
2791cb93a386Sopenharmony_ci            });
2792cb93a386Sopenharmony_ci        }
2793cb93a386Sopenharmony_ci
2794cb93a386Sopenharmony_ci        ImGui::End();
2795cb93a386Sopenharmony_ci    }
2796cb93a386Sopenharmony_ci}
2797cb93a386Sopenharmony_ci
2798cb93a386Sopenharmony_civoid Viewer::dumpShadersToResources() {
2799cb93a386Sopenharmony_ci    // Sort the list of cached shaders so we can maintain some minimal level of consistency.
2800cb93a386Sopenharmony_ci    // It doesn't really matter, but it will keep files from switching places unpredictably.
2801cb93a386Sopenharmony_ci    std::vector<const CachedShader*> shaders;
2802cb93a386Sopenharmony_ci    shaders.reserve(fCachedShaders.size());
2803cb93a386Sopenharmony_ci    for (const CachedShader& shader : fCachedShaders) {
2804cb93a386Sopenharmony_ci        shaders.push_back(&shader);
2805cb93a386Sopenharmony_ci    }
2806cb93a386Sopenharmony_ci
2807cb93a386Sopenharmony_ci    std::sort(shaders.begin(), shaders.end(), [](const CachedShader* a, const CachedShader* b) {
2808cb93a386Sopenharmony_ci        return std::tie(a->fShader[kFragment_GrShaderType], a->fShader[kVertex_GrShaderType]) <
2809cb93a386Sopenharmony_ci               std::tie(b->fShader[kFragment_GrShaderType], b->fShader[kVertex_GrShaderType]);
2810cb93a386Sopenharmony_ci    });
2811cb93a386Sopenharmony_ci
2812cb93a386Sopenharmony_ci    // Make the resources/sksl/SlideName/ directory.
2813cb93a386Sopenharmony_ci    SkString directory = SkStringPrintf("%ssksl/%s",
2814cb93a386Sopenharmony_ci                                        GetResourcePath().c_str(),
2815cb93a386Sopenharmony_ci                                        fSlides[fCurrentSlide]->getName().c_str());
2816cb93a386Sopenharmony_ci    if (!sk_mkdir(directory.c_str())) {
2817cb93a386Sopenharmony_ci        SkDEBUGFAILF("Unable to create directory '%s'", directory.c_str());
2818cb93a386Sopenharmony_ci        return;
2819cb93a386Sopenharmony_ci    }
2820cb93a386Sopenharmony_ci
2821cb93a386Sopenharmony_ci    int index = 0;
2822cb93a386Sopenharmony_ci    for (const auto& entry : shaders) {
2823cb93a386Sopenharmony_ci        SkString vertPath = SkStringPrintf("%s/Vertex_%02d.vert", directory.c_str(), index);
2824cb93a386Sopenharmony_ci        FILE* vertFile = sk_fopen(vertPath.c_str(), kWrite_SkFILE_Flag);
2825cb93a386Sopenharmony_ci        if (vertFile) {
2826cb93a386Sopenharmony_ci            const SkSL::String& vertText = entry->fShader[kVertex_GrShaderType];
2827cb93a386Sopenharmony_ci            SkAssertResult(sk_fwrite(vertText.c_str(), vertText.size(), vertFile));
2828cb93a386Sopenharmony_ci            sk_fclose(vertFile);
2829cb93a386Sopenharmony_ci        } else {
2830cb93a386Sopenharmony_ci            SkDEBUGFAILF("Unable to write shader to path '%s'", vertPath.c_str());
2831cb93a386Sopenharmony_ci        }
2832cb93a386Sopenharmony_ci
2833cb93a386Sopenharmony_ci        SkString fragPath = SkStringPrintf("%s/Fragment_%02d.frag", directory.c_str(), index);
2834cb93a386Sopenharmony_ci        FILE* fragFile = sk_fopen(fragPath.c_str(), kWrite_SkFILE_Flag);
2835cb93a386Sopenharmony_ci        if (fragFile) {
2836cb93a386Sopenharmony_ci            const SkSL::String& fragText = entry->fShader[kFragment_GrShaderType];
2837cb93a386Sopenharmony_ci            SkAssertResult(sk_fwrite(fragText.c_str(), fragText.size(), fragFile));
2838cb93a386Sopenharmony_ci            sk_fclose(fragFile);
2839cb93a386Sopenharmony_ci        } else {
2840cb93a386Sopenharmony_ci            SkDEBUGFAILF("Unable to write shader to path '%s'", fragPath.c_str());
2841cb93a386Sopenharmony_ci        }
2842cb93a386Sopenharmony_ci
2843cb93a386Sopenharmony_ci        ++index;
2844cb93a386Sopenharmony_ci    }
2845cb93a386Sopenharmony_ci}
2846cb93a386Sopenharmony_ci
2847cb93a386Sopenharmony_civoid Viewer::onIdle() {
2848cb93a386Sopenharmony_ci    SkTArray<std::function<void()>> actionsToRun;
2849cb93a386Sopenharmony_ci    actionsToRun.swap(fDeferredActions);
2850cb93a386Sopenharmony_ci
2851cb93a386Sopenharmony_ci    for (const auto& fn : actionsToRun) {
2852cb93a386Sopenharmony_ci        fn();
2853cb93a386Sopenharmony_ci    }
2854cb93a386Sopenharmony_ci
2855cb93a386Sopenharmony_ci    fStatsLayer.beginTiming(fAnimateTimer);
2856cb93a386Sopenharmony_ci    fAnimTimer.updateTime();
2857cb93a386Sopenharmony_ci    bool animateWantsInval = fSlides[fCurrentSlide]->animate(fAnimTimer.nanos());
2858cb93a386Sopenharmony_ci    fStatsLayer.endTiming(fAnimateTimer);
2859cb93a386Sopenharmony_ci
2860cb93a386Sopenharmony_ci    ImGuiIO& io = ImGui::GetIO();
2861cb93a386Sopenharmony_ci    // ImGui always has at least one "active" window, which is the default "Debug" window. It may
2862cb93a386Sopenharmony_ci    // not be visible, though. So we need to redraw if there is at least one visible window, or
2863cb93a386Sopenharmony_ci    // more than one active window. Newly created windows are active but not visible for one frame
2864cb93a386Sopenharmony_ci    // while they determine their layout and sizing.
2865cb93a386Sopenharmony_ci    if (animateWantsInval || fStatsLayer.getActive() || fRefresh ||
2866cb93a386Sopenharmony_ci        io.MetricsActiveWindows > 1 || io.MetricsRenderWindows > 0) {
2867cb93a386Sopenharmony_ci        fWindow->inval();
2868cb93a386Sopenharmony_ci    }
2869cb93a386Sopenharmony_ci}
2870cb93a386Sopenharmony_ci
2871cb93a386Sopenharmony_citemplate <typename OptionsFunc>
2872cb93a386Sopenharmony_cistatic void WriteStateObject(SkJSONWriter& writer, const char* name, const char* value,
2873cb93a386Sopenharmony_ci                             OptionsFunc&& optionsFunc) {
2874cb93a386Sopenharmony_ci    writer.beginObject();
2875cb93a386Sopenharmony_ci    {
2876cb93a386Sopenharmony_ci        writer.appendString(kName , name);
2877cb93a386Sopenharmony_ci        writer.appendString(kValue, value);
2878cb93a386Sopenharmony_ci
2879cb93a386Sopenharmony_ci        writer.beginArray(kOptions);
2880cb93a386Sopenharmony_ci        {
2881cb93a386Sopenharmony_ci            optionsFunc(writer);
2882cb93a386Sopenharmony_ci        }
2883cb93a386Sopenharmony_ci        writer.endArray();
2884cb93a386Sopenharmony_ci    }
2885cb93a386Sopenharmony_ci    writer.endObject();
2886cb93a386Sopenharmony_ci}
2887cb93a386Sopenharmony_ci
2888cb93a386Sopenharmony_ci
2889cb93a386Sopenharmony_civoid Viewer::updateUIState() {
2890cb93a386Sopenharmony_ci    if (!fWindow) {
2891cb93a386Sopenharmony_ci        return;
2892cb93a386Sopenharmony_ci    }
2893cb93a386Sopenharmony_ci    if (fWindow->sampleCount() < 1) {
2894cb93a386Sopenharmony_ci        return; // Surface hasn't been created yet.
2895cb93a386Sopenharmony_ci    }
2896cb93a386Sopenharmony_ci
2897cb93a386Sopenharmony_ci    SkDynamicMemoryWStream memStream;
2898cb93a386Sopenharmony_ci    SkJSONWriter writer(&memStream);
2899cb93a386Sopenharmony_ci    writer.beginArray();
2900cb93a386Sopenharmony_ci
2901cb93a386Sopenharmony_ci    // Slide state
2902cb93a386Sopenharmony_ci    WriteStateObject(writer, kSlideStateName, fSlides[fCurrentSlide]->getName().c_str(),
2903cb93a386Sopenharmony_ci        [this](SkJSONWriter& writer) {
2904cb93a386Sopenharmony_ci            for(const auto& slide : fSlides) {
2905cb93a386Sopenharmony_ci                writer.appendString(slide->getName().c_str());
2906cb93a386Sopenharmony_ci            }
2907cb93a386Sopenharmony_ci        });
2908cb93a386Sopenharmony_ci
2909cb93a386Sopenharmony_ci    // Backend state
2910cb93a386Sopenharmony_ci    WriteStateObject(writer, kBackendStateName, kBackendTypeStrings[fBackendType],
2911cb93a386Sopenharmony_ci        [](SkJSONWriter& writer) {
2912cb93a386Sopenharmony_ci            for (const auto& str : kBackendTypeStrings) {
2913cb93a386Sopenharmony_ci                writer.appendString(str);
2914cb93a386Sopenharmony_ci            }
2915cb93a386Sopenharmony_ci        });
2916cb93a386Sopenharmony_ci
2917cb93a386Sopenharmony_ci    // MSAA state
2918cb93a386Sopenharmony_ci    const auto countString = SkStringPrintf("%d", fWindow->sampleCount());
2919cb93a386Sopenharmony_ci    WriteStateObject(writer, kMSAAStateName, countString.c_str(),
2920cb93a386Sopenharmony_ci        [this](SkJSONWriter& writer) {
2921cb93a386Sopenharmony_ci            writer.appendS32(0);
2922cb93a386Sopenharmony_ci
2923cb93a386Sopenharmony_ci            if (sk_app::Window::kRaster_BackendType == fBackendType) {
2924cb93a386Sopenharmony_ci                return;
2925cb93a386Sopenharmony_ci            }
2926cb93a386Sopenharmony_ci
2927cb93a386Sopenharmony_ci            for (int msaa : {4, 8, 16}) {
2928cb93a386Sopenharmony_ci                writer.appendS32(msaa);
2929cb93a386Sopenharmony_ci            }
2930cb93a386Sopenharmony_ci        });
2931cb93a386Sopenharmony_ci
2932cb93a386Sopenharmony_ci    // Path renderer state
2933cb93a386Sopenharmony_ci    GpuPathRenderers pr = fWindow->getRequestedDisplayParams().fGrContextOptions.fGpuPathRenderers;
2934cb93a386Sopenharmony_ci    WriteStateObject(writer, kPathRendererStateName, gPathRendererNames[pr].c_str(),
2935cb93a386Sopenharmony_ci        [this](SkJSONWriter& writer) {
2936cb93a386Sopenharmony_ci            auto ctx = fWindow->directContext();
2937cb93a386Sopenharmony_ci            if (!ctx) {
2938cb93a386Sopenharmony_ci                writer.appendString("Software");
2939cb93a386Sopenharmony_ci            } else {
2940cb93a386Sopenharmony_ci                writer.appendString(gPathRendererNames[GpuPathRenderers::kDefault].c_str());
2941cb93a386Sopenharmony_ci#if SK_GPU_V1
2942cb93a386Sopenharmony_ci                if (fWindow->sampleCount() > 1 || FLAGS_dmsaa) {
2943cb93a386Sopenharmony_ci                    const auto* caps = ctx->priv().caps();
2944cb93a386Sopenharmony_ci                    if (skgpu::v1::AtlasPathRenderer::IsSupported(ctx)) {
2945cb93a386Sopenharmony_ci                        writer.appendString(
2946cb93a386Sopenharmony_ci                                gPathRendererNames[GpuPathRenderers::kAtlas].c_str());
2947cb93a386Sopenharmony_ci                    }
2948cb93a386Sopenharmony_ci                    if (skgpu::v1::TessellationPathRenderer::IsSupported(*caps)) {
2949cb93a386Sopenharmony_ci                        writer.appendString(
2950cb93a386Sopenharmony_ci                                gPathRendererNames[GpuPathRenderers::kTessellation].c_str());
2951cb93a386Sopenharmony_ci                    }
2952cb93a386Sopenharmony_ci                }
2953cb93a386Sopenharmony_ci#endif
2954cb93a386Sopenharmony_ci                if (1 == fWindow->sampleCount()) {
2955cb93a386Sopenharmony_ci                    writer.appendString(gPathRendererNames[GpuPathRenderers::kSmall].c_str());
2956cb93a386Sopenharmony_ci                }
2957cb93a386Sopenharmony_ci                writer.appendString(gPathRendererNames[GpuPathRenderers::kTriangulating].c_str());
2958cb93a386Sopenharmony_ci                writer.appendString(gPathRendererNames[GpuPathRenderers::kNone].c_str());
2959cb93a386Sopenharmony_ci            }
2960cb93a386Sopenharmony_ci        });
2961cb93a386Sopenharmony_ci
2962cb93a386Sopenharmony_ci    // Softkey state
2963cb93a386Sopenharmony_ci    WriteStateObject(writer, kSoftkeyStateName, kSoftkeyHint,
2964cb93a386Sopenharmony_ci        [this](SkJSONWriter& writer) {
2965cb93a386Sopenharmony_ci            writer.appendString(kSoftkeyHint);
2966cb93a386Sopenharmony_ci            for (const auto& softkey : fCommands.getCommandsAsSoftkeys()) {
2967cb93a386Sopenharmony_ci                writer.appendString(softkey.c_str());
2968cb93a386Sopenharmony_ci            }
2969cb93a386Sopenharmony_ci        });
2970cb93a386Sopenharmony_ci
2971cb93a386Sopenharmony_ci    writer.endArray();
2972cb93a386Sopenharmony_ci    writer.flush();
2973cb93a386Sopenharmony_ci
2974cb93a386Sopenharmony_ci    auto data = memStream.detachAsData();
2975cb93a386Sopenharmony_ci
2976cb93a386Sopenharmony_ci    // TODO: would be cool to avoid this copy
2977cb93a386Sopenharmony_ci    const SkString cstring(static_cast<const char*>(data->data()), data->size());
2978cb93a386Sopenharmony_ci
2979cb93a386Sopenharmony_ci    fWindow->setUIState(cstring.c_str());
2980cb93a386Sopenharmony_ci}
2981cb93a386Sopenharmony_ci
2982cb93a386Sopenharmony_civoid Viewer::onUIStateChanged(const SkString& stateName, const SkString& stateValue) {
2983cb93a386Sopenharmony_ci    // For those who will add more features to handle the state change in this function:
2984cb93a386Sopenharmony_ci    // After the change, please call updateUIState no notify the frontend (e.g., Android app).
2985cb93a386Sopenharmony_ci    // For example, after slide change, updateUIState is called inside setupCurrentSlide;
2986cb93a386Sopenharmony_ci    // after backend change, updateUIState is called in this function.
2987cb93a386Sopenharmony_ci    if (stateName.equals(kSlideStateName)) {
2988cb93a386Sopenharmony_ci        for (int i = 0; i < fSlides.count(); ++i) {
2989cb93a386Sopenharmony_ci            if (fSlides[i]->getName().equals(stateValue)) {
2990cb93a386Sopenharmony_ci                this->setCurrentSlide(i);
2991cb93a386Sopenharmony_ci                return;
2992cb93a386Sopenharmony_ci            }
2993cb93a386Sopenharmony_ci        }
2994cb93a386Sopenharmony_ci
2995cb93a386Sopenharmony_ci        SkDebugf("Slide not found: %s", stateValue.c_str());
2996cb93a386Sopenharmony_ci    } else if (stateName.equals(kBackendStateName)) {
2997cb93a386Sopenharmony_ci        for (int i = 0; i < sk_app::Window::kBackendTypeCount; i++) {
2998cb93a386Sopenharmony_ci            if (stateValue.equals(kBackendTypeStrings[i])) {
2999cb93a386Sopenharmony_ci                if (fBackendType != i) {
3000cb93a386Sopenharmony_ci                    fBackendType = (sk_app::Window::BackendType)i;
3001cb93a386Sopenharmony_ci                    for(auto& slide : fSlides) {
3002cb93a386Sopenharmony_ci                        slide->gpuTeardown();
3003cb93a386Sopenharmony_ci                    }
3004cb93a386Sopenharmony_ci                    fWindow->detach();
3005cb93a386Sopenharmony_ci                    fWindow->attach(backend_type_for_window(fBackendType));
3006cb93a386Sopenharmony_ci                }
3007cb93a386Sopenharmony_ci                break;
3008cb93a386Sopenharmony_ci            }
3009cb93a386Sopenharmony_ci        }
3010cb93a386Sopenharmony_ci    } else if (stateName.equals(kMSAAStateName)) {
3011cb93a386Sopenharmony_ci        DisplayParams params = fWindow->getRequestedDisplayParams();
3012cb93a386Sopenharmony_ci        int sampleCount = atoi(stateValue.c_str());
3013cb93a386Sopenharmony_ci        if (sampleCount != params.fMSAASampleCount) {
3014cb93a386Sopenharmony_ci            params.fMSAASampleCount = sampleCount;
3015cb93a386Sopenharmony_ci            fWindow->setRequestedDisplayParams(params);
3016cb93a386Sopenharmony_ci            fWindow->inval();
3017cb93a386Sopenharmony_ci            this->updateTitle();
3018cb93a386Sopenharmony_ci            this->updateUIState();
3019cb93a386Sopenharmony_ci        }
3020cb93a386Sopenharmony_ci    } else if (stateName.equals(kPathRendererStateName)) {
3021cb93a386Sopenharmony_ci        DisplayParams params = fWindow->getRequestedDisplayParams();
3022cb93a386Sopenharmony_ci        for (const auto& pair : gPathRendererNames) {
3023cb93a386Sopenharmony_ci            if (pair.second == stateValue.c_str()) {
3024cb93a386Sopenharmony_ci                if (params.fGrContextOptions.fGpuPathRenderers != pair.first) {
3025cb93a386Sopenharmony_ci                    params.fGrContextOptions.fGpuPathRenderers = pair.first;
3026cb93a386Sopenharmony_ci                    fWindow->setRequestedDisplayParams(params);
3027cb93a386Sopenharmony_ci                    fWindow->inval();
3028cb93a386Sopenharmony_ci                    this->updateTitle();
3029cb93a386Sopenharmony_ci                    this->updateUIState();
3030cb93a386Sopenharmony_ci                }
3031cb93a386Sopenharmony_ci                break;
3032cb93a386Sopenharmony_ci            }
3033cb93a386Sopenharmony_ci        }
3034cb93a386Sopenharmony_ci    } else if (stateName.equals(kSoftkeyStateName)) {
3035cb93a386Sopenharmony_ci        if (!stateValue.equals(kSoftkeyHint)) {
3036cb93a386Sopenharmony_ci            fCommands.onSoftkey(stateValue);
3037cb93a386Sopenharmony_ci            this->updateUIState(); // This is still needed to reset the value to kSoftkeyHint
3038cb93a386Sopenharmony_ci        }
3039cb93a386Sopenharmony_ci    } else if (stateName.equals(kRefreshStateName)) {
3040cb93a386Sopenharmony_ci        // This state is actually NOT in the UI state.
3041cb93a386Sopenharmony_ci        // We use this to allow Android to quickly set bool fRefresh.
3042cb93a386Sopenharmony_ci        fRefresh = stateValue.equals(kON);
3043cb93a386Sopenharmony_ci    } else {
3044cb93a386Sopenharmony_ci        SkDebugf("Unknown stateName: %s", stateName.c_str());
3045cb93a386Sopenharmony_ci    }
3046cb93a386Sopenharmony_ci}
3047cb93a386Sopenharmony_ci
3048cb93a386Sopenharmony_cibool Viewer::onKey(skui::Key key, skui::InputState state, skui::ModifierKey modifiers) {
3049cb93a386Sopenharmony_ci    return fCommands.onKey(key, state, modifiers);
3050cb93a386Sopenharmony_ci}
3051cb93a386Sopenharmony_ci
3052cb93a386Sopenharmony_cibool Viewer::onChar(SkUnichar c, skui::ModifierKey modifiers) {
3053cb93a386Sopenharmony_ci    if (fSlides[fCurrentSlide]->onChar(c)) {
3054cb93a386Sopenharmony_ci        fWindow->inval();
3055cb93a386Sopenharmony_ci        return true;
3056cb93a386Sopenharmony_ci    } else {
3057cb93a386Sopenharmony_ci        return fCommands.onChar(c, modifiers);
3058cb93a386Sopenharmony_ci    }
3059cb93a386Sopenharmony_ci}
3060