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