• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
PictFileFactory(const SkString & filename)89     PictFileFactory(const SkString& filename) : fFilename(filename) {}
operator ()() const90     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:
PathFinderFactory(const SkString & filename)100     PathFinderFactory(const SkString& filename) : fFilename(filename) {}
operator ()() const101     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:
SVGFileFactory(const SkString & filename)111     SVGFileFactory(const SkString& filename) : fFilename(filename) {}
operator ()() const112     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:
PdfFileViewerFactory(const SkString & filename)123     PdfFileViewerFactory(const SkString& filename) : fFilename(filename) {}
operator ()() const124     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 
post_event_to_sink(SkEvent * evt,SkEventSink * sink)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 
skip_until(const char * str,const char * skip)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 
skip_past(const char * str,const char * skip)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 
readTitleFromPrefs(SkString * title)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 
writeTitleToPrefs(const char * title)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 
DefaultDeviceManager()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 
~DefaultDeviceManager()220     ~DefaultDeviceManager() override {
221 #if SK_SUPPORT_GPU
222         SkSafeUnref(fCurContext);
223         SkSafeUnref(fCurIntf);
224 #endif
225     }
226 
setUpBackend(SampleWindow * win,const BackendOptions & backendOptions)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 
tearDownBackend(SampleWindow * win)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 
makeSurface(SampleWindow::DeviceType dType,SampleWindow * win)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 
publishCanvas(SampleWindow::DeviceType dType,SkCanvas * renderingCanvas,SampleWindow * win)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 
windowSizeChanged(SampleWindow * win)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 
getGrContext()397     GrContext* getGrContext() override {
398 #if SK_SUPPORT_GPU
399         return fCurContext;
400 #else
401         return nullptr;
402 #endif
403     }
404 
numColorSamples() const405     int numColorSamples() const override {
406 #if SK_SUPPORT_GPU
407         return fMSAASampleCount;
408 #else
409         return 0;
410 #endif
411     }
412 
getColorBits()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 
postInvalDelay()440 void SampleWindow::postInvalDelay() {
441     (new SkEvent(view_inval_msg, this->getSinkID()))->postDelay(1);
442 }
443 
isInvalEvent(const SkEvent & evt)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:
AutoUnrefArray()453     AutoUnrefArray() {}
~AutoUnrefArray()454     ~AutoUnrefArray() {
455         int count = fObjs.count();
456         for (int i = 0; i < count; ++i) {
457             fObjs[i]->unref();
458         }
459     }
push_back()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.
SkGMRegistyToSampleRegistry()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:
FlagsFilterCanvas(SkCanvas * canvas,SkOSMenu::TriState lcd,SkOSMenu::TriState aa,SkOSMenu::TriState subpixel,int hinting,int filterQuality)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:
onFilter(SkTCopyOnFirstWrite<SkPaint> * paint,Type t) const546     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:
serialize(SkTypeface * tf)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:
deserialize(const void * data,size_t size)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 
tileSize() const628 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 
curr_view(SkWindow * wind)636 static SkView* curr_view(SkWindow* wind) {
637     SkView::F2BIter iter(wind);
638     return iter.next();
639 }
640 
curr_title(SkWindow * wind,SkString * title)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 
sendAnimatePulse()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 
setZoomCenter(float x,float y)661 void SampleWindow::setZoomCenter(float x, float y) {
662     fZoomCenterX = x;
663     fZoomCenterY = y;
664 }
665 
zoomIn()666 bool SampleWindow::zoomIn() {
667     // Arbitrarily decided
668     if (fFatBitsScale == kMaxFatBitsScale) return false;
669     fFatBitsScale++;
670     this->inval(nullptr);
671     return true;
672 }
673 
zoomOut()674 bool SampleWindow::zoomOut() {
675     if (fFatBitsScale == 1) return false;
676     fFatBitsScale--;
677     this->inval(nullptr);
678     return true;
679 }
680 
updatePointer(int x,int y)681 void SampleWindow::updatePointer(int x, int y) {
682     fMouseX = x;
683     fMouseY = y;
684     if (fShowZoomer) {
685         this->inval(nullptr);
686     }
687 }
688 
cycle_devicetype(SampleWindow::DeviceType ct)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 
getSampleTitle(const SkViewFactory * sampleFactory)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 
compareSampleTitle(const SkViewFactory * first,const SkViewFactory * second)711 static bool compareSampleTitle(const SkViewFactory* first, const SkViewFactory* second) {
712     return strcmp(getSampleTitle(first).c_str(), getSampleTitle(second).c_str()) < 0;
713 }
714 
find_by_title(const SkViewFactory * const * factories,int count,const char title[])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 
restrict_samples(SkTDArray<const SkViewFactory * > & factories,const SkString titles[],int count)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 
SampleWindow(void * hwnd,int argc,char ** argv,DeviceManager * devManager)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 
~SampleWindow()1042 SampleWindow::~SampleWindow() {
1043     SkSafeUnref(fDevManager);
1044 }
1045 
1046 
findByTitle(const char title[])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 
listTitles()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 
capture_bitmap(SkCanvas * canvas)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 
drawText(SkCanvas * canvas,SkString str,SkScalar left,SkScalar top,SkPaint & paint)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 
draw(SkCanvas * canvas)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;
magnify(SkCanvas * canvas)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 
set_color_ref(SkPaint & paint,SkColor c)1211 static SkPaint& set_color_ref(SkPaint& paint, SkColor c) {
1212     paint.setColor(c);
1213     return paint;
1214 }
1215 
show_lcd_box(SkCanvas * canvas,SkScalar x,SkScalar y,SkColor c,SkScalar sx,SkScalar sy)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 
show_lcd_circle(SkCanvas * canvas,SkScalar x,SkScalar y,SkColor c,SkScalar,SkScalar)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  */
show_lcd_grid(SkCanvas * canvas,const SkBitmap & bitmap,const SkIRect & origSrc,const SkRect & dst,ShowLCDProc proc)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 
showZoomer(SkCanvas * canvas)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 
onDraw(SkCanvas * canvas)1370 void SampleWindow::onDraw(SkCanvas* canvas) {
1371 }
1372 
1373 #include "SkColorPriv.h"
1374 
saveToPdf()1375 void SampleWindow::saveToPdf()
1376 {
1377     fSaveToPdf = true;
1378     this->inval(nullptr);
1379 }
1380 
beforeChildren(SkCanvas * canvas)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"
afterChildren(SkCanvas * orig)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 
beforeChild(SkView * child,SkCanvas * canvas)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 
changeOffset(SkVector delta)1530 void SampleWindow::changeOffset(SkVector delta) {
1531     fOffset += delta;
1532     this->updateMatrix();
1533 }
1534 
changeZoomLevel(float delta)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 
updateMatrix()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 }
previousSample()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"
nextSample()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 
goToSample(int i)1610 bool SampleWindow::goToSample(int i) {
1611     this->resetFPS();
1612     fCurrIndex = (i) % fSamples.count();
1613     this->loadView((*fSamples[fCurrIndex])());
1614     return true;
1615 }
1616 
getSampleTitle(int i)1617 SkString SampleWindow::getSampleTitle(int i) {
1618     return ::getSampleTitle(fSamples[i]);
1619 }
1620 
sampleCount()1621 int SampleWindow::sampleCount() {
1622     return fSamples.count();
1623 }
1624 
showOverview()1625 void SampleWindow::showOverview() {
1626     this->loadView(create_overview(fSamples.count(), fSamples.begin()));
1627 }
1628 
postAnimatingEvent()1629 void SampleWindow::postAnimatingEvent() {
1630     if (fAnimating) {
1631         (new SkEvent(ANIMATING_EVENTTYPE, this->getSinkID()))->postDelay(ANIMATING_DELAY);
1632     }
1633 }
1634 
getMonitorColorSpace()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 
onEvent(const SkEvent & evt)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 
onQuery(SkEvent * query)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 
onHandleChar(SkUnichar uni)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 
setDeviceType(DeviceType type)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 
setDeviceColorType(SkColorType ct,sk_sp<SkColorSpace> cs)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 
toggleSlideshow()2005 void SampleWindow::toggleSlideshow() {
2006     fAnimating = !fAnimating;
2007     this->postAnimatingEvent();
2008     this->updateTitle();
2009 }
2010 
toggleRendering()2011 void SampleWindow::toggleRendering() {
2012     this->setDeviceType(cycle_devicetype(fDeviceType));
2013     this->updateTitle();
2014     this->inval(nullptr);
2015 }
2016 
toggleFPS()2017 void SampleWindow::toggleFPS() {
2018     fMeasureFPS = !fMeasureFPS;
2019     this->updateTitle();
2020     this->inval(nullptr);
2021 }
2022 
resetFPS()2023 void SampleWindow::resetFPS() {
2024     fCumulativeFPS_Time = 0;
2025     fCumulativeFPS_Count = 0;
2026 }
2027 
toggleDistanceFieldFonts()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 
setPixelGeometry(int pixelGeometryIndex)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 
onHandleKey(SkKey key)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 
onDispatchClick(int x,int y,Click::State state,void * owner,unsigned modi)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:
GestureClick(SkView * target)2163     GestureClick(SkView* target) : SkView::Click(target) {
2164         this->setType(gGestureClickType);
2165     }
2166 
IsGesture(Click * click)2167     static bool IsGesture(Click* click) {
2168         return click->isType(gGestureClickType);
2169     }
2170 };
2171 
onFindClickHandler(SkScalar x,SkScalar y,unsigned modi)2172 SkView::Click* SampleWindow::onFindClickHandler(SkScalar x, SkScalar y,
2173                                                 unsigned modi) {
2174     return new GestureClick(this);
2175 }
2176 
onClick(Click * click)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 
loadView(SkView * view)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 
trystate_str(SkOSMenu::TriState state,const char trueStr[],const char falseStr[])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 
getRawTitle(SkString * title)2243 bool SampleWindow::getRawTitle(SkString* title) {
2244     return curr_title(this, title);
2245 }
2246 
updateTitle()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 
onSizeChange()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 
SkTBSort(T array[],int count)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 
rand_rect(SkIRect * rect,SkRandom & rand)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 
dumpRect(const SkIRect & r)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 
test_rects(const SkIRect rect[],int count)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 
test()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);
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);
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"
set_cmd_line_args(int,char * [],const char * resourceDir)2494 IOS_launch_type set_cmd_line_args(int , char *[], const char* resourceDir) {
2495     SetResourcePath(resourceDir);
2496     return kApplication__iOSLaunchType;
2497 }
2498 #endif
2499 
application_init()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 
application_term()2509 void application_term() {
2510     SkEvent::Term();
2511 }
2512