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