SampleApp.cpp 70.7 KB
Newer Older
1 2 3 4 5 6
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
reed's avatar
reed committed
7

8 9
#include "SampleApp.h"

10
#include "OverView.h"
caryclark's avatar
caryclark committed
11
#include "Resources.h"
12 13
#include "SampleCode.h"
#include "SamplePipeControllers.h"
14
#include "SkAnimTimer.h"
reed@android.com's avatar
reed@android.com committed
15
#include "SkCanvas.h"
16 17
#include "SkCommandLineFlags.h"
#include "SkData.h"
reed@android.com's avatar
reed@android.com committed
18
#include "SkDevice.h"
19
#include "SkDocument.h"
20
#include "SkGPipe.h"
reed@android.com's avatar
reed@android.com committed
21
#include "SkGraphics.h"
22
#include "SkImageEncoder.h"
23
#include "SkOSFile.h"
reed@android.com's avatar
reed@android.com committed
24 25
#include "SkPaint.h"
#include "SkPicture.h"
26
#include "SkPictureRecorder.h"
reed@android.com's avatar
reed@android.com committed
27
#include "SkStream.h"
28
#include "SkSurface.h"
29
#include "SkTSort.h"
30
#include "SkTime.h"
Scroggo's avatar
Scroggo committed
31
#include "SkTypeface.h"
32 33
#include "SkWindow.h"
#include "sk_tool_utils.h"
34

35
#if SK_SUPPORT_GPU
36
#include "gl/GrGLInterface.h"
37
#include "gl/GrGLUtil.h"
38
#include "GrRenderTarget.h"
39 40 41 42 43
#include "GrContext.h"
#include "SkGpuDevice.h"
#else
class GrContext;
#endif
44

45 46 47 48 49 50
extern SampleView* CreateSamplePictFileView(const char filename[]);

class PictFileFactory : public SkViewFactory {
    SkString fFilename;
public:
    PictFileFactory(const SkString& filename) : fFilename(filename) {}
51
    SkView* operator() () const SK_OVERRIDE {
52 53 54 55
        return CreateSamplePictFileView(fFilename.c_str());
    }
};

56 57 58 59 60 61 62
#ifdef SAMPLE_PDF_FILE_VIEWER
extern SampleView* CreateSamplePdfFileViewer(const char filename[]);

class PdfFileViewerFactory : public SkViewFactory {
    SkString fFilename;
public:
    PdfFileViewerFactory(const SkString& filename) : fFilename(filename) {}
63
    SkView* operator() () const SK_OVERRIDE {
64 65 66 67 68
        return CreateSamplePdfFileViewer(fFilename.c_str());
    }
};
#endif  // SAMPLE_PDF_FILE_VIEWER

69 70
#define PIPE_FILEx
#ifdef  PIPE_FILE
71 72 73
#define FILE_PATH "/path/to/drawing.data"
#endif

74
#define PIPE_NETx
75 76 77 78
#ifdef  PIPE_NET
#include "SkSockets.h"
SkTCPServer gServer;
#endif
79

80
#define USE_ARROWS_FOR_ZOOM true
81 82 83 84

#if SK_ANGLE
//#define DEFAULT_TO_ANGLE 1
#else
85
#define DEFAULT_TO_GPU 0 // if 1 default rendering is on GPU
86
#endif
87

reed@android.com's avatar
reed@android.com committed
88
#define ANIMATING_EVENTTYPE "nextSample"
89
#define ANIMATING_DELAY     250
reed@android.com's avatar
reed@android.com committed
90

91
#ifdef SK_DEBUG
92
    #define FPS_REPEAT_MULTIPLIER   1
93
#else
94
    #define FPS_REPEAT_MULTIPLIER   10
95
#endif
96
#define FPS_REPEAT_COUNT    (10 * FPS_REPEAT_MULTIPLIER)
97

98 99
static SampleWindow* gSampleWindow;

100 101
static bool gShowGMBounds;

102
static void post_event_to_sink(SkEvent* evt, SkEventSink* sink) {
103 104 105
    evt->setTargetID(sink->getSinkID())->post();
}

106 107
static SkAnimTimer gAnimTimer;

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
///////////////////////////////////////////////////////////////////////////////

static const char* skip_until(const char* str, const char* skip) {
    if (!str) {
        return NULL;
    }
    return strstr(str, skip);
}

static const char* skip_past(const char* str, const char* skip) {
    const char* found = skip_until(str, skip);
    if (!found) {
        return NULL;
    }
    return found + strlen(skip);
}

static const char* gPrefFileName = "sampleapp_prefs.txt";

127
static bool readTitleFromPrefs(SkString* title) {
128 129 130 131 132
    SkFILEStream stream(gPrefFileName);
    if (!stream.isValid()) {
        return false;
    }

reed@google.com's avatar
reed@google.com committed
133
    size_t len = stream.getLength();
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
    SkString data(len);
    stream.read(data.writable_str(), len);
    const char* s = data.c_str();

    s = skip_past(s, "curr-slide-title");
    s = skip_past(s, "=");
    s = skip_past(s, "\"");
    const char* stop = skip_until(s, "\"");
    if (stop > s) {
        title->set(s, stop - s);
        return true;
    }
    return false;
}

149
static void writeTitleToPrefs(const char* title) {
150 151 152 153 154 155 156 157
    SkFILEWStream stream(gPrefFileName);
    SkString data;
    data.printf("curr-slide-title = \"%s\"\n", title);
    stream.write(data.c_str(), data.size());
}

///////////////////////////////////////////////////////////////////////////////

158 159 160
class SampleWindow::DefaultDeviceManager : public SampleWindow::DeviceManager {
public:

161 162 163 164 165 166 167 168
    DefaultDeviceManager() {
#if SK_SUPPORT_GPU
        fCurContext = NULL;
        fCurIntf = NULL;
        fCurRenderTarget = NULL;
        fMSAASampleCount = 0;
#endif
        fBackend = kNone_BackEndType;
169 170 171
    }

    virtual ~DefaultDeviceManager() {
172
#if SK_SUPPORT_GPU
173 174 175
        SkSafeUnref(fCurContext);
        SkSafeUnref(fCurIntf);
        SkSafeUnref(fCurRenderTarget);
176
#endif
177 178
    }

179
    virtual void setUpBackend(SampleWindow* win, int msaaSampleCount) {
180 181 182
        SkASSERT(kNone_BackEndType == fBackend);

        fBackend = kNone_BackEndType;
183

184
#if SK_SUPPORT_GPU
185 186 187 188 189 190 191 192 193 194 195
        switch (win->getDeviceType()) {
            case kRaster_DeviceType:
                // fallthrough
            case kPicture_DeviceType:
                // fallthrough
            case kGPU_DeviceType:
                // fallthrough
            case kNullGPU_DeviceType:
                // all these guys use the native backend
                fBackend = kNativeGL_BackEndType;
                break;
196
#if SK_ANGLE
197 198 199 200
            case kANGLE_DeviceType:
                // ANGLE is really the only odd man out
                fBackend = kANGLE_BackEndType;
                break;
201
#endif // SK_ANGLE
202 203 204
            default:
                SkASSERT(false);
                break;
205
        }
206 207
        AttachmentInfo attachmentInfo;
        bool result = win->attach(fBackend, msaaSampleCount, &attachmentInfo);
208
        if (!result) {
209
            SkDebugf("Failed to initialize GL");
210
            return;
211
        }
212
        fMSAASampleCount = msaaSampleCount;
213 214

        SkASSERT(NULL == fCurIntf);
215
        SkAutoTUnref<const GrGLInterface> glInterface;
216 217 218 219 220 221 222
        switch (win->getDeviceType()) {
            case kRaster_DeviceType:
                // fallthrough
            case kPicture_DeviceType:
                // fallthrough
            case kGPU_DeviceType:
                // all these guys use the native interface
223
                glInterface.reset(GrGLCreateNativeInterface());
224
                break;
225
#if SK_ANGLE
226
            case kANGLE_DeviceType:
227
                glInterface.reset(GrGLCreateANGLEInterface());
228
                break;
229
#endif // SK_ANGLE
230
            case kNullGPU_DeviceType:
231
                glInterface.reset(GrGLCreateNullInterface());
232 233 234 235
                break;
            default:
                SkASSERT(false);
                break;
236
        }
237

238 239 240 241
        // Currently SampleApp does not use NVPR. TODO: Provide an NVPR device type that is skipped
        // when the driver doesn't support NVPR.
        fCurIntf = GrGLInterfaceRemoveNVPR(glInterface.get());

242
        SkASSERT(NULL == fCurContext);
243
        fCurContext = GrContext::Create(kOpenGL_GrBackend, (GrBackendContext) fCurIntf);
244 245 246 247 248

        if (NULL == fCurContext || NULL == fCurIntf) {
            // We need some context and interface to see results
            SkSafeUnref(fCurContext);
            SkSafeUnref(fCurIntf);
249 250
            fCurContext = NULL;
            fCurIntf = NULL;
251
            SkDebugf("Failed to setup 3D");
252 253

            win->detach();
254
        }
255
#endif // SK_SUPPORT_GPU
256
        // call windowSizeChanged to create the render target
257
        this->windowSizeChanged(win);
258 259
    }

260
    virtual void tearDownBackend(SampleWindow *win) {
261
#if SK_SUPPORT_GPU
262 263 264 265 266 267
        if (fCurContext) {
            // in case we have outstanding refs to this guy (lua?)
            fCurContext->abandonContext();
            fCurContext->unref();
            fCurContext = NULL;
        }
268 269 270 271 272 273

        SkSafeUnref(fCurIntf);
        fCurIntf = NULL;

        SkSafeUnref(fCurRenderTarget);
        fCurRenderTarget = NULL;
274
#endif
275 276
        win->detach();
        fBackend = kNone_BackEndType;
277 278
    }

reed's avatar
reed committed
279 280
    virtual SkSurface* createSurface(SampleWindow::DeviceType dType,
                                     SampleWindow* win) SK_OVERRIDE {
281
#if SK_SUPPORT_GPU
bsalomon's avatar
bsalomon committed
282
        if (IsGpuDeviceType(dType) && fCurContext) {
283 284
            SkSurfaceProps props(win->getSurfaceProps());
            return SkSurface::NewRenderTargetDirect(fCurRenderTarget, &props);
285
        }
reed's avatar
reed committed
286 287
#endif
        return NULL;
288 289 290 291 292
    }

    virtual void publishCanvas(SampleWindow::DeviceType dType,
                               SkCanvas* canvas,
                               SampleWindow* win) {
293
#if SK_SUPPORT_GPU
294
        if (fCurContext) {
295
            // in case we have queued drawing calls
296 297
            fCurContext->flush();

298
            if (!IsGpuDeviceType(dType)) {
299
                // need to send the raster bits to the (gpu) window
300
                fCurContext->setRenderTarget(fCurRenderTarget);
301
                const SkBitmap& bm = win->getBitmap();
302
                fCurRenderTarget->writePixels(0, 0, bm.width(), bm.height(),
303
                                             SkImageInfo2GrPixelConfig(bm.colorType(),
jvanverth's avatar
jvanverth committed
304 305
                                                                       bm.alphaType(),
                                                                       bm.profileType()),
306
                                             bm.getPixels(),
307 308
                                             bm.rowBytes(),
                                             GrContext::kFlushWrites_PixelOp);
309 310
            }
        }
311
#endif
312 313

        win->present();
314 315 316
    }

    virtual void windowSizeChanged(SampleWindow* win) {
317
#if SK_SUPPORT_GPU
318
        if (fCurContext) {
319 320
            AttachmentInfo attachmentInfo;
            win->attach(fBackend, fMSAASampleCount, &attachmentInfo);
321
            SkSafeUnref(fCurRenderTarget);
322
            fCurRenderTarget = win->renderTarget(attachmentInfo, fCurIntf, fCurContext);
323
        }
324
#endif
325 326
    }

327
    virtual GrContext* getGrContext() {
328
#if SK_SUPPORT_GPU
329
        return fCurContext;
330 331 332
#else
        return NULL;
#endif
333
    }
334

335
    GrRenderTarget* getGrRenderTarget() SK_OVERRIDE {
336
#if SK_SUPPORT_GPU
337
        return fCurRenderTarget;
338 339 340
#else
        return NULL;
#endif
341 342
    }

343
private:
344 345

#if SK_SUPPORT_GPU
346 347 348
    GrContext*              fCurContext;
    const GrGLInterface*    fCurIntf;
    GrRenderTarget*         fCurRenderTarget;
349 350
    int fMSAASampleCount;
#endif
351 352 353 354

