Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Classes | Macros | Typedefs | Functions | Variables
Viewer.cpp File Reference
#include "tools/viewer/Viewer.h"
#include "bench/GpuTools.h"
#include "gm/gm.h"
#include "include/core/SkAlphaType.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkBlendMode.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkColorPriv.h"
#include "include/core/SkColorType.h"
#include "include/core/SkData.h"
#include "include/core/SkFontTypes.h"
#include "include/core/SkGraphics.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkPicture.h"
#include "include/core/SkPictureRecorder.h"
#include "include/core/SkRect.h"
#include "include/core/SkSamplingOptions.h"
#include "include/core/SkSerialProcs.h"
#include "include/core/SkStream.h"
#include "include/core/SkSurface.h"
#include "include/core/SkSurfaceProps.h"
#include "include/core/SkTextBlob.h"
#include "include/encode/SkPngEncoder.h"
#include "include/gpu/GrDirectContext.h"
#include "include/private/base/SkDebug.h"
#include "include/private/base/SkTPin.h"
#include "include/private/base/SkTo.h"
#include "include/utils/SkPaintFilterCanvas.h"
#include "src/base/SkBase64.h"
#include "src/base/SkTLazy.h"
#include "src/base/SkTSort.h"
#include "src/base/SkUTF.h"
#include "src/core/SkAutoPixmapStorage.h"
#include "src/core/SkLRUCache.h"
#include "src/core/SkMD5.h"
#include "src/core/SkOSFile.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkScan.h"
#include "src/core/SkStringUtils.h"
#include "src/core/SkTaskGroup.h"
#include "src/core/SkTextBlobPriv.h"
#include "src/image/SkImage_Base.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLString.h"
#include "src/text/GlyphRun.h"
#include "src/utils/SkJSONWriter.h"
#include "src/utils/SkOSPath.h"
#include "src/utils/SkShaderUtils.h"
#include "tools/CodecUtils.h"
#include "tools/DecodeUtils.h"
#include "tools/Resources.h"
#include "tools/RuntimeBlendUtils.h"
#include "tools/SkMetaData.h"
#include "tools/flags/CommandLineFlags.h"
#include "tools/flags/CommonFlags.h"
#include "tools/skui/InputState.h"
#include "tools/skui/Key.h"
#include "tools/skui/ModifierKey.h"
#include "tools/trace/EventTracingPriv.h"
#include "tools/viewer/BisectSlide.h"
#include "tools/viewer/GMSlide.h"
#include "tools/viewer/ImageSlide.h"
#include "tools/viewer/MSKPSlide.h"
#include "tools/viewer/SKPSlide.h"
#include "tools/viewer/SkSLDebuggerSlide.h"
#include "tools/viewer/SkSLSlide.h"
#include "tools/viewer/Slide.h"
#include "tools/viewer/SlideDir.h"
#include <algorithm>
#include <cfloat>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <initializer_list>
#include <map>
#include <memory>
#include <optional>
#include <ratio>
#include <regex>
#include <tuple>
#include <utility>
#include <vector>
#include "imgui.h"
#include "misc/cpp/imgui_stdlib.h"

Go to the source code of this file.

Classes

class  CapturingShaderErrorHandler
 
struct  NamedPrimaries
 
class  NullSlide
 
struct  SkPaintTitleUpdater
 
class  OveridePaintFilterCanvas
 

Macros

#define GL_BACKEND_STR
 
#define VK_BACKEND_STR
 
#define MTL_BACKEND_STR
 
#define D3D_BACKEND_STR
 
#define DAWN_BACKEND_STR
 
#define BACKENDS_STR_EVALUATOR(sw, gl, vk, mtl, d3d, dawn)   sw gl vk mtl d3d dawn
 
#define BACKENDS_STR
 
#define PATH_PREFIX   ""
 
#define MAX_ZOOM_LEVEL   8.0f
 
#define MIN_ZOOM_LEVEL   -8.0f
 

Typedefs

using OverrideFlag = SkSL::Compiler::OverrideFlag
 

Functions

static DEFINE_string (slide, "", "Start on this sample.")
 
static DEFINE_bool (list, false, "List samples?")
 
static DEFINE_string2 (backend, b, "sw", "Backend to use. Allowed values are " BACKENDS_STR ".")
 
static DEFINE_int (msaa, 1, "Number of subpixel samples. 0 for no HW antialiasing.")
 
