1 /* 2 * Copyright 2011 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 8 #include "SampleApp.h" 9 10 #include "OverView.h" 11 #include "Resources.h" 12 #include "SampleCode.h" 13 #include "SkAnimTimer.h" 14 #include "SkCanvas.h" 15 #include "SkColorSpace_XYZ.h" 16 #include "SkCommandLineFlags.h" 17 #include "SkCommonFlagsPathRenderer.h" 18 #include "SkData.h" 19 #include "SkDocument.h" 20 #include "SkGraphics.h" 21 #include "SkOSFile.h" 22 #include "SkOSPath.h" 23 #include "SkPaint.h" 24 #include "SkPaintFilterCanvas.h" 25 #include "SkPicture.h" 26 #include "SkPictureRecorder.h" 27 #include "SkPM4fPriv.h" 28 #include "SkStream.h" 29 #include "SkSurface.h" 30 #include "SkTemplates.h" 31 #include "SkTSort.h" 32 #include "SkTime.h" 33 #include "SkTypeface.h" 34 #include "SkWindow.h" 35 #include "sk_tool_utils.h" 36 #include "SkScan.h" 37 #include "SkClipOpPriv.h" 38 39 #include "SkReadBuffer.h" 40 #include "SkStream.h" 41 42 #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) 43 #include "SkCGUtils.h" 44 #endif 45 46 #define PICTURE_MEANS_PIPE false 47 #define SERIALIZE_PICTURE true 48 49 #if SK_SUPPORT_GPU 50 # include "gl/GrGLInterface.h" 51 # include "gl/GrGLUtil.h" 52 # include "GrContext.h" 53 # include "SkGr.h" 54 # if SK_ANGLE 55 # include "gl/angle/GLTestContext_angle.h" 56 # endif 57 #else 58 class GrContext; 59 #endif 60 61 enum OutputColorSpace { 62 kLegacy_OutputColorSpace, 63 kSRGB_OutputColorSpace, 64 kNarrow_OutputColorSpace, 65 kMonitor_OutputColorSpace, 66 }; 67 68 const struct { 69 SkColorType fColorType; 70 OutputColorSpace fColorSpace; 71 const char* fName; 72 } gConfig[] = { 73 { kN32_SkColorType, kLegacy_OutputColorSpace, "L32" }, 74 { kN32_SkColorType, kSRGB_OutputColorSpace, "S32" }, 75 { kRGBA_F16_SkColorType, kSRGB_OutputColorSpace, "F16" }, 76 { kRGBA_F16_SkColorType, kNarrow_OutputColorSpace, "F16 Narrow" }, 77 { kRGBA_F16_SkColorType, kMonitor_OutputColorSpace, "F16 Device" }, 78 }; 79 80 // Should be 3x + 1 81 #define kMaxFatBitsScale 28 82 83 extern SampleView* CreateSamplePictFileView(const char filename[]); 84 85 class PictFileFactory : public SkViewFactory { 86 SkString fFilename; 87 public: 88 PictFileFactory(const SkString& filename) : fFilename(filename) {} 89 SkView* operator() () const override { 90 return CreateSamplePictFileView(fFilename.c_str()); 91 } 92 }; 93 94 extern SampleView* CreateSamplePathFinderView(const char filename[]); 95 96 class PathFinderFactory : public SkViewFactory { 97 SkString fFilename; 98 public: 99 PathFinderFactory(const SkString& filename) : fFilename(filename) {} 100 SkView* operator() () const override { 101 return CreateSamplePathFinderView(fFilename.c_str()); 102 } 103 }; 104 105 extern SampleView* CreateSampleSVGFileView(const SkString& filename); 106 107 class SVGFileFactory : public SkViewFactory { 108 SkString fFilename; 109 public: 110 SVGFileFactory(const SkString& filename) : fFilename(filename) {} 111 SkView* operator() () const override { 112 return CreateSampleSVGFileView(fFilename); 113 } 114 }; 115 116 #ifdef SAMPLE_PDF_FILE_VIEWER 117 extern SampleView* CreateSamplePdfFileViewer(const char filename[]); 118 119 class PdfFileViewerFactory : public SkViewFactory { 120 SkString fFilename; 121 public: 122 PdfFileViewerFactory(const SkString& filename) : fFilename(filename) {} 123 SkView* operator() () const override { 124 return CreateSamplePdfFileViewer(fFilename.c_str()); 125 } 126 }; 127 #endif // SAMPLE_PDF_FILE_VIEWER 128 129 #if SK_ANGLE 130 //#define DEFAULT_TO_ANGLE 1 131 #else 132 #define DEFAULT_TO_GPU 0 // if 1 default rendering is on GPU 133 #endif 134 135 #define ANIMATING_EVENTTYPE "nextSample" 136 #define ANIMATING_DELAY 250 137 138 #ifdef SK_DEBUG 139 #define FPS_REPEAT_MULTIPLIER 1 140 #else 141 #define FPS_REPEAT_MULTIPLIER 10 142 #endif 143 #define FPS_REPEAT_COUNT (10 * FPS_REPEAT_MULTIPLIER) 144 145 static SampleWindow* gSampleWindow; 146 147 static bool gShowGMBounds; 148 149 static void post_event_to_sink(SkEvent* evt, SkEventSink* sink) { 150 evt->setTargetID(sink->getSinkID())->post(); 151 } 152 153 static SkAnimTimer gAnimTimer; 154 155 /////////////////////////////////////////////////////////////////////////////// 156 157 static const char* skip_until(const char* str, const char* skip) { 158 if (!str) { 159 return nullptr; 160 } 161 return strstr(str, skip); 162 } 163 164 static const char* skip_past(const char* str, const char* skip) { 165 const char* found = skip_until(str, skip); 166 if (!found) { 167 return nullptr; 168 } 169 return found + strlen(skip); 170 } 171 172 static const char* gPrefFileName = "sampleapp_prefs.txt"; 173 174 static bool readTitleFromPrefs(SkString* title) { 175 SkFILEStream stream(gPrefFileName); 176 if (!stream.isValid()) { 177 return false; 178 } 179 180 size_t len = stream.getLength(); 181 SkString data(len); 182 stream.read(data.writable_str(), len); 183 const char* s = data.c_str(); 184 185 s = skip_past(s, "curr-slide-title"); 186 s = skip_past(s, "="); 187 s = skip_past(s, "\""); 188 const char* stop = skip_until(s, "\""); 189 if (stop > s) { 190 title->set(s, stop - s); 191 return true; 192 } 193 return false; 194 } 195 196 static void writeTitleToPrefs(const char* title) { 197 SkFILEWStream stream(gPrefFileName); 198 SkString data; 199 data.printf("curr-slide-title = \"%s\"\n", title); 200 stream.write(data.c_str(), data.size()); 201 } 202 203 /////////////////////////////////////////////////////////////////////////////// 204 205 class SampleWindow::DefaultDeviceManager : public SampleWindow::DeviceManager { 206 public: 207 208 DefaultDeviceManager() { 209 #if SK_SUPPORT_GPU 210 fCurContext = nullptr; 211 fCurIntf = nullptr; 212 fMSAASampleCount = 0; 213 fDeepColor = false; 214 fActualColorBits = 0; 215 #endif 216 fBackend = kNone_BackEndType; 217 } 218 219 ~DefaultDeviceManager() override { 220 #if SK_SUPPORT_GPU 221 SkSafeUnref(fCurContext); 222 SkSafeUnref(fCurIntf); 223 #endif 224 } 225 226 void setUpBackend(SampleWindow* win, const BackendOptions& backendOptions) override { 227 SkASSERT(kNone_BackEndType == fBackend); 228 229 fBackend = kNone_BackEndType; 230 231 #if SK_SUPPORT_GPU 232 switch (win->getDeviceType()) { 233 case kRaster_DeviceType: // fallthrough 234 case kGPU_DeviceType: 235 // all these guys use the native backend 236 fBackend = kNativeGL_BackEndType; 237 break; 238 #if SK_ANGLE 239 case kANGLE_DeviceType: 240 // ANGLE is really the only odd man out 241 fBackend = kANGLE_BackEndType; 242 break; 243 #endif // SK_ANGLE 244 default: 245 SkASSERT(false); 246 break; 247 } 248 AttachmentInfo attachmentInfo; 249 bool result = win->attach(fBackend, backendOptions.fMSAASampleCount, 250 backendOptions.fDeepColor, &attachmentInfo); 251 if (!result) { 252 SkDebugf("Failed to initialize GL"); 253 return; 254 } 255 fMSAASampleCount = backendOptions.fMSAASampleCount; 256 fDeepColor = backendOptions.fDeepColor; 257 // Assume that we have at least 24-bit output, for backends that don't supply this data 258 fActualColorBits = SkTMax(attachmentInfo.fColorBits, 24); 259 260 SkASSERT(nullptr == fCurIntf); 261 switch (win->getDeviceType()) { 262 case kRaster_DeviceType: // fallthrough 263 case kGPU_DeviceType: 264 // all these guys use the native interface 265 fCurIntf = GrGLCreateNativeInterface(); 266 break; 267 #if SK_ANGLE 268 case kANGLE_DeviceType: 269 fCurIntf = sk_gpu_test::CreateANGLEGLInterface(); 270 break; 271 #endif // SK_ANGLE 272 default: 273 SkASSERT(false); 274 break; 275 } 276 277 SkASSERT(nullptr == fCurContext); 278 fCurContext = GrContext::Create(kOpenGL_GrBackend, (GrBackendContext) fCurIntf, 279 backendOptions.fGrContextOptions); 280 281 if (nullptr == fCurContext || nullptr == fCurIntf) { 282 // We need some context and interface to see results 283 SkSafeUnref(fCurContext); 284 SkSafeUnref(fCurIntf); 285 fCurContext = nullptr; 286 fCurIntf = nullptr; 287 SkDebugf("Failed to setup 3D"); 288 289 win->release(); 290 } 291 #endif // SK_SUPPORT_GPU 292 // call windowSizeChanged to create the gpu-backed Surface 293 this->windowSizeChanged(win); 294 } 295 296 void tearDownBackend(SampleWindow *win) override { 297 #if SK_SUPPORT_GPU 298 if (fCurContext) { 299 // in case we have outstanding refs to this guy (lua?) 300 fCurContext->abandonContext(); 301 fCurContext->unref(); 302 fCurContext = nullptr; 303 } 304 305 SkSafeUnref(fCurIntf); 306 fCurIntf = nullptr; 307 308 fGpuSurface = nullptr; 309 #endif 310 win->release(); 311 fBackend = kNone_BackEndType; 312 } 313 314 sk_sp<SkSurface> makeSurface(SampleWindow::DeviceType dType, SampleWindow* win) override { 315 #if SK_SUPPORT_GPU 316 if (IsGpuDeviceType(dType) && fCurContext) { 317 SkSurfaceProps props(win->getSurfaceProps()); 318 if (kRGBA_F16_SkColorType == win->info().colorType() || fActualColorBits > 24) { 319 // If we're rendering to F16, we need an off-screen surface - the current render 320 // target is most likely the wrong format. 321 // 322 // If we're using a deep (10-bit or higher) surface, we probably need an off-screen 323 // surface. 10-bit, in particular, has strange gamma behavior. 324 return SkSurface::MakeRenderTarget(fCurContext, SkBudgeted::kNo, win->info(), 325 fMSAASampleCount, &props); 326 } else { 327 return fGpuSurface; 328 } 329 } 330 #endif 331 return nullptr; 332 } 333 334 void publishCanvas(SampleWindow::DeviceType dType, 335 SkCanvas* renderingCanvas, SampleWindow* win) override { 336 #if SK_SUPPORT_GPU 337 if (!IsGpuDeviceType(dType) || 338 kRGBA_F16_SkColorType == win->info().colorType() || 339 fActualColorBits > 24) { 340 // We made/have an off-screen surface. Extract the pixels exactly as we rendered them: 341 SkImageInfo info = win->info(); 342 size_t rowBytes = info.minRowBytes(); 343 size_t size = info.getSafeSize(rowBytes); 344 auto data = SkData::MakeUninitialized(size); 345 SkASSERT(data); 346 347 if (!renderingCanvas->readPixels(info, data->writable_data(), rowBytes, 0, 0)) { 348 SkDEBUGFAIL("Failed to read canvas pixels"); 349 return; 350 } 351 352 // Now, re-interpret those pixels as sRGB, so they won't be color converted when we 353 // draw then to FBO0. This ensures that if we rendered in any strange gamut, we'll see 354 // the "correct" output (because we generated the pixel values we wanted in the 355 // offscreen canvas). 356 auto colorSpace = kRGBA_F16_SkColorType == info.colorType() 357 ? SkColorSpace::MakeSRGBLinear() 358 : SkColorSpace::MakeSRGB(); 359 auto offscreenImage = SkImage::MakeRasterData(info.makeColorSpace(colorSpace), data, 360 rowBytes); 361 362 SkCanvas* gpuCanvas = fGpuSurface->getCanvas(); 363 364 // With ten-bit output, we need to manually apply the gamma of the output device 365 // (unless we're in non-gamma correct mode, in which case our data is already 366 // fake-sRGB, like we're expected to put in the 10-bit buffer): 367 bool doGamma = (fActualColorBits == 30) && win->info().colorSpace(); 368 369 SkPaint gammaPaint; 370 gammaPaint.setBlendMode(SkBlendMode::kSrc); 371 if (doGamma) { 372 gammaPaint.setColorFilter(sk_tool_utils::MakeLinearToSRGBColorFilter()); 373 } 374 375 gpuCanvas->drawImage(offscreenImage, 0, 0, &gammaPaint); 376 } 377 378 fGpuSurface->prepareForExternalIO(); 379 #endif 380 381 win->present(); 382 } 383 384 void windowSizeChanged(SampleWindow* win) override { 385 #if SK_SUPPORT_GPU 386 if (fCurContext) { 387 AttachmentInfo attachmentInfo; 388 win->attach(fBackend, fMSAASampleCount, fDeepColor, &attachmentInfo); 389 fActualColorBits = SkTMax(attachmentInfo.fColorBits, 24); 390 fGpuSurface = win->makeGpuBackedSurface(attachmentInfo, fCurIntf, fCurContext); 391 } 392 #endif 393 } 394 395 GrContext* getGrContext() override { 396 #if SK_SUPPORT_GPU 397 return fCurContext; 398 #else 399 return nullptr; 400 #endif 401 } 402 403 int numColorSamples() const override { 404 #if SK_SUPPORT_GPU 405 return fMSAASampleCount; 406 #else 407 return 0; 408 #endif 409 } 410 411 int getColorBits() override { 412 #if SK_SUPPORT_GPU 413 return fActualColorBits; 414 #else 415 return 24; 416 #endif 417 } 418 419 private: 420 421 #if SK_SUPPORT_GPU 422 GrContext* fCurContext; 423 const GrGLInterface* fCurIntf; 424 sk_sp<SkSurface> fGpuSurface; 425 int fMSAASampleCount; 426 bool fDeepColor; 427 int fActualColorBits; 428 #endif 429 430 SkOSWindow::SkBackEndTypes fBackend; 431 432 typedef SampleWindow::DeviceManager INHERITED; 433 }; 434 435 /////////////// 436 static const char view_inval_msg[] = "view-inval-msg"; 437 438 void SampleWindow::postInvalDelay() { 439 (new SkEvent(view_inval_msg, this->getSinkID()))->postDelay(1); 440 } 441 442 static bool isInvalEvent(const SkEvent& evt) { 443 return evt.isType(view_inval_msg); 444 } 445 ////////////////// 446 447 #include "GMSampleView.h" 448 449 class AutoUnrefArray { 450 public: 451 AutoUnrefArray() {} 452 ~AutoUnrefArray() { 453 int count = fObjs.count(); 454 for (int i = 0; i < count; ++i) { 455 fObjs[i]->unref(); 456 } 457 } 458 SkRefCnt*& push_back() { return *fObjs.append(); } 459 460 private: 461 SkTDArray<SkRefCnt*> fObjs; 462 }; 463 464 // registers GMs as Samples 465 // This can't be performed during static initialization because it could be 466 // run before GMRegistry has been fully built. 467 static void SkGMRegistyToSampleRegistry() { 468 static bool gOnce; 469 static AutoUnrefArray fRegisters; 470 471 if (!gOnce) { 472 const skiagm::GMRegistry* gmreg = skiagm::GMRegistry::Head(); 473 while (gmreg) { 474 fRegisters.push_back() = new SkViewRegister(gmreg->factory()); 475 gmreg = gmreg->next(); 476 } 477 gOnce = true; 478 } 479 } 480 481 ////////////////////////////////////////////////////////////////////////////// 482 483 enum FlipAxisEnum { 484 kFlipAxis_X = (1 << 0), 485 kFlipAxis_Y = (1 << 1) 486 }; 487 488 #include "SkDrawFilter.h" 489 490 struct HintingState { 491 SkPaint::Hinting hinting; 492 const char* name; 493 const char* label; 494 }; 495 static HintingState gHintingStates[] = { 496 {SkPaint::kNo_Hinting, "Mixed", nullptr }, 497 {SkPaint::kNo_Hinting, "None", "H0 " }, 498 {SkPaint::kSlight_Hinting, "Slight", "Hs " }, 499 {SkPaint::kNormal_Hinting, "Normal", "Hn " }, 500 {SkPaint::kFull_Hinting, "Full", "Hf " }, 501 }; 502 503 struct PixelGeometryState { 504 SkPixelGeometry pixelGeometry; 505 const char* name; 506 const char* label; 507 }; 508 static PixelGeometryState gPixelGeometryStates[] = { 509 {SkPixelGeometry::kUnknown_SkPixelGeometry, "Mixed", nullptr }, 510 {SkPixelGeometry::kUnknown_SkPixelGeometry, "Flat", "{Flat} " }, 511 {SkPixelGeometry::kRGB_H_SkPixelGeometry, "RGB H", "{RGB H} " }, 512 {SkPixelGeometry::kBGR_H_SkPixelGeometry, "BGR H", "{BGR H} " }, 513 {SkPixelGeometry::kRGB_V_SkPixelGeometry, "RGB_V", "{RGB V} " }, 514 {SkPixelGeometry::kBGR_V_SkPixelGeometry, "BGR_V", "{BGR V} " }, 515 }; 516 517 struct FilterQualityState { 518 SkFilterQuality fQuality; 519 const char* fName; 520 const char* fLabel; 521 }; 522 static FilterQualityState gFilterQualityStates[] = { 523 { kNone_SkFilterQuality, "Mixed", nullptr }, 524 { kNone_SkFilterQuality, "None", "F0 " }, 525 { kLow_SkFilterQuality, "Low", "F1 " }, 526 { kMedium_SkFilterQuality, "Medium", "F2 " }, 527 { kHigh_SkFilterQuality, "High", "F3 " }, 528 }; 529 530 class FlagsFilterCanvas : public SkPaintFilterCanvas { 531 public: 532 FlagsFilterCanvas(SkCanvas* canvas, SkOSMenu::TriState lcd, SkOSMenu::TriState aa, 533 SkOSMenu::TriState subpixel, int hinting, int filterQuality) 534 : INHERITED(canvas) 535 , fLCDState(lcd) 536 , fAAState(aa) 537 , fSubpixelState(subpixel) 538 , fHintingState(hinting) 539 , fFilterQualityIndex(filterQuality) { 540 SkASSERT((unsigned)filterQuality < SK_ARRAY_COUNT(gFilterQualityStates)); 541 } 542 543 protected: 544 bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type t) const override { 545 if (!*paint) { 546 return true; 547 } 548 549 if (kText_Type == t && SkOSMenu::kMixedState != fLCDState) { 550 paint->writable()->setLCDRenderText(SkOSMenu::kOnState == fLCDState); 551 } 552 if (SkOSMenu::kMixedState != fAAState) { 553 paint->writable()->setAntiAlias(SkOSMenu::kOnState == fAAState); 554 } 555 if (0 != fFilterQualityIndex) { 556 paint->writable()->setFilterQuality(gFilterQualityStates[fFilterQualityIndex].fQuality); 557 } 558 if (SkOSMenu::kMixedState != fSubpixelState) { 559 paint->writable()->setSubpixelText(SkOSMenu::kOnState == fSubpixelState); 560 } 561 if (0 != fHintingState && fHintingState < (int)SK_ARRAY_COUNT(gHintingStates)) { 562 paint->writable()->setHinting(gHintingStates[fHintingState].hinting); 563 } 564 return true; 565 } 566 567 private: 568 SkOSMenu::TriState fLCDState; 569 SkOSMenu::TriState fAAState; 570 SkOSMenu::TriState fSubpixelState; 571 int fHintingState; 572 int fFilterQualityIndex; 573 574 typedef SkPaintFilterCanvas INHERITED; 575 }; 576 577 /////////////////////////////////////////////////////////////////////////////// 578 579 class SampleTFSerializer : public SkTypefaceSerializer { 580 public: 581 sk_sp<SkData> serialize(SkTypeface* tf) override { 582 tf->ref(); 583 return SkData::MakeWithCopy(&tf, sizeof(tf)); 584 } 585 }; 586 587 class SampleTFDeserializer : public SkTypefaceDeserializer { 588 public: 589 sk_sp<SkTypeface> deserialize(const void* data, size_t size) override { 590 SkASSERT(sizeof(SkTypeface*) == size); 591 SkTypeface* tf; 592 memcpy(&tf, data, size); 593 return sk_sp<SkTypeface>(tf); // this was ref'd in SampleTFSerializer 594 } 595 }; 596 597 /////////////////////////////////////////////////////////////////////////////// 598 599 enum TilingMode { 600 kNo_Tiling, 601 kAbs_128x128_Tiling, 602 kAbs_256x256_Tiling, 603 kRel_4x4_Tiling, 604 kRel_1x16_Tiling, 605 kRel_16x1_Tiling, 606 607 kLast_TilingMode_Enum 608 }; 609 610 struct TilingInfo { 611 const char* label; 612 SkScalar w, h; 613 }; 614 615 static const struct TilingInfo gTilingInfo[] = { 616 { "No tiling", SK_Scalar1 , SK_Scalar1 }, // kNo_Tiling 617 { "128x128" , SkIntToScalar(128), SkIntToScalar(128) }, // kAbs_128x128_Tiling 618 { "256x256" , SkIntToScalar(256), SkIntToScalar(256) }, // kAbs_256x256_Tiling 619 { "1/4x1/4" , SK_Scalar1 / 4 , SK_Scalar1 / 4 }, // kRel_4x4_Tiling 620 { "1/1x1/16" , SK_Scalar1 , SK_Scalar1 / 16 }, // kRel_1x16_Tiling 621 { "1/16x1/1" , SK_Scalar1 / 16 , SK_Scalar1 }, // kRel_16x1_Tiling 622 }; 623 static_assert((SK_ARRAY_COUNT(gTilingInfo) == kLast_TilingMode_Enum), 624 "Incomplete_tiling_labels"); 625 626 SkSize SampleWindow::tileSize() const { 627 SkASSERT((TilingMode)fTilingMode < kLast_TilingMode_Enum); 628 const struct TilingInfo* info = gTilingInfo + fTilingMode; 629 return SkSize::Make(info->w > SK_Scalar1 ? info->w : this->width() * info->w, 630 info->h > SK_Scalar1 ? info->h : this->height() * info->h); 631 } 632 ////////////////////////////////////////////////////////////////////////////// 633 634 static SkView* curr_view(SkWindow* wind) { 635 SkView::F2BIter iter(wind); 636 return iter.next(); 637 } 638 639 static bool curr_title(SkWindow* wind, SkString* title) { 640 SkView* view = curr_view(wind); 641 if (view) { 642 SkEvent evt(gTitleEvtName); 643 if (view->doQuery(&evt)) { 644 title->set(evt.findString(gTitleEvtName)); 645 return true; 646 } 647 } 648 return false; 649 } 650 651 bool SampleWindow::sendAnimatePulse() { 652 SkView* view = curr_view(this); 653 if (SampleView::IsSampleView(view)) { 654 return ((SampleView*)view)->animate(gAnimTimer); 655 } 656 return false; 657 } 658 659 void SampleWindow::setZoomCenter(float x, float y) { 660 fZoomCenterX = x; 661 fZoomCenterY = y; 662 } 663 664 bool SampleWindow::zoomIn() { 665 // Arbitrarily decided 666 if (fFatBitsScale == kMaxFatBitsScale) return false; 667 fFatBitsScale++; 668 this->inval(nullptr); 669 return true; 670 } 671 672 bool SampleWindow::zoomOut() { 673 if (fFatBitsScale == 1) return false; 674 fFatBitsScale--; 675 this->inval(nullptr); 676 return true; 677 } 678 679 void SampleWindow::updatePointer(int x, int y) { 680 fMouseX = x; 681 fMouseY = y; 682 if (fShowZoomer) { 683 this->inval(nullptr); 684 } 685 } 686 687 static inline SampleWindow::DeviceType cycle_devicetype(SampleWindow::DeviceType ct) { 688 static const SampleWindow::DeviceType gCT[] = { 689 SampleWindow::kRaster_DeviceType 690 #if SK_SUPPORT_GPU 691 , SampleWindow::kGPU_DeviceType 692 #if SK_ANGLE 693 , SampleWindow::kANGLE_DeviceType 694 #endif // SK_ANGLE 695 #endif // SK_SUPPORT_GPU 696 }; 697 static_assert(SK_ARRAY_COUNT(gCT) == SampleWindow::kDeviceTypeCnt, "array_size_mismatch"); 698 return gCT[ct]; 699 } 700 701 static SkString getSampleTitle(const SkViewFactory* sampleFactory) { 702 SkView* view = (*sampleFactory)(); 703 SkString title; 704 SampleCode::RequestTitle(view, &title); 705 view->unref(); 706 return title; 707 } 708 709 static bool compareSampleTitle(const SkViewFactory* first, const SkViewFactory* second) { 710 return strcmp(getSampleTitle(first).c_str(), getSampleTitle(second).c_str()) < 0; 711 } 712 713 static int find_by_title(const SkViewFactory* const* factories, int count, const char title[]) { 714 for (int i = 0; i < count; i++) { 715 if (getSampleTitle(factories[i]).equals(title)) { 716 return i; 717 } 718 } 719 return -1; 720 } 721 722 static void restrict_samples(SkTDArray<const SkViewFactory*>& factories, const SkString titles[], 723 int count) { 724 int newCount = 0; 725 for (int i = 0; i < count; ++i) { 726 int index = find_by_title(factories.begin(), factories.count(), titles[i].c_str()); 727 if (index >= 0) { 728 SkTSwap(factories.begin()[newCount], factories.begin()[index]); 729 newCount += 1; 730 } 731 } 732 if (newCount) { 733 factories.setCount(newCount); 734 } 735 } 736 737 DEFINE_string(slide, "", "Start on this sample."); 738 DEFINE_string(pictureDir, "", "Read pictures from here."); 739 DEFINE_string(picture, "", "Path to single picture."); 740 DEFINE_string(pathfinder, "", "SKP file with a single path to isolate."); 741 DEFINE_string(svg, "", "Path to single SVG file."); 742 DEFINE_string(svgDir, "", "Read SVGs from here."); 743 DEFINE_string(sequence, "", "Path to file containing the desired samples/gms to show."); 744 DEFINE_bool(sort, false, "Sort samples by title."); 745 DEFINE_bool(list, false, "List samples?"); 746 DEFINE_bool(startgpu, false, "Start up with gpu?"); 747 DEFINE_bool(redraw, false, "Force continuous redrawing, for profiling or debugging tools."); 748 #ifdef SAMPLE_PDF_FILE_VIEWER 749 DEFINE_string(pdfPath, "", "Path to direcotry of pdf files."); 750 #endif 751 #if SK_SUPPORT_GPU 752 DEFINE_pathrenderer_flag; 753 DEFINE_int32(msaa, 0, "Request multisampling with this count."); 754 DEFINE_bool(deepColor, false, "Request deep color (10-bit/channel or more) display buffer."); 755 #endif 756 757 #include "SkTaskGroup.h" 758 759 SampleWindow::SampleWindow(void* hwnd, int argc, char** argv, DeviceManager* devManager) 760 : INHERITED(hwnd) 761 , fDevManager(nullptr) { 762 763 SkCommandLineFlags::Parse(argc, argv); 764 765 fCurrIndex = -1; 766 767 if (!FLAGS_pictureDir.isEmpty()) { 768 SkOSFile::Iter iter(FLAGS_pictureDir[0], "skp"); 769 SkString filename; 770 while (iter.next(&filename)) { 771 *fSamples.append() = new PictFileFactory( 772 SkOSPath::Join(FLAGS_pictureDir[0], filename.c_str())); 773 } 774 } 775 if (!FLAGS_picture.isEmpty()) { 776 SkString path(FLAGS_picture[0]); 777 fCurrIndex = fSamples.count(); 778 *fSamples.append() = new PictFileFactory(path); 779 } 780 if (!FLAGS_pathfinder.isEmpty()) { 781 SkString path(FLAGS_pathfinder[0]); 782 fCurrIndex = fSamples.count(); 783 *fSamples.append() = new PathFinderFactory(path); 784 } 785 if (!FLAGS_svg.isEmpty()) { 786 SkString path(FLAGS_svg[0]); 787 fCurrIndex = fSamples.count(); 788 *fSamples.append() = new SVGFileFactory(path); 789 } 790 if (!FLAGS_svgDir.isEmpty()) { 791 SkOSFile::Iter iter(FLAGS_svgDir[0], "svg"); 792 SkString filename; 793 while (iter.next(&filename)) { 794 *fSamples.append() = new SVGFileFactory( 795 SkOSPath::Join(FLAGS_svgDir[0], filename.c_str())); 796 } 797 } 798 #ifdef SAMPLE_PDF_FILE_VIEWER 799 if (!FLAGS_pdfPath.isEmpty()) { 800 SkOSFile::Iter iter(FLAGS_pdfPath[0], "pdf"); 801 SkString filename; 802 while (iter.next(&filename)) { 803 *fSamples.append() = new PdfFileViewerFactory( 804 SkOSPath::Join(FLAGS_pictureDir[0], filename.c_str())); 805 } 806 } 807 #endif 808 SkGMRegistyToSampleRegistry(); 809 { 810 const SkViewRegister* reg = SkViewRegister::Head(); 811 while (reg) { 812 *fSamples.append() = reg->factory(); 813 reg = reg->next(); 814 } 815 } 816 817 if (!FLAGS_sequence.isEmpty()) { 818 // The sequence file just contains a list (separated by CRs) of the samples or GM:gms 819 // you want to restrict to. Only these will appear when you cycle through. 820 // If none are found, or the file is empty, then it will be ignored, and all samples 821 // will be available. 822 SkFILEStream stream(FLAGS_sequence[0]); 823 if (stream.isValid()) { 824 size_t len = stream.getLength(); 825 SkAutoTMalloc<char> storage(len + 1); 826 char* buffer = storage.get(); 827 stream.read(buffer, len); 828 buffer[len] = 0; 829 830 SkTArray<SkString> titles; 831 SkStrSplit(buffer, "\n\r", &titles); 832 restrict_samples(fSamples, titles.begin(), titles.count()); 833 } 834 } 835 836 if (FLAGS_sort) { 837 // Sort samples, so foo.skp and foo.pdf are consecutive and we can quickly spot where 838 // skp -> pdf -> png fails. 839 SkTQSort(fSamples.begin(), fSamples.end() ? fSamples.end() - 1 : nullptr, compareSampleTitle); 840 } 841 842 if (!FLAGS_slide.isEmpty()) { 843 fCurrIndex = findByTitle(FLAGS_slide[0]); 844 if (fCurrIndex < 0) { 845 fprintf(stderr, "Unknown sample \"%s\"\n", FLAGS_slide[0]); 846 listTitles(); 847 } 848 } 849 850 #if SK_SUPPORT_GPU 851 fBackendOptions.fGrContextOptions.fGpuPathRenderers = CollectGpuPathRenderersFromFlags(); 852 fBackendOptions.fMSAASampleCount = FLAGS_msaa; 853 fBackendOptions.fDeepColor = FLAGS_deepColor; 854 #endif 855 fColorConfigIndex = 0; 856 857 if (FLAGS_list) { 858 listTitles(); 859 } 860 861 if (fCurrIndex < 0) { 862 SkString title; 863 if (readTitleFromPrefs(&title)) { 864 fCurrIndex = findByTitle(title.c_str()); 865 } 866 } 867 868 if (fCurrIndex < 0) { 869 fCurrIndex = 0; 870 } 871 872 static SkTaskGroup::Enabler enabled(-1); 873 gSampleWindow = this; 874 875 fDeviceType = kRaster_DeviceType; 876 #if SK_SUPPORT_GPU 877 if (FLAGS_startgpu) { 878 fDeviceType = kGPU_DeviceType; 879 } 880 #endif 881 882 #if DEFAULT_TO_GPU 883 fDeviceType = kGPU_DeviceType; 884 #endif 885 #if SK_ANGLE && DEFAULT_TO_ANGLE 886 fDeviceType = kANGLE_DeviceType; 887 #endif 888 889 fUseClip = false; 890 fUsePicture = false; 891 fAnimating = false; 892 fRotate = false; 893 fPerspAnim = false; 894 fRequestGrabImage = false; 895 fTilingMode = kNo_Tiling; 896 fMeasureFPS = false; 897 fUseDeferredCanvas = false; 898 fLCDState = SkOSMenu::kMixedState; 899 fAAState = SkOSMenu::kMixedState; 900 fSubpixelState = SkOSMenu::kMixedState; 901 fHintingState = 0; 902 fPixelGeometryIndex = 0; 903 fFilterQualityIndex = 0; 904 fFlipAxis = 0; 905 906 fMouseX = fMouseY = 0; 907 fFatBitsScale = 8; 908 fTypeface = SkTypeface::MakeFromName("Courier", SkFontStyle(SkFontStyle::kBold_Weight, 909 SkFontStyle::kNormal_Width, 910 SkFontStyle::kUpright_Slant)); 911 fShowZoomer = false; 912 913 fZoomLevel = 0; 914 fZoomScale = SK_Scalar1; 915 fOffset = { 0, 0 }; 916 917 fMagnify = false; 918 919 fSaveToPdf = false; 920 fSaveToSKP = false; 921 922 if (true) { 923 fPipeSerializer.setTypefaceSerializer(new SampleTFSerializer); 924 fPipeDeserializer.setTypefaceDeserializer(new SampleTFDeserializer); 925 } 926 927 int sinkID = this->getSinkID(); 928 fAppMenu = new SkOSMenu; 929 fAppMenu->setTitle("Global Settings"); 930 int itemID; 931 932 itemID = fAppMenu->appendList("ColorType", "ColorType", sinkID, 0, 933 gConfig[0].fName, 934 gConfig[1].fName, 935 gConfig[2].fName, 936 gConfig[3].fName, 937 gConfig[4].fName, 938 nullptr); 939 fAppMenu->assignKeyEquivalentToItem(itemID, 'C'); 940 941 itemID = fAppMenu->appendList("Device Type", "Device Type", sinkID, 0, 942 "Raster", 943 "OpenGL", 944 #if SK_ANGLE 945 "ANGLE", 946 #endif 947 nullptr); 948 fAppMenu->assignKeyEquivalentToItem(itemID, 'd'); 949 itemID = fAppMenu->appendTriState("AA", "AA", sinkID, fAAState); 950 fAppMenu->assignKeyEquivalentToItem(itemID, 'b'); 951 itemID = fAppMenu->appendTriState("LCD", "LCD", sinkID, fLCDState); 952 fAppMenu->assignKeyEquivalentToItem(itemID, 'l'); 953 itemID = fAppMenu->appendList("FilterQuality", "FilterQuality", sinkID, fFilterQualityIndex, 954 gFilterQualityStates[0].fName, 955 gFilterQualityStates[1].fName, 956 gFilterQualityStates[2].fName, 957 gFilterQualityStates[3].fName, 958 gFilterQualityStates[4].fName, 959 nullptr); 960 fAppMenu->assignKeyEquivalentToItem(itemID, 'n'); 961 itemID = fAppMenu->appendTriState("Subpixel", "Subpixel", sinkID, fSubpixelState); 962 fAppMenu->assignKeyEquivalentToItem(itemID, 's'); 963 itemID = fAppMenu->appendList("Hinting", "Hinting", sinkID, fHintingState, 964 gHintingStates[0].name, 965 gHintingStates[1].name, 966 gHintingStates[2].name, 967 gHintingStates[3].name, 968 gHintingStates[4].name, 969 nullptr); 970 fAppMenu->assignKeyEquivalentToItem(itemID, 'h'); 971 972 itemID = fAppMenu->appendList("Pixel Geometry", "Pixel Geometry", sinkID, fPixelGeometryIndex, 973 gPixelGeometryStates[0].name, 974 gPixelGeometryStates[1].name, 975 gPixelGeometryStates[2].name, 976 gPixelGeometryStates[3].name, 977 gPixelGeometryStates[4].name, 978 gPixelGeometryStates[5].name, 979 nullptr); 980 fAppMenu->assignKeyEquivalentToItem(itemID, 'P'); 981 982 itemID =fAppMenu->appendList("Tiling", "Tiling", sinkID, fTilingMode, 983 gTilingInfo[kNo_Tiling].label, 984 gTilingInfo[kAbs_128x128_Tiling].label, 985 gTilingInfo[kAbs_256x256_Tiling].label, 986 gTilingInfo[kRel_4x4_Tiling].label, 987 gTilingInfo[kRel_1x16_Tiling].label, 988 gTilingInfo[kRel_16x1_Tiling].label, 989 nullptr); 990 fAppMenu->assignKeyEquivalentToItem(itemID, 't'); 991 992 itemID = fAppMenu->appendSwitch("Slide Show", "Slide Show" , sinkID, false); 993 fAppMenu->assignKeyEquivalentToItem(itemID, 'a'); 994 itemID = fAppMenu->appendSwitch("Clip", "Clip" , sinkID, fUseClip); 995 fAppMenu->assignKeyEquivalentToItem(itemID, 'c'); 996 itemID = fAppMenu->appendSwitch("Flip X", "Flip X" , sinkID, false); 997 fAppMenu->assignKeyEquivalentToItem(itemID, 'x'); 998 itemID = fAppMenu->appendSwitch("Flip Y", "Flip Y" , sinkID, false); 999 fAppMenu->assignKeyEquivalentToItem(itemID, 'y'); 1000 itemID = fAppMenu->appendSwitch("Zoomer", "Zoomer" , sinkID, fShowZoomer); 1001 fAppMenu->assignKeyEquivalentToItem(itemID, 'z'); 1002 itemID = fAppMenu->appendSwitch("Magnify", "Magnify" , sinkID, fMagnify); 1003 fAppMenu->assignKeyEquivalentToItem(itemID, 'm'); 1004 1005 itemID = fAppMenu->appendAction("Save to PDF", sinkID); 1006 fAppMenu->assignKeyEquivalentToItem(itemID, 'e'); 1007 1008 this->addMenu(fAppMenu); 1009 fSlideMenu = new SkOSMenu; 1010 this->addMenu(fSlideMenu); 1011 1012 this->setVisibleP(true); 1013 this->setClipToBounds(false); 1014 1015 this->loadView((*fSamples[fCurrIndex])()); 1016 1017 if (nullptr == devManager) { 1018 fDevManager = new DefaultDeviceManager(); 1019 } else { 1020 devManager->ref(); 1021 fDevManager = devManager; 1022 } 1023 fDevManager->setUpBackend(this, fBackendOptions); 1024 1025 // If another constructor set our dimensions, ensure that our 1026 // onSizeChange gets called. 1027 if (this->height() && this->width()) { 1028 this->onSizeChange(); 1029 } 1030 1031 // can't call this synchronously, since it may require a subclass to 1032 // to implement, or the caller may need us to have returned from the 1033 // constructor first. Hence we post an event to ourselves. 1034 // this->updateTitle(); 1035 post_event_to_sink(new SkEvent(gUpdateWindowTitleEvtName), this); 1036 1037 gAnimTimer.run(); 1038 } 1039 1040 SampleWindow::~SampleWindow() { 1041 SkSafeUnref(fDevManager); 1042 } 1043 1044 1045 int SampleWindow::findByTitle(const char title[]) { 1046 int i, count = fSamples.count(); 1047 for (i = 0; i < count; i++) { 1048 if (getSampleTitle(i).equals(title)) { 1049 return i; 1050 } 1051 } 1052 return -1; 1053 } 1054 1055 void SampleWindow::listTitles() { 1056 int count = fSamples.count(); 1057 SkDebugf("All Slides:\n"); 1058 for (int i = 0; i < count; i++) { 1059 SkDebugf(" %s\n", getSampleTitle(i).c_str()); 1060 } 1061 } 1062 1063 static SkBitmap capture_bitmap(SkCanvas* canvas) { 1064 SkBitmap bm; 1065 if (bm.tryAllocPixels(canvas->imageInfo())) { 1066 canvas->readPixels(&bm, 0, 0); 1067 } 1068 return bm; 1069 } 1070 1071 static void drawText(SkCanvas* canvas, SkString str, SkScalar left, SkScalar top, SkPaint& paint) { 1072 SkColor desiredColor = paint.getColor(); 1073 paint.setColor(SK_ColorWHITE); 1074 const char* c_str = str.c_str(); 1075 size_t size = str.size(); 1076 SkRect bounds; 1077 paint.measureText(c_str, size, &bounds); 1078 bounds.offset(left, top); 1079 SkScalar inset = SkIntToScalar(-2); 1080 bounds.inset(inset, inset); 1081 canvas->drawRect(bounds, paint); 1082 paint.setColor(desiredColor); 1083 canvas->drawText(c_str, size, left, top, paint); 1084 } 1085 1086 #define XCLIP_N 8 1087 #define YCLIP_N 8 1088 1089 #include "SkDeferredCanvas.h" 1090 #include "SkDumpCanvas.h" 1091 1092 void SampleWindow::draw(SkCanvas* canvas) { 1093 gAnimTimer.updateTime(); 1094 1095 if (fGesture.isActive()) { 1096 this->updateMatrix(); 1097 } 1098 1099 if (fMeasureFPS) { 1100 fMeasureFPS_Time = 0; 1101 } 1102 1103 SkSize tile = this->tileSize(); 1104 1105 if (kNo_Tiling == fTilingMode) { 1106 SkDebugfDumper dumper; 1107 SkDumpCanvas dump(&dumper); 1108 SkDeferredCanvas deferred(canvas, SkDeferredCanvas::kEager); 1109 SkCanvas* c = fUseDeferredCanvas ? &deferred : canvas; 1110 this->INHERITED::draw(c); // no looping or surfaces needed 1111 } else { 1112 const SkScalar w = SkScalarCeilToScalar(tile.width()); 1113 const SkScalar h = SkScalarCeilToScalar(tile.height()); 1114 SkImageInfo info = SkImageInfo::MakeN32Premul(SkScalarTruncToInt(w), SkScalarTruncToInt(h)); 1115 auto surface(canvas->makeSurface(info)); 1116 SkCanvas* tileCanvas = surface->getCanvas(); 1117 1118 for (SkScalar y = 0; y < height(); y += h) { 1119 for (SkScalar x = 0; x < width(); x += w) { 1120 SkAutoCanvasRestore acr(tileCanvas, true); 1121 tileCanvas->translate(-x, -y); 1122 tileCanvas->clear(0); 1123 this->INHERITED::draw(tileCanvas); 1124 surface->draw(canvas, x, y, nullptr); 1125 } 1126 } 1127 1128 // for drawing the borders between tiles 1129 SkPaint paint; 1130 paint.setColor(0x60FF00FF); 1131 paint.setStyle(SkPaint::kStroke_Style); 1132 1133 for (SkScalar y = 0; y < height(); y += tile.height()) { 1134 for (SkScalar x = 0; x < width(); x += tile.width()) { 1135 canvas->drawRect(SkRect::MakeXYWH(x, y, tile.width(), tile.height()), paint); 1136 } 1137 } 1138 } 1139 1140 if (fShowZoomer && !fSaveToPdf) { 1141 showZoomer(canvas); 1142 } 1143 if (fMagnify && !fSaveToPdf) { 1144 magnify(canvas); 1145 } 1146 1147 if (fMeasureFPS && fMeasureFPS_Time) { 1148 this->updateTitle(); 1149 this->postInvalDelay(); 1150 } 1151 1152 if (this->sendAnimatePulse() || FLAGS_redraw) { 1153 this->inval(nullptr); 1154 } 1155 1156 // do this last 1157 fDevManager->publishCanvas(fDeviceType, canvas, this); 1158 } 1159 1160 static float clipW = 200; 1161 static float clipH = 200; 1162 void SampleWindow::magnify(SkCanvas* canvas) { 1163 SkRect r; 1164 int count = canvas->save(); 1165 1166 SkMatrix m = canvas->getTotalMatrix(); 1167 if (!m.invert(&m)) { 1168 return; 1169 } 1170 SkPoint offset, center; 1171 SkScalar mouseX = fMouseX * SK_Scalar1; 1172 SkScalar mouseY = fMouseY * SK_Scalar1; 1173 m.mapXY(mouseX - clipW/2, mouseY - clipH/2, &offset); 1174 m.mapXY(mouseX, mouseY, ¢er); 1175 1176 r.set(0, 0, clipW * m.getScaleX(), clipH * m.getScaleX()); 1177 r.offset(offset.fX, offset.fY); 1178 1179 SkPaint paint; 1180 paint.setColor(0xFF66AAEE); 1181 paint.setStyle(SkPaint::kStroke_Style); 1182 paint.setStrokeWidth(10.f * m.getScaleX()); 1183 //lense offset 1184 //canvas->translate(0, -250); 1185 canvas->drawRect(r, paint); 1186 canvas->clipRect(r); 1187 1188 m = canvas->getTotalMatrix(); 1189 m.setTranslate(-center.fX, -center.fY); 1190 m.postScale(0.5f * fFatBitsScale, 0.5f * fFatBitsScale); 1191 m.postTranslate(center.fX, center.fY); 1192 canvas->concat(m); 1193 1194 this->INHERITED::draw(canvas); 1195 1196 canvas->restoreToCount(count); 1197 } 1198 1199 static SkPaint& set_color_ref(SkPaint& paint, SkColor c) { 1200 paint.setColor(c); 1201 return paint; 1202 } 1203 1204 static void show_lcd_box(SkCanvas* canvas, SkScalar x, SkScalar y, SkColor c, 1205 SkScalar sx, SkScalar sy) { 1206 const SkScalar w = (1 - 1/sx) / 3; 1207 SkPaint paint; 1208 SkRect r = SkRect::MakeXYWH(x, y, w, 1 - 1/sy); 1209 canvas->drawRect(r, set_color_ref(paint, SkColorSetRGB(SkColorGetR(c), 0, 0))); 1210 r.offset(w, 0); 1211 canvas->drawRect(r, set_color_ref(paint, SkColorSetRGB(0, SkColorGetG(c), 0))); 1212 r.offset(w, 0); 1213 canvas->drawRect(r, set_color_ref(paint, SkColorSetRGB(0, 0, SkColorGetB(c)))); 1214 } 1215 1216 static void show_lcd_circle(SkCanvas* canvas, SkScalar x, SkScalar y, SkColor c, 1217 SkScalar, SkScalar) { 1218 const SkRect r = SkRect::MakeXYWH(x, y, 1, 1); 1219 const SkScalar cx = x + 0.5f; 1220 const SkScalar cy = y + 0.5f; 1221 1222 SkPaint paint; 1223 paint.setAntiAlias(true); 1224 1225 SkPath path; 1226 path.addArc(r, 0, 120); path.lineTo(cx, cy); 1227 canvas->drawPath(path, set_color_ref(paint, SkColorSetRGB(SkColorGetR(c), 0, 0))); 1228 1229 path.reset(); path.addArc(r, 120, 120); path.lineTo(cx, cy); 1230 canvas->drawPath(path, set_color_ref(paint, SkColorSetRGB(0, SkColorGetG(c), 0))); 1231 1232 path.reset(); path.addArc(r, 240, 120); path.lineTo(cx, cy); 1233 canvas->drawPath(path, set_color_ref(paint, SkColorSetRGB(0, 0, SkColorGetB(c)))); 1234 } 1235 1236 typedef void (*ShowLCDProc)(SkCanvas*, SkScalar, SkScalar, SkColor, SkScalar, SkScalar); 1237 1238 /* 1239 * Like drawBitmapRect but we manually draw each pixels in RGB 1240 */ 1241 static void show_lcd_grid(SkCanvas* canvas, const SkBitmap& bitmap, 1242 const SkIRect& origSrc, const SkRect& dst, ShowLCDProc proc) { 1243 SkIRect src; 1244 if (!src.intersect(origSrc, bitmap.bounds())) { 1245 return; 1246 } 1247 const SkScalar sx = dst.width() / src.width(); 1248 const SkScalar sy = dst.height() / src.height(); 1249 1250 SkAutoCanvasRestore acr(canvas, true); 1251 canvas->translate(dst.left(), dst.top()); 1252 canvas->scale(sx, sy); 1253 1254 for (int y = 0; y < src.height(); ++y) { 1255 for (int x = 0; x < src.width(); ++x) { 1256 proc(canvas, SkIntToScalar(x), SkIntToScalar(y), 1257 bitmap.getColor(src.left() + x, src.top() + y), sx, sy); 1258 } 1259 } 1260 } 1261 1262 void SampleWindow::showZoomer(SkCanvas* canvas) { 1263 int count = canvas->save(); 1264 canvas->resetMatrix(); 1265 // Ensure the mouse position is on screen. 1266 int width = SkScalarRoundToInt(this->width()); 1267 int height = SkScalarRoundToInt(this->height()); 1268 if (fMouseX >= width) fMouseX = width - 1; 1269 else if (fMouseX < 0) fMouseX = 0; 1270 if (fMouseY >= height) fMouseY = height - 1; 1271 else if (fMouseY < 0) fMouseY = 0; 1272 1273 SkBitmap bitmap = capture_bitmap(canvas); 1274 bitmap.lockPixels(); 1275 1276 // Find the size of the zoomed in view, forced to be odd, so the examined pixel is in the middle. 1277 int zoomedWidth = (width >> 1) | 1; 1278 int zoomedHeight = (height >> 1) | 1; 1279 SkIRect src; 1280 src.set(0, 0, zoomedWidth / fFatBitsScale, zoomedHeight / fFatBitsScale); 1281 src.offset(fMouseX - (src.width()>>1), fMouseY - (src.height()>>1)); 1282 SkRect dest; 1283 dest.set(0, 0, SkIntToScalar(zoomedWidth), SkIntToScalar(zoomedHeight)); 1284 dest.offset(SkIntToScalar(width - zoomedWidth), SkIntToScalar(height - zoomedHeight)); 1285 SkPaint paint; 1286 // Clear the background behind our zoomed in view 1287 paint.setColor(SK_ColorWHITE); 1288 canvas->drawRect(dest, paint); 1289 switch (fFatBitsScale) { 1290 case kMaxFatBitsScale: 1291 show_lcd_grid(canvas, bitmap, src, dest, show_lcd_box); 1292 break; 1293 case kMaxFatBitsScale - 1: 1294 show_lcd_grid(canvas, bitmap, src, dest, show_lcd_circle); 1295 break; 1296 default: 1297 canvas->drawBitmapRect(bitmap, src, dest, nullptr); 1298 break; 1299 } 1300 1301 paint.setColor(SK_ColorBLACK); 1302 paint.setStyle(SkPaint::kStroke_Style); 1303 // Draw a border around the pixel in the middle 1304 SkRect originalPixel; 1305 originalPixel.set(SkIntToScalar(fMouseX), SkIntToScalar(fMouseY), SkIntToScalar(fMouseX + 1), SkIntToScalar(fMouseY + 1)); 1306 SkMatrix matrix; 1307 SkRect scalarSrc; 1308 scalarSrc.set(src); 1309 SkColor color = bitmap.getColor(fMouseX, fMouseY); 1310 if (matrix.setRectToRect(scalarSrc, dest, SkMatrix::kFill_ScaleToFit)) { 1311 SkRect pixel; 1312 matrix.mapRect(&pixel, originalPixel); 1313 // TODO Perhaps measure the values and make the outline white if it's "dark" 1314 if (color == SK_ColorBLACK) { 1315 paint.setColor(SK_ColorWHITE); 1316 } 1317 canvas->drawRect(pixel, paint); 1318 } 1319 paint.setColor(SK_ColorBLACK); 1320 // Draw a border around the destination rectangle 1321 canvas->drawRect(dest, paint); 1322 paint.setStyle(SkPaint::kStrokeAndFill_Style); 1323 // Identify the pixel and its color on screen 1324 paint.setTypeface(fTypeface); 1325 paint.setAntiAlias(true); 1326 paint.setTextSize(18); 1327 SkScalar lineHeight = paint.getFontMetrics(nullptr); 1328 SkString string; 1329 string.appendf("(%i, %i)", fMouseX, fMouseY); 1330 SkScalar left = dest.fLeft + SkIntToScalar(3); 1331 SkScalar i = SK_Scalar1; 1332 drawText(canvas, string, left, lineHeight * i + dest.fTop, paint); 1333 // Alpha 1334 i += SK_Scalar1; 1335 string.reset(); 1336 string.appendf("A: %X", SkColorGetA(color)); 1337 drawText(canvas, string, left, lineHeight * i + dest.fTop, paint); 1338 // Red 1339 i += SK_Scalar1; 1340 string.reset(); 1341 string.appendf("R: %X", SkColorGetR(color)); 1342 paint.setColor(SK_ColorRED); 1343 drawText(canvas, string, left, lineHeight * i + dest.fTop, paint); 1344 // Green 1345 i += SK_Scalar1; 1346 string.reset(); 1347 string.appendf("G: %X", SkColorGetG(color)); 1348 paint.setColor(0xFF008800); 1349 drawText(canvas, string, left, lineHeight * i + dest.fTop, paint); 1350 // Blue 1351 i += SK_Scalar1; 1352 string.reset(); 1353 string.appendf("B: %X", SkColorGetB(color)); 1354 paint.setColor(SK_ColorBLUE); 1355 drawText(canvas, string, left, lineHeight * i + dest.fTop, paint); 1356 canvas->restoreToCount(count); 1357 } 1358 1359 void SampleWindow::onDraw(SkCanvas* canvas) { 1360 } 1361 1362 #include "SkColorPriv.h" 1363 1364 void SampleWindow::saveToPdf() 1365 { 1366 fSaveToPdf = true; 1367 this->inval(nullptr); 1368 } 1369 1370 SkCanvas* SampleWindow::beforeChildren(SkCanvas* canvas) { 1371 if (fSaveToPdf) { 1372 SkString name; 1373 if (!this->getRawTitle(&name)) { 1374 name.set("unknown_sample"); 1375 } 1376 name.append(".pdf"); 1377 #ifdef SK_BUILD_FOR_ANDROID 1378 name.prepend("/sdcard/"); 1379 #endif 1380 fPDFDocument = SkDocument::MakePDF(name.c_str()); 1381 canvas = fPDFDocument->beginPage(this->width(), this->height()); 1382 } else if (fSaveToSKP) { 1383 canvas = fRecorder.beginRecording(9999, 9999, nullptr, 0); 1384 } else if (fUsePicture) { 1385 if (PICTURE_MEANS_PIPE) { 1386 fPipeStream.reset(new SkDynamicMemoryWStream); 1387 canvas = fPipeSerializer.beginWrite(SkRect::MakeWH(this->width(), this->height()), 1388 fPipeStream.get()); 1389 } else { 1390 canvas = fRecorder.beginRecording(9999, 9999, nullptr, 0); 1391 } 1392 } else { 1393 canvas = this->INHERITED::beforeChildren(canvas); 1394 } 1395 1396 if (fUseClip) { 1397 canvas->drawColor(0xFFFF88FF); 1398 canvas->clipPath(fClipPath, kIntersect_SkClipOp, true); 1399 } 1400 1401 // Install a flags filter proxy canvas if needed 1402 if (fLCDState != SkOSMenu::kMixedState || 1403 fAAState != SkOSMenu::kMixedState || 1404 fSubpixelState != SkOSMenu::kMixedState || 1405 fHintingState > 0 || 1406 fFilterQualityIndex > 0) { 1407 canvas = new FlagsFilterCanvas(canvas, fLCDState, fAAState, fSubpixelState, fHintingState, 1408 fFilterQualityIndex); 1409 fFlagsFilterCanvas.reset(canvas); 1410 } 1411 1412 return canvas; 1413 } 1414 #include "SkMultiPictureDraw.h" 1415 void SampleWindow::afterChildren(SkCanvas* orig) { 1416 fFlagsFilterCanvas.reset(nullptr); 1417 1418 if (fSaveToPdf) { 1419 fSaveToPdf = false; 1420 fPDFDocument->endPage(); 1421 fPDFDocument.reset(nullptr); 1422 // We took over the draw calls in order to create the PDF, so we need 1423 // to redraw. 1424 this->inval(nullptr); 1425 return; 1426 } 1427 1428 if (fRequestGrabImage) { 1429 fRequestGrabImage = false; 1430 1431 SkBitmap bmp = capture_bitmap(orig); 1432 if (!bmp.isNull()) { 1433 static int gSampleGrabCounter; 1434 SkString name; 1435 name.printf("sample_grab_%d.png", gSampleGrabCounter++); 1436 sk_tool_utils::EncodeImageToFile(name.c_str(), bmp, 1437 SkEncodedImageFormat::kPNG, 100); 1438 } 1439 this->inval(nullptr); 1440 return; 1441 } 1442 1443 if (fSaveToSKP) { 1444 sk_sp<SkPicture> picture(fRecorder.finishRecordingAsPicture()); 1445 SkFILEWStream stream("sample_app.skp"); 1446 picture->serialize(&stream); 1447 fSaveToSKP = false; 1448 this->inval(nullptr); 1449 return; 1450 } 1451 1452 if (fUsePicture) { 1453 if (PICTURE_MEANS_PIPE) { 1454 fPipeSerializer.endWrite(); 1455 sk_sp<SkData> data(fPipeStream->detachAsData()); 1456 fPipeDeserializer.playback(data->data(), data->size(), orig); 1457 fPipeStream.reset(); 1458 } else { 1459 sk_sp<SkPicture> picture(fRecorder.finishRecordingAsPicture()); 1460 if (SERIALIZE_PICTURE) { 1461 auto data = picture->serialize(); 1462 picture = SkPicture::MakeFromData(data.get(), nullptr); 1463 } 1464 orig->drawPicture(picture.get()); 1465 } 1466 } 1467 1468 // Do this after presentGL and other finishing, rather than in afterChild 1469 if (fMeasureFPS) { 1470 orig->flush(); 1471 fTimer.end(); 1472 fMeasureFPS_Time += fTimer.fWall; 1473 } 1474 } 1475 1476 void SampleWindow::beforeChild(SkView* child, SkCanvas* canvas) { 1477 if (fRotate) { 1478 SkScalar cx = this->width() / 2; 1479 SkScalar cy = this->height() / 2; 1480 canvas->rotate(gAnimTimer.scaled(10), cx, cy); 1481 } 1482 1483 if (fPerspAnim) { 1484 SkScalar secs = gAnimTimer.scaled(1); 1485 1486 static const SkScalar gAnimPeriod = 10 * SK_Scalar1; 1487 static const SkScalar gAnimMag = SK_Scalar1 / 1000; 1488 SkScalar t = SkScalarMod(secs, gAnimPeriod); 1489 if (SkScalarFloorToInt(secs / gAnimPeriod) & 0x1) { 1490 t = gAnimPeriod - t; 1491 } 1492 t = 2 * t - gAnimPeriod; 1493 t *= gAnimMag / gAnimPeriod; 1494 SkMatrix m; 1495 m.reset(); 1496 #if 1 1497 m.setPerspY(t); 1498 #else 1499 m.setPerspY(SK_Scalar1 / 1000); 1500 m.setSkewX(8.0f / 25); 1501 m.dump(); 1502 #endif 1503 canvas->concat(m); 1504 } 1505 1506 if (fMeasureFPS) { 1507 (void)SampleView::SetRepeatDraw(child, FPS_REPEAT_COUNT); 1508 fTimer.start(); 1509 } else { 1510 (void)SampleView::SetRepeatDraw(child, 1); 1511 } 1512 if (fPerspAnim || fRotate) { 1513 this->inval(nullptr); 1514 } 1515 } 1516 1517 void SampleWindow::changeOffset(SkVector delta) { 1518 fOffset += delta; 1519 this->updateMatrix(); 1520 } 1521 1522 void SampleWindow::changeZoomLevel(float delta) { 1523 fZoomLevel += delta; 1524 if (fZoomLevel > 0) { 1525 fZoomLevel = SkMinScalar(fZoomLevel, MAX_ZOOM_LEVEL); 1526 fZoomScale = fZoomLevel + SK_Scalar1; 1527 } else if (fZoomLevel < 0) { 1528 fZoomLevel = SkMaxScalar(fZoomLevel, MIN_ZOOM_LEVEL); 1529 fZoomScale = SK_Scalar1 / (SK_Scalar1 - fZoomLevel); 1530 } else { 1531 fZoomScale = SK_Scalar1; 1532 } 1533 this->updateMatrix(); 1534 } 1535 1536 void SampleWindow::updateMatrix(){ 1537 SkMatrix m; 1538 m.reset(); 1539 1540 if (fZoomLevel) { 1541 SkPoint center; 1542 //m = this->getLocalMatrix();//.invert(&m); 1543 m.mapXY(fZoomCenterX, fZoomCenterY, ¢er); 1544 SkScalar cx = center.fX; 1545 SkScalar cy = center.fY; 1546 1547 m.setTranslate(-cx, -cy); 1548 m.postScale(fZoomScale, fZoomScale); 1549 m.postTranslate(cx, cy); 1550 } 1551 1552 m.postTranslate(fOffset.fX, fOffset.fY); 1553 1554 if (fFlipAxis) { 1555 m.preTranslate(fZoomCenterX, fZoomCenterY); 1556 if (fFlipAxis & kFlipAxis_X) { 1557 m.preScale(-SK_Scalar1, SK_Scalar1); 1558 } 1559 if (fFlipAxis & kFlipAxis_Y) { 1560 m.preScale(SK_Scalar1, -SK_Scalar1); 1561 } 1562 m.preTranslate(-fZoomCenterX, -fZoomCenterY); 1563 //canvas->concat(m); 1564 } 1565 // Apply any gesture matrix 1566 m.preConcat(fGesture.localM()); 1567 m.preConcat(fGesture.globalM()); 1568 1569 this->setLocalMatrix(m); 1570 1571 this->updateTitle(); 1572 this->inval(nullptr); 1573 } 1574 bool SampleWindow::previousSample() { 1575 fCurrIndex = (fCurrIndex - 1 + fSamples.count()) % fSamples.count(); 1576 this->loadView((*fSamples[fCurrIndex])()); 1577 return true; 1578 } 1579 1580 #include "SkResourceCache.h" 1581 #include "SkGlyphCache.h" 1582 bool SampleWindow::nextSample() { 1583 fCurrIndex = (fCurrIndex + 1) % fSamples.count(); 1584 this->loadView((*fSamples[fCurrIndex])()); 1585 1586 if (false) { 1587 SkResourceCache::TestDumpMemoryStatistics(); 1588 SkGlyphCache::Dump(); 1589 SkDebugf("\n"); 1590 } 1591 1592 return true; 1593 } 1594 1595 bool SampleWindow::goToSample(int i) { 1596 fCurrIndex = (i) % fSamples.count(); 1597 this->loadView((*fSamples[fCurrIndex])()); 1598 return true; 1599 } 1600 1601 SkString SampleWindow::getSampleTitle(int i) { 1602 return ::getSampleTitle(fSamples[i]); 1603 } 1604 1605 int SampleWindow::sampleCount() { 1606 return fSamples.count(); 1607 } 1608 1609 void SampleWindow::showOverview() { 1610 this->loadView(create_overview(fSamples.count(), fSamples.begin())); 1611 } 1612 1613 void SampleWindow::postAnimatingEvent() { 1614 if (fAnimating) { 1615 (new SkEvent(ANIMATING_EVENTTYPE, this->getSinkID()))->postDelay(ANIMATING_DELAY); 1616 } 1617 } 1618 1619 static sk_sp<SkColorSpace> getMonitorColorSpace() { 1620 #if defined(SK_BUILD_FOR_MAC) 1621 CGColorSpaceRef cs = CGDisplayCopyColorSpace(CGMainDisplayID()); 1622 CFDataRef dataRef = CGColorSpaceCopyICCProfile(cs); 1623 const uint8_t* data = CFDataGetBytePtr(dataRef); 1624 size_t size = CFDataGetLength(dataRef); 1625 1626 sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeICC(data, size); 1627 1628 CFRelease(cs); 1629 CFRelease(dataRef); 1630 return colorSpace; 1631 #elif defined(SK_BUILD_FOR_WIN) 1632 DISPLAY_DEVICE dd = { sizeof(DISPLAY_DEVICE) }; 1633 1634 // Chrome's code for this currently just gets the primary monitor's profile. This code iterates 1635 // over all attached monitors, so it's "better" in that sense. Making intelligent use of this 1636 // information (via things like MonitorFromWindow or MonitorFromRect to pick the correct 1637 // profile for a particular window or region of a window), is an exercise left to the reader. 1638 for (int i = 0; EnumDisplayDevices(NULL, i, &dd, 0); ++i) { 1639 if (dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) { 1640 // There are other helpful things in dd at this point: 1641 // dd.DeviceString has a longer name for the adapter 1642 // dd.StateFlags indicates primary display, mirroring, etc... 1643 HDC dc = CreateDC(NULL, dd.DeviceName, NULL, NULL); 1644 if (dc) { 1645 char icmPath[MAX_PATH + 1]; 1646 DWORD pathLength = MAX_PATH; 1647 BOOL success = GetICMProfileA(dc, &pathLength, icmPath); 1648 DeleteDC(dc); 1649 if (success) { 1650 sk_sp<SkData> iccData = SkData::MakeFromFileName(icmPath); 1651 return SkColorSpace::MakeICC(iccData->data(), iccData->size()); 1652 } 1653 } 1654 } 1655 } 1656 1657 return nullptr; 1658 #else 1659 return nullptr; 1660 #endif 1661 } 1662 1663 bool SampleWindow::onEvent(const SkEvent& evt) { 1664 if (evt.isType(gUpdateWindowTitleEvtName)) { 1665 this->updateTitle(); 1666 return true; 1667 } 1668 if (evt.isType(ANIMATING_EVENTTYPE)) { 1669 if (fAnimating) { 1670 this->nextSample(); 1671 this->postAnimatingEvent(); 1672 } 1673 return true; 1674 } 1675 if (evt.isType("set-curr-index")) { 1676 this->goToSample(evt.getFast32()); 1677 return true; 1678 } 1679 if (isInvalEvent(evt)) { 1680 this->inval(nullptr); 1681 return true; 1682 } 1683 int selected = -1; 1684 if (SkOSMenu::FindListIndex(evt, "Device Type", &selected)) { 1685 this->setDeviceType((DeviceType)selected); 1686 return true; 1687 } 1688 if (SkOSMenu::FindListIndex(evt, "ColorType", &selected)) { 1689 fColorConfigIndex = selected; 1690 sk_sp<SkColorSpace> colorSpace = nullptr; 1691 switch (gConfig[selected].fColorSpace) { 1692 case kSRGB_OutputColorSpace: 1693 colorSpace = SkColorSpace::MakeSRGB(); 1694 break; 1695 case kNarrow_OutputColorSpace: 1696 { 1697 // NarrowGamut RGB (an artifically smaller than sRGB gamut) 1698 SkColorSpacePrimaries primaries ={ 1699 0.54f, 0.33f, // Rx, Ry 1700 0.33f, 0.50f, // Gx, Gy 1701 0.25f, 0.20f, // Bx, By 1702 0.3127f, 0.3290f, // Wx, Wy 1703 }; 1704 SkMatrix44 narrowGamutRGBMatrix(SkMatrix44::kUninitialized_Constructor); 1705 primaries.toXYZD50(&narrowGamutRGBMatrix); 1706 colorSpace = SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, 1707 narrowGamutRGBMatrix); 1708 } 1709 break; 1710 case kMonitor_OutputColorSpace: 1711 colorSpace = getMonitorColorSpace(); 1712 if (!colorSpace) { 1713 // Fallback for platforms / machines where we can't get a monitor profile 1714 colorSpace = SkColorSpace::MakeSRGB(); 1715 } 1716 break; 1717 case kLegacy_OutputColorSpace: 1718 default: 1719 // Do nothing 1720 break; 1721 } 1722 if (kRGBA_F16_SkColorType == gConfig[selected].fColorType) { 1723 SkASSERT(colorSpace); 1724 SkASSERT(SkColorSpace_Base::Type::kXYZ == as_CSB(colorSpace)->type()); 1725 SkColorSpace_XYZ* csXYZ = static_cast<SkColorSpace_XYZ*>(colorSpace.get()); 1726 colorSpace = csXYZ->makeLinearGamma(); 1727 } 1728 this->setDeviceColorType(gConfig[selected].fColorType, colorSpace); 1729 return true; 1730 } 1731 if (SkOSMenu::FindSwitchState(evt, "Slide Show", nullptr)) { 1732 this->toggleSlideshow(); 1733 return true; 1734 } 1735 if (SkOSMenu::FindTriState(evt, "AA", &fAAState) || 1736 SkOSMenu::FindTriState(evt, "LCD", &fLCDState) || 1737 SkOSMenu::FindListIndex(evt, "FilterQuality", &fFilterQualityIndex) || 1738 SkOSMenu::FindTriState(evt, "Subpixel", &fSubpixelState) || 1739 SkOSMenu::FindListIndex(evt, "Hinting", &fHintingState) || 1740 SkOSMenu::FindSwitchState(evt, "Clip", &fUseClip) || 1741 SkOSMenu::FindSwitchState(evt, "Zoomer", &fShowZoomer) || 1742 SkOSMenu::FindSwitchState(evt, "Magnify", &fMagnify)) 1743 { 1744 this->inval(nullptr); 1745 this->updateTitle(); 1746 return true; 1747 } 1748 if (SkOSMenu::FindListIndex(evt, "Pixel Geometry", &fPixelGeometryIndex)) { 1749 this->setPixelGeometry(fPixelGeometryIndex); 1750 return true; 1751 } 1752 if (SkOSMenu::FindListIndex(evt, "Tiling", &fTilingMode)) { 1753 if (SampleView::IsSampleView(curr_view(this))) { 1754 ((SampleView*)curr_view(this))->onTileSizeChanged(this->tileSize()); 1755 } 1756 this->inval(nullptr); 1757 this->updateTitle(); 1758 return true; 1759 } 1760 if (SkOSMenu::FindSwitchState(evt, "Flip X", nullptr)) { 1761 fFlipAxis ^= kFlipAxis_X; 1762 this->updateMatrix(); 1763 return true; 1764 } 1765 if (SkOSMenu::FindSwitchState(evt, "Flip Y", nullptr)) { 1766 fFlipAxis ^= kFlipAxis_Y; 1767 this->updateMatrix(); 1768 return true; 1769 } 1770 if (SkOSMenu::FindAction(evt,"Save to PDF")) { 1771 this->saveToPdf(); 1772 return true; 1773 } 1774 return this->INHERITED::onEvent(evt); 1775 } 1776 1777 bool SampleWindow::onQuery(SkEvent* query) { 1778 if (query->isType("get-slide-count")) { 1779 query->setFast32(fSamples.count()); 1780 return true; 1781 } 1782 if (query->isType("get-slide-title")) { 1783 SkView* view = (*fSamples[query->getFast32()])(); 1784 SkEvent evt(gTitleEvtName); 1785 if (view->doQuery(&evt)) { 1786 query->setString("title", evt.findString(gTitleEvtName)); 1787 } 1788 SkSafeUnref(view); 1789 return true; 1790 } 1791 if (query->isType("use-fast-text")) { 1792 SkEvent evt(gFastTextEvtName); 1793 return curr_view(this)->doQuery(&evt); 1794 } 1795 if (query->isType("ignore-window-bitmap")) { 1796 query->setFast32(this->getGrContext() != nullptr); 1797 return true; 1798 } 1799 return this->INHERITED::onQuery(query); 1800 } 1801 1802 DECLARE_bool(portableFonts); 1803 1804 bool SampleWindow::onHandleChar(SkUnichar uni) { 1805 { 1806 SkView* view = curr_view(this); 1807 if (view) { 1808 SkEvent evt(gCharEvtName); 1809 evt.setFast32(uni); 1810 if (view->doQuery(&evt)) { 1811 return true; 1812 } 1813 } 1814 } 1815 1816 int dx = 0xFF; 1817 int dy = 0xFF; 1818 1819 switch (uni) { 1820 case '5': dx = 0; dy = 0; break; 1821 case '8': dx = 0; dy = -1; break; 1822 case '6': dx = 1; dy = 0; break; 1823 case '2': dx = 0; dy = 1; break; 1824 case '4': dx = -1; dy = 0; break; 1825 case '7': dx = -1; dy = -1; break; 1826 case '9': dx = 1; dy = -1; break; 1827 case '3': dx = 1; dy = 1; break; 1828 case '1': dx = -1; dy = 1; break; 1829 1830 default: 1831 break; 1832 } 1833 1834 if (0xFF != dx && 0xFF != dy) { 1835 this->changeOffset({SkIntToScalar(dx / 32.0f), SkIntToScalar(dy / 32.0f)}); 1836 return true; 1837 } 1838 1839 switch (uni) { 1840 case 27: // ESC 1841 gAnimTimer.stop(); 1842 if (this->sendAnimatePulse()) { 1843 this->inval(nullptr); 1844 } 1845 break; 1846 case ' ': 1847 gAnimTimer.togglePauseResume(); 1848 if (this->sendAnimatePulse()) { 1849 this->inval(nullptr); 1850 } 1851 break; 1852 case 'A': 1853 if (gSkUseAnalyticAA.load() && !gSkForceAnalyticAA.load()) { 1854 gSkForceAnalyticAA = true; 1855 } else { 1856 gSkUseAnalyticAA = !gSkUseAnalyticAA.load(); 1857 gSkForceAnalyticAA = false; 1858 } 1859 this->inval(nullptr); 1860 this->updateTitle(); 1861 break; 1862 case 'B': 1863 post_event_to_sink(new SkEvent("PictFileView::toggleBBox"), curr_view(this)); 1864 // Cannot call updateTitle() synchronously, because the toggleBBox event is still in 1865 // the queue. 1866 post_event_to_sink(new SkEvent(gUpdateWindowTitleEvtName), this); 1867 this->inval(nullptr); 1868 break; 1869 case 'D': 1870 toggleDistanceFieldFonts(); 1871 break; 1872 case 'E': 1873 fUseDeferredCanvas = !fUseDeferredCanvas; 1874 this->inval(nullptr); 1875 break; 1876 case 'f': 1877 // only 1878 toggleFPS(); 1879 break; 1880 case 'F': 1881 FLAGS_portableFonts ^= true; 1882 this->inval(nullptr); 1883 break; 1884 case 'g': 1885 fRequestGrabImage = true; 1886 this->inval(nullptr); 1887 break; 1888 case 'G': 1889 gShowGMBounds = !gShowGMBounds; 1890 post_event_to_sink(GMSampleView::NewShowSizeEvt(gShowGMBounds), 1891 curr_view(this)); 1892 this->inval(nullptr); 1893 break; 1894 case 'i': 1895 this->zoomIn(); 1896 break; 1897 case 'o': 1898 this->zoomOut(); 1899 break; 1900 case 'r': 1901 fRotate = !fRotate; 1902 this->inval(nullptr); 1903 this->updateTitle(); 1904 return true; 1905 case 'k': 1906 fPerspAnim = !fPerspAnim; 1907 this->inval(nullptr); 1908 this->updateTitle(); 1909 return true; 1910 case 'K': 1911 fSaveToSKP = true; 1912 this->inval(nullptr); 1913 return true; 1914 case 'M': 1915 fUsePicture = !fUsePicture; 1916 this->inval(nullptr); 1917 this->updateTitle(); 1918 return true; 1919 #if SK_SUPPORT_GPU 1920 case 'p': 1921 { 1922 GrContext* grContext = this->getGrContext(); 1923 if (grContext) { 1924 size_t cacheBytes; 1925 grContext->getResourceCacheUsage(nullptr, &cacheBytes); 1926 grContext->freeGpuResources(); 1927 SkDebugf("Purged %d bytes from the GPU resource cache.\n", cacheBytes); 1928 } 1929 } 1930 return true; 1931 #endif 1932 default: 1933 break; 1934 } 1935 1936 if (fAppMenu->handleKeyEquivalent(uni)|| fSlideMenu->handleKeyEquivalent(uni)) { 1937 this->onUpdateMenu(fAppMenu); 1938 this->onUpdateMenu(fSlideMenu); 1939 return true; 1940 } 1941 return this->INHERITED::onHandleChar(uni); 1942 } 1943 1944 void SampleWindow::setDeviceType(DeviceType type) { 1945 if (type == fDeviceType) 1946 return; 1947 1948 fDevManager->tearDownBackend(this); 1949 fDeviceType = type; 1950 fDevManager->setUpBackend(this, fBackendOptions); 1951 1952 this->updateTitle(); 1953 this->inval(nullptr); 1954 } 1955 1956 void SampleWindow::setDeviceColorType(SkColorType ct, sk_sp<SkColorSpace> cs) { 1957 this->setColorType(ct, std::move(cs)); 1958 1959 fDevManager->tearDownBackend(this); 1960 fDevManager->setUpBackend(this, fBackendOptions); 1961 1962 this->updateTitle(); 1963 this->inval(nullptr); 1964 } 1965 1966 void SampleWindow::toggleSlideshow() { 1967 fAnimating = !fAnimating; 1968 this->postAnimatingEvent(); 1969 this->updateTitle(); 1970 } 1971 1972 void SampleWindow::toggleRendering() { 1973 this->setDeviceType(cycle_devicetype(fDeviceType)); 1974 this->updateTitle(); 1975 this->inval(nullptr); 1976 } 1977 1978 void SampleWindow::toggleFPS() { 1979 fMeasureFPS = !fMeasureFPS; 1980 this->updateTitle(); 1981 this->inval(nullptr); 1982 } 1983 1984 void SampleWindow::toggleDistanceFieldFonts() { 1985 // reset backend 1986 fDevManager->tearDownBackend(this); 1987 fDevManager->setUpBackend(this, fBackendOptions); 1988 1989 SkSurfaceProps props = this->getSurfaceProps(); 1990 uint32_t flags = props.flags() ^ SkSurfaceProps::kUseDeviceIndependentFonts_Flag; 1991 this->setSurfaceProps(SkSurfaceProps(flags, props.pixelGeometry())); 1992 1993 this->updateTitle(); 1994 this->inval(nullptr); 1995 } 1996 1997 void SampleWindow::setPixelGeometry(int pixelGeometryIndex) { 1998 // reset backend 1999 fDevManager->tearDownBackend(this); 2000 fDevManager->setUpBackend(this, fBackendOptions); 2001 2002 const SkSurfaceProps& oldProps = this->getSurfaceProps(); 2003 SkSurfaceProps newProps(oldProps.flags(), SkSurfaceProps::kLegacyFontHost_InitType); 2004 if (pixelGeometryIndex > 0) { 2005 newProps = SkSurfaceProps(oldProps.flags(), 2006 gPixelGeometryStates[pixelGeometryIndex].pixelGeometry); 2007 } 2008 this->setSurfaceProps(newProps); 2009 2010 this->updateTitle(); 2011 this->inval(nullptr); 2012 } 2013 2014 #include "SkDumpCanvas.h" 2015 2016 bool SampleWindow::onHandleKey(SkKey key) { 2017 { 2018 SkView* view = curr_view(this); 2019 if (view) { 2020 SkEvent evt(gKeyEvtName); 2021 evt.setFast32(key); 2022 if (view->doQuery(&evt)) { 2023 return true; 2024 } 2025 } 2026 } 2027 2028 int dx = 0xFF; 2029 int dy = 0xFF; 2030 2031 switch (key) { 2032 case kRight_SkKey: 2033 if (this->nextSample()) { 2034 return true; 2035 } 2036 break; 2037 case kLeft_SkKey: 2038 if (this->previousSample()) { 2039 return true; 2040 } 2041 return true; 2042 case kUp_SkKey: 2043 this->changeZoomLevel(1.f / 32.f); 2044 return true; 2045 case kDown_SkKey: 2046 this->changeZoomLevel(-1.f / 32.f); 2047 return true; 2048 case kOK_SkKey: { 2049 SkString title; 2050 if (curr_title(this, &title)) { 2051 writeTitleToPrefs(title.c_str()); 2052 } 2053 return true; 2054 } 2055 case kBack_SkKey: 2056 this->showOverview(); 2057 return true; 2058 2059 case k5_SkKey: dx = 0; dy = 0; break; 2060 case k8_SkKey: dx = 0; dy = -1; break; 2061 case k6_SkKey: dx = 1; dy = 0; break; 2062 case k2_SkKey: dx = 0; dy = 1; break; 2063 case k4_SkKey: dx = -1; dy = 0; break; 2064 case k7_SkKey: dx = -1; dy = -1; break; 2065 case k9_SkKey: dx = 1; dy = -1; break; 2066 case k3_SkKey: dx = 1; dy = 1; break; 2067 case k1_SkKey: dx = -1; dy = 1; break; 2068 2069 default: 2070 break; 2071 } 2072 2073 if (0xFF != dx && 0xFF != dy) { 2074 this->changeOffset({SkIntToScalar(dx / 32.0f), SkIntToScalar(dy / 32.0f)}); 2075 return true; 2076 } 2077 2078 return this->INHERITED::onHandleKey(key); 2079 } 2080 2081 /////////////////////////////////////////////////////////////////////////////// 2082 2083 static const char gGestureClickType[] = "GestureClickType"; 2084 2085 bool SampleWindow::onDispatchClick(int x, int y, Click::State state, 2086 void* owner, unsigned modi) { 2087 if (Click::kMoved_State == state) { 2088 updatePointer(x, y); 2089 } 2090 int w = SkScalarRoundToInt(this->width()); 2091 int h = SkScalarRoundToInt(this->height()); 2092 2093 // check for the resize-box 2094 if (w - x < 16 && h - y < 16) { 2095 return false; // let the OS handle the click 2096 } 2097 else if (fMagnify) { 2098 //it's only necessary to update the drawing if there's a click 2099 this->inval(nullptr); 2100 return false; //prevent dragging while magnify is enabled 2101 } else { 2102 // capture control+option, and trigger debugger 2103 if ((modi & kControl_SkModifierKey) && (modi & kOption_SkModifierKey)) { 2104 if (Click::kDown_State == state) { 2105 SkEvent evt("debug-hit-test"); 2106 evt.setS32("debug-hit-test-x", x); 2107 evt.setS32("debug-hit-test-y", y); 2108 curr_view(this)->doEvent(evt); 2109 } 2110 return true; 2111 } else { 2112 return this->INHERITED::onDispatchClick(x, y, state, owner, modi); 2113 } 2114 } 2115 } 2116 2117 class GestureClick : public SkView::Click { 2118 public: 2119 GestureClick(SkView* target) : SkView::Click(target) { 2120 this->setType(gGestureClickType); 2121 } 2122 2123 static bool IsGesture(Click* click) { 2124 return click->isType(gGestureClickType); 2125 } 2126 }; 2127 2128 SkView::Click* SampleWindow::onFindClickHandler(SkScalar x, SkScalar y, 2129 unsigned modi) { 2130 return new GestureClick(this); 2131 } 2132 2133 bool SampleWindow::onClick(Click* click) { 2134 if (GestureClick::IsGesture(click)) { 2135 float x = static_cast<float>(click->fICurr.fX); 2136 float y = static_cast<float>(click->fICurr.fY); 2137 2138 switch (click->fState) { 2139 case SkView::Click::kDown_State: 2140 fGesture.touchBegin(click->fOwner, x, y); 2141 break; 2142 case SkView::Click::kMoved_State: 2143 fGesture.touchMoved(click->fOwner, x, y); 2144 this->updateMatrix(); 2145 break; 2146 case SkView::Click::kUp_State: 2147 fGesture.touchEnd(click->fOwner); 2148 this->updateMatrix(); 2149 break; 2150 } 2151 return true; 2152 } 2153 return false; 2154 } 2155 2156 /////////////////////////////////////////////////////////////////////////////// 2157 2158 void SampleWindow::loadView(SkView* view) { 2159 SkView::F2BIter iter(this); 2160 SkView* prev = iter.next(); 2161 if (prev) { 2162 prev->detachFromParent(); 2163 } 2164 2165 view->setVisibleP(true); 2166 view->setClipToBounds(false); 2167 this->attachChildToFront(view)->unref(); 2168 view->setSize(this->width(), this->height()); 2169 2170 //repopulate the slide menu when a view is loaded 2171 fSlideMenu->reset(); 2172 2173 this->onUpdateMenu(fSlideMenu); 2174 this->updateTitle(); 2175 } 2176 2177 static const char* gDeviceTypePrefix[] = { 2178 "raster: ", 2179 #if SK_SUPPORT_GPU 2180 "opengl: ", 2181 #if SK_ANGLE 2182 "angle: ", 2183 #endif // SK_ANGLE 2184 #endif // SK_SUPPORT_GPU 2185 }; 2186 static_assert(SK_ARRAY_COUNT(gDeviceTypePrefix) == SampleWindow::kDeviceTypeCnt, 2187 "array_size_mismatch"); 2188 2189 static const char* trystate_str(SkOSMenu::TriState state, 2190 const char trueStr[], const char falseStr[]) { 2191 if (SkOSMenu::kOnState == state) { 2192 return trueStr; 2193 } else if (SkOSMenu::kOffState == state) { 2194 return falseStr; 2195 } 2196 return nullptr; 2197 } 2198 2199 bool SampleWindow::getRawTitle(SkString* title) { 2200 return curr_title(this, title); 2201 } 2202 2203 void SampleWindow::updateTitle() { 2204 SkString title; 2205 if (!this->getRawTitle(&title)) { 2206 title.set("<unknown>"); 2207 } 2208 2209 title.prepend(gDeviceTypePrefix[fDeviceType]); 2210 2211 if (gSkUseAnalyticAA) { 2212 if (gSkForceAnalyticAA) { 2213 title.prepend("<FAAA> "); 2214 } else { 2215 title.prepend("<AAA> "); 2216 } 2217 } 2218 if (fTilingMode != kNo_Tiling) { 2219 title.prependf("<T: %s> ", gTilingInfo[fTilingMode].label); 2220 } 2221 if (fAnimating) { 2222 title.prepend("<A> "); 2223 } 2224 if (fRotate) { 2225 title.prepend("<R> "); 2226 } 2227 if (fPerspAnim) { 2228 title.prepend("<K> "); 2229 } 2230 if (this->getSurfaceProps().flags() & SkSurfaceProps::kUseDeviceIndependentFonts_Flag) { 2231 title.prepend("<DIF> "); 2232 } 2233 if (fUsePicture) { 2234 title.prepend("<P> "); 2235 } 2236 if (fUseDeferredCanvas) { 2237 title.prepend("<E> "); 2238 } 2239 2240 title.prepend(trystate_str(fLCDState, "LCD ", "lcd ")); 2241 title.prepend(trystate_str(fAAState, "AA ", "aa ")); 2242 title.prepend(gFilterQualityStates[fFilterQualityIndex].fLabel); 2243 title.prepend(trystate_str(fSubpixelState, "S ", "s ")); 2244 title.prepend(fFlipAxis & kFlipAxis_X ? "X " : nullptr); 2245 title.prepend(fFlipAxis & kFlipAxis_Y ? "Y " : nullptr); 2246 title.prepend(gHintingStates[fHintingState].label); 2247 title.prepend(gPixelGeometryStates[fPixelGeometryIndex].label); 2248 2249 if (fOffset.fX || fOffset.fY) { 2250 title.prependf("(%.2f, %.2f) ", SkScalarToFloat(fOffset.fX), SkScalarToFloat(fOffset.fY)); 2251 } 2252 if (fZoomLevel) { 2253 title.prependf("{%.2f} ", SkScalarToFloat(fZoomLevel)); 2254 } 2255 2256 if (fMeasureFPS) { 2257 title.appendf(" %8.4f ms", fMeasureFPS_Time / (float)FPS_REPEAT_COUNT); 2258 } 2259 2260 #if SK_SUPPORT_GPU 2261 if (IsGpuDeviceType(fDeviceType) && 2262 fDevManager && 2263 fDevManager->numColorSamples() > 0) { 2264 title.appendf(" [MSAA: %d]", 2265 fDevManager->numColorSamples()); 2266 } 2267 #endif 2268 2269 title.appendf(" %s", gConfig[fColorConfigIndex].fName); 2270 2271 if (fDevManager && fDevManager->getColorBits() > 24) { 2272 title.appendf(" %d bpc", fDevManager->getColorBits()); 2273 } 2274 2275 this->setTitle(title.c_str()); 2276 } 2277 2278 void SampleWindow::onSizeChange() { 2279 this->INHERITED::onSizeChange(); 2280 2281 SkView::F2BIter iter(this); 2282 SkView* view = iter.next(); 2283 view->setSize(this->width(), this->height()); 2284 2285 // rebuild our clippath 2286 { 2287 const SkScalar W = this->width(); 2288 const SkScalar H = this->height(); 2289 2290 fClipPath.reset(); 2291 #if 0 2292 for (SkScalar y = SK_Scalar1; y < H; y += SkIntToScalar(32)) { 2293 SkRect r; 2294 r.set(SK_Scalar1, y, SkIntToScalar(30), y + SkIntToScalar(30)); 2295 for (; r.fLeft < W; r.offset(SkIntToScalar(32), 0)) 2296 fClipPath.addRect(r); 2297 } 2298 #else 2299 SkRect r; 2300 r.set(0, 0, W, H); 2301 fClipPath.addRect(r, SkPath::kCCW_Direction); 2302 r.set(W/4, H/4, W*3/4, H*3/4); 2303 fClipPath.addRect(r, SkPath::kCW_Direction); 2304 #endif 2305 } 2306 2307 fZoomCenterX = SkScalarHalf(this->width()); 2308 fZoomCenterY = SkScalarHalf(this->height()); 2309 2310 #ifdef SK_BUILD_FOR_ANDROID 2311 // FIXME: The first draw after a size change does not work on Android, so 2312 // we post an invalidate. 2313 this->postInvalDelay(); 2314 #endif 2315 this->updateTitle(); // to refresh our config 2316 fDevManager->windowSizeChanged(this); 2317 2318 if (fTilingMode != kNo_Tiling && SampleView::IsSampleView(view)) { 2319 ((SampleView*)view)->onTileSizeChanged(this->tileSize()); 2320 } 2321 } 2322 2323 /////////////////////////////////////////////////////////////////////////////// 2324 2325 template <typename T> void SkTBSort(T array[], int count) { 2326 for (int i = 1; i < count - 1; i++) { 2327 bool didSwap = false; 2328 for (int j = count - 1; j > i; --j) { 2329 if (array[j] < array[j-1]) { 2330 T tmp(array[j-1]); 2331 array[j-1] = array[j]; 2332 array[j] = tmp; 2333 didSwap = true; 2334 } 2335 } 2336 if (!didSwap) { 2337 break; 2338 } 2339 } 2340 2341 for (int k = 0; k < count - 1; k++) { 2342 SkASSERT(!(array[k+1] < array[k])); 2343 } 2344 } 2345 2346 #include "SkRandom.h" 2347 2348 static void rand_rect(SkIRect* rect, SkRandom& rand) { 2349 int bits = 8; 2350 int shift = 32 - bits; 2351 rect->set(rand.nextU() >> shift, rand.nextU() >> shift, 2352 rand.nextU() >> shift, rand.nextU() >> shift); 2353 rect->sort(); 2354 } 2355 2356 static void dumpRect(const SkIRect& r) { 2357 SkDebugf(" { %d, %d, %d, %d },\n", 2358 r.fLeft, r.fTop, 2359 r.fRight, r.fBottom); 2360 } 2361 2362 static void test_rects(const SkIRect rect[], int count) { 2363 SkRegion rgn0, rgn1; 2364 2365 for (int i = 0; i < count; i++) { 2366 rgn0.op(rect[i], SkRegion::kUnion_Op); 2367 // dumpRect(rect[i]); 2368 } 2369 rgn1.setRects(rect, count); 2370 2371 if (rgn0 != rgn1) { 2372 SkDebugf("\n"); 2373 for (int i = 0; i < count; i++) { 2374 dumpRect(rect[i]); 2375 } 2376 SkDebugf("\n"); 2377 } 2378 } 2379 2380 static void test() { 2381 size_t i; 2382 2383 const SkIRect r0[] = { 2384 { 0, 0, 1, 1 }, 2385 { 2, 2, 3, 3 }, 2386 }; 2387 const SkIRect r1[] = { 2388 { 0, 0, 1, 3 }, 2389 { 1, 1, 2, 2 }, 2390 { 2, 0, 3, 3 }, 2391 }; 2392 const SkIRect r2[] = { 2393 { 0, 0, 1, 2 }, 2394 { 2, 1, 3, 3 }, 2395 { 4, 0, 5, 1 }, 2396 { 6, 0, 7, 4 }, 2397 }; 2398 2399 static const struct { 2400 const SkIRect* fRects; 2401 int fCount; 2402 } gRecs[] = { 2403 { r0, SK_ARRAY_COUNT(r0) }, 2404 { r1, SK_ARRAY_COUNT(r1) }, 2405 { r2, SK_ARRAY_COUNT(r2) }, 2406 }; 2407 2408 for (i = 0; i < SK_ARRAY_COUNT(gRecs); i++) { 2409 test_rects(gRecs[i].fRects, gRecs[i].fCount); 2410 } 2411 2412 SkRandom rand; 2413 for (i = 0; i < 10000; i++) { 2414 SkRegion rgn0, rgn1; 2415 2416 const int N = 8; 2417 SkIRect rect[N]; 2418 for (int j = 0; j < N; j++) { 2419 rand_rect(&rect[j], rand); 2420 } 2421 test_rects(rect, N); 2422 } 2423 } 2424 2425 // FIXME: this should be in a header 2426 SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv); 2427 SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv) { 2428 if (false) { // avoid bit rot, suppress warning 2429 test(); 2430 } 2431 return new SampleWindow(hwnd, argc, argv, nullptr); 2432 } 2433 2434 // FIXME: this should be in a header 2435 void get_preferred_size(int* x, int* y, int* width, int* height); 2436 void get_preferred_size(int* x, int* y, int* width, int* height) { 2437 *x = 10; 2438 *y = 50; 2439 *width = 640; 2440 *height = 480; 2441 } 2442 2443 #ifdef SK_BUILD_FOR_IOS 2444 #include "SkApplication.h" 2445 IOS_launch_type set_cmd_line_args(int , char *[], const char* resourceDir) { 2446 SetResourcePath(resourceDir); 2447 return kApplication__iOSLaunchType; 2448 } 2449 #endif 2450 2451 void application_init() { 2452 // setenv("ANDROID_ROOT", "../../../data", 0); 2453 #ifdef SK_BUILD_FOR_MAC 2454 setenv("ANDROID_ROOT", "/android/device/data", 0); 2455 #endif 2456 SkGraphics::Init(); 2457 SkEvent::Init(); 2458 } 2459 2460 void application_term() { 2461 SkEvent::Term(); 2462 } 2463