    SkOSWindow::SkBackEndTypes fBackend;

    typedef SampleWindow::DeviceManager INHERITED;
355 356
};

357 358 359
///////////////
static const char view_inval_msg[] = "view-inval-msg";

Scroggo's avatar
Scroggo committed
360
void SampleWindow::postInvalDelay() {
361
    (new SkEvent(view_inval_msg, this->getSinkID()))->postDelay(1);
362 363 364 365 366 367 368
}

static bool isInvalEvent(const SkEvent& evt) {
    return evt.isType(view_inval_msg);
}
//////////////////

369 370 371 372
SkFuncViewFactory::SkFuncViewFactory(SkViewCreateFunc func)
    : fCreateFunc(func) {
}

373
SkView* SkFuncViewFactory::operator() () const {
374 375 376 377 378 379 380 381 382 383
    return (*fCreateFunc)();
}

#include "GMSampleView.h"

SkGMSampleViewFactory::SkGMSampleViewFactory(GMFactoryFunc func)
    : fFunc(func) {
}

SkView* SkGMSampleViewFactory::operator() () const {
384 385 386
    skiagm::GM* gm = fFunc(NULL);
    gm->setMode(skiagm::GM::kSample_Mode);
    return new GMSampleView(gm);
387 388
}

reed@android.com's avatar
reed@android.com committed
389
SkViewRegister* SkViewRegister::gHead;
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
SkViewRegister::SkViewRegister(SkViewFactory* fact) : fFact(fact) {
    fFact->ref();
    fChain = gHead;
    gHead = this;
}

SkViewRegister::SkViewRegister(SkViewCreateFunc func) {
    fFact = new SkFuncViewFactory(func);
    fChain = gHead;
    gHead = this;
}

SkViewRegister::SkViewRegister(GMFactoryFunc func) {
    fFact = new SkGMSampleViewFactory(func);
    fChain = gHead;
    gHead = this;
}

class AutoUnrefArray {
public:
    AutoUnrefArray() {}
    ~AutoUnrefArray() {
        int count = fObjs.count();
        for (int i = 0; i < count; ++i) {
            fObjs[i]->unref();
        }
    }
    SkRefCnt*& push_back() { return *fObjs.append(); }
418

419 420 421 422 423 424 425
private:
    SkTDArray<SkRefCnt*> fObjs;
};

// registers GMs as Samples
// This can't be performed during static initialization because it could be
// run before GMRegistry has been fully built.
426
static void SkGMRegistyToSampleRegistry() {
reed@android.com's avatar
reed@android.com committed
427
    static bool gOnce;
428
    static AutoUnrefArray fRegisters;
429

reed@android.com's avatar
reed@android.com committed
430
    if (!gOnce) {
431 432 433 434 435
        const skiagm::GMRegistry* gmreg = skiagm::GMRegistry::Head();
        while (gmreg) {
            fRegisters.push_back() = new SkViewRegister(gmreg->factory());
            gmreg = gmreg->next();
        }
reed@android.com's avatar
reed@android.com committed
436 437 438 439
        gOnce = true;
    }
}

440 441
//////////////////////////////////////////////////////////////////////////////

reed@google.com's avatar
reed@google.com committed
442 443 444 445
enum FlipAxisEnum {
    kFlipAxis_X = (1 << 0),
    kFlipAxis_Y = (1 << 1)
};
446

reed@google.com's avatar
reed@google.com committed
447 448
#include "SkDrawFilter.h"

449 450 451 452 453 454 455 456 457 458 459 460 461
struct HintingState {
    SkPaint::Hinting hinting;
    const char* name;
    const char* label;
};
static HintingState gHintingStates[] = {
    {SkPaint::kNo_Hinting, "Mixed", NULL },
    {SkPaint::kNo_Hinting, "None", "H0 " },
    {SkPaint::kSlight_Hinting, "Slight", "Hs " },
    {SkPaint::kNormal_Hinting, "Normal", "Hn " },
    {SkPaint::kFull_Hinting, "Full", "Hf " },
};

462 463 464 465 466 467 468 469 470 471 472 473 474
struct FilterLevelState {
    SkPaint::FilterLevel    fLevel;
    const char*             fName;
    const char*             fLabel;
};
static FilterLevelState gFilterLevelStates[] = {
    { SkPaint::kNone_FilterLevel,   "Mixed",    NULL    },
    { SkPaint::kNone_FilterLevel,   "None",     "F0 "   },
    { SkPaint::kLow_FilterLevel,    "Low",      "F1 "   },
    { SkPaint::kMedium_FilterLevel, "Medium",   "F2 "   },
    { SkPaint::kHigh_FilterLevel,   "High",     "F3 "   },
};

reed@google.com's avatar
reed@google.com committed
475 476
class FlagsDrawFilter : public SkDrawFilter {
public:
477 478 479 480 481 482 483 484 485 486
    FlagsDrawFilter(SkOSMenu::TriState lcd, SkOSMenu::TriState aa,
                    SkOSMenu::TriState subpixel, int hinting, int filterlevel)
        : fLCDState(lcd)
        , fAAState(aa)
        , fSubpixelState(subpixel)
        , fHintingState(hinting)
        , fFilterLevelIndex(filterlevel)
    {
        SkASSERT((unsigned)filterlevel < SK_ARRAY_COUNT(gFilterLevelStates));
    }
487

488
    virtual bool filter(SkPaint* paint, Type t) {
489 490
        if (kText_Type == t && SkOSMenu::kMixedState != fLCDState) {
            paint->setLCDRenderText(SkOSMenu::kOnState == fLCDState);
reed@google.com's avatar
reed@google.com committed
491
        }
492 493
        if (SkOSMenu::kMixedState != fAAState) {
            paint->setAntiAlias(SkOSMenu::kOnState == fAAState);
494
        }
495 496
        if (0 != fFilterLevelIndex) {
            paint->setFilterLevel(gFilterLevelStates[fFilterLevelIndex].fLevel);
497
        }
498 499 500
        if (SkOSMenu::kMixedState != fSubpixelState) {
            paint->setSubpixelText(SkOSMenu::kOnState == fSubpixelState);
        }
501
        if (0 != fHintingState && fHintingState < (int)SK_ARRAY_COUNT(gHintingStates)) {
502
            paint->setHinting(gHintingStates[fHintingState].hinting);
503
        }
504
        return true;
505 506 507
    }

private:
508 509
    SkOSMenu::TriState  fLCDState;
    SkOSMenu::TriState  fAAState;
510 511
    SkOSMenu::TriState  fSubpixelState;
    int fHintingState;
512
    int fFilterLevelIndex;
513 514
};

reed@android.com's avatar
reed@android.com committed
515 516
//////////////////////////////////////////////////////////////////////////////

517 518 519
#define MAX_ZOOM_LEVEL  8
#define MIN_ZOOM_LEVEL  -8

520 521
static const char gCharEvtName[] = "SampleCode_Char_Event";
static const char gKeyEvtName[] = "SampleCode_Key_Event";
reed@android.com's avatar
reed@android.com committed
522 523
static const char gTitleEvtName[] = "SampleCode_Title_Event";
static const char gPrefSizeEvtName[] = "SampleCode_PrefSize_Event";
524
static const char gFastTextEvtName[] = "SampleCode_FastText_Event";
525
static const char gUpdateWindowTitleEvtName[] = "SampleCode_UpdateWindowTitle";
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545

bool SampleCode::CharQ(const SkEvent& evt, SkUnichar* outUni) {
    if (evt.isType(gCharEvtName, sizeof(gCharEvtName) - 1)) {
        if (outUni) {
            *outUni = evt.getFast32();
        }
        return true;
    }
    return false;
}

bool SampleCode::KeyQ(const SkEvent& evt, SkKey* outKey) {
    if (evt.isType(gKeyEvtName, sizeof(gKeyEvtName) - 1)) {
        if (outKey) {
            *outKey = (SkKey)evt.getFast32();
        }
        return true;
    }
    return false;
}
reed@android.com's avatar
reed@android.com committed
546 547 548 549 550 551 552 553 554 555

bool SampleCode::TitleQ(const SkEvent& evt) {
    return evt.isType(gTitleEvtName, sizeof(gTitleEvtName) - 1);
}

void SampleCode::TitleR(SkEvent* evt, const char title[]) {
    SkASSERT(evt && TitleQ(*evt));
    evt->setString(gTitleEvtName, title);
}

556 557 558 559 560 561 562 563 564
bool SampleCode::RequestTitle(SkView* view, SkString* title) {
    SkEvent evt(gTitleEvtName);
    if (view->doQuery(&evt)) {
        title->set(evt.findString(gTitleEvtName));
        return true;
    }
    return false;
}

reed@android.com's avatar
reed@android.com committed
565 566 567 568 569 570 571 572 573 574 575 576
bool SampleCode::PrefSizeQ(const SkEvent& evt) {
    return evt.isType(gPrefSizeEvtName, sizeof(gPrefSizeEvtName) - 1);
}

void SampleCode::PrefSizeR(SkEvent* evt, SkScalar width, SkScalar height) {
    SkASSERT(evt && PrefSizeQ(*evt));
    SkScalar size[2];
    size[0] = width;
    size[1] = height;
    evt->setScalars(gPrefSizeEvtName, 2, size);
}

577 578 579 580 581 582
bool SampleCode::FastTextQ(const SkEvent& evt) {
    return evt.isType(gFastTextEvtName, sizeof(gFastTextEvtName) - 1);
}

///////////////////////////////////////////////////////////////////////////////

583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
enum TilingMode {
    kNo_Tiling,
    kAbs_128x128_Tiling,
    kAbs_256x256_Tiling,
    kRel_4x4_Tiling,
    kRel_1x16_Tiling,
    kRel_16x1_Tiling,

    kLast_TilingMode_Enum
};

struct TilingInfo {
    const char* label;
    SkScalar    w, h;
};

599
static const struct TilingInfo gTilingInfo[] = {
600 601 602 603 604 605 606 607 608 609
    { "No tiling", SK_Scalar1        , SK_Scalar1         }, // kNo_Tiling
    { "128x128"  , SkIntToScalar(128), SkIntToScalar(128) }, // kAbs_128x128_Tiling
    { "256x256"  , SkIntToScalar(256), SkIntToScalar(256) }, // kAbs_256x256_Tiling
    { "1/4x1/4"  , SK_Scalar1 / 4    , SK_Scalar1 / 4     }, // kRel_4x4_Tiling
    { "1/1x1/16" , SK_Scalar1        , SK_Scalar1 / 16    }, // kRel_1x16_Tiling
    { "1/16x1/1" , SK_Scalar1 / 16   , SK_Scalar1         }, // kRel_16x1_Tiling
};
SK_COMPILE_ASSERT((SK_ARRAY_COUNT(gTilingInfo) == kLast_TilingMode_Enum),
                  Incomplete_tiling_labels);

610 611 612 613 614 615
SkSize SampleWindow::tileSize() const {
    SkASSERT((TilingMode)fTilingMode < kLast_TilingMode_Enum);
    const struct TilingInfo* info = gTilingInfo + fTilingMode;
    return SkSize::Make(info->w > SK_Scalar1 ? info->w : this->width() * info->w,
                        info->h > SK_Scalar1 ? info->h : this->height() * info->h);
}
616 617
//////////////////////////////////////////////////////////////////////////////

618 619 620 621 622
static SkView* curr_view(SkWindow* wind) {
    SkView::F2BIter iter(wind);
    return iter.next();
}

623 624 625 626 627 628 629 630 631 632 633 634
static bool curr_title(SkWindow* wind, SkString* title) {
    SkView* view = curr_view(wind);
    if (view) {
        SkEvent evt(gTitleEvtName);
        if (view->doQuery(&evt)) {
            title->set(evt.findString(gTitleEvtName));
            return true;
        }
    }
    return false;
}

reed's avatar
reed committed
635 636 637
bool SampleWindow::sendAnimatePulse() {
    SkView* view = curr_view(this);
    if (SampleView::IsSampleView(view)) {
638
        return ((SampleView*)view)->animate(gAnimTimer);
reed's avatar
reed committed
639 640 641 642
    }
    return false;
}

643 644
void SampleWindow::setZoomCenter(float x, float y)
{
645 646
    fZoomCenterX = x;
    fZoomCenterY = y;
647
}
reed@android.com's avatar
reed@android.com committed
648

