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