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