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