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