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