Scroggo's avatar
Scroggo committed
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
bool SampleWindow::zoomIn()
{
    // Arbitrarily decided
    if (fFatBitsScale == 25) return false;
    fFatBitsScale++;
    this->inval(NULL);
    return true;
}

bool SampleWindow::zoomOut()
{
    if (fFatBitsScale == 1) return false;
    fFatBitsScale--;
    this->inval(NULL);
    return true;
}

void SampleWindow::updatePointer(int x, int y)
{
    fMouseX = x;
    fMouseY = y;
    if (fShowZoomer) {
        this->inval(NULL);
    }
}

675 676 677
static inline SampleWindow::DeviceType cycle_devicetype(SampleWindow::DeviceType ct) {
    static const SampleWindow::DeviceType gCT[] = {
        SampleWindow::kPicture_DeviceType,
678
#if SK_SUPPORT_GPU
679
        SampleWindow::kGPU_DeviceType,
680 681
#if SK_ANGLE
        SampleWindow::kANGLE_DeviceType,
682
#endif // SK_ANGLE
683
        SampleWindow::kRaster_DeviceType, // skip the null gpu device in normal cycling
684
#endif // SK_SUPPORT_GPU
685
        SampleWindow::kRaster_DeviceType
reed@android.com's avatar
reed@android.com committed
686
    };
687
    SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gCT) == SampleWindow::kDeviceTypeCnt, array_size_mismatch);
reed@android.com's avatar
reed@android.com committed
688 689 690
    return gCT[ct];
}

691 692 693 694 695 696 697 698
static SkString getSampleTitle(const SkViewFactory* sampleFactory) {
    SkView* view = (*sampleFactory)();
    SkString title;
    SampleCode::RequestTitle(view, &title);
    view->unref();
    return title;
}

699
static bool compareSampleTitle(const SkViewFactory* first, const SkViewFactory* second) {
700
    return strcmp(getSampleTitle(first).c_str(), getSampleTitle(second).c_str()) < 0;
701 702
}

703 704 705 706 707 708 709 710 711 712
DEFINE_string(slide, "", "Start on this sample.");
DEFINE_int32(msaa, 0, "Request multisampling with this count.");
DEFINE_string(pictureDir, "", "Read pictures from here.");
DEFINE_string(picture, "", "Path to single picture.");
DEFINE_bool(sort, false, "Sort samples by title.");
DEFINE_bool(list, false, "List samples?");
#ifdef SAMPLE_PDF_FILE_VIEWER
DEFINE_string(pdfPath, "", "Path to direcotry of pdf files.");
#endif

reed's avatar
reed committed
713 714
#include "SkTaskGroup.h"

715
SampleWindow::SampleWindow(void* hwnd, int argc, char** argv, DeviceManager* devManager)
716 717
    : INHERITED(hwnd)
    , fDevManager(NULL) {
718

719 720
    SkCommandLineFlags::Parse(argc, argv);

721 722
    fCurrIndex = -1;

723 724 725 726 727
    if (!FLAGS_pictureDir.isEmpty()) {
        SkOSFile::Iter iter(FLAGS_pictureDir[0], "skp");
        SkString filename;
        while (iter.next(&filename)) {
            *fSamples.append() = new PictFileFactory(
tfarina's avatar
tfarina committed
728
                SkOSPath::Join(FLAGS_pictureDir[0], filename.c_str()));
729 730 731 732 733 734 735
        }
    }
    if (!FLAGS_picture.isEmpty()) {
        SkString path(FLAGS_picture[0]);
        fCurrIndex = fSamples.count();
        *fSamples.append() = new PictFileFactory(path);
    }
736
#ifdef SAMPLE_PDF_FILE_VIEWER
737 738 739 740 741
    if (!FLAGS_pdfPath.isEmpty()) {
        SkOSFile::Iter iter(FLAGS_pdfPath[0], "pdf");
        SkString filename;
        while (iter.next(&filename)) {
            *fSamples.append() = new PdfFileViewerFactory(
tfarina's avatar
tfarina committed
742
                SkOSPath::Join(FLAGS_pictureDir[0], filename.c_str()));
743 744 745
        }
    }
#endif
746 747 748 749 750 751 752 753 754
    SkGMRegistyToSampleRegistry();
    {
        const SkViewRegister* reg = SkViewRegister::Head();
        while (reg) {
            *fSamples.append() = reg->factory();
            reg = reg->next();
        }
    }

755
    if (FLAGS_sort) {
756 757 758 759 760
        // Sort samples, so foo.skp and foo.pdf are consecutive and we can quickly spot where
        // skp -> pdf -> png fails.
        SkTQSort(fSamples.begin(), fSamples.end() ? fSamples.end() - 1 : NULL, compareSampleTitle);
    }

761 762 763 764
    if (!FLAGS_slide.isEmpty()) {
        fCurrIndex = findByTitle(FLAGS_slide[0]);
        if (fCurrIndex < 0) {
            fprintf(stderr, "Unknown sample \"%s\"\n", FLAGS_slide[0]);
765
            listTitles();
766 767 768
        }
    }

769 770 771 772 773 774
    fMSAASampleCount = FLAGS_msaa;

    if (FLAGS_list) {
        listTitles();
    }

775 776 777 778 779 780 781 782 783 784 785
    if (fCurrIndex < 0) {
        SkString title;
        if (readTitleFromPrefs(&title)) {
            fCurrIndex = findByTitle(title.c_str());
        }
    }

    if (fCurrIndex < 0) {
        fCurrIndex = 0;
    }

reed's avatar
reed committed
786
    static SkTaskGroup::Enabler enabled(-1);
787 788
    gSampleWindow = this;

789 790 791 792 793
#ifdef  PIPE_FILE
    //Clear existing file or create file if it doesn't exist
    FILE* f = fopen(FILE_PATH, "wb");
    fclose(f);
#endif
794

robertphillips@google.com's avatar
robertphillips@google.com committed
795
    fDeviceType = kRaster_DeviceType;
796 797 798 799 800 801

#if DEFAULT_TO_GPU
    fDeviceType = kGPU_DeviceType;
#endif
#if SK_ANGLE && DEFAULT_TO_ANGLE
    fDeviceType = kANGLE_DeviceType;
802
#endif
803

reed@android.com's avatar
reed@android.com committed
804
    fUseClip = false;
805
    fNClip = false;
reed@android.com's avatar
reed@android.com committed
806
    fAnimating = false;
807
    fRotate = false;
808
    fPerspAnim = false;
809
    fRequestGrabImage = false;
810
    fPipeState = SkOSMenu::kOffState;
811
    fTilingMode = kNo_Tiling;
812
    fMeasureFPS = false;
813 814
    fLCDState = SkOSMenu::kMixedState;
    fAAState = SkOSMenu::kMixedState;
815 816
    fSubpixelState = SkOSMenu::kMixedState;
    fHintingState = 0;
817
    fFilterLevelIndex = 0;
reed@google.com's avatar
reed@google.com committed
818
    fFlipAxis = 0;
reed@android.com's avatar
reed@android.com committed
819 820
    fScrollTestX = fScrollTestY = 0;

Scroggo's avatar
Scroggo committed
821
    fMouseX = fMouseY = 0;
822
    fFatBitsScale = 8;
Scroggo's avatar
Scroggo committed
823 824
    fTypeface = SkTypeface::CreateFromTypeface(NULL, SkTypeface::kBold);
    fShowZoomer = false;
825

826 827
    fZoomLevel = 0;
    fZoomScale = SK_Scalar1;
828

829
    fMagnify = false;
830

831 832
    fSaveToPdf = false;

833 834
    fTransitionNext = 6;
    fTransitionPrev = 2;
835

836
    int sinkID = this->getSinkID();
837 838
    fAppMenu = new SkOSMenu;
    fAppMenu->setTitle("Global Settings");
839
    int itemID;
840 841 842

    itemID =fAppMenu->appendList("Device Type", "Device Type", sinkID, 0,
                                "Raster", "Picture", "OpenGL",
843 844 845 846
#if SK_ANGLE
                                "ANGLE",
#endif
                                NULL);
847 848 849 850 851
    fAppMenu->assignKeyEquivalentToItem(itemID, 'd');
    itemID = fAppMenu->appendTriState("AA", "AA", sinkID, fAAState);
    fAppMenu->assignKeyEquivalentToItem(itemID, 'b');
    itemID = fAppMenu->appendTriState("LCD", "LCD", sinkID, fLCDState);
    fAppMenu->assignKeyEquivalentToItem(itemID, 'l');
852 853 854 855 856 857 858
    itemID = fAppMenu->appendList("FilterLevel", "FilterLevel", sinkID, fFilterLevelIndex,
                                  gFilterLevelStates[0].fName,
                                  gFilterLevelStates[1].fName,
                                  gFilterLevelStates[2].fName,
                                  gFilterLevelStates[3].fName,
                                  gFilterLevelStates[4].fName,
                                  NULL);
859
    fAppMenu->assignKeyEquivalentToItem(itemID, 'n');
860 861 862 863 864 865 866
    itemID = fAppMenu->appendTriState("Subpixel", "Subpixel", sinkID, fSubpixelState);
    fAppMenu->assignKeyEquivalentToItem(itemID, 's');
    itemID = fAppMenu->appendList("Hinting", "Hinting", sinkID, fHintingState,
                                  gHintingStates[0].name,
                                  gHintingStates[1].name,
                                  gHintingStates[2].name,
                                  gHintingStates[3].name,
867 868
                                  gHintingStates[4].name,
                                  NULL);
869
    fAppMenu->assignKeyEquivalentToItem(itemID, 'h');
870

871
    fUsePipeMenuItemID = fAppMenu->appendTriState("Pipe", "Pipe" , sinkID,
872
                                                  fPipeState);
873
    fAppMenu->assignKeyEquivalentToItem(fUsePipeMenuItemID, 'P');
874

875 876 877 878 879 880 881 882
    itemID =fAppMenu->appendList("Tiling", "Tiling", sinkID, fTilingMode,
                                 gTilingInfo[kNo_Tiling].label,
                                 gTilingInfo[kAbs_128x128_Tiling].label,
                                 gTilingInfo[kAbs_256x256_Tiling].label,
                                 gTilingInfo[kRel_4x4_Tiling].label,
                                 gTilingInfo[kRel_1x16_Tiling].label,
                                 gTilingInfo[kRel_16x1_Tiling].label,
                                 NULL);
883 884
    fAppMenu->assignKeyEquivalentToItem(itemID, 't');

885 886 887
    itemID = fAppMenu->appendSwitch("Slide Show", "Slide Show" , sinkID, false);
    fAppMenu->assignKeyEquivalentToItem(itemID, 'a');
    itemID = fAppMenu->appendSwitch("Clip", "Clip" , sinkID, fUseClip);
888
    fAppMenu->assignKeyEquivalentToItem(itemID, 'c');
889
    itemID = fAppMenu->appendSwitch("Flip X", "Flip X" , sinkID, false);
890 891 892 893 894 895 896
    fAppMenu->assignKeyEquivalentToItem(itemID, 'x');
    itemID = fAppMenu->appendSwitch("Flip Y", "Flip Y" , sinkID, false);
    fAppMenu->assignKeyEquivalentToItem(itemID, 'y');
    itemID = fAppMenu->appendSwitch("Zoomer", "Zoomer" , sinkID, fShowZoomer);
    fAppMenu->assignKeyEquivalentToItem(itemID, 'z');
    itemID = fAppMenu->appendSwitch("Magnify", "Magnify" , sinkID, fMagnify);
    fAppMenu->assignKeyEquivalentToItem(itemID, 'm');
897 898 899
    itemID =fAppMenu->appendList("Transition-Next", "Transition-Next", sinkID,
                                fTransitionNext, "Up", "Up and Right", "Right",
                                "Down and Right", "Down", "Down and Left",
900
                                "Left", "Up and Left", NULL);
901
    fAppMenu->assignKeyEquivalentToItem(itemID, 'j');
902 903 904
    itemID =fAppMenu->appendList("Transition-Prev", "Transition-Prev", sinkID,
                                fTransitionPrev, "Up", "Up and Right", "Right",
                                "Down and Right", "Down", "Down and Left",
905
                                "Left", "Up and Left", NULL);
906 907 908
    fAppMenu->assignKeyEquivalentToItem(itemID, 'k');
    itemID = fAppMenu->appendAction("Save to PDF", sinkID);
    fAppMenu->assignKeyEquivalentToItem(itemID, 'e');
909

910 911 912
    this->addMenu(fAppMenu);
    fSlideMenu = new SkOSMenu;
    this->addMenu(fSlideMenu);
913

914
    this->setColorType(kN32_SkColorType);
915
    this->setVisibleP(true);
916
    this->setClipToBounds(false);
reed@android.com's avatar
reed@android.com committed
917

918
    this->loadView((*fSamples[fCurrIndex])());
919

920 921 922 923 924 925
    if (NULL == devManager) {
        fDevManager = new DefaultDeviceManager();
    } else {
        devManager->ref();
        fDevManager = devManager;
    }
926
    fDevManager->setUpBackend(this, fMSAASampleCount);
927

Scroggo's avatar
Scroggo committed
928 929 930 931 932
    // If another constructor set our dimensions, ensure that our
    // onSizeChange gets called.
    if (this->height() && this->width()) {
        this->onSizeChange();
    }
933 934 935 936 937

    // can't call this synchronously, since it may require a subclass to
    // to implement, or the caller may need us to have returned from the
    // constructor first. Hence we post an event to ourselves.
//    this->updateTitle();
938
    post_event_to_sink(new SkEvent(gUpdateWindowTitleEvtName), this);
939 940

    gAnimTimer.run();
reed@android.com's avatar
reed@android.com committed
941 942 943
}

