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