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 "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, &center);
   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, &center);
   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