2016-04-06 13:08:59 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2016 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "include/core/SkCanvas.h"
|
|
|
|
#include "include/core/SkData.h"
|
|
|
|
#include "include/core/SkGraphics.h"
|
|
|
|
#include "include/core/SkPictureRecorder.h"
|
|
|
|
#include "include/core/SkStream.h"
|
|
|
|
#include "include/core/SkSurface.h"
|
|
|
|
#include "include/gpu/GrContext.h"
|
|
|
|
#include "include/private/SkTo.h"
|
|
|
|
#include "include/utils/SkPaintFilterCanvas.h"
|
|
|
|
#include "src/core/SkColorSpacePriv.h"
|
|
|
|
#include "src/core/SkImagePriv.h"
|
|
|
|
#include "src/core/SkMD5.h"
|
|
|
|
#include "src/core/SkMakeUnique.h"
|
|
|
|
#include "src/core/SkOSFile.h"
|
|
|
|
#include "src/core/SkScan.h"
|
|
|
|
#include "src/core/SkTaskGroup.h"
|
|
|
|
#include "src/gpu/GrContextPriv.h"
|
|
|
|
#include "src/gpu/GrGpu.h"
|
|
|
|
#include "src/gpu/GrPersistentCacheUtils.h"
|
|
|
|
#include "src/gpu/ccpr/GrCoverageCountingPathRenderer.h"
|
|
|
|
#include "src/utils/SkJSONWriter.h"
|
|
|
|
#include "src/utils/SkOSPath.h"
|
|
|
|
#include "tools/Resources.h"
|
|
|
|
#include "tools/ToolUtils.h"
|
|
|
|
#include "tools/flags/CommandLineFlags.h"
|
|
|
|
#include "tools/flags/CommonFlags.h"
|
|
|
|
#include "tools/trace/EventTracingPriv.h"
|
|
|
|
#include "tools/viewer/BisectSlide.h"
|
|
|
|
#include "tools/viewer/GMSlide.h"
|
|
|
|
#include "tools/viewer/ImageSlide.h"
|
|
|
|
#include "tools/viewer/ParticlesSlide.h"
|
|
|
|
#include "tools/viewer/SKPSlide.h"
|
|
|
|
#include "tools/viewer/SampleSlide.h"
|
|
|
|
#include "tools/viewer/SlideDir.h"
|
|
|
|
#include "tools/viewer/SvgSlide.h"
|
|
|
|
#include "tools/viewer/Viewer.h"
|
2017-02-24 23:04:47 +00:00
|
|
|
|
2018-06-13 13:59:02 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <map>
|
|
|
|
|
2018-09-19 15:31:27 +00:00
|
|
|
#include "imgui.h"
|
2019-04-12 15:47:19 +00:00
|
|
|
#include "misc/cpp/imgui_stdlib.h" // For ImGui support of std::string
|
2018-05-25 16:43:51 +00:00
|
|
|
|
2018-05-04 16:23:24 +00:00
|
|
|
#if defined(SK_ENABLE_SKOTTIE)
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "tools/viewer/SkottieSlide.h"
|
2018-05-04 16:23:24 +00:00
|
|
|
#endif
|
|
|
|
|
2019-05-03 17:13:35 +00:00
|
|
|
class CapturingShaderErrorHandler : public GrContextOptions::ShaderErrorHandler {
|
|
|
|
public:
|
|
|
|
void compileError(const char* shader, const char* errors) override {
|
|
|
|
fShaders.push_back(SkString(shader));
|
|
|
|
fErrors.push_back(SkString(errors));
|
|
|
|
}
|
|
|
|
|
|
|
|
void reset() {
|
|
|
|
fShaders.reset();
|
|
|
|
fErrors.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
SkTArray<SkString> fShaders;
|
|
|
|
SkTArray<SkString> fErrors;
|
|
|
|
};
|
|
|
|
|
|
|
|
static CapturingShaderErrorHandler gShaderErrorHandler;
|
|
|
|
|
2016-05-04 20:49:13 +00:00
|
|
|
using namespace sk_app;
|
|
|
|
|
2017-02-28 00:00:53 +00:00
|
|
|
static std::map<GpuPathRenderers, std::string> gPathRendererNames;
|
|
|
|
|
2016-04-06 13:08:59 +00:00
|
|
|
Application* Application::Create(int argc, char** argv, void* platformData) {
|
2016-05-04 20:49:13 +00:00
|
|
|
return new Viewer(argc, argv, platformData);
|
2016-04-06 13:08:59 +00:00
|
|
|
}
|
|
|
|
|
2017-10-13 18:35:50 +00:00
|
|
|
static DEFINE_string(slide, "", "Start on this sample.");
|
|
|
|
static DEFINE_bool(list, false, "List samples?");
|
2017-02-14 20:16:46 +00:00
|
|
|
|
2019-08-02 19:04:52 +00:00
|
|
|
#if defined(SK_VULKAN)
|
2016-07-27 21:29:18 +00:00
|
|
|
# define BACKENDS_STR "\"sw\", \"gl\", and \"vk\""
|
2019-02-08 20:36:14 +00:00
|
|
|
#elif defined(SK_METAL) && defined(SK_BUILD_FOR_MAC)
|
|
|
|
# define BACKENDS_STR "\"sw\", \"gl\", and \"mtl\""
|
2019-08-02 19:04:52 +00:00
|
|
|
#elif defined(SK_DAWN)
|
|
|
|
# define BACKENDS_STR "\"sw\", \"gl\", and \"dawn\""
|
2016-07-26 19:56:32 +00:00
|
|
|
#else
|
|
|
|
# define BACKENDS_STR "\"sw\" and \"gl\""
|
|
|
|
#endif
|
|
|
|
|
2016-10-18 19:33:53 +00:00
|
|
|
static DEFINE_string2(backend, b, "sw", "Backend to use. Allowed values are " BACKENDS_STR ".");
|
2016-07-26 19:56:32 +00:00
|
|
|
|
2019-03-21 16:42:21 +00:00
|
|
|
static DEFINE_int(msaa, 1, "Number of subpixel samples. 0 for no HW antialiasing.");
|
2017-02-22 19:00:42 +00:00
|
|
|
|
2019-06-17 20:16:49 +00:00
|
|
|
static DEFINE_int(internalSamples, 4,
|
|
|
|
"Number of samples for internal draws that use MSAA or mixed samples.");
|
|
|
|
|
2019-03-21 16:31:36 +00:00
|
|
|
static DEFINE_string(bisect, "", "Path to a .skp or .svg file to bisect.");
|
2018-02-20 20:23:32 +00:00
|
|
|
|
2019-03-21 16:31:36 +00:00
|
|
|
static DEFINE_string2(file, f, "", "Open a single file for viewing.");
|
2018-05-08 14:36:18 +00:00
|
|
|
|
2019-03-25 15:54:59 +00:00
|
|
|
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 ~");
|
|
|
|
|
2019-03-21 18:08:08 +00:00
|
|
|
#if defined(SK_BUILD_FOR_ANDROID)
|
|
|
|
static DEFINE_string(jpgs, "/data/local/tmp/resources", "Directory to read jpgs from.");
|
2019-03-25 15:54:59 +00:00
|
|
|
static DEFINE_string(skps, "/data/local/tmp/skps", "Directory to read skps from.");
|
|
|
|
static DEFINE_string(lotties, "/data/local/tmp/lotties",
|
|
|
|
"Directory to read (Bodymovin) jsons from.");
|
2019-03-21 18:08:08 +00:00
|
|
|
#else
|
|
|
|
static DEFINE_string(jpgs, "jpgs", "Directory to read jpgs from.");
|
2019-03-25 15:54:59 +00:00
|
|
|
static DEFINE_string(skps, "skps", "Directory to read skps from.");
|
|
|
|
static DEFINE_string(lotties, "lotties", "Directory to read (Bodymovin) jsons from.");
|
2019-03-21 18:08:08 +00:00
|
|
|
#endif
|
|
|
|
|
2019-03-25 15:54:59 +00:00
|
|
|
static DEFINE_string(svgs, "", "Directory to read SVGs from, or a single SVG 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.");
|
|
|
|
|
|
|
|
|
2017-08-17 18:37:06 +00:00
|
|
|
const char* kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = {
|
2017-02-24 23:04:47 +00:00
|
|
|
"OpenGL",
|
2017-08-17 18:37:06 +00:00
|
|
|
#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
|
|
|
|
"ANGLE",
|
|
|
|
#endif
|
2019-08-02 19:04:52 +00:00
|
|
|
#ifdef SK_DAWN
|
|
|
|
"Dawn",
|
|
|
|
#endif
|
2016-06-17 16:29:14 +00:00
|
|
|
#ifdef SK_VULKAN
|
2017-02-24 23:04:47 +00:00
|
|
|
"Vulkan",
|
2019-02-08 20:36:14 +00:00
|
|
|
#endif
|
2019-09-03 13:42:57 +00:00
|
|
|
#ifdef SK_METAL
|
2019-02-08 20:36:14 +00:00
|
|
|
"Metal",
|
2016-06-17 16:29:14 +00:00
|
|
|
#endif
|
2017-02-24 23:04:47 +00:00
|
|
|
"Raster"
|
2016-05-20 13:01:06 +00:00
|
|
|
};
|
|
|
|
|
2016-07-26 19:56:32 +00:00
|
|
|
static sk_app::Window::BackendType get_backend_type(const char* str) {
|
2019-08-02 19:04:52 +00:00
|
|
|
#ifdef SK_DAWN
|
|
|
|
if (0 == strcmp(str, "dawn")) {
|
|
|
|
return sk_app::Window::kDawn_BackendType;
|
|
|
|
} else
|
|
|
|
#endif
|
2016-07-26 19:56:32 +00:00
|
|
|
#ifdef SK_VULKAN
|
|
|
|
if (0 == strcmp(str, "vk")) {
|
|
|
|
return sk_app::Window::kVulkan_BackendType;
|
|
|
|
} else
|
2017-08-17 18:37:06 +00:00
|
|
|
#endif
|
|
|
|
#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
|
|
|
|
if (0 == strcmp(str, "angle")) {
|
|
|
|
return sk_app::Window::kANGLE_BackendType;
|
|
|
|
} else
|
2019-02-08 20:36:14 +00:00
|
|
|
#endif
|
2019-09-03 13:42:57 +00:00
|
|
|
#ifdef SK_METAL
|
|
|
|
if (0 == strcmp(str, "mtl")) {
|
|
|
|
return sk_app::Window::kMetal_BackendType;
|
|
|
|
} else
|
2016-07-26 19:56:32 +00:00
|
|
|
#endif
|
|
|
|
if (0 == strcmp(str, "gl")) {
|
|
|
|
return sk_app::Window::kNativeGL_BackendType;
|
|
|
|
} else if (0 == strcmp(str, "sw")) {
|
|
|
|
return sk_app::Window::kRaster_BackendType;
|
|
|
|
} else {
|
|
|
|
SkDebugf("Unknown backend type, %s, defaulting to sw.", str);
|
|
|
|
return sk_app::Window::kRaster_BackendType;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-24 14:49:14 +00:00
|
|
|
static SkColorSpacePrimaries gSrgbPrimaries = {
|
|
|
|
0.64f, 0.33f,
|
|
|
|
0.30f, 0.60f,
|
|
|
|
0.15f, 0.06f,
|
|
|
|
0.3127f, 0.3290f };
|
|
|
|
|
|
|
|
static SkColorSpacePrimaries gAdobePrimaries = {
|
|
|
|
0.64f, 0.33f,
|
|
|
|
0.21f, 0.71f,
|
|
|
|
0.15f, 0.06f,
|
|
|
|
0.3127f, 0.3290f };
|
|
|
|
|
|
|
|
static SkColorSpacePrimaries gP3Primaries = {
|
|
|
|
0.680f, 0.320f,
|
|
|
|
0.265f, 0.690f,
|
|
|
|
0.150f, 0.060f,
|
|
|
|
0.3127f, 0.3290f };
|
|
|
|
|
|
|
|
static SkColorSpacePrimaries gRec2020Primaries = {
|
|
|
|
0.708f, 0.292f,
|
|
|
|
0.170f, 0.797f,
|
|
|
|
0.131f, 0.046f,
|
|
|
|
0.3127f, 0.3290f };
|
|
|
|
|
|
|
|
struct NamedPrimaries {
|
|
|
|
const char* fName;
|
|
|
|
SkColorSpacePrimaries* fPrimaries;
|
|
|
|
} gNamedPrimaries[] = {
|
|
|
|
{ "sRGB", &gSrgbPrimaries },
|
|
|
|
{ "AdobeRGB", &gAdobePrimaries },
|
|
|
|
{ "P3", &gP3Primaries },
|
|
|
|
{ "Rec. 2020", &gRec2020Primaries },
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool primaries_equal(const SkColorSpacePrimaries& a, const SkColorSpacePrimaries& b) {
|
|
|
|
return memcmp(&a, &b, sizeof(SkColorSpacePrimaries)) == 0;
|
|
|
|
}
|
|
|
|
|
2017-11-08 14:54:10 +00:00
|
|
|
static Window::BackendType backend_type_for_window(Window::BackendType backendType) {
|
|
|
|
// In raster mode, we still use GL for the window.
|
|
|
|
// This lets us render the GUI faster (and correct).
|
|
|
|
return Window::kRaster_BackendType == backendType ? Window::kNativeGL_BackendType : backendType;
|
|
|
|
}
|
|
|
|
|
2019-03-01 19:37:30 +00:00
|
|
|
class NullSlide : public Slide {
|
|
|
|
SkISize getDimensions() const override {
|
|
|
|
return SkISize::Make(640, 480);
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw(SkCanvas* canvas) override {
|
|
|
|
canvas->clear(0xffff11ff);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-05-27 15:52:52 +00:00
|
|
|
const char* kName = "name";
|
|
|
|
const char* kValue = "value";
|
|
|
|
const char* kOptions = "options";
|
|
|
|
const char* kSlideStateName = "Slide";
|
|
|
|
const char* kBackendStateName = "Backend";
|
2017-02-24 23:04:47 +00:00
|
|
|
const char* kMSAAStateName = "MSAA";
|
2017-02-28 00:00:53 +00:00
|
|
|
const char* kPathRendererStateName = "Path renderer";
|
2016-06-03 15:47:23 +00:00
|
|
|
const char* kSoftkeyStateName = "Softkey";
|
|
|
|
const char* kSoftkeyHint = "Please select a softkey";
|
2016-06-07 13:57:40 +00:00
|
|
|
const char* kFpsStateName = "FPS";
|
2016-06-13 19:26:45 +00:00
|
|
|
const char* kON = "ON";
|
|
|
|
const char* kOFF = "OFF";
|
2016-07-06 21:11:32 +00:00
|
|
|
const char* kRefreshStateName = "Refresh";
|
2016-05-27 15:52:52 +00:00
|
|
|
|
2016-05-04 20:49:13 +00:00
|
|
|
Viewer::Viewer(int argc, char** argv, void* platformData)
|
2018-01-16 21:23:03 +00:00
|
|
|
: fCurrentSlide(-1)
|
|
|
|
, fRefresh(false)
|
2017-12-01 16:23:53 +00:00
|
|
|
, fSaveToSKP(false)
|
2019-03-14 15:39:02 +00:00
|
|
|
, fShowSlideDimensions(false)
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
, fShowImGuiDebugWindow(false)
|
2017-11-14 20:32:20 +00:00
|
|
|
, fShowSlidePicker(false)
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
, fShowImGuiTestWindow(false)
|
2017-02-13 14:39:57 +00:00
|
|
|
, fShowZoomWindow(false)
|
2018-06-26 18:23:20 +00:00
|
|
|
, fZoomWindowFixed(false)
|
|
|
|
, fZoomWindowLocation{0.0f, 0.0f}
|
2017-02-13 14:39:57 +00:00
|
|
|
, fLastImage(nullptr)
|
2018-07-24 22:01:53 +00:00
|
|
|
, fZoomUI(false)
|
2016-06-17 16:29:14 +00:00
|
|
|
, fBackendType(sk_app::Window::kNativeGL_BackendType)
|
2017-03-06 16:47:26 +00:00
|
|
|
, fColorMode(ColorMode::kLegacy)
|
2017-02-24 14:49:14 +00:00
|
|
|
, fColorSpacePrimaries(gSrgbPrimaries)
|
2017-11-09 15:27:55 +00:00
|
|
|
// Our UI can only tweak gamma (currently), so start out gamma-only
|
2019-01-04 22:03:00 +00:00
|
|
|
, fColorSpaceTransferFn(SkNamedTransferFn::k2Dot2)
|
2016-04-11 15:30:40 +00:00
|
|
|
, fZoomLevel(0.0f)
|
2018-04-23 16:55:06 +00:00
|
|
|
, fRotation(0.0f)
|
2018-08-09 19:18:46 +00:00
|
|
|
, fOffset{0.5f, 0.5f}
|
2017-06-07 14:00:30 +00:00
|
|
|
, fGestureDevice(GestureDevice::kNone)
|
2018-11-26 19:50:05 +00:00
|
|
|
, fTiled(false)
|
|
|
|
, fDrawTileBoundaries(false)
|
|
|
|
, fTileScale{0.25f, 0.25f}
|
2018-05-02 19:40:20 +00:00
|
|
|
, fPerspectiveMode(kPerspective_Off)
|
2016-04-08 19:51:45 +00:00
|
|
|
{
|
2016-10-14 13:12:53 +00:00
|
|
|
SkGraphics::Init();
|
2017-02-28 00:00:53 +00:00
|
|
|
|
2017-12-15 19:48:09 +00:00
|
|
|
gPathRendererNames[GpuPathRenderers::kAll] = "All Path Renderers";
|
|
|
|
gPathRendererNames[GpuPathRenderers::kStencilAndCover] = "NV_path_rendering";
|
|
|
|
gPathRendererNames[GpuPathRenderers::kSmall] = "Small paths (cached sdf or alpha masks)";
|
2019-07-19 20:20:53 +00:00
|
|
|
gPathRendererNames[GpuPathRenderers::kCoverageCounting] = "CCPR";
|
2017-12-15 19:48:09 +00:00
|
|
|
gPathRendererNames[GpuPathRenderers::kTessellating] = "Tessellating";
|
|
|
|
gPathRendererNames[GpuPathRenderers::kNone] = "Software masks";
|
2017-02-28 00:00:53 +00:00
|
|
|
|
2016-04-08 14:24:09 +00:00
|
|
|
SkDebugf("Command line arguments: ");
|
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
|
|
SkDebugf("%s ", argv[i]);
|
|
|
|
}
|
|
|
|
SkDebugf("\n");
|
|
|
|
|
2019-03-20 15:50:33 +00:00
|
|
|
CommandLineFlags::Parse(argc, argv);
|
2016-11-29 21:35:19 +00:00
|
|
|
#ifdef SK_BUILD_FOR_ANDROID
|
2017-05-26 16:06:21 +00:00
|
|
|
SetResourcePath("/data/local/tmp/resources");
|
2016-11-29 21:35:19 +00:00
|
|
|
#endif
|
2016-04-08 14:24:09 +00:00
|
|
|
|
2019-03-22 20:30:07 +00:00
|
|
|
ToolUtils::SetDefaultFontMgr();
|
2018-02-20 22:06:07 +00:00
|
|
|
|
2017-07-24 15:38:01 +00:00
|
|
|
initializeEventTracingForTools();
|
2017-07-20 19:43:35 +00:00
|
|
|
static SkTaskGroup::Enabler kTaskGroupEnabler(FLAGS_threads);
|
2016-10-14 13:12:53 +00:00
|
|
|
|
2016-07-26 19:56:32 +00:00
|
|
|
fBackendType = get_backend_type(FLAGS_backend[0]);
|
2016-04-06 13:08:59 +00:00
|
|
|
fWindow = Window::CreateNativeWindow(platformData);
|
|
|
|
|
2017-02-24 23:04:47 +00:00
|
|
|
DisplayParams displayParams;
|
|
|
|
displayParams.fMSAASampleCount = FLAGS_msaa;
|
2017-12-18 21:22:34 +00:00
|
|
|
SetCtxOptionsFromCommonFlags(&displayParams.fGrContextOptions);
|
2019-04-12 15:47:19 +00:00
|
|
|
displayParams.fGrContextOptions.fPersistentCache = &fPersistentCache;
|
2019-09-03 18:59:26 +00:00
|
|
|
displayParams.fGrContextOptions.fShaderCacheStrategy =
|
|
|
|
GrContextOptions::ShaderCacheStrategy::kBackendSource;
|
2019-05-03 17:13:35 +00:00
|
|
|
displayParams.fGrContextOptions.fShaderErrorHandler = &gShaderErrorHandler;
|
|
|
|
displayParams.fGrContextOptions.fSuppressPrints = true;
|
2019-06-24 17:54:24 +00:00
|
|
|
displayParams.fGrContextOptions.fInternalMultisampleCount = FLAGS_internalSamples;
|
2017-02-24 23:04:47 +00:00
|
|
|
fWindow->setRequestedDisplayParams(displayParams);
|
|
|
|
|
2017-12-19 16:15:16 +00:00
|
|
|
// Configure timers
|
|
|
|
fStatsLayer.setActive(false);
|
|
|
|
fAnimateTimer = fStatsLayer.addTimer("Animate", SK_ColorMAGENTA, 0xffff66ff);
|
|
|
|
fPaintTimer = fStatsLayer.addTimer("Paint", SK_ColorGREEN);
|
|
|
|
fFlushTimer = fStatsLayer.addTimer("Flush", SK_ColorRED, 0xffff6666);
|
|
|
|
|
2016-04-06 13:08:59 +00:00
|
|
|
// register callbacks
|
2016-05-10 13:50:49 +00:00
|
|
|
fCommands.attach(fWindow);
|
2017-12-08 21:45:43 +00:00
|
|
|
fWindow->pushLayer(this);
|
2017-12-19 16:15:16 +00:00
|
|
|
fWindow->pushLayer(&fStatsLayer);
|
2017-12-08 21:46:09 +00:00
|
|
|
fWindow->pushLayer(&fImGuiLayer);
|
2016-04-06 13:08:59 +00:00
|
|
|
|
2016-05-10 13:50:49 +00:00
|
|
|
// add key-bindings
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
fCommands.addCommand(' ', "GUI", "Toggle Debug GUI", [this]() {
|
|
|
|
this->fShowImGuiDebugWindow = !this->fShowImGuiDebugWindow;
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2017-11-14 20:32:20 +00:00
|
|
|
// Command to jump directly to the slide picker and give it focus
|
|
|
|
fCommands.addCommand('/', "GUI", "Jump to slide picker", [this]() {
|
|
|
|
this->fShowImGuiDebugWindow = true;
|
|
|
|
this->fShowSlidePicker = true;
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
|
|
|
// Alias that to Backspace, to match SampleApp
|
2019-08-29 14:39:22 +00:00
|
|
|
fCommands.addCommand(skui::Key::kBack, "Backspace", "GUI", "Jump to slide picker", [this]() {
|
2017-11-14 20:32:20 +00:00
|
|
|
this->fShowImGuiDebugWindow = true;
|
|
|
|
this->fShowSlidePicker = true;
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
fCommands.addCommand('g', "GUI", "Toggle GUI Demo", [this]() {
|
|
|
|
this->fShowImGuiTestWindow = !this->fShowImGuiTestWindow;
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2017-02-13 14:39:57 +00:00
|
|
|
fCommands.addCommand('z', "GUI", "Toggle zoom window", [this]() {
|
|
|
|
this->fShowZoomWindow = !this->fShowZoomWindow;
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2018-06-26 18:23:20 +00:00
|
|
|
fCommands.addCommand('Z', "GUI", "Toggle zoom window state", [this]() {
|
|
|
|
this->fZoomWindowFixed = !this->fZoomWindowFixed;
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2019-03-27 20:23:26 +00:00
|
|
|
fCommands.addCommand('v', "VSync", "Toggle vsync on/off", [this]() {
|
|
|
|
DisplayParams params = fWindow->getRequestedDisplayParams();
|
|
|
|
params.fDisableVsync = !params.fDisableVsync;
|
|
|
|
fWindow->setRequestedDisplayParams(params);
|
|
|
|
this->updateTitle();
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2019-07-22 21:00:49 +00:00
|
|
|
fCommands.addCommand('r', "Redraw", "Toggle redraw", [this]() {
|
|
|
|
fRefresh = !fRefresh;
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2016-05-10 13:50:49 +00:00
|
|
|
fCommands.addCommand('s', "Overlays", "Toggle stats display", [this]() {
|
2017-12-19 16:15:16 +00:00
|
|
|
fStatsLayer.setActive(!fStatsLayer.getActive());
|
2016-05-10 13:50:49 +00:00
|
|
|
fWindow->inval();
|
|
|
|
});
|
2017-11-03 17:36:07 +00:00
|
|
|
fCommands.addCommand('0', "Overlays", "Reset stats", [this]() {
|
2017-12-19 16:15:16 +00:00
|
|
|
fStatsLayer.resetMeasurements();
|
2017-11-03 17:36:07 +00:00
|
|
|
this->updateTitle();
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2017-02-08 15:47:28 +00:00
|
|
|
fCommands.addCommand('c', "Modes", "Cycle color mode", [this]() {
|
2017-03-06 16:47:26 +00:00
|
|
|
switch (fColorMode) {
|
|
|
|
case ColorMode::kLegacy:
|
2018-11-26 18:55:19 +00:00
|
|
|
this->setColorMode(ColorMode::kColorManaged8888);
|
2017-03-06 16:47:26 +00:00
|
|
|
break;
|
2018-11-26 18:55:19 +00:00
|
|
|
case ColorMode::kColorManaged8888:
|
|
|
|
this->setColorMode(ColorMode::kColorManagedF16);
|
2017-03-06 16:47:26 +00:00
|
|
|
break;
|
2018-11-26 18:55:19 +00:00
|
|
|
case ColorMode::kColorManagedF16:
|
2019-09-18 15:22:44 +00:00
|
|
|
this->setColorMode(ColorMode::kColorManagedF16Norm);
|
|
|
|
break;
|
|
|
|
case ColorMode::kColorManagedF16Norm:
|
2017-03-06 16:47:26 +00:00
|
|
|
this->setColorMode(ColorMode::kLegacy);
|
|
|
|
break;
|
2017-02-08 15:47:28 +00:00
|
|
|
}
|
2016-05-10 13:50:49 +00:00
|
|
|
});
|
2019-08-29 14:39:22 +00:00
|
|
|
fCommands.addCommand(skui::Key::kRight, "Right", "Navigation", "Next slide", [this]() {
|
2018-01-16 21:23:03 +00:00
|
|
|
this->setCurrentSlide(fCurrentSlide < fSlides.count() - 1 ? fCurrentSlide + 1 : 0);
|
2016-05-10 13:50:49 +00:00
|
|
|
});
|
2019-08-29 14:39:22 +00:00
|
|
|
fCommands.addCommand(skui::Key::kLeft, "Left", "Navigation", "Previous slide", [this]() {
|
2018-01-16 21:23:03 +00:00
|
|
|
this->setCurrentSlide(fCurrentSlide > 0 ? fCurrentSlide - 1 : fSlides.count() - 1);
|
2016-05-10 13:50:49 +00:00
|
|
|
});
|
2019-08-29 14:39:22 +00:00
|
|
|
fCommands.addCommand(skui::Key::kUp, "Up", "Transform", "Zoom in", [this]() {
|
2016-05-10 13:50:49 +00:00
|
|
|
this->changeZoomLevel(1.f / 32.f);
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2019-08-29 14:39:22 +00:00
|
|
|
fCommands.addCommand(skui::Key::kDown, "Down", "Transform", "Zoom out", [this]() {
|
2016-05-10 13:50:49 +00:00
|
|
|
this->changeZoomLevel(-1.f / 32.f);
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2016-05-20 13:01:06 +00:00
|
|
|
fCommands.addCommand('d', "Modes", "Change rendering backend", [this]() {
|
2017-08-17 18:37:06 +00:00
|
|
|
sk_app::Window::BackendType newBackend = (sk_app::Window::BackendType)(
|
|
|
|
(fBackendType + 1) % sk_app::Window::kBackendTypeCount);
|
2017-01-05 18:50:49 +00:00
|
|
|
// Switching to and from Vulkan is problematic on Linux so disabled for now
|
2017-08-17 18:37:06 +00:00
|
|
|
#if defined(SK_BUILD_FOR_UNIX) && defined(SK_VULKAN)
|
|
|
|
if (newBackend == sk_app::Window::kVulkan_BackendType) {
|
|
|
|
newBackend = (sk_app::Window::BackendType)((newBackend + 1) %
|
|
|
|
sk_app::Window::kBackendTypeCount);
|
|
|
|
} else if (fBackendType == sk_app::Window::kVulkan_BackendType) {
|
|
|
|
newBackend = sk_app::Window::kVulkan_BackendType;
|
2017-01-05 18:50:49 +00:00
|
|
|
}
|
|
|
|
#endif
|
2017-02-28 20:45:01 +00:00
|
|
|
this->setBackend(newBackend);
|
2016-05-20 13:01:06 +00:00
|
|
|
});
|
2017-12-01 16:23:53 +00:00
|
|
|
fCommands.addCommand('K', "IO", "Save slide to SKP", [this]() {
|
|
|
|
fSaveToSKP = true;
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2019-03-14 15:39:02 +00:00
|
|
|
fCommands.addCommand('&', "Overlays", "Show slide dimensios", [this]() {
|
|
|
|
fShowSlideDimensions = !fShowSlideDimensions;
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2018-04-13 18:30:23 +00:00
|
|
|
fCommands.addCommand('G', "Modes", "Geometry", [this]() {
|
|
|
|
DisplayParams params = fWindow->getRequestedDisplayParams();
|
|
|
|
uint32_t flags = params.fSurfaceProps.flags();
|
|
|
|
if (!fPixelGeometryOverrides) {
|
|
|
|
fPixelGeometryOverrides = true;
|
|
|
|
params.fSurfaceProps = SkSurfaceProps(flags, kUnknown_SkPixelGeometry);
|
|
|
|
} else {
|
|
|
|
switch (params.fSurfaceProps.pixelGeometry()) {
|
|
|
|
case kUnknown_SkPixelGeometry:
|
|
|
|
params.fSurfaceProps = SkSurfaceProps(flags, kRGB_H_SkPixelGeometry);
|
|
|
|
break;
|
|
|
|
case kRGB_H_SkPixelGeometry:
|
|
|
|
params.fSurfaceProps = SkSurfaceProps(flags, kBGR_H_SkPixelGeometry);
|
|
|
|
break;
|
|
|
|
case kBGR_H_SkPixelGeometry:
|
|
|
|
params.fSurfaceProps = SkSurfaceProps(flags, kRGB_V_SkPixelGeometry);
|
|
|
|
break;
|
|
|
|
case kRGB_V_SkPixelGeometry:
|
|
|
|
params.fSurfaceProps = SkSurfaceProps(flags, kBGR_V_SkPixelGeometry);
|
|
|
|
break;
|
|
|
|
case kBGR_V_SkPixelGeometry:
|
|
|
|
params.fSurfaceProps = SkSurfaceProps(flags, SkSurfaceProps::kLegacyFontHost_InitType);
|
|
|
|
fPixelGeometryOverrides = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fWindow->setRequestedDisplayParams(params);
|
|
|
|
this->updateTitle();
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2019-01-23 15:34:59 +00:00
|
|
|
fCommands.addCommand('H', "Font", "Hinting mode", [this]() {
|
2019-01-04 15:11:46 +00:00
|
|
|
if (!fFontOverrides.fHinting) {
|
|
|
|
fFontOverrides.fHinting = true;
|
2019-05-07 20:50:29 +00:00
|
|
|
fFont.setHinting(SkFontHinting::kNone);
|
2018-02-12 21:37:28 +00:00
|
|
|
} else {
|
2019-01-04 15:11:46 +00:00
|
|
|
switch (fFont.getHinting()) {
|
2019-05-07 20:50:29 +00:00
|
|
|
case SkFontHinting::kNone:
|
|
|
|
fFont.setHinting(SkFontHinting::kSlight);
|
2018-02-12 21:37:28 +00:00
|
|
|
break;
|
2019-05-07 20:50:29 +00:00
|
|
|
case SkFontHinting::kSlight:
|
|
|
|
fFont.setHinting(SkFontHinting::kNormal);
|
2018-02-12 21:37:28 +00:00
|
|
|
break;
|
2019-05-07 20:50:29 +00:00
|
|
|
case SkFontHinting::kNormal:
|
|
|
|
fFont.setHinting(SkFontHinting::kFull);
|
2018-02-12 21:37:28 +00:00
|
|
|
break;
|
2019-05-07 20:50:29 +00:00
|
|
|
case SkFontHinting::kFull:
|
|
|
|
fFont.setHinting(SkFontHinting::kNone);
|
2019-01-04 15:11:46 +00:00
|
|
|
fFontOverrides.fHinting = false;
|
2018-02-12 21:37:28 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this->updateTitle();
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
|
|
|
fCommands.addCommand('A', "Paint", "Antialias Mode", [this]() {
|
2019-01-23 15:34:59 +00:00
|
|
|
if (!fPaintOverrides.fAntiAlias) {
|
|
|
|
fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Alias;
|
|
|
|
fPaintOverrides.fAntiAlias = true;
|
2018-02-12 21:37:28 +00:00
|
|
|
fPaint.setAntiAlias(false);
|
|
|
|
gSkUseAnalyticAA = gSkForceAnalyticAA = false;
|
|
|
|
} else {
|
|
|
|
fPaint.setAntiAlias(true);
|
2019-01-23 15:34:59 +00:00
|
|
|
switch (fPaintOverrides.fAntiAliasState) {
|
2018-02-12 21:37:28 +00:00
|
|
|
case SkPaintFields::AntiAliasState::Alias:
|
2019-01-23 15:34:59 +00:00
|
|
|
fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Normal;
|
2018-04-17 15:16:32 +00:00
|
|
|
gSkUseAnalyticAA = gSkForceAnalyticAA = false;
|
2018-02-12 21:37:28 +00:00
|
|
|
break;
|
|
|
|
case SkPaintFields::AntiAliasState::Normal:
|
2019-01-23 15:34:59 +00:00
|
|
|
fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::AnalyticAAEnabled;
|
2018-02-12 21:37:28 +00:00
|
|
|
gSkUseAnalyticAA = true;
|
2018-04-17 15:16:32 +00:00
|
|
|
gSkForceAnalyticAA = false;
|
2018-02-12 21:37:28 +00:00
|
|
|
break;
|
|
|
|
case SkPaintFields::AntiAliasState::AnalyticAAEnabled:
|
2019-01-23 15:34:59 +00:00
|
|
|
fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::AnalyticAAForced;
|
2018-04-17 15:16:32 +00:00
|
|
|
gSkUseAnalyticAA = gSkForceAnalyticAA = true;
|
2018-02-12 21:37:28 +00:00
|
|
|
break;
|
|
|
|
case SkPaintFields::AntiAliasState::AnalyticAAForced:
|
2019-01-23 15:34:59 +00:00
|
|
|
fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Alias;
|
|
|
|
fPaintOverrides.fAntiAlias = false;
|
2018-02-12 21:37:28 +00:00
|
|
|
gSkUseAnalyticAA = fPaintOverrides.fOriginalSkUseAnalyticAA;
|
|
|
|
gSkForceAnalyticAA = fPaintOverrides.fOriginalSkForceAnalyticAA;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this->updateTitle();
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2018-04-13 18:30:23 +00:00
|
|
|
fCommands.addCommand('D', "Modes", "DFT", [this]() {
|
|
|
|
DisplayParams params = fWindow->getRequestedDisplayParams();
|
|
|
|
uint32_t flags = params.fSurfaceProps.flags();
|
|
|
|
flags ^= SkSurfaceProps::kUseDeviceIndependentFonts_Flag;
|
|
|
|
params.fSurfaceProps = SkSurfaceProps(flags, params.fSurfaceProps.pixelGeometry());
|
|
|
|
fWindow->setRequestedDisplayParams(params);
|
|
|
|
this->updateTitle();
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2019-01-23 15:34:59 +00:00
|
|
|
fCommands.addCommand('L', "Font", "Subpixel Antialias Mode", [this]() {
|
2019-01-10 18:55:35 +00:00
|
|
|
if (!fFontOverrides.fEdging) {
|
|
|
|
fFontOverrides.fEdging = true;
|
|
|
|
fFont.setEdging(SkFont::Edging::kAlias);
|
2018-02-12 21:37:28 +00:00
|
|
|
} else {
|
2019-01-10 18:55:35 +00:00
|
|
|
switch (fFont.getEdging()) {
|
|
|
|
case SkFont::Edging::kAlias:
|
|
|
|
fFont.setEdging(SkFont::Edging::kAntiAlias);
|
|
|
|
break;
|
|
|
|
case SkFont::Edging::kAntiAlias:
|
|
|
|
fFont.setEdging(SkFont::Edging::kSubpixelAntiAlias);
|
|
|
|
break;
|
|
|
|
case SkFont::Edging::kSubpixelAntiAlias:
|
|
|
|
fFont.setEdging(SkFont::Edging::kAlias);
|
|
|
|
fFontOverrides.fEdging = false;
|
|
|
|
break;
|
2018-02-12 21:37:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
this->updateTitle();
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2019-01-23 15:34:59 +00:00
|
|
|
fCommands.addCommand('S', "Font", "Subpixel Position Mode", [this]() {
|
2019-01-10 18:55:35 +00:00
|
|
|
if (!fFontOverrides.fSubpixel) {
|
|
|
|
fFontOverrides.fSubpixel = true;
|
|
|
|
fFont.setSubpixel(false);
|
2018-02-12 21:37:28 +00:00
|
|
|
} else {
|
2019-01-10 18:55:35 +00:00
|
|
|
if (!fFont.isSubpixel()) {
|
|
|
|
fFont.setSubpixel(true);
|
2018-02-12 21:37:28 +00:00
|
|
|
} else {
|
2019-01-10 18:55:35 +00:00
|
|
|
fFontOverrides.fSubpixel = false;
|
2018-02-12 21:37:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
this->updateTitle();
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2019-08-27 20:20:39 +00:00
|
|
|
fCommands.addCommand('B', "Font", "Baseline Snapping", [this]() {
|
|
|
|
if (!fFontOverrides.fBaselineSnap) {
|
|
|
|
fFontOverrides.fBaselineSnap = true;
|
|
|
|
fFont.setBaselineSnap(false);
|
|
|
|
} else {
|
|
|
|
if (!fFont.isBaselineSnap()) {
|
|
|
|
fFont.setBaselineSnap(true);
|
|
|
|
} else {
|
|
|
|
fFontOverrides.fBaselineSnap = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this->updateTitle();
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2018-05-02 19:40:20 +00:00
|
|
|
fCommands.addCommand('p', "Transform", "Toggle Perspective Mode", [this]() {
|
|
|
|
fPerspectiveMode = (kPerspective_Real == fPerspectiveMode) ? kPerspective_Fake
|
|
|
|
: kPerspective_Real;
|
|
|
|
this->updateTitle();
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
|
|
|
fCommands.addCommand('P', "Transform", "Toggle Perspective", [this]() {
|
|
|
|
fPerspectiveMode = (kPerspective_Off == fPerspectiveMode) ? kPerspective_Real
|
|
|
|
: kPerspective_Off;
|
|
|
|
this->updateTitle();
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2019-01-10 14:40:58 +00:00
|
|
|
fCommands.addCommand('a', "Transform", "Toggle Animation", [this]() {
|
|
|
|
fAnimTimer.togglePauseResume();
|
|
|
|
});
|
2018-07-24 22:01:53 +00:00
|
|
|
fCommands.addCommand('u', "GUI", "Zoom UI", [this]() {
|
|
|
|
fZoomUI = !fZoomUI;
|
|
|
|
fStatsLayer.setDisplayScale(fZoomUI ? 2.0f : 1.0f);
|
|
|
|
fWindow->inval();
|
|
|
|
});
|
2017-11-22 17:07:41 +00:00
|
|
|
|
2016-04-08 14:24:09 +00:00
|
|
|
// set up slides
|
|
|
|
this->initSlides();
|
2017-02-14 20:16:46 +00:00
|
|
|
if (FLAGS_list) {
|
|
|
|
this->listNames();
|
|
|
|
}
|
2016-04-08 14:24:09 +00:00
|
|
|
|
2018-04-26 19:55:00 +00:00
|
|
|
fPerspectivePoints[0].set(0, 0);
|
|
|
|
fPerspectivePoints[1].set(1, 0);
|
|
|
|
fPerspectivePoints[2].set(0, 1);
|
|
|
|
fPerspectivePoints[3].set(1, 1);
|
2016-04-21 14:59:44 +00:00
|
|
|
fAnimTimer.run();
|
|
|
|
|
2017-12-08 15:21:31 +00:00
|
|
|
auto gamutImage = GetResourceAsImage("images/gamut.png");
|
2017-02-24 14:49:14 +00:00
|
|
|
if (gamutImage) {
|
2017-04-28 15:12:19 +00:00
|
|
|
fImGuiGamutPaint.setShader(gamutImage->makeShader());
|
2017-02-24 14:49:14 +00:00
|
|
|
}
|
|
|
|
fImGuiGamutPaint.setColor(SK_ColorWHITE);
|
|
|
|
fImGuiGamutPaint.setFilterQuality(kLow_SkFilterQuality);
|
|
|
|
|
2019-02-26 05:39:23 +00:00
|
|
|
fWindow->attach(backend_type_for_window(fBackendType));
|
2019-03-01 19:37:30 +00:00
|
|
|
this->setCurrentSlide(this->startupSlide());
|
2016-04-08 14:24:09 +00:00
|
|
|
}
|
|
|
|
|
2016-05-04 20:49:13 +00:00
|
|
|
void Viewer::initSlides() {
|
2018-04-05 18:34:45 +00:00
|
|
|
using SlideFactory = sk_sp<Slide>(*)(const SkString& name, const SkString& path);
|
|
|
|
static const struct {
|
|
|
|
const char* fExtension;
|
|
|
|
const char* fDirName;
|
2019-03-20 15:50:33 +00:00
|
|
|
const CommandLineFlags::StringArray& fFlags;
|
2018-04-05 18:34:45 +00:00
|
|
|
const SlideFactory fFactory;
|
|
|
|
} gExternalSlidesInfo[] = {
|
|
|
|
{ ".skp", "skp-dir", FLAGS_skps,
|
|
|
|
[](const SkString& name, const SkString& path) -> sk_sp<Slide> {
|
|
|
|
return sk_make_sp<SKPSlide>(name, path);}
|
|
|
|
},
|
|
|
|
{ ".jpg", "jpg-dir", FLAGS_jpgs,
|
|
|
|
[](const SkString& name, const SkString& path) -> sk_sp<Slide> {
|
|
|
|
return sk_make_sp<ImageSlide>(name, path);}
|
|
|
|
},
|
2018-05-04 16:23:24 +00:00
|
|
|
#if defined(SK_ENABLE_SKOTTIE)
|
2018-07-19 17:27:49 +00:00
|
|
|
{ ".json", "skottie-dir", FLAGS_lotties,
|
2018-04-05 18:34:45 +00:00
|
|
|
[](const SkString& name, const SkString& path) -> sk_sp<Slide> {
|
|
|
|
return sk_make_sp<SkottieSlide>(name, path);}
|
|
|
|
},
|
2018-05-04 16:23:24 +00:00
|
|
|
#endif
|
2018-07-31 20:38:43 +00:00
|
|
|
#if defined(SK_XML)
|
2018-04-05 18:34:45 +00:00
|
|
|
{ ".svg", "svg-dir", FLAGS_svgs,
|
|
|
|
[](const SkString& name, const SkString& path) -> sk_sp<Slide> {
|
|
|
|
return sk_make_sp<SvgSlide>(name, path);}
|
|
|
|
},
|
2018-06-29 18:32:21 +00:00
|
|
|
#endif
|
2018-04-05 18:34:45 +00:00
|
|
|
};
|
|
|
|
|
2018-09-05 19:41:23 +00:00
|
|
|
SkTArray<sk_sp<Slide>> dirSlides;
|
2018-04-05 18:34:45 +00:00
|
|
|
|
2019-03-20 15:50:33 +00:00
|
|
|
const auto addSlide =
|
|
|
|
[&](const SkString& name, const SkString& path, const SlideFactory& fact) {
|
|
|
|
if (CommandLineFlags::ShouldSkip(FLAGS_match, name.c_str())) {
|
|
|
|
return;
|
|
|
|
}
|
2017-12-30 17:27:00 +00:00
|
|
|
|
2019-03-20 15:50:33 +00:00
|
|
|
if (auto slide = fact(name, path)) {
|
|
|
|
dirSlides.push_back(slide);
|
|
|
|
fSlides.push_back(std::move(slide));
|
|
|
|
}
|
|
|
|
};
|
2018-02-15 23:40:48 +00:00
|
|
|
|
2018-05-08 14:36:18 +00:00
|
|
|
if (!FLAGS_file.isEmpty()) {
|
|
|
|
// single file mode
|
|
|
|
const SkString file(FLAGS_file[0]);
|
|
|
|
|
|
|
|
if (sk_exists(file.c_str(), kRead_SkFILE_Flag)) {
|
|
|
|
for (const auto& sinfo : gExternalSlidesInfo) {
|
|
|
|
if (file.endsWith(sinfo.fExtension)) {
|
|
|
|
addSlide(SkOSPath::Basename(file.c_str()), file, sinfo.fFactory);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "Unsupported file type \"%s\"\n", file.c_str());
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Cannot read \"%s\"\n", file.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bisect slide.
|
|
|
|
if (!FLAGS_bisect.isEmpty()) {
|
|
|
|
sk_sp<BisectSlide> bisect = BisectSlide::Create(FLAGS_bisect[0]);
|
2019-03-20 15:50:33 +00:00
|
|
|
if (bisect && !CommandLineFlags::ShouldSkip(FLAGS_match, bisect->getName().c_str())) {
|
2018-05-08 14:36:18 +00:00
|
|
|
if (FLAGS_bisect.count() >= 2) {
|
|
|
|
for (const char* ch = FLAGS_bisect[1]; *ch; ++ch) {
|
|
|
|
bisect->onChar(*ch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fSlides.push_back(std::move(bisect));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GMs
|
|
|
|
int firstGM = fSlides.count();
|
2018-07-30 21:07:07 +00:00
|
|
|
for (skiagm::GMFactory gmFactory : skiagm::GMRegistry::Range()) {
|
2019-08-12 20:39:24 +00:00
|
|
|
std::unique_ptr<skiagm::GM> gm = gmFactory();
|
2019-03-20 15:50:33 +00:00
|
|
|
if (!CommandLineFlags::ShouldSkip(FLAGS_match, gm->getName())) {
|
2019-08-12 20:39:24 +00:00
|
|
|
sk_sp<Slide> slide(new GMSlide(std::move(gm)));
|
2018-05-08 14:36:18 +00:00
|
|
|
fSlides.push_back(std::move(slide));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// reverse gms
|
|
|
|
int numGMs = fSlides.count() - firstGM;
|
|
|
|
for (int i = 0; i < numGMs/2; ++i) {
|
|
|
|
std::swap(fSlides[firstGM + i], fSlides[fSlides.count() - i - 1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// samples
|
2018-08-08 15:36:17 +00:00
|
|
|
for (const SampleFactory factory : SampleRegistry::Range()) {
|
|
|
|
sk_sp<Slide> slide(new SampleSlide(factory));
|
2019-03-20 15:50:33 +00:00
|
|
|
if (!CommandLineFlags::ShouldSkip(FLAGS_match, slide->getName().c_str())) {
|
2018-05-08 14:36:18 +00:00
|
|
|
fSlides.push_back(slide);
|
|
|
|
}
|
Experimental Particle System
This adds a new "Particles" slide to viewer, that allows
editing, loading, and saving particle effects. All of the
particle system code is in modules/particles.
There are many rough edges and some not-yet-finished changes
to generalize the model[1]. A rough overview:
- SkReflected.h implements a lightweight reflection system
for classes derived from SkReflected. Adding a new class
involves deriving from SkReflected, adding a macro to the
class declaration, and implementing visitFields(), which
simply calls a virtual on an SkFieldVisitor for each field.
Currently, emitters and affectors use this mechanism.
- SkParticleSerialization.h demonstrates two useful field
visitors - for serializing to and from JSON. The driver
code that uses those is directly in ParticlesSlide.
- SkParticleData.h and SkCurve.h define a variety of helper
types for talking about particles, both for parameterizing
individual values, and communicating about the state of a
particle among the effect, affectors, and emitters.
- SkParticleEffect.h defines the static data definition of
an effect (SkParticleEffectParams), as well as a running
instance of an effect (SkParticleEffect). The effect has
simple update() and draw() methods.
- ParticlesSlide.cpp adds a third field visitor to generate
GUIs for interactively editing the running effect.
---
1: The critical change I'd like to make is to remove all
special case behavior over time and at spawn (setting sprite
frames, size over time, color over time, etc...). Integration
is the only fixed function behavior. Everything else is driven
by two lists of affectors. One is applied at spawn time, using
the effect's lifetime to evaluate curves. This allows spawning
particles with different colors as the effect ages out, for
example. The second list is applied every frame to update
existing particles, and is driven by the particle's lifetime.
This allows particles to change color after being spawned, for
example.
With a small set of affectors using a single expressive curve
primitive (keyframed list of cubic curve segments), we can
have affectors that update color, size, velocity, position,
sprite frame, etc., and implement many complex behaviors.
Bug: skia:
Change-Id: Id9402bef22825d55d021c5a2f9e5e41791aabaf4
Reviewed-on: https://skia-review.googlesource.com/c/181404
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Mike Reed <reed@google.com>
2019-02-12 18:27:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Particle demo
|
|
|
|
{
|
|
|
|
// TODO: Convert this to a sample
|
|
|
|
sk_sp<Slide> slide(new ParticlesSlide());
|
2019-03-20 15:50:33 +00:00
|
|
|
if (!CommandLineFlags::ShouldSkip(FLAGS_match, slide->getName().c_str())) {
|
Experimental Particle System
This adds a new "Particles" slide to viewer, that allows
editing, loading, and saving particle effects. All of the
particle system code is in modules/particles.
There are many rough edges and some not-yet-finished changes
to generalize the model[1]. A rough overview:
- SkReflected.h implements a lightweight reflection system
for classes derived from SkReflected. Adding a new class
involves deriving from SkReflected, adding a macro to the
class declaration, and implementing visitFields(), which
simply calls a virtual on an SkFieldVisitor for each field.
Currently, emitters and affectors use this mechanism.
- SkParticleSerialization.h demonstrates two useful field
visitors - for serializing to and from JSON. The driver
code that uses those is directly in ParticlesSlide.
- SkParticleData.h and SkCurve.h define a variety of helper
types for talking about particles, both for parameterizing
individual values, and communicating about the state of a
particle among the effect, affectors, and emitters.
- SkParticleEffect.h defines the static data definition of
an effect (SkParticleEffectParams), as well as a running
instance of an effect (SkParticleEffect). The effect has
simple update() and draw() methods.
- ParticlesSlide.cpp adds a third field visitor to generate
GUIs for interactively editing the running effect.
---
1: The critical change I'd like to make is to remove all
special case behavior over time and at spawn (setting sprite
frames, size over time, color over time, etc...). Integration
is the only fixed function behavior. Everything else is driven
by two lists of affectors. One is applied at spawn time, using
the effect's lifetime to evaluate curves. This allows spawning
particles with different colors as the effect ages out, for
example. The second list is applied every frame to update
existing particles, and is driven by the particle's lifetime.
This allows particles to change color after being spawned, for
example.
With a small set of affectors using a single expressive curve
primitive (keyframed list of cubic curve segments), we can
have affectors that update color, size, velocity, position,
sprite frame, etc., and implement many complex behaviors.
Bug: skia:
Change-Id: Id9402bef22825d55d021c5a2f9e5e41791aabaf4
Reviewed-on: https://skia-review.googlesource.com/c/181404
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Mike Reed <reed@google.com>
2019-02-12 18:27:51 +00:00
|
|
|
fSlides.push_back(std::move(slide));
|
|
|
|
}
|
2018-05-08 14:36:18 +00:00
|
|
|
}
|
|
|
|
|
2018-04-05 18:34:45 +00:00
|
|
|
for (const auto& info : gExternalSlidesInfo) {
|
|
|
|
for (const auto& flag : info.fFlags) {
|
|
|
|
if (SkStrEndsWith(flag.c_str(), info.fExtension)) {
|
|
|
|
// single file
|
|
|
|
addSlide(SkOSPath::Basename(flag.c_str()), flag, info.fFactory);
|
|
|
|
} else {
|
|
|
|
// directory
|
|
|
|
SkOSFile::Iter it(flag.c_str(), info.fExtension);
|
|
|
|
SkString name;
|
|
|
|
while (it.next(&name)) {
|
|
|
|
addSlide(name, SkOSPath::Join(flag.c_str(), name.c_str()), info.fFactory);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!dirSlides.empty()) {
|
|
|
|
fSlides.push_back(
|
|
|
|
sk_make_sp<SlideDir>(SkStringPrintf("%s[%s]", info.fDirName, flag.c_str()),
|
|
|
|
std::move(dirSlides)));
|
2018-12-11 14:54:31 +00:00
|
|
|
dirSlides.reset(); // NOLINT(bugprone-use-after-move)
|
2018-04-05 15:57:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-01 19:37:30 +00:00
|
|
|
|
|
|
|
if (!fSlides.count()) {
|
|
|
|
sk_sp<Slide> slide(new NullSlide());
|
|
|
|
fSlides.push_back(std::move(slide));
|
|
|
|
}
|
2016-04-08 14:24:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-04 20:49:13 +00:00
|
|
|
Viewer::~Viewer() {
|
2016-04-06 13:08:59 +00:00
|
|
|
fWindow->detach();
|
|
|
|
delete fWindow;
|
|
|
|
}
|
|
|
|
|
2018-02-12 21:37:28 +00:00
|
|
|
struct SkPaintTitleUpdater {
|
|
|
|
SkPaintTitleUpdater(SkString* title) : fTitle(title), fCount(0) {}
|
|
|
|
void append(const char* s) {
|
|
|
|
if (fCount == 0) {
|
|
|
|
fTitle->append(" {");
|
|
|
|
} else {
|
|
|
|
fTitle->append(", ");
|
|
|
|
}
|
|
|
|
fTitle->append(s);
|
|
|
|
++fCount;
|
|
|
|
}
|
|
|
|
void done() {
|
|
|
|
if (fCount > 0) {
|
|
|
|
fTitle->append("}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SkString* fTitle;
|
|
|
|
int fCount;
|
|
|
|
};
|
|
|
|
|
2016-05-06 20:28:57 +00:00
|
|
|
void Viewer::updateTitle() {
|
2017-02-24 23:04:47 +00:00
|
|
|
if (!fWindow) {
|
|
|
|
return;
|
|
|
|
}
|
2018-02-03 01:32:49 +00:00
|
|
|
if (fWindow->sampleCount() < 1) {
|
2017-02-24 23:04:47 +00:00
|
|
|
return; // Surface hasn't been created yet.
|
|
|
|
}
|
|
|
|
|
2016-05-04 20:49:13 +00:00
|
|
|
SkString title("Viewer: ");
|
2016-04-08 19:51:45 +00:00
|
|
|
title.append(fSlides[fCurrentSlide]->getName());
|
2016-06-16 20:03:24 +00:00
|
|
|
|
2019-03-22 14:57:16 +00:00
|
|
|
if (gSkUseAnalyticAA) {
|
2017-08-03 15:08:15 +00:00
|
|
|
if (gSkForceAnalyticAA) {
|
|
|
|
title.append(" <FAAA>");
|
|
|
|
} else {
|
|
|
|
title.append(" <AAA>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-12 21:37:28 +00:00
|
|
|
SkPaintTitleUpdater paintTitle(&title);
|
2019-01-23 15:34:59 +00:00
|
|
|
auto paintFlag = [this, &paintTitle](bool SkPaintFields::* flag,
|
|
|
|
bool (SkPaint::* isFlag)() const,
|
2018-05-09 22:23:51 +00:00
|
|
|
const char* on, const char* off)
|
|
|
|
{
|
2019-01-23 15:34:59 +00:00
|
|
|
if (fPaintOverrides.*flag) {
|
2018-05-09 22:23:51 +00:00
|
|
|
paintTitle.append((fPaint.*isFlag)() ? on : off);
|
2018-02-12 21:37:28 +00:00
|
|
|
}
|
2018-05-09 22:23:51 +00:00
|
|
|
};
|
|
|
|
|
2019-01-23 15:34:59 +00:00
|
|
|
auto fontFlag = [this, &paintTitle](bool SkFontFields::* flag, bool (SkFont::* isFlag)() const,
|
|
|
|
const char* on, const char* off)
|
|
|
|
{
|
|
|
|
if (fFontOverrides.*flag) {
|
|
|
|
paintTitle.append((fFont.*isFlag)() ? on : off);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
paintFlag(&SkPaintFields::fAntiAlias, &SkPaint::isAntiAlias, "Antialias", "Alias");
|
|
|
|
paintFlag(&SkPaintFields::fDither, &SkPaint::isDither, "DITHER", "No Dither");
|
2019-03-07 18:14:26 +00:00
|
|
|
if (fPaintOverrides.fFilterQuality) {
|
|
|
|
switch (fPaint.getFilterQuality()) {
|
|
|
|
case kNone_SkFilterQuality:
|
|
|
|
paintTitle.append("NoFilter");
|
|
|
|
break;
|
|
|
|
case kLow_SkFilterQuality:
|
|
|
|
paintTitle.append("LowFilter");
|
|
|
|
break;
|
|
|
|
case kMedium_SkFilterQuality:
|
|
|
|
paintTitle.append("MediumFilter");
|
|
|
|
break;
|
|
|
|
case kHigh_SkFilterQuality:
|
|
|
|
paintTitle.append("HighFilter");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2019-01-23 15:34:59 +00:00
|
|
|
|
|
|
|
fontFlag(&SkFontFields::fForceAutoHinting, &SkFont::isForceAutoHinting,
|
|
|
|
"Force Autohint", "No Force Autohint");
|
|
|
|
fontFlag(&SkFontFields::fEmbolden, &SkFont::isEmbolden, "Fake Bold", "No Fake Bold");
|
2019-08-26 20:59:09 +00:00
|
|
|
fontFlag(&SkFontFields::fBaselineSnap, &SkFont::isBaselineSnap, "BaseSnap", "No BaseSnap");
|
2019-01-23 15:34:59 +00:00
|
|
|
fontFlag(&SkFontFields::fLinearMetrics, &SkFont::isLinearMetrics,
|
|
|
|
"Linear Metrics", "Non-Linear Metrics");
|
|
|
|
fontFlag(&SkFontFields::fEmbeddedBitmaps, &SkFont::isEmbeddedBitmaps,
|
|
|
|
"Bitmap Text", "No Bitmap Text");
|
|
|
|
fontFlag(&SkFontFields::fSubpixel, &SkFont::isSubpixel, "Subpixel Text", "Pixel Text");
|
|
|
|
|
|
|
|
if (fFontOverrides.fEdging) {
|
|
|
|
switch (fFont.getEdging()) {
|
|
|
|
case SkFont::Edging::kAlias:
|
|
|
|
paintTitle.append("Alias Text");
|
|
|
|
break;
|
|
|
|
case SkFont::Edging::kAntiAlias:
|
|
|
|
paintTitle.append("Antialias Text");
|
|
|
|
break;
|
|
|
|
case SkFont::Edging::kSubpixelAntiAlias:
|
|
|
|
paintTitle.append("Subpixel Antialias Text");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-05-09 22:23:51 +00:00
|
|
|
|
2019-01-04 15:11:46 +00:00
|
|
|
if (fFontOverrides.fHinting) {
|
|
|
|
switch (fFont.getHinting()) {
|
2019-05-07 20:50:29 +00:00
|
|
|
case SkFontHinting::kNone:
|
2018-02-12 21:37:28 +00:00
|
|
|
paintTitle.append("No Hinting");
|
|
|
|
break;
|
2019-05-07 20:50:29 +00:00
|
|
|
case SkFontHinting::kSlight:
|
2018-02-12 21:37:28 +00:00
|
|
|
paintTitle.append("Slight Hinting");
|
|
|
|
break;
|
2019-05-07 20:50:29 +00:00
|
|
|
case SkFontHinting::kNormal:
|
2018-02-12 21:37:28 +00:00
|
|
|
paintTitle.append("Normal Hinting");
|
|
|
|
break;
|
2019-05-07 20:50:29 +00:00
|
|
|
case SkFontHinting::kFull:
|
2018-02-12 21:37:28 +00:00
|
|
|
paintTitle.append("Full Hinting");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
paintTitle.done();
|
|
|
|
|
2017-03-06 16:47:26 +00:00
|
|
|
switch (fColorMode) {
|
|
|
|
case ColorMode::kLegacy:
|
|
|
|
title.append(" Legacy 8888");
|
|
|
|
break;
|
2018-11-26 18:55:19 +00:00
|
|
|
case ColorMode::kColorManaged8888:
|
2017-03-06 16:47:26 +00:00
|
|
|
title.append(" ColorManaged 8888");
|
|
|
|
break;
|
2018-11-26 18:55:19 +00:00
|
|
|
case ColorMode::kColorManagedF16:
|
2017-03-06 16:47:26 +00:00
|
|
|
title.append(" ColorManaged F16");
|
|
|
|
break;
|
2019-09-18 15:22:44 +00:00
|
|
|
case ColorMode::kColorManagedF16Norm:
|
|
|
|
title.append(" ColorManaged F16 Norm");
|
|
|
|
break;
|
2017-03-06 16:47:26 +00:00
|
|
|
}
|
2017-02-24 14:49:14 +00:00
|
|
|
|
2017-03-06 16:47:26 +00:00
|
|
|
if (ColorMode::kLegacy != fColorMode) {
|
2017-02-24 14:49:14 +00:00
|
|
|
int curPrimaries = -1;
|
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(gNamedPrimaries); ++i) {
|
|
|
|
if (primaries_equal(*gNamedPrimaries[i].fPrimaries, fColorSpacePrimaries)) {
|
|
|
|
curPrimaries = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-11-26 18:55:19 +00:00
|
|
|
title.appendf(" %s Gamma %f",
|
|
|
|
curPrimaries >= 0 ? gNamedPrimaries[curPrimaries].fName : "Custom",
|
2019-01-04 22:03:00 +00:00
|
|
|
fColorSpaceTransferFn.g);
|
2016-05-06 20:28:57 +00:00
|
|
|
}
|
2017-02-08 15:47:28 +00:00
|
|
|
|
2018-04-13 18:30:23 +00:00
|
|
|
const DisplayParams& params = fWindow->getRequestedDisplayParams();
|
|
|
|
if (fPixelGeometryOverrides) {
|
|
|
|
switch (params.fSurfaceProps.pixelGeometry()) {
|
|
|
|
case kUnknown_SkPixelGeometry:
|
|
|
|
title.append( " Flat");
|
|
|
|
break;
|
|
|
|
case kRGB_H_SkPixelGeometry:
|
|
|
|
title.append( " RGB");
|
|
|
|
break;
|
|
|
|
case kBGR_H_SkPixelGeometry:
|
|
|
|
title.append( " BGR");
|
|
|
|
break;
|
|
|
|
case kRGB_V_SkPixelGeometry:
|
|
|
|
title.append( " RGBV");
|
|
|
|
break;
|
|
|
|
case kBGR_V_SkPixelGeometry:
|
|
|
|
title.append( " BGRV");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (params.fSurfaceProps.isUseDeviceIndependentFonts()) {
|
|
|
|
title.append(" DFT");
|
|
|
|
}
|
|
|
|
|
2017-02-24 23:04:47 +00:00
|
|
|
title.append(" [");
|
2016-05-20 13:01:06 +00:00
|
|
|
title.append(kBackendTypeStrings[fBackendType]);
|
2018-02-03 01:32:49 +00:00
|
|
|
int msaa = fWindow->sampleCount();
|
|
|
|
if (msaa > 1) {
|
2017-02-24 23:04:47 +00:00
|
|
|
title.appendf(" MSAA: %i", msaa);
|
|
|
|
}
|
|
|
|
title.append("]");
|
2017-02-28 00:00:53 +00:00
|
|
|
|
|
|
|
GpuPathRenderers pr = fWindow->getRequestedDisplayParams().fGrContextOptions.fGpuPathRenderers;
|
2019-03-30 06:31:23 +00:00
|
|
|
if (GpuPathRenderers::kAll != pr) {
|
2017-02-28 00:00:53 +00:00
|
|
|
title.appendf(" [Path renderer: %s]", gPathRendererNames[pr].c_str());
|
|
|
|
}
|
|
|
|
|
2018-05-02 19:40:20 +00:00
|
|
|
if (kPerspective_Real == fPerspectiveMode) {
|
|
|
|
title.append(" Perpsective (Real)");
|
|
|
|
} else if (kPerspective_Fake == fPerspectiveMode) {
|
|
|
|
title.append(" Perspective (Fake)");
|
|
|
|
}
|
|
|
|
|
2016-05-06 20:28:57 +00:00
|
|
|
fWindow->setTitle(title.c_str());
|
|
|
|
}
|
|
|
|
|
2018-01-16 21:23:03 +00:00
|
|
|
int Viewer::startupSlide() const {
|
2017-02-14 20:16:46 +00:00
|
|
|
|
|
|
|
if (!FLAGS_slide.isEmpty()) {
|
|
|
|
int count = fSlides.count();
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
if (fSlides[i]->getName().equals(FLAGS_slide[0])) {
|
2018-01-16 21:23:03 +00:00
|
|
|
return i;
|
2017-02-14 20:16:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "Unknown slide \"%s\"\n", FLAGS_slide[0]);
|
|
|
|
this->listNames();
|
|
|
|
}
|
|
|
|
|
2018-01-16 21:23:03 +00:00
|
|
|
return 0;
|
2017-02-14 20:16:46 +00:00
|
|
|
}
|
|
|
|
|
2018-01-16 21:23:03 +00:00
|
|
|
void Viewer::listNames() const {
|
2017-02-14 20:16:46 +00:00
|
|
|
SkDebugf("All Slides:\n");
|
2018-01-16 21:23:03 +00:00
|
|
|
for (const auto& slide : fSlides) {
|
|
|
|
SkDebugf(" %s\n", slide->getName().c_str());
|
2017-02-14 20:16:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-16 21:23:03 +00:00
|
|
|
void Viewer::setCurrentSlide(int slide) {
|
|
|
|
SkASSERT(slide >= 0 && slide < fSlides.count());
|
|
|
|
|
|
|
|
if (slide == fCurrentSlide) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fCurrentSlide >= 0) {
|
|
|
|
fSlides[fCurrentSlide]->unload();
|
2016-05-27 15:52:52 +00:00
|
|
|
}
|
2016-06-13 19:26:45 +00:00
|
|
|
|
2018-01-16 21:23:03 +00:00
|
|
|
fSlides[slide]->load(SkIntToScalar(fWindow->width()),
|
|
|
|
SkIntToScalar(fWindow->height()));
|
|
|
|
fCurrentSlide = slide;
|
|
|
|
this->setupCurrentSlide();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Viewer::setupCurrentSlide() {
|
2018-01-22 18:39:30 +00:00
|
|
|
if (fCurrentSlide >= 0) {
|
|
|
|
// prepare dimensions for image slides
|
|
|
|
fGesture.resetTouchState();
|
|
|
|
fDefaultMatrix.reset();
|
|
|
|
|
|
|
|
const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
|
|
|
|
const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
|
|
|
|
const SkRect windowRect = SkRect::MakeIWH(fWindow->width(), fWindow->height());
|
|
|
|
|
|
|
|
// Start with a matrix that scales the slide to the available screen space
|
|
|
|
if (fWindow->scaleContentToFit()) {
|
|
|
|
if (windowRect.width() > 0 && windowRect.height() > 0) {
|
|
|
|
fDefaultMatrix.setRectToRect(slideBounds, windowRect, SkMatrix::kStart_ScaleToFit);
|
|
|
|
}
|
2016-05-20 14:32:19 +00:00
|
|
|
}
|
|
|
|
|
2018-01-22 18:39:30 +00:00
|
|
|
// Prevent the user from dragging content so far outside the window they can't find it again
|
2018-03-28 20:23:31 +00:00
|
|
|
fGesture.setTransLimit(slideBounds, windowRect, this->computePreTouchMatrix());
|
2016-05-20 14:32:19 +00:00
|
|
|
|
2018-01-22 18:39:30 +00:00
|
|
|
this->updateTitle();
|
|
|
|
this->updateUIState();
|
2017-11-03 17:36:07 +00:00
|
|
|
|
2018-01-22 18:39:30 +00:00
|
|
|
fStatsLayer.resetMeasurements();
|
2017-11-03 17:36:07 +00:00
|
|
|
|
2018-01-22 18:39:30 +00:00
|
|
|
fWindow->inval();
|
|
|
|
}
|
2016-04-08 19:51:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#define MAX_ZOOM_LEVEL 8
|
|
|
|
#define MIN_ZOOM_LEVEL -8
|
|
|
|
|
2016-05-04 20:49:13 +00:00
|
|
|
void Viewer::changeZoomLevel(float delta) {
|
2016-04-08 19:51:45 +00:00
|
|
|
fZoomLevel += delta;
|
Simplify some Viewer code, and fix a few bugs
The content rect was always identical to the window rect,
so most of the related code did nothing. The translation
limit code is always useful (to avoid dragging the slide
way off-screen with the mouse), so always include it.
The auto-scaling to fit the screen is also still useful,
but just base it on the window rect.
The zoom code has four state variables, only used two of
them, and one was a trivially derived computation. Fold
most of that work into computeMatrix. (The translation
was always zero -- we never changed the zoom center.)
Include fDefaultMatrix in the matrix from computeMatrix,
rather than needing to apply it specially to the canvas.
Don't apply the inverse default matrix to touch or mouse
points. The absolute positions of those touch points is
not important, but because that matrix includes scale
(and sometimes very large or very small scale), it just
had the effect of greatly amplifying or damping the drag
speed. Without it, the slide always pans at the speed of
the touch/mouse drag -- which seems more desirable.
The use of the inverse default matrix was a clever trick,
but it caused the translation (applied to the global mtx)
to be scaled, so the slide was always pinned incorrectly.
Instead, supply the unmodified window rect and the default
matrix, so the trans limit code can do the obvious correct
thing: xform the slide bounds completely, then limit the
translation that will be applied after that. Slides are
now correctly pinned to screen edge regardless of how
much zoom is present in the default matrix.
Note: There are still several bugs related to all of this
code, but given the web of xform state, it's hard to
unravel. The touch gesture still doesn't know about
viewer's zoom, so that's ignored when doing the pinning.
Beyond that, it doesn't even know about window resize -
it only configures the translation limit when setting up
a slide. I had a fix for all of this (doing the
translation limiting in computeMatrix), but then the touch
gesture doesn't know about it, and can accumulate drag
motion that needs to be un-dragged to get back on-screen,
even though the slide is never really translated that far.
SkTouchGesture is in include. No one uses it except viewer:
TBR=bsalomon@google.com
Bug: skia:
Change-Id: I460cc07c3de6d36e63826f57d359faf1facf5ab3
Reviewed-on: https://skia-review.googlesource.com/18524
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Yuqian Li <liyuqian@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-06-05 12:46:04 +00:00
|
|
|
fZoomLevel = SkScalarPin(fZoomLevel, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL);
|
2018-04-23 16:55:06 +00:00
|
|
|
this->preTouchMatrixChanged();
|
|
|
|
}
|
2016-04-08 19:51:45 +00:00
|
|
|
|
2018-04-23 16:55:06 +00:00
|
|
|
void Viewer::preTouchMatrixChanged() {
|
|
|
|
// Update the trans limit as the transform changes.
|
2018-03-28 20:23:31 +00:00
|
|
|
const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
|
|
|
|
const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
|
|
|
|
const SkRect windowRect = SkRect::MakeIWH(fWindow->width(), fWindow->height());
|
|
|
|
fGesture.setTransLimit(slideBounds, windowRect, this->computePreTouchMatrix());
|
|
|
|
}
|
2016-04-08 19:51:45 +00:00
|
|
|
|
2018-05-02 19:40:20 +00:00
|
|
|
SkMatrix Viewer::computePerspectiveMatrix() {
|
|
|
|
SkScalar w = fWindow->width(), h = fWindow->height();
|
|
|
|
SkPoint orthoPts[4] = { { 0, 0 }, { w, 0 }, { 0, h }, { w, h } };
|
|
|
|
SkPoint perspPts[4] = {
|
|
|
|
{ fPerspectivePoints[0].fX * w, fPerspectivePoints[0].fY * h },
|
|
|
|
{ fPerspectivePoints[1].fX * w, fPerspectivePoints[1].fY * h },
|
|
|
|
{ fPerspectivePoints[2].fX * w, fPerspectivePoints[2].fY * h },
|
|
|
|
{ fPerspectivePoints[3].fX * w, fPerspectivePoints[3].fY * h }
|
|
|
|
};
|
|
|
|
SkMatrix m;
|
|
|
|
m.setPolyToPoly(orthoPts, perspPts, 4);
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2018-03-28 20:23:31 +00:00
|
|
|
SkMatrix Viewer::computePreTouchMatrix() {
|
|
|
|
SkMatrix m = fDefaultMatrix;
|
2019-03-21 20:50:22 +00:00
|
|
|
|
|
|
|
SkScalar zoomScale = exp(fZoomLevel);
|
2018-08-09 19:18:46 +00:00
|
|
|
m.preTranslate((fOffset.x() - 0.5f) * 2.0f, (fOffset.y() - 0.5f) * 2.0f);
|
Simplify some Viewer code, and fix a few bugs
The content rect was always identical to the window rect,
so most of the related code did nothing. The translation
limit code is always useful (to avoid dragging the slide
way off-screen with the mouse), so always include it.
The auto-scaling to fit the screen is also still useful,
but just base it on the window rect.
The zoom code has four state variables, only used two of
them, and one was a trivially derived computation. Fold
most of that work into computeMatrix. (The translation
was always zero -- we never changed the zoom center.)
Include fDefaultMatrix in the matrix from computeMatrix,
rather than needing to apply it specially to the canvas.
Don't apply the inverse default matrix to touch or mouse
points. The absolute positions of those touch points is
not important, but because that matrix includes scale
(and sometimes very large or very small scale), it just
had the effect of greatly amplifying or damping the drag
speed. Without it, the slide always pans at the speed of
the touch/mouse drag -- which seems more desirable.
The use of the inverse default matrix was a clever trick,
but it caused the translation (applied to the global mtx)
to be scaled, so the slide was always pinned incorrectly.
Instead, supply the unmodified window rect and the default
matrix, so the trans limit code can do the obvious correct
thing: xform the slide bounds completely, then limit the
translation that will be applied after that. Slides are
now correctly pinned to screen edge regardless of how
much zoom is present in the default matrix.
Note: There are still several bugs related to all of this
code, but given the web of xform state, it's hard to
unravel. The touch gesture still doesn't know about
viewer's zoom, so that's ignored when doing the pinning.
Beyond that, it doesn't even know about window resize -
it only configures the translation limit when setting up
a slide. I had a fix for all of this (doing the
translation limiting in computeMatrix), but then the touch
gesture doesn't know about it, and can accumulate drag
motion that needs to be un-dragged to get back on-screen,
even though the slide is never really translated that far.
SkTouchGesture is in include. No one uses it except viewer:
TBR=bsalomon@google.com
Bug: skia:
Change-Id: I460cc07c3de6d36e63826f57d359faf1facf5ab3
Reviewed-on: https://skia-review.googlesource.com/18524
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Yuqian Li <liyuqian@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-06-05 12:46:04 +00:00
|
|
|
m.preScale(zoomScale, zoomScale);
|
2018-04-26 20:22:42 +00:00
|
|
|
|
|
|
|
const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
|
|
|
|
m.preRotate(fRotation, slideSize.width() * 0.5f, slideSize.height() * 0.5f);
|
2018-04-26 19:55:00 +00:00
|
|
|
|
2018-05-02 19:40:20 +00:00
|
|
|
if (kPerspective_Real == fPerspectiveMode) {
|
|
|
|
SkMatrix persp = this->computePerspectiveMatrix();
|
2018-04-26 20:22:42 +00:00
|
|
|
m.postConcat(persp);
|
2018-04-26 19:55:00 +00:00
|
|
|
}
|
|
|
|
|
2018-03-28 20:23:31 +00:00
|
|
|
return m;
|
|
|
|
}
|
2016-04-08 19:51:45 +00:00
|
|
|
|
2018-03-28 20:23:31 +00:00
|
|
|
SkMatrix Viewer::computeMatrix() {
|
|
|
|
SkMatrix m = fGesture.localM();
|
|
|
|
m.preConcat(fGesture.globalM());
|
|
|
|
m.preConcat(this->computePreTouchMatrix());
|
2016-05-17 19:44:20 +00:00
|
|
|
return m;
|
2016-04-08 19:51:45 +00:00
|
|
|
}
|
|
|
|
|
2017-02-28 20:45:01 +00:00
|
|
|
void Viewer::setBackend(sk_app::Window::BackendType backendType) {
|
2019-05-07 13:55:45 +00:00
|
|
|
fPersistentCache.reset();
|
|
|
|
fCachedGLSL.reset();
|
2017-02-28 20:45:01 +00:00
|
|
|
fBackendType = backendType;
|
|
|
|
|
|
|
|
fWindow->detach();
|
|
|
|
|
2017-11-08 14:54:10 +00:00
|
|
|
#if defined(SK_BUILD_FOR_WIN)
|
2017-08-17 18:37:06 +00:00
|
|
|
// Switching between OpenGL, Vulkan, and ANGLE in the same window is problematic at this point
|
|
|
|
// on Windows, so we just delete the window and recreate it.
|
2017-11-08 14:54:10 +00:00
|
|
|
DisplayParams params = fWindow->getRequestedDisplayParams();
|
|
|
|
delete fWindow;
|
|
|
|
fWindow = Window::CreateNativeWindow(nullptr);
|
|
|
|
|
|
|
|
// re-register callbacks
|
|
|
|
fCommands.attach(fWindow);
|
2017-12-08 21:45:43 +00:00
|
|
|
fWindow->pushLayer(this);
|
2017-12-19 16:15:16 +00:00
|
|
|
fWindow->pushLayer(&fStatsLayer);
|
2017-12-08 21:46:09 +00:00
|
|
|
fWindow->pushLayer(&fImGuiLayer);
|
|
|
|
|
2017-11-08 14:54:10 +00:00
|
|
|
// Don't allow the window to re-attach. If we're in MSAA mode, the params we grabbed above
|
|
|
|
// will still include our correct sample count. But the re-created fWindow will lose that
|
|
|
|
// information. On Windows, we need to re-create the window when changing sample count,
|
|
|
|
// so we'll incorrectly detect that situation, then re-initialize the window in GL mode,
|
|
|
|
// rendering this tear-down step pointless (and causing the Vulkan window context to fail
|
|
|
|
// as if we had never changed windows at all).
|
|
|
|
fWindow->setRequestedDisplayParams(params, false);
|
2017-02-28 20:45:01 +00:00
|
|
|
#endif
|
|
|
|
|
2017-11-08 14:54:10 +00:00
|
|
|
fWindow->attach(backend_type_for_window(fBackendType));
|
2017-02-28 20:45:01 +00:00
|
|
|
}
|
|
|
|
|
2017-03-06 16:47:26 +00:00
|
|
|
void Viewer::setColorMode(ColorMode colorMode) {
|
|
|
|
fColorMode = colorMode;
|
2017-02-08 15:47:28 +00:00
|
|
|
this->updateTitle();
|
|
|
|
fWindow->inval();
|
|
|
|
}
|
|
|
|
|
2018-02-12 21:37:28 +00:00
|
|
|
class OveridePaintFilterCanvas : public SkPaintFilterCanvas {
|
|
|
|
public:
|
2019-01-04 15:11:46 +00:00
|
|
|
OveridePaintFilterCanvas(SkCanvas* canvas, SkPaint* paint, Viewer::SkPaintFields* pfields,
|
|
|
|
SkFont* font, Viewer::SkFontFields* ffields)
|
|
|
|
: SkPaintFilterCanvas(canvas), fPaint(paint), fPaintOverrides(pfields), fFont(font), fFontOverrides(ffields)
|
2018-02-12 21:37:28 +00:00
|
|
|
{ }
|
2018-09-24 17:01:54 +00:00
|
|
|
const SkTextBlob* filterTextBlob(const SkPaint& paint, const SkTextBlob* blob,
|
|
|
|
sk_sp<SkTextBlob>* cache) {
|
|
|
|
bool blobWillChange = false;
|
|
|
|
for (SkTextBlobRunIterator it(blob); !it.done(); it.next()) {
|
2019-01-04 15:11:46 +00:00
|
|
|
SkTCopyOnFirstWrite<SkFont> filteredFont(it.font());
|
|
|
|
bool shouldDraw = this->filterFont(&filteredFont);
|
|
|
|
if (it.font() != *filteredFont || !shouldDraw) {
|
2018-09-24 17:01:54 +00:00
|
|
|
blobWillChange = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!blobWillChange) {
|
|
|
|
return blob;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkTextBlobBuilder builder;
|
|
|
|
for (SkTextBlobRunIterator it(blob); !it.done(); it.next()) {
|
2019-01-04 15:11:46 +00:00
|
|
|
SkTCopyOnFirstWrite<SkFont> filteredFont(it.font());
|
|
|
|
bool shouldDraw = this->filterFont(&filteredFont);
|
2018-09-24 17:01:54 +00:00
|
|
|
if (!shouldDraw) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-01-04 15:11:46 +00:00
|
|
|
SkFont font = *filteredFont;
|
2018-12-05 22:28:14 +00:00
|
|
|
|
2018-09-24 17:01:54 +00:00
|
|
|
const SkTextBlobBuilder::RunBuffer& runBuffer
|
|
|
|
= it.positioning() == SkTextBlobRunIterator::kDefault_Positioning
|
2018-12-05 22:28:14 +00:00
|
|
|
? SkTextBlobBuilderPriv::AllocRunText(&builder, font,
|
2019-03-06 23:19:08 +00:00
|
|
|
it.glyphCount(), it.offset().x(),it.offset().y(), it.textSize(), SkString())
|
2018-09-24 17:01:54 +00:00
|
|
|
: it.positioning() == SkTextBlobRunIterator::kHorizontal_Positioning
|
2018-12-05 22:28:14 +00:00
|
|
|
? SkTextBlobBuilderPriv::AllocRunTextPosH(&builder, font,
|
2019-03-06 23:19:08 +00:00
|
|
|
it.glyphCount(), it.offset().y(), it.textSize(), SkString())
|
2018-09-24 17:01:54 +00:00
|
|
|
: it.positioning() == SkTextBlobRunIterator::kFull_Positioning
|
2018-12-05 22:28:14 +00:00
|
|
|
? SkTextBlobBuilderPriv::AllocRunTextPos(&builder, font,
|
2018-09-24 17:01:54 +00:00
|
|
|
it.glyphCount(), it.textSize(), SkString())
|
|
|
|
: (SkASSERT_RELEASE(false), SkTextBlobBuilder::RunBuffer());
|
|
|
|
uint32_t glyphCount = it.glyphCount();
|
|
|
|
if (it.glyphs()) {
|
|
|
|
size_t glyphSize = sizeof(decltype(*it.glyphs()));
|
|
|
|
memcpy(runBuffer.glyphs, it.glyphs(), glyphCount * glyphSize);
|
|
|
|
}
|
|
|
|
if (it.pos()) {
|
|
|
|
size_t posSize = sizeof(decltype(*it.pos()));
|
|
|
|
uint8_t positioning = it.positioning();
|
|
|
|
memcpy(runBuffer.pos, it.pos(), glyphCount * positioning * posSize);
|
|
|
|
}
|
|
|
|
if (it.text()) {
|
|
|
|
size_t textSize = sizeof(decltype(*it.text()));
|
|
|
|
uint32_t textCount = it.textSize();
|
|
|
|
memcpy(runBuffer.utf8text, it.text(), textCount * textSize);
|
|
|
|
}
|
|
|
|
if (it.clusters()) {
|
|
|
|
size_t clusterSize = sizeof(decltype(*it.clusters()));
|
|
|
|
memcpy(runBuffer.clusters, it.clusters(), glyphCount * clusterSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*cache = builder.make();
|
|
|
|
return cache->get();
|
|
|
|
}
|
|
|
|
void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
|
|
|
|
const SkPaint& paint) override {
|
|
|
|
sk_sp<SkTextBlob> cache;
|
|
|
|
this->SkPaintFilterCanvas::onDrawTextBlob(
|
|
|
|
this->filterTextBlob(paint, blob, &cache), x, y, paint);
|
|
|
|
}
|
2019-01-04 15:11:46 +00:00
|
|
|
bool filterFont(SkTCopyOnFirstWrite<SkFont>* font) const {
|
2019-03-21 17:35:44 +00:00
|
|
|
if (fFontOverrides->fSize) {
|
2019-01-04 15:11:46 +00:00
|
|
|
font->writable()->setSize(fFont->getSize());
|
2018-06-07 21:54:07 +00:00
|
|
|
}
|
2019-03-21 17:35:44 +00:00
|
|
|
if (fFontOverrides->fScaleX) {
|
|
|
|
font->writable()->setScaleX(fFont->getScaleX());
|
|
|
|
}
|
|
|
|
if (fFontOverrides->fSkewX) {
|
|
|
|
font->writable()->setSkewX(fFont->getSkewX());
|
|
|
|
}
|
2019-01-04 15:11:46 +00:00
|
|
|
if (fFontOverrides->fHinting) {
|
|
|
|
font->writable()->setHinting(fFont->getHinting());
|
2018-02-12 21:37:28 +00:00
|
|
|
}
|
2019-01-23 15:34:59 +00:00
|
|
|
if (fFontOverrides->fEdging) {
|
|
|
|
font->writable()->setEdging(fFont->getEdging());
|
2019-01-21 18:51:32 +00:00
|
|
|
}
|
2019-01-23 15:34:59 +00:00
|
|
|
if (fFontOverrides->fEmbolden) {
|
|
|
|
font->writable()->setEmbolden(fFont->isEmbolden());
|
2019-01-21 18:51:32 +00:00
|
|
|
}
|
2019-08-26 20:59:09 +00:00
|
|
|
if (fFontOverrides->fBaselineSnap) {
|
|
|
|
font->writable()->setBaselineSnap(fFont->isBaselineSnap());
|
|
|
|
}
|
2019-01-23 15:34:59 +00:00
|
|
|
if (fFontOverrides->fLinearMetrics) {
|
|
|
|
font->writable()->setLinearMetrics(fFont->isLinearMetrics());
|
2019-01-21 18:51:32 +00:00
|
|
|
}
|
2019-01-23 15:34:59 +00:00
|
|
|
if (fFontOverrides->fSubpixel) {
|
|
|
|
font->writable()->setSubpixel(fFont->isSubpixel());
|
2019-01-21 18:51:32 +00:00
|
|
|
}
|
2019-01-23 15:34:59 +00:00
|
|
|
if (fFontOverrides->fEmbeddedBitmaps) {
|
|
|
|
font->writable()->setEmbeddedBitmaps(fFont->isEmbeddedBitmaps());
|
2019-01-21 18:51:32 +00:00
|
|
|
}
|
2019-01-23 15:34:59 +00:00
|
|
|
if (fFontOverrides->fForceAutoHinting) {
|
|
|
|
font->writable()->setForceAutoHinting(fFont->isForceAutoHinting());
|
2019-01-21 18:51:32 +00:00
|
|
|
}
|
2019-01-23 15:34:59 +00:00
|
|
|
|
2019-01-04 15:11:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
2018-08-27 22:11:57 +00:00
|
|
|
bool onFilter(SkPaint& paint) const override {
|
2019-01-23 15:34:59 +00:00
|
|
|
if (fPaintOverrides->fAntiAlias) {
|
2018-08-27 22:11:57 +00:00
|
|
|
paint.setAntiAlias(fPaint->isAntiAlias());
|
2019-01-04 15:11:46 +00:00
|
|
|
}
|
2019-01-23 15:34:59 +00:00
|
|
|
if (fPaintOverrides->fDither) {
|
2018-08-27 22:11:57 +00:00
|
|
|
paint.setDither(fPaint->isDither());
|
2019-01-04 15:11:46 +00:00
|
|
|
}
|
2019-03-07 18:14:26 +00:00
|
|
|
if (fPaintOverrides->fFilterQuality) {
|
2018-08-27 22:11:57 +00:00
|
|
|
paint.setFilterQuality(fPaint->getFilterQuality());
|
2019-03-07 18:14:26 +00:00
|
|
|
}
|
2018-02-12 21:37:28 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
SkPaint* fPaint;
|
|
|
|
Viewer::SkPaintFields* fPaintOverrides;
|
2019-01-04 15:11:46 +00:00
|
|
|
SkFont* fFont;
|
|
|
|
Viewer::SkFontFields* fFontOverrides;
|
2018-02-12 21:37:28 +00:00
|
|
|
};
|
|
|
|
|
2019-03-04 16:00:10 +00:00
|
|
|
void Viewer::drawSlide(SkSurface* surface) {
|
2019-03-01 19:37:30 +00:00
|
|
|
if (fCurrentSlide < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-04 16:00:10 +00:00
|
|
|
SkAutoCanvasRestore autorestore(surface->getCanvas(), false);
|
2017-02-22 20:21:11 +00:00
|
|
|
|
2017-02-08 15:47:28 +00:00
|
|
|
// By default, we render directly into the window's surface/canvas
|
2019-03-04 16:00:10 +00:00
|
|
|
SkSurface* slideSurface = surface;
|
|
|
|
SkCanvas* slideCanvas = surface->getCanvas();
|
2017-02-13 14:39:57 +00:00
|
|
|
fLastImage.reset();
|
2016-04-08 19:51:45 +00:00
|
|
|
|
2017-03-15 14:24:55 +00:00
|
|
|
// If we're in any of the color managed modes, construct the color space we're going to use
|
2018-11-26 18:55:19 +00:00
|
|
|
sk_sp<SkColorSpace> colorSpace = nullptr;
|
2017-03-15 14:24:55 +00:00
|
|
|
if (ColorMode::kLegacy != fColorMode) {
|
2019-01-04 22:03:00 +00:00
|
|
|
skcms_Matrix3x3 toXYZ;
|
2017-03-15 14:24:55 +00:00
|
|
|
SkAssertResult(fColorSpacePrimaries.toXYZD50(&toXYZ));
|
2018-11-26 18:55:19 +00:00
|
|
|
colorSpace = SkColorSpace::MakeRGB(fColorSpaceTransferFn, toXYZ);
|
2017-03-15 14:24:55 +00:00
|
|
|
}
|
|
|
|
|
2017-12-01 16:23:53 +00:00
|
|
|
if (fSaveToSKP) {
|
|
|
|
SkPictureRecorder recorder;
|
|
|
|
SkCanvas* recorderCanvas = recorder.beginRecording(
|
|
|
|
SkRect::Make(fSlides[fCurrentSlide]->getDimensions()));
|
|
|
|
fSlides[fCurrentSlide]->draw(recorderCanvas);
|
|
|
|
sk_sp<SkPicture> picture(recorder.finishRecordingAsPicture());
|
|
|
|
SkFILEWStream stream("sample_app.skp");
|
|
|
|
picture->serialize(&stream);
|
|
|
|
fSaveToSKP = false;
|
|
|
|
}
|
|
|
|
|
2018-11-26 19:50:05 +00:00
|
|
|
// Grab some things we'll need to make surfaces (for tiling or general offscreen rendering)
|
2019-09-18 15:22:44 +00:00
|
|
|
SkColorType colorType;
|
|
|
|
switch (fColorMode) {
|
|
|
|
case ColorMode::kLegacy:
|
|
|
|
case ColorMode::kColorManaged8888:
|
|
|
|
colorType = kN32_SkColorType;
|
|
|
|
break;
|
|
|
|
case ColorMode::kColorManagedF16:
|
|
|
|
colorType = kRGBA_F16_SkColorType;
|
|
|
|
break;
|
|
|
|
case ColorMode::kColorManagedF16Norm:
|
|
|
|
colorType = kRGBA_F16Norm_SkColorType;
|
|
|
|
break;
|
|
|
|
}
|
2018-11-26 19:50:05 +00:00
|
|
|
|
|
|
|
auto make_surface = [=](int w, int h) {
|
2019-03-04 16:00:10 +00:00
|
|
|
SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
|
|
|
|
slideCanvas->getProps(&props);
|
|
|
|
|
2018-11-26 19:50:05 +00:00
|
|
|
SkImageInfo info = SkImageInfo::Make(w, h, colorType, kPremul_SkAlphaType, colorSpace);
|
|
|
|
return Window::kRaster_BackendType == this->fBackendType
|
|
|
|
? SkSurface::MakeRaster(info, &props)
|
2019-03-04 16:00:10 +00:00
|
|
|
: slideCanvas->makeSurface(info, &props);
|
2018-11-26 19:50:05 +00:00
|
|
|
};
|
|
|
|
|
2018-11-26 18:55:19 +00:00
|
|
|
// We need to render offscreen if we're...
|
|
|
|
// ... in fake perspective or zooming (so we have a snapped copy of the results)
|
|
|
|
// ... in any raster mode, because the window surface is actually GL
|
|
|
|
// ... in any color managed mode, because we always make the window surface with no color space
|
2017-02-08 15:47:28 +00:00
|
|
|
sk_sp<SkSurface> offscreenSurface = nullptr;
|
2018-11-26 18:55:19 +00:00
|
|
|
if (kPerspective_Fake == fPerspectiveMode ||
|
2017-03-06 16:47:26 +00:00
|
|
|
fShowZoomWindow ||
|
2018-11-26 18:55:19 +00:00
|
|
|
Window::kRaster_BackendType == fBackendType ||
|
|
|
|
colorSpace != nullptr) {
|
2017-03-15 14:24:55 +00:00
|
|
|
|
2018-11-26 19:50:05 +00:00
|
|
|
offscreenSurface = make_surface(fWindow->width(), fWindow->height());
|
2019-03-04 16:00:10 +00:00
|
|
|
slideSurface = offscreenSurface.get();
|
2018-07-25 17:28:44 +00:00
|
|
|
slideCanvas = offscreenSurface->getCanvas();
|
2016-06-16 21:10:34 +00:00
|
|
|
}
|
|
|
|
|
2017-02-22 20:21:11 +00:00
|
|
|
int count = slideCanvas->save();
|
2017-02-08 15:47:28 +00:00
|
|
|
slideCanvas->clear(SK_ColorWHITE);
|
2017-02-09 17:10:20 +00:00
|
|
|
// Time the painting logic of the slide
|
2017-12-19 16:15:16 +00:00
|
|
|
fStatsLayer.beginTiming(fPaintTimer);
|
2018-11-26 19:50:05 +00:00
|
|
|
if (fTiled) {
|
|
|
|
int tileW = SkScalarCeilToInt(fWindow->width() * fTileScale.width());
|
|
|
|
int tileH = SkScalarCeilToInt(fWindow->height() * fTileScale.height());
|
|
|
|
sk_sp<SkSurface> tileSurface = make_surface(tileW, tileH);
|
|
|
|
SkCanvas* tileCanvas = tileSurface->getCanvas();
|
|
|
|
SkMatrix m = this->computeMatrix();
|
|
|
|
for (int y = 0; y < fWindow->height(); y += tileH) {
|
|
|
|
for (int x = 0; x < fWindow->width(); x += tileW) {
|
|
|
|
SkAutoCanvasRestore acr(tileCanvas, true);
|
|
|
|
tileCanvas->translate(-x, -y);
|
|
|
|
tileCanvas->clear(SK_ColorTRANSPARENT);
|
|
|
|
tileCanvas->concat(m);
|
2019-01-04 15:11:46 +00:00
|
|
|
OveridePaintFilterCanvas filterCanvas(tileCanvas, &fPaint, &fPaintOverrides,
|
|
|
|
&fFont, &fFontOverrides);
|
2018-11-26 19:50:05 +00:00
|
|
|
fSlides[fCurrentSlide]->draw(&filterCanvas);
|
|
|
|
tileSurface->draw(slideCanvas, x, y, nullptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw borders between tiles
|
|
|
|
if (fDrawTileBoundaries) {
|
|
|
|
SkPaint border;
|
|
|
|
border.setColor(0x60FF00FF);
|
|
|
|
border.setStyle(SkPaint::kStroke_Style);
|
|
|
|
for (int y = 0; y < fWindow->height(); y += tileH) {
|
|
|
|
for (int x = 0; x < fWindow->width(); x += tileW) {
|
|
|
|
slideCanvas->drawRect(SkRect::MakeXYWH(x, y, tileW, tileH), border);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
slideCanvas->concat(this->computeMatrix());
|
|
|
|
if (kPerspective_Real == fPerspectiveMode) {
|
|
|
|
slideCanvas->clipRect(SkRect::MakeWH(fWindow->width(), fWindow->height()));
|
|
|
|
}
|
2019-01-04 15:11:46 +00:00
|
|
|
OveridePaintFilterCanvas filterCanvas(slideCanvas, &fPaint, &fPaintOverrides, &fFont, &fFontOverrides);
|
2018-11-26 19:50:05 +00:00
|
|
|
fSlides[fCurrentSlide]->draw(&filterCanvas);
|
|
|
|
}
|
2017-12-19 16:15:16 +00:00
|
|
|
fStatsLayer.endTiming(fPaintTimer);
|
2017-02-22 20:21:11 +00:00
|
|
|
slideCanvas->restoreToCount(count);
|
2017-02-09 17:10:20 +00:00
|
|
|
|
|
|
|
// Force a flush so we can time that, too
|
2017-12-19 16:15:16 +00:00
|
|
|
fStatsLayer.beginTiming(fFlushTimer);
|
2019-03-04 16:00:10 +00:00
|
|
|
slideSurface->flush();
|
2017-12-19 16:15:16 +00:00
|
|
|
fStatsLayer.endTiming(fFlushTimer);
|
2017-02-08 15:47:28 +00:00
|
|
|
|
|
|
|
// If we rendered offscreen, snap an image and push the results to the window's canvas
|
|
|
|
if (offscreenSurface) {
|
2017-02-13 14:39:57 +00:00
|
|
|
fLastImage = offscreenSurface->makeImageSnapshot();
|
2017-02-08 15:47:28 +00:00
|
|
|
|
2019-03-04 16:00:10 +00:00
|
|
|
SkCanvas* canvas = surface->getCanvas();
|
2017-02-22 20:21:11 +00:00
|
|
|
SkPaint paint;
|
|
|
|
paint.setBlendMode(SkBlendMode::kSrc);
|
2018-05-02 19:40:20 +00:00
|
|
|
int prePerspectiveCount = canvas->save();
|
|
|
|
if (kPerspective_Fake == fPerspectiveMode) {
|
|
|
|
paint.setFilterQuality(kHigh_SkFilterQuality);
|
|
|
|
canvas->clear(SK_ColorWHITE);
|
|
|
|
canvas->concat(this->computePerspectiveMatrix());
|
|
|
|
}
|
2018-11-26 18:55:19 +00:00
|
|
|
canvas->drawImage(fLastImage, 0, 0, &paint);
|
2018-05-02 19:40:20 +00:00
|
|
|
canvas->restoreToCount(prePerspectiveCount);
|
2016-06-16 21:10:34 +00:00
|
|
|
}
|
2019-03-14 15:39:02 +00:00
|
|
|
|
|
|
|
if (fShowSlideDimensions) {
|
|
|
|
SkRect r = SkRect::Make(fSlides[fCurrentSlide]->getDimensions());
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setColor(0x40FFFF00);
|
|
|
|
surface->getCanvas()->drawRect(r, paint);
|
|
|
|
}
|
2016-06-13 19:26:45 +00:00
|
|
|
}
|
|
|
|
|
2017-02-24 20:22:53 +00:00
|
|
|
void Viewer::onBackendCreated() {
|
2018-01-16 21:23:03 +00:00
|
|
|
this->setupCurrentSlide();
|
2017-02-24 20:22:53 +00:00
|
|
|
fWindow->show();
|
|
|
|
}
|
2017-02-14 20:16:46 +00:00
|
|
|
|
2019-03-04 16:00:10 +00:00
|
|
|
void Viewer::onPaint(SkSurface* surface) {
|
|
|
|
this->drawSlide(surface);
|
2016-04-07 18:09:51 +00:00
|
|
|
|
2019-03-04 16:00:10 +00:00
|
|
|
fCommands.drawHelp(surface->getCanvas());
|
2016-07-06 21:11:32 +00:00
|
|
|
|
2017-12-08 21:46:09 +00:00
|
|
|
this->drawImGui();
|
2018-11-01 16:52:34 +00:00
|
|
|
|
|
|
|
if (GrContext* ctx = fWindow->getGrContext()) {
|
|
|
|
// Clean out cache items that haven't been used in more than 10 seconds.
|
|
|
|
ctx->performDeferredCleanup(std::chrono::seconds(10));
|
|
|
|
}
|
2016-04-07 18:09:51 +00:00
|
|
|
}
|
|
|
|
|
2018-08-09 19:06:19 +00:00
|
|
|
void Viewer::onResize(int width, int height) {
|
2018-08-13 14:42:17 +00:00
|
|
|
if (fCurrentSlide >= 0) {
|
|
|
|
fSlides[fCurrentSlide]->resize(width, height);
|
|
|
|
}
|
2018-08-09 19:06:19 +00:00
|
|
|
}
|
|
|
|
|
2018-02-20 02:43:47 +00:00
|
|
|
SkPoint Viewer::mapEvent(float x, float y) {
|
|
|
|
const auto m = this->computeMatrix();
|
|
|
|
SkMatrix inv;
|
|
|
|
|
|
|
|
SkAssertResult(m.invert(&inv));
|
|
|
|
|
|
|
|
return inv.mapXY(x, y);
|
|
|
|
}
|
|
|
|
|
2019-08-29 14:39:22 +00:00
|
|
|
bool Viewer::onTouch(intptr_t owner, skui::InputState state, float x, float y) {
|
2017-06-07 14:00:30 +00:00
|
|
|
if (GestureDevice::kMouse == fGestureDevice) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-02-20 02:43:47 +00:00
|
|
|
|
|
|
|
const auto slidePt = this->mapEvent(x, y);
|
2019-08-29 14:39:22 +00:00
|
|
|
if (fSlides[fCurrentSlide]->onMouse(slidePt.x(), slidePt.y(), state, skui::ModifierKey::kNone)) {
|
2018-02-20 02:43:47 +00:00
|
|
|
fWindow->inval();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-05-17 19:44:20 +00:00
|
|
|
void* castedOwner = reinterpret_cast<void*>(owner);
|
|
|
|
switch (state) {
|
2019-08-29 14:39:22 +00:00
|
|
|
case skui::InputState::kUp: {
|
2016-05-17 19:44:20 +00:00
|
|
|
fGesture.touchEnd(castedOwner);
|
2018-07-23 17:46:01 +00:00
|
|
|
#if defined(SK_BUILD_FOR_IOS)
|
|
|
|
// TODO: move IOS swipe detection higher up into the platform code
|
|
|
|
SkPoint dir;
|
|
|
|
if (fGesture.isFling(&dir)) {
|
|
|
|
// swiping left or right
|
|
|
|
if (SkTAbs(dir.fX) > SkTAbs(dir.fY)) {
|
|
|
|
if (dir.fX < 0) {
|
|
|
|
this->setCurrentSlide(fCurrentSlide < fSlides.count() - 1 ?
|
|
|
|
fCurrentSlide + 1 : 0);
|
|
|
|
} else {
|
|
|
|
this->setCurrentSlide(fCurrentSlide > 0 ?
|
|
|
|
fCurrentSlide - 1 : fSlides.count() - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fGesture.reset();
|
|
|
|
}
|
|
|
|
#endif
|
2016-05-17 19:44:20 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-08-29 14:39:22 +00:00
|
|
|
case skui::InputState::kDown: {
|
Simplify some Viewer code, and fix a few bugs
The content rect was always identical to the window rect,
so most of the related code did nothing. The translation
limit code is always useful (to avoid dragging the slide
way off-screen with the mouse), so always include it.
The auto-scaling to fit the screen is also still useful,
but just base it on the window rect.
The zoom code has four state variables, only used two of
them, and one was a trivially derived computation. Fold
most of that work into computeMatrix. (The translation
was always zero -- we never changed the zoom center.)
Include fDefaultMatrix in the matrix from computeMatrix,
rather than needing to apply it specially to the canvas.
Don't apply the inverse default matrix to touch or mouse
points. The absolute positions of those touch points is
not important, but because that matrix includes scale
(and sometimes very large or very small scale), it just
had the effect of greatly amplifying or damping the drag
speed. Without it, the slide always pans at the speed of
the touch/mouse drag -- which seems more desirable.
The use of the inverse default matrix was a clever trick,
but it caused the translation (applied to the global mtx)
to be scaled, so the slide was always pinned incorrectly.
Instead, supply the unmodified window rect and the default
matrix, so the trans limit code can do the obvious correct
thing: xform the slide bounds completely, then limit the
translation that will be applied after that. Slides are
now correctly pinned to screen edge regardless of how
much zoom is present in the default matrix.
Note: There are still several bugs related to all of this
code, but given the web of xform state, it's hard to
unravel. The touch gesture still doesn't know about
viewer's zoom, so that's ignored when doing the pinning.
Beyond that, it doesn't even know about window resize -
it only configures the translation limit when setting up
a slide. I had a fix for all of this (doing the
translation limiting in computeMatrix), but then the touch
gesture doesn't know about it, and can accumulate drag
motion that needs to be un-dragged to get back on-screen,
even though the slide is never really translated that far.
SkTouchGesture is in include. No one uses it except viewer:
TBR=bsalomon@google.com
Bug: skia:
Change-Id: I460cc07c3de6d36e63826f57d359faf1facf5ab3
Reviewed-on: https://skia-review.googlesource.com/18524
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Yuqian Li <liyuqian@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-06-05 12:46:04 +00:00
|
|
|
fGesture.touchBegin(castedOwner, x, y);
|
2016-05-17 19:44:20 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-08-29 14:39:22 +00:00
|
|
|
case skui::InputState::kMove: {
|
Simplify some Viewer code, and fix a few bugs
The content rect was always identical to the window rect,
so most of the related code did nothing. The translation
limit code is always useful (to avoid dragging the slide
way off-screen with the mouse), so always include it.
The auto-scaling to fit the screen is also still useful,
but just base it on the window rect.
The zoom code has four state variables, only used two of
them, and one was a trivially derived computation. Fold
most of that work into computeMatrix. (The translation
was always zero -- we never changed the zoom center.)
Include fDefaultMatrix in the matrix from computeMatrix,
rather than needing to apply it specially to the canvas.
Don't apply the inverse default matrix to touch or mouse
points. The absolute positions of those touch points is
not important, but because that matrix includes scale
(and sometimes very large or very small scale), it just
had the effect of greatly amplifying or damping the drag
speed. Without it, the slide always pans at the speed of
the touch/mouse drag -- which seems more desirable.
The use of the inverse default matrix was a clever trick,
but it caused the translation (applied to the global mtx)
to be scaled, so the slide was always pinned incorrectly.
Instead, supply the unmodified window rect and the default
matrix, so the trans limit code can do the obvious correct
thing: xform the slide bounds completely, then limit the
translation that will be applied after that. Slides are
now correctly pinned to screen edge regardless of how
much zoom is present in the default matrix.
Note: There are still several bugs related to all of this
code, but given the web of xform state, it's hard to
unravel. The touch gesture still doesn't know about
viewer's zoom, so that's ignored when doing the pinning.
Beyond that, it doesn't even know about window resize -
it only configures the translation limit when setting up
a slide. I had a fix for all of this (doing the
translation limiting in computeMatrix), but then the touch
gesture doesn't know about it, and can accumulate drag
motion that needs to be un-dragged to get back on-screen,
even though the slide is never really translated that far.
SkTouchGesture is in include. No one uses it except viewer:
TBR=bsalomon@google.com
Bug: skia:
Change-Id: I460cc07c3de6d36e63826f57d359faf1facf5ab3
Reviewed-on: https://skia-review.googlesource.com/18524
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Yuqian Li <liyuqian@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-06-05 12:46:04 +00:00
|
|
|
fGesture.touchMoved(castedOwner, x, y);
|
2016-05-17 19:44:20 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-09-09 20:53:39 +00:00
|
|
|
default: {
|
|
|
|
// kLeft and kRight are only for swipes
|
|
|
|
SkASSERT(false);
|
|
|
|
break;
|
|
|
|
}
|
2016-05-17 19:44:20 +00:00
|
|
|
}
|
2017-06-07 14:00:30 +00:00
|
|
|
fGestureDevice = fGesture.isBeingTouched() ? GestureDevice::kTouch : GestureDevice::kNone;
|
2016-05-17 19:44:20 +00:00
|
|
|
fWindow->inval();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-08-29 14:39:22 +00:00
|
|
|
bool Viewer::onMouse(int x, int y, skui::InputState state, skui::ModifierKey modifiers) {
|
2017-12-20 16:58:34 +00:00
|
|
|
if (GestureDevice::kTouch == fGestureDevice) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-02-20 02:43:47 +00:00
|
|
|
const auto slidePt = this->mapEvent(x, y);
|
|
|
|
if (fSlides[fCurrentSlide]->onMouse(slidePt.x(), slidePt.y(), state, modifiers)) {
|
|
|
|
fWindow->inval();
|
|
|
|
return true;
|
2017-12-20 16:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (state) {
|
2019-08-29 14:39:22 +00:00
|
|
|
case skui::InputState::kUp: {
|
2017-12-20 16:58:34 +00:00
|
|
|
fGesture.touchEnd(nullptr);
|
|
|
|
break;
|
2017-12-08 21:46:09 +00:00
|
|
|
}
|
2019-08-29 14:39:22 +00:00
|
|
|
case skui::InputState::kDown: {
|
2017-12-20 16:58:34 +00:00
|
|
|
fGesture.touchBegin(nullptr, x, y);
|
|
|
|
break;
|
|
|
|
}
|
2019-08-29 14:39:22 +00:00
|
|
|
case skui::InputState::kMove: {
|
2017-12-20 16:58:34 +00:00
|
|
|
fGesture.touchMoved(nullptr, x, y);
|
|
|
|
break;
|
2017-05-04 18:00:59 +00:00
|
|
|
}
|
2019-09-09 20:53:39 +00:00
|
|
|
default: {
|
|
|
|
SkASSERT(false); // shouldn't see kRight or kLeft here
|
|
|
|
break;
|
|
|
|
}
|
2017-05-04 18:00:59 +00:00
|
|
|
}
|
2017-12-20 16:58:34 +00:00
|
|
|
fGestureDevice = fGesture.isBeingTouched() ? GestureDevice::kMouse : GestureDevice::kNone;
|
|
|
|
|
2019-08-29 14:39:22 +00:00
|
|
|
if (state != skui::InputState::kMove || fGesture.isBeingTouched()) {
|
2017-12-20 16:58:34 +00:00
|
|
|
fWindow->inval();
|
|
|
|
}
|
2017-05-04 18:00:59 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-09-09 20:53:39 +00:00
|
|
|
bool Viewer::onFling(skui::InputState state) {
|
|
|
|
if (skui::InputState::kRight == state) {
|
|
|
|
this->setCurrentSlide(fCurrentSlide > 0 ? fCurrentSlide - 1 : fSlides.count() - 1);
|
|
|
|
return true;
|
|
|
|
} else if (skui::InputState::kLeft == state) {
|
|
|
|
this->setCurrentSlide(fCurrentSlide < fSlides.count() - 1 ? fCurrentSlide + 1 : 0);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Viewer::onPinch(skui::InputState state, float scale, float x, float y) {
|
|
|
|
switch (state) {
|
|
|
|
case skui::InputState::kDown:
|
|
|
|
fGesture.startZoom();
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
case skui::InputState::kMove:
|
|
|
|
fGesture.updateZoom(scale, x, y, x, y);
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
case skui::InputState::kUp:
|
|
|
|
fGesture.endZoom();
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
SkASSERT(false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-02-24 14:49:14 +00:00
|
|
|
static void ImGui_Primaries(SkColorSpacePrimaries* primaries, SkPaint* gamutPaint) {
|
2019-02-09 21:32:58 +00:00
|
|
|
// The gamut image covers a (0.8 x 0.9) shaped region
|
|
|
|
ImGui::DragCanvas dc(primaries, { 0.0f, 0.9f }, { 0.8f, 0.0f });
|
2017-02-24 14:49:14 +00:00
|
|
|
|
|
|
|
// Background image. Only draw a subset of the image, to avoid the regions less than zero.
|
|
|
|
// Simplifes re-mapping math, clipping behavior, and increases resolution in the useful area.
|
|
|
|
// Magic numbers are pixel locations of the origin and upper-right corner.
|
2019-02-09 21:32:58 +00:00
|
|
|
dc.fDrawList->AddImage(gamutPaint, dc.fPos,
|
|
|
|
ImVec2(dc.fPos.x + dc.fSize.x, dc.fPos.y + dc.fSize.y),
|
|
|
|
ImVec2(242, 61), ImVec2(1897, 1922));
|
|
|
|
|
|
|
|
dc.dragPoint((SkPoint*)(&primaries->fRX), true, 0xFF000040);
|
|
|
|
dc.dragPoint((SkPoint*)(&primaries->fGX), true, 0xFF004000);
|
|
|
|
dc.dragPoint((SkPoint*)(&primaries->fBX), true, 0xFF400000);
|
|
|
|
dc.dragPoint((SkPoint*)(&primaries->fWX), true);
|
|
|
|
dc.fDrawList->AddPolyline(dc.fScreenPoints.begin(), 3, 0xFFFFFFFF, true, 1.5f);
|
2018-04-26 19:55:00 +00:00
|
|
|
}
|
|
|
|
|
2018-06-26 18:23:20 +00:00
|
|
|
static bool ImGui_DragLocation(SkPoint* pt) {
|
2019-02-09 21:32:58 +00:00
|
|
|
ImGui::DragCanvas dc(pt);
|
|
|
|
dc.fillColor(IM_COL32(0, 0, 0, 128));
|
|
|
|
dc.dragPoint(pt);
|
|
|
|
return dc.fDragging;
|
|
|
|
}
|
2018-06-26 18:23:20 +00:00
|
|
|
|
2019-02-09 21:32:58 +00:00
|
|
|
static bool ImGui_DragQuad(SkPoint* pts) {
|
|
|
|
ImGui::DragCanvas dc(pts);
|
|
|
|
dc.fillColor(IM_COL32(0, 0, 0, 128));
|
2018-06-26 18:23:20 +00:00
|
|
|
|
2019-02-09 21:32:58 +00:00
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
|
|
dc.dragPoint(pts + i);
|
|
|
|
}
|
2018-06-26 18:23:20 +00:00
|
|
|
|
2019-02-09 21:32:58 +00:00
|
|
|
dc.fDrawList->AddLine(dc.fScreenPoints[0], dc.fScreenPoints[1], 0xFFFFFFFF);
|
|
|
|
dc.fDrawList->AddLine(dc.fScreenPoints[1], dc.fScreenPoints[3], 0xFFFFFFFF);
|
|
|
|
dc.fDrawList->AddLine(dc.fScreenPoints[3], dc.fScreenPoints[2], 0xFFFFFFFF);
|
|
|
|
dc.fDrawList->AddLine(dc.fScreenPoints[2], dc.fScreenPoints[0], 0xFFFFFFFF);
|
2018-06-26 18:23:20 +00:00
|
|
|
|
2019-02-09 21:32:58 +00:00
|
|
|
return dc.fDragging;
|
2017-02-24 14:49:14 +00:00
|
|
|
}
|
|
|
|
|
2017-12-08 21:46:09 +00:00
|
|
|
void Viewer::drawImGui() {
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
// Support drawing the ImGui demo window. Superfluous, but gives a good idea of what's possible
|
|
|
|
if (fShowImGuiTestWindow) {
|
2018-06-29 18:30:48 +00:00
|
|
|
ImGui::ShowDemoWindow(&fShowImGuiTestWindow);
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (fShowImGuiDebugWindow) {
|
2017-02-24 14:49:14 +00:00
|
|
|
// We have some dynamic content that sizes to fill available size. If the scroll bar isn't
|
|
|
|
// always visible, we can end up in a layout feedback loop.
|
2018-06-29 18:30:48 +00:00
|
|
|
ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
|
2017-03-07 20:16:34 +00:00
|
|
|
DisplayParams params = fWindow->getRequestedDisplayParams();
|
|
|
|
bool paramsChanged = false;
|
2019-04-12 15:47:19 +00:00
|
|
|
const GrContext* ctx = fWindow->getGrContext();
|
|
|
|
|
2017-02-24 14:49:14 +00:00
|
|
|
if (ImGui::Begin("Tools", &fShowImGuiDebugWindow,
|
|
|
|
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
2017-02-28 20:45:01 +00:00
|
|
|
if (ImGui::CollapsingHeader("Backend")) {
|
|
|
|
int newBackend = static_cast<int>(fBackendType);
|
|
|
|
ImGui::RadioButton("Raster", &newBackend, sk_app::Window::kRaster_BackendType);
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::RadioButton("OpenGL", &newBackend, sk_app::Window::kNativeGL_BackendType);
|
2017-08-17 18:37:06 +00:00
|
|
|
#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::RadioButton("ANGLE", &newBackend, sk_app::Window::kANGLE_BackendType);
|
|
|
|
#endif
|
2019-08-02 19:04:52 +00:00
|
|
|
#if defined(SK_DAWN)
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::RadioButton("Dawn", &newBackend, sk_app::Window::kDawn_BackendType);
|
|
|
|
#endif
|
2017-02-28 20:45:01 +00:00
|
|
|
#if defined(SK_VULKAN)
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::RadioButton("Vulkan", &newBackend, sk_app::Window::kVulkan_BackendType);
|
2019-02-08 20:36:14 +00:00
|
|
|
#endif
|
2019-09-03 13:42:57 +00:00
|
|
|
#if defined(SK_METAL)
|
2019-02-08 20:36:14 +00:00
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::RadioButton("Metal", &newBackend, sk_app::Window::kMetal_BackendType);
|
2017-02-28 20:45:01 +00:00
|
|
|
#endif
|
|
|
|
if (newBackend != fBackendType) {
|
|
|
|
fDeferredActions.push_back([=]() {
|
|
|
|
this->setBackend(static_cast<sk_app::Window::BackendType>(newBackend));
|
|
|
|
});
|
|
|
|
}
|
2017-03-01 19:59:05 +00:00
|
|
|
|
2017-05-02 20:15:53 +00:00
|
|
|
bool* wire = ¶ms.fGrContextOptions.fWireframeMode;
|
|
|
|
if (ctx && ImGui::Checkbox("Wireframe Mode", wire)) {
|
|
|
|
paramsChanged = true;
|
|
|
|
}
|
2017-03-07 20:16:34 +00:00
|
|
|
|
2017-03-08 22:10:24 +00:00
|
|
|
if (ctx) {
|
|
|
|
int sampleCount = fWindow->sampleCount();
|
|
|
|
ImGui::Text("MSAA: "); ImGui::SameLine();
|
2018-02-03 01:32:49 +00:00
|
|
|
ImGui::RadioButton("1", &sampleCount, 1); ImGui::SameLine();
|
2017-03-08 22:10:24 +00:00
|
|
|
ImGui::RadioButton("4", &sampleCount, 4); ImGui::SameLine();
|
|
|
|
ImGui::RadioButton("8", &sampleCount, 8); ImGui::SameLine();
|
|
|
|
ImGui::RadioButton("16", &sampleCount, 16);
|
|
|
|
|
|
|
|
if (sampleCount != params.fMSAASampleCount) {
|
|
|
|
params.fMSAASampleCount = sampleCount;
|
|
|
|
paramsChanged = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-13 18:30:23 +00:00
|
|
|
int pixelGeometryIdx = 0;
|
|
|
|
if (fPixelGeometryOverrides) {
|
|
|
|
pixelGeometryIdx = params.fSurfaceProps.pixelGeometry() + 1;
|
|
|
|
}
|
|
|
|
if (ImGui::Combo("Pixel Geometry", &pixelGeometryIdx,
|
|
|
|
"Default\0Flat\0RGB\0BGR\0RGBV\0BGRV\0\0"))
|
|
|
|
{
|
|
|
|
uint32_t flags = params.fSurfaceProps.flags();
|
|
|
|
if (pixelGeometryIdx == 0) {
|
|
|
|
fPixelGeometryOverrides = false;
|
|
|
|
params.fSurfaceProps = SkSurfaceProps(flags, SkSurfaceProps::kLegacyFontHost_InitType);
|
|
|
|
} else {
|
|
|
|
fPixelGeometryOverrides = true;
|
|
|
|
SkPixelGeometry pixelGeometry = SkTo<SkPixelGeometry>(pixelGeometryIdx - 1);
|
|
|
|
params.fSurfaceProps = SkSurfaceProps(flags, pixelGeometry);
|
|
|
|
}
|
|
|
|
paramsChanged = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool useDFT = params.fSurfaceProps.isUseDeviceIndependentFonts();
|
|
|
|
if (ImGui::Checkbox("DFT", &useDFT)) {
|
|
|
|
uint32_t flags = params.fSurfaceProps.flags();
|
|
|
|
if (useDFT) {
|
|
|
|
flags |= SkSurfaceProps::kUseDeviceIndependentFonts_Flag;
|
|
|
|
} else {
|
|
|
|
flags &= ~SkSurfaceProps::kUseDeviceIndependentFonts_Flag;
|
|
|
|
}
|
|
|
|
SkPixelGeometry pixelGeometry = params.fSurfaceProps.pixelGeometry();
|
|
|
|
params.fSurfaceProps = SkSurfaceProps(flags, pixelGeometry);
|
|
|
|
paramsChanged = true;
|
|
|
|
}
|
|
|
|
|
2017-03-01 19:59:05 +00:00
|
|
|
if (ImGui::TreeNode("Path Renderers")) {
|
|
|
|
GpuPathRenderers prevPr = params.fGrContextOptions.fGpuPathRenderers;
|
|
|
|
auto prButton = [&](GpuPathRenderers x) {
|
|
|
|
if (ImGui::RadioButton(gPathRendererNames[x].c_str(), prevPr == x)) {
|
2017-03-07 20:16:34 +00:00
|
|
|
if (x != params.fGrContextOptions.fGpuPathRenderers) {
|
|
|
|
params.fGrContextOptions.fGpuPathRenderers = x;
|
|
|
|
paramsChanged = true;
|
|
|
|
}
|
2017-03-01 19:59:05 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!ctx) {
|
|
|
|
ImGui::RadioButton("Software", true);
|
2018-02-03 01:32:49 +00:00
|
|
|
} else if (fWindow->sampleCount() > 1) {
|
2017-03-01 19:59:05 +00:00
|
|
|
prButton(GpuPathRenderers::kAll);
|
2019-02-04 18:26:26 +00:00
|
|
|
if (ctx->priv().caps()->shaderCaps()->pathRenderingSupport()) {
|
2017-03-01 19:59:05 +00:00
|
|
|
prButton(GpuPathRenderers::kStencilAndCover);
|
|
|
|
}
|
|
|
|
prButton(GpuPathRenderers::kTessellating);
|
|
|
|
prButton(GpuPathRenderers::kNone);
|
|
|
|
} else {
|
|
|
|
prButton(GpuPathRenderers::kAll);
|
2018-05-11 14:14:21 +00:00
|
|
|
if (GrCoverageCountingPathRenderer::IsSupported(
|
2019-02-04 18:26:26 +00:00
|
|
|
*ctx->priv().caps())) {
|
2017-07-14 21:17:41 +00:00
|
|
|
prButton(GpuPathRenderers::kCoverageCounting);
|
|
|
|
}
|
2017-03-16 12:45:39 +00:00
|
|
|
prButton(GpuPathRenderers::kSmall);
|
2017-03-01 19:59:05 +00:00
|
|
|
prButton(GpuPathRenderers::kTessellating);
|
|
|
|
prButton(GpuPathRenderers::kNone);
|
|
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
|
|
}
|
2017-02-28 20:45:01 +00:00
|
|
|
}
|
|
|
|
|
2019-03-08 17:35:06 +00:00
|
|
|
if (ImGui::CollapsingHeader("Tiling")) {
|
|
|
|
ImGui::Checkbox("Enable", &fTiled);
|
|
|
|
ImGui::Checkbox("Draw Boundaries", &fDrawTileBoundaries);
|
|
|
|
ImGui::SliderFloat("Horizontal", &fTileScale.fWidth, 0.1f, 1.0f);
|
|
|
|
ImGui::SliderFloat("Vertical", &fTileScale.fHeight, 0.1f, 1.0f);
|
|
|
|
}
|
|
|
|
|
2018-04-23 16:55:06 +00:00
|
|
|
if (ImGui::CollapsingHeader("Transform")) {
|
|
|
|
float zoom = fZoomLevel;
|
|
|
|
if (ImGui::SliderFloat("Zoom", &zoom, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL)) {
|
|
|
|
fZoomLevel = zoom;
|
|
|
|
this->preTouchMatrixChanged();
|
|
|
|
paramsChanged = true;
|
|
|
|
}
|
|
|
|
float deg = fRotation;
|
2018-05-04 14:33:04 +00:00
|
|
|
if (ImGui::SliderFloat("Rotate", °, -30, 360, "%.3f deg")) {
|
2018-04-23 16:55:06 +00:00
|
|
|
fRotation = deg;
|
|
|
|
this->preTouchMatrixChanged();
|
|
|
|
paramsChanged = true;
|
|
|
|
}
|
2018-06-26 18:23:20 +00:00
|
|
|
if (ImGui::CollapsingHeader("Subpixel offset", ImGuiTreeNodeFlags_NoTreePushOnOpen)) {
|
|
|
|
if (ImGui_DragLocation(&fOffset)) {
|
|
|
|
this->preTouchMatrixChanged();
|
|
|
|
paramsChanged = true;
|
|
|
|
}
|
2018-08-09 19:18:46 +00:00
|
|
|
} else if (fOffset != SkVector{0.5f, 0.5f}) {
|
|
|
|
this->preTouchMatrixChanged();
|
|
|
|
paramsChanged = true;
|
|
|
|
fOffset = {0.5f, 0.5f};
|
2018-06-26 18:23:20 +00:00
|
|
|
}
|
2018-05-02 19:40:20 +00:00
|
|
|
int perspectiveMode = static_cast<int>(fPerspectiveMode);
|
|
|
|
if (ImGui::Combo("Perspective", &perspectiveMode, "Off\0Real\0Fake\0\0")) {
|
|
|
|
fPerspectiveMode = static_cast<PerspectiveMode>(perspectiveMode);
|
2018-04-26 19:55:00 +00:00
|
|
|
this->preTouchMatrixChanged();
|
2018-06-26 18:23:20 +00:00
|
|
|
paramsChanged = true;
|
2018-04-26 19:55:00 +00:00
|
|
|
}
|
2018-06-26 18:23:20 +00:00
|
|
|
if (perspectiveMode != kPerspective_Off && ImGui_DragQuad(fPerspectivePoints)) {
|
2018-04-26 19:55:00 +00:00
|
|
|
this->preTouchMatrixChanged();
|
2018-06-26 18:23:20 +00:00
|
|
|
paramsChanged = true;
|
2018-04-26 19:55:00 +00:00
|
|
|
}
|
2018-04-23 16:55:06 +00:00
|
|
|
}
|
|
|
|
|
2018-04-17 15:16:32 +00:00
|
|
|
if (ImGui::CollapsingHeader("Paint")) {
|
|
|
|
int aliasIdx = 0;
|
2019-01-23 15:34:59 +00:00
|
|
|
if (fPaintOverrides.fAntiAlias) {
|
|
|
|
aliasIdx = SkTo<int>(fPaintOverrides.fAntiAliasState) + 1;
|
2018-04-17 15:16:32 +00:00
|
|
|
}
|
|
|
|
if (ImGui::Combo("Anti-Alias", &aliasIdx,
|
2019-03-22 14:57:16 +00:00
|
|
|
"Default\0Alias\0Normal\0AnalyticAAEnabled\0AnalyticAAForced\0\0"))
|
2018-04-17 15:16:32 +00:00
|
|
|
{
|
|
|
|
gSkUseAnalyticAA = fPaintOverrides.fOriginalSkUseAnalyticAA;
|
|
|
|
gSkForceAnalyticAA = fPaintOverrides.fOriginalSkForceAnalyticAA;
|
|
|
|
if (aliasIdx == 0) {
|
2019-01-23 15:34:59 +00:00
|
|
|
fPaintOverrides.fAntiAliasState = SkPaintFields::AntiAliasState::Alias;
|
|
|
|
fPaintOverrides.fAntiAlias = false;
|
2018-04-17 15:16:32 +00:00
|
|
|
} else {
|
2019-01-23 15:34:59 +00:00
|
|
|
fPaintOverrides.fAntiAlias = true;
|
|
|
|
fPaintOverrides.fAntiAliasState = SkTo<SkPaintFields::AntiAliasState>(aliasIdx-1);
|
2018-04-17 15:16:32 +00:00
|
|
|
fPaint.setAntiAlias(aliasIdx > 1);
|
2019-01-23 15:34:59 +00:00
|
|
|
switch (fPaintOverrides.fAntiAliasState) {
|
2018-04-17 15:16:32 +00:00
|
|
|
case SkPaintFields::AntiAliasState::Alias:
|
|
|
|
break;
|
|
|
|
case SkPaintFields::AntiAliasState::Normal:
|
|
|
|
break;
|
|
|
|
case SkPaintFields::AntiAliasState::AnalyticAAEnabled:
|
|
|
|
gSkUseAnalyticAA = true;
|
|
|
|
gSkForceAnalyticAA = false;
|
|
|
|
break;
|
|
|
|
case SkPaintFields::AntiAliasState::AnalyticAAForced:
|
|
|
|
gSkUseAnalyticAA = gSkForceAnalyticAA = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
paramsChanged = true;
|
|
|
|
}
|
|
|
|
|
2018-05-09 22:23:51 +00:00
|
|
|
auto paintFlag = [this, ¶msChanged](const char* label, const char* items,
|
2019-01-23 15:34:59 +00:00
|
|
|
bool SkPaintFields::* flag,
|
2018-05-09 22:23:51 +00:00
|
|
|
bool (SkPaint::* isFlag)() const,
|
|
|
|
void (SkPaint::* setFlag)(bool) )
|
2018-04-17 15:16:32 +00:00
|
|
|
{
|
2018-05-09 22:23:51 +00:00
|
|
|
int itemIndex = 0;
|
2019-01-23 15:34:59 +00:00
|
|
|
if (fPaintOverrides.*flag) {
|
2018-05-09 22:23:51 +00:00
|
|
|
itemIndex = (fPaint.*isFlag)() ? 2 : 1;
|
2018-04-17 15:16:32 +00:00
|
|
|
}
|
2018-05-09 22:23:51 +00:00
|
|
|
if (ImGui::Combo(label, &itemIndex, items)) {
|
|
|
|
if (itemIndex == 0) {
|
2019-01-23 15:34:59 +00:00
|
|
|
fPaintOverrides.*flag = false;
|
2018-05-09 22:23:51 +00:00
|
|
|
} else {
|
2019-01-23 15:34:59 +00:00
|
|
|
fPaintOverrides.*flag = true;
|
2018-05-09 22:23:51 +00:00
|
|
|
(fPaint.*setFlag)(itemIndex == 2);
|
|
|
|
}
|
|
|
|
paramsChanged = true;
|
2018-04-17 15:16:32 +00:00
|
|
|
}
|
2018-05-09 22:23:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
paintFlag("Dither",
|
|
|
|
"Default\0No Dither\0Dither\0\0",
|
2019-01-23 15:34:59 +00:00
|
|
|
&SkPaintFields::fDither,
|
2018-05-09 22:23:51 +00:00
|
|
|
&SkPaint::isDither, &SkPaint::setDither);
|
2019-03-07 18:14:26 +00:00
|
|
|
|
|
|
|
int filterQualityIdx = 0;
|
|
|
|
if (fPaintOverrides.fFilterQuality) {
|
|
|
|
filterQualityIdx = SkTo<int>(fPaint.getFilterQuality()) + 1;
|
|
|
|
}
|
|
|
|
if (ImGui::Combo("Filter Quality", &filterQualityIdx,
|
|
|
|
"Default\0None\0Low\0Medium\0High\0\0"))
|
|
|
|
{
|
|
|
|
if (filterQualityIdx == 0) {
|
|
|
|
fPaintOverrides.fFilterQuality = false;
|
|
|
|
fPaint.setFilterQuality(kNone_SkFilterQuality);
|
|
|
|
} else {
|
|
|
|
fPaint.setFilterQuality(SkTo<SkFilterQuality>(filterQualityIdx - 1));
|
|
|
|
fPaintOverrides.fFilterQuality = true;
|
|
|
|
}
|
|
|
|
paramsChanged = true;
|
|
|
|
}
|
2019-01-23 15:34:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ImGui::CollapsingHeader("Font")) {
|
|
|
|
int hintingIdx = 0;
|
|
|
|
if (fFontOverrides.fHinting) {
|
|
|
|
hintingIdx = SkTo<int>(fFont.getHinting()) + 1;
|
|
|
|
}
|
|
|
|
if (ImGui::Combo("Hinting", &hintingIdx,
|
|
|
|
"Default\0None\0Slight\0Normal\0Full\0\0"))
|
|
|
|
{
|
|
|
|
if (hintingIdx == 0) {
|
|
|
|
fFontOverrides.fHinting = false;
|
2019-05-07 20:50:29 +00:00
|
|
|
fFont.setHinting(SkFontHinting::kNone);
|
2019-01-23 15:34:59 +00:00
|
|
|
} else {
|
|
|
|
fFont.setHinting(SkTo<SkFontHinting>(hintingIdx - 1));
|
|
|
|
fFontOverrides.fHinting = true;
|
|
|
|
}
|
|
|
|
paramsChanged = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto fontFlag = [this, ¶msChanged](const char* label, const char* items,
|
|
|
|
bool SkFontFields::* flag,
|
|
|
|
bool (SkFont::* isFlag)() const,
|
|
|
|
void (SkFont::* setFlag)(bool) )
|
|
|
|
{
|
|
|
|
int itemIndex = 0;
|
|
|
|
if (fFontOverrides.*flag) {
|
|
|
|
itemIndex = (fFont.*isFlag)() ? 2 : 1;
|
|
|
|
}
|
|
|
|
if (ImGui::Combo(label, &itemIndex, items)) {
|
|
|
|
if (itemIndex == 0) {
|
|
|
|
fFontOverrides.*flag = false;
|
|
|
|
} else {
|
|
|
|
fFontOverrides.*flag = true;
|
|
|
|
(fFont.*setFlag)(itemIndex == 2);
|
|
|
|
}
|
|
|
|
paramsChanged = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
fontFlag("Fake Bold Glyphs",
|
|
|
|
"Default\0No Fake Bold\0Fake Bold\0\0",
|
|
|
|
&SkFontFields::fEmbolden,
|
|
|
|
&SkFont::isEmbolden, &SkFont::setEmbolden);
|
|
|
|
|
2019-08-26 20:59:09 +00:00
|
|
|
fontFlag("Baseline Snapping",
|
|
|
|
"Default\0No Baseline Snapping\0Baseline Snapping\0\0",
|
|
|
|
&SkFontFields::fBaselineSnap,
|
|
|
|
&SkFont::isBaselineSnap, &SkFont::setBaselineSnap);
|
|
|
|
|
2019-01-23 15:34:59 +00:00
|
|
|
fontFlag("Linear Text",
|
|
|
|
"Default\0No Linear Text\0Linear Text\0\0",
|
|
|
|
&SkFontFields::fLinearMetrics,
|
|
|
|
&SkFont::isLinearMetrics, &SkFont::setLinearMetrics);
|
|
|
|
|
|
|
|
fontFlag("Subpixel Position Glyphs",
|
|
|
|
"Default\0Pixel Text\0Subpixel Text\0\0",
|
|
|
|
&SkFontFields::fSubpixel,
|
|
|
|
&SkFont::isSubpixel, &SkFont::setSubpixel);
|
|
|
|
|
|
|
|
fontFlag("Embedded Bitmap Text",
|
|
|
|
"Default\0No Embedded Bitmaps\0Embedded Bitmaps\0\0",
|
|
|
|
&SkFontFields::fEmbeddedBitmaps,
|
|
|
|
&SkFont::isEmbeddedBitmaps, &SkFont::setEmbeddedBitmaps);
|
|
|
|
|
|
|
|
fontFlag("Force Auto-Hinting",
|
|
|
|
"Default\0No Force Auto-Hinting\0Force Auto-Hinting\0\0",
|
|
|
|
&SkFontFields::fForceAutoHinting,
|
|
|
|
&SkFont::isForceAutoHinting, &SkFont::setForceAutoHinting);
|
|
|
|
|
|
|
|
int edgingIdx = 0;
|
|
|
|
if (fFontOverrides.fEdging) {
|
|
|
|
edgingIdx = SkTo<int>(fFont.getEdging()) + 1;
|
|
|
|
}
|
|
|
|
if (ImGui::Combo("Edging", &edgingIdx,
|
|
|
|
"Default\0Alias\0Antialias\0Subpixel Antialias\0\0"))
|
|
|
|
{
|
|
|
|
if (edgingIdx == 0) {
|
|
|
|
fFontOverrides.fEdging = false;
|
|
|
|
fFont.setEdging(SkFont::Edging::kAlias);
|
|
|
|
} else {
|
|
|
|
fFont.setEdging(SkTo<SkFont::Edging>(edgingIdx-1));
|
|
|
|
fFontOverrides.fEdging = true;
|
|
|
|
}
|
|
|
|
paramsChanged = true;
|
|
|
|
}
|
|
|
|
|
2019-03-21 17:35:44 +00:00
|
|
|
ImGui::Checkbox("Override Size", &fFontOverrides.fSize);
|
|
|
|
if (fFontOverrides.fSize) {
|
|
|
|
ImGui::DragFloat2("TextRange", fFontOverrides.fSizeRange,
|
2018-06-07 21:54:07 +00:00
|
|
|
0.001f, -10.0f, 300.0f, "%.6f", 2.0f);
|
2019-01-04 15:11:46 +00:00
|
|
|
float textSize = fFont.getSize();
|
2018-06-07 21:54:07 +00:00
|
|
|
if (ImGui::DragFloat("TextSize", &textSize, 0.001f,
|
2019-03-21 17:35:44 +00:00
|
|
|
fFontOverrides.fSizeRange[0],
|
|
|
|
fFontOverrides.fSizeRange[1],
|
2018-06-07 21:54:07 +00:00
|
|
|
"%.6f", 2.0f))
|
|
|
|
{
|
2019-01-04 15:11:46 +00:00
|
|
|
fFont.setSize(textSize);
|
2019-03-21 17:35:44 +00:00
|
|
|
paramsChanged = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::Checkbox("Override ScaleX", &fFontOverrides.fScaleX);
|
|
|
|
if (fFontOverrides.fScaleX) {
|
|
|
|
float scaleX = fFont.getScaleX();
|
|
|
|
if (ImGui::SliderFloat("ScaleX", &scaleX, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL)) {
|
|
|
|
fFont.setScaleX(scaleX);
|
|
|
|
paramsChanged = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::Checkbox("Override SkewX", &fFontOverrides.fSkewX);
|
|
|
|
if (fFontOverrides.fSkewX) {
|
|
|
|
float skewX = fFont.getSkewX();
|
|
|
|
if (ImGui::SliderFloat("SkewX", &skewX, MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL)) {
|
|
|
|
fFont.setSkewX(skewX);
|
2018-06-07 21:54:07 +00:00
|
|
|
paramsChanged = true;
|
|
|
|
}
|
|
|
|
}
|
2018-04-17 15:16:32 +00:00
|
|
|
}
|
|
|
|
|
2018-05-15 14:09:52 +00:00
|
|
|
{
|
|
|
|
SkMetaData controls;
|
|
|
|
if (fSlides[fCurrentSlide]->onGetControls(&controls)) {
|
|
|
|
if (ImGui::CollapsingHeader("Current Slide")) {
|
|
|
|
SkMetaData::Iter iter(controls);
|
|
|
|
const char* name;
|
|
|
|
SkMetaData::Type type;
|
|
|
|
int count;
|
2018-08-03 15:14:02 +00:00
|
|
|
while ((name = iter.next(&type, &count)) != nullptr) {
|
2018-05-15 14:09:52 +00:00
|
|
|
if (type == SkMetaData::kScalar_Type) {
|
|
|
|
float val[3];
|
|
|
|
SkASSERT(count == 3);
|
|
|
|
controls.findScalars(name, &count, val);
|
|
|
|
if (ImGui::SliderFloat(name, &val[0], val[1], val[2])) {
|
|
|
|
controls.setScalars(name, 3, val);
|
|
|
|
}
|
2019-03-22 21:03:59 +00:00
|
|
|
} else if (type == SkMetaData::kBool_Type) {
|
|
|
|
bool val;
|
|
|
|
SkASSERT(count == 1);
|
|
|
|
controls.findBool(name, &val);
|
|
|
|
if (ImGui::Checkbox(name, &val)) {
|
|
|
|
controls.setBool(name, val);
|
|
|
|
}
|
2018-05-15 14:09:52 +00:00
|
|
|
}
|
|
|
|
}
|
2018-08-03 15:14:02 +00:00
|
|
|
fSlides[fCurrentSlide]->onSetControls(controls);
|
2018-05-15 14:09:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-23 14:01:07 +00:00
|
|
|
if (fShowSlidePicker) {
|
|
|
|
ImGui::SetNextTreeNodeOpen(true);
|
|
|
|
}
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
if (ImGui::CollapsingHeader("Slide")) {
|
|
|
|
static ImGuiTextFilter filter;
|
2017-11-08 18:11:36 +00:00
|
|
|
static ImVector<const char*> filteredSlideNames;
|
|
|
|
static ImVector<int> filteredSlideIndices;
|
|
|
|
|
2017-11-14 20:32:20 +00:00
|
|
|
if (fShowSlidePicker) {
|
|
|
|
ImGui::SetKeyboardFocusHere();
|
|
|
|
fShowSlidePicker = false;
|
|
|
|
}
|
|
|
|
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
filter.Draw();
|
2017-11-08 18:11:36 +00:00
|
|
|
filteredSlideNames.clear();
|
|
|
|
filteredSlideIndices.clear();
|
|
|
|
int filteredIndex = 0;
|
|
|
|
for (int i = 0; i < fSlides.count(); ++i) {
|
|
|
|
const char* slideName = fSlides[i]->getName().c_str();
|
|
|
|
if (filter.PassFilter(slideName) || i == fCurrentSlide) {
|
|
|
|
if (i == fCurrentSlide) {
|
|
|
|
filteredIndex = filteredSlideIndices.size();
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
}
|
2017-11-08 18:11:36 +00:00
|
|
|
filteredSlideNames.push_back(slideName);
|
|
|
|
filteredSlideIndices.push_back(i);
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
}
|
|
|
|
}
|
2017-11-08 18:11:36 +00:00
|
|
|
|
|
|
|
if (ImGui::ListBox("", &filteredIndex, filteredSlideNames.begin(),
|
|
|
|
filteredSlideNames.size(), 20)) {
|
2018-01-16 21:23:03 +00:00
|
|
|
this->setCurrentSlide(filteredSlideIndices[filteredIndex]);
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
}
|
|
|
|
}
|
2017-02-24 14:49:14 +00:00
|
|
|
|
|
|
|
if (ImGui::CollapsingHeader("Color Mode")) {
|
2017-03-06 16:47:26 +00:00
|
|
|
ColorMode newMode = fColorMode;
|
|
|
|
auto cmButton = [&](ColorMode mode, const char* label) {
|
|
|
|
if (ImGui::RadioButton(label, mode == fColorMode)) {
|
|
|
|
newMode = mode;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
cmButton(ColorMode::kLegacy, "Legacy 8888");
|
2018-11-26 18:55:19 +00:00
|
|
|
cmButton(ColorMode::kColorManaged8888, "Color Managed 8888");
|
|
|
|
cmButton(ColorMode::kColorManagedF16, "Color Managed F16");
|
2019-09-18 15:22:44 +00:00
|
|
|
cmButton(ColorMode::kColorManagedF16Norm, "Color Managed F16 Norm");
|
2017-03-06 16:47:26 +00:00
|
|
|
|
|
|
|
if (newMode != fColorMode) {
|
2018-11-26 18:55:19 +00:00
|
|
|
this->setColorMode(newMode);
|
2017-02-24 14:49:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Pick from common gamuts:
|
|
|
|
int primariesIdx = 4; // Default: Custom
|
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(gNamedPrimaries); ++i) {
|
|
|
|
if (primaries_equal(*gNamedPrimaries[i].fPrimaries, fColorSpacePrimaries)) {
|
|
|
|
primariesIdx = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-26 18:55:19 +00:00
|
|
|
// Let user adjust the gamma
|
2019-01-04 22:03:00 +00:00
|
|
|
ImGui::SliderFloat("Gamma", &fColorSpaceTransferFn.g, 0.5f, 3.5f);
|
2017-11-09 15:27:55 +00:00
|
|
|
|
2017-02-24 14:49:14 +00:00
|
|
|
if (ImGui::Combo("Primaries", &primariesIdx,
|
|
|
|
"sRGB\0AdobeRGB\0P3\0Rec. 2020\0Custom\0\0")) {
|
|
|
|
if (primariesIdx >= 0 && primariesIdx <= 3) {
|
|
|
|
fColorSpacePrimaries = *gNamedPrimaries[primariesIdx].fPrimaries;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allow direct editing of gamut
|
|
|
|
ImGui_Primaries(&fColorSpacePrimaries, &fImGuiGamutPaint);
|
|
|
|
}
|
2019-01-10 14:40:58 +00:00
|
|
|
|
|
|
|
if (ImGui::CollapsingHeader("Animation")) {
|
2019-07-11 20:32:53 +00:00
|
|
|
bool isPaused = AnimTimer::kPaused_State == fAnimTimer.state();
|
2019-01-10 14:40:58 +00:00
|
|
|
if (ImGui::Checkbox("Pause", &isPaused)) {
|
|
|
|
fAnimTimer.togglePauseResume();
|
|
|
|
}
|
2019-01-10 16:27:34 +00:00
|
|
|
|
|
|
|
float speed = fAnimTimer.getSpeed();
|
|
|
|
if (ImGui::DragFloat("Speed", &speed, 0.1f)) {
|
|
|
|
fAnimTimer.setSpeed(speed);
|
|
|
|
}
|
2019-01-10 14:40:58 +00:00
|
|
|
}
|
2019-04-12 15:47:19 +00:00
|
|
|
|
2019-04-25 15:34:07 +00:00
|
|
|
bool backendIsGL = Window::kNativeGL_BackendType == fBackendType
|
|
|
|
#if SK_ANGLE && defined(SK_BUILD_FOR_WIN)
|
|
|
|
|| Window::kANGLE_BackendType == fBackendType
|
|
|
|
#endif
|
|
|
|
;
|
|
|
|
|
|
|
|
// HACK: If we get here when SKSL caching isn't enabled, and we're on a backend other
|
|
|
|
// than GL, we need to force it on. Just do that on the first frame after the backend
|
|
|
|
// switch, then resume normal operation.
|
2019-09-03 18:59:26 +00:00
|
|
|
if (!backendIsGL &&
|
|
|
|
params.fGrContextOptions.fShaderCacheStrategy !=
|
|
|
|
GrContextOptions::ShaderCacheStrategy::kSkSL) {
|
|
|
|
params.fGrContextOptions.fShaderCacheStrategy =
|
|
|
|
GrContextOptions::ShaderCacheStrategy::kSkSL;
|
2019-04-25 15:34:07 +00:00
|
|
|
paramsChanged = true;
|
|
|
|
fPersistentCache.reset();
|
|
|
|
} else if (ImGui::CollapsingHeader("Shaders")) {
|
2019-04-12 15:47:19 +00:00
|
|
|
// To re-load shaders from the currently active programs, we flush all caches on one
|
|
|
|
// frame, then set a flag to poll the cache on the next frame.
|
|
|
|
static bool gLoadPending = false;
|
|
|
|
if (gLoadPending) {
|
|
|
|
auto collectShaders = [this](sk_sp<const SkData> key, sk_sp<SkData> data,
|
|
|
|
int hitCount) {
|
|
|
|
CachedGLSL& entry(fCachedGLSL.push_back());
|
|
|
|
entry.fKey = key;
|
|
|
|
SkMD5 hash;
|
|
|
|
hash.write(key->bytes(), key->size());
|
|
|
|
SkMD5::Digest digest = hash.finish();
|
|
|
|
for (int i = 0; i < 16; ++i) {
|
|
|
|
entry.fKeyString.appendf("%02x", digest.data[i]);
|
|
|
|
}
|
|
|
|
|
2019-09-03 18:59:26 +00:00
|
|
|
SkReader32 reader(data->data(), data->size());
|
|
|
|
entry.fShaderType = reader.readU32();
|
|
|
|
GrPersistentCacheUtils::UnpackCachedShaders(&reader, entry.fShader,
|
|
|
|
entry.fInputs,
|
|
|
|
kGrShaderTypeCount);
|
2019-04-12 15:47:19 +00:00
|
|
|
};
|
|
|
|
fCachedGLSL.reset();
|
|
|
|
fPersistentCache.foreach(collectShaders);
|
|
|
|
gLoadPending = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Defer actually doing the load/save logic so that we can trigger a save when we
|
|
|
|
// start or finish hovering on a tree node in the list below:
|
|
|
|
bool doLoad = ImGui::Button("Load"); ImGui::SameLine();
|
2019-04-25 15:34:07 +00:00
|
|
|
bool doSave = ImGui::Button("Save");
|
|
|
|
if (backendIsGL) {
|
|
|
|
ImGui::SameLine();
|
2019-09-03 18:59:26 +00:00
|
|
|
bool sksl = params.fGrContextOptions.fShaderCacheStrategy ==
|
|
|
|
GrContextOptions::ShaderCacheStrategy::kSkSL;
|
|
|
|
if (ImGui::Checkbox("SkSL", &sksl)) {
|
|
|
|
params.fGrContextOptions.fShaderCacheStrategy = sksl
|
|
|
|
? GrContextOptions::ShaderCacheStrategy::kSkSL
|
|
|
|
: GrContextOptions::ShaderCacheStrategy::kBackendSource;
|
2019-04-25 15:34:07 +00:00
|
|
|
paramsChanged = true;
|
|
|
|
doLoad = true;
|
|
|
|
fDeferredActions.push_back([=]() { fPersistentCache.reset(); });
|
|
|
|
}
|
2019-04-19 18:16:19 +00:00
|
|
|
}
|
2019-04-12 15:47:19 +00:00
|
|
|
|
|
|
|
ImGui::BeginChild("##ScrollingRegion");
|
|
|
|
for (auto& entry : fCachedGLSL) {
|
|
|
|
bool inTreeNode = ImGui::TreeNode(entry.fKeyString.c_str());
|
|
|
|
bool hovered = ImGui::IsItemHovered();
|
|
|
|
if (hovered != entry.fHovered) {
|
|
|
|
// Force a save to patch the highlight shader in/out
|
|
|
|
entry.fHovered = hovered;
|
|
|
|
doSave = true;
|
|
|
|
}
|
|
|
|
if (inTreeNode) {
|
|
|
|
// Full width, and a reasonable amount of space for each shader.
|
|
|
|
ImVec2 boxSize(-1.0f, ImGui::GetTextLineHeight() * 20.0f);
|
|
|
|
ImGui::InputTextMultiline("##VP", &entry.fShader[kVertex_GrShaderType],
|
|
|
|
boxSize);
|
|
|
|
ImGui::InputTextMultiline("##FP", &entry.fShader[kFragment_GrShaderType],
|
|
|
|
boxSize);
|
|
|
|
ImGui::TreePop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::EndChild();
|
|
|
|
|
|
|
|
if (doLoad) {
|
|
|
|
fPersistentCache.reset();
|
|
|
|
fWindow->getGrContext()->priv().getGpu()->resetShaderCacheForTesting();
|
|
|
|
gLoadPending = true;
|
|
|
|
}
|
|
|
|
if (doSave) {
|
|
|
|
// The hovered item (if any) gets a special shader to make it identifiable
|
2019-05-07 13:55:45 +00:00
|
|
|
auto shaderCaps = ctx->priv().caps()->shaderCaps();
|
2019-09-03 18:59:26 +00:00
|
|
|
bool sksl = params.fGrContextOptions.fShaderCacheStrategy ==
|
|
|
|
GrContextOptions::ShaderCacheStrategy::kSkSL;
|
2019-05-07 13:55:45 +00:00
|
|
|
|
2019-06-12 15:35:41 +00:00
|
|
|
SkSL::String highlight;
|
|
|
|
if (!sksl) {
|
|
|
|
highlight = shaderCaps->versionDeclString();
|
|
|
|
if (shaderCaps->usesPrecisionModifiers()) {
|
|
|
|
highlight.append("precision mediump float;\n");
|
|
|
|
}
|
2019-05-07 13:55:45 +00:00
|
|
|
}
|
|
|
|
const char* f4Type = sksl ? "half4" : "vec4";
|
2019-04-19 18:16:19 +00:00
|
|
|
highlight.appendf("out %s sk_FragColor;\n"
|
|
|
|
"void main() { sk_FragColor = %s(1, 0, 1, 0.5); }",
|
|
|
|
f4Type, f4Type);
|
2019-04-12 15:47:19 +00:00
|
|
|
|
|
|
|
fPersistentCache.reset();
|
|
|
|
fWindow->getGrContext()->priv().getGpu()->resetShaderCacheForTesting();
|
|
|
|
for (auto& entry : fCachedGLSL) {
|
|
|
|
SkSL::String backup = entry.fShader[kFragment_GrShaderType];
|
|
|
|
if (entry.fHovered) {
|
|
|
|
entry.fShader[kFragment_GrShaderType] = highlight;
|
|
|
|
}
|
|
|
|
|
2019-04-25 13:44:43 +00:00
|
|
|
auto data = GrPersistentCacheUtils::PackCachedShaders(entry.fShaderType,
|
|
|
|
entry.fShader,
|
|
|
|
entry.fInputs,
|
2019-09-24 20:03:41 +00:00
|
|
|
kGrShaderTypeCount);
|
2019-04-12 15:47:19 +00:00
|
|
|
fPersistentCache.store(*entry.fKey, *data);
|
|
|
|
|
|
|
|
entry.fShader[kFragment_GrShaderType] = backup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
}
|
2017-03-07 20:16:34 +00:00
|
|
|
if (paramsChanged) {
|
|
|
|
fDeferredActions.push_back([=]() {
|
|
|
|
fWindow->setRequestedDisplayParams(params);
|
|
|
|
fWindow->inval();
|
|
|
|
this->updateTitle();
|
|
|
|
});
|
|
|
|
}
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
ImGui::End();
|
|
|
|
}
|
|
|
|
|
2019-05-03 17:13:35 +00:00
|
|
|
if (gShaderErrorHandler.fErrors.count()) {
|
|
|
|
ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
|
|
|
|
ImGui::Begin("Shader Errors");
|
|
|
|
for (int i = 0; i < gShaderErrorHandler.fErrors.count(); ++i) {
|
|
|
|
ImGui::TextWrapped("%s", gShaderErrorHandler.fErrors[i].c_str());
|
|
|
|
ImGui::TextWrapped("%s", gShaderErrorHandler.fShaders[i].c_str());
|
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
gShaderErrorHandler.reset();
|
|
|
|
}
|
|
|
|
|
2017-02-13 14:39:57 +00:00
|
|
|
if (fShowZoomWindow && fLastImage) {
|
2018-06-29 18:30:48 +00:00
|
|
|
ImGui::SetNextWindowSize(ImVec2(200, 200), ImGuiCond_FirstUseEver);
|
|
|
|
if (ImGui::Begin("Zoom", &fShowZoomWindow)) {
|
2017-11-13 20:36:36 +00:00
|
|
|
static int zoomFactor = 8;
|
|
|
|
if (ImGui::Button("<<")) {
|
|
|
|
zoomFactor = SkTMax(zoomFactor / 2, 4);
|
|
|
|
}
|
|
|
|
ImGui::SameLine(); ImGui::Text("%2d", zoomFactor); ImGui::SameLine();
|
|
|
|
if (ImGui::Button(">>")) {
|
|
|
|
zoomFactor = SkTMin(zoomFactor * 2, 32);
|
|
|
|
}
|
2017-02-13 14:39:57 +00:00
|
|
|
|
2018-06-26 18:23:20 +00:00
|
|
|
if (!fZoomWindowFixed) {
|
|
|
|
ImVec2 mousePos = ImGui::GetMousePos();
|
|
|
|
fZoomWindowLocation = SkPoint::Make(mousePos.x, mousePos.y);
|
|
|
|
}
|
|
|
|
SkScalar x = fZoomWindowLocation.x();
|
|
|
|
SkScalar y = fZoomWindowLocation.y();
|
|
|
|
int xInt = SkScalarRoundToInt(x);
|
|
|
|
int yInt = SkScalarRoundToInt(y);
|
2017-02-13 14:39:57 +00:00
|
|
|
ImVec2 avail = ImGui::GetContentRegionAvail();
|
|
|
|
|
2017-11-13 20:36:36 +00:00
|
|
|
uint32_t pixel = 0;
|
|
|
|
SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
|
2018-06-26 18:23:20 +00:00
|
|
|
if (fLastImage->readPixels(info, &pixel, info.minRowBytes(), xInt, yInt)) {
|
2017-11-13 20:36:36 +00:00
|
|
|
ImGui::SameLine();
|
2019-02-20 15:13:06 +00:00
|
|
|
ImGui::Text("(X, Y): %d, %d RGBA: %X %X %X %X",
|
2018-06-26 18:23:20 +00:00
|
|
|
xInt, yInt,
|
2017-11-21 19:59:31 +00:00
|
|
|
SkGetPackedR32(pixel), SkGetPackedG32(pixel),
|
2017-11-13 20:36:36 +00:00
|
|
|
SkGetPackedB32(pixel), SkGetPackedA32(pixel));
|
|
|
|
}
|
|
|
|
|
2017-12-08 21:46:09 +00:00
|
|
|
fImGuiLayer.skiaWidget(avail, [=](SkCanvas* c) {
|
2017-11-13 20:36:36 +00:00
|
|
|
// Translate so the region of the image that's under the mouse cursor is centered
|
|
|
|
// in the zoom canvas:
|
|
|
|
c->scale(zoomFactor, zoomFactor);
|
2018-06-26 18:23:20 +00:00
|
|
|
c->translate(avail.x * 0.5f / zoomFactor - x - 0.5f,
|
|
|
|
avail.y * 0.5f / zoomFactor - y - 0.5f);
|
2017-11-13 20:36:36 +00:00
|
|
|
c->drawImage(this->fLastImage, 0, 0);
|
|
|
|
|
|
|
|
SkPaint outline;
|
|
|
|
outline.setStyle(SkPaint::kStroke_Style);
|
2018-06-26 18:23:20 +00:00
|
|
|
c->drawRect(SkRect::MakeXYWH(x, y, 1, 1), outline);
|
2017-11-13 20:36:36 +00:00
|
|
|
});
|
2017-02-13 14:39:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::End();
|
|
|
|
}
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
}
|
|
|
|
|
2016-07-06 21:11:32 +00:00
|
|
|
void Viewer::onIdle() {
|
2017-02-24 16:57:23 +00:00
|
|
|
for (int i = 0; i < fDeferredActions.count(); ++i) {
|
|
|
|
fDeferredActions[i]();
|
|
|
|
}
|
|
|
|
fDeferredActions.reset();
|
|
|
|
|
2017-12-19 16:15:16 +00:00
|
|
|
fStatsLayer.beginTiming(fAnimateTimer);
|
2016-04-08 19:51:45 +00:00
|
|
|
fAnimTimer.updateTime();
|
2019-07-11 20:32:53 +00:00
|
|
|
bool animateWantsInval = fSlides[fCurrentSlide]->animate(fAnimTimer.nanos());
|
2017-12-19 16:15:16 +00:00
|
|
|
fStatsLayer.endTiming(fAnimateTimer);
|
2017-02-09 17:10:20 +00:00
|
|
|
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
2018-08-03 17:03:19 +00:00
|
|
|
// ImGui always has at least one "active" window, which is the default "Debug" window. It may
|
|
|
|
// not be visible, though. So we need to redraw if there is at least one visible window, or
|
|
|
|
// more than one active window. Newly created windows are active but not visible for one frame
|
|
|
|
// while they determine their layout and sizing.
|
|
|
|
if (animateWantsInval || fStatsLayer.getActive() || fRefresh ||
|
|
|
|
io.MetricsActiveWindows > 1 || io.MetricsRenderWindows > 0) {
|
2016-04-08 19:51:45 +00:00
|
|
|
fWindow->inval();
|
|
|
|
}
|
2016-04-06 13:08:59 +00:00
|
|
|
}
|
2016-05-27 15:52:52 +00:00
|
|
|
|
2018-06-19 01:23:06 +00:00
|
|
|
template <typename OptionsFunc>
|
|
|
|
static void WriteStateObject(SkJSONWriter& writer, const char* name, const char* value,
|
|
|
|
OptionsFunc&& optionsFunc) {
|
|
|
|
writer.beginObject();
|
|
|
|
{
|
|
|
|
writer.appendString(kName , name);
|
|
|
|
writer.appendString(kValue, value);
|
|
|
|
|
|
|
|
writer.beginArray(kOptions);
|
|
|
|
{
|
|
|
|
optionsFunc(writer);
|
|
|
|
}
|
|
|
|
writer.endArray();
|
|
|
|
}
|
|
|
|
writer.endObject();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-05-27 15:52:52 +00:00
|
|
|
void Viewer::updateUIState() {
|
2017-02-24 23:04:47 +00:00
|
|
|
if (!fWindow) {
|
|
|
|
return;
|
|
|
|
}
|
2018-02-03 01:32:49 +00:00
|
|
|
if (fWindow->sampleCount() < 1) {
|
2017-02-24 23:04:47 +00:00
|
|
|
return; // Surface hasn't been created yet.
|
|
|
|
}
|
|
|
|
|
2018-06-19 01:23:06 +00:00
|
|
|
SkDynamicMemoryWStream memStream;
|
|
|
|
SkJSONWriter writer(&memStream);
|
|
|
|
writer.beginArray();
|
|
|
|
|
2016-06-03 15:47:23 +00:00
|
|
|
// Slide state
|
2018-06-19 01:23:06 +00:00
|
|
|
WriteStateObject(writer, kSlideStateName, fSlides[fCurrentSlide]->getName().c_str(),
|
|
|
|
[this](SkJSONWriter& writer) {
|
|
|
|
for(const auto& slide : fSlides) {
|
|
|
|
writer.appendString(slide->getName().c_str());
|
|
|
|
}
|
|
|
|
});
|
2016-05-27 15:52:52 +00:00
|
|
|
|
2016-06-03 15:47:23 +00:00
|
|
|
// Backend state
|
2018-06-19 01:23:06 +00:00
|
|
|
WriteStateObject(writer, kBackendStateName, kBackendTypeStrings[fBackendType],
|
|
|
|
[](SkJSONWriter& writer) {
|
|
|
|
for (const auto& str : kBackendTypeStrings) {
|
|
|
|
writer.appendString(str);
|
|
|
|
}
|
|
|
|
});
|
2016-05-27 15:52:52 +00:00
|
|
|
|
2017-02-24 23:04:47 +00:00
|
|
|
// MSAA state
|
2018-06-19 01:23:06 +00:00
|
|
|
const auto countString = SkStringPrintf("%d", fWindow->sampleCount());
|
|
|
|
WriteStateObject(writer, kMSAAStateName, countString.c_str(),
|
|
|
|
[this](SkJSONWriter& writer) {
|
|
|
|
writer.appendS32(0);
|
|
|
|
|
|
|
|
if (sk_app::Window::kRaster_BackendType == fBackendType) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int msaa : {4, 8, 16}) {
|
|
|
|
writer.appendS32(msaa);
|
|
|
|
}
|
|
|
|
});
|
2017-02-24 23:04:47 +00:00
|
|
|
|
2017-02-28 00:00:53 +00:00
|
|
|
// Path renderer state
|
|
|
|
GpuPathRenderers pr = fWindow->getRequestedDisplayParams().fGrContextOptions.fGpuPathRenderers;
|
2018-06-19 01:23:06 +00:00
|
|
|
WriteStateObject(writer, kPathRendererStateName, gPathRendererNames[pr].c_str(),
|
|
|
|
[this](SkJSONWriter& writer) {
|
|
|
|
const GrContext* ctx = fWindow->getGrContext();
|
|
|
|
if (!ctx) {
|
|
|
|
writer.appendString("Software");
|
|
|
|
} else {
|
2019-02-04 18:26:26 +00:00
|
|
|
const auto* caps = ctx->priv().caps();
|
2018-06-19 01:23:06 +00:00
|
|
|
|
|
|
|
writer.appendString(gPathRendererNames[GpuPathRenderers::kAll].c_str());
|
|
|
|
if (fWindow->sampleCount() > 1) {
|
|
|
|
if (caps->shaderCaps()->pathRenderingSupport()) {
|
|
|
|
writer.appendString(
|
|
|
|
gPathRendererNames[GpuPathRenderers::kStencilAndCover].c_str());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if(GrCoverageCountingPathRenderer::IsSupported(*caps)) {
|
|
|
|
writer.appendString(
|
|
|
|
gPathRendererNames[GpuPathRenderers::kCoverageCounting].c_str());
|
|
|
|
}
|
|
|
|
writer.appendString(gPathRendererNames[GpuPathRenderers::kSmall].c_str());
|
|
|
|
}
|
|
|
|
writer.appendString(
|
|
|
|
gPathRendererNames[GpuPathRenderers::kTessellating].c_str());
|
|
|
|
writer.appendString(gPathRendererNames[GpuPathRenderers::kNone].c_str());
|
|
|
|
}
|
|
|
|
});
|
2017-02-28 00:00:53 +00:00
|
|
|
|
2016-06-03 15:47:23 +00:00
|
|
|
// Softkey state
|
2018-06-19 01:23:06 +00:00
|
|
|
WriteStateObject(writer, kSoftkeyStateName, kSoftkeyHint,
|
|
|
|
[this](SkJSONWriter& writer) {
|
|
|
|
writer.appendString(kSoftkeyHint);
|
|
|
|
for (const auto& softkey : fCommands.getCommandsAsSoftkeys()) {
|
|
|
|
writer.appendString(softkey.c_str());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
writer.endArray();
|
|
|
|
writer.flush();
|
|
|
|
|
|
|
|
auto data = memStream.detachAsData();
|
2016-06-03 15:47:23 +00:00
|
|
|
|
2018-06-19 01:23:06 +00:00
|
|
|
// TODO: would be cool to avoid this copy
|
|
|
|
const SkString cstring(static_cast<const char*>(data->data()), data->size());
|
2016-05-27 15:52:52 +00:00
|
|
|
|
2018-06-19 01:23:06 +00:00
|
|
|
fWindow->setUIState(cstring.c_str());
|
2016-05-27 15:52:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Viewer::onUIStateChanged(const SkString& stateName, const SkString& stateValue) {
|
2016-06-02 19:16:25 +00:00
|
|
|
// For those who will add more features to handle the state change in this function:
|
|
|
|
// After the change, please call updateUIState no notify the frontend (e.g., Android app).
|
|
|
|
// For example, after slide change, updateUIState is called inside setupCurrentSlide;
|
|
|
|
// after backend change, updateUIState is called in this function.
|
2016-05-27 15:52:52 +00:00
|
|
|
if (stateName.equals(kSlideStateName)) {
|
2018-01-16 21:23:03 +00:00
|
|
|
for (int i = 0; i < fSlides.count(); ++i) {
|
|
|
|
if (fSlides[i]->getName().equals(stateValue)) {
|
|
|
|
this->setCurrentSlide(i);
|
|
|
|
return;
|
2016-05-27 15:52:52 +00:00
|
|
|
}
|
|
|
|
}
|
2018-01-16 21:23:03 +00:00
|
|
|
|
|
|
|
SkDebugf("Slide not found: %s", stateValue.c_str());
|
2016-06-02 19:16:25 +00:00
|
|
|
} else if (stateName.equals(kBackendStateName)) {
|
|
|
|
for (int i = 0; i < sk_app::Window::kBackendTypeCount; i++) {
|
|
|
|
if (stateValue.equals(kBackendTypeStrings[i])) {
|
|
|
|
if (fBackendType != i) {
|
|
|
|
fBackendType = (sk_app::Window::BackendType)i;
|
|
|
|
fWindow->detach();
|
2017-11-08 14:54:10 +00:00
|
|
|
fWindow->attach(backend_type_for_window(fBackendType));
|
2016-06-02 19:16:25 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-02-24 23:04:47 +00:00
|
|
|
} else if (stateName.equals(kMSAAStateName)) {
|
|
|
|
DisplayParams params = fWindow->getRequestedDisplayParams();
|
|
|
|
int sampleCount = atoi(stateValue.c_str());
|
|
|
|
if (sampleCount != params.fMSAASampleCount) {
|
|
|
|
params.fMSAASampleCount = sampleCount;
|
|
|
|
fWindow->setRequestedDisplayParams(params);
|
|
|
|
fWindow->inval();
|
2017-03-07 20:16:34 +00:00
|
|
|
this->updateTitle();
|
|
|
|
this->updateUIState();
|
2017-02-28 00:00:53 +00:00
|
|
|
}
|
|
|
|
} else if (stateName.equals(kPathRendererStateName)) {
|
|
|
|
DisplayParams params = fWindow->getRequestedDisplayParams();
|
|
|
|
for (const auto& pair : gPathRendererNames) {
|
|
|
|
if (pair.second == stateValue.c_str()) {
|
|
|
|
if (params.fGrContextOptions.fGpuPathRenderers != pair.first) {
|
|
|
|
params.fGrContextOptions.fGpuPathRenderers = pair.first;
|
|
|
|
fWindow->setRequestedDisplayParams(params);
|
|
|
|
fWindow->inval();
|
2017-03-07 20:16:34 +00:00
|
|
|
this->updateTitle();
|
|
|
|
this->updateUIState();
|
2017-02-28 00:00:53 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2017-02-24 23:04:47 +00:00
|
|
|
}
|
2016-06-03 15:47:23 +00:00
|
|
|
} else if (stateName.equals(kSoftkeyStateName)) {
|
|
|
|
if (!stateValue.equals(kSoftkeyHint)) {
|
|
|
|
fCommands.onSoftkey(stateValue);
|
2017-03-07 20:16:34 +00:00
|
|
|
this->updateUIState(); // This is still needed to reset the value to kSoftkeyHint
|
2016-06-03 15:47:23 +00:00
|
|
|
}
|
2016-07-06 21:11:32 +00:00
|
|
|
} else if (stateName.equals(kRefreshStateName)) {
|
|
|
|
// This state is actually NOT in the UI state.
|
|
|
|
// We use this to allow Android to quickly set bool fRefresh.
|
|
|
|
fRefresh = stateValue.equals(kON);
|
2016-05-27 15:52:52 +00:00
|
|
|
} else {
|
|
|
|
SkDebugf("Unknown stateName: %s", stateName.c_str());
|
|
|
|
}
|
|
|
|
}
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
|
2019-08-29 14:39:22 +00:00
|
|
|
bool Viewer::onKey(skui::Key key, skui::InputState state, skui::ModifierKey modifiers) {
|
2017-12-08 21:46:09 +00:00
|
|
|
return fCommands.onKey(key, state, modifiers);
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
}
|
|
|
|
|
2019-08-29 14:39:22 +00:00
|
|
|
bool Viewer::onChar(SkUnichar c, skui::ModifierKey modifiers) {
|
2017-12-08 21:46:09 +00:00
|
|
|
if (fSlides[fCurrentSlide]->onChar(c)) {
|
2017-02-14 20:16:46 +00:00
|
|
|
fWindow->inval();
|
|
|
|
return true;
|
2017-12-08 21:45:43 +00:00
|
|
|
} else {
|
|
|
|
return fCommands.onChar(c, modifiers);
|
2017-02-14 20:16:46 +00:00
|
|
|
}
|
Integrate the ImGui library with viewer
Code and docs are at: https://github.com/ocornut/imgui
ImGui is an open source immediate mode GUI library that's
lightweight and fairly simply to integrate. Widget functions
return their state, and the library emits vertex and index
data to render everything. It's got a huge set of built-in
widgets and really robust layout control.
For the initial integration, I had to fix up event handling
in the viewer's app framework (to get mouse wheel and more
keys, etc...).
The new viewer 'Debug' window is toggled with the space bar.
For this change, I've added one feature to that window: the
slide picker. It's got a list of all slides, with filtering
support, and the ability to click to switch slides.
I also included the ImGui 'Demo' window (toggled with 'g').
This is nicely laid out, and includes examples of pretty
much everything the library can do. It also serves as good
documentation - find something that looks like what you want,
and then go look at the corresponding code (all of it is in
imgui_demo.cpp).
I have other CLs with other features (like directly editing
the primaries of the working color space), but I wanted to
land this chunk first, then start adding more features.
Other than adding new debugging features, there are few
more outstanding work items:
1) Raster doesn't render the GUI correctly, due to non-
invertible pos -> UV matrices. Florin is working on that.
2) Touch inputs aren't being routed yet, so the GUI isn't
usable on Android yet. Might also be tough to work with,
given the size.
3) ImGui has clipboard integration (that's why it wants
the C, X, and V keys), but we need to wire it up to the
OS' clipboard functions.
4) Draw commands can carry a void* payload to support
drawing images (using whatever mechanism the engine has).
I'd like to set that up (probably using SkImage*), which
makes it really easy to add visualization of off-screen
images in GMs, etc...
BUG=skia:
Change-Id: Iac2a63e37228d33141cb55b7e4d60bf11b7e9ae1
Reviewed-on: https://skia-review.googlesource.com/7702
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2017-02-10 18:36:16 +00:00
|
|
|
}
|