static DEFINE_bool (dmsaa, false, "Use internal MSAA to render to non-MSAA surfaces?")
 
static DEFINE_string (bisect, "", "Path to a .skp or .svg file to bisect.")
 
static DEFINE_string2 (file, f, "", "Open a single file for viewing.")
 
static DEFINE_string2 (match, m, nullptr, "[~][^]substring[$] [...] of name to run.\n" "Multiple matches may be separated by spaces.\n" "~ causes a matching name to always be skipped\n" "^ requires the start of the name to match\n" "$ requires the end of the name to match\n" "^ and $ requires an exact match\n" "If a name does not match any list entry,\n" "it is skipped unless some list entry starts with ~")
 
static DEFINE_string (jpgs, PATH_PREFIX "jpgs", "Directory to read jpgs from.")
 
static DEFINE_string (jxls, PATH_PREFIX "jxls", "Directory to read jxls from.")
 
static DEFINE_string (skps, PATH_PREFIX "skps", "Directory to read skps from.")
 
static DEFINE_string (mskps, PATH_PREFIX "mskps", "Directory to read mskps from.")
 
static DEFINE_string (lotties, PATH_PREFIX "lotties", "Directory to read (Bodymovin) jsons from.")
 
static DEFINE_string (svgs, "", "Directory to read SVGs from, or a single SVG file.")
 
static DEFINE_string (rives, "", "Directory to read RIVs from, or a single .riv file.")
 
static DEFINE_int_2 (threads, j, -1, "Run threadsafe tests on a threadpool with this many extra threads, " "defaulting to one extra thread per core.")
 
static DEFINE_bool (redraw, false, "Toggle continuous redraw.")
 
static DEFINE_bool (offscreen, false, "Force rendering to an offscreen surface.")
 
static DEFINE_bool (stats, false, "Display stats overlay on startup.")
 
static DEFINE_bool (createProtected, false, "Create a protected native backend (e.g., in EGL).")
 
static bool is_graphite_backend_type (sk_app::Window::BackendType type)
 
const char * get_backend_string (sk_app::Window::BackendType type)
 
static sk_app::Window::BackendType get_backend_type (const char *str)
 
static bool primaries_equal (const SkColorSpacePrimaries &a, const SkColorSpacePrimaries &b)
 
static Window::BackendType backend_type_for_window (Window::BackendType backendType)
 
static sk_sp< SkDatadata_from_file (FILE *fp)
 
static sk_sp< SkDatabase64_string_to_data (const std::string &s)
 
static std::vector< sk_sp< SkImage > > find_data_uri_images (sk_sp< SkData > data)
 
static SkSerialProcs serial_procs_using_png ()
 
static void ImGui_Primaries (SkColorSpacePrimaries *primaries, SkPaint *gamutPaint)
 
static bool ImGui_DragLocation (SkPoint *pt)
 
static bool ImGui_DragQuad (SkPoint *pts)
 
static std::string build_sksl_highlight_shader ()
 
static std::string build_metal_highlight_shader (const std::string &inShader)
 
static std::string build_glsl_highlight_shader (const GrShaderCaps &shaderCaps)
 
template<typename OptionsFunc >
static void WriteStateObject (SkJSONWriter &writer, const char *name, const char *value, OptionsFunc &&optionsFunc)
 

Variables

static CapturingShaderErrorHandler gShaderErrorHandler
 
static std::map< GpuPathRenderers, std::string > gGaneshPathRendererNames
 
static SkColorSpacePrimaries gSrgbPrimaries
 
static SkColorSpacePrimaries gAdobePrimaries
 
static SkColorSpacePrimaries gP3Primaries
 
static SkColorSpacePrimaries gRec2020Primaries
 
struct NamedPrimaries gNamedPrimaries []
 
static const char kName [] = "name"
 
static const char kValue [] = "value"
 
static const char kOptions [] = "options"
 
static const char kSlideStateName [] = "Slide"
 
static const char kBackendStateName [] = "Backend"
 
static const char kMSAAStateName [] = "MSAA"
 
static const char kPathRendererStateName [] = "Path renderer"
 
static const char kSoftkeyStateName [] = "Softkey"
 
static const char kSoftkeyHint [] = "Please select a softkey"
 
static const char kON [] = "ON"
 
static const char kRefreshStateName [] = "Refresh"
 

