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