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