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