SampleWindow::~SampleWindow() {
944
    SkSafeUnref(fTypeface);
945
    SkSafeUnref(fDevManager);
reed@android.com's avatar
reed@android.com committed
946 947
}

948

949 950 951 952 953 954 955
int SampleWindow::findByTitle(const char title[]) {
    int i, count = fSamples.count();
    for (i = 0; i < count; i++) {
        if (getSampleTitle(i).equals(title)) {
            return i;
        }
    }
956
    return -1;
957 958
}

959 960 961 962 963 964 965 966
void SampleWindow::listTitles() {
    int count = fSamples.count();
    SkDebugf("All Slides:\n");
    for (int i = 0; i < count; i++) {
        SkDebugf("    %s\n", getSampleTitle(i).c_str());
    }
}

967 968
static SkBitmap capture_bitmap(SkCanvas* canvas) {
    SkBitmap bm;
reed's avatar
reed committed
969
    if (bm.tryAllocPixels(canvas->imageInfo())) {
970 971
        canvas->readPixels(&bm, 0, 0);
    }
972 973 974 975 976
    return bm;
}

static bool bitmap_diff(SkCanvas* canvas, const SkBitmap& orig,
                        SkBitmap* diff) {
reed's avatar
reed committed
977
    SkBitmap src = capture_bitmap(canvas);
978

979 980 981 982 983 984 985 986 987 988 989 990 991 992
    SkAutoLockPixels alp0(src);
    SkAutoLockPixels alp1(orig);
    for (int y = 0; y < src.height(); y++) {
        const void* srcP = src.getAddr(0, y);
        const void* origP = orig.getAddr(0, y);
        size_t bytes = src.width() * src.bytesPerPixel();
        if (memcmp(srcP, origP, bytes)) {
            SkDebugf("---------- difference on line %d\n", y);
            return true;
        }
    }
    return false;
}

Scroggo's avatar
Scroggo committed
993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
static void drawText(SkCanvas* canvas, SkString string, SkScalar left, SkScalar top, SkPaint& paint)
{
    SkColor desiredColor = paint.getColor();
    paint.setColor(SK_ColorWHITE);
    const char* c_str = string.c_str();
    size_t size = string.size();
    SkRect bounds;
    paint.measureText(c_str, size, &bounds);
    bounds.offset(left, top);
    SkScalar inset = SkIntToScalar(-2);
    bounds.inset(inset, inset);
    canvas->drawRect(bounds, paint);
    if (desiredColor != SK_ColorBLACK) {
        paint.setColor(SK_ColorBLACK);
        canvas->drawText(c_str, size, left + SK_Scalar1, top + SK_Scalar1, paint);
    }
    paint.setColor(desiredColor);
    canvas->drawText(c_str, size, left, top, paint);
}

1013 1014
#define XCLIP_N  8
#define YCLIP_N  8
1015 1016

void SampleWindow::draw(SkCanvas* canvas) {
1017
    gAnimTimer.updateTime();
1018

1019 1020
    if (fGesture.isActive()) {
        this->updateMatrix();
reed@google.com's avatar
reed@google.com committed
1021
    }
1022

1023 1024 1025 1026
    if (fMeasureFPS) {
        fMeasureFPS_Time = 0;
    }

1027
    if (fNClip) {
1028 1029
        this->INHERITED::draw(canvas);
        SkBitmap orig = capture_bitmap(canvas);
1030 1031 1032 1033 1034 1035

        const SkScalar w = this->width();
        const SkScalar h = this->height();
        const SkScalar cw = w / XCLIP_N;
        const SkScalar ch = h / YCLIP_N;
        for (int y = 0; y < YCLIP_N; y++) {
1036 1037 1038 1039 1040 1041
            SkRect r;
            r.fTop = y * ch;
            r.fBottom = (y + 1) * ch;
            if (y == YCLIP_N - 1) {
                r.fBottom = h;
            }
1042 1043
            for (int x = 0; x < XCLIP_N; x++) {
                SkAutoCanvasRestore acr(canvas, true);
1044 1045 1046 1047 1048
                r.fLeft = x * cw;
                r.fRight = (x + 1) * cw;
                if (x == XCLIP_N - 1) {
                    r.fRight = w;
                }
1049 1050 1051 1052
                canvas->clipRect(r);
                this->INHERITED::draw(canvas);
            }
        }
1053

1054 1055 1056
        SkBitmap diff;
        if (bitmap_diff(canvas, orig, &diff)) {
        }
1057
    } else {
1058
        SkSize tile = this->tileSize();
1059

1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
        if (kNo_Tiling == fTilingMode) {
            this->INHERITED::draw(canvas); // no looping or surfaces needed
        } else {
            const int w = SkScalarRoundToInt(tile.width());
            const int h = SkScalarRoundToInt(tile.height());
            SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
            SkAutoTUnref<SkSurface> surface(canvas->newSurface(info));
            SkCanvas* tileCanvas = surface->getCanvas();

            for (SkScalar y = 0; y < height(); y += tile.height()) {
                for (SkScalar x = 0; x < width(); x += tile.width()) {
                    SkAutoCanvasRestore acr(tileCanvas, true);
                    tileCanvas->translate(-x, -y);
                    tileCanvas->clear(0);
                    this->INHERITED::draw(tileCanvas);
                    surface->draw(canvas, x, y, NULL);
                }
1077
            }
1078

1079
            // for drawing the borders between tiles
1080 1081 1082
            SkPaint paint;
            paint.setColor(0x60FF00FF);
            paint.setStyle(SkPaint::kStroke_Style);
1083 1084 1085

            for (SkScalar y = 0; y < height(); y += tile.height()) {
                for (SkScalar x = 0; x < width(); x += tile.width()) {
1086
                    canvas->drawRect(SkRect::MakeXYWH(x, y, tile.width(), tile.height()), paint);
1087 1088 1089
                }
            }
        }
1090
    }
1091
    if (fShowZoomer && !fSaveToPdf) {
1092 1093
        showZoomer(canvas);
    }
1094 1095 1096
    if (fMagnify && !fSaveToPdf) {
        magnify(canvas);
    }
1097

1098 1099 1100 1101 1102
    if (fMeasureFPS && fMeasureFPS_Time) {
        this->updateTitle();
        this->postInvalDelay();
    }

reed's avatar
reed committed
1103 1104 1105 1106
    if (this->sendAnimatePulse()) {
        this->inval(NULL);
    }

1107
    // do this last
1108
    fDevManager->publishCanvas(fDeviceType, canvas, this);
1109 1110
}

1111 1112 1113 1114 1115
static float clipW = 200;
static float clipH = 200;
void SampleWindow::magnify(SkCanvas* canvas) {
    SkRect r;
    int count = canvas->save();
1116

1117
    SkMatrix m = canvas->getTotalMatrix();
1118 1119 1120
    if (!m.invert(&m)) {
        return;
    }
1121
    SkPoint offset, center;
1122 1123 1124 1125
    SkScalar mouseX = fMouseX * SK_Scalar1;
    SkScalar mouseY = fMouseY * SK_Scalar1;
    m.mapXY(mouseX - clipW/2, mouseY - clipH/2, &offset);
    m.mapXY(mouseX, mouseY, &center);
1126

1127 1128
    r.set(0, 0, clipW * m.getScaleX(), clipH * m.getScaleX());
    r.offset(offset.fX, offset.fY);
1129

1130 1131 1132
    SkPaint paint;
    paint.setColor(0xFF66AAEE);
    paint.setStyle(SkPaint::kStroke_Style);
1133
    paint.setStrokeWidth(10.f * m.getScaleX());
1134 1135 1136 1137
    //lense offset
    //canvas->translate(0, -250);
    canvas->drawRect(r, paint);
    canvas->clipRect(r);
1138

1139 1140
    m = canvas->getTotalMatrix();
    m.setTranslate(-center.fX, -center.fY);
1141
    m.postScale(0.5f * fFatBitsScale, 0.5f * fFatBitsScale);
1142 1143
    m.postTranslate(center.fX, center.fY);
    canvas->concat(m);
1144

1145
    this->INHERITED::draw(canvas);
1146

1147 1148 1149
    canvas->restoreToCount(count);
}

1150
void SampleWindow::showZoomer(SkCanvas* canvas) {
Scroggo's avatar
Scroggo committed
1151 1152 1153
        int count = canvas->save();
        canvas->resetMatrix();
        // Ensure the mouse position is on screen.
1154 1155
        int width = SkScalarRoundToInt(this->width());
        int height = SkScalarRoundToInt(this->height());
Scroggo's avatar
Scroggo committed
1156 1157 1158 1159
        if (fMouseX >= width) fMouseX = width - 1;
        else if (fMouseX < 0) fMouseX = 0;
        if (fMouseY >= height) fMouseY = height - 1;
        else if (fMouseY < 0) fMouseY = 0;
1160

Scroggo's avatar
Scroggo committed
1161
        SkBitmap bitmap = capture_bitmap(canvas);
1162 1163
        bitmap.lockPixels();

Scroggo's avatar
Scroggo committed
1164
        // Find the size of the zoomed in view, forced to be odd, so the examined pixel is in the middle.
1165 1166
        int zoomedWidth = (width >> 1) | 1;
        int zoomedHeight = (height >> 1) | 1;
Scroggo's avatar
Scroggo committed
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232
        SkIRect src;
        src.set(0, 0, zoomedWidth / fFatBitsScale, zoomedHeight / fFatBitsScale);
        src.offset(fMouseX - (src.width()>>1), fMouseY - (src.height()>>1));
        SkRect dest;
        dest.set(0, 0, SkIntToScalar(zoomedWidth), SkIntToScalar(zoomedHeight));
        dest.offset(SkIntToScalar(width - zoomedWidth), SkIntToScalar(height - zoomedHeight));
        SkPaint paint;
        // Clear the background behind our zoomed in view
        paint.setColor(SK_ColorWHITE);
        canvas->drawRect(dest, paint);
        canvas->drawBitmapRect(bitmap, &src, dest);
        paint.setColor(SK_ColorBLACK);
        paint.setStyle(SkPaint::kStroke_Style);
        // Draw a border around the pixel in the middle
        SkRect originalPixel;
        originalPixel.set(SkIntToScalar(fMouseX), SkIntToScalar(fMouseY), SkIntToScalar(fMouseX + 1), SkIntToScalar(fMouseY + 1));
        SkMatrix matrix;
        SkRect scalarSrc;
        scalarSrc.set(src);
        SkColor color = bitmap.getColor(fMouseX, fMouseY);
        if (matrix.setRectToRect(scalarSrc, dest, SkMatrix::kFill_ScaleToFit)) {
            SkRect pixel;
            matrix.mapRect(&pixel, originalPixel);
            // TODO Perhaps measure the values and make the outline white if it's "dark"
            if (color == SK_ColorBLACK) {
                paint.setColor(SK_ColorWHITE);
            }
            canvas->drawRect(pixel, paint);
        }
        paint.setColor(SK_ColorBLACK);
        // Draw a border around the destination rectangle
        canvas->drawRect(dest, paint);
        paint.setStyle(SkPaint::kStrokeAndFill_Style);
        // Identify the pixel and its color on screen
        paint.setTypeface(fTypeface);
        paint.setAntiAlias(true);
        SkScalar lineHeight = paint.getFontMetrics(NULL);
        SkString string;
        string.appendf("(%i, %i)", fMouseX, fMouseY);
        SkScalar left = dest.fLeft + SkIntToScalar(3);
        SkScalar i = SK_Scalar1;
        drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
        // Alpha
        i += SK_Scalar1;
        string.reset();
        string.appendf("A: %X", SkColorGetA(color));
        drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
        // Red
        i += SK_Scalar1;
        string.reset();
        string.appendf("R: %X", SkColorGetR(color));
        paint.setColor(SK_ColorRED);
        drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
        // Green
        i += SK_Scalar1;
        string.reset();
        string.appendf("G: %X", SkColorGetG(color));
        paint.setColor(SK_ColorGREEN);
        drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
        // Blue
        i += SK_Scalar1;
        string.reset();
        string.appendf("B: %X", SkColorGetB(color));
        paint.setColor(SK_ColorBLUE);
        drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
        canvas->restoreToCount(count);
1233 1234
}

