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