SampleApp.cpp revision 2eb595292b9624371d1698ae9da7accb4c09c26c
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, 10);
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(GrGpu::kOpenGL_Shaders_Engine, NULL);
382        #else
383            fGrContext = GrContext::Create(GrGpu::kOpenGL_Fixed_Engine, 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    SkIPoint viewport;
709    bool alreadyGPU = canvas->getViewport(&viewport);
710
711    if (kGPU_CanvasType != fCanvasType) {
712#ifdef SK_SUPPORT_GL
713        detachGL();
714#endif
715    }
716
717    switch (fCanvasType) {
718        case kRaster_CanvasType:
719            canvas = this->INHERITED::beforeChildren(canvas);
720            break;
721        case kPicture_CanvasType:
722            fPicture = new SkPicture;
723            canvas = fPicture->beginRecording(9999, 9999);
724            break;
725        case kGPU_CanvasType: {
726            if (!alreadyGPU && make3DReady()) {
727                SkDevice* device = canvas->getDevice();
728                const SkBitmap& bitmap = device->accessBitmap(true);
729
730                GrRenderTarget* renderTarget;
731                renderTarget = fGrContext->createRenderTargetFrom3DApiState();
732                fGpuCanvas = new SkGpuCanvas(fGrContext, renderTarget);
733                renderTarget->unref();
734
735                device = fGpuCanvas->createDevice(SkBitmap::kARGB_8888_Config,
736                                                  bitmap.width(), bitmap.height(),
737                                                  false, false);
738                fGpuCanvas->setDevice(device)->unref();
739
740                fGpuCanvas->concat(canvas->getTotalMatrix());
741                canvas = fGpuCanvas;
742
743            } else {
744                canvas = this->INHERITED::beforeChildren(canvas);
745            }
746            break;
747        }
748    }
749
750    if (fUseClip) {
751        canvas->drawColor(0xFFFF88FF);
752        canvas->clipPath(fClipPath);
753    }
754
755    return canvas;
756}
757
758static void paint_rgn(const SkBitmap& bm, const SkIRect& r,
759                      const SkRegion& rgn) {
760    SkCanvas    canvas(bm);
761    SkRegion    inval(rgn);
762
763    inval.translate(r.fLeft, r.fTop);
764    canvas.clipRegion(inval);
765    canvas.drawColor(0xFFFF8080);
766}
767
768void SampleWindow::afterChildren(SkCanvas* orig) {
769    if (fRequestGrabImage) {
770        fRequestGrabImage = false;
771
772        SkCanvas* canvas = fGpuCanvas ? fGpuCanvas : orig;
773        SkDevice* device = canvas->getDevice();
774        SkBitmap bmp;
775        if (device->accessBitmap(false).copyTo(&bmp, SkBitmap::kARGB_8888_Config)) {
776            static int gSampleGrabCounter;
777            SkString name;
778            name.printf("sample_grab_%d", gSampleGrabCounter++);
779            SkImageEncoder::EncodeFile(name.c_str(), bmp,
780                                       SkImageEncoder::kPNG_Type, 100);
781        }
782    }
783
784    switch (fCanvasType) {
785        case kRaster_CanvasType:
786            break;
787        case kPicture_CanvasType:
788            if (true) {
789                SkPicture* pict = new SkPicture(*fPicture);
790                fPicture->unref();
791                orig->drawPicture(*pict);
792                pict->unref();
793            } else if (true) {
794                SkDynamicMemoryWStream ostream;
795                fPicture->serialize(&ostream);
796                fPicture->unref();
797
798                SkMemoryStream istream(ostream.getStream(), ostream.getOffset());
799                SkPicture pict(&istream);
800                orig->drawPicture(pict);
801            } else {
802                fPicture->draw(orig);
803                fPicture->unref();
804            }
805            fPicture = NULL;
806            break;
807#ifdef SK_SUPPORT_GL
808        case kGPU_CanvasType:
809            delete fGpuCanvas;
810            fGpuCanvas = NULL;
811            presentGL();
812            break;
813#endif
814    }
815
816//    if ((fScrollTestX | fScrollTestY) != 0)
817    if (false) {
818        const SkBitmap& bm = orig->getDevice()->accessBitmap(true);
819        int dx = fScrollTestX * 7;
820        int dy = fScrollTestY * 7;
821        SkIRect r;
822        SkRegion inval;
823
824        r.set(50, 50, 50+100, 50+100);
825        bm.scrollRect(&r, dx, dy, &inval);
826        paint_rgn(bm, r, inval);
827    }
828}
829
830void SampleWindow::beforeChild(SkView* child, SkCanvas* canvas) {
831    if (fScale) {
832        SkScalar scale = SK_Scalar1 * 7 / 10;
833        SkScalar cx = this->width() / 2;
834        SkScalar cy = this->height() / 2;
835        canvas->translate(cx, cy);
836        canvas->scale(scale, scale);
837        canvas->translate(-cx, -cy);
838    }
839    if (fRotate) {
840        SkScalar cx = this->width() / 2;
841        SkScalar cy = this->height() / 2;
842        canvas->translate(cx, cy);
843        canvas->rotate(SkIntToScalar(30));
844        canvas->translate(-cx, -cy);
845    }
846
847    if (kUnknown_SkTriState != fLCDState ||
848        kUnknown_SkTriState != fAAState) {
849        canvas->setDrawFilter(new FlagsDrawFilter(fLCDState, fAAState))->unref();
850    }
851
852    SampleView::SetRepeatDraw(child, fMeasureFPS ? FPS_REPEAT_COUNT : 1);
853    if (fMeasureFPS) {
854        fMeasureFPS_Time = SkTime::GetMSecs();
855    }
856}
857
858void SampleWindow::afterChild(SkView* child, SkCanvas* canvas) {
859    canvas->setDrawFilter(NULL);
860
861    if (fMeasureFPS) {
862        fMeasureFPS_Time = SkTime::GetMSecs() - fMeasureFPS_Time;
863        this->updateTitle();
864        postInvalDelay(this->getSinkID());
865    }
866}
867
868static SkBitmap::Config gConfigCycle[] = {
869    SkBitmap::kNo_Config,           // none -> none
870    SkBitmap::kNo_Config,           // a1 -> none
871    SkBitmap::kNo_Config,           // a8 -> none
872    SkBitmap::kNo_Config,           // index8 -> none
873    SkBitmap::kARGB_4444_Config,    // 565 -> 4444
874    SkBitmap::kARGB_8888_Config,    // 4444 -> 8888
875    SkBitmap::kRGB_565_Config       // 8888 -> 565
876};
877
878static SkBitmap::Config cycle_configs(SkBitmap::Config c) {
879    return gConfigCycle[c];
880}
881
882void SampleWindow::changeZoomLevel(int delta) {
883    fZoomLevel += delta;
884    if (fZoomLevel > 0) {
885        fZoomLevel = SkMin32(fZoomLevel, MAX_ZOOM_LEVEL);
886        fZoomScale = SkIntToScalar(fZoomLevel + 1);
887    } else if (fZoomLevel < 0) {
888        fZoomLevel = SkMax32(fZoomLevel, MIN_ZOOM_LEVEL);
889        fZoomScale = SK_Scalar1 / (1 - fZoomLevel);
890    } else {
891        fZoomScale = SK_Scalar1;
892    }
893
894    this->inval(NULL);
895}
896
897bool SampleWindow::nextSample() {
898    fCurrIndex = (fCurrIndex + 1) % fSamples.count();
899    this->loadView(fSamples[fCurrIndex]());
900    return true;
901}
902
903bool SampleWindow::onEvent(const SkEvent& evt) {
904    if (evt.isType(ANIMATING_EVENTTYPE)) {
905        if (fAnimating) {
906            this->nextSample();
907            this->postAnimatingEvent();
908        }
909        return true;
910    }
911    if (evt.isType("set-curr-index")) {
912        fCurrIndex = evt.getFast32() % fSamples.count();
913        this->loadView(fSamples[fCurrIndex]());
914        return true;
915    }
916    if (isInvalEvent(evt)) {
917        this->inval(NULL);
918        return true;
919    }
920    return this->INHERITED::onEvent(evt);
921}
922
923bool SampleWindow::onQuery(SkEvent* query) {
924    if (query->isType("get-slide-count")) {
925        query->setFast32(fSamples.count());
926        return true;
927    }
928    if (query->isType("get-slide-title")) {
929        SkView* view = fSamples[query->getFast32()]();
930        SkEvent evt(gTitleEvtName);
931        if (view->doQuery(&evt)) {
932            query->setString("title", evt.findString(gTitleEvtName));
933        }
934        SkSafeUnref(view);
935        return true;
936    }
937    if (query->isType("use-fast-text")) {
938        SkEvent evt(gFastTextEvtName);
939        return curr_view(this)->doQuery(&evt);
940    }
941    return this->INHERITED::onQuery(query);
942}
943
944static void cleanup_for_filename(SkString* name) {
945    char* str = name->writable_str();
946    for (size_t i = 0; i < name->size(); i++) {
947        switch (str[i]) {
948            case ':': str[i] = '-'; break;
949            case '/': str[i] = '-'; break;
950            case ' ': str[i] = '_'; break;
951            default: break;
952        }
953    }
954}
955
956bool SampleWindow::onHandleChar(SkUnichar uni) {
957    {
958        SkView* view = curr_view(this);
959        if (view) {
960            SkEvent evt(gCharEvtName);
961            evt.setFast32(uni);
962            if (view->doQuery(&evt)) {
963                return true;
964            }
965        }
966    }
967
968    int dx = 0xFF;
969    int dy = 0xFF;
970
971    switch (uni) {
972        case '5': dx =  0; dy =  0; break;
973        case '8': dx =  0; dy = -1; break;
974        case '6': dx =  1; dy =  0; break;
975        case '2': dx =  0; dy =  1; break;
976        case '4': dx = -1; dy =  0; break;
977        case '7': dx = -1; dy = -1; break;
978        case '9': dx =  1; dy = -1; break;
979        case '3': dx =  1; dy =  1; break;
980        case '1': dx = -1; dy =  1; break;
981
982        default:
983            break;
984    }
985
986    if (0xFF != dx && 0xFF != dy) {
987        if ((dx | dy) == 0) {
988            fScrollTestX = fScrollTestY = 0;
989        } else {
990            fScrollTestX += dx;
991            fScrollTestY += dy;
992        }
993        this->inval(NULL);
994        return true;
995    }
996
997    switch (uni) {
998        case 'a':
999            fAnimating = !fAnimating;
1000            this->postAnimatingEvent();
1001            this->updateTitle();
1002            return true;
1003        case 'b':
1004            fAAState = cycle_tristate(fAAState);
1005            this->updateTitle();
1006            this->inval(NULL);
1007            break;
1008        case 'c':
1009            fUseClip = !fUseClip;
1010            this->inval(NULL);
1011            this->updateTitle();
1012            return true;
1013        case 'd':
1014            SkGraphics::SetFontCacheUsed(0);
1015            return true;
1016        case 'f':
1017            fMeasureFPS = !fMeasureFPS;
1018            this->inval(NULL);
1019            break;
1020        case 'g':
1021            fRequestGrabImage = true;
1022            this->inval(NULL);
1023            break;
1024        case 'i':
1025            this->zoomIn();
1026            break;
1027        case 'l':
1028            fLCDState = cycle_tristate(fLCDState);
1029            this->updateTitle();
1030            this->inval(NULL);
1031            break;
1032        case 'o':
1033            this->zoomOut();
1034            break;
1035        case 'r':
1036            fRotate = !fRotate;
1037            this->inval(NULL);
1038            this->updateTitle();
1039            return true;
1040        case 's':
1041            fScale = !fScale;
1042            this->inval(NULL);
1043            this->updateTitle();
1044            return true;
1045        case 'x':
1046            fFlipAxis ^= kFlipAxis_X;
1047            this->updateTitle();
1048            this->inval(NULL);
1049            break;
1050        case 'y':
1051            fFlipAxis ^= kFlipAxis_Y;
1052            this->updateTitle();
1053            this->inval(NULL);
1054            break;
1055        case 'z':
1056            this->toggleZoomer();
1057            break;
1058        default:
1059            break;
1060    }
1061
1062    return this->INHERITED::onHandleChar(uni);
1063}
1064
1065#include "SkDumpCanvas.h"
1066
1067bool SampleWindow::onHandleKey(SkKey key) {
1068    {
1069        SkView* view = curr_view(this);
1070        if (view) {
1071            SkEvent evt(gKeyEvtName);
1072            evt.setFast32(key);
1073            if (view->doQuery(&evt)) {
1074                return true;
1075            }
1076        }
1077    }
1078
1079    switch (key) {
1080        case kRight_SkKey:
1081            if (this->nextSample()) {
1082                return true;
1083            }
1084            break;
1085        case kLeft_SkKey:
1086            fCanvasType = cycle_canvastype(fCanvasType);
1087            this->updateTitle();
1088            this->inval(NULL);
1089            return true;
1090        case kUp_SkKey:
1091            if (USE_ARROWS_FOR_ZOOM) {
1092                this->changeZoomLevel(1);
1093            } else {
1094                fNClip = !fNClip;
1095                this->inval(NULL);
1096            }
1097            this->updateTitle();
1098            return true;
1099        case kDown_SkKey:
1100            if (USE_ARROWS_FOR_ZOOM) {
1101                this->changeZoomLevel(-1);
1102            } else {
1103                this->setConfig(cycle_configs(this->getBitmap().config()));
1104            }
1105            this->updateTitle();
1106            return true;
1107        case kOK_SkKey:
1108            if (false) {
1109                SkDebugfDumper dumper;
1110                SkDumpCanvas dc(&dumper);
1111                this->draw(&dc);
1112            } else {
1113                fRepeatDrawing = !fRepeatDrawing;
1114                if (fRepeatDrawing) {
1115                    this->inval(NULL);
1116                }
1117            }
1118            return true;
1119        case kBack_SkKey:
1120            this->loadView(NULL);
1121            return true;
1122        default:
1123            break;
1124    }
1125    return this->INHERITED::onHandleKey(key);
1126}
1127
1128///////////////////////////////////////////////////////////////////////////////
1129
1130static const char gGestureClickType[] = "GestureClickType";
1131
1132bool SampleWindow::onDispatchClick(int x, int y, Click::State state) {
1133    if (Click::kMoved_State == state) {
1134        updatePointer(x, y);
1135    }
1136    int w = SkScalarRound(this->width());
1137    int h = SkScalarRound(this->height());
1138
1139    // check for the resize-box
1140    if (w - x < 16 && h - y < 16) {
1141        return false;   // let the OS handle the click
1142    } else {
1143        return this->INHERITED::onDispatchClick(x, y, state);
1144    }
1145}
1146
1147class GestureClick : public SkView::Click {
1148public:
1149    GestureClick(SkView* target) : SkView::Click(target) {
1150        this->setType(gGestureClickType);
1151    }
1152
1153    static bool IsGesture(Click* click) {
1154        return click->isType(gGestureClickType);
1155    }
1156};
1157
1158SkView::Click* SampleWindow::onFindClickHandler(SkScalar x, SkScalar y) {
1159    return new GestureClick(this);
1160}
1161
1162bool SampleWindow::onClick(Click* click) {
1163    if (GestureClick::IsGesture(click)) {
1164        float x = SkScalarToFloat(click->fCurr.fX);
1165        float y = SkScalarToFloat(click->fCurr.fY);
1166        switch (click->fState) {
1167            case SkView::Click::kDown_State:
1168                fGesture.touchBegin(click, x, y);
1169                break;
1170            case SkView::Click::kMoved_State:
1171                fGesture.touchMoved(click, x, y);
1172                this->inval(NULL);
1173                break;
1174            case SkView::Click::kUp_State:
1175                fGesture.touchEnd(click);
1176                this->inval(NULL);
1177                break;
1178        }
1179        return true;
1180    }
1181    return false;
1182}
1183
1184///////////////////////////////////////////////////////////////////////////////
1185
1186void SampleWindow::loadView(SkView* view) {
1187    SkView::F2BIter iter(this);
1188    SkView* prev = iter.next();
1189    if (prev) {
1190        prev->detachFromParent();
1191    }
1192
1193    if (NULL == view) {
1194        view = create_overview(fSamples.count(), fSamples.begin());
1195    }
1196    view->setVisibleP(true);
1197    view->setClipToBounds(false);
1198    this->attachChildToFront(view)->unref();
1199    view->setSize(this->width(), this->height());
1200
1201    this->updateTitle();
1202}
1203
1204static const char* gConfigNames[] = {
1205    "unknown config",
1206    "A1",
1207    "A8",
1208    "Index8",
1209    "565",
1210    "4444",
1211    "8888"
1212};
1213
1214static const char* configToString(SkBitmap::Config c) {
1215    return gConfigNames[c];
1216}
1217
1218static const char* gCanvasTypePrefix[] = {
1219    "raster: ",
1220    "picture: ",
1221    "opengl: "
1222};
1223
1224static const char* trystate_str(SkTriState state,
1225                                const char trueStr[], const char falseStr[]) {
1226    if (kTrue_SkTriState == state) {
1227        return trueStr;
1228    } else if (kFalse_SkTriState == state) {
1229        return falseStr;
1230    }
1231    return NULL;
1232}
1233
1234void SampleWindow::updateTitle() {
1235    SkString title;
1236
1237    SkView::F2BIter iter(this);
1238    SkView* view = iter.next();
1239    SkEvent evt(gTitleEvtName);
1240    if (view->doQuery(&evt)) {
1241        title.set(evt.findString(gTitleEvtName));
1242    }
1243    if (title.size() == 0) {
1244        title.set("<unknown>");
1245    }
1246
1247    title.prepend(gCanvasTypePrefix[fCanvasType]);
1248
1249    title.prepend(" ");
1250    title.prepend(configToString(this->getBitmap().config()));
1251
1252    if (fAnimating) {
1253        title.prepend("<A> ");
1254    }
1255    if (fScale) {
1256        title.prepend("<S> ");
1257    }
1258    if (fRotate) {
1259        title.prepend("<R> ");
1260    }
1261    if (fNClip) {
1262        title.prepend("<C> ");
1263    }
1264
1265    title.prepend(trystate_str(fLCDState, "LCD ", "lcd "));
1266    title.prepend(trystate_str(fAAState, "AA ", "aa "));
1267    title.prepend(fFlipAxis & kFlipAxis_X ? "X " : NULL);
1268    title.prepend(fFlipAxis & kFlipAxis_Y ? "Y " : NULL);
1269
1270    if (fZoomLevel) {
1271        title.prependf("{%d} ", fZoomLevel);
1272    }
1273
1274    if (fMeasureFPS) {
1275        title.appendf(" %4d ms", fMeasureFPS_Time);
1276    }
1277
1278    this->setTitle(title.c_str());
1279}
1280
1281void SampleWindow::onSizeChange() {
1282    this->INHERITED::onSizeChange();
1283
1284    SkView::F2BIter iter(this);
1285    SkView* view = iter.next();
1286    view->setSize(this->width(), this->height());
1287
1288    // rebuild our clippath
1289    {
1290        const SkScalar W = this->width();
1291        const SkScalar H = this->height();
1292
1293        fClipPath.reset();
1294#if 0
1295        for (SkScalar y = SK_Scalar1; y < H; y += SkIntToScalar(32)) {
1296            SkRect r;
1297            r.set(SK_Scalar1, y, SkIntToScalar(30), y + SkIntToScalar(30));
1298            for (; r.fLeft < W; r.offset(SkIntToScalar(32), 0))
1299                fClipPath.addRect(r);
1300        }
1301#else
1302        SkRect r;
1303        r.set(0, 0, W, H);
1304        fClipPath.addRect(r, SkPath::kCCW_Direction);
1305        r.set(W/4, H/4, W*3/4, H*3/4);
1306        fClipPath.addRect(r, SkPath::kCW_Direction);
1307#endif
1308    }
1309
1310    this->updateTitle();    // to refresh our config
1311}
1312
1313///////////////////////////////////////////////////////////////////////////////
1314
1315static const char repeat_count_tag[] = "sample-set-repeat-count";
1316
1317void SampleView::SetRepeatDraw(SkView* view, int count) {
1318    SkEvent evt(repeat_count_tag);
1319    evt.setFast32(count);
1320    (void)view->doEvent(evt);
1321}
1322
1323bool SampleView::onEvent(const SkEvent& evt) {
1324    if (evt.isType(repeat_count_tag)) {
1325        fRepeatCount = evt.getFast32();
1326        return true;
1327    }
1328    return this->INHERITED::onEvent(evt);
1329}
1330
1331bool SampleView::onQuery(SkEvent* evt) {
1332    return this->INHERITED::onQuery(evt);
1333}
1334
1335void SampleView::onDraw(SkCanvas* canvas) {
1336    this->onDrawBackground(canvas);
1337    for (int i = 0; i < fRepeatCount; i++) {
1338        SkAutoCanvasRestore acr(canvas, true);
1339        this->onDrawContent(canvas);
1340    }
1341}
1342
1343void SampleView::onDrawBackground(SkCanvas* canvas) {
1344    canvas->drawColor(SK_ColorWHITE);
1345}
1346
1347///////////////////////////////////////////////////////////////////////////////
1348
1349template <typename T> void SkTBSort(T array[], int count) {
1350    for (int i = 1; i < count - 1; i++) {
1351        bool didSwap = false;
1352        for (int j = count - 1; j > i; --j) {
1353            if (array[j] < array[j-1]) {
1354                T tmp(array[j-1]);
1355                array[j-1] = array[j];
1356                array[j] = tmp;
1357                didSwap = true;
1358            }
1359        }
1360        if (!didSwap) {
1361            break;
1362        }
1363    }
1364
1365    for (int k = 0; k < count - 1; k++) {
1366        SkASSERT(!(array[k+1] < array[k]));
1367    }
1368}
1369
1370#include "SkRandom.h"
1371
1372static void rand_rect(SkIRect* rect, SkRandom& rand) {
1373    int bits = 8;
1374    int shift = 32 - bits;
1375    rect->set(rand.nextU() >> shift, rand.nextU() >> shift,
1376              rand.nextU() >> shift, rand.nextU() >> shift);
1377    rect->sort();
1378}
1379
1380static void dumpRect(const SkIRect& r) {
1381    SkDebugf(" { %d, %d, %d, %d },\n",
1382             r.fLeft, r.fTop,
1383             r.fRight, r.fBottom);
1384}
1385
1386static void test_rects(const SkIRect rect[], int count) {
1387    SkRegion rgn0, rgn1;
1388
1389    for (int i = 0; i < count; i++) {
1390        rgn0.op(rect[i], SkRegion::kUnion_Op);
1391     //   dumpRect(rect[i]);
1392    }
1393    rgn1.setRects(rect, count);
1394
1395    if (rgn0 != rgn1) {
1396        SkDebugf("\n");
1397        for (int i = 0; i < count; i++) {
1398            dumpRect(rect[i]);
1399        }
1400        SkDebugf("\n");
1401    }
1402}
1403
1404static void test() {
1405    size_t i;
1406
1407    const SkIRect r0[] = {
1408        { 0, 0, 1, 1 },
1409        { 2, 2, 3, 3 },
1410    };
1411    const SkIRect r1[] = {
1412        { 0, 0, 1, 3 },
1413        { 1, 1, 2, 2 },
1414        { 2, 0, 3, 3 },
1415    };
1416    const SkIRect r2[] = {
1417        { 0, 0, 1, 2 },
1418        { 2, 1, 3, 3 },
1419        { 4, 0, 5, 1 },
1420        { 6, 0, 7, 4 },
1421    };
1422
1423    static const struct {
1424        const SkIRect* fRects;
1425        int            fCount;
1426    } gRecs[] = {
1427        { r0, SK_ARRAY_COUNT(r0) },
1428        { r1, SK_ARRAY_COUNT(r1) },
1429        { r2, SK_ARRAY_COUNT(r2) },
1430    };
1431
1432    for (i = 0; i < SK_ARRAY_COUNT(gRecs); i++) {
1433        test_rects(gRecs[i].fRects, gRecs[i].fCount);
1434    }
1435
1436    SkRandom rand;
1437    for (i = 0; i < 10000; i++) {
1438        SkRegion rgn0, rgn1;
1439
1440        const int N = 8;
1441        SkIRect rect[N];
1442        for (int j = 0; j < N; j++) {
1443            rand_rect(&rect[j], rand);
1444        }
1445        test_rects(rect, N);
1446    }
1447}
1448
1449SkOSWindow* create_sk_window(void* hwnd) {
1450//    test();
1451    return new SampleWindow(hwnd);
1452}
1453
1454void get_preferred_size(int* x, int* y, int* width, int* height) {
1455    *x = 10;
1456    *y = 50;
1457    *width = 640;
1458    *height = 480;
1459}
1460
1461void application_init() {
1462//    setenv("ANDROID_ROOT", "../../../data", 0);
1463#ifdef SK_BUILD_FOR_MAC
1464    setenv("ANDROID_ROOT", "/android/device/data", 0);
1465#endif
1466    SkGraphics::Init();
1467    SkEvent::Init();
1468}
1469
1470void application_term() {
1471    SkEvent::Term();
1472    SkGraphics::Term();
1473}
1474