reed@android.com's avatar
reed@android.com committed
1235 1236 1237 1238 1239
void SampleWindow::onDraw(SkCanvas* canvas) {
}

#include "SkColorPriv.h"

1240 1241 1242 1243 1244 1245
void SampleWindow::saveToPdf()
{
    fSaveToPdf = true;
    this->inval(NULL);
}

reed@android.com's avatar
reed@android.com committed
1246
SkCanvas* SampleWindow::beforeChildren(SkCanvas* canvas) {
1247
    if (fSaveToPdf) {
1248 1249 1250 1251 1252 1253 1254 1255 1256 1257
        SkString name;
        if (!this->getRawTitle(&name)) {
            name.set("unknown_sample");
        }
        name.append(".pdf");
#ifdef SK_BUILD_FOR_ANDROID
        name.prepend("/sdcard/");
#endif
        fPDFDocument.reset(SkDocument::CreatePDF(name.c_str()));
        canvas = fPDFDocument->beginPage(this->width(), this->height());
1258
    } else if (kPicture_DeviceType == fDeviceType) {
1259
        canvas = fRecorder.beginRecording(9999, 9999, NULL, 0);
1260
    } else {
1261
#if SK_SUPPORT_GPU
1262
        if (kNullGPU_DeviceType != fDeviceType)
1263
#endif
1264 1265
        {
            canvas = this->INHERITED::beforeChildren(canvas);
1266
        }
reed@android.com's avatar
reed@android.com committed
1267 1268 1269 1270
    }

    if (fUseClip) {
        canvas->drawColor(0xFFFF88FF);
reed@google.com's avatar
reed@google.com committed
1271
        canvas->clipPath(fClipPath, SkRegion::kIntersect_Op, true);
reed@android.com's avatar
reed@android.com committed
1272 1273 1274 1275
    }

    return canvas;
}
reed's avatar
reed committed
1276
#include "SkMultiPictureDraw.h"
reed@android.com's avatar
reed@android.com committed
1277
void SampleWindow::afterChildren(SkCanvas* orig) {
1278 1279
    if (fSaveToPdf) {
        fSaveToPdf = false;
1280 1281
        fPDFDocument->endPage();
        fPDFDocument.reset(NULL);
1282 1283 1284 1285 1286
        // We took over the draw calls in order to create the PDF, so we need
        // to redraw.
        this->inval(NULL);
        return;
    }
1287

1288 1289
    if (fRequestGrabImage) {
        fRequestGrabImage = false;
1290

reed's avatar
reed committed
1291 1292
        SkBitmap bmp = capture_bitmap(orig);
        if (!bmp.isNull()) {
1293 1294
            static int gSampleGrabCounter;
            SkString name;
1295
            name.printf("sample_grab_%d.png", gSampleGrabCounter++);
1296
            SkImageEncoder::EncodeFile(name.c_str(), bmp,
1297 1298 1299 1300
                                       SkImageEncoder::kPNG_Type, 100);
        }
    }

1301
    if (kPicture_DeviceType == fDeviceType) {
1302
        SkAutoTUnref<const SkPicture> picture(fRecorder.endRecording());
1303

reed's avatar
reed committed
1304 1305 1306 1307
        if (true) {
            picture->preroll(NULL, NULL, NULL, NULL);
        }

1308
        if (true) {
1309
            this->installDrawFilter(orig);
reed's avatar
reed committed
1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330
            
            if (true) {
                SkImageInfo info;
                size_t rowBytes;
                void* addr = orig->accessTopLayerPixels(&info, &rowBytes);
                if (addr) {
                    SkSurface* surfs[4];
                    SkMultiPictureDraw md;

                    SkImageInfo n = SkImageInfo::Make(info.width()/2, info.height()/2,
                                                      info.colorType(), info.alphaType());
                    int index = 0;
                    for (int y = 0; y < 2; ++y) {
                        for (int x = 0; x < 2; ++x) {
                            char* p = (char*)addr;
                            p += y * n.height() * rowBytes;
                            p += x * n.width() * sizeof(SkPMColor);
                            surfs[index] = SkSurface::NewRasterDirect(n, p, rowBytes);
                            SkCanvas* c = surfs[index]->getCanvas();
                            c->translate(SkIntToScalar(-x * n.width()),
                                         SkIntToScalar(-y * n.height()));
1331
                            c->concat(orig->getTotalMatrix());
reed's avatar
reed committed
1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343
                            md.add(c, picture, NULL, NULL);
                            index++;
                        }
                    }
                    md.draw();
                    for (int i = 0; i < 4; ++i) {
                        surfs[i]->unref();
                    }
                }
            } else {
                orig->drawPicture(picture);
            }
1344 1345
        } else if (true) {
            SkDynamicMemoryWStream ostream;
1346
            picture->serialize(&ostream);
1347 1348

            SkAutoDataUnref data(ostream.copyToData());
1349
            SkMemoryStream istream(data->data(), data->size());
1350 1351
            SkAutoTUnref<SkPicture> pict(SkPicture::CreateFromStream(&istream));
            if (pict.get() != NULL) {
1352
                orig->drawPicture(pict.get());
1353
            }
1354
        } else {
1355
            picture->playback(orig);
1356
        }
reed@android.com's avatar
reed@android.com committed
1357
    }
1358

1359
    // Do this after presentGL and other finishing, rather than in afterChild
1360 1361
    if (fMeasureFPS && fMeasureFPS_StartTime) {
        fMeasureFPS_Time += SkTime::GetMSecs() - fMeasureFPS_StartTime;
1362
    }
reed@android.com's avatar
reed@android.com committed
1363 1364
}

1365 1366 1367 1368 1369
void SampleWindow::beforeChild(SkView* child, SkCanvas* canvas) {
    if (fRotate) {
        SkScalar cx = this->width() / 2;
        SkScalar cy = this->height() / 2;
        canvas->translate(cx, cy);
1370
        canvas->rotate(gAnimTimer.scaled(10));
1371 1372
        canvas->translate(-cx, -cy);
    }
1373

1374
    if (fPerspAnim) {
1375
        SkScalar secs = gAnimTimer.scaled(1);
1376 1377 1378

        static const SkScalar gAnimPeriod = 10 * SK_Scalar1;
        static const SkScalar gAnimMag = SK_Scalar1 / 1000;
1379 1380
        SkScalar t = SkScalarMod(secs, gAnimPeriod);
        if (SkScalarFloorToInt(SkScalarDiv(secs, gAnimPeriod)) & 0x1) {
1381 1382 1383 1384 1385 1386
            t = gAnimPeriod - t;
        }
        t = 2 * t - gAnimPeriod;
        t = SkScalarMul(SkScalarDiv(t, gAnimPeriod), gAnimMag);
        SkMatrix m;
        m.reset();
reed's avatar
reed committed
1387
#if 1
1388
        m.setPerspY(t);
reed's avatar
reed committed
1389 1390 1391 1392 1393
#else
        m.setPerspY(SK_Scalar1 / 1000);
        m.setSkewX(SkScalarDiv(8, 25));
        m.dump();
#endif
1394 1395
        canvas->concat(m);
    }
1396

1397
    this->installDrawFilter(canvas);
1398 1399

    if (fMeasureFPS) {
1400
        if (SampleView::SetRepeatDraw(child, FPS_REPEAT_COUNT)) {
1401
            fMeasureFPS_StartTime = SkTime::GetMSecs();
1402 1403 1404
        }
    } else {
        (void)SampleView::SetRepeatDraw(child, 1);
1405
    }
1406
    if (fPerspAnim || fRotate) {
1407 1408
        this->inval(NULL);
    }
1409 1410 1411
}

void SampleWindow::afterChild(SkView* child, SkCanvas* canvas) {
1412
    canvas->setDrawFilter(NULL);
1413 1414
}

1415 1416 1417
static SkColorType gColorTypeCycle[] = {
    kUnknown_SkColorType,           // none -> none
    kUnknown_SkColorType,           // a8 -> none
1418
    kN32_SkColorType,               // 565 -> 8888
1419
    kN32_SkColorType,               // 4444 -> 8888
1420 1421 1422
    kRGB_565_SkColorType,           // 8888 -> 565
    kRGB_565_SkColorType,           // 8888 -> 565
    kUnknown_SkColorType,           // index8 -> none
reed@android.com's avatar
reed@android.com committed
1423 1424
};

1425
static SkColorType cycle_colortypes(SkColorType c) {
1426
    return gColorTypeCycle[c];
reed@android.com's avatar
reed@android.com committed
1427 1428
}

1429
void SampleWindow::changeZoomLevel(float delta) {
1430
    fZoomLevel += delta;
1431
    if (fZoomLevel > 0) {
1432 1433
        fZoomLevel = SkMinScalar(fZoomLevel, MAX_ZOOM_LEVEL);
        fZoomScale = fZoomLevel + SK_Scalar1;
1434
    } else if (fZoomLevel < 0) {
1435 1436
        fZoomLevel = SkMaxScalar(fZoomLevel, MIN_ZOOM_LEVEL);
        fZoomScale = SK_Scalar1 / (SK_Scalar1 - fZoomLevel);
1437 1438 1439
    } else {
        fZoomScale = SK_Scalar1;
    }
1440 1441
    this->updateMatrix();
}
1442

1443 1444 1445 1446 1447 1448 1449 1450 1451
void SampleWindow::updateMatrix(){
    SkMatrix m;
    m.reset();
    if (fZoomLevel) {
        SkPoint center;
        //m = this->getLocalMatrix();//.invert(&m);
        m.mapXY(fZoomCenterX, fZoomCenterY, &center);
        SkScalar cx = center.fX;
        SkScalar cy = center.fY;
1452

1453 1454 1455 1456
        m.setTranslate(-cx, -cy);
        m.postScale(fZoomScale, fZoomScale);
        m.postTranslate(cx, cy);
    }
1457

1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471
    if (fFlipAxis) {
        m.preTranslate(fZoomCenterX, fZoomCenterY);
        if (fFlipAxis & kFlipAxis_X) {
            m.preScale(-SK_Scalar1, SK_Scalar1);
        }
        if (fFlipAxis & kFlipAxis_Y) {
            m.preScale(SK_Scalar1, -SK_Scalar1);
        }
        m.preTranslate(-fZoomCenterX, -fZoomCenterY);
        //canvas->concat(m);
    }
    // Apply any gesture matrix
    m.preConcat(fGesture.localM());
    m.preConcat(fGesture.globalM());
1472

1473
    this->setLocalMatrix(m);
1474

1475
    this->updateTitle();
1476 1477
    this->inval(NULL);
}
1478
bool SampleWindow::previousSample() {
Scroggo's avatar
Scroggo committed
1479
    fCurrIndex = (fCurrIndex - 1 + fSamples.count()) % fSamples.count();
reed's avatar
reed committed
1480
    this->loadView((*fSamples[fCurrIndex])());
1481 1482 1483
    return true;
}

reed@android.com's avatar
reed@android.com committed
1484
bool SampleWindow::nextSample() {
1485
    fCurrIndex = (fCurrIndex + 1) % fSamples.count();
reed's avatar
reed committed
1486
    this->loadView((*fSamples[fCurrIndex])());
1487
    return true;
reed@android.com's avatar
reed@android.com committed
1488 1489
}