Macro Definition Documentation

◆ BACKENDS_STR

#define BACKENDS_STR
Value:
#define D3D_BACKEND_STR
Definition Viewer.cpp:200
#define GL_BACKEND_STR
Definition Viewer.cpp:185
#define DAWN_BACKEND_STR
Definition Viewer.cpp:205
#define MTL_BACKEND_STR
Definition Viewer.cpp:195
#define VK_BACKEND_STR
Definition Viewer.cpp:190
#define BACKENDS_STR_EVALUATOR(sw, gl, vk, mtl, d3d, dawn)
Definition Viewer.cpp:207

Definition at line 208 of file Viewer.cpp.

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

◆ BACKENDS_STR_EVALUATOR

#define BACKENDS_STR_EVALUATOR (   sw,
  gl,
  vk,
  mtl,
  d3d,
  dawn 
)    sw gl vk mtl d3d dawn

Definition at line 207 of file Viewer.cpp.

◆ D3D_BACKEND_STR

#define D3D_BACKEND_STR

Definition at line 200 of file Viewer.cpp.

◆ DAWN_BACKEND_STR

#define DAWN_BACKEND_STR

Definition at line 205 of file Viewer.cpp.

◆ GL_BACKEND_STR

#define GL_BACKEND_STR

Definition at line 185 of file Viewer.cpp.

◆ MAX_ZOOM_LEVEL

#define MAX_ZOOM_LEVEL   8.0f

Definition at line 1383 of file Viewer.cpp.

◆ MIN_ZOOM_LEVEL

#define MIN_ZOOM_LEVEL   -8.0f

Definition at line 1384 of file Viewer.cpp.

◆ MTL_BACKEND_STR

#define MTL_BACKEND_STR

Definition at line 195 of file Viewer.cpp.

◆ PATH_PREFIX

#define PATH_PREFIX   ""

Definition at line 258 of file Viewer.cpp.

◆ VK_BACKEND_STR

#define VK_BACKEND_STR

Definition at line 190 of file Viewer.cpp.

Typedef Documentation

◆ OverrideFlag

Definition at line 171 of file Viewer.cpp.

Function Documentation

◆ backend_type_for_window()

static Window::BackendType backend_type_for_window ( Window::BackendType  backendType)
static

Definition at line 469 of file Viewer.cpp.

469 {
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}

◆ base64_string_to_data()

static sk_sp< SkData > base64_string_to_data ( const std::string &  s)
static

Definition at line 885 of file Viewer.cpp.

885 {
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}

◆ build_glsl_highlight_shader()

static std::string build_glsl_highlight_shader ( const GrShaderCaps shaderCaps)
static

Definition at line 2076 of file Viewer.cpp.

2076 {
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}

◆ build_metal_highlight_shader()

static std::string build_metal_highlight_shader ( const std::string &  inShader)
static

Definition at line 2063 of file Viewer.cpp.

2063 {
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}

◆ build_sksl_highlight_shader()

static std::string build_sksl_highlight_shader ( )
static

Definition at line 2059 of file Viewer.cpp.

2059 {
2060 return std::string("void main() { sk_FragColor = half4(1, 0, 1, 0.5); }");
2061}

◆ data_from_file()

static sk_sp< SkData > data_from_file ( FILE *  fp)
static

Definition at line 876 of file Viewer.cpp.

876 {
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}

◆ DEFINE_bool() [1/6]

static DEFINE_bool ( createProtected  ,
false  ,
"Create a protected native backend (e.g., in EGL)."   
)
static

◆ DEFINE_bool() [2/6]

static DEFINE_bool ( dmsaa  ,
false  ,
"Use internal MSAA to render to non-MSAA surfaces?"   
)
static

◆ DEFINE_bool() [3/6]

static DEFINE_bool ( list  ,
false  ,
"List samples?"   
)
static

◆ DEFINE_bool() [4/6]

static DEFINE_bool ( offscreen  ,
false  ,
"Force rendering to an offscreen surface."   
)
static

◆ DEFINE_bool() [5/6]

static DEFINE_bool ( redraw  ,
false  ,
"Toggle continuous redraw."   
)
static

◆ DEFINE_bool() [6/6]

static DEFINE_bool ( stats  ,
false  ,
"Display stats overlay on startup."   
)
static

◆ DEFINE_int()

static DEFINE_int ( msaa  ,
,
"Number of subpixel samples. 0 for no HW antialiasing."   
)
static

