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