1490 1491
bool SampleWindow::goToSample(int i) {
    fCurrIndex = (i) % fSamples.count();
reed's avatar
reed committed
1492
    this->loadView((*fSamples[fCurrIndex])());
1493 1494 1495 1496
    return true;
}

SkString SampleWindow::getSampleTitle(int i) {
1497
    return ::getSampleTitle(fSamples[i]);
1498 1499 1500 1501 1502 1503
}

int SampleWindow::sampleCount() {
    return fSamples.count();
}

1504
void SampleWindow::showOverview() {
reed's avatar
reed committed
1505
    this->loadView(create_overview(fSamples.count(), fSamples.begin()));
1506 1507
}

1508
void SampleWindow::installDrawFilter(SkCanvas* canvas) {
1509 1510
    canvas->setDrawFilter(new FlagsDrawFilter(fLCDState, fAAState, fSubpixelState,
                                              fHintingState, fFilterLevelIndex))->unref();
1511 1512
}

1513 1514
void SampleWindow::postAnimatingEvent() {
    if (fAnimating) {
1515
        (new SkEvent(ANIMATING_EVENTTYPE, this->getSinkID()))->postDelay(ANIMATING_DELAY);
1516 1517
    }
}
1518

reed@android.com's avatar
reed@android.com committed
1519
bool SampleWindow::onEvent(const SkEvent& evt) {
1520 1521 1522 1523
    if (evt.isType(gUpdateWindowTitleEvtName)) {
        this->updateTitle();
        return true;
    }
reed@android.com's avatar
reed@android.com committed
1524 1525 1526 1527 1528 1529 1530
    if (evt.isType(ANIMATING_EVENTTYPE)) {
        if (fAnimating) {
            this->nextSample();
            this->postAnimatingEvent();
        }
        return true;
    }
1531 1532
    if (evt.isType("replace-transition-view")) {
        this->loadView((SkView*)SkEventSink::FindSink(evt.getFast32()));
1533 1534
        return true;
    }
1535
    if (evt.isType("set-curr-index")) {
1536
        this->goToSample(evt.getFast32());
1537 1538
        return true;
    }
1539 1540 1541 1542
    if (isInvalEvent(evt)) {
        this->inval(NULL);
        return true;
    }
1543
    int selected = -1;
1544
    if (SkOSMenu::FindListIndex(evt, "Device Type", &selected)) {
1545
        this->setDeviceType((DeviceType)selected);
1546
        return true;
1547
    }
1548
    if (SkOSMenu::FindTriState(evt, "Pipe", &fPipeState)) {
1549
#ifdef PIPE_NET
1550
        if (!fPipeState != SkOSMenu::kOnState)
1551 1552
            gServer.disconnectAll();
#endif
1553
        (void)SampleView::SetUsePipe(curr_view(this), fPipeState);
1554 1555
        this->updateTitle();
        this->inval(NULL);
1556 1557
        return true;
    }
1558
    if (SkOSMenu::FindSwitchState(evt, "Slide Show", NULL)) {
1559 1560 1561
        this->toggleSlideshow();
        return true;
    }
1562 1563
    if (SkOSMenu::FindTriState(evt, "AA", &fAAState) ||
        SkOSMenu::FindTriState(evt, "LCD", &fLCDState) ||
1564
        SkOSMenu::FindListIndex(evt, "FilterLevel", &fFilterLevelIndex) ||
1565 1566
        SkOSMenu::FindTriState(evt, "Subpixel", &fSubpixelState) ||
        SkOSMenu::FindListIndex(evt, "Hinting", &fHintingState) ||
1567 1568 1569 1570
        SkOSMenu::FindSwitchState(evt, "Clip", &fUseClip) ||
        SkOSMenu::FindSwitchState(evt, "Zoomer", &fShowZoomer) ||
        SkOSMenu::FindSwitchState(evt, "Magnify", &fMagnify) ||
        SkOSMenu::FindListIndex(evt, "Transition-Next", &fTransitionNext) ||
1571 1572 1573 1574 1575 1576 1577 1578 1579
        SkOSMenu::FindListIndex(evt, "Transition-Prev", &fTransitionPrev)) {
        this->inval(NULL);
        this->updateTitle();
        return true;
    }
    if (SkOSMenu::FindListIndex(evt, "Tiling", &fTilingMode)) {
        if (SampleView::IsSampleView(curr_view(this))) {
            ((SampleView*)curr_view(this))->onTileSizeChanged(this->tileSize());
        }
1580 1581 1582 1583
        this->inval(NULL);
        this->updateTitle();
        return true;
    }
1584
    if (SkOSMenu::FindSwitchState(evt, "Flip X", NULL)) {
1585
        fFlipAxis ^= kFlipAxis_X;
1586
        this->updateMatrix();
1587 1588
        return true;
    }
1589
    if (SkOSMenu::FindSwitchState(evt, "Flip Y", NULL)) {
1590
        fFlipAxis ^= kFlipAxis_Y;
1591
        this->updateMatrix();
1592 1593
        return true;
    }
1594
    if (SkOSMenu::FindAction(evt,"Save to PDF")) {
1595 1596
        this->saveToPdf();
        return true;
1597
    }
reed@android.com's avatar
reed@android.com committed
1598 1599 1600
    return this->INHERITED::onEvent(evt);
}

1601 1602 1603 1604 1605 1606
bool SampleWindow::onQuery(SkEvent* query) {
    if (query->isType("get-slide-count")) {
        query->setFast32(fSamples.count());
        return true;
    }
    if (query->isType("get-slide-title")) {
1607
        SkView* view = (*fSamples[query->getFast32()])();
1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618
        SkEvent evt(gTitleEvtName);
        if (view->doQuery(&evt)) {
            query->setString("title", evt.findString(gTitleEvtName));
        }
        SkSafeUnref(view);
        return true;
    }
    if (query->isType("use-fast-text")) {
        SkEvent evt(gFastTextEvtName);
        return curr_view(this)->doQuery(&evt);
    }
1619 1620 1621 1622
    if (query->isType("ignore-window-bitmap")) {
        query->setFast32(this->getGrContext() != NULL);
        return true;
    }
1623 1624 1625
    return this->INHERITED::onQuery(query);
}

Cary Clark's avatar
Cary Clark committed
1626
DECLARE_bool(portableFonts);
1627

reed@android.com's avatar
reed@android.com committed
1628
bool SampleWindow::onHandleChar(SkUnichar uni) {
1629 1630 1631 1632 1633 1634 1635 1636 1637 1638
    {
        SkView* view = curr_view(this);
        if (view) {
            SkEvent evt(gCharEvtName);
            evt.setFast32(uni);
            if (view->doQuery(&evt)) {
                return true;
            }
        }
    }
1639

reed@android.com's avatar
reed@android.com committed
1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652
    int dx = 0xFF;
    int dy = 0xFF;

    switch (uni) {
        case '5': dx =  0; dy =  0; break;
        case '8': dx =  0; dy = -1; break;
        case '6': dx =  1; dy =  0; break;
        case '2': dx =  0; dy =  1; break;
        case '4': dx = -1; dy =  0; break;
        case '7': dx = -1; dy = -1; break;
        case '9': dx =  1; dy = -1; break;
        case '3': dx =  1; dy =  1; break;
        case '1': dx = -1; dy =  1; break;
1653

reed@android.com's avatar
reed@android.com committed
1654 1655 1656
        default:
            break;
    }
1657

reed@android.com's avatar
reed@android.com committed
1658 1659 1660 1661 1662 1663 1664 1665 1666 1667
    if (0xFF != dx && 0xFF != dy) {
        if ((dx | dy) == 0) {
            fScrollTestX = fScrollTestY = 0;
        } else {
            fScrollTestX += dx;
            fScrollTestY += dy;
        }
        this->inval(NULL);
        return true;
    }
1668

reed@android.com's avatar
reed@android.com committed
1669
    switch (uni) {
1670 1671 1672 1673 1674 1675
        case 27:    // ESC
            gAnimTimer.stop();
            if (this->sendAnimatePulse()) {
                this->inval(NULL);
            }
            break;
reed's avatar
reed committed
1676
        case ' ':
1677
            gAnimTimer.togglePauseResume();
reed's avatar
reed committed
1678 1679 1680 1681
            if (this->sendAnimatePulse()) {
                this->inval(NULL);
            }
            break;
1682
        case 'B':
1683 1684 1685 1686
            post_event_to_sink(SkNEW_ARGS(SkEvent, ("PictFileView::toggleBBox")), curr_view(this));
            // Cannot call updateTitle() synchronously, because the toggleBBox event is still in
            // the queue.
            post_event_to_sink(SkNEW_ARGS(SkEvent, (gUpdateWindowTitleEvtName)), this);
1687 1688
            this->inval(NULL);
            break;
1689 1690 1691
        case 'D':
            toggleDistanceFieldFonts();
            break;
1692
        case 'f':
1693
            // only
1694
            toggleFPS();
1695
            break;
1696
        case 'F':
Cary Clark's avatar
Cary Clark committed
1697
            FLAGS_portableFonts ^= true;
1698 1699
            this->inval(NULL);
            break;
1700 1701 1702
        case 'g':
            fRequestGrabImage = true;
            this->inval(NULL);
1703 1704 1705
            break;
        case 'G':
            gShowGMBounds = !gShowGMBounds;
1706
            post_event_to_sink(GMSampleView::NewShowSizeEvt(gShowGMBounds),
1707 1708
                            curr_view(this));
            this->inval(NULL);
1709
            break;
1710 1711 1712
        case 'i':
            this->zoomIn();
            break;
1713 1714 1715
        case 'o':
            this->zoomOut();
            break;
1716 1717 1718 1719 1720
        case 'r':
            fRotate = !fRotate;
            this->inval(NULL);
            this->updateTitle();
            return true;
1721 1722 1723 1724 1725
        case 'k':
            fPerspAnim = !fPerspAnim;
            this->inval(NULL);
            this->updateTitle();
            return true;
1726
#if SK_SUPPORT_GPU
1727
        case '\\':
1728 1729 1730
            this->setDeviceType(kNullGPU_DeviceType);
            this->inval(NULL);
            this->updateTitle();
1731
            return true;
1732 1733 1734 1735
        case 'p':
            {
                GrContext* grContext = this->getGrContext();
                if (grContext) {
1736 1737
                    size_t cacheBytes;
                    grContext->getResourceCacheUsage(NULL, &cacheBytes);
1738
                    grContext->freeGpuResources();
1739
                    SkDebugf("Purged %d bytes from the GPU resource cache.\n", cacheBytes);
1740 1741 1742
                }
            }
            return true;
1743
#endif
reed@android.com's avatar
reed@android.com committed
1744 1745
        default:
            break;
reed@android.com's avatar
reed@android.com committed
1746
    }
1747

1748 1749 1750
    if (fAppMenu->handleKeyEquivalent(uni)|| fSlideMenu->handleKeyEquivalent(uni)) {
        this->onUpdateMenu(fAppMenu);
        this->onUpdateMenu(fSlideMenu);
1751 1752
        return true;
    }
reed@android.com's avatar
reed@android.com committed
1753 1754 1755
    return this->INHERITED::onHandleChar(uni);
}

1756
void SampleWindow::setDeviceType(DeviceType type) {
1757 1758 1759 1760 1761 1762 1763
    if (type == fDeviceType)
        return;

    fDevManager->tearDownBackend(this);

    fDeviceType = type;

1764
    fDevManager->setUpBackend(this, fMSAASampleCount);
1765

1766 1767 1768 1769
    this->updateTitle();
    this->inval(NULL);
}

1770 1771 1772 1773 1774 1775 1776
void SampleWindow::toggleSlideshow() {
    fAnimating = !fAnimating;
    this->postAnimatingEvent();
    this->updateTitle();
}

void SampleWindow::toggleRendering() {
1777
    this->setDeviceType(cycle_devicetype(fDeviceType));
1778 1779 1780 1781
    this->updateTitle();
    this->inval(NULL);
}

1782 1783 1784 1785 1786 1787
void SampleWindow::toggleFPS() {
    fMeasureFPS = !fMeasureFPS;
    this->updateTitle();
    this->inval(NULL);
}

1788 1789 1790 1791 1792 1793 1794 1795 1796
void SampleWindow::toggleDistanceFieldFonts() {
    SkSurfaceProps props = this->getSurfaceProps();
    uint32_t flags = props.flags() ^ SkSurfaceProps::kUseDistanceFieldFonts_Flag;
    this->setSurfaceProps(SkSurfaceProps(flags, props.pixelGeometry()));

    this->updateTitle();
    this->inval(NULL);
}

