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