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