reed@android.com's avatar
reed@android.com committed
1797 1798 1799
#include "SkDumpCanvas.h"

bool SampleWindow::onHandleKey(SkKey key) {
1800 1801 1802 1803 1804 1805 1806 1807 1808 1809
    {
        SkView* view = curr_view(this);
        if (view) {
            SkEvent evt(gKeyEvtName);
            evt.setFast32(key);
            if (view->doQuery(&evt)) {
                return true;
            }
        }
    }
reed@android.com's avatar
reed@android.com committed
1810 1811 1812 1813 1814 1815 1816
    switch (key) {
        case kRight_SkKey:
            if (this->nextSample()) {
                return true;
            }
            break;
        case kLeft_SkKey:
1817 1818 1819
            if (this->previousSample()) {
                return true;
            }
reed@android.com's avatar
reed@android.com committed
1820 1821
            return true;
        case kUp_SkKey:
1822
            if (USE_ARROWS_FOR_ZOOM) {
1823
                this->changeZoomLevel(1.f / 32.f);
1824 1825 1826
            } else {
                fNClip = !fNClip;
                this->inval(NULL);
1827
                this->updateTitle();
1828
            }
reed@android.com's avatar
reed@android.com committed
1829 1830
            return true;
        case kDown_SkKey:
1831
            if (USE_ARROWS_FOR_ZOOM) {
1832
                this->changeZoomLevel(-1.f / 32.f);
1833
            } else {
1834
                this->setColorType(cycle_colortypes(this->getBitmap().colorType()));
1835
                this->updateTitle();
1836
            }
reed@android.com's avatar
reed@android.com committed
1837
            return true;
1838 1839 1840 1841
        case kOK_SkKey: {
            SkString title;
            if (curr_title(this, &title)) {
                writeTitleToPrefs(title.c_str());
reed@android.com's avatar
reed@android.com committed
1842 1843
            }
            return true;
1844
        }
1845
        case kBack_SkKey:
1846
            this->showOverview();
1847
            return true;
reed@android.com's avatar
reed@android.com committed
1848 1849 1850 1851 1852 1853
        default:
            break;
    }
    return this->INHERITED::onHandleKey(key);
}

reed@google.com's avatar
reed@google.com committed
1854 1855 1856 1857
///////////////////////////////////////////////////////////////////////////////

static const char gGestureClickType[] = "GestureClickType";

1858
bool SampleWindow::onDispatchClick(int x, int y, Click::State state,
1859
        void* owner, unsigned modi) {
Scroggo's avatar
Scroggo committed
1860 1861 1862
    if (Click::kMoved_State == state) {
        updatePointer(x, y);
    }
1863 1864
    int w = SkScalarRoundToInt(this->width());
    int h = SkScalarRoundToInt(this->height());
1865 1866 1867 1868

    // check for the resize-box
    if (w - x < 16 && h - y < 16) {
        return false;   // let the OS handle the click
1869
    }
1870 1871 1872 1873
    else if (fMagnify) {
        //it's only necessary to update the drawing if there's a click
        this->inval(NULL);
        return false; //prevent dragging while magnify is enabled
1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886
    } else {
        // capture control+option, and trigger debugger
        if ((modi & kControl_SkModifierKey) && (modi & kOption_SkModifierKey)) {
            if (Click::kDown_State == state) {
                SkEvent evt("debug-hit-test");
                evt.setS32("debug-hit-test-x", x);
                evt.setS32("debug-hit-test-y", y);
                curr_view(this)->doEvent(evt);
            }
            return true;
        } else {
            return this->INHERITED::onDispatchClick(x, y, state, owner, modi);
        }
1887 1888 1889
    }
}

reed@google.com's avatar
reed@google.com committed
1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900
class GestureClick : public SkView::Click {
public:
    GestureClick(SkView* target) : SkView::Click(target) {
        this->setType(gGestureClickType);
    }

    static bool IsGesture(Click* click) {
        return click->isType(gGestureClickType);
    }
};

1901 1902
SkView::Click* SampleWindow::onFindClickHandler(SkScalar x, SkScalar y,
                                                unsigned modi) {
reed@google.com's avatar
reed@google.com committed
1903 1904 1905 1906 1907
    return new GestureClick(this);
}

bool SampleWindow::onClick(Click* click) {
    if (GestureClick::IsGesture(click)) {
1908 1909
        float x = static_cast<float>(click->fICurr.fX);
        float y = static_cast<float>(click->fICurr.fY);
1910

reed@google.com's avatar
reed@google.com committed
1911 1912
        switch (click->fState) {
            case SkView::Click::kDown_State:
1913
                fGesture.touchBegin(click->fOwner, x, y);
reed@google.com's avatar
reed@google.com committed
1914 1915
                break;
            case SkView::Click::kMoved_State:
1916
                fGesture.touchMoved(click->fOwner, x, y);
1917
                this->updateMatrix();
reed@google.com's avatar
reed@google.com committed
1918 1919
                break;
            case SkView::Click::kUp_State:
1920
                fGesture.touchEnd(click->fOwner);
1921
                this->updateMatrix();
reed@google.com's avatar
reed@google.com committed
1922 1923 1924 1925 1926 1927 1928 1929 1930
                break;
        }
        return true;
    }
    return false;
}

///////////////////////////////////////////////////////////////////////////////

reed@android.com's avatar
reed@android.com committed
1931 1932 1933 1934 1935 1936
void SampleWindow::loadView(SkView* view) {
    SkView::F2BIter iter(this);
    SkView* prev = iter.next();
    if (prev) {
        prev->detachFromParent();
    }
1937

reed@android.com's avatar
reed@android.com committed
1938
    view->setVisibleP(true);
1939
    view->setClipToBounds(false);
reed@android.com's avatar
reed@android.com committed
1940 1941 1942
    this->attachChildToFront(view)->unref();
    view->setSize(this->width(), this->height());

1943
    //repopulate the slide menu when a view is loaded
1944
    fSlideMenu->reset();
1945

1946
    (void)SampleView::SetUsePipe(view, fPipeState);
1947 1948 1949 1950 1951
    if (SampleView::IsSampleView(view)) {
        SampleView* sampleView = (SampleView*)view;
        sampleView->requestMenu(fSlideMenu);
        sampleView->onTileSizeChanged(this->tileSize());
    }
1952
    this->onUpdateMenu(fSlideMenu);
reed@android.com's avatar
reed@android.com committed
1953 1954 1955
    this->updateTitle();
}

1956
static const char* gDeviceTypePrefix[] = {
reed@android.com's avatar
reed@android.com committed
1957 1958
    "raster: ",
    "picture: ",
1959
#if SK_SUPPORT_GPU
1960
    "opengl: ",
1961 1962
#if SK_ANGLE
    "angle: ",
1963
#endif // SK_ANGLE
1964
    "null-gl: "
1965
#endif // SK_SUPPORT_GPU
reed@android.com's avatar
reed@android.com committed
1966
};
1967 1968
SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gDeviceTypePrefix) == SampleWindow::kDeviceTypeCnt,
                  array_size_mismatch);
reed@android.com's avatar
reed@android.com committed
1969

1970
static const char* trystate_str(SkOSMenu::TriState state,
reed@google.com's avatar
reed@google.com committed
1971
                                const char trueStr[], const char falseStr[]) {
1972
    if (SkOSMenu::kOnState == state) {
reed@google.com's avatar
reed@google.com committed
1973
        return trueStr;
1974
    } else if (SkOSMenu::kOffState == state) {
reed@google.com's avatar
reed@google.com committed
1975 1976 1977 1978 1979
        return falseStr;
    }
    return NULL;
}

1980 1981 1982
bool SampleWindow::getRawTitle(SkString* title) {
    return curr_title(this, title);
}
reed@android.com's avatar
reed@android.com committed
1983

1984
void SampleWindow::updateTitle() {
1985
    SkString title;
1986
    if (!this->getRawTitle(&title)) {
reed@android.com's avatar
reed@android.com committed
1987 1988
        title.set("<unknown>");
    }
1989

1990
    title.prepend(gDeviceTypePrefix[fDeviceType]);
reed@android.com's avatar
reed@android.com committed
1991 1992

    title.prepend(" ");
1993
    title.prepend(sk_tool_utils::colortype_name(this->getBitmap().colorType()));
1994

1995 1996 1997
    if (fTilingMode != kNo_Tiling) {
        title.prependf("<T: %s> ", gTilingInfo[fTilingMode].label);
    }
reed@android.com's avatar
reed@android.com committed
1998 1999 2000
    if (fAnimating) {
        title.prepend("<A> ");
    }
2001 2002 2003
    if (fRotate) {
        title.prepend("<R> ");
    }
2004 2005 2006
    if (fNClip) {
        title.prepend("<C> ");
    }
2007 2008 2009
    if (fPerspAnim) {
        title.prepend("<K> ");
    }
2010 2011 2012
    if (this->getSurfaceProps().flags() & SkSurfaceProps::kUseDistanceFieldFonts_Flag) {
        title.prepend("<DFF> ");
    }
reed@google.com's avatar
reed@google.com committed
2013 2014 2015

    title.prepend(trystate_str(fLCDState, "LCD ", "lcd "));
    title.prepend(trystate_str(fAAState, "AA ", "aa "));
2016
    title.prepend(gFilterLevelStates[fFilterLevelIndex].fLabel);
2017
    title.prepend(trystate_str(fSubpixelState, "S ", "s "));
reed@google.com's avatar
reed@google.com committed
2018 2019
    title.prepend(fFlipAxis & kFlipAxis_X ? "X " : NULL);
    title.prepend(fFlipAxis & kFlipAxis_Y ? "Y " : NULL);
2020
    title.prepend(gHintingStates[fHintingState].label);
2021 2022

    if (fZoomLevel) {
2023
        title.prependf("{%.2f} ", SkScalarToFloat(fZoomLevel));
2024
    }
2025

2026
    if (fMeasureFPS) {
2027
        title.appendf(" %8.3f ms", fMeasureFPS_Time / (float)FPS_REPEAT_COUNT);
2028
    }
2029 2030

    SkView* view = curr_view(this);
2031
    if (SampleView::IsSampleView(view)) {
2032 2033 2034 2035 2036 2037 2038
        switch (fPipeState) {
            case SkOSMenu::kOnState:
                title.prepend("<Pipe> ");
                break;
            case SkOSMenu::kMixedState:
                title.prepend("<Tiled Pipe> ");
                break;
2039

2040 2041 2042
            default:
                break;
        }
2043 2044
        title.prepend("! ");
    }
2045

2046
#if SK_SUPPORT_GPU
2047
    if (IsGpuDeviceType(fDeviceType) &&
bsalomon's avatar
bsalomon committed
2048
        fDevManager &&
2049
        fDevManager->getGrRenderTarget() &&
2050 2051 2052 2053
        fDevManager->getGrRenderTarget()->numSamples() > 0) {
        title.appendf(" [MSAA: %d]",
                       fDevManager->getGrRenderTarget()->numSamples());
    }
2054
#endif
2055

reed@android.com's avatar
reed@android.com committed
2056 2057 2058 2059 2060
    this->setTitle(title.c_str());
}

void SampleWindow::onSizeChange() {
    this->INHERITED::onSizeChange();
2061

reed@android.com's avatar
reed@android.com committed
2062 2063 2064
    SkView::F2BIter iter(this);
    SkView* view = iter.next();
    view->setSize(this->width(), this->height());
2065

reed@android.com's avatar
reed@android.com committed
2066 2067 2068 2069
    // rebuild our clippath
    {
        const SkScalar W = this->width();
        const SkScalar H = this->height();
2070

reed@android.com's avatar
reed@android.com committed
2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086
        fClipPath.reset();
#if 0
        for (SkScalar y = SK_Scalar1; y < H; y += SkIntToScalar(32)) {
            SkRect r;
            r.set(SK_Scalar1, y, SkIntToScalar(30), y + SkIntToScalar(30));
            for (; r.fLeft < W; r.offset(SkIntToScalar(32), 0))
                fClipPath.addRect(r);
        }
#else
        SkRect r;
        r.set(0, 0, W, H);
        fClipPath.addRect(r, SkPath::kCCW_Direction);
        r.set(W/4, H/4, W*3/4, H*3/4);
        fClipPath.addRect(r, SkPath::kCW_Direction);
#endif
    }
2087

2088 2089 2090
    fZoomCenterX = SkScalarHalf(this->width());
    fZoomCenterY = SkScalarHalf(this->height());

2091
#ifdef SK_BUILD_FOR_ANDROID
Scroggo's avatar
Scroggo committed
2092 2093
    // FIXME: The first draw after a size change does not work on Android, so
    // we post an invalidate.
Scroggo's avatar
Scroggo committed
2094
    this->postInvalDelay();
Scroggo's avatar
Scroggo committed
2095
#endif
reed@android.com's avatar
reed@android.com committed
2096
    this->updateTitle();    // to refresh our config
2097
    fDevManager->windowSizeChanged(this);
2098 2099 2100 2101

    if (fTilingMode != kNo_Tiling && SampleView::IsSampleView(view)) {
        ((SampleView*)view)->onTileSizeChanged(this->tileSize());
    }
reed@android.com's avatar
reed@android.com committed
2102 2103 2104 2105
}

