SampleApp.cpp revision e23f194bf9747267b9dd42e238dfe217ed585c03
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            this->installDrawFilter(orig);
1056            orig->drawPicture(*pict);
1057            pict->unref();
1058        } else if (true) {
1059            SkDynamicMemoryWStream ostream;
1060            fPicture->serialize(&ostream);
1061            fPicture->unref();
1062
1063            SkAutoDataUnref data(ostream.copyToData());
1064            SkMemoryStream istream(data.data(), data.size());
1065            SkPicture pict(&istream);
1066            orig->drawPicture(pict);
1067        } else {
1068            fPicture->draw(orig);
1069            fPicture->unref();
1070        }
1071        fPicture = NULL;
1072    }
1073
1074    // Do this after presentGL and other finishing, rather than in afterChild
1075    if (fMeasureFPS && fMeasureFPS_Time) {
1076        fMeasureFPS_Time = SkTime::GetMSecs() - fMeasureFPS_Time;
1077        this->updateTitle();
1078        this->postInvalDelay();
1079    }
1080
1081    //    if ((fScrollTestX | fScrollTestY) != 0)
1082    if (false) {
1083        const SkBitmap& bm = orig->getDevice()->accessBitmap(true);
1084        int dx = fScrollTestX * 7;
1085        int dy = fScrollTestY * 7;
1086        SkIRect r;
1087        SkRegion inval;
1088
1089        r.set(50, 50, 50+100, 50+100);
1090        bm.scrollRect(&r, dx, dy, &inval);
1091        paint_rgn(bm, r, inval);
1092    }
1093#ifdef DEBUGGER
1094    SkView* curr = curr_view(this);
1095    if (fDebugger && !is_debugger(curr) && !is_transition(curr) && !is_overview(curr)) {
1096        //Stop Pipe when fDebugger is active
1097        fUsePipe = false;
1098        (void)SampleView::SetUsePipe(curr, false);
1099        fAppMenu.getItemByID(fUsePipeMenuItemID)->setBool(fUsePipe);
1100        this->onUpdateMenu(&fAppMenu);
1101
1102        //Reset any transformations
1103        fGesture.stop();
1104        fGesture.reset();
1105
1106        this->loadView(create_debugger(gTempDataStore.begin(),
1107                                       gTempDataStore.count()));
1108    }
1109#endif
1110}
1111
1112void SampleWindow::beforeChild(SkView* child, SkCanvas* canvas) {
1113    if (fScale) {
1114        SkScalar scale = SK_Scalar1 * 7 / 10;
1115        SkScalar cx = this->width() / 2;
1116        SkScalar cy = this->height() / 2;
1117        canvas->translate(cx, cy);
1118        canvas->scale(scale, scale);
1119        canvas->translate(-cx, -cy);
1120    }
1121    if (fRotate) {
1122        SkScalar cx = this->width() / 2;
1123        SkScalar cy = this->height() / 2;
1124        canvas->translate(cx, cy);
1125        canvas->rotate(SkIntToScalar(30));
1126        canvas->translate(-cx, -cy);
1127    }
1128    if (fPerspAnim) {
1129        fPerspAnimTime += SampleCode::GetAnimSecondsDelta();
1130
1131        static const SkScalar gAnimPeriod = 10 * SK_Scalar1;
1132        static const SkScalar gAnimMag = SK_Scalar1 / 1000;
1133        SkScalar t = SkScalarMod(fPerspAnimTime, gAnimPeriod);
1134        if (SkScalarFloorToInt(SkScalarDiv(fPerspAnimTime, gAnimPeriod)) & 0x1) {
1135            t = gAnimPeriod - t;
1136        }
1137        t = 2 * t - gAnimPeriod;
1138        t = SkScalarMul(SkScalarDiv(t, gAnimPeriod), gAnimMag);
1139        SkMatrix m;
1140        m.reset();
1141        m.setPerspY(t);
1142        canvas->concat(m);
1143    }
1144
1145    this->installDrawFilter(canvas);
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::installDrawFilter(SkCanvas* canvas) {
1267    canvas->setDrawFilter(new FlagsDrawFilter(fLCDState, fAAState,
1268                                              fFilterState, fHintingState))->unref();
1269}
1270
1271void SampleWindow::postAnimatingEvent() {
1272    if (fAnimating) {
1273        (new SkEvent(ANIMATING_EVENTTYPE, this->getSinkID()))->postDelay(ANIMATING_DELAY);
1274    }
1275}
1276
1277bool SampleWindow::onEvent(const SkEvent& evt) {
1278    if (evt.isType(gUpdateWindowTitleEvtName)) {
1279        this->updateTitle();
1280        return true;
1281    }
1282    if (evt.isType(ANIMATING_EVENTTYPE)) {
1283        if (fAnimating) {
1284            this->nextSample();
1285            this->postAnimatingEvent();
1286        }
1287        return true;
1288    }
1289    if (evt.isType("replace-transition-view")) {
1290        this->loadView((SkView*)SkEventSink::FindSink(evt.getFast32()));
1291        return true;
1292    }
1293    if (evt.isType("set-curr-index")) {
1294        this->goToSample(evt.getFast32());
1295        return true;
1296    }
1297    if (isInvalEvent(evt)) {
1298        this->inval(NULL);
1299        return true;
1300    }
1301    int selected = -1;
1302    if (SkOSMenu::FindListIndex(evt, "Device Type", &selected)) {
1303        this->setDeviceType((DeviceType)selected);
1304        return true;
1305    }
1306    if (SkOSMenu::FindSwitchState(evt, "Pipe", &fUsePipe)) {
1307#ifdef PIPE_NET
1308        if (!fUsePipe)
1309            gServer.disconnectAll();
1310#endif
1311        (void)SampleView::SetUsePipe(curr_view(this), fUsePipe);
1312        this->updateTitle();
1313        this->inval(NULL);
1314        return true;
1315    }
1316    if (SkOSMenu::FindSwitchState(evt, "Slide Show", NULL)) {
1317        this->toggleSlideshow();
1318        return true;
1319    }
1320    if (SkOSMenu::FindTriState(evt, "AA", &fAAState) ||
1321        SkOSMenu::FindTriState(evt, "LCD", &fLCDState) ||
1322        SkOSMenu::FindTriState(evt, "Filter", &fFilterState) ||
1323        SkOSMenu::FindTriState(evt, "Hinting", &fHintingState) ||
1324        SkOSMenu::FindSwitchState(evt, "Clip", &fUseClip) ||
1325        SkOSMenu::FindSwitchState(evt, "Zoomer", &fShowZoomer) ||
1326        SkOSMenu::FindSwitchState(evt, "Magnify", &fMagnify) ||
1327        SkOSMenu::FindListIndex(evt, "Transition-Next", &fTransitionNext) ||
1328        SkOSMenu::FindListIndex(evt, "Transition-Prev", &fTransitionPrev)) {
1329        this->inval(NULL);
1330        this->updateTitle();
1331        return true;
1332    }
1333    if (SkOSMenu::FindSwitchState(evt, "Flip X", NULL)) {
1334        fFlipAxis ^= kFlipAxis_X;
1335        this->updateMatrix();
1336        return true;
1337    }
1338    if (SkOSMenu::FindSwitchState(evt, "Flip Y", NULL)) {
1339        fFlipAxis ^= kFlipAxis_Y;
1340        this->updateMatrix();
1341        return true;
1342    }
1343    if (SkOSMenu::FindAction(evt,"Save to PDF")) {
1344        this->saveToPdf();
1345        return true;
1346    }
1347#ifdef DEBUGGER
1348    if (SkOSMenu::FindSwitchState(evt, "Debugger", &fDebugger)) {
1349        if (fDebugger) {
1350            fUsePipe = true;
1351            (void)SampleView::SetUsePipe(curr_view(this), true);
1352        } else {
1353            this->loadView(fSamples[fCurrIndex]());
1354        }
1355        this->inval(NULL);
1356        return true;
1357    }
1358#endif
1359    return this->INHERITED::onEvent(evt);
1360}
1361
1362bool SampleWindow::onQuery(SkEvent* query) {
1363    if (query->isType("get-slide-count")) {
1364        query->setFast32(fSamples.count());
1365        return true;
1366    }
1367    if (query->isType("get-slide-title")) {
1368        SkView* view = (*fSamples[query->getFast32()])();
1369        SkEvent evt(gTitleEvtName);
1370        if (view->doQuery(&evt)) {
1371            query->setString("title", evt.findString(gTitleEvtName));
1372        }
1373        SkSafeUnref(view);
1374        return true;
1375    }
1376    if (query->isType("use-fast-text")) {
1377        SkEvent evt(gFastTextEvtName);
1378        return curr_view(this)->doQuery(&evt);
1379    }
1380    if (query->isType("ignore-window-bitmap")) {
1381        query->setFast32(this->getGrContext() != NULL);
1382        return true;
1383    }
1384    return this->INHERITED::onQuery(query);
1385}
1386
1387static void cleanup_for_filename(SkString* name) {
1388    char* str = name->writable_str();
1389    for (size_t i = 0; i < name->size(); i++) {
1390        switch (str[i]) {
1391            case ':': str[i] = '-'; break;
1392            case '/': str[i] = '-'; break;
1393            case ' ': str[i] = '_'; break;
1394            default: break;
1395        }
1396    }
1397}
1398
1399bool SampleWindow::onHandleChar(SkUnichar uni) {
1400    {
1401        SkView* view = curr_view(this);
1402        if (view) {
1403            SkEvent evt(gCharEvtName);
1404            evt.setFast32(uni);
1405            if (view->doQuery(&evt)) {
1406                return true;
1407            }
1408        }
1409    }
1410
1411    int dx = 0xFF;
1412    int dy = 0xFF;
1413
1414    switch (uni) {
1415        case '5': dx =  0; dy =  0; break;
1416        case '8': dx =  0; dy = -1; break;
1417        case '6': dx =  1; dy =  0; break;
1418        case '2': dx =  0; dy =  1; break;
1419        case '4': dx = -1; dy =  0; break;
1420        case '7': dx = -1; dy = -1; break;
1421        case '9': dx =  1; dy = -1; break;
1422        case '3': dx =  1; dy =  1; break;
1423        case '1': dx = -1; dy =  1; break;
1424
1425        default:
1426            break;
1427    }
1428
1429    if (0xFF != dx && 0xFF != dy) {
1430        if ((dx | dy) == 0) {
1431            fScrollTestX = fScrollTestY = 0;
1432        } else {
1433            fScrollTestX += dx;
1434            fScrollTestY += dy;
1435        }
1436        this->inval(NULL);
1437        return true;
1438    }
1439
1440    switch (uni) {
1441        case 'f':
1442            // only
1443            toggleFPS();
1444            break;
1445        case 'g':
1446            fRequestGrabImage = true;
1447            this->inval(NULL);
1448            break;
1449        case 'i':
1450            this->zoomIn();
1451            break;
1452        case 'o':
1453            this->zoomOut();
1454            break;
1455        case 'r':
1456            fRotate = !fRotate;
1457            this->inval(NULL);
1458            this->updateTitle();
1459            return true;
1460        case 'k':
1461            fPerspAnim = !fPerspAnim;
1462            this->inval(NULL);
1463            this->updateTitle();
1464            return true;
1465        case '\\':
1466            if (fDevManager->supportsDeviceType(kNullGPU_DeviceType)) {
1467                fDeviceType=  kNullGPU_DeviceType;
1468                this->inval(NULL);
1469                this->updateTitle();
1470            }
1471            return true;
1472        case 's':
1473            fScale = !fScale;
1474            this->inval(NULL);
1475            this->updateTitle();
1476            return true;
1477        default:
1478            break;
1479    }
1480
1481    if (fAppMenu.handleKeyEquivalent(uni)|| fSlideMenu.handleKeyEquivalent(uni)) {
1482        this->onUpdateMenu(&fAppMenu);
1483        this->onUpdateMenu(&fSlideMenu);
1484        return true;
1485    }
1486    return this->INHERITED::onHandleChar(uni);
1487}
1488
1489void SampleWindow::setDeviceType(DeviceType type) {
1490    if (type != fDeviceType && fDevManager->supportsDeviceType(fDeviceType))
1491        fDeviceType = type;
1492    this->updateTitle();
1493    this->inval(NULL);
1494}
1495
1496void SampleWindow::toggleSlideshow() {
1497    fAnimating = !fAnimating;
1498    this->postAnimatingEvent();
1499    this->updateTitle();
1500}
1501
1502void SampleWindow::toggleRendering() {
1503    DeviceType origDevType = fDeviceType;
1504    do {
1505        fDeviceType = cycle_devicetype(fDeviceType);
1506    } while (origDevType != fDeviceType &&
1507             !fDevManager->supportsDeviceType(fDeviceType));
1508    this->updateTitle();
1509    this->inval(NULL);
1510}
1511
1512void SampleWindow::toggleFPS() {
1513    fMeasureFPS = !fMeasureFPS;
1514    this->updateTitle();
1515    this->inval(NULL);
1516}
1517
1518#include "SkDumpCanvas.h"
1519
1520bool SampleWindow::onHandleKey(SkKey key) {
1521    {
1522        SkView* view = curr_view(this);
1523        if (view) {
1524            SkEvent evt(gKeyEvtName);
1525            evt.setFast32(key);
1526            if (view->doQuery(&evt)) {
1527                return true;
1528            }
1529        }
1530    }
1531    switch (key) {
1532        case kRight_SkKey:
1533            if (this->nextSample()) {
1534                return true;
1535            }
1536            break;
1537        case kLeft_SkKey:
1538            toggleRendering();
1539            return true;
1540        case kUp_SkKey:
1541            if (USE_ARROWS_FOR_ZOOM) {
1542                this->changeZoomLevel(1.f);
1543            } else {
1544                fNClip = !fNClip;
1545                this->inval(NULL);
1546                this->updateTitle();
1547            }
1548            return true;
1549        case kDown_SkKey:
1550            if (USE_ARROWS_FOR_ZOOM) {
1551                this->changeZoomLevel(-1.f);
1552            } else {
1553                this->setConfig(cycle_configs(this->getBitmap().config()));
1554                this->updateTitle();
1555            }
1556            return true;
1557        case kOK_SkKey:
1558            if (false) {
1559                SkDebugfDumper dumper;
1560                SkDumpCanvas dc(&dumper);
1561                this->draw(&dc);
1562            } else {
1563                fRepeatDrawing = !fRepeatDrawing;
1564                if (fRepeatDrawing) {
1565                    this->inval(NULL);
1566                }
1567            }
1568            return true;
1569        case kBack_SkKey:
1570            this->showOverview();
1571            return true;
1572        default:
1573            break;
1574    }
1575    return this->INHERITED::onHandleKey(key);
1576}
1577
1578///////////////////////////////////////////////////////////////////////////////
1579
1580static const char gGestureClickType[] = "GestureClickType";
1581
1582bool SampleWindow::onDispatchClick(int x, int y, Click::State state,
1583        void* owner) {
1584    if (Click::kMoved_State == state) {
1585        updatePointer(x, y);
1586    }
1587    int w = SkScalarRound(this->width());
1588    int h = SkScalarRound(this->height());
1589
1590    // check for the resize-box
1591    if (w - x < 16 && h - y < 16) {
1592        return false;   // let the OS handle the click
1593    }
1594    else if (fMagnify) {
1595        //it's only necessary to update the drawing if there's a click
1596        this->inval(NULL);
1597        return false; //prevent dragging while magnify is enabled
1598    }
1599    else {
1600        return this->INHERITED::onDispatchClick(x, y, state, owner);
1601    }
1602}
1603
1604class GestureClick : public SkView::Click {
1605public:
1606    GestureClick(SkView* target) : SkView::Click(target) {
1607        this->setType(gGestureClickType);
1608    }
1609
1610    static bool IsGesture(Click* click) {
1611        return click->isType(gGestureClickType);
1612    }
1613};
1614
1615SkView::Click* SampleWindow::onFindClickHandler(SkScalar x, SkScalar y) {
1616    return new GestureClick(this);
1617}
1618
1619bool SampleWindow::onClick(Click* click) {
1620    if (GestureClick::IsGesture(click)) {
1621        float x = click->fICurr.fX;
1622        float y = click->fICurr.fY;
1623
1624        switch (click->fState) {
1625            case SkView::Click::kDown_State:
1626                fGesture.touchBegin(click->fOwner, x, y);
1627                break;
1628            case SkView::Click::kMoved_State:
1629                fGesture.touchMoved(click->fOwner, x, y);
1630                this->updateMatrix();
1631                break;
1632            case SkView::Click::kUp_State:
1633                fGesture.touchEnd(click->fOwner);
1634                this->updateMatrix();
1635                break;
1636        }
1637        return true;
1638    }
1639    return false;
1640}
1641
1642///////////////////////////////////////////////////////////////////////////////
1643
1644void SampleWindow::loadView(SkView* view) {
1645    SkView::F2BIter iter(this);
1646    SkView* prev = iter.next();
1647    if (prev) {
1648        prev->detachFromParent();
1649    }
1650
1651    view->setVisibleP(true);
1652    view->setClipToBounds(false);
1653    this->attachChildToFront(view)->unref();
1654    view->setSize(this->width(), this->height());
1655
1656    //repopulate the slide menu when a view is loaded
1657    fSlideMenu.reset();
1658#ifdef DEBUGGER
1659    if (!is_debugger(view) && !is_overview(view) && !is_transition(view) && fDebugger) {
1660        //Force Pipe to be on if using debugger
1661        fUsePipe = true;
1662    }
1663#endif
1664    (void)SampleView::SetUsePipe(view, fUsePipe);
1665    if (SampleView::IsSampleView(view))
1666        ((SampleView*)view)->requestMenu(&fSlideMenu);
1667    this->onUpdateMenu(&fSlideMenu);
1668    this->updateTitle();
1669}
1670
1671static const char* gConfigNames[] = {
1672    "unknown config",
1673    "A1",
1674    "A8",
1675    "Index8",
1676    "565",
1677    "4444",
1678    "8888"
1679};
1680
1681static const char* configToString(SkBitmap::Config c) {
1682    return gConfigNames[c];
1683}
1684
1685static const char* gDeviceTypePrefix[] = {
1686    "raster: ",
1687    "picture: ",
1688    "opengl: ",
1689    "null-gl: "
1690};
1691
1692static const char* trystate_str(SkOSMenu::TriState state,
1693                                const char trueStr[], const char falseStr[]) {
1694    if (SkOSMenu::kOnState == state) {
1695        return trueStr;
1696    } else if (SkOSMenu::kOffState == state) {
1697        return falseStr;
1698    }
1699    return NULL;
1700}
1701
1702void SampleWindow::updateTitle() {
1703    SkString title;
1704
1705    SkView::F2BIter iter(this);
1706    SkView* view = iter.next();
1707    SkEvent evt(gTitleEvtName);
1708    if (view->doQuery(&evt)) {
1709        title.set(evt.findString(gTitleEvtName));
1710    }
1711    if (title.size() == 0) {
1712        title.set("<unknown>");
1713    }
1714
1715    title.prepend(gDeviceTypePrefix[fDeviceType]);
1716
1717    title.prepend(" ");
1718    title.prepend(configToString(this->getBitmap().config()));
1719
1720    if (fAnimating) {
1721        title.prepend("<A> ");
1722    }
1723    if (fScale) {
1724        title.prepend("<S> ");
1725    }
1726    if (fRotate) {
1727        title.prepend("<R> ");
1728    }
1729    if (fNClip) {
1730        title.prepend("<C> ");
1731    }
1732    if (fPerspAnim) {
1733        title.prepend("<K> ");
1734    }
1735
1736    title.prepend(trystate_str(fLCDState, "LCD ", "lcd "));
1737    title.prepend(trystate_str(fAAState, "AA ", "aa "));
1738    title.prepend(trystate_str(fFilterState, "H ", "h "));
1739    title.prepend(fFlipAxis & kFlipAxis_X ? "X " : NULL);
1740    title.prepend(fFlipAxis & kFlipAxis_Y ? "Y " : NULL);
1741
1742    if (fZoomLevel) {
1743        title.prependf("{%.2f} ", SkScalarToFloat(fZoomLevel));
1744    }
1745
1746    if (fMeasureFPS) {
1747        title.appendf(" %6.1f ms", fMeasureFPS_Time / (float)FPS_REPEAT_MULTIPLIER);
1748    }
1749    if (fUsePipe && SampleView::IsSampleView(view)) {
1750        title.prepend("<P> ");
1751    }
1752    if (SampleView::IsSampleView(view)) {
1753        title.prepend("! ");
1754    }
1755
1756    this->setTitle(title.c_str());
1757}
1758
1759void SampleWindow::onSizeChange() {
1760    this->INHERITED::onSizeChange();
1761
1762    SkView::F2BIter iter(this);
1763    SkView* view = iter.next();
1764    view->setSize(this->width(), this->height());
1765
1766    // rebuild our clippath
1767    {
1768        const SkScalar W = this->width();
1769        const SkScalar H = this->height();
1770
1771        fClipPath.reset();
1772#if 0
1773        for (SkScalar y = SK_Scalar1; y < H; y += SkIntToScalar(32)) {
1774            SkRect r;
1775            r.set(SK_Scalar1, y, SkIntToScalar(30), y + SkIntToScalar(30));
1776            for (; r.fLeft < W; r.offset(SkIntToScalar(32), 0))
1777                fClipPath.addRect(r);
1778        }
1779#else
1780        SkRect r;
1781        r.set(0, 0, W, H);
1782        fClipPath.addRect(r, SkPath::kCCW_Direction);
1783        r.set(W/4, H/4, W*3/4, H*3/4);
1784        fClipPath.addRect(r, SkPath::kCW_Direction);
1785#endif
1786    }
1787
1788    fZoomCenterX = SkScalarHalf(this->width());
1789    fZoomCenterY = SkScalarHalf(this->height());
1790
1791#ifdef SK_BUILD_FOR_ANDROID
1792    // FIXME: The first draw after a size change does not work on Android, so
1793    // we post an invalidate.
1794    this->postInvalDelay();
1795#endif
1796    this->updateTitle();    // to refresh our config
1797    fDevManager->windowSizeChanged(this);
1798}
1799
1800///////////////////////////////////////////////////////////////////////////////
1801
1802static const char is_sample_view_tag[] = "sample-is-sample-view";
1803static const char repeat_count_tag[] = "sample-set-repeat-count";
1804static const char set_use_pipe_tag[] = "sample-set-use-pipe";
1805
1806bool SampleView::IsSampleView(SkView* view) {
1807    SkEvent evt(is_sample_view_tag);
1808    return view->doQuery(&evt);
1809}
1810
1811bool SampleView::SetRepeatDraw(SkView* view, int count) {
1812    SkEvent evt(repeat_count_tag);
1813    evt.setFast32(count);
1814    return view->doEvent(evt);
1815}
1816
1817bool SampleView::SetUsePipe(SkView* view, bool pred) {
1818    SkEvent evt(set_use_pipe_tag);
1819    evt.setFast32(pred);
1820    return view->doEvent(evt);
1821}
1822
1823bool SampleView::onEvent(const SkEvent& evt) {
1824    if (evt.isType(repeat_count_tag)) {
1825        fRepeatCount = evt.getFast32();
1826        return true;
1827    }
1828    if (evt.isType(set_use_pipe_tag)) {
1829        fUsePipe = !!evt.getFast32();
1830        return true;
1831    }
1832    return this->INHERITED::onEvent(evt);
1833}
1834
1835bool SampleView::onQuery(SkEvent* evt) {
1836    if (evt->isType(is_sample_view_tag)) {
1837        return true;
1838    }
1839    return this->INHERITED::onQuery(evt);
1840}
1841
1842#ifdef TEST_GPIPE
1843    #include "SkGPipe.h"
1844
1845class SimplePC : public SkGPipeController {
1846public:
1847    SimplePC(SkCanvas* target);
1848    ~SimplePC();
1849
1850    /**
1851     * User this method to halt/restart pipe
1852     */
1853    void setWriteToPipe(bool writeToPipe) { fWriteToPipe = writeToPipe; }
1854    virtual void* requestBlock(size_t minRequest, size_t* actual);
1855    virtual void notifyWritten(size_t bytes);
1856
1857private:
1858    SkGPipeReader   fReader;
1859    void*           fBlock;
1860    size_t          fBlockSize;
1861    size_t          fBytesWritten;
1862    int             fAtomsWritten;
1863    SkGPipeReader::Status   fStatus;
1864    bool            fWriteToPipe;
1865
1866    size_t        fTotalWritten;
1867};
1868
1869SimplePC::SimplePC(SkCanvas* target) : fReader(target) {
1870    fBlock = NULL;
1871    fBlockSize = fBytesWritten = 0;
1872    fStatus = SkGPipeReader::kDone_Status;
1873    fTotalWritten = 0;
1874    fAtomsWritten = 0;
1875    fWriteToPipe = true;
1876}
1877
1878SimplePC::~SimplePC() {
1879//    SkASSERT(SkGPipeReader::kDone_Status == fStatus);
1880    if (fTotalWritten) {
1881        if (fWriteToPipe) {
1882            SkDebugf("--- %d bytes %d atoms, status %d\n", fTotalWritten,
1883                     fAtomsWritten, fStatus);
1884#ifdef  PIPE_FILE
1885            //File is open in append mode
1886            FILE* f = fopen(FILE_PATH, "ab");
1887            SkASSERT(f != NULL);
1888            fwrite((const char*)fBlock + fBytesWritten, 1, bytes, f);
1889            fclose(f);
1890#endif
1891#ifdef PIPE_NET
1892            if (fAtomsWritten > 1 && fTotalWritten > 4) { //ignore done
1893                gServer.acceptConnections();
1894                gServer.writePacket(fBlock, fTotalWritten);
1895            }
1896#endif
1897#ifdef  DEBUGGER
1898            gTempDataStore.reset();
1899            gTempDataStore.append(fTotalWritten, (const char*)fBlock);
1900#endif
1901        }
1902    }
1903    sk_free(fBlock);
1904}
1905
1906void* SimplePC::requestBlock(size_t minRequest, size_t* actual) {
1907    sk_free(fBlock);
1908
1909    fBlockSize = minRequest * 4;
1910    fBlock = sk_malloc_throw(fBlockSize);
1911    fBytesWritten = 0;
1912    *actual = fBlockSize;
1913    return fBlock;
1914}
1915
1916void SimplePC::notifyWritten(size_t bytes) {
1917    SkASSERT(fBytesWritten + bytes <= fBlockSize);
1918    fStatus = fReader.playback((const char*)fBlock + fBytesWritten, bytes);
1919    SkASSERT(SkGPipeReader::kError_Status != fStatus);
1920    fBytesWritten += bytes;
1921    fTotalWritten += bytes;
1922
1923    fAtomsWritten += 1;
1924}
1925
1926#endif
1927
1928void SampleView::draw(SkCanvas* canvas) {
1929#ifdef TEST_GPIPE
1930    if (fUsePipe) {
1931        SkGPipeWriter writer;
1932        SimplePC controller(canvas);
1933        uint32_t flags = SkGPipeWriter::kCrossProcess_Flag;
1934        canvas = writer.startRecording(&controller, flags);
1935        //Must draw before controller goes out of scope and sends data
1936        this->INHERITED::draw(canvas);
1937        //explicitly end recording to ensure writer is flushed before the memory
1938        //is freed in the deconstructor of the controller
1939        writer.endRecording();
1940        controller.setWriteToPipe(fUsePipe);
1941    }
1942    else
1943        this->INHERITED::draw(canvas);
1944#else
1945    this->INHERITED::draw(canvas);
1946#endif
1947}
1948void SampleView::onDraw(SkCanvas* canvas) {
1949    this->onDrawBackground(canvas);
1950
1951    for (int i = 0; i < fRepeatCount; i++) {
1952        SkAutoCanvasRestore acr(canvas, true);
1953        this->onDrawContent(canvas);
1954    }
1955}
1956
1957void SampleView::onDrawBackground(SkCanvas* canvas) {
1958    canvas->drawColor(fBGColor);
1959}
1960
1961///////////////////////////////////////////////////////////////////////////////
1962
1963template <typename T> void SkTBSort(T array[], int count) {
1964    for (int i = 1; i < count - 1; i++) {
1965        bool didSwap = false;
1966        for (int j = count - 1; j > i; --j) {
1967            if (array[j] < array[j-1]) {
1968                T tmp(array[j-1]);
1969                array[j-1] = array[j];
1970                array[j] = tmp;
1971                didSwap = true;
1972            }
1973        }
1974        if (!didSwap) {
1975            break;
1976        }
1977    }
1978
1979    for (int k = 0; k < count - 1; k++) {
1980        SkASSERT(!(array[k+1] < array[k]));
1981    }
1982}
1983
1984#include "SkRandom.h"
1985
1986static void rand_rect(SkIRect* rect, SkRandom& rand) {
1987    int bits = 8;
1988    int shift = 32 - bits;
1989    rect->set(rand.nextU() >> shift, rand.nextU() >> shift,
1990              rand.nextU() >> shift, rand.nextU() >> shift);
1991    rect->sort();
1992}
1993
1994static void dumpRect(const SkIRect& r) {
1995    SkDebugf(" { %d, %d, %d, %d },\n",
1996             r.fLeft, r.fTop,
1997             r.fRight, r.fBottom);
1998}
1999
2000static void test_rects(const SkIRect rect[], int count) {
2001    SkRegion rgn0, rgn1;
2002
2003    for (int i = 0; i < count; i++) {
2004        rgn0.op(rect[i], SkRegion::kUnion_Op);
2005     //   dumpRect(rect[i]);
2006    }
2007    rgn1.setRects(rect, count);
2008
2009    if (rgn0 != rgn1) {
2010        SkDebugf("\n");
2011        for (int i = 0; i < count; i++) {
2012            dumpRect(rect[i]);
2013        }
2014        SkDebugf("\n");
2015    }
2016}
2017
2018static void test() {
2019    size_t i;
2020
2021    const SkIRect r0[] = {
2022        { 0, 0, 1, 1 },
2023        { 2, 2, 3, 3 },
2024    };
2025    const SkIRect r1[] = {
2026        { 0, 0, 1, 3 },
2027        { 1, 1, 2, 2 },
2028        { 2, 0, 3, 3 },
2029    };
2030    const SkIRect r2[] = {
2031        { 0, 0, 1, 2 },
2032        { 2, 1, 3, 3 },
2033        { 4, 0, 5, 1 },
2034        { 6, 0, 7, 4 },
2035    };
2036
2037    static const struct {
2038        const SkIRect* fRects;
2039        int            fCount;
2040    } gRecs[] = {
2041        { r0, SK_ARRAY_COUNT(r0) },
2042        { r1, SK_ARRAY_COUNT(r1) },
2043        { r2, SK_ARRAY_COUNT(r2) },
2044    };
2045
2046    for (i = 0; i < SK_ARRAY_COUNT(gRecs); i++) {
2047        test_rects(gRecs[i].fRects, gRecs[i].fCount);
2048    }
2049
2050    SkRandom rand;
2051    for (i = 0; i < 10000; i++) {
2052        SkRegion rgn0, rgn1;
2053
2054        const int N = 8;
2055        SkIRect rect[N];
2056        for (int j = 0; j < N; j++) {
2057            rand_rect(&rect[j], rand);
2058        }
2059        test_rects(rect, N);
2060    }
2061}
2062
2063SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv) {
2064//    test();
2065    return new SampleWindow(hwnd, argc, argv, NULL);
2066}
2067
2068void get_preferred_size(int* x, int* y, int* width, int* height) {
2069    *x = 10;
2070    *y = 50;
2071    *width = 640;
2072    *height = 480;
2073}
2074
2075void application_init() {
2076//    setenv("ANDROID_ROOT", "../../../data", 0);
2077#ifdef SK_BUILD_FOR_MAC
2078    setenv("ANDROID_ROOT", "/android/device/data", 0);
2079#endif
2080    SkGraphics::Init();
2081    SkEvent::Init();
2082}
2083
2084void application_term() {
2085    SkEvent::Term();
2086    SkGraphics::Term();
2087}
2088