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