///////////////////////////////////////////////////////////////////////////////

2106
static const char is_sample_view_tag[] = "sample-is-sample-view";
2107
static const char repeat_count_tag[] = "sample-set-repeat-count";
2108
static const char set_use_pipe_tag[] = "sample-set-use-pipe";
2109

2110 2111 2112 2113 2114
bool SampleView::IsSampleView(SkView* view) {
    SkEvent evt(is_sample_view_tag);
    return view->doQuery(&evt);
}

2115
bool SampleView::SetRepeatDraw(SkView* view, int count) {
2116 2117
    SkEvent evt(repeat_count_tag);
    evt.setFast32(count);
2118
    return view->doEvent(evt);
2119 2120
}

2121 2122 2123
bool SampleView::SetUsePipe(SkView* view, SkOSMenu::TriState state) {
    SkEvent evt;
    evt.setS32(set_use_pipe_tag, state);
2124 2125 2126
    return view->doEvent(evt);
}

2127 2128 2129 2130 2131
bool SampleView::onEvent(const SkEvent& evt) {
    if (evt.isType(repeat_count_tag)) {
        fRepeatCount = evt.getFast32();
        return true;
    }
2132

2133 2134 2135
    int32_t pipeHolder;
    if (evt.findS32(set_use_pipe_tag, &pipeHolder)) {
        fPipeState = static_cast<SkOSMenu::TriState>(pipeHolder);
2136 2137
        return true;
    }
2138

2139 2140 2141 2142
    return this->INHERITED::onEvent(evt);
}

bool SampleView::onQuery(SkEvent* evt) {
2143 2144 2145
    if (evt->isType(is_sample_view_tag)) {
        return true;
    }
2146 2147 2148
    return this->INHERITED::onQuery(evt);
}

reed@google.com's avatar
reed@google.com committed
2149 2150 2151 2152 2153

class SimplePC : public SkGPipeController {
public:
    SimplePC(SkCanvas* target);
    ~SimplePC();
2154

reed@google.com's avatar
reed@google.com committed
2155 2156 2157 2158
    virtual void* requestBlock(size_t minRequest, size_t* actual);
    virtual void notifyWritten(size_t bytes);

private:
reed@google.com's avatar
reed@google.com committed
2159 2160 2161 2162 2163
    SkGPipeReader   fReader;
    void*           fBlock;
    size_t          fBlockSize;
    size_t          fBytesWritten;
    int             fAtomsWritten;
reed@google.com's avatar
reed@google.com committed
2164 2165 2166 2167 2168 2169 2170 2171 2172 2173
    SkGPipeReader::Status   fStatus;

    size_t        fTotalWritten;
};

SimplePC::SimplePC(SkCanvas* target) : fReader(target) {
    fBlock = NULL;
    fBlockSize = fBytesWritten = 0;
    fStatus = SkGPipeReader::kDone_Status;
    fTotalWritten = 0;
reed@google.com's avatar
reed@google.com committed
2174
    fAtomsWritten = 0;
2175
    fReader.setBitmapDecoder(&SkImageDecoder::DecodeMemory);
reed@google.com's avatar
reed@google.com committed
2176 2177 2178 2179
}

SimplePC::~SimplePC() {
//    SkASSERT(SkGPipeReader::kDone_Status == fStatus);
2180
    if (fTotalWritten) {
2181 2182
        SkDebugf("--- %d bytes %d atoms, status %d\n", fTotalWritten,
                 fAtomsWritten, fStatus);
2183
#ifdef  PIPE_FILE
2184 2185 2186 2187 2188
        //File is open in append mode
        FILE* f = fopen(FILE_PATH, "ab");
        SkASSERT(f != NULL);
        fwrite((const char*)fBlock + fBytesWritten, 1, bytes, f);
        fclose(f);
2189 2190
#endif
#ifdef PIPE_NET
2191 2192 2193 2194
        if (fAtomsWritten > 1 && fTotalWritten > 4) { //ignore done
            gServer.acceptConnections();
            gServer.writePacket(fBlock, fTotalWritten);
        }
2195
#endif
2196
    }
2197
    sk_free(fBlock);
reed@google.com's avatar
reed@google.com committed
2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215
}

void* SimplePC::requestBlock(size_t minRequest, size_t* actual) {
    sk_free(fBlock);

    fBlockSize = minRequest * 4;
    fBlock = sk_malloc_throw(fBlockSize);
    fBytesWritten = 0;
    *actual = fBlockSize;
    return fBlock;
}

void SimplePC::notifyWritten(size_t bytes) {
    SkASSERT(fBytesWritten + bytes <= fBlockSize);
    fStatus = fReader.playback((const char*)fBlock + fBytesWritten, bytes);
    SkASSERT(SkGPipeReader::kError_Status != fStatus);
    fBytesWritten += bytes;
    fTotalWritten += bytes;
2216

reed@google.com's avatar
reed@google.com committed
2217
    fAtomsWritten += 1;
reed@google.com's avatar
reed@google.com committed
2218 2219
}

2220
void SampleView::draw(SkCanvas* canvas) {
2221 2222 2223
    if (SkOSMenu::kOffState == fPipeState) {
        this->INHERITED::draw(canvas);
    } else {
2224 2225
        SkGPipeWriter writer;
        SimplePC controller(canvas);
reed's avatar
reed committed
2226 2227
        SkBitmap bitmap = capture_bitmap(canvas);
        TiledPipeController tc(bitmap, &SkImageDecoder::DecodeMemory, &canvas->getTotalMatrix());
2228 2229 2230 2231 2232 2233
        SkGPipeController* pc;
        if (SkOSMenu::kMixedState == fPipeState) {
            pc = &tc;
        } else {
            pc = &controller;
        }
2234
        uint32_t flags = SkGPipeWriter::kCrossProcess_Flag;
2235

2236
        canvas = writer.startRecording(pc, flags);
2237 2238
        //Must draw before controller goes out of scope and sends data
        this->INHERITED::draw(canvas);
2239 2240 2241
        //explicitly end recording to ensure writer is flushed before the memory
        //is freed in the deconstructor of the controller
        writer.endRecording();
2242
    }
2243
}
2244

2245
void SampleView::onDraw(SkCanvas* canvas) {
reed@google.com's avatar
reed@google.com committed
2246 2247
    this->onDrawBackground(canvas);

2248
    for (int i = 0; i < fRepeatCount; i++) {
2249 2250
        SkAutoCanvasRestore acr(canvas, true);
        this->onDrawContent(canvas);
2251 2252 2253 2254
    }
}

void SampleView::onDrawBackground(SkCanvas* canvas) {
2255
    canvas->drawColor(fBGColor);
2256 2257 2258 2259
}

///////////////////////////////////////////////////////////////////////////////

2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274
template <typename T> void SkTBSort(T array[], int count) {
    for (int i = 1; i < count - 1; i++) {
        bool didSwap = false;
        for (int j = count - 1; j > i; --j) {
            if (array[j] < array[j-1]) {
                T tmp(array[j-1]);
                array[j-1] = array[j];
                array[j] = tmp;
                didSwap = true;
            }
        }
        if (!didSwap) {
            break;
        }
    }
2275

2276 2277 2278 2279 2280 2281 2282
    for (int k = 0; k < count - 1; k++) {
        SkASSERT(!(array[k+1] < array[k]));
    }
}

#include "SkRandom.h"

2283
static void rand_rect(SkIRect* rect, SkRandom& rand) {
2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345
    int bits = 8;
    int shift = 32 - bits;
    rect->set(rand.nextU() >> shift, rand.nextU() >> shift,
              rand.nextU() >> shift, rand.nextU() >> shift);
    rect->sort();
}

static void dumpRect(const SkIRect& r) {
    SkDebugf(" { %d, %d, %d, %d },\n",
             r.fLeft, r.fTop,
             r.fRight, r.fBottom);
}

static void test_rects(const SkIRect rect[], int count) {
    SkRegion rgn0, rgn1;

    for (int i = 0; i < count; i++) {
        rgn0.op(rect[i], SkRegion::kUnion_Op);
     //   dumpRect(rect[i]);
    }
    rgn1.setRects(rect, count);

    if (rgn0 != rgn1) {
        SkDebugf("\n");
        for (int i = 0; i < count; i++) {
            dumpRect(rect[i]);
        }
        SkDebugf("\n");
    }
}

static void test() {
    size_t i;

    const SkIRect r0[] = {
        { 0, 0, 1, 1 },
        { 2, 2, 3, 3 },
    };
    const SkIRect r1[] = {
        { 0, 0, 1, 3 },
        { 1, 1, 2, 2 },
        { 2, 0, 3, 3 },
    };
    const SkIRect r2[] = {
        { 0, 0, 1, 2 },
        { 2, 1, 3, 3 },
        { 4, 0, 5, 1 },
        { 6, 0, 7, 4 },
    };

    static const struct {
        const SkIRect* fRects;
        int            fCount;
    } gRecs[] = {
        { r0, SK_ARRAY_COUNT(r0) },
        { r1, SK_ARRAY_COUNT(r1) },
        { r2, SK_ARRAY_COUNT(r2) },
    };

    for (i = 0; i < SK_ARRAY_COUNT(gRecs); i++) {
        test_rects(gRecs[i].fRects, gRecs[i].fCount);
    }
2346

2347
    SkRandom rand;
2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359
    for (i = 0; i < 10000; i++) {
        SkRegion rgn0, rgn1;

        const int N = 8;
        SkIRect rect[N];
        for (int j = 0; j < N; j++) {
            rand_rect(&rect[j], rand);
        }
        test_rects(rect, N);
    }
}

2360 2361
// FIXME: this should be in a header
SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv);
2362
SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv) {
2363 2364 2365
    if (false) { // avoid bit rot, suppress warning
        test();
    }
2366
    return new SampleWindow(hwnd, argc, argv, NULL);
reed@android.com's avatar
reed@android.com committed
2367 2368
}

2369 2370
// FIXME: this should be in a header
void get_preferred_size(int* x, int* y, int* width, int* height);
reed@android.com's avatar
reed@android.com committed
2371 2372 2373 2374 2375 2376 2377
void get_preferred_size(int* x, int* y, int* width, int* height) {
    *x = 10;
    *y = 50;
    *width = 640;
    *height = 480;
}

2378
#ifdef SK_BUILD_FOR_IOS
2379
#include "SkApplication.h"
caryclark's avatar
caryclark committed
2380
IOS_launch_type set_cmd_line_args(int , char *[], const char* resourceDir) {
caryclark's avatar
caryclark committed
2381
    SetResourcePath(resourceDir);
caryclark's avatar
caryclark committed
2382
    return kApplication__iOSLaunchType;
2383 2384 2385
}
#endif

reed@android.com's avatar
reed@android.com committed
2386 2387
void application_init() {
//    setenv("ANDROID_ROOT", "../../../data", 0);
reed@android.com's avatar
reed@android.com committed
2388
#ifdef SK_BUILD_FOR_MAC
reed@android.com's avatar
reed@android.com committed
2389
    setenv("ANDROID_ROOT", "/android/device/data", 0);
reed@android.com's avatar
reed@android.com committed
2390
#endif
2391 2392
    SkGraphics::Init();
    SkEvent::Init();
reed@android.com's avatar
reed@android.com committed
2393 2394 2395
}

void application_term() {
2396 2397
    SkEvent::Term();
    SkGraphics::Term();
reed@android.com's avatar
reed@android.com committed
2398
}