◆ DEFINE_int_2()

static DEFINE_int_2 ( threads  ,
,
1,
"Run threadsafe tests on a threadpool with this many extra  threads,
" "defaulting to one extra thread per core."   
)
static

◆ DEFINE_string() [1/9]

static DEFINE_string ( bisect  ,
""  ,
"Path to a .skp or .svg file to bisect."   
)
static

◆ DEFINE_string() [2/9]

static DEFINE_string ( jpgs  ,
PATH_PREFIX "jpgs"  ,
"Directory to read jpgs from."   
)
static

◆ DEFINE_string() [3/9]

static DEFINE_string ( jxls  ,
PATH_PREFIX "jxls"  ,
"Directory to read jxls from."   
)
static

◆ DEFINE_string() [4/9]

static DEFINE_string ( lotties  ,
PATH_PREFIX "lotties"  ,
"Directory to read (Bodymovin) jsons from."   
)
static

◆ DEFINE_string() [5/9]

static DEFINE_string ( mskps  ,
PATH_PREFIX "mskps"  ,
"Directory to read mskps from."   
)
static

◆ DEFINE_string() [6/9]

static DEFINE_string ( rives  ,
""  ,
"Directory to read RIVs  from,
or a single .riv file."   
)
static

◆ DEFINE_string() [7/9]

static DEFINE_string ( skps  ,
PATH_PREFIX "skps"  ,
"Directory to read skps from."   
)
static

◆ DEFINE_string() [8/9]

static DEFINE_string ( slide  ,
""  ,
"Start on this sample."   
)
static

◆ DEFINE_string() [9/9]

static DEFINE_string ( svgs  ,
""  ,
"Directory to read SVGs  from,
or a single SVG file."   
)
static

◆ DEFINE_string2() [1/3]

static DEFINE_string2 ( backend  ,
b  ,
"sw"  ,
"Backend to use. Allowed values are " BACKENDS_STR "."   
)
static

◆ DEFINE_string2() [2/3]

static DEFINE_string2 ( file  ,
,
""  ,
"Open a single file for viewing."   
)
static

◆ DEFINE_string2() [3/3]

static DEFINE_string2 ( match  ,
,
nullptr  ,
"substring of name to run.\n" "Multiple matches may be separated by spaces.\n" "~ causes a matching name to always be skipped\n" "^ requires the start of the name to match\n" "$ requires the end of the name to match\n" "^ and $ requires an exact match\n" "If a name does not match any list  entry[~][^][$][...],
\n" "it is skipped unless some list entry starts with ~"   
)
static

◆ find_data_uri_images()

static std::vector< sk_sp< SkImage > > find_data_uri_images ( sk_sp< SkData data)
static

Definition at line 900 of file Viewer.cpp.

900 {
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}

◆ get_backend_string()

const char * get_backend_string ( sk_app::Window::BackendType  type)

Definition at line 350 of file Viewer.cpp.

350 {
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}

◆ get_backend_type()

static sk_app::Window::BackendType get_backend_type ( const char *  str)
static

Definition at line 382 of file Viewer.cpp.

382 {
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}

◆ ImGui_DragLocation()

static bool ImGui_DragLocation ( SkPoint pt)
static

Definition at line 2036 of file Viewer.cpp.

2036 {
2037 ImGui::DragCanvas dc(pt);
2038 dc.fillColor(IM_COL32(0, 0, 0, 128));
2039 dc.dragPoint(pt);
2040 return dc.fDragging;
2041}

◆ ImGui_DragQuad()

static bool ImGui_DragQuad ( SkPoint pts)
static

Definition at line 2043 of file Viewer.cpp.

2043 {
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}

◆ ImGui_Primaries()

static void ImGui_Primaries ( SkColorSpacePrimaries primaries,
SkPaint gamutPaint 
)
static

Definition at line 2018 of file Viewer.cpp.

2018 {
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}

◆ is_graphite_backend_type()

static bool is_graphite_backend_type ( sk_app::Window::BackendType  type)
static

Definition at line 286 of file Viewer.cpp.

286 {
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}

◆ primaries_equal()

static bool primaries_equal ( const SkColorSpacePrimaries a,
const SkColorSpacePrimaries b 
)
static

Definition at line 465 of file Viewer.cpp.

