SampleApp.cpp revision 961ddb04a0a7aba843032d829ab867518e52559e
1#include "SkCanvas.h"
2#include "SkDevice.h"
3#include "SkGpuCanvas.h"
4#include "SkGraphics.h"
5#include "SkImageEncoder.h"
6#include "SkPaint.h"
7#include "SkPicture.h"
8#include "SkStream.h"
9#include "SkTime.h"
10#include "SkWindow.h"
11
12#include "SampleCode.h"
13#include "GrContext.h"
14#include "SkTouchGesture.h"
15#include "SkTypeface.h"
16
17#define USE_ARROWS_FOR_ZOOM true
18//#define DEFAULT_TO_GPU
19
20extern SkView* create_overview(int, const SkViewFactory[]);
21
22#define SK_SUPPORT_GL
23
24#define ANIMATING_EVENTTYPE "nextSample"
25#define ANIMATING_DELAY     750
26
27#ifdef SK_DEBUG
28    #define FPS_REPEAT_COUNT    10
29#else
30    #define FPS_REPEAT_COUNT    100
31#endif
32
33#ifdef SK_SUPPORT_GL
34    #include "GrGLConfig.h"
35#endif
36
37///////////////
38static const char view_inval_msg[] = "view-inval-msg";
39
40static void postInvalDelay(SkEventSinkID sinkID) {
41    SkEvent* evt = new SkEvent(view_inval_msg);
42    evt->post(sinkID, 1);
43}
44
45static bool isInvalEvent(const SkEvent& evt) {
46    return evt.isType(view_inval_msg);
47}
48//////////////////
49
50SkViewRegister* SkViewRegister::gHead;
51SkViewRegister::SkViewRegister(SkViewFactory fact) : fFact(fact) {
52    static bool gOnce;
53    if (!gOnce) {
54        gHead = NULL;
55        gOnce = true;
56    }
57
58    fChain = gHead;
59    gHead = this;
60}
61
62#if defined(SK_SUPPORT_GL)
63    #define SK_USE_SHADERS
64#endif
65
66#ifdef SK_BUILD_FOR_MAC
67#include <CoreFoundation/CoreFoundation.h>
68#include <CoreFoundation/CFURLAccess.h>
69
70static void testpdf() {
71    CFStringRef path = CFStringCreateWithCString(NULL, "/test.pdf",
72                                                 kCFStringEncodingUTF8);
73    CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path,
74                                              kCFURLPOSIXPathStyle,
75                                              false);
76    CFRelease(path);
77    CGRect box = CGRectMake(0, 0, 8*72, 10*72);
78    CGContextRef cg = CGPDFContextCreateWithURL(url, &box, NULL);
79    CFRelease(url);
80
81    CGContextBeginPage(cg, &box);
82    CGRect r = CGRectMake(10, 10, 40 + 0.5, 50 + 0.5);
83    CGContextFillEllipseInRect(cg, r);
84    CGContextEndPage(cg);
85    CGContextRelease(cg);
86
87    if (false) {
88        SkBitmap bm;
89        bm.setConfig(SkBitmap::kA8_Config, 64, 64);
90        bm.allocPixels();
91        bm.eraseColor(0);
92
93        SkCanvas canvas(bm);
94
95    }
96}
97#endif
98
99//////////////////////////////////////////////////////////////////////////////
100
101enum FlipAxisEnum {
102    kFlipAxis_X = (1 << 0),
103    kFlipAxis_Y = (1 << 1)
104};
105
106enum SkTriState {
107    kFalse_SkTriState,
108    kTrue_SkTriState,
109    kUnknown_SkTriState,
110};
111
112static SkTriState cycle_tristate(SkTriState state) {
113    static const SkTriState gCycle[] = {
114        /* kFalse_SkTriState   -> */  kUnknown_SkTriState,
115        /* kTrue_SkTriState    -> */  kFalse_SkTriState,
116        /* kUnknown_SkTriState -> */  kTrue_SkTriState,
117    };
118    return gCycle[state];
119}
120
121#include "SkDrawFilter.h"
122
123class FlagsDrawFilter : public SkDrawFilter {
124public:
125    FlagsDrawFilter(SkTriState lcd, SkTriState aa) : fLCDState(lcd), fAAState(aa) {}
126
127    virtual void filter(SkPaint* paint, Type t) {
128        if (kText_Type == t && kUnknown_SkTriState != fLCDState) {
129            paint->setLCDRenderText(kTrue_SkTriState == fLCDState);
130        }
131        if (kUnknown_SkTriState != fAAState) {
132            paint->setAntiAlias(kTrue_SkTriState == fAAState);
133        }
134    }
135
136private:
137    SkTriState  fLCDState;
138    SkTriState  fAAState;
139};
140
141//////////////////////////////////////////////////////////////////////////////
142
143#define MAX_ZOOM_LEVEL  8
144#define MIN_ZOOM_LEVEL  -8
145
146static const char gCharEvtName[] = "SampleCode_Char_Event";
147static const char gKeyEvtName[] = "SampleCode_Key_Event";
148static const char gTitleEvtName[] = "SampleCode_Title_Event";
149static const char gPrefSizeEvtName[] = "SampleCode_PrefSize_Event";
150static const char gFastTextEvtName[] = "SampleCode_FastText_Event";
151
152bool SampleCode::CharQ(const SkEvent& evt, SkUnichar* outUni) {
153    if (evt.isType(gCharEvtName, sizeof(gCharEvtName) - 1)) {
154        if (outUni) {
155            *outUni = evt.getFast32();
156        }
157        return true;
158    }
159    return false;
160}
161
162bool SampleCode::KeyQ(const SkEvent& evt, SkKey* outKey) {
163    if (evt.isType(gKeyEvtName, sizeof(gKeyEvtName) - 1)) {
164        if (outKey) {
165            *outKey = (SkKey)evt.getFast32();
166        }
167        return true;
168    }
169    return false;
170}
171
172bool SampleCode::TitleQ(const SkEvent& evt) {
173    return evt.isType(gTitleEvtName, sizeof(gTitleEvtName) - 1);
174}
175
176void SampleCode::TitleR(SkEvent* evt, const char title[]) {
177    SkASSERT(evt && TitleQ(*evt));
178    evt->setString(gTitleEvtName, title);
179}
180
181bool SampleCode::PrefSizeQ(const SkEvent& evt) {
182    return evt.isType(gPrefSizeEvtName, sizeof(gPrefSizeEvtName) - 1);
183}
184
185void SampleCode::PrefSizeR(SkEvent* evt, SkScalar width, SkScalar height) {
186    SkASSERT(evt && PrefSizeQ(*evt));
187    SkScalar size[2];
188    size[0] = width;
189    size[1] = height;
190    evt->setScalars(gPrefSizeEvtName, 2, size);
191}
192
193bool SampleCode::FastTextQ(const SkEvent& evt) {
194    return evt.isType(gFastTextEvtName, sizeof(gFastTextEvtName) - 1);
195}
196
197///////////////////////////////////////////////////////////////////////////////
198
199static SkMSec gAnimTime;
200static SkMSec gAnimTimePrev;
201
202SkMSec SampleCode::GetAnimTime() { return gAnimTime; }
203SkMSec SampleCode::GetAnimTimeDelta() { return gAnimTime - gAnimTimePrev; }
204SkScalar SampleCode::GetAnimSecondsDelta() {
205    return SkDoubleToScalar(GetAnimTimeDelta() / 1000.0);
206}
207
208SkScalar SampleCode::GetAnimScalar(SkScalar speed, SkScalar period) {
209    // since gAnimTime can be up to 32 bits, we can't convert it to a float
210    // or we'll lose the low bits. Hence we use doubles for the intermediate
211    // calculations
212    double seconds = (double)gAnimTime / 1000.0;
213    double value = SkScalarToDouble(speed) * seconds;
214    if (period) {
215        value = ::fmod(value, SkScalarToDouble(period));
216    }
217    return SkDoubleToScalar(value);
218}
219
220//////////////////////////////////////////////////////////////////////////////
221
222static SkView* curr_view(SkWindow* wind) {
223    SkView::F2BIter iter(wind);
224    return iter.next();
225}
226
227class SampleWindow : public SkOSWindow {
228    SkTDArray<SkViewFactory> fSamples;
229public:
230    SampleWindow(void* hwnd);
231    virtual ~SampleWindow();
232
233    virtual void draw(SkCanvas* canvas);
234
235protected:
236    virtual void onDraw(SkCanvas* canvas);
237    virtual bool onHandleKey(SkKey key);
238    virtual bool onHandleChar(SkUnichar);
239    virtual void onSizeChange();
240
241    virtual SkCanvas* beforeChildren(SkCanvas*);
242    virtual void afterChildren(SkCanvas*);
243    virtual void beforeChild(SkView* child, SkCanvas* canvas);
244    virtual void afterChild(SkView* child, SkCanvas* canvas);
245
246    virtual bool onEvent(const SkEvent& evt);
247    virtual bool onQuery(SkEvent* evt);
248
249    virtual bool onDispatchClick(int x, int y, Click::State);
250    virtual bool onClick(Click* click);
251    virtual Click* onFindClickHandler(SkScalar x, SkScalar y);
252
253#if 0
254    virtual bool handleChar(SkUnichar uni);
255    virtual bool handleEvent(const SkEvent& evt);
256    virtual bool handleKey(SkKey key);
257    virtual bool handleKeyUp(SkKey key);
258    virtual bool onHandleKeyUp(SkKey key);
259#endif
260
261private:
262    int fCurrIndex;
263
264    SkPicture* fPicture;
265    SkGpuCanvas* fGpuCanvas;
266    GrContext* fGrContext;
267    SkPath fClipPath;
268
269    SkTouchGesture fGesture;
270    int      fZoomLevel;
271    SkScalar fZoomScale;
272
273    enum CanvasType {
274        kRaster_CanvasType,
275        kPicture_CanvasType,
276        kGPU_CanvasType
277    };
278    CanvasType fCanvasType;
279
280    bool fUseClip;
281    bool fNClip;
282    bool fRepeatDrawing;
283    bool fAnimating;
284    bool fRotate;
285    bool fScale;
286    bool fRequestGrabImage;
287    bool fMeasureFPS;
288    SkMSec fMeasureFPS_Time;
289
290    // The following are for the 'fatbits' drawing
291    // Latest position of the mouse.
292    int fMouseX, fMouseY;
293    int fFatBitsScale;
294    // Used by the text showing position and color values.
295    SkTypeface* fTypeface;
296    bool fShowZoomer;
297
298    SkTriState fLCDState;
299    SkTriState fAAState;
300    unsigned   fFlipAxis;
301
302    int fScrollTestX, fScrollTestY;
303
304    bool make3DReady();
305    void changeZoomLevel(int delta);
306
307    void loadView(SkView*);
308    void updateTitle();
309    bool nextSample();
310
311    void toggleZoomer();
312    bool zoomIn();
313    bool zoomOut();
314    void updatePointer(int x, int y);
315
316    void postAnimatingEvent() {
317        if (fAnimating) {
318            SkEvent* evt = new SkEvent(ANIMATING_EVENTTYPE);
319            evt->post(this->getSinkID(), ANIMATING_DELAY);
320        }
321    }
322
323
324    static CanvasType cycle_canvastype(CanvasType);
325
326    typedef SkOSWindow INHERITED;
327};
328
329bool SampleWindow::zoomIn()
330{
331    // Arbitrarily decided
332    if (fFatBitsScale == 25) return false;
333    fFatBitsScale++;
334    this->inval(NULL);
335    return true;
336}
337
338bool SampleWindow::zoomOut()
339{
340    if (fFatBitsScale == 1) return false;
341    fFatBitsScale--;
342    this->inval(NULL);
343    return true;
344}
345
346void SampleWindow::toggleZoomer()
347{
348    fShowZoomer = !fShowZoomer;
349    this->inval(NULL);
350}
351
352void SampleWindow::updatePointer(int x, int y)
353{
354    fMouseX = x;
355    fMouseY = y;
356    if (fShowZoomer) {
357        this->inval(NULL);
358    }
359}
360
361bool SampleWindow::make3DReady() {
362
363#if defined(SK_SUPPORT_GL)
364    if (attachGL()) {
365        if (NULL != fGrContext) {
366        // various gr lifecycle tests
367        #if   0
368            fGrContext->freeGpuResources();
369        #elif 0
370            // this will leak resources.
371            fGrContext->contextLost();
372        #elif 0
373            GrAssert(1 == fGrContext->refcnt());
374            fGrContext->unref();
375            fGrContext = NULL;
376        #endif
377        }
378
379        if (NULL == fGrContext) {
380        #if defined(SK_USE_SHADERS)
381            fGrContext = GrContext::Create(kOpenGL_Shaders_GrEngine, NULL);
382        #else
383            fGrContext = GrContext::Create(kOpenGL_Fixed_GrEngine, NULL);
384        #endif
385            SkDebugf("---- constructor\n");
386        }
387
388        if (NULL != fGrContext) {
389            return true;
390        } else {
391            detachGL();
392        }
393    }
394#endif
395    SkDebugf("Failed to setup 3D");
396    return false;
397}
398
399SampleWindow::CanvasType SampleWindow::cycle_canvastype(CanvasType ct) {
400    static const CanvasType gCT[] = {
401        kPicture_CanvasType,
402        kGPU_CanvasType,
403        kRaster_CanvasType
404    };
405    return gCT[ct];
406}
407
408SampleWindow::SampleWindow(void* hwnd) : INHERITED(hwnd) {
409    fPicture = NULL;
410    fGpuCanvas = NULL;
411
412    fGrContext = NULL;
413
414#ifdef DEFAULT_TO_GPU
415    fCanvasType = kGPU_CanvasType;
416#else
417    fCanvasType = kRaster_CanvasType;
418#endif
419    fUseClip = false;
420    fNClip = false;
421    fRepeatDrawing = false;
422    fAnimating = false;
423    fRotate = false;
424    fScale = false;
425    fRequestGrabImage = false;
426    fMeasureFPS = false;
427    fLCDState = kUnknown_SkTriState;
428    fAAState = kUnknown_SkTriState;
429    fFlipAxis = 0;
430    fScrollTestX = fScrollTestY = 0;
431
432    fMouseX = fMouseY = 0;
433    fFatBitsScale = 8;
434    fTypeface = SkTypeface::CreateFromTypeface(NULL, SkTypeface::kBold);
435    fShowZoomer = false;
436
437    fZoomLevel = 0;
438    fZoomScale = SK_Scalar1;
439
440//    this->setConfig(SkBitmap::kRGB_565_Config);
441    this->setConfig(SkBitmap::kARGB_8888_Config);
442    this->setVisibleP(true);
443    this->setClipToBounds(false);
444
445    {
446        const SkViewRegister* reg = SkViewRegister::Head();
447        while (reg) {
448            *fSamples.append() = reg->factory();
449            reg = reg->next();
450        }
451    }
452    fCurrIndex = 0;
453    this->loadView(fSamples[fCurrIndex]());
454
455#ifdef SK_BUILD_FOR_MAC
456    testpdf();
457#endif
458}
459
460SampleWindow::~SampleWindow() {
461    delete fPicture;
462    delete fGpuCanvas;
463    if (NULL != fGrContext) {
464        fGrContext->unref();
465    }
466    fTypeface->unref();
467}
468
469static SkBitmap capture_bitmap(SkCanvas* canvas) {
470    SkBitmap bm;
471    const SkBitmap& src = canvas->getDevice()->accessBitmap(false);
472    src.copyTo(&bm, src.config());
473    return bm;
474}
475
476static bool bitmap_diff(SkCanvas* canvas, const SkBitmap& orig,
477                        SkBitmap* diff) {
478    const SkBitmap& src = canvas->getDevice()->accessBitmap(false);
479
480    SkAutoLockPixels alp0(src);
481    SkAutoLockPixels alp1(orig);
482    for (int y = 0; y < src.height(); y++) {
483        const void* srcP = src.getAddr(0, y);
484        const void* origP = orig.getAddr(0, y);
485        size_t bytes = src.width() * src.bytesPerPixel();
486        if (memcmp(srcP, origP, bytes)) {
487            SkDebugf("---------- difference on line %d\n", y);
488            return true;
489        }
490    }
491    return false;
492}
493
494static void drawText(SkCanvas* canvas, SkString string, SkScalar left, SkScalar top, SkPaint& paint)
495{
496    SkColor desiredColor = paint.getColor();
497    paint.setColor(SK_ColorWHITE);
498    const char* c_str = string.c_str();
499    size_t size = string.size();
500    SkRect bounds;
501    paint.measureText(c_str, size, &bounds);
502    bounds.offset(left, top);
503    SkScalar inset = SkIntToScalar(-2);
504    bounds.inset(inset, inset);
505    canvas->drawRect(bounds, paint);
506    if (desiredColor != SK_ColorBLACK) {
507        paint.setColor(SK_ColorBLACK);
508        canvas->drawText(c_str, size, left + SK_Scalar1, top + SK_Scalar1, paint);
509    }
510    paint.setColor(desiredColor);
511    canvas->drawText(c_str, size, left, top, paint);
512}
513
514#define XCLIP_N  8
515#define YCLIP_N  8
516
517void SampleWindow::draw(SkCanvas* canvas) {
518    // update the animation time
519    gAnimTimePrev = gAnimTime;
520    gAnimTime = SkTime::GetMSecs();
521
522    SkScalar cx = SkScalarHalf(this->width());
523    SkScalar cy = SkScalarHalf(this->height());
524
525    if (fZoomLevel) {
526        SkMatrix m;
527        SkPoint center;
528        m = canvas->getTotalMatrix();//.invert(&m);
529        m.mapXY(cx, cy, &center);
530        cx = center.fX;
531        cy = center.fY;
532
533        m.setTranslate(-cx, -cy);
534        m.postScale(fZoomScale, fZoomScale);
535        m.postTranslate(cx, cy);
536
537        canvas->concat(m);
538    }
539
540    if (fFlipAxis) {
541        SkMatrix m;
542        m.setTranslate(cx, cy);
543        if (fFlipAxis & kFlipAxis_X) {
544            m.preScale(-SK_Scalar1, SK_Scalar1);
545        }
546        if (fFlipAxis & kFlipAxis_Y) {
547            m.preScale(SK_Scalar1, -SK_Scalar1);
548        }
549        m.preTranslate(-cx, -cy);
550        canvas->concat(m);
551    }
552
553    // Apply any gesture matrix
554    if (true) {
555        const SkMatrix& localM = fGesture.localM();
556        if (localM.getType() & SkMatrix::kScale_Mask) {
557            canvas->setExternalMatrix(&localM);
558        }
559        canvas->concat(localM);
560        canvas->concat(fGesture.globalM());
561
562        if (fGesture.isActive()) {
563            this->inval(NULL);
564        }
565    }
566
567    if (fNClip) {
568        this->INHERITED::draw(canvas);
569        SkBitmap orig = capture_bitmap(canvas);
570
571        const SkScalar w = this->width();
572        const SkScalar h = this->height();
573        const SkScalar cw = w / XCLIP_N;
574        const SkScalar ch = h / YCLIP_N;
575        for (int y = 0; y < YCLIP_N; y++) {
576            SkRect r;
577            r.fTop = y * ch;
578            r.fBottom = (y + 1) * ch;
579            if (y == YCLIP_N - 1) {
580                r.fBottom = h;
581            }
582            for (int x = 0; x < XCLIP_N; x++) {
583                SkAutoCanvasRestore acr(canvas, true);
584                r.fLeft = x * cw;
585                r.fRight = (x + 1) * cw;
586                if (x == XCLIP_N - 1) {
587                    r.fRight = w;
588                }
589                canvas->clipRect(r);
590                this->INHERITED::draw(canvas);
591            }
592        }
593
594        SkBitmap diff;
595        if (bitmap_diff(canvas, orig, &diff)) {
596        }
597    } else {
598        this->INHERITED::draw(canvas);
599    }
600    if (fShowZoomer) {
601        int count = canvas->save();
602        canvas->resetMatrix();
603        // Ensure the mouse position is on screen.
604        int width = SkScalarRound(this->width());
605        int height = SkScalarRound(this->height());
606        if (fMouseX >= width) fMouseX = width - 1;
607        else if (fMouseX < 0) fMouseX = 0;
608        if (fMouseY >= height) fMouseY = height - 1;
609        else if (fMouseY < 0) fMouseY = 0;
610        SkBitmap bitmap = capture_bitmap(canvas);
611        // Find the size of the zoomed in view, forced to be odd, so the examined pixel is in the middle.
612        int zoomedWidth = (width >> 1) | 1;
613        int zoomedHeight = (height >> 1) | 1;
614        SkIRect src;
615        src.set(0, 0, zoomedWidth / fFatBitsScale, zoomedHeight / fFatBitsScale);
616        src.offset(fMouseX - (src.width()>>1), fMouseY - (src.height()>>1));
617        SkRect dest;
618        dest.set(0, 0, SkIntToScalar(zoomedWidth), SkIntToScalar(zoomedHeight));
619        dest.offset(SkIntToScalar(width - zoomedWidth), SkIntToScalar(height - zoomedHeight));
620        SkPaint paint;
621        // Clear the background behind our zoomed in view
622        paint.setColor(SK_ColorWHITE);
623        canvas->drawRect(dest, paint);
624        canvas->drawBitmapRect(bitmap, &src, dest);
625        paint.setColor(SK_ColorBLACK);
626        paint.setStyle(SkPaint::kStroke_Style);
627        // Draw a border around the pixel in the middle
628        SkRect originalPixel;
629        originalPixel.set(SkIntToScalar(fMouseX), SkIntToScalar(fMouseY), SkIntToScalar(fMouseX + 1), SkIntToScalar(fMouseY + 1));
630        SkMatrix matrix;
631        SkRect scalarSrc;
632        scalarSrc.set(src);
633        SkColor color = bitmap.getColor(fMouseX, fMouseY);
634        if (matrix.setRectToRect(scalarSrc, dest, SkMatrix::kFill_ScaleToFit)) {
635            SkRect pixel;
636            matrix.mapRect(&pixel, originalPixel);
637            // TODO Perhaps measure the values and make the outline white if it's "dark"
638            if (color == SK_ColorBLACK) {
639                paint.setColor(SK_ColorWHITE);
640            }
641            canvas->drawRect(pixel, paint);
642        }
643        paint.setColor(SK_ColorBLACK);
644        // Draw a border around the destination rectangle
645        canvas->drawRect(dest, paint);
646        paint.setStyle(SkPaint::kStrokeAndFill_Style);
647        // Identify the pixel and its color on screen
648        paint.setTypeface(fTypeface);
649        paint.setAntiAlias(true);
650        SkScalar lineHeight = paint.getFontMetrics(NULL);
651        SkString string;
652        string.appendf("(%i, %i)", fMouseX, fMouseY);
653        SkScalar left = dest.fLeft + SkIntToScalar(3);
654        SkScalar i = SK_Scalar1;
655        drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
656        // Alpha
657        i += SK_Scalar1;
658        string.reset();
659        string.appendf("A: %X", SkColorGetA(color));
660        drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
661        // Red
662        i += SK_Scalar1;
663        string.reset();
664        string.appendf("R: %X", SkColorGetR(color));
665        paint.setColor(SK_ColorRED);
666        drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
667        // Green
668        i += SK_Scalar1;
669        string.reset();
670        string.appendf("G: %X", SkColorGetG(color));
671        paint.setColor(SK_ColorGREEN);
672        drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
673        // Blue
674        i += SK_Scalar1;
675        string.reset();
676        string.appendf("B: %X", SkColorGetB(color));
677        paint.setColor(SK_ColorBLUE);
678        drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
679        canvas->restoreToCount(count);
680    }
681}
682
683void SampleWindow::onDraw(SkCanvas* canvas) {
684    if (fRepeatDrawing) {
685        this->inval(NULL);
686    }
687}
688
689#include "SkColorPriv.h"
690
691static void reverseRedAndBlue(const SkBitmap& bm) {
692    SkASSERT(bm.config() == SkBitmap::kARGB_8888_Config);
693    uint8_t* p = (uint8_t*)bm.getPixels();
694    uint8_t* stop = p + bm.getSize();
695    while (p < stop) {
696        // swap red/blue (to go from ARGB(int) to RGBA(memory) and premultiply
697        unsigned scale = SkAlpha255To256(p[3]);
698        unsigned r = p[2];
699        unsigned b = p[0];
700        p[0] = SkAlphaMul(r, scale);
701        p[1] = SkAlphaMul(p[1], scale);
702        p[2] = SkAlphaMul(b, scale);
703        p += 4;
704    }
705}
706
707SkCanvas* SampleWindow::beforeChildren(SkCanvas* canvas) {
708    if (kGPU_CanvasType != fCanvasType) {
709#ifdef SK_SUPPORT_GL
710        detachGL();
711#endif
712    }
713
714    switch (fCanvasType) {
715        case kRaster_CanvasType:
716            canvas = this->INHERITED::beforeChildren(canvas);
717            break;
718        case kPicture_CanvasType:
719            fPicture = new SkPicture;
720            canvas = fPicture->beginRecording(9999, 9999);
721            break;
722        case kGPU_CanvasType: {
723            if (make3DReady()) {
724                SkDevice* device = canvas->getDevice();
725                const SkBitmap& bitmap = device->accessBitmap(true);
726
727                GrRenderTarget* renderTarget;
728                renderTarget = fGrContext->createRenderTargetFrom3DApiState();
729                fGpuCanvas = new SkGpuCanvas(fGrContext, renderTarget);
730                renderTarget->unref();
731
732                device = fGpuCanvas->createDevice(SkBitmap::kARGB_8888_Config,
733                                                  bitmap.width(), bitmap.height(),
734                                                  false, false);
735                fGpuCanvas->setDevice(device)->unref();
736
737                fGpuCanvas->concat(canvas->getTotalMatrix());
738                canvas = fGpuCanvas;
739
740            } else {
741                canvas = this->INHERITED::beforeChildren(canvas);
742            }
743            break;
744        }
745    }
746
747    if (fUseClip) {
748        canvas->drawColor(0xFFFF88FF);
749        canvas->clipPath(fClipPath);
750    }
751
752    return canvas;
753}
754
755static void paint_rgn(const SkBitmap& bm, const SkIRect& r,
756                      const SkRegion& rgn) {
757    SkCanvas    canvas(bm);
758    SkRegion    inval(rgn);
759
760    inval.translate(r.fLeft, r.fTop);
761    canvas.clipRegion(inval);
762    canvas.drawColor(0xFFFF8080);
763}
764
765void SampleWindow::afterChildren(SkCanvas* orig) {
766    if (fRequestGrabImage) {
767        fRequestGrabImage = false;
768
769        SkCanvas* canvas = fGpuCanvas ? fGpuCanvas : orig;
770        SkDevice* device = canvas->getDevice();
771        SkBitmap bmp;
772        if (device->accessBitmap(false).copyTo(&bmp, SkBitmap::kARGB_8888_Config)) {
773            static int gSampleGrabCounter;
774            SkString name;
775            name.printf("sample_grab_%d", gSampleGrabCounter++);
776            SkImageEncoder::EncodeFile(name.c_str(), bmp,
777                                       SkImageEncoder::kPNG_Type, 100);
778        }
779    }
780
781    switch (fCanvasType) {
782        case kRaster_CanvasType:
783            break;
784        case kPicture_CanvasType:
785            if (true) {
786                SkPicture* pict = new SkPicture(*fPicture);
787                fPicture->unref();
788                orig->drawPicture(*pict);
789                pict->unref();
790            } else if (true) {
791                SkDynamicMemoryWStream ostream;
792                fPicture->serialize(&ostream);
793                fPicture->unref();
794
795                SkMemoryStream istream(ostream.getStream(), ostream.getOffset());
796                SkPicture pict(&istream);
797                orig->drawPicture(pict);
798            } else {
799                fPicture->draw(orig);
800                fPicture->unref();
801            }
802            fPicture = NULL;
803            break;
804#ifdef SK_SUPPORT_GL
805        case kGPU_CanvasType:
806            delete fGpuCanvas;
807            fGpuCanvas = NULL;
808            presentGL();
809            break;
810#endif
811    }
812
813    // Do this after presentGL and other finishing, rather than in afterChild
814    if (fMeasureFPS && fMeasureFPS_Time) {
815        fMeasureFPS_Time = SkTime::GetMSecs() - fMeasureFPS_Time;
816        this->updateTitle();
817        postInvalDelay(this->getSinkID());
818    }
819
820    //    if ((fScrollTestX | fScrollTestY) != 0)
821    if (false) {
822        const SkBitmap& bm = orig->getDevice()->accessBitmap(true);
823        int dx = fScrollTestX * 7;
824        int dy = fScrollTestY * 7;
825        SkIRect r;
826        SkRegion inval;
827
828        r.set(50, 50, 50+100, 50+100);
829        bm.scrollRect(&r, dx, dy, &inval);
830        paint_rgn(bm, r, inval);
831    }
832}
833
834void SampleWindow::beforeChild(SkView* child, SkCanvas* canvas) {
835    if (fScale) {
836        SkScalar scale = SK_Scalar1 * 7 / 10;
837        SkScalar cx = this->width() / 2;
838        SkScalar cy = this->height() / 2;
839        canvas->translate(cx, cy);
840        canvas->scale(scale, scale);
841        canvas->translate(-cx, -cy);
842    }
843    if (fRotate) {
844        SkScalar cx = this->width() / 2;
845        SkScalar cy = this->height() / 2;
846        canvas->translate(cx, cy);
847        canvas->rotate(SkIntToScalar(30));
848        canvas->translate(-cx, -cy);
849    }
850
851    if (kUnknown_SkTriState != fLCDState ||
852        kUnknown_SkTriState != fAAState) {
853        canvas->setDrawFilter(new FlagsDrawFilter(fLCDState, fAAState))->unref();
854    }
855
856    if (fMeasureFPS) {
857        fMeasureFPS_Time = 0;   // 0 means the child is not aware of repeat-draw
858        if (SampleView::SetRepeatDraw(child, FPS_REPEAT_COUNT)) {
859            fMeasureFPS_Time = SkTime::GetMSecs();
860        }
861    } else {
862        (void)SampleView::SetRepeatDraw(child, 1);
863    }
864}
865
866void SampleWindow::afterChild(SkView* child, SkCanvas* canvas) {
867    canvas->setDrawFilter(NULL);
868}
869
870static SkBitmap::Config gConfigCycle[] = {
871    SkBitmap::kNo_Config,           // none -> none
872    SkBitmap::kNo_Config,           // a1 -> none
873    SkBitmap::kNo_Config,           // a8 -> none
874    SkBitmap::kNo_Config,           // index8 -> none
875    SkBitmap::kARGB_4444_Config,    // 565 -> 4444
876    SkBitmap::kARGB_8888_Config,    // 4444 -> 8888
877    SkBitmap::kRGB_565_Config       // 8888 -> 565
878};
879
880static SkBitmap::Config cycle_configs(SkBitmap::Config c) {
881    return gConfigCycle[c];
882}
883
884void SampleWindow::changeZoomLevel(int delta) {
885    fZoomLevel += delta;
886    if (fZoomLevel > 0) {
887        fZoomLevel = SkMin32(fZoomLevel, MAX_ZOOM_LEVEL);
888        fZoomScale = SkIntToScalar(fZoomLevel + 1);
889    } else if (fZoomLevel < 0) {
890        fZoomLevel = SkMax32(fZoomLevel, MIN_ZOOM_LEVEL);
891        fZoomScale = SK_Scalar1 / (1 - fZoomLevel);
892    } else {
893        fZoomScale = SK_Scalar1;
894    }
895
896    this->inval(NULL);
897}
898
899bool SampleWindow::nextSample() {
900    fCurrIndex = (fCurrIndex + 1) % fSamples.count();
901    this->loadView(fSamples[fCurrIndex]());
902    return true;
903}
904
905bool SampleWindow::onEvent(const SkEvent& evt) {
906    if (evt.isType(ANIMATING_EVENTTYPE)) {
907        if (fAnimating) {
908            this->nextSample();
909            this->postAnimatingEvent();
910        }
911        return true;
912    }
913    if (evt.isType("set-curr-index")) {
914        fCurrIndex = evt.getFast32() % fSamples.count();
915        this->loadView(fSamples[fCurrIndex]());
916        return true;
917    }
918    if (isInvalEvent(evt)) {
919        this->inval(NULL);
920        return true;
921    }
922    return this->INHERITED::onEvent(evt);
923}
924
925bool SampleWindow::onQuery(SkEvent* query) {
926    if (query->isType("get-slide-count")) {
927        query->setFast32(fSamples.count());
928        return true;
929    }
930    if (query->isType("get-slide-title")) {
931        SkView* view = fSamples[query->getFast32()]();
932        SkEvent evt(gTitleEvtName);
933        if (view->doQuery(&evt)) {
934            query->setString("title", evt.findString(gTitleEvtName));
935        }
936        SkSafeUnref(view);
937        return true;
938    }
939    if (query->isType("use-fast-text")) {
940        SkEvent evt(gFastTextEvtName);
941        return curr_view(this)->doQuery(&evt);
942    }
943    return this->INHERITED::onQuery(query);
944}
945
946static void cleanup_for_filename(SkString* name) {
947    char* str = name->writable_str();
948    for (size_t i = 0; i < name->size(); i++) {
949        switch (str[i]) {
950            case ':': str[i] = '-'; break;
951            case '/': str[i] = '-'; break;
952            case ' ': str[i] = '_'; break;
953            default: break;
954        }
955    }
956}
957
958bool SampleWindow::onHandleChar(SkUnichar uni) {
959    {
960        SkView* view = curr_view(this);
961        if (view) {
962            SkEvent evt(gCharEvtName);
963            evt.setFast32(uni);
964            if (view->doQuery(&evt)) {
965                return true;
966            }
967        }
968    }
969
970    int dx = 0xFF;
971    int dy = 0xFF;
972
973    switch (uni) {
974        case '5': dx =  0; dy =  0; break;
975        case '8': dx =  0; dy = -1; break;
976        case '6': dx =  1; dy =  0; break;
977        case '2': dx =  0; dy =  1; break;
978        case '4': dx = -1; dy =  0; break;
979        case '7': dx = -1; dy = -1; break;
980        case '9': dx =  1; dy = -1; break;
981        case '3': dx =  1; dy =  1; break;
982        case '1': dx = -1; dy =  1; break;
983
984        default:
985            break;
986    }
987
988    if (0xFF != dx && 0xFF != dy) {
989        if ((dx | dy) == 0) {
990            fScrollTestX = fScrollTestY = 0;
991        } else {
992            fScrollTestX += dx;
993            fScrollTestY += dy;
994        }
995        this->inval(NULL);
996        return true;
997    }
998
999    switch (uni) {
1000        case 'a':
1001            fAnimating = !fAnimating;
1002            this->postAnimatingEvent();
1003            this->updateTitle();
1004            return true;
1005        case 'b':
1006            fAAState = cycle_tristate(fAAState);
1007            this->updateTitle();
1008            this->inval(NULL);
1009            break;
1010        case 'c':
1011            fUseClip = !fUseClip;
1012            this->inval(NULL);
1013            this->updateTitle();
1014            return true;
1015        case 'd':
1016            SkGraphics::SetFontCacheUsed(0);
1017            return true;
1018        case 'f':
1019            fMeasureFPS = !fMeasureFPS;
1020            this->inval(NULL);
1021            break;
1022        case 'g':
1023            fRequestGrabImage = true;
1024            this->inval(NULL);
1025            break;
1026        case 'i':
1027            this->zoomIn();
1028            break;
1029        case 'l':
1030            fLCDState = cycle_tristate(fLCDState);
1031            this->updateTitle();
1032            this->inval(NULL);
1033            break;
1034        case 'o':
1035            this->zoomOut();
1036            break;
1037        case 'r':
1038            fRotate = !fRotate;
1039            this->inval(NULL);
1040            this->updateTitle();
1041            return true;
1042        case 's':
1043            fScale = !fScale;
1044            this->inval(NULL);
1045            this->updateTitle();
1046            return true;
1047        case 'x':
1048            fFlipAxis ^= kFlipAxis_X;
1049            this->updateTitle();
1050            this->inval(NULL);
1051            break;
1052        case 'y':
1053            fFlipAxis ^= kFlipAxis_Y;
1054            this->updateTitle();
1055            this->inval(NULL);
1056            break;
1057        case 'z':
1058            this->toggleZoomer();
1059            break;
1060        default:
1061            break;
1062    }
1063
1064    return this->INHERITED::onHandleChar(uni);
1065}
1066
1067#include "SkDumpCanvas.h"
1068
1069bool SampleWindow::onHandleKey(SkKey key) {
1070    {
1071        SkView* view = curr_view(this);
1072        if (view) {
1073            SkEvent evt(gKeyEvtName);
1074            evt.setFast32(key);
1075            if (view->doQuery(&evt)) {
1076                return true;
1077            }
1078        }
1079    }
1080
1081    switch (key) {
1082        case kRight_SkKey:
1083            if (this->nextSample()) {
1084                return true;
1085            }
1086            break;
1087        case kLeft_SkKey:
1088            fCanvasType = cycle_canvastype(fCanvasType);
1089            this->updateTitle();
1090            this->inval(NULL);
1091            return true;
1092        case kUp_SkKey:
1093            if (USE_ARROWS_FOR_ZOOM) {
1094                this->changeZoomLevel(1);
1095            } else {
1096                fNClip = !fNClip;
1097                this->inval(NULL);
1098            }
1099            this->updateTitle();
1100            return true;
1101        case kDown_SkKey:
1102            if (USE_ARROWS_FOR_ZOOM) {
1103                this->changeZoomLevel(-1);
1104            } else {
1105                this->setConfig(cycle_configs(this->getBitmap().config()));
1106            }
1107            this->updateTitle();
1108            return true;
1109        case kOK_SkKey:
1110            if (false) {
1111                SkDebugfDumper dumper;
1112                SkDumpCanvas dc(&dumper);
1113                this->draw(&dc);
1114            } else {
1115                fRepeatDrawing = !fRepeatDrawing;
1116                if (fRepeatDrawing) {
1117                    this->inval(NULL);
1118                }
1119            }
1120            return true;
1121        case kBack_SkKey:
1122            this->loadView(NULL);
1123            return true;
1124        default:
1125            break;
1126    }
1127    return this->INHERITED::onHandleKey(key);
1128}
1129
1130///////////////////////////////////////////////////////////////////////////////
1131
1132static const char gGestureClickType[] = "GestureClickType";
1133
1134bool SampleWindow::onDispatchClick(int x, int y, Click::State state) {
1135    if (Click::kMoved_State == state) {
1136        updatePointer(x, y);
1137    }
1138    int w = SkScalarRound(this->width());
1139    int h = SkScalarRound(this->height());
1140
1141    // check for the resize-box
1142    if (w - x < 16 && h - y < 16) {
1143        return false;   // let the OS handle the click
1144    } else {
1145        return this->INHERITED::onDispatchClick(x, y, state);
1146    }
1147}
1148
1149class GestureClick : public SkView::Click {
1150public:
1151    GestureClick(SkView* target) : SkView::Click(target) {
1152        this->setType(gGestureClickType);
1153    }
1154
1155    static bool IsGesture(Click* click) {
1156        return click->isType(gGestureClickType);
1157    }
1158};
1159
1160SkView::Click* SampleWindow::onFindClickHandler(SkScalar x, SkScalar y) {
1161    return new GestureClick(this);
1162}
1163
1164bool SampleWindow::onClick(Click* click) {
1165    if (GestureClick::IsGesture(click)) {
1166        float x = SkScalarToFloat(click->fCurr.fX);
1167        float y = SkScalarToFloat(click->fCurr.fY);
1168        switch (click->fState) {
1169            case SkView::Click::kDown_State:
1170                fGesture.touchBegin(click, x, y);
1171                break;
1172            case SkView::Click::kMoved_State:
1173                fGesture.touchMoved(click, x, y);
1174                this->inval(NULL);
1175                break;
1176            case SkView::Click::kUp_State:
1177                fGesture.touchEnd(click);
1178                this->inval(NULL);
1179                break;
1180        }
1181        return true;
1182    }
1183    return false;
1184}
1185
1186///////////////////////////////////////////////////////////////////////////////
1187
1188void SampleWindow::loadView(SkView* view) {
1189    SkView::F2BIter iter(this);
1190    SkView* prev = iter.next();
1191    if (prev) {
1192        prev->detachFromParent();
1193    }
1194
1195    if (NULL == view) {
1196        view = create_overview(fSamples.count(), fSamples.begin());
1197    }
1198    view->setVisibleP(true);
1199    view->setClipToBounds(false);
1200    this->attachChildToFront(view)->unref();
1201    view->setSize(this->width(), this->height());
1202
1203    this->updateTitle();
1204}
1205
1206static const char* gConfigNames[] = {
1207    "unknown config",
1208    "A1",
1209    "A8",
1210    "Index8",
1211    "565",
1212    "4444",
1213    "8888"
1214};
1215
1216static const char* configToString(SkBitmap::Config c) {
1217    return gConfigNames[c];
1218}
1219
1220static const char* gCanvasTypePrefix[] = {
1221    "raster: ",
1222    "picture: ",
1223    "opengl: "
1224};
1225
1226static const char* trystate_str(SkTriState state,
1227                                const char trueStr[], const char falseStr[]) {
1228    if (kTrue_SkTriState == state) {
1229        return trueStr;
1230    } else if (kFalse_SkTriState == state) {
1231        return falseStr;
1232    }
1233    return NULL;
1234}
1235
1236void SampleWindow::updateTitle() {
1237    SkString title;
1238
1239    SkView::F2BIter iter(this);
1240    SkView* view = iter.next();
1241    SkEvent evt(gTitleEvtName);
1242    if (view->doQuery(&evt)) {
1243        title.set(evt.findString(gTitleEvtName));
1244    }
1245    if (title.size() == 0) {
1246        title.set("<unknown>");
1247    }
1248
1249    title.prepend(gCanvasTypePrefix[fCanvasType]);
1250
1251    title.prepend(" ");
1252    title.prepend(configToString(this->getBitmap().config()));
1253
1254    if (fAnimating) {
1255        title.prepend("<A> ");
1256    }
1257    if (fScale) {
1258        title.prepend("<S> ");
1259    }
1260    if (fRotate) {
1261        title.prepend("<R> ");
1262    }
1263    if (fNClip) {
1264        title.prepend("<C> ");
1265    }
1266
1267    title.prepend(trystate_str(fLCDState, "LCD ", "lcd "));
1268    title.prepend(trystate_str(fAAState, "AA ", "aa "));
1269    title.prepend(fFlipAxis & kFlipAxis_X ? "X " : NULL);
1270    title.prepend(fFlipAxis & kFlipAxis_Y ? "Y " : NULL);
1271
1272    if (fZoomLevel) {
1273        title.prependf("{%d} ", fZoomLevel);
1274    }
1275
1276    if (fMeasureFPS) {
1277        title.appendf(" %4d ms", fMeasureFPS_Time);
1278    }
1279
1280    this->setTitle(title.c_str());
1281}
1282
1283void SampleWindow::onSizeChange() {
1284    this->INHERITED::onSizeChange();
1285
1286    SkView::F2BIter iter(this);
1287    SkView* view = iter.next();
1288    view->setSize(this->width(), this->height());
1289
1290    // rebuild our clippath
1291    {
1292        const SkScalar W = this->width();
1293        const SkScalar H = this->height();
1294
1295        fClipPath.reset();
1296#if 0
1297        for (SkScalar y = SK_Scalar1; y < H; y += SkIntToScalar(32)) {
1298            SkRect r;
1299            r.set(SK_Scalar1, y, SkIntToScalar(30), y + SkIntToScalar(30));
1300            for (; r.fLeft < W; r.offset(SkIntToScalar(32), 0))
1301                fClipPath.addRect(r);
1302        }
1303#else
1304        SkRect r;
1305        r.set(0, 0, W, H);
1306        fClipPath.addRect(r, SkPath::kCCW_Direction);
1307        r.set(W/4, H/4, W*3/4, H*3/4);
1308        fClipPath.addRect(r, SkPath::kCW_Direction);
1309#endif
1310    }
1311
1312    this->updateTitle();    // to refresh our config
1313}
1314
1315///////////////////////////////////////////////////////////////////////////////
1316
1317static const char repeat_count_tag[] = "sample-set-repeat-count";
1318
1319bool SampleView::SetRepeatDraw(SkView* view, int count) {
1320    SkEvent evt(repeat_count_tag);
1321    evt.setFast32(count);
1322    return view->doEvent(evt);
1323}
1324
1325bool SampleView::onEvent(const SkEvent& evt) {
1326    if (evt.isType(repeat_count_tag)) {
1327        fRepeatCount = evt.getFast32();
1328        return true;
1329    }
1330    return this->INHERITED::onEvent(evt);
1331}
1332
1333bool SampleView::onQuery(SkEvent* evt) {
1334    return this->INHERITED::onQuery(evt);
1335}
1336
1337#define TEST_GPIPE
1338
1339#ifdef TEST_GPIPE
1340    #include "SkGPipe.h"
1341
1342class SimplePC : public SkGPipeController {
1343public:
1344    SimplePC(SkCanvas* target);
1345    ~SimplePC();
1346
1347    virtual void* requestBlock(size_t minRequest, size_t* actual);
1348    virtual void notifyWritten(size_t bytes);
1349
1350private:
1351    SkGPipeReader   fReader;
1352    void*           fBlock;
1353    size_t          fBlockSize;
1354    size_t          fBytesWritten;
1355    int             fAtomsWritten;
1356    SkGPipeReader::Status   fStatus;
1357
1358    size_t        fTotalWritten;
1359};
1360
1361SimplePC::SimplePC(SkCanvas* target) : fReader(target) {
1362    fBlock = NULL;
1363    fBlockSize = fBytesWritten = 0;
1364    fStatus = SkGPipeReader::kDone_Status;
1365    fTotalWritten = 0;
1366    fAtomsWritten = 0;
1367}
1368
1369SimplePC::~SimplePC() {
1370//    SkASSERT(SkGPipeReader::kDone_Status == fStatus);
1371    sk_free(fBlock);
1372
1373    SkDebugf("--- %d bytes %d atoms, status %d\n", fTotalWritten,
1374             fAtomsWritten, fStatus);
1375}
1376
1377void* SimplePC::requestBlock(size_t minRequest, size_t* actual) {
1378    sk_free(fBlock);
1379
1380    fBlockSize = minRequest * 4;
1381    fBlock = sk_malloc_throw(fBlockSize);
1382    fBytesWritten = 0;
1383    *actual = fBlockSize;
1384    return fBlock;
1385}
1386
1387void SimplePC::notifyWritten(size_t bytes) {
1388    SkASSERT(fBytesWritten + bytes <= fBlockSize);
1389
1390    fStatus = fReader.playback((const char*)fBlock + fBytesWritten, bytes);
1391    SkASSERT(SkGPipeReader::kError_Status != fStatus);
1392    fBytesWritten += bytes;
1393    fTotalWritten += bytes;
1394
1395    fAtomsWritten += 1;
1396}
1397
1398#endif
1399
1400
1401void SampleView::onDraw(SkCanvas* canvas) {
1402    this->onDrawBackground(canvas);
1403
1404#ifdef TEST_GPIPE
1405    SimplePC controller(canvas);
1406    SkGPipeWriter writer;
1407    canvas = writer.startRecording(&controller);
1408#endif
1409
1410    for (int i = 0; i < fRepeatCount; i++) {
1411        SkAutoCanvasRestore acr(canvas, true);
1412        this->onDrawContent(canvas);
1413    }
1414}
1415
1416void SampleView::onDrawBackground(SkCanvas* canvas) {
1417    canvas->drawColor(fBGColor);
1418}
1419
1420///////////////////////////////////////////////////////////////////////////////
1421
1422template <typename T> void SkTBSort(T array[], int count) {
1423    for (int i = 1; i < count - 1; i++) {
1424        bool didSwap = false;
1425        for (int j = count - 1; j > i; --j) {
1426            if (array[j] < array[j-1]) {
1427                T tmp(array[j-1]);
1428                array[j-1] = array[j];
1429                array[j] = tmp;
1430                didSwap = true;
1431            }
1432        }
1433        if (!didSwap) {
1434            break;
1435        }
1436    }
1437
1438    for (int k = 0; k < count - 1; k++) {
1439        SkASSERT(!(array[k+1] < array[k]));
1440    }
1441}
1442
1443#include "SkRandom.h"
1444
1445static void rand_rect(SkIRect* rect, SkRandom& rand) {
1446    int bits = 8;
1447    int shift = 32 - bits;
1448    rect->set(rand.nextU() >> shift, rand.nextU() >> shift,
1449              rand.nextU() >> shift, rand.nextU() >> shift);
1450    rect->sort();
1451}
1452
1453static void dumpRect(const SkIRect& r) {
1454    SkDebugf(" { %d, %d, %d, %d },\n",
1455             r.fLeft, r.fTop,
1456             r.fRight, r.fBottom);
1457}
1458
1459static void test_rects(const SkIRect rect[], int count) {
1460    SkRegion rgn0, rgn1;
1461
1462    for (int i = 0; i < count; i++) {
1463        rgn0.op(rect[i], SkRegion::kUnion_Op);
1464     //   dumpRect(rect[i]);
1465    }
1466    rgn1.setRects(rect, count);
1467
1468    if (rgn0 != rgn1) {
1469        SkDebugf("\n");
1470        for (int i = 0; i < count; i++) {
1471            dumpRect(rect[i]);
1472        }
1473        SkDebugf("\n");
1474    }
1475}
1476
1477static void test() {
1478    size_t i;
1479
1480    const SkIRect r0[] = {
1481        { 0, 0, 1, 1 },
1482        { 2, 2, 3, 3 },
1483    };
1484    const SkIRect r1[] = {
1485        { 0, 0, 1, 3 },
1486        { 1, 1, 2, 2 },
1487        { 2, 0, 3, 3 },
1488    };
1489    const SkIRect r2[] = {
1490        { 0, 0, 1, 2 },
1491        { 2, 1, 3, 3 },
1492        { 4, 0, 5, 1 },
1493        { 6, 0, 7, 4 },
1494    };
1495
1496    static const struct {
1497        const SkIRect* fRects;
1498        int            fCount;
1499    } gRecs[] = {
1500        { r0, SK_ARRAY_COUNT(r0) },
1501        { r1, SK_ARRAY_COUNT(r1) },
1502        { r2, SK_ARRAY_COUNT(r2) },
1503    };
1504
1505    for (i = 0; i < SK_ARRAY_COUNT(gRecs); i++) {
1506        test_rects(gRecs[i].fRects, gRecs[i].fCount);
1507    }
1508
1509    SkRandom rand;
1510    for (i = 0; i < 10000; i++) {
1511        SkRegion rgn0, rgn1;
1512
1513        const int N = 8;
1514        SkIRect rect[N];
1515        for (int j = 0; j < N; j++) {
1516            rand_rect(&rect[j], rand);
1517        }
1518        test_rects(rect, N);
1519    }
1520}
1521
1522SkOSWindow* create_sk_window(void* hwnd) {
1523//    test();
1524    return new SampleWindow(hwnd);
1525}
1526
1527void get_preferred_size(int* x, int* y, int* width, int* height) {
1528    *x = 10;
1529    *y = 50;
1530    *width = 640;
1531    *height = 480;
1532}
1533
1534void application_init() {
1535//    setenv("ANDROID_ROOT", "../../../data", 0);
1536#ifdef SK_BUILD_FOR_MAC
1537    setenv("ANDROID_ROOT", "/android/device/data", 0);
1538#endif
1539    SkGraphics::Init();
1540    SkEvent::Init();
1541}
1542
1543void application_term() {
1544    SkEvent::Term();
1545    SkGraphics::Term();
1546}
1547