blob: e25fc2e9779ed02b17584959e94dbb69bd876e16 [file] [log] [blame]
jvanverth9f372462016-04-06 06:08:59 -07001/*
2* Copyright 2016 Google Inc.
3*
4* Use of this source code is governed by a BSD-style license that can be
5* found in the LICENSE file.
6*/
7
jvanverth34524262016-05-04 13:49:13 -07008#include "Viewer.h"
jvanverth9f372462016-04-06 06:08:59 -07009
jvanverth2bb3b6d2016-04-08 07:24:09 -070010#include "GMSlide.h"
liyuqian6f163d22016-06-13 12:26:45 -070011#include "ImageSlide.h"
Greg Daniel9fcc7432016-11-29 16:35:19 -050012#include "Resources.h"
jvanverthc7027ab2016-06-16 09:52:35 -070013#include "SampleSlide.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070014#include "SKPSlide.h"
jvanverth9f372462016-04-06 06:08:59 -070015
Greg Daniel285db442016-10-14 09:12:53 -040016#include "SkATrace.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070017#include "SkCanvas.h"
Brian Osman2dd96932016-10-18 15:33:53 -040018#include "SkCommandLineFlags.h"
liyuqian74959a12016-06-16 14:10:34 -070019#include "SkDashPathEffect.h"
Greg Daniel285db442016-10-14 09:12:53 -040020#include "SkGraphics.h"
Brian Osmanf750fbc2017-02-08 10:47:28 -050021#include "SkImagePriv.h"
liyuqian6f163d22016-06-13 12:26:45 -070022#include "SkMetaData.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070023#include "SkOSFile.h"
Ben Wagnerbf111d72016-11-07 18:05:29 -050024#include "SkOSPath.h"
jvanverth2bb3b6d2016-04-08 07:24:09 -070025#include "SkRandom.h"
26#include "SkStream.h"
liyuqian74959a12016-06-16 14:10:34 -070027#include "SkSurface.h"
liyuqian2edb0f42016-07-06 14:11:32 -070028#include "SkTime.h"
jvanverth9f372462016-04-06 06:08:59 -070029
jvanverth34524262016-05-04 13:49:13 -070030using namespace sk_app;
31
jvanverth9f372462016-04-06 06:08:59 -070032Application* Application::Create(int argc, char** argv, void* platformData) {
jvanverth34524262016-05-04 13:49:13 -070033 return new Viewer(argc, argv, platformData);
jvanverth9f372462016-04-06 06:08:59 -070034}
35
jvanverth9f372462016-04-06 06:08:59 -070036static void on_paint_handler(SkCanvas* canvas, void* userData) {
jvanverth34524262016-05-04 13:49:13 -070037 Viewer* vv = reinterpret_cast<Viewer*>(userData);
jvanverth9f372462016-04-06 06:08:59 -070038
39 return vv->onPaint(canvas);
40}
41
jvanverth814e38d2016-06-06 08:48:47 -070042static bool on_touch_handler(intptr_t owner, Window::InputState state, float x, float y, void* userData)
liyuqiand3cdbca2016-05-17 12:44:20 -070043{
44 Viewer* viewer = reinterpret_cast<Viewer*>(userData);
45
46 return viewer->onTouch(owner, state, x, y);
47}
48
liyuqiane5a6cd92016-05-27 08:52:52 -070049static void on_ui_state_changed_handler(const SkString& stateName, const SkString& stateValue, void* userData) {
50 Viewer* viewer = reinterpret_cast<Viewer*>(userData);
51
52 return viewer->onUIStateChanged(stateName, stateValue);
53}
54
Brian Osman2dd96932016-10-18 15:33:53 -040055static DEFINE_bool2(fullscreen, f, true, "Run fullscreen.");
Brian Osman16adfa32016-10-18 14:42:44 -040056
Brian Osman2dd96932016-10-18 15:33:53 -040057static DEFINE_string2(match, m, nullptr,
jvanverth2bb3b6d2016-04-08 07:24:09 -070058 "[~][^]substring[$] [...] of bench name to run.\n"
59 "Multiple matches may be separated by spaces.\n"
60 "~ causes a matching bench to always be skipped\n"
61 "^ requires the start of the bench to match\n"
62 "$ requires the end of the bench to match\n"
63 "^ and $ requires an exact match\n"
64 "If a bench does not match any list entry,\n"
65 "it is skipped unless some list entry starts with ~");
bsalomon6c471f72016-07-26 12:56:32 -070066
67#ifdef SK_VULKAN
jvanverthb8794cc2016-07-27 14:29:18 -070068# define BACKENDS_STR "\"sw\", \"gl\", and \"vk\""
bsalomon6c471f72016-07-26 12:56:32 -070069#else
70# define BACKENDS_STR "\"sw\" and \"gl\""
71#endif
72
liyuqian71491dc2016-06-09 12:02:34 -070073#ifdef SK_BUILD_FOR_ANDROID
Brian Osman2dd96932016-10-18 15:33:53 -040074static DEFINE_string(skps, "/data/local/tmp/skia", "Directory to read skps from.");
75static DEFINE_string(jpgs, "/data/local/tmp/skia", "Directory to read jpgs from.");
liyuqian71491dc2016-06-09 12:02:34 -070076#else
Brian Osman2dd96932016-10-18 15:33:53 -040077static DEFINE_string(skps, "skps", "Directory to read skps from.");
78static DEFINE_string(jpgs, "jpgs", "Directory to read jpgs from.");
liyuqian71491dc2016-06-09 12:02:34 -070079#endif
jvanverth2bb3b6d2016-04-08 07:24:09 -070080
Brian Osman2dd96932016-10-18 15:33:53 -040081static DEFINE_string2(backend, b, "sw", "Backend to use. Allowed values are " BACKENDS_STR ".");
bsalomon6c471f72016-07-26 12:56:32 -070082
Brian Osman2dd96932016-10-18 15:33:53 -040083static DEFINE_bool(atrace, false, "Enable support for using ATrace. ATrace is only supported on Android.");
Greg Daniel285db442016-10-14 09:12:53 -040084
jvanverthaf236b52016-05-20 06:01:06 -070085const char *kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = {
86 " [OpenGL]",
jvanverth063ece72016-06-17 09:29:14 -070087#ifdef SK_VULKAN
liyuqiand94ad582016-06-07 14:22:37 -070088 " [Vulkan]",
jvanverth063ece72016-06-17 09:29:14 -070089#endif
liyuqiand94ad582016-06-07 14:22:37 -070090 " [Raster]"
jvanverthaf236b52016-05-20 06:01:06 -070091};
92
bsalomon6c471f72016-07-26 12:56:32 -070093static sk_app::Window::BackendType get_backend_type(const char* str) {
94#ifdef SK_VULKAN
95 if (0 == strcmp(str, "vk")) {
96 return sk_app::Window::kVulkan_BackendType;
97 } else
98#endif
99 if (0 == strcmp(str, "gl")) {
100 return sk_app::Window::kNativeGL_BackendType;
101 } else if (0 == strcmp(str, "sw")) {
102 return sk_app::Window::kRaster_BackendType;
103 } else {
104 SkDebugf("Unknown backend type, %s, defaulting to sw.", str);
105 return sk_app::Window::kRaster_BackendType;
106 }
107}
108
liyuqiane5a6cd92016-05-27 08:52:52 -0700109const char* kName = "name";
110const char* kValue = "value";
111const char* kOptions = "options";
112const char* kSlideStateName = "Slide";
113const char* kBackendStateName = "Backend";
liyuqianb73c24b2016-06-03 08:47:23 -0700114const char* kSoftkeyStateName = "Softkey";
115const char* kSoftkeyHint = "Please select a softkey";
liyuqian1f508fd2016-06-07 06:57:40 -0700116const char* kFpsStateName = "FPS";
liyuqian6f163d22016-06-13 12:26:45 -0700117const char* kON = "ON";
118const char* kOFF = "OFF";
liyuqian2edb0f42016-07-06 14:11:32 -0700119const char* kRefreshStateName = "Refresh";
liyuqiane5a6cd92016-05-27 08:52:52 -0700120
jvanverth34524262016-05-04 13:49:13 -0700121Viewer::Viewer(int argc, char** argv, void* platformData)
jvanverthc265a922016-04-08 12:51:45 -0700122 : fCurrentMeasurement(0)
123 , fDisplayStats(false)
liyuqian2edb0f42016-07-06 14:11:32 -0700124 , fRefresh(false)
jvanverth063ece72016-06-17 09:29:14 -0700125 , fBackendType(sk_app::Window::kNativeGL_BackendType)
Brian Osmanf750fbc2017-02-08 10:47:28 -0500126 , fColorType(kN32_SkColorType)
127 , fColorSpace(nullptr)
egdaniel2a0bb0a2016-04-11 08:30:40 -0700128 , fZoomCenterX(0.0f)
129 , fZoomCenterY(0.0f)
130 , fZoomLevel(0.0f)
131 , fZoomScale(SK_Scalar1)
jvanverthc265a922016-04-08 12:51:45 -0700132{
Greg Daniel285db442016-10-14 09:12:53 -0400133 SkGraphics::Init();
jvanverth3d6ed3a2016-04-07 11:09:51 -0700134 memset(fMeasurements, 0, sizeof(fMeasurements));
jvanverth9f372462016-04-06 06:08:59 -0700135
jvanverth2bb3b6d2016-04-08 07:24:09 -0700136 SkDebugf("Command line arguments: ");
137 for (int i = 1; i < argc; ++i) {
138 SkDebugf("%s ", argv[i]);
139 }
140 SkDebugf("\n");
141
142 SkCommandLineFlags::Parse(argc, argv);
Greg Daniel9fcc7432016-11-29 16:35:19 -0500143#ifdef SK_BUILD_FOR_ANDROID
144 SetResourcePath("/data/local/tmp/skia");
145#endif
jvanverth2bb3b6d2016-04-08 07:24:09 -0700146
Greg Daniel285db442016-10-14 09:12:53 -0400147 if (FLAGS_atrace) {
148 SkEventTracer::SetInstance(new SkATrace());
149 }
150
bsalomon6c471f72016-07-26 12:56:32 -0700151 fBackendType = get_backend_type(FLAGS_backend[0]);
jvanverth9f372462016-04-06 06:08:59 -0700152 fWindow = Window::CreateNativeWindow(platformData);
jvanverthaf236b52016-05-20 06:01:06 -0700153 fWindow->attach(fBackendType, DisplayParams());
jvanverth9f372462016-04-06 06:08:59 -0700154
155 // register callbacks
brianosman622c8d52016-05-10 06:50:49 -0700156 fCommands.attach(fWindow);
jvanverth9f372462016-04-06 06:08:59 -0700157 fWindow->registerPaintFunc(on_paint_handler, this);
liyuqiand3cdbca2016-05-17 12:44:20 -0700158 fWindow->registerTouchFunc(on_touch_handler, this);
liyuqiane5a6cd92016-05-27 08:52:52 -0700159 fWindow->registerUIStateChangedFunc(on_ui_state_changed_handler, this);
jvanverth9f372462016-04-06 06:08:59 -0700160
brianosman622c8d52016-05-10 06:50:49 -0700161 // add key-bindings
162 fCommands.addCommand('s', "Overlays", "Toggle stats display", [this]() {
163 this->fDisplayStats = !this->fDisplayStats;
164 fWindow->inval();
165 });
Brian Osmanf750fbc2017-02-08 10:47:28 -0500166 fCommands.addCommand('c', "Modes", "Cycle color mode", [this]() {
167 if (!fColorSpace) {
168 // Legacy -> sRGB
169 this->setColorMode(kN32_SkColorType, SkColorSpace::MakeSRGB());
170 } else if (kN32_SkColorType == fColorType) {
171 // sRGB -> F16 sRGB
172 this->setColorMode(kRGBA_F16_SkColorType, SkColorSpace::MakeSRGBLinear());
173 } else {
174 // F16 sRGB -> Legacy
175 this->setColorMode(kN32_SkColorType, nullptr);
176 }
brianosman622c8d52016-05-10 06:50:49 -0700177 });
178 fCommands.addCommand(Window::Key::kRight, "Right", "Navigation", "Next slide", [this]() {
179 int previousSlide = fCurrentSlide;
180 fCurrentSlide++;
181 if (fCurrentSlide >= fSlides.count()) {
182 fCurrentSlide = 0;
183 }
184 this->setupCurrentSlide(previousSlide);
185 });
186 fCommands.addCommand(Window::Key::kLeft, "Left", "Navigation", "Previous slide", [this]() {
187 int previousSlide = fCurrentSlide;
188 fCurrentSlide--;
189 if (fCurrentSlide < 0) {
190 fCurrentSlide = fSlides.count() - 1;
191 }
192 this->setupCurrentSlide(previousSlide);
193 });
194 fCommands.addCommand(Window::Key::kUp, "Up", "Transform", "Zoom in", [this]() {
195 this->changeZoomLevel(1.f / 32.f);
196 fWindow->inval();
197 });
198 fCommands.addCommand(Window::Key::kDown, "Down", "Transform", "Zoom out", [this]() {
199 this->changeZoomLevel(-1.f / 32.f);
200 fWindow->inval();
201 });
jvanverthaf236b52016-05-20 06:01:06 -0700202 fCommands.addCommand('d', "Modes", "Change rendering backend", [this]() {
Jim Van Verthd63c1022017-01-05 13:50:49 -0500203#if defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC)
jvanverthb8794cc2016-07-27 14:29:18 -0700204 if (sk_app::Window::kRaster_BackendType == fBackendType) {
205 fBackendType = sk_app::Window::kNativeGL_BackendType;
206#ifdef SK_VULKAN
207 } else if (sk_app::Window::kNativeGL_BackendType == fBackendType) {
208 fBackendType = sk_app::Window::kVulkan_BackendType;
209#endif
210 } else {
211 fBackendType = sk_app::Window::kRaster_BackendType;
212 }
Jim Van Verthd63c1022017-01-05 13:50:49 -0500213#elif defined(SK_BUILD_FOR_UNIX)
214 // Switching to and from Vulkan is problematic on Linux so disabled for now
215 if (sk_app::Window::kRaster_BackendType == fBackendType) {
216 fBackendType = sk_app::Window::kNativeGL_BackendType;
217 } else if (sk_app::Window::kNativeGL_BackendType == fBackendType) {
218 fBackendType = sk_app::Window::kRaster_BackendType;
219 }
220#endif
jvanverthaf236b52016-05-20 06:01:06 -0700221 fWindow->detach();
222
Jim Van Verthd63c1022017-01-05 13:50:49 -0500223#if defined(SK_BUILD_FOR_WIN) && defined(SK_VULKAN)
224 // Switching from OpenGL to Vulkan in the same window is problematic at this point on
225 // Windows, so we just delete the window and recreate it.
jvanverthaf236b52016-05-20 06:01:06 -0700226 if (sk_app::Window::kVulkan_BackendType == fBackendType) {
jvanverthb8794cc2016-07-27 14:29:18 -0700227 delete fWindow;
228 fWindow = Window::CreateNativeWindow(nullptr);
jvanverthaf236b52016-05-20 06:01:06 -0700229
jvanverthb8794cc2016-07-27 14:29:18 -0700230 // re-register callbacks
231 fCommands.attach(fWindow);
232 fWindow->registerPaintFunc(on_paint_handler, this);
233 fWindow->registerTouchFunc(on_touch_handler, this);
234 fWindow->registerUIStateChangedFunc(on_ui_state_changed_handler, this);
235 }
236#endif
jvanverthaf236b52016-05-20 06:01:06 -0700237 fWindow->attach(fBackendType, DisplayParams());
jvanverthb8794cc2016-07-27 14:29:18 -0700238
jvanverthaf236b52016-05-20 06:01:06 -0700239 this->updateTitle();
jvanverth85f758c2016-05-27 06:47:08 -0700240 fWindow->inval();
jvanverthb8794cc2016-07-27 14:29:18 -0700241 fWindow->show();
jvanverthaf236b52016-05-20 06:01:06 -0700242 });
brianosman622c8d52016-05-10 06:50:49 -0700243
jvanverth2bb3b6d2016-04-08 07:24:09 -0700244 // set up slides
245 this->initSlides();
246
djsollen12d62a72016-04-21 07:59:44 -0700247 fAnimTimer.run();
248
jvanverth2bb3b6d2016-04-08 07:24:09 -0700249 // set up first frame
jvanverth2bb3b6d2016-04-08 07:24:09 -0700250 fCurrentSlide = 0;
jvanverthc265a922016-04-08 12:51:45 -0700251 setupCurrentSlide(-1);
jvanverthc265a922016-04-08 12:51:45 -0700252
jvanverth9f372462016-04-06 06:08:59 -0700253 fWindow->show();
254}
255
jvanverth34524262016-05-04 13:49:13 -0700256void Viewer::initSlides() {
liyuqian1f508fd2016-06-07 06:57:40 -0700257 fAllSlideNames = Json::Value(Json::arrayValue);
258
jvanverth2bb3b6d2016-04-08 07:24:09 -0700259 const skiagm::GMRegistry* gms(skiagm::GMRegistry::Head());
260 while (gms) {
Ben Wagner145dbcd2016-11-03 14:40:50 -0400261 std::unique_ptr<skiagm::GM> gm(gms->factory()(nullptr));
jvanverth2bb3b6d2016-04-08 07:24:09 -0700262
263 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, gm->getName())) {
264 sk_sp<Slide> slide(new GMSlide(gm.release()));
265 fSlides.push_back(slide);
266 }
267
268 gms = gms->next();
269 }
270
271 // reverse array
272 for (int i = 0; i < fSlides.count()/2; ++i) {
273 sk_sp<Slide> temp = fSlides[i];
274 fSlides[i] = fSlides[fSlides.count() - i - 1];
275 fSlides[fSlides.count() - i - 1] = temp;
276 }
277
jvanverthc7027ab2016-06-16 09:52:35 -0700278 // samples
279 const SkViewRegister* reg = SkViewRegister::Head();
280 while (reg) {
281 sk_sp<Slide> slide(new SampleSlide(reg->factory()));
brianosmane1d20072016-07-12 09:07:33 -0700282 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, slide->getName().c_str())) {
283 fSlides.push_back(slide);
284 }
jvanverthc7027ab2016-06-16 09:52:35 -0700285 reg = reg->next();
286 }
287
jvanverth2bb3b6d2016-04-08 07:24:09 -0700288 // SKPs
289 for (int i = 0; i < FLAGS_skps.count(); i++) {
290 if (SkStrEndsWith(FLAGS_skps[i], ".skp")) {
jvanverthc265a922016-04-08 12:51:45 -0700291 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, FLAGS_skps[i])) {
292 continue;
293 }
294
jvanverth2bb3b6d2016-04-08 07:24:09 -0700295 SkString path(FLAGS_skps[i]);
jvanverthc265a922016-04-08 12:51:45 -0700296 sk_sp<SKPSlide> slide(new SKPSlide(SkOSPath::Basename(path.c_str()), path));
jvanverth2bb3b6d2016-04-08 07:24:09 -0700297 if (slide) {
298 fSlides.push_back(slide);
299 }
300 } else {
301 SkOSFile::Iter it(FLAGS_skps[i], ".skp");
jvanverthc265a922016-04-08 12:51:45 -0700302 SkString skpName;
303 while (it.next(&skpName)) {
304 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, skpName.c_str())) {
305 continue;
306 }
307
308 SkString path = SkOSPath::Join(FLAGS_skps[i], skpName.c_str());
309 sk_sp<SKPSlide> slide(new SKPSlide(skpName, path));
jvanverth2bb3b6d2016-04-08 07:24:09 -0700310 if (slide) {
311 fSlides.push_back(slide);
312 }
313 }
314 }
315 }
liyuqian6f163d22016-06-13 12:26:45 -0700316
317 // JPGs
318 for (int i = 0; i < FLAGS_jpgs.count(); i++) {
319 SkOSFile::Iter it(FLAGS_jpgs[i], ".jpg");
320 SkString jpgName;
321 while (it.next(&jpgName)) {
brianosmane1d20072016-07-12 09:07:33 -0700322 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, jpgName.c_str())) {
323 continue;
324 }
325
liyuqian6f163d22016-06-13 12:26:45 -0700326 SkString path = SkOSPath::Join(FLAGS_jpgs[i], jpgName.c_str());
327 sk_sp<ImageSlide> slide(new ImageSlide(jpgName, path));
328 if (slide) {
329 fSlides.push_back(slide);
330 }
331 }
332 }
jvanverth2bb3b6d2016-04-08 07:24:09 -0700333}
334
335
jvanverth34524262016-05-04 13:49:13 -0700336Viewer::~Viewer() {
jvanverth9f372462016-04-06 06:08:59 -0700337 fWindow->detach();
338 delete fWindow;
339}
340
brianosman05de2162016-05-06 13:28:57 -0700341void Viewer::updateTitle() {
jvanverth34524262016-05-04 13:49:13 -0700342 SkString title("Viewer: ");
jvanverthc265a922016-04-08 12:51:45 -0700343 title.append(fSlides[fCurrentSlide]->getName());
brianosmanb109b8c2016-06-16 13:03:24 -0700344
Brian Osmanf750fbc2017-02-08 10:47:28 -0500345 title.appendf(" %s", sk_tool_utils::colortype_name(fColorType));
346
347 // TODO: Find a short string to describe the gamut of the color space?
348 if (fColorSpace) {
349 title.append(" ColorManaged");
brianosman05de2162016-05-06 13:28:57 -0700350 }
Brian Osmanf750fbc2017-02-08 10:47:28 -0500351
jvanverthaf236b52016-05-20 06:01:06 -0700352 title.append(kBackendTypeStrings[fBackendType]);
brianosman05de2162016-05-06 13:28:57 -0700353 fWindow->setTitle(title.c_str());
354}
355
356void Viewer::setupCurrentSlide(int previousSlide) {
liyuqiane5a6cd92016-05-27 08:52:52 -0700357 if (fCurrentSlide == previousSlide) {
358 return; // no change; do nothing
359 }
360
liyuqian6f163d22016-06-13 12:26:45 -0700361 // prepare dimensions for image slides
jvanverthc7027ab2016-06-16 09:52:35 -0700362 fSlides[fCurrentSlide]->load(SkIntToScalar(fWindow->width()), SkIntToScalar(fWindow->height()));
liyuqian6f163d22016-06-13 12:26:45 -0700363
liyuqiane46e4f02016-05-20 07:32:19 -0700364 fGesture.reset();
365 fDefaultMatrix.reset();
366 fDefaultMatrixInv.reset();
367
368 if (fWindow->supportsContentRect() && fWindow->scaleContentToFit()) {
369 const SkRect contentRect = fWindow->getContentRect();
370 const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
371 const SkRect slideBounds = SkRect::MakeIWH(slideSize.width(), slideSize.height());
372 if (contentRect.width() > 0 && contentRect.height() > 0) {
373 fDefaultMatrix.setRectToRect(slideBounds, contentRect, SkMatrix::kStart_ScaleToFit);
liyuqianbeb1c672016-05-20 11:41:01 -0700374 SkAssertResult(fDefaultMatrix.invert(&fDefaultMatrixInv));
liyuqiane46e4f02016-05-20 07:32:19 -0700375 }
376 }
377
378 if (fWindow->supportsContentRect()) {
379 const SkISize slideSize = fSlides[fCurrentSlide]->getDimensions();
380 SkRect windowRect = fWindow->getContentRect();
381 fDefaultMatrixInv.mapRect(&windowRect);
jvanverth1e305ba2016-06-01 09:39:15 -0700382 fGesture.setTransLimit(SkRect::MakeWH(SkIntToScalar(slideSize.width()),
383 SkIntToScalar(slideSize.height())),
384 windowRect);
liyuqiane46e4f02016-05-20 07:32:19 -0700385 }
386
brianosman05de2162016-05-06 13:28:57 -0700387 this->updateTitle();
liyuqiane5a6cd92016-05-27 08:52:52 -0700388 this->updateUIState();
jvanverthc265a922016-04-08 12:51:45 -0700389 if (previousSlide >= 0) {
390 fSlides[previousSlide]->unload();
391 }
jvanverthc265a922016-04-08 12:51:45 -0700392 fWindow->inval();
393}
394
395#define MAX_ZOOM_LEVEL 8
396#define MIN_ZOOM_LEVEL -8
397
jvanverth34524262016-05-04 13:49:13 -0700398void Viewer::changeZoomLevel(float delta) {
jvanverthc265a922016-04-08 12:51:45 -0700399 fZoomLevel += delta;
400 if (fZoomLevel > 0) {
401 fZoomLevel = SkMinScalar(fZoomLevel, MAX_ZOOM_LEVEL);
402 fZoomScale = fZoomLevel + SK_Scalar1;
403 } else if (fZoomLevel < 0) {
404 fZoomLevel = SkMaxScalar(fZoomLevel, MIN_ZOOM_LEVEL);
405 fZoomScale = SK_Scalar1 / (SK_Scalar1 - fZoomLevel);
406 } else {
407 fZoomScale = SK_Scalar1;
408 }
jvanverthc265a922016-04-08 12:51:45 -0700409}
410
liyuqiand3cdbca2016-05-17 12:44:20 -0700411SkMatrix Viewer::computeMatrix() {
jvanverthc265a922016-04-08 12:51:45 -0700412 SkMatrix m;
413 m.reset();
414
415 if (fZoomLevel) {
416 SkPoint center;
417 //m = this->getLocalMatrix();//.invert(&m);
418 m.mapXY(fZoomCenterX, fZoomCenterY, &center);
419 SkScalar cx = center.fX;
420 SkScalar cy = center.fY;
421
422 m.setTranslate(-cx, -cy);
423 m.postScale(fZoomScale, fZoomScale);
424 m.postTranslate(cx, cy);
425 }
426
liyuqiand3cdbca2016-05-17 12:44:20 -0700427 m.preConcat(fGesture.localM());
428 m.preConcat(fGesture.globalM());
jvanverthc265a922016-04-08 12:51:45 -0700429
liyuqiand3cdbca2016-05-17 12:44:20 -0700430 return m;
jvanverthc265a922016-04-08 12:51:45 -0700431}
432
Brian Osmanf750fbc2017-02-08 10:47:28 -0500433void Viewer::setColorMode(SkColorType colorType, sk_sp<SkColorSpace> colorSpace) {
434 fColorType = colorType;
435 fColorSpace = std::move(colorSpace);
liyuqian6f163d22016-06-13 12:26:45 -0700436
Brian Osmanf750fbc2017-02-08 10:47:28 -0500437 // When we're in color managed mode, we tag our window surface as sRGB. If we've switched into
438 // or out of legacy mode, we need to update our window configuration.
439 DisplayParams params = fWindow->getDisplayParams();
440 if (SkToBool(fColorSpace) != SkToBool(params.fColorSpace)) {
441 params.fColorSpace = fColorSpace ? SkColorSpace::MakeSRGB() : nullptr;
442 fWindow->setDisplayParams(params);
443 }
444
445 this->updateTitle();
446 fWindow->inval();
447}
448
449void Viewer::drawSlide(SkCanvas* canvas) {
jvanverthc265a922016-04-08 12:51:45 -0700450 int count = canvas->save();
djsollen12d62a72016-04-21 07:59:44 -0700451 if (fWindow->supportsContentRect()) {
452 SkRect contentRect = fWindow->getContentRect();
453 canvas->clipRect(contentRect);
454 canvas->translate(contentRect.fLeft, contentRect.fTop);
455 }
456
Brian Osmanf750fbc2017-02-08 10:47:28 -0500457 // By default, we render directly into the window's surface/canvas
458 SkCanvas* slideCanvas = canvas;
jvanverth3d6ed3a2016-04-07 11:09:51 -0700459
Brian Osmanf750fbc2017-02-08 10:47:28 -0500460 // ... but if we're in F16, or the gamut isn't sRGB, we need to render offscreen
461 sk_sp<SkSurface> offscreenSurface = nullptr;
Brian Osman462334e2017-02-09 11:22:57 -0500462 if (kRGBA_F16_SkColorType == fColorType ||
463 (fColorSpace && fColorSpace != SkColorSpace::MakeSRGB())) {
Brian Osmanf750fbc2017-02-08 10:47:28 -0500464 SkImageInfo info = SkImageInfo::Make(fWindow->width(), fWindow->height(), fColorType,
465 kPremul_SkAlphaType, fColorSpace);
466 offscreenSurface = canvas->makeSurface(info);
467 slideCanvas = offscreenSurface->getCanvas();
468 }
469
470 slideCanvas->clear(SK_ColorWHITE);
471 slideCanvas->concat(fDefaultMatrix);
472 slideCanvas->concat(computeMatrix());
473
474 fSlides[fCurrentSlide]->draw(slideCanvas);
475
476 // If we rendered offscreen, snap an image and push the results to the window's canvas
477 if (offscreenSurface) {
478 auto slideImage = offscreenSurface->makeImageSnapshot();
479
480 // Tag the image with the sRGB gamut, so no further color space conversion happens
481 sk_sp<SkColorSpace> cs = (kRGBA_F16_SkColorType == fColorType)
482 ? SkColorSpace::MakeSRGBLinear() : SkColorSpace::MakeSRGB();
483 auto retaggedImage = SkImageMakeRasterCopyAndAssignColorSpace(slideImage.get(), cs.get());
484 canvas->drawImage(retaggedImage, 0, 0);
liyuqian74959a12016-06-16 14:10:34 -0700485 }
486
jvanverthc265a922016-04-08 12:51:45 -0700487 canvas->restoreToCount(count);
liyuqian6f163d22016-06-13 12:26:45 -0700488}
489
490void Viewer::onPaint(SkCanvas* canvas) {
liyuqian2edb0f42016-07-06 14:11:32 -0700491 // Record measurements
492 double startTime = SkTime::GetMSecs();
493
Brian Osmanf750fbc2017-02-08 10:47:28 -0500494 drawSlide(canvas);
jvanverthc265a922016-04-08 12:51:45 -0700495
496 if (fDisplayStats) {
497 drawStats(canvas);
498 }
brianosman622c8d52016-05-10 06:50:49 -0700499 fCommands.drawHelp(canvas);
liyuqian2edb0f42016-07-06 14:11:32 -0700500
501 fMeasurements[fCurrentMeasurement++] = SkTime::GetMSecs() - startTime;
502 fCurrentMeasurement &= (kMeasurementCount - 1); // fast mod
503 SkASSERT(fCurrentMeasurement < kMeasurementCount);
504 updateUIState(); // Update the FPS
jvanverth3d6ed3a2016-04-07 11:09:51 -0700505}
506
jvanverth814e38d2016-06-06 08:48:47 -0700507bool Viewer::onTouch(intptr_t owner, Window::InputState state, float x, float y) {
liyuqiand3cdbca2016-05-17 12:44:20 -0700508 void* castedOwner = reinterpret_cast<void*>(owner);
liyuqiane46e4f02016-05-20 07:32:19 -0700509 SkPoint touchPoint = fDefaultMatrixInv.mapXY(x, y);
liyuqiand3cdbca2016-05-17 12:44:20 -0700510 switch (state) {
511 case Window::kUp_InputState: {
512 fGesture.touchEnd(castedOwner);
513 break;
514 }
515 case Window::kDown_InputState: {
liyuqiane46e4f02016-05-20 07:32:19 -0700516 fGesture.touchBegin(castedOwner, touchPoint.fX, touchPoint.fY);
liyuqiand3cdbca2016-05-17 12:44:20 -0700517 break;
518 }
519 case Window::kMove_InputState: {
liyuqiane46e4f02016-05-20 07:32:19 -0700520 fGesture.touchMoved(castedOwner, touchPoint.fX, touchPoint.fY);
liyuqiand3cdbca2016-05-17 12:44:20 -0700521 break;
522 }
523 }
524 fWindow->inval();
525 return true;
526}
527
jvanverth34524262016-05-04 13:49:13 -0700528void Viewer::drawStats(SkCanvas* canvas) {
jvanverth3d6ed3a2016-04-07 11:09:51 -0700529 static const float kPixelPerMS = 2.0f;
530 static const int kDisplayWidth = 130;
531 static const int kDisplayHeight = 100;
532 static const int kDisplayPadding = 10;
533 static const int kGraphPadding = 3;
534 static const SkScalar kBaseMS = 1000.f / 60.f; // ms/frame to hit 60 fps
535
536 SkISize canvasSize = canvas->getDeviceSize();
537 SkRect rect = SkRect::MakeXYWH(SkIntToScalar(canvasSize.fWidth-kDisplayWidth-kDisplayPadding),
538 SkIntToScalar(kDisplayPadding),
539 SkIntToScalar(kDisplayWidth), SkIntToScalar(kDisplayHeight));
540 SkPaint paint;
541 canvas->save();
542
djsollen12d62a72016-04-21 07:59:44 -0700543 if (fWindow->supportsContentRect()) {
544 SkRect contentRect = fWindow->getContentRect();
545 canvas->clipRect(contentRect);
546 canvas->translate(contentRect.fLeft, contentRect.fTop);
547 }
548
jvanverth3d6ed3a2016-04-07 11:09:51 -0700549 canvas->clipRect(rect);
550 paint.setColor(SK_ColorBLACK);
551 canvas->drawRect(rect, paint);
552 // draw the 16ms line
553 paint.setColor(SK_ColorLTGRAY);
554 canvas->drawLine(rect.fLeft, rect.fBottom - kBaseMS*kPixelPerMS,
555 rect.fRight, rect.fBottom - kBaseMS*kPixelPerMS, paint);
556 paint.setColor(SK_ColorRED);
557 paint.setStyle(SkPaint::kStroke_Style);
558 canvas->drawRect(rect, paint);
559
560 int x = SkScalarTruncToInt(rect.fLeft) + kGraphPadding;
561 const int xStep = 2;
562 const int startY = SkScalarTruncToInt(rect.fBottom);
563 int i = fCurrentMeasurement;
564 do {
565 int endY = startY - (int)(fMeasurements[i] * kPixelPerMS + 0.5); // round to nearest value
566 canvas->drawLine(SkIntToScalar(x), SkIntToScalar(startY),
567 SkIntToScalar(x), SkIntToScalar(endY), paint);
568 i++;
569 i &= (kMeasurementCount - 1); // fast mod
570 x += xStep;
571 } while (i != fCurrentMeasurement);
jvanverth9f372462016-04-06 06:08:59 -0700572
573 canvas->restore();
574}
575
liyuqian2edb0f42016-07-06 14:11:32 -0700576void Viewer::onIdle() {
jvanverthc265a922016-04-08 12:51:45 -0700577 fAnimTimer.updateTime();
liyuqian2edb0f42016-07-06 14:11:32 -0700578 if (fSlides[fCurrentSlide]->animate(fAnimTimer) || fDisplayStats || fRefresh) {
jvanverthc265a922016-04-08 12:51:45 -0700579 fWindow->inval();
580 }
jvanverth9f372462016-04-06 06:08:59 -0700581}
liyuqiane5a6cd92016-05-27 08:52:52 -0700582
583void Viewer::updateUIState() {
liyuqianb73c24b2016-06-03 08:47:23 -0700584 // Slide state
liyuqiane5a6cd92016-05-27 08:52:52 -0700585 Json::Value slideState(Json::objectValue);
586 slideState[kName] = kSlideStateName;
587 slideState[kValue] = fSlides[fCurrentSlide]->getName().c_str();
liyuqian1f508fd2016-06-07 06:57:40 -0700588 if (fAllSlideNames.size() == 0) {
589 for(auto slide : fSlides) {
590 fAllSlideNames.append(Json::Value(slide->getName().c_str()));
591 }
liyuqiane5a6cd92016-05-27 08:52:52 -0700592 }
liyuqian1f508fd2016-06-07 06:57:40 -0700593 slideState[kOptions] = fAllSlideNames;
liyuqiane5a6cd92016-05-27 08:52:52 -0700594
liyuqianb73c24b2016-06-03 08:47:23 -0700595 // Backend state
liyuqiane5a6cd92016-05-27 08:52:52 -0700596 Json::Value backendState(Json::objectValue);
597 backendState[kName] = kBackendStateName;
liyuqian6cb70252016-06-02 12:16:25 -0700598 backendState[kValue] = kBackendTypeStrings[fBackendType];
liyuqiane5a6cd92016-05-27 08:52:52 -0700599 backendState[kOptions] = Json::Value(Json::arrayValue);
liyuqianb73c24b2016-06-03 08:47:23 -0700600 for (auto str : kBackendTypeStrings) {
liyuqian6cb70252016-06-02 12:16:25 -0700601 backendState[kOptions].append(Json::Value(str));
602 }
liyuqiane5a6cd92016-05-27 08:52:52 -0700603
liyuqianb73c24b2016-06-03 08:47:23 -0700604 // Softkey state
605 Json::Value softkeyState(Json::objectValue);
606 softkeyState[kName] = kSoftkeyStateName;
607 softkeyState[kValue] = kSoftkeyHint;
608 softkeyState[kOptions] = Json::Value(Json::arrayValue);
609 softkeyState[kOptions].append(kSoftkeyHint);
610 for (const auto& softkey : fCommands.getCommandsAsSoftkeys()) {
611 softkeyState[kOptions].append(Json::Value(softkey.c_str()));
612 }
613
liyuqian1f508fd2016-06-07 06:57:40 -0700614 // FPS state
615 Json::Value fpsState(Json::objectValue);
616 fpsState[kName] = kFpsStateName;
617 double measurement = fMeasurements[
618 (fCurrentMeasurement + (kMeasurementCount-1)) % kMeasurementCount
619 ];
620 fpsState[kValue] = SkStringPrintf("%8.3lf ms", measurement).c_str();
621 fpsState[kOptions] = Json::Value(Json::arrayValue);
622
liyuqiane5a6cd92016-05-27 08:52:52 -0700623 Json::Value state(Json::arrayValue);
624 state.append(slideState);
625 state.append(backendState);
liyuqianb73c24b2016-06-03 08:47:23 -0700626 state.append(softkeyState);
liyuqian1f508fd2016-06-07 06:57:40 -0700627 state.append(fpsState);
liyuqiane5a6cd92016-05-27 08:52:52 -0700628
629 fWindow->setUIState(state);
630}
631
632void Viewer::onUIStateChanged(const SkString& stateName, const SkString& stateValue) {
liyuqian6cb70252016-06-02 12:16:25 -0700633 // For those who will add more features to handle the state change in this function:
634 // After the change, please call updateUIState no notify the frontend (e.g., Android app).
635 // For example, after slide change, updateUIState is called inside setupCurrentSlide;
636 // after backend change, updateUIState is called in this function.
liyuqiane5a6cd92016-05-27 08:52:52 -0700637 if (stateName.equals(kSlideStateName)) {
638 int previousSlide = fCurrentSlide;
639 fCurrentSlide = 0;
640 for(auto slide : fSlides) {
641 if (slide->getName().equals(stateValue)) {
642 setupCurrentSlide(previousSlide);
643 break;
644 }
645 fCurrentSlide++;
646 }
647 if (fCurrentSlide >= fSlides.count()) {
648 fCurrentSlide = previousSlide;
649 SkDebugf("Slide not found: %s", stateValue.c_str());
650 }
liyuqian6cb70252016-06-02 12:16:25 -0700651 } else if (stateName.equals(kBackendStateName)) {
652 for (int i = 0; i < sk_app::Window::kBackendTypeCount; i++) {
653 if (stateValue.equals(kBackendTypeStrings[i])) {
654 if (fBackendType != i) {
655 fBackendType = (sk_app::Window::BackendType)i;
656 fWindow->detach();
657 fWindow->attach(fBackendType, DisplayParams());
658 fWindow->inval();
659 updateTitle();
660 updateUIState();
661 }
662 break;
663 }
664 }
liyuqianb73c24b2016-06-03 08:47:23 -0700665 } else if (stateName.equals(kSoftkeyStateName)) {
666 if (!stateValue.equals(kSoftkeyHint)) {
667 fCommands.onSoftkey(stateValue);
668 updateUIState(); // This is still needed to reset the value to kSoftkeyHint
669 }
liyuqian2edb0f42016-07-06 14:11:32 -0700670 } else if (stateName.equals(kRefreshStateName)) {
671 // This state is actually NOT in the UI state.
672 // We use this to allow Android to quickly set bool fRefresh.
673 fRefresh = stateValue.equals(kON);
liyuqiane5a6cd92016-05-27 08:52:52 -0700674 } else {
675 SkDebugf("Unknown stateName: %s", stateName.c_str());
676 }
677}