SampleApp.cpp revision 59f46b81f8bdd1b524f5cc43bc27603f9604c71a
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
34#include "SkGPipe.h"
35#include "SamplePipeControllers.h"
36
37extern SampleView* CreateSamplePictFileView(const char filename[]);
38
39class PictFileFactory : public SkViewFactory {
40    SkString fFilename;
41public:
42    PictFileFactory(const SkString& filename) : fFilename(filename) {}
43    virtual SkView* operator() () const SK_OVERRIDE {
44        return CreateSamplePictFileView(fFilename.c_str());
45    }
46};
47
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
67#define USE_ARROWS_FOR_ZOOM true
68
69#if SK_ANGLE
70//#define DEFAULT_TO_ANGLE 1
71#else
72//#define DEFAULT_TO_GPU 1
73#endif
74
75extern SkView* create_overview(int, const SkViewFactory*[]);
76extern bool is_overview(SkView* view);
77extern SkView* create_transition(SkView*, SkView*, int);
78extern bool is_transition(SkView* view);
79
80
81#define ANIMATING_EVENTTYPE "nextSample"
82#define ANIMATING_DELAY     750
83
84#ifdef SK_DEBUG
85    #define FPS_REPEAT_MULTIPLIER   1
86#else
87    #define FPS_REPEAT_MULTIPLIER   10
88#endif
89#define FPS_REPEAT_COUNT    (10 * FPS_REPEAT_MULTIPLIER)
90
91static SampleWindow* gSampleWindow;
92
93static void postEventToSink(SkEvent* evt, SkEventSink* sink) {
94    evt->setTargetID(sink->getSinkID())->post();
95}
96
97///////////////////////////////////////////////////////////////////////////////
98
99static const char* skip_until(const char* str, const char* skip) {
100    if (!str) {
101        return NULL;
102    }
103    return strstr(str, skip);
104}
105
106static const char* skip_past(const char* str, const char* skip) {
107    const char* found = skip_until(str, skip);
108    if (!found) {
109        return NULL;
110    }
111    return found + strlen(skip);
112}
113
114static const char* gPrefFileName = "sampleapp_prefs.txt";
115
116static bool readTitleFromPrefs(SkString* title) {
117    SkFILEStream stream(gPrefFileName);
118    if (!stream.isValid()) {
119        return false;
120    }
121
122    int len = stream.getLength();
123    SkString data(len);
124    stream.read(data.writable_str(), len);
125    const char* s = data.c_str();
126
127    s = skip_past(s, "curr-slide-title");
128    s = skip_past(s, "=");
129    s = skip_past(s, "\"");
130    const char* stop = skip_until(s, "\"");
131    if (stop > s) {
132        title->set(s, stop - s);
133        return true;
134    }
135    return false;
136}
137
138static void writeTitleToPrefs(const char* title) {
139    SkFILEWStream stream(gPrefFileName);
140    SkString data;
141    data.printf("curr-slide-title = \"%s\"\n", title);
142    stream.write(data.c_str(), data.size());
143}
144
145///////////////////////////////////////////////////////////////////////////////
146
147class SampleWindow::DefaultDeviceManager : public SampleWindow::DeviceManager {
148public:
149
150    DefaultDeviceManager()
151        : fCurContext(NULL)
152        , fCurIntf(NULL)
153        , fCurRenderTarget(NULL)
154        , fBackend(kNone_BackEndType)
155        , fMSAASampleCount(0) {
156    }
157
158    virtual ~DefaultDeviceManager() {
159        SkSafeUnref(fCurContext);
160        SkSafeUnref(fCurIntf);
161        SkSafeUnref(fCurRenderTarget);
162    }
163
164    virtual void setUpBackend(SampleWindow* win, int msaaSampleCount) {
165        SkASSERT(kNone_BackEndType == fBackend);
166
167        fBackend = kNone_BackEndType;
168
169        switch (win->getDeviceType()) {
170            case kRaster_DeviceType:
171                // fallthrough
172            case kPicture_DeviceType:
173                // fallthrough
174            case kGPU_DeviceType:
175                // fallthrough
176            case kNullGPU_DeviceType:
177                // all these guys use the native backend
178                fBackend = kNativeGL_BackEndType;
179                break;
180#if SK_ANGLE
181            case kANGLE_DeviceType:
182                // ANGLE is really the only odd man out
183                fBackend = kANGLE_BackEndType;
184                break;
185#endif
186            default:
187                SkASSERT(false);
188                break;
189        }
190
191        bool result = win->attach(fBackend, msaaSampleCount);
192        if (!result) {
193            SkDebugf("Failed to initialize GL");
194            return;
195        }
196        fMSAASampleCount = msaaSampleCount;
197
198        SkASSERT(NULL == fCurIntf);
199        switch (win->getDeviceType()) {
200            case kRaster_DeviceType:
201                // fallthrough
202            case kPicture_DeviceType:
203                // fallthrough
204            case kGPU_DeviceType:
205                // all these guys use the native interface
206                fCurIntf = GrGLCreateNativeInterface();
207                break;
208#if SK_ANGLE
209            case kANGLE_DeviceType:
210                fCurIntf = GrGLCreateANGLEInterface();
211                break;
212#endif
213            case kNullGPU_DeviceType:
214                fCurIntf = GrGLCreateNullInterface();
215                break;
216            default:
217                SkASSERT(false);
218                break;
219        }
220
221        SkASSERT(NULL == fCurContext);
222        fCurContext = GrContext::Create(kOpenGL_Shaders_GrEngine,
223                                        (GrPlatform3DContext) fCurIntf);
224
225        if (NULL == fCurContext || NULL == fCurIntf) {
226            // We need some context and interface to see results
227            SkSafeUnref(fCurContext);
228            SkSafeUnref(fCurIntf);
229            SkDebugf("Failed to setup 3D");
230
231            win->detach();
232        }
233
234        // call windowSizeChanged to create the render target
235        windowSizeChanged(win);
236    }
237
238    virtual void tearDownBackend(SampleWindow *win) {
239
240        SkSafeUnref(fCurContext);
241        fCurContext = NULL;
242
243        SkSafeUnref(fCurIntf);
244        fCurIntf = NULL;
245
246        SkSafeUnref(fCurRenderTarget);
247        fCurRenderTarget = NULL;
248
249        win->detach();
250        fBackend = kNone_BackEndType;
251    }
252
253    virtual bool prepareCanvas(SampleWindow::DeviceType dType,
254                               SkCanvas* canvas,
255                               SampleWindow* win) {
256        switch (dType) {
257            case kRaster_DeviceType:
258                // fallthrough
259            case kPicture_DeviceType:
260                // fallthrough
261#if SK_ANGLE
262            case kANGLE_DeviceType:
263#endif
264                break;
265            case kGPU_DeviceType:
266            case kNullGPU_DeviceType:
267                if (fCurContext) {
268                    canvas->setDevice(new SkGpuDevice(fCurContext,
269                                                    fCurRenderTarget))->unref();
270                } else {
271                    return false;
272                }
273                break;
274            default:
275                SkASSERT(false);
276                return false;
277        }
278        return true;
279    }
280
281    virtual void publishCanvas(SampleWindow::DeviceType dType,
282                               SkCanvas* canvas,
283                               SampleWindow* win) {
284        if (fCurContext) {
285            // in case we have queued drawing calls
286            fCurContext->flush();
287
288            if (kGPU_DeviceType != dType && kNullGPU_DeviceType != dType) {
289                // need to send the raster bits to the (gpu) window
290                fCurContext->setRenderTarget(fCurRenderTarget);
291                const SkBitmap& bm = win->getBitmap();
292                fCurRenderTarget->writePixels(0, 0, bm.width(), bm.height(),
293                                             kSkia8888_PM_GrPixelConfig,
294                                             bm.getPixels(),
295                                             bm.rowBytes());
296            }
297        }
298
299        win->present();
300    }
301
302    virtual void windowSizeChanged(SampleWindow* win) {
303
304        if (fCurContext) {
305            win->attach(fBackend, fMSAASampleCount);
306
307            GrPlatformRenderTargetDesc desc;
308            desc.fWidth = SkScalarRound(win->width());
309            desc.fHeight = SkScalarRound(win->height());
310            desc.fConfig = kSkia8888_PM_GrPixelConfig;
311            GR_GL_GetIntegerv(fCurIntf, GR_GL_SAMPLES, &desc.fSampleCnt);
312            GR_GL_GetIntegerv(fCurIntf, GR_GL_STENCIL_BITS, &desc.fStencilBits);
313            GrGLint buffer;
314            GR_GL_GetIntegerv(fCurIntf, GR_GL_FRAMEBUFFER_BINDING, &buffer);
315            desc.fRenderTargetHandle = buffer;
316
317            SkSafeUnref(fCurRenderTarget);
318            fCurRenderTarget = fCurContext->createPlatformRenderTarget(desc);
319        }
320    }
321
322    virtual GrContext* getGrContext() {
323        return fCurContext;
324    }
325
326    virtual GrRenderTarget* getGrRenderTarget() SK_OVERRIDE {
327        return fCurRenderTarget;
328    }
329
330private:
331    GrContext*              fCurContext;
332    const GrGLInterface*    fCurIntf;
333    GrRenderTarget*         fCurRenderTarget;
334
335    SkOSWindow::SkBackEndTypes fBackend;
336    int fMSAASampleCount;
337
338    typedef SampleWindow::DeviceManager INHERITED;
339};
340
341///////////////
342static const char view_inval_msg[] = "view-inval-msg";
343
344void SampleWindow::postInvalDelay() {
345    (new SkEvent(view_inval_msg, this->getSinkID()))->postDelay(1);
346}
347
348static bool isInvalEvent(const SkEvent& evt) {
349    return evt.isType(view_inval_msg);
350}
351//////////////////
352
353SkFuncViewFactory::SkFuncViewFactory(SkViewCreateFunc func)
354    : fCreateFunc(func) {
355}
356
357SkView* SkFuncViewFactory::operator() () const {
358    return (*fCreateFunc)();
359}
360
361#include "GMSampleView.h"
362
363SkGMSampleViewFactory::SkGMSampleViewFactory(GMFactoryFunc func)
364    : fFunc(func) {
365}
366
367SkView* SkGMSampleViewFactory::operator() () const {
368    return new GMSampleView(fFunc(NULL));
369}
370
371SkViewRegister* SkViewRegister::gHead;
372SkViewRegister::SkViewRegister(SkViewFactory* fact) : fFact(fact) {
373    fFact->ref();
374    fChain = gHead;
375    gHead = this;
376}
377
378SkViewRegister::SkViewRegister(SkViewCreateFunc func) {
379    fFact = new SkFuncViewFactory(func);
380    fChain = gHead;
381    gHead = this;
382}
383
384SkViewRegister::SkViewRegister(GMFactoryFunc func) {
385    fFact = new SkGMSampleViewFactory(func);
386    fChain = gHead;
387    gHead = this;
388}
389
390class AutoUnrefArray {
391public:
392    AutoUnrefArray() {}
393    ~AutoUnrefArray() {
394        int count = fObjs.count();
395        for (int i = 0; i < count; ++i) {
396            fObjs[i]->unref();
397        }
398    }
399    SkRefCnt*& push_back() { return *fObjs.append(); }
400
401private:
402    SkTDArray<SkRefCnt*> fObjs;
403};
404
405// registers GMs as Samples
406// This can't be performed during static initialization because it could be
407// run before GMRegistry has been fully built.
408static void SkGMRegistyToSampleRegistry() {
409    static bool gOnce;
410    static AutoUnrefArray fRegisters;
411
412    if (!gOnce) {
413        const skiagm::GMRegistry* gmreg = skiagm::GMRegistry::Head();
414        while (gmreg) {
415            fRegisters.push_back() = new SkViewRegister(gmreg->factory());
416            gmreg = gmreg->next();
417        }
418        gOnce = true;
419    }
420}
421
422#if 0
423#include <CoreFoundation/CoreFoundation.h>
424#include <CoreFoundation/CFURLAccess.h>
425
426static void testpdf() {
427    CFStringRef path = CFStringCreateWithCString(NULL, "/test.pdf",
428                                                 kCFStringEncodingUTF8);
429    CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path,
430                                              kCFURLPOSIXPathStyle,
431                                              false);
432    CFRelease(path);
433    CGRect box = CGRectMake(0, 0, 8*72, 10*72);
434    CGContextRef cg = CGPDFContextCreateWithURL(url, &box, NULL);
435    CFRelease(url);
436
437    CGContextBeginPage(cg, &box);
438    CGRect r = CGRectMake(10, 10, 40 + 0.5, 50 + 0.5);
439    CGContextFillEllipseInRect(cg, r);
440    CGContextEndPage(cg);
441    CGContextRelease(cg);
442
443    if (false) {
444        SkBitmap bm;
445        bm.setConfig(SkBitmap::kA8_Config, 64, 64);
446        bm.allocPixels();
447        bm.eraseColor(0);
448
449        SkCanvas canvas(bm);
450
451    }
452}
453#endif
454
455//////////////////////////////////////////////////////////////////////////////
456
457enum FlipAxisEnum {
458    kFlipAxis_X = (1 << 0),
459    kFlipAxis_Y = (1 << 1)
460};
461
462#include "SkDrawFilter.h"
463
464class FlagsDrawFilter : public SkDrawFilter {
465public:
466    FlagsDrawFilter(SkOSMenu::TriState lcd, SkOSMenu::TriState aa, SkOSMenu::TriState filter,
467                    SkOSMenu::TriState hinting) :
468        fLCDState(lcd), fAAState(aa), fFilterState(filter), fHintingState(hinting) {}
469
470    virtual void filter(SkPaint* paint, Type t) {
471        if (kText_Type == t && SkOSMenu::kMixedState != fLCDState) {
472            paint->setLCDRenderText(SkOSMenu::kOnState == fLCDState);
473        }
474        if (SkOSMenu::kMixedState != fAAState) {
475            paint->setAntiAlias(SkOSMenu::kOnState == fAAState);
476        }
477        if (SkOSMenu::kMixedState != fFilterState) {
478            paint->setFilterBitmap(SkOSMenu::kOnState == fFilterState);
479        }
480        if (SkOSMenu::kMixedState != fHintingState) {
481            paint->setHinting(SkOSMenu::kOnState == fHintingState ?
482                              SkPaint::kNormal_Hinting :
483                              SkPaint::kSlight_Hinting);
484        }
485    }
486
487private:
488    SkOSMenu::TriState  fLCDState;
489    SkOSMenu::TriState  fAAState;
490    SkOSMenu::TriState  fFilterState;
491    SkOSMenu::TriState  fHintingState;
492};
493
494//////////////////////////////////////////////////////////////////////////////
495
496#define MAX_ZOOM_LEVEL  8
497#define MIN_ZOOM_LEVEL  -8
498
499static const char gCharEvtName[] = "SampleCode_Char_Event";
500static const char gKeyEvtName[] = "SampleCode_Key_Event";
501static const char gTitleEvtName[] = "SampleCode_Title_Event";
502static const char gPrefSizeEvtName[] = "SampleCode_PrefSize_Event";
503static const char gFastTextEvtName[] = "SampleCode_FastText_Event";
504static const char gUpdateWindowTitleEvtName[] = "SampleCode_UpdateWindowTitle";
505
506bool SampleCode::CharQ(const SkEvent& evt, SkUnichar* outUni) {
507    if (evt.isType(gCharEvtName, sizeof(gCharEvtName) - 1)) {
508        if (outUni) {
509            *outUni = evt.getFast32();
510        }
511        return true;
512    }
513    return false;
514}
515
516bool SampleCode::KeyQ(const SkEvent& evt, SkKey* outKey) {
517    if (evt.isType(gKeyEvtName, sizeof(gKeyEvtName) - 1)) {
518        if (outKey) {
519            *outKey = (SkKey)evt.getFast32();
520        }
521        return true;
522    }
523    return false;
524}
525
526bool SampleCode::TitleQ(const SkEvent& evt) {
527    return evt.isType(gTitleEvtName, sizeof(gTitleEvtName) - 1);
528}
529
530void SampleCode::TitleR(SkEvent* evt, const char title[]) {
531    SkASSERT(evt && TitleQ(*evt));
532    evt->setString(gTitleEvtName, title);
533}
534
535bool SampleCode::RequestTitle(SkView* view, SkString* title) {
536    SkEvent evt(gTitleEvtName);
537    if (view->doQuery(&evt)) {
538        title->set(evt.findString(gTitleEvtName));
539        return true;
540    }
541    return false;
542}
543
544bool SampleCode::PrefSizeQ(const SkEvent& evt) {
545    return evt.isType(gPrefSizeEvtName, sizeof(gPrefSizeEvtName) - 1);
546}
547
548void SampleCode::PrefSizeR(SkEvent* evt, SkScalar width, SkScalar height) {
549    SkASSERT(evt && PrefSizeQ(*evt));
550    SkScalar size[2];
551    size[0] = width;
552    size[1] = height;
553    evt->setScalars(gPrefSizeEvtName, 2, size);
554}
555
556bool SampleCode::FastTextQ(const SkEvent& evt) {
557    return evt.isType(gFastTextEvtName, sizeof(gFastTextEvtName) - 1);
558}
559
560///////////////////////////////////////////////////////////////////////////////
561
562static SkMSec gAnimTime;
563static SkMSec gAnimTimePrev;
564
565SkMSec SampleCode::GetAnimTime() { return gAnimTime; }
566SkMSec SampleCode::GetAnimTimeDelta() { return gAnimTime - gAnimTimePrev; }
567SkScalar SampleCode::GetAnimSecondsDelta() {
568    return SkDoubleToScalar(GetAnimTimeDelta() / 1000.0);
569}
570
571SkScalar SampleCode::GetAnimScalar(SkScalar speed, SkScalar period) {
572    // since gAnimTime can be up to 32 bits, we can't convert it to a float
573    // or we'll lose the low bits. Hence we use doubles for the intermediate
574    // calculations
575    double seconds = (double)gAnimTime / 1000.0;
576    double value = SkScalarToDouble(speed) * seconds;
577    if (period) {
578        value = ::fmod(value, SkScalarToDouble(period));
579    }
580    return SkDoubleToScalar(value);
581}
582
583SkScalar SampleCode::GetAnimSinScalar(SkScalar amplitude,
584                                      SkScalar periodInSec,
585                                      SkScalar phaseInSec) {
586    if (!periodInSec) {
587        return 0;
588    }
589    double t = (double)gAnimTime / 1000.0 + phaseInSec;
590    t *= SkScalarToFloat(2 * SK_ScalarPI) / periodInSec;
591    amplitude = SK_ScalarHalf * amplitude;
592    return SkScalarMul(amplitude, SkDoubleToScalar(sin(t))) + amplitude;
593}
594
595GrContext* SampleCode::GetGr() {
596    return gSampleWindow ? gSampleWindow->getGrContext() : NULL;
597}
598
599// some GMs rely on having a skiagm::GetGr function defined
600namespace skiagm {
601    // FIXME: this should be moved into a header
602    GrContext* GetGr();
603    GrContext* GetGr() { return SampleCode::GetGr(); }
604}
605
606//////////////////////////////////////////////////////////////////////////////
607
608static SkView* curr_view(SkWindow* wind) {
609    SkView::F2BIter iter(wind);
610    return iter.next();
611}
612
613static bool curr_title(SkWindow* wind, SkString* title) {
614    SkView* view = curr_view(wind);
615    if (view) {
616        SkEvent evt(gTitleEvtName);
617        if (view->doQuery(&evt)) {
618            title->set(evt.findString(gTitleEvtName));
619            return true;
620        }
621    }
622    return false;
623}
624
625void SampleWindow::setZoomCenter(float x, float y)
626{
627    fZoomCenterX = SkFloatToScalar(x);
628    fZoomCenterY = SkFloatToScalar(y);
629}
630
631bool SampleWindow::zoomIn()
632{
633    // Arbitrarily decided
634    if (fFatBitsScale == 25) return false;
635    fFatBitsScale++;
636    this->inval(NULL);
637    return true;
638}
639
640bool SampleWindow::zoomOut()
641{
642    if (fFatBitsScale == 1) return false;
643    fFatBitsScale--;
644    this->inval(NULL);
645    return true;
646}
647
648void SampleWindow::updatePointer(int x, int y)
649{
650    fMouseX = x;
651    fMouseY = y;
652    if (fShowZoomer) {
653        this->inval(NULL);
654    }
655}
656
657static inline SampleWindow::DeviceType cycle_devicetype(SampleWindow::DeviceType ct) {
658    static const SampleWindow::DeviceType gCT[] = {
659        SampleWindow::kPicture_DeviceType,
660        SampleWindow::kGPU_DeviceType,
661#if SK_ANGLE
662        SampleWindow::kANGLE_DeviceType,
663#endif
664        SampleWindow::kRaster_DeviceType, // skip the null gpu device in normal cycling
665        SampleWindow::kRaster_DeviceType
666    };
667    return gCT[ct];
668}
669
670static void usage(const char * argv0) {
671    SkDebugf("%s [--slide sampleName] [-i resourcePath] [--msaa sampleCount] [--pictureDir path]\n", argv0);
672    SkDebugf("    sampleName: sample at which to start.\n");
673    SkDebugf("    resourcePath: directory that stores image resources.\n");
674    SkDebugf("    msaa: request multisampling with the given sample count.\n");
675}
676
677SampleWindow::SampleWindow(void* hwnd, int argc, char** argv, DeviceManager* devManager)
678    : INHERITED(hwnd)
679    , fDevManager(NULL) {
680
681    this->registerPictFileSamples(argv, argc);
682    SkGMRegistyToSampleRegistry();
683    {
684        const SkViewRegister* reg = SkViewRegister::Head();
685        while (reg) {
686            *fSamples.append() = reg->factory();
687            reg = reg->next();
688        }
689    }
690
691    const char* resourcePath = NULL;
692    fCurrIndex = -1;
693    fMSAASampleCount = 0;
694
695    const char* const commandName = argv[0];
696    char* const* stop = argv + argc;
697    for (++argv; argv < stop; ++argv) {
698        if (strcmp(*argv, "-i") == 0) {
699            argv++;
700            if (argv < stop && **argv) {
701                resourcePath = *argv;
702            }
703        } else if (strcmp(*argv, "--slide") == 0) {
704            argv++;
705            if (argv < stop && **argv) {
706                fCurrIndex = findByTitle(*argv);
707                if (fCurrIndex < 0) {
708                    fprintf(stderr, "Unknown sample \"%s\"\n", *argv);
709                    listTitles();
710                }
711            }
712        } else if (strcmp(*argv, "--msaa") == 0) {
713            ++argv;
714            if (argv < stop && **argv) {
715                fMSAASampleCount = atoi(*argv);
716            }
717        } else if (strcmp(*argv, "--list") == 0) {
718            listTitles();
719        }
720        else {
721            usage(commandName);
722        }
723    }
724
725    if (fCurrIndex < 0) {
726        SkString title;
727        if (readTitleFromPrefs(&title)) {
728            fCurrIndex = findByTitle(title.c_str());
729        }
730    }
731
732    if (fCurrIndex < 0) {
733        fCurrIndex = 0;
734    }
735
736    gSampleWindow = this;
737
738#ifdef  PIPE_FILE
739    //Clear existing file or create file if it doesn't exist
740    FILE* f = fopen(FILE_PATH, "wb");
741    fclose(f);
742#endif
743
744    fPicture = NULL;
745
746    fDeviceType = kRaster_DeviceType;
747
748#if DEFAULT_TO_GPU
749    fDeviceType = kGPU_DeviceType;
750#endif
751#if SK_ANGLE && DEFAULT_TO_ANGLE
752    fDeviceType = kANGLE_DeviceType;
753#endif
754
755    fUseClip = false;
756    fNClip = false;
757    fAnimating = false;
758    fRotate = false;
759    fPerspAnim = false;
760    fPerspAnimTime = 0;
761    fScale = false;
762    fRequestGrabImage = false;
763    fPipeState = SkOSMenu::kOffState;
764    fMeasureFPS = false;
765    fLCDState = SkOSMenu::kMixedState;
766    fAAState = SkOSMenu::kMixedState;
767    fFilterState = SkOSMenu::kMixedState;
768    fHintingState = SkOSMenu::kMixedState;
769    fFlipAxis = 0;
770    fScrollTestX = fScrollTestY = 0;
771
772    fMouseX = fMouseY = 0;
773    fFatBitsScale = 8;
774    fTypeface = SkTypeface::CreateFromTypeface(NULL, SkTypeface::kBold);
775    fShowZoomer = false;
776
777    fZoomLevel = 0;
778    fZoomScale = SK_Scalar1;
779
780    fMagnify = false;
781    fDebugger = false;
782
783    fSaveToPdf = false;
784    fPdfCanvas = NULL;
785
786    fTransitionNext = 6;
787    fTransitionPrev = 2;
788
789    int sinkID = this->getSinkID();
790    fAppMenu = new SkOSMenu;
791    fAppMenu->setTitle("Global Settings");
792    int itemID;
793
794    itemID =fAppMenu->appendList("Device Type", "Device Type", sinkID, 0,
795                                "Raster", "Picture", "OpenGL",
796#if SK_ANGLE
797                                "ANGLE",
798#endif
799                                NULL);
800    fAppMenu->assignKeyEquivalentToItem(itemID, 'd');
801    itemID = fAppMenu->appendTriState("AA", "AA", sinkID, fAAState);
802    fAppMenu->assignKeyEquivalentToItem(itemID, 'b');
803    itemID = fAppMenu->appendTriState("LCD", "LCD", sinkID, fLCDState);
804    fAppMenu->assignKeyEquivalentToItem(itemID, 'l');
805    itemID = fAppMenu->appendTriState("Filter", "Filter", sinkID, fFilterState);
806    fAppMenu->assignKeyEquivalentToItem(itemID, 'n');
807    itemID = fAppMenu->appendTriState("Hinting", "Hinting", sinkID, fHintingState);
808    fAppMenu->assignKeyEquivalentToItem(itemID, 'h');
809    fUsePipeMenuItemID = fAppMenu->appendTriState("Pipe", "Pipe" , sinkID,
810                                                  fPipeState);
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        if (fPipeState != SkOSMenu::kOffState) {
1349            fPipeState = SkOSMenu::kOffState;
1350            (void)SampleView::SetUsePipe(curr, fPipeState);
1351            fAppMenu->getItemByID(fUsePipeMenuItemID)->setTriState(fPipeState);
1352            this->onUpdateMenu(fAppMenu);
1353        }
1354
1355        //Reset any transformations
1356        fGesture.stop();
1357        fGesture.reset();
1358
1359        this->loadView(create_debugger(gTempDataStore.begin(),
1360                                       gTempDataStore.count()));
1361    }
1362#endif
1363}
1364
1365void SampleWindow::beforeChild(SkView* child, SkCanvas* canvas) {
1366    if (fScale) {
1367        SkScalar scale = SK_Scalar1 * 7 / 10;
1368        SkScalar cx = this->width() / 2;
1369        SkScalar cy = this->height() / 2;
1370        canvas->translate(cx, cy);
1371        canvas->scale(scale, scale);
1372        canvas->translate(-cx, -cy);
1373    }
1374    if (fRotate) {
1375        SkScalar cx = this->width() / 2;
1376        SkScalar cy = this->height() / 2;
1377        canvas->translate(cx, cy);
1378        canvas->rotate(SkIntToScalar(30));
1379        canvas->translate(-cx, -cy);
1380    }
1381    if (fPerspAnim) {
1382        fPerspAnimTime += SampleCode::GetAnimSecondsDelta();
1383
1384        static const SkScalar gAnimPeriod = 10 * SK_Scalar1;
1385        static const SkScalar gAnimMag = SK_Scalar1 / 1000;
1386        SkScalar t = SkScalarMod(fPerspAnimTime, gAnimPeriod);
1387        if (SkScalarFloorToInt(SkScalarDiv(fPerspAnimTime, gAnimPeriod)) & 0x1) {
1388            t = gAnimPeriod - t;
1389        }
1390        t = 2 * t - gAnimPeriod;
1391        t = SkScalarMul(SkScalarDiv(t, gAnimPeriod), gAnimMag);
1392        SkMatrix m;
1393        m.reset();
1394        m.setPerspY(t);
1395        canvas->concat(m);
1396    }
1397
1398    this->installDrawFilter(canvas);
1399
1400    if (fMeasureFPS) {
1401        fMeasureFPS_Time = 0;   // 0 means the child is not aware of repeat-draw
1402        if (SampleView::SetRepeatDraw(child, FPS_REPEAT_COUNT)) {
1403            fMeasureFPS_Time = SkTime::GetMSecs();
1404        }
1405    } else {
1406        (void)SampleView::SetRepeatDraw(child, 1);
1407    }
1408    if (fPerspAnim) {
1409        this->inval(NULL);
1410    }
1411}
1412
1413void SampleWindow::afterChild(SkView* child, SkCanvas* canvas) {
1414    canvas->setDrawFilter(NULL);
1415}
1416
1417static SkBitmap::Config gConfigCycle[] = {
1418    SkBitmap::kNo_Config,           // none -> none
1419    SkBitmap::kNo_Config,           // a1 -> none
1420    SkBitmap::kNo_Config,           // a8 -> none
1421    SkBitmap::kNo_Config,           // index8 -> none
1422    SkBitmap::kARGB_4444_Config,    // 565 -> 4444
1423    SkBitmap::kARGB_8888_Config,    // 4444 -> 8888
1424    SkBitmap::kRGB_565_Config       // 8888 -> 565
1425};
1426
1427static SkBitmap::Config cycle_configs(SkBitmap::Config c) {
1428    return gConfigCycle[c];
1429}
1430
1431void SampleWindow::changeZoomLevel(float delta) {
1432    fZoomLevel += SkFloatToScalar(delta);
1433    if (fZoomLevel > 0) {
1434        fZoomLevel = SkMinScalar(fZoomLevel, MAX_ZOOM_LEVEL);
1435        fZoomScale = fZoomLevel + SK_Scalar1;
1436    } else if (fZoomLevel < 0) {
1437        fZoomLevel = SkMaxScalar(fZoomLevel, MIN_ZOOM_LEVEL);
1438        fZoomScale = SK_Scalar1 / (SK_Scalar1 - fZoomLevel);
1439    } else {
1440        fZoomScale = SK_Scalar1;
1441    }
1442    this->updateMatrix();
1443}
1444
1445void SampleWindow::updateMatrix(){
1446    SkMatrix m;
1447    m.reset();
1448    if (fZoomLevel) {
1449        SkPoint center;
1450        //m = this->getLocalMatrix();//.invert(&m);
1451        m.mapXY(fZoomCenterX, fZoomCenterY, &center);
1452        SkScalar cx = center.fX;
1453        SkScalar cy = center.fY;
1454
1455        m.setTranslate(-cx, -cy);
1456        m.postScale(fZoomScale, fZoomScale);
1457        m.postTranslate(cx, cy);
1458    }
1459
1460    if (fFlipAxis) {
1461        m.preTranslate(fZoomCenterX, fZoomCenterY);
1462        if (fFlipAxis & kFlipAxis_X) {
1463            m.preScale(-SK_Scalar1, SK_Scalar1);
1464        }
1465        if (fFlipAxis & kFlipAxis_Y) {
1466            m.preScale(SK_Scalar1, -SK_Scalar1);
1467        }
1468        m.preTranslate(-fZoomCenterX, -fZoomCenterY);
1469        //canvas->concat(m);
1470    }
1471    // Apply any gesture matrix
1472    m.preConcat(fGesture.localM());
1473    m.preConcat(fGesture.globalM());
1474
1475    this->setLocalMatrix(m);
1476
1477    this->updateTitle();
1478    this->inval(NULL);
1479}
1480bool SampleWindow::previousSample() {
1481    fCurrIndex = (fCurrIndex - 1 + fSamples.count()) % fSamples.count();
1482    this->loadView(create_transition(curr_view(this), (*fSamples[fCurrIndex])(),
1483                                     fTransitionPrev));
1484    return true;
1485}
1486
1487bool SampleWindow::nextSample() {
1488    fCurrIndex = (fCurrIndex + 1) % fSamples.count();
1489    this->loadView(create_transition(curr_view(this), (*fSamples[fCurrIndex])(),
1490                                     fTransitionNext));
1491    return true;
1492}
1493
1494bool SampleWindow::goToSample(int i) {
1495    fCurrIndex = (i) % fSamples.count();
1496    this->loadView(create_transition(curr_view(this),(*fSamples[fCurrIndex])(), 6));
1497    return true;
1498}
1499
1500SkString SampleWindow::getSampleTitle(int i) {
1501    SkView* view = (*fSamples[i])();
1502    SkString title;
1503    SampleCode::RequestTitle(view, &title);
1504    view->unref();
1505    return title;
1506}
1507
1508int SampleWindow::sampleCount() {
1509    return fSamples.count();
1510}
1511
1512void SampleWindow::showOverview() {
1513    this->loadView(create_transition(curr_view(this),
1514                                     create_overview(fSamples.count(), fSamples.begin()),
1515                                     4));
1516}
1517
1518void SampleWindow::installDrawFilter(SkCanvas* canvas) {
1519    canvas->setDrawFilter(new FlagsDrawFilter(fLCDState, fAAState,
1520                                              fFilterState, fHintingState))->unref();
1521}
1522
1523void SampleWindow::postAnimatingEvent() {
1524    if (fAnimating) {
1525        (new SkEvent(ANIMATING_EVENTTYPE, this->getSinkID()))->postDelay(ANIMATING_DELAY);
1526    }
1527}
1528
1529bool SampleWindow::onEvent(const SkEvent& evt) {
1530    if (evt.isType(gUpdateWindowTitleEvtName)) {
1531        this->updateTitle();
1532        return true;
1533    }
1534    if (evt.isType(ANIMATING_EVENTTYPE)) {
1535        if (fAnimating) {
1536            this->nextSample();
1537            this->postAnimatingEvent();
1538        }
1539        return true;
1540    }
1541    if (evt.isType("replace-transition-view")) {
1542        this->loadView((SkView*)SkEventSink::FindSink(evt.getFast32()));
1543        return true;
1544    }
1545    if (evt.isType("set-curr-index")) {
1546        this->goToSample(evt.getFast32());
1547        return true;
1548    }
1549    if (isInvalEvent(evt)) {
1550        this->inval(NULL);
1551        return true;
1552    }
1553    int selected = -1;
1554    if (SkOSMenu::FindListIndex(evt, "Device Type", &selected)) {
1555        this->setDeviceType((DeviceType)selected);
1556        return true;
1557    }
1558    if (SkOSMenu::FindTriState(evt, "Pipe", &fPipeState)) {
1559#ifdef PIPE_NET
1560        if (!fPipeState != SkOSMenu::kOnState)
1561            gServer.disconnectAll();
1562#endif
1563        (void)SampleView::SetUsePipe(curr_view(this), fPipeState);
1564        this->updateTitle();
1565        this->inval(NULL);
1566        return true;
1567    }
1568    if (SkOSMenu::FindSwitchState(evt, "Slide Show", NULL)) {
1569        this->toggleSlideshow();
1570        return true;
1571    }
1572    if (SkOSMenu::FindTriState(evt, "AA", &fAAState) ||
1573        SkOSMenu::FindTriState(evt, "LCD", &fLCDState) ||
1574        SkOSMenu::FindTriState(evt, "Filter", &fFilterState) ||
1575        SkOSMenu::FindTriState(evt, "Hinting", &fHintingState) ||
1576        SkOSMenu::FindSwitchState(evt, "Clip", &fUseClip) ||
1577        SkOSMenu::FindSwitchState(evt, "Zoomer", &fShowZoomer) ||
1578        SkOSMenu::FindSwitchState(evt, "Magnify", &fMagnify) ||
1579        SkOSMenu::FindListIndex(evt, "Transition-Next", &fTransitionNext) ||
1580        SkOSMenu::FindListIndex(evt, "Transition-Prev", &fTransitionPrev)) {
1581        this->inval(NULL);
1582        this->updateTitle();
1583        return true;
1584    }
1585    if (SkOSMenu::FindSwitchState(evt, "Flip X", NULL)) {
1586        fFlipAxis ^= kFlipAxis_X;
1587        this->updateMatrix();
1588        return true;
1589    }
1590    if (SkOSMenu::FindSwitchState(evt, "Flip Y", NULL)) {
1591        fFlipAxis ^= kFlipAxis_Y;
1592        this->updateMatrix();
1593        return true;
1594    }
1595    if (SkOSMenu::FindAction(evt,"Save to PDF")) {
1596        this->saveToPdf();
1597        return true;
1598    }
1599#ifdef DEBUGGER
1600    if (SkOSMenu::FindSwitchState(evt, "Debugger", &fDebugger)) {
1601        if (fDebugger) {
1602            fPipeState = SkOSMenu::kOnState;
1603            (void)SampleView::SetUsePipe(curr_view(this), fPipeState);
1604        } else {
1605            this->loadView((*fSamples[fCurrIndex])());
1606        }
1607        this->inval(NULL);
1608        return true;
1609    }
1610#endif
1611    return this->INHERITED::onEvent(evt);
1612}
1613
1614bool SampleWindow::onQuery(SkEvent* query) {
1615    if (query->isType("get-slide-count")) {
1616        query->setFast32(fSamples.count());
1617        return true;
1618    }
1619    if (query->isType("get-slide-title")) {
1620        SkView* view = (*fSamples[query->getFast32()])();
1621        SkEvent evt(gTitleEvtName);
1622        if (view->doQuery(&evt)) {
1623            query->setString("title", evt.findString(gTitleEvtName));
1624        }
1625        SkSafeUnref(view);
1626        return true;
1627    }
1628    if (query->isType("use-fast-text")) {
1629        SkEvent evt(gFastTextEvtName);
1630        return curr_view(this)->doQuery(&evt);
1631    }
1632    if (query->isType("ignore-window-bitmap")) {
1633        query->setFast32(this->getGrContext() != NULL);
1634        return true;
1635    }
1636    return this->INHERITED::onQuery(query);
1637}
1638
1639#if 0 // UNUSED
1640static void cleanup_for_filename(SkString* name) {
1641    char* str = name->writable_str();
1642    for (size_t i = 0; i < name->size(); i++) {
1643        switch (str[i]) {
1644            case ':': str[i] = '-'; break;
1645            case '/': str[i] = '-'; break;
1646            case ' ': str[i] = '_'; break;
1647            default: break;
1648        }
1649    }
1650}
1651#endif
1652
1653bool SampleWindow::onHandleChar(SkUnichar uni) {
1654    {
1655        SkView* view = curr_view(this);
1656        if (view) {
1657            SkEvent evt(gCharEvtName);
1658            evt.setFast32(uni);
1659            if (view->doQuery(&evt)) {
1660                return true;
1661            }
1662        }
1663    }
1664
1665    int dx = 0xFF;
1666    int dy = 0xFF;
1667
1668    switch (uni) {
1669        case '5': dx =  0; dy =  0; break;
1670        case '8': dx =  0; dy = -1; break;
1671        case '6': dx =  1; dy =  0; break;
1672        case '2': dx =  0; dy =  1; break;
1673        case '4': dx = -1; dy =  0; break;
1674        case '7': dx = -1; dy = -1; break;
1675        case '9': dx =  1; dy = -1; break;
1676        case '3': dx =  1; dy =  1; break;
1677        case '1': dx = -1; dy =  1; break;
1678
1679        default:
1680            break;
1681    }
1682
1683    if (0xFF != dx && 0xFF != dy) {
1684        if ((dx | dy) == 0) {
1685            fScrollTestX = fScrollTestY = 0;
1686        } else {
1687            fScrollTestX += dx;
1688            fScrollTestY += dy;
1689        }
1690        this->inval(NULL);
1691        return true;
1692    }
1693
1694    switch (uni) {
1695        case 'f':
1696            // only
1697            toggleFPS();
1698            break;
1699        case 'g':
1700            fRequestGrabImage = true;
1701            this->inval(NULL);
1702            break;
1703        case 'i':
1704            this->zoomIn();
1705            break;
1706        case 'o':
1707            this->zoomOut();
1708            break;
1709        case 'r':
1710            fRotate = !fRotate;
1711            this->inval(NULL);
1712            this->updateTitle();
1713            return true;
1714        case 'k':
1715            fPerspAnim = !fPerspAnim;
1716            this->inval(NULL);
1717            this->updateTitle();
1718            return true;
1719        case '\\':
1720            this->setDeviceType(kNullGPU_DeviceType);
1721            this->inval(NULL);
1722            this->updateTitle();
1723            return true;
1724        case 'p':
1725            {
1726                GrContext* grContext = this->getGrContext();
1727                if (grContext) {
1728                    size_t cacheBytes = grContext->getGpuTextureCacheBytes();
1729                    grContext->freeGpuResources();
1730                    SkDebugf("Purged %d bytes from the GPU resource cache.\n",
1731                             cacheBytes);
1732                }
1733            }
1734            return true;
1735        case 's':
1736            fScale = !fScale;
1737            this->inval(NULL);
1738            this->updateTitle();
1739            return true;
1740        default:
1741            break;
1742    }
1743
1744    if (fAppMenu->handleKeyEquivalent(uni)|| fSlideMenu->handleKeyEquivalent(uni)) {
1745        this->onUpdateMenu(fAppMenu);
1746        this->onUpdateMenu(fSlideMenu);
1747        return true;
1748    }
1749    return this->INHERITED::onHandleChar(uni);
1750}
1751
1752void SampleWindow::setDeviceType(DeviceType type) {
1753    if (type == fDeviceType)
1754        return;
1755
1756    fDevManager->tearDownBackend(this);
1757
1758    fDeviceType = type;
1759
1760    fDevManager->setUpBackend(this, fMSAASampleCount);
1761
1762    this->updateTitle();
1763    this->inval(NULL);
1764}
1765
1766void SampleWindow::toggleSlideshow() {
1767    fAnimating = !fAnimating;
1768    this->postAnimatingEvent();
1769    this->updateTitle();
1770}
1771
1772void SampleWindow::toggleRendering() {
1773    this->setDeviceType(cycle_devicetype(fDeviceType));
1774    this->updateTitle();
1775    this->inval(NULL);
1776}
1777
1778void SampleWindow::toggleFPS() {
1779    fMeasureFPS = !fMeasureFPS;
1780    this->updateTitle();
1781    this->inval(NULL);
1782}
1783
1784#include "SkDumpCanvas.h"
1785
1786bool SampleWindow::onHandleKey(SkKey key) {
1787    {
1788        SkView* view = curr_view(this);
1789        if (view) {
1790            SkEvent evt(gKeyEvtName);
1791            evt.setFast32(key);
1792            if (view->doQuery(&evt)) {
1793                return true;
1794            }
1795        }
1796    }
1797    switch (key) {
1798        case kRight_SkKey:
1799            if (this->nextSample()) {
1800                return true;
1801            }
1802            break;
1803        case kLeft_SkKey:
1804            toggleRendering();
1805            return true;
1806        case kUp_SkKey:
1807            if (USE_ARROWS_FOR_ZOOM) {
1808                this->changeZoomLevel(1.f);
1809            } else {
1810                fNClip = !fNClip;
1811                this->inval(NULL);
1812                this->updateTitle();
1813            }
1814            return true;
1815        case kDown_SkKey:
1816            if (USE_ARROWS_FOR_ZOOM) {
1817                this->changeZoomLevel(-1.f);
1818            } else {
1819                this->setConfig(cycle_configs(this->getBitmap().config()));
1820                this->updateTitle();
1821            }
1822            return true;
1823        case kOK_SkKey: {
1824            SkString title;
1825            if (curr_title(this, &title)) {
1826                writeTitleToPrefs(title.c_str());
1827            }
1828            return true;
1829        }
1830        case kBack_SkKey:
1831            this->showOverview();
1832            return true;
1833        default:
1834            break;
1835    }
1836    return this->INHERITED::onHandleKey(key);
1837}
1838
1839///////////////////////////////////////////////////////////////////////////////
1840
1841static const char gGestureClickType[] = "GestureClickType";
1842
1843bool SampleWindow::onDispatchClick(int x, int y, Click::State state,
1844        void* owner) {
1845    if (Click::kMoved_State == state) {
1846        updatePointer(x, y);
1847    }
1848    int w = SkScalarRound(this->width());
1849    int h = SkScalarRound(this->height());
1850
1851    // check for the resize-box
1852    if (w - x < 16 && h - y < 16) {
1853        return false;   // let the OS handle the click
1854    }
1855    else if (fMagnify) {
1856        //it's only necessary to update the drawing if there's a click
1857        this->inval(NULL);
1858        return false; //prevent dragging while magnify is enabled
1859    }
1860    else {
1861        return this->INHERITED::onDispatchClick(x, y, state, owner);
1862    }
1863}
1864
1865class GestureClick : public SkView::Click {
1866public:
1867    GestureClick(SkView* target) : SkView::Click(target) {
1868        this->setType(gGestureClickType);
1869    }
1870
1871    static bool IsGesture(Click* click) {
1872        return click->isType(gGestureClickType);
1873    }
1874};
1875
1876SkView::Click* SampleWindow::onFindClickHandler(SkScalar x, SkScalar y) {
1877    return new GestureClick(this);
1878}
1879
1880bool SampleWindow::onClick(Click* click) {
1881    if (GestureClick::IsGesture(click)) {
1882        float x = static_cast<float>(click->fICurr.fX);
1883        float y = static_cast<float>(click->fICurr.fY);
1884
1885        switch (click->fState) {
1886            case SkView::Click::kDown_State:
1887                fGesture.touchBegin(click->fOwner, x, y);
1888                break;
1889            case SkView::Click::kMoved_State:
1890                fGesture.touchMoved(click->fOwner, x, y);
1891                this->updateMatrix();
1892                break;
1893            case SkView::Click::kUp_State:
1894                fGesture.touchEnd(click->fOwner);
1895                this->updateMatrix();
1896                break;
1897        }
1898        return true;
1899    }
1900    return false;
1901}
1902
1903///////////////////////////////////////////////////////////////////////////////
1904
1905void SampleWindow::loadView(SkView* view) {
1906    SkView::F2BIter iter(this);
1907    SkView* prev = iter.next();
1908    if (prev) {
1909        prev->detachFromParent();
1910    }
1911
1912    view->setVisibleP(true);
1913    view->setClipToBounds(false);
1914    this->attachChildToFront(view)->unref();
1915    view->setSize(this->width(), this->height());
1916
1917    //repopulate the slide menu when a view is loaded
1918    fSlideMenu->reset();
1919#ifdef DEBUGGER
1920    if (!is_debugger(view) && !is_overview(view) && !is_transition(view) && fDebugger) {
1921        //Force Pipe to be on if using debugger
1922        fPipeState = SkOSMenu::kOnState;
1923    }
1924#endif
1925    (void)SampleView::SetUsePipe(view, fPipeState);
1926    if (SampleView::IsSampleView(view))
1927        ((SampleView*)view)->requestMenu(fSlideMenu);
1928    this->onUpdateMenu(fSlideMenu);
1929    this->updateTitle();
1930}
1931
1932static const char* gConfigNames[] = {
1933    "unknown config",
1934    "A1",
1935    "A8",
1936    "Index8",
1937    "565",
1938    "4444",
1939    "8888"
1940};
1941
1942static const char* configToString(SkBitmap::Config c) {
1943    return gConfigNames[c];
1944}
1945
1946static const char* gDeviceTypePrefix[] = {
1947    "raster: ",
1948    "picture: ",
1949    "opengl: ",
1950#if SK_ANGLE
1951    "angle: ",
1952#endif
1953    "null-gl: "
1954};
1955
1956static const bool gDeviceTypeIsGPU[] = {
1957    false,
1958    false,
1959    true,
1960#if SK_ANGLE
1961    true,
1962#endif
1963    true
1964};
1965
1966static const char* trystate_str(SkOSMenu::TriState state,
1967                                const char trueStr[], const char falseStr[]) {
1968    if (SkOSMenu::kOnState == state) {
1969        return trueStr;
1970    } else if (SkOSMenu::kOffState == state) {
1971        return falseStr;
1972    }
1973    return NULL;
1974}
1975
1976void SampleWindow::updateTitle() {
1977    SkView* view = curr_view(this);
1978
1979    SkString title;
1980    if (!curr_title(this, &title)) {
1981        title.set("<unknown>");
1982    }
1983
1984    title.prepend(gDeviceTypePrefix[fDeviceType]);
1985
1986    title.prepend(" ");
1987    title.prepend(configToString(this->getBitmap().config()));
1988
1989    if (fAnimating) {
1990        title.prepend("<A> ");
1991    }
1992    if (fScale) {
1993        title.prepend("<S> ");
1994    }
1995    if (fRotate) {
1996        title.prepend("<R> ");
1997    }
1998    if (fNClip) {
1999        title.prepend("<C> ");
2000    }
2001    if (fPerspAnim) {
2002        title.prepend("<K> ");
2003    }
2004
2005    title.prepend(trystate_str(fLCDState, "LCD ", "lcd "));
2006    title.prepend(trystate_str(fAAState, "AA ", "aa "));
2007    title.prepend(trystate_str(fFilterState, "H ", "h "));
2008    title.prepend(fFlipAxis & kFlipAxis_X ? "X " : NULL);
2009    title.prepend(fFlipAxis & kFlipAxis_Y ? "Y " : NULL);
2010
2011    if (fZoomLevel) {
2012        title.prependf("{%.2f} ", SkScalarToFloat(fZoomLevel));
2013    }
2014
2015    if (fMeasureFPS) {
2016        title.appendf(" %6.1f ms", fMeasureFPS_Time / (float)FPS_REPEAT_MULTIPLIER);
2017    }
2018    if (SampleView::IsSampleView(view)) {
2019        switch (fPipeState) {
2020            case SkOSMenu::kOnState:
2021                title.prepend("<Pipe> ");
2022                break;
2023            case SkOSMenu::kMixedState:
2024                title.prepend("<Tiled Pipe> ");
2025                break;
2026
2027            default:
2028                break;
2029        }
2030        title.prepend("! ");
2031    }
2032
2033    if (gDeviceTypeIsGPU[fDeviceType] &&
2034        NULL != fDevManager &&
2035        fDevManager->getGrRenderTarget()->numSamples() > 0) {
2036        title.appendf(" [MSAA: %d]",
2037                       fDevManager->getGrRenderTarget()->numSamples());
2038    }
2039
2040    this->setTitle(title.c_str());
2041}
2042
2043void SampleWindow::onSizeChange() {
2044    this->INHERITED::onSizeChange();
2045
2046    SkView::F2BIter iter(this);
2047    SkView* view = iter.next();
2048    view->setSize(this->width(), this->height());
2049
2050    // rebuild our clippath
2051    {
2052        const SkScalar W = this->width();
2053        const SkScalar H = this->height();
2054
2055        fClipPath.reset();
2056#if 0
2057        for (SkScalar y = SK_Scalar1; y < H; y += SkIntToScalar(32)) {
2058            SkRect r;
2059            r.set(SK_Scalar1, y, SkIntToScalar(30), y + SkIntToScalar(30));
2060            for (; r.fLeft < W; r.offset(SkIntToScalar(32), 0))
2061                fClipPath.addRect(r);
2062        }
2063#else
2064        SkRect r;
2065        r.set(0, 0, W, H);
2066        fClipPath.addRect(r, SkPath::kCCW_Direction);
2067        r.set(W/4, H/4, W*3/4, H*3/4);
2068        fClipPath.addRect(r, SkPath::kCW_Direction);
2069#endif
2070    }
2071
2072    fZoomCenterX = SkScalarHalf(this->width());
2073    fZoomCenterY = SkScalarHalf(this->height());
2074
2075#ifdef SK_BUILD_FOR_ANDROID
2076    // FIXME: The first draw after a size change does not work on Android, so
2077    // we post an invalidate.
2078    this->postInvalDelay();
2079#endif
2080    this->updateTitle();    // to refresh our config
2081    fDevManager->windowSizeChanged(this);
2082}
2083
2084///////////////////////////////////////////////////////////////////////////////
2085
2086static const char is_sample_view_tag[] = "sample-is-sample-view";
2087static const char repeat_count_tag[] = "sample-set-repeat-count";
2088static const char set_use_pipe_tag[] = "sample-set-use-pipe";
2089
2090bool SampleView::IsSampleView(SkView* view) {
2091    SkEvent evt(is_sample_view_tag);
2092    return view->doQuery(&evt);
2093}
2094
2095bool SampleView::SetRepeatDraw(SkView* view, int count) {
2096    SkEvent evt(repeat_count_tag);
2097    evt.setFast32(count);
2098    return view->doEvent(evt);
2099}
2100
2101bool SampleView::SetUsePipe(SkView* view, SkOSMenu::TriState state) {
2102    SkEvent evt;
2103    evt.setS32(set_use_pipe_tag, state);
2104    return view->doEvent(evt);
2105}
2106
2107bool SampleView::onEvent(const SkEvent& evt) {
2108    if (evt.isType(repeat_count_tag)) {
2109        fRepeatCount = evt.getFast32();
2110        return true;
2111    }
2112    int32_t pipeHolder;
2113    if (evt.findS32(set_use_pipe_tag, &pipeHolder)) {
2114        fPipeState = static_cast<SkOSMenu::TriState>(pipeHolder);
2115        return true;
2116    }
2117    return this->INHERITED::onEvent(evt);
2118}
2119
2120bool SampleView::onQuery(SkEvent* evt) {
2121    if (evt->isType(is_sample_view_tag)) {
2122        return true;
2123    }
2124    return this->INHERITED::onQuery(evt);
2125}
2126
2127
2128class SimplePC : public SkGPipeController {
2129public:
2130    SimplePC(SkCanvas* target);
2131    ~SimplePC();
2132
2133    virtual void* requestBlock(size_t minRequest, size_t* actual);
2134    virtual void notifyWritten(size_t bytes);
2135
2136private:
2137    SkGPipeReader   fReader;
2138    void*           fBlock;
2139    size_t          fBlockSize;
2140    size_t          fBytesWritten;
2141    int             fAtomsWritten;
2142    SkGPipeReader::Status   fStatus;
2143
2144    size_t        fTotalWritten;
2145};
2146
2147SimplePC::SimplePC(SkCanvas* target) : fReader(target) {
2148    fBlock = NULL;
2149    fBlockSize = fBytesWritten = 0;
2150    fStatus = SkGPipeReader::kDone_Status;
2151    fTotalWritten = 0;
2152    fAtomsWritten = 0;
2153}
2154
2155SimplePC::~SimplePC() {
2156//    SkASSERT(SkGPipeReader::kDone_Status == fStatus);
2157    if (fTotalWritten) {
2158        SkDebugf("--- %d bytes %d atoms, status %d\n", fTotalWritten,
2159                 fAtomsWritten, fStatus);
2160#ifdef  PIPE_FILE
2161        //File is open in append mode
2162        FILE* f = fopen(FILE_PATH, "ab");
2163        SkASSERT(f != NULL);
2164        fwrite((const char*)fBlock + fBytesWritten, 1, bytes, f);
2165        fclose(f);
2166#endif
2167#ifdef PIPE_NET
2168        if (fAtomsWritten > 1 && fTotalWritten > 4) { //ignore done
2169            gServer.acceptConnections();
2170            gServer.writePacket(fBlock, fTotalWritten);
2171        }
2172#endif
2173#ifdef  DEBUGGER
2174        gTempDataStore.reset();
2175        gTempDataStore.append(fTotalWritten, (const char*)fBlock);
2176#endif
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
2201void SampleView::draw(SkCanvas* canvas) {
2202    if (SkOSMenu::kOffState == fPipeState) {
2203        this->INHERITED::draw(canvas);
2204    } else {
2205        SkGPipeWriter writer;
2206        SimplePC controller(canvas);
2207        TiledPipeController tc(canvas->getDevice()->accessBitmap(false),
2208                               &canvas->getTotalMatrix());
2209        SkGPipeController* pc;
2210        if (SkOSMenu::kMixedState == fPipeState) {
2211            pc = &tc;
2212        } else {
2213            pc = &controller;
2214        }
2215        uint32_t flags = SkGPipeWriter::kCrossProcess_Flag;
2216
2217        canvas = writer.startRecording(pc, flags);
2218        //Must draw before controller goes out of scope and sends data
2219        this->INHERITED::draw(canvas);
2220        //explicitly end recording to ensure writer is flushed before the memory
2221        //is freed in the deconstructor of the controller
2222        writer.endRecording();
2223    }
2224}
2225void SampleView::onDraw(SkCanvas* canvas) {
2226    this->onDrawBackground(canvas);
2227
2228    for (int i = 0; i < fRepeatCount; i++) {
2229        SkAutoCanvasRestore acr(canvas, true);
2230        this->onDrawContent(canvas);
2231    }
2232}
2233
2234void SampleView::onDrawBackground(SkCanvas* canvas) {
2235    canvas->drawColor(fBGColor);
2236}
2237
2238///////////////////////////////////////////////////////////////////////////////
2239
2240template <typename T> void SkTBSort(T array[], int count) {
2241    for (int i = 1; i < count - 1; i++) {
2242        bool didSwap = false;
2243        for (int j = count - 1; j > i; --j) {
2244            if (array[j] < array[j-1]) {
2245                T tmp(array[j-1]);
2246                array[j-1] = array[j];
2247                array[j] = tmp;
2248                didSwap = true;
2249            }
2250        }
2251        if (!didSwap) {
2252            break;
2253        }
2254    }
2255
2256    for (int k = 0; k < count - 1; k++) {
2257        SkASSERT(!(array[k+1] < array[k]));
2258    }
2259}
2260
2261#include "SkRandom.h"
2262
2263static void rand_rect(SkIRect* rect, SkRandom& rand) {
2264    int bits = 8;
2265    int shift = 32 - bits;
2266    rect->set(rand.nextU() >> shift, rand.nextU() >> shift,
2267              rand.nextU() >> shift, rand.nextU() >> shift);
2268    rect->sort();
2269}
2270
2271static void dumpRect(const SkIRect& r) {
2272    SkDebugf(" { %d, %d, %d, %d },\n",
2273             r.fLeft, r.fTop,
2274             r.fRight, r.fBottom);
2275}
2276
2277static void test_rects(const SkIRect rect[], int count) {
2278    SkRegion rgn0, rgn1;
2279
2280    for (int i = 0; i < count; i++) {
2281        rgn0.op(rect[i], SkRegion::kUnion_Op);
2282     //   dumpRect(rect[i]);
2283    }
2284    rgn1.setRects(rect, count);
2285
2286    if (rgn0 != rgn1) {
2287        SkDebugf("\n");
2288        for (int i = 0; i < count; i++) {
2289            dumpRect(rect[i]);
2290        }
2291        SkDebugf("\n");
2292    }
2293}
2294
2295static void test() {
2296    size_t i;
2297
2298    const SkIRect r0[] = {
2299        { 0, 0, 1, 1 },
2300        { 2, 2, 3, 3 },
2301    };
2302    const SkIRect r1[] = {
2303        { 0, 0, 1, 3 },
2304        { 1, 1, 2, 2 },
2305        { 2, 0, 3, 3 },
2306    };
2307    const SkIRect r2[] = {
2308        { 0, 0, 1, 2 },
2309        { 2, 1, 3, 3 },
2310        { 4, 0, 5, 1 },
2311        { 6, 0, 7, 4 },
2312    };
2313
2314    static const struct {
2315        const SkIRect* fRects;
2316        int            fCount;
2317    } gRecs[] = {
2318        { r0, SK_ARRAY_COUNT(r0) },
2319        { r1, SK_ARRAY_COUNT(r1) },
2320        { r2, SK_ARRAY_COUNT(r2) },
2321    };
2322
2323    for (i = 0; i < SK_ARRAY_COUNT(gRecs); i++) {
2324        test_rects(gRecs[i].fRects, gRecs[i].fCount);
2325    }
2326
2327    SkRandom rand;
2328    for (i = 0; i < 10000; i++) {
2329        SkRegion rgn0, rgn1;
2330
2331        const int N = 8;
2332        SkIRect rect[N];
2333        for (int j = 0; j < N; j++) {
2334            rand_rect(&rect[j], rand);
2335        }
2336        test_rects(rect, N);
2337    }
2338}
2339
2340// FIXME: this should be in a header
2341SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv);
2342SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv) {
2343    if (false) { // avoid bit rot, suppress warning
2344        test();
2345    }
2346    return new SampleWindow(hwnd, argc, argv, NULL);
2347}
2348
2349// FIXME: this should be in a header
2350void get_preferred_size(int* x, int* y, int* width, int* height);
2351void get_preferred_size(int* x, int* y, int* width, int* height) {
2352    *x = 10;
2353    *y = 50;
2354    *width = 640;
2355    *height = 480;
2356}
2357
2358// FIXME: this should be in a header
2359void application_init();
2360void application_init() {
2361//    setenv("ANDROID_ROOT", "../../../data", 0);
2362#ifdef SK_BUILD_FOR_MAC
2363    setenv("ANDROID_ROOT", "/android/device/data", 0);
2364#endif
2365    SkGraphics::Init();
2366    SkEvent::Init();
2367}
2368
2369// FIXME: this should be in a header
2370void application_term();
2371void application_term() {
2372    SkEvent::Term();
2373    SkGraphics::Term();
2374}
2375