465 {
466 return memcmp(&a, &b, sizeof(SkColorSpacePrimaries)) == 0;
467}

◆ serial_procs_using_png()

static SkSerialProcs serial_procs_using_png ( )
static

Definition at line 1676 of file Viewer.cpp.

1676 {
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}

◆ WriteStateObject()

template<typename OptionsFunc >
static void WriteStateObject ( SkJSONWriter writer,
const char *  name,
const char *  value,
OptionsFunc &&  optionsFunc 
)
static

Definition at line 3150 of file Viewer.cpp.

3151 {
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}

Variable Documentation

◆ gAdobePrimaries

SkColorSpacePrimaries gAdobePrimaries
static
Initial value:
= {
0.64f, 0.33f,
0.21f, 0.71f,
0.15f, 0.06f,
0.3127f, 0.3290f }

Definition at line 437 of file Viewer.cpp.

437 {
438 0.64f, 0.33f,
439 0.21f, 0.71f,
440 0.15f, 0.06f,
441 0.3127f, 0.3290f };

◆ gGaneshPathRendererNames

std::map<GpuPathRenderers, std::string> gGaneshPathRendererNames
static

Definition at line 173 of file Viewer.cpp.

◆ gNamedPrimaries

struct NamedPrimaries gNamedPrimaries[]
Initial value:
= {
{ "sRGB", &gSrgbPrimaries },
{ "AdobeRGB", &gAdobePrimaries },
{ "P3", &gP3Primaries },
{ "Rec. 2020", &gRec2020Primaries },
}

◆ gP3Primaries

SkColorSpacePrimaries gP3Primaries
static
Initial value:
= {
0.680f, 0.320f,
0.265f, 0.690f,
0.150f, 0.060f,
0.3127f, 0.3290f }

Definition at line 443 of file Viewer.cpp.

443 {
444 0.680f, 0.320f,
445 0.265f, 0.690f,
446 0.150f, 0.060f,
447 0.3127f, 0.3290f };

◆ gRec2020Primaries

SkColorSpacePrimaries gRec2020Primaries
static
Initial value:
= {
0.708f, 0.292f,
0.170f, 0.797f,
0.131f, 0.046f,
0.3127f, 0.3290f }

Definition at line 449 of file Viewer.cpp.

449 {
450 0.708f, 0.292f,
451 0.170f, 0.797f,
452 0.131f, 0.046f,
453 0.3127f, 0.3290f };

◆ gShaderErrorHandler

CapturingShaderErrorHandler gShaderErrorHandler
static

Definition at line 165 of file Viewer.cpp.

◆ gSrgbPrimaries

SkColorSpacePrimaries gSrgbPrimaries
static
Initial value:
= {
0.64f, 0.33f,
0.30f, 0.60f,
0.15f, 0.06f,
0.3127f, 0.3290f }

Definition at line 431 of file Viewer.cpp.

431 {
432 0.64f, 0.33f,
433 0.30f, 0.60f,
434 0.15f, 0.06f,
435 0.3127f, 0.3290f };

◆ kBackendStateName

const char kBackendStateName[] = "Backend"
static

Definition at line 485 of file Viewer.cpp.

◆ kMSAAStateName

const char kMSAAStateName[] = "MSAA"
static

Definition at line 486 of file Viewer.cpp.

◆ kName

const char kName[] = "name"
static

Definition at line 481 of file Viewer.cpp.

◆ kON

const char kON[] = "ON"
static

Definition at line 490 of file Viewer.cpp.

◆ kOptions

const char kOptions[] = "options"
static

Definition at line 483 of file Viewer.cpp.

◆ kPathRendererStateName

const char kPathRendererStateName[] = "Path renderer"
static

Definition at line 487 of file Viewer.cpp.

◆ kRefreshStateName

const char kRefreshStateName[] = "Refresh"
static

Definition at line 491 of file Viewer.cpp.

◆ kSlideStateName

const char kSlideStateName[] = "Slide"
static

Definition at line 484 of file Viewer.cpp.

◆ kSoftkeyHint

const char kSoftkeyHint[] = "Please select a softkey"
static

Definition at line 489 of file Viewer.cpp.

◆ kSoftkeyStateName

const char kSoftkeyStateName[] = "Softkey"
static

Definition at line 488 of file Viewer.cpp.

◆ kValue

const char kValue[] = "value"
static

Definition at line 482 of file Viewer.cpp.