blob: e45303a49e09d67829faf1c440b68b447b979d05 [file] [log] [blame]
#include "SampleApp.h"
#include "SkData.h"
#include "SkCanvas.h"
#include "SkDevice.h"
#include "SkGpuDevice.h"
#include "SkGraphics.h"
#include "SkImageEncoder.h"
#include "SkPaint.h"
#include "SkPicture.h"
#include "SkStream.h"
#include "SkTime.h"
#include "SkWindow.h"
#include "SampleCode.h"
#include "GrContext.h"
#include "SkTypeface.h"
#include "GrGLInterface.h"
#include "SkPDFDevice.h"
#include "SkPDFDocument.h"
#include "SkStream.h"
#define TEST_GPIPEx
#ifdef TEST_GPIPE
#define PIPE_FILE
#define FILE_PATH "/path/to/drawing.data"
#endif
#define USE_ARROWS_FOR_ZOOM true
//#define DEFAULT_TO_GPU
extern SkView* create_overview(int, const SkViewFactory[]);
#define ANIMATING_EVENTTYPE "nextSample"
#define ANIMATING_DELAY 750
#ifdef SK_DEBUG
#define FPS_REPEAT_MULTIPLIER 1
#else
#define FPS_REPEAT_MULTIPLIER 10
#endif
#define FPS_REPEAT_COUNT (10 * FPS_REPEAT_MULTIPLIER)
static SampleWindow* gSampleWindow;
///////////////
class SampleWindow::DefaultDeviceManager : public SampleWindow::DeviceManager {
public:
DefaultDeviceManager() {
fGrRenderTarget = NULL;
fGrContext = NULL;
}
virtual ~DefaultDeviceManager() {
SkSafeUnref(fGrRenderTarget);
SkSafeUnref(fGrContext);
}
virtual void init(SampleWindow* win) {
win->attachGL();
if (NULL == fGrContext) {
fGrContext = GrContext::Create(kOpenGL_Shaders_GrEngine, NULL);
}
if (NULL == fGrContext) {
SkDebugf("Failed to setup 3D");
win->detachGL();
}
}
virtual bool supportsDeviceType(SampleWindow::DeviceType dType) {
switch (dType) {
case kRaster_DeviceType:
case kPicture_DeviceType: // fallthru
return true;
case kGPU_DeviceType:
return NULL != fGrContext && NULL != fGrRenderTarget;
default:
return false;
}
}
virtual bool prepareCanvas(SampleWindow::DeviceType dType,
SkCanvas* canvas,
SampleWindow* win) {
if (kGPU_DeviceType == dType) {
if (fGrContext) {
canvas->setDevice(new SkGpuDevice(fGrContext,
fGrRenderTarget))->unref();
} else {
return false;
}
}
return true;
}
virtual void publishCanvas(SampleWindow::DeviceType dType,
SkCanvas* canvas,
SampleWindow* win) {
if (fGrContext) {
// in case we have queued drawing calls
fGrContext->flush();
if (dType != kGPU_DeviceType) {
// need to send the raster bits to the (gpu) window
fGrContext->setRenderTarget(fGrRenderTarget);
const SkBitmap& bm = win->getBitmap();
fGrContext->writePixels(0, 0, bm.width(), bm.height(),
kRGBA_8888_GrPixelConfig, bm.getPixels(),
bm.rowBytes());
}
}
win->presentGL();
}
virtual void windowSizeChanged(SampleWindow* win) {
if (fGrContext) {
win->attachGL();
GrPlatformSurfaceDesc desc;
desc.reset();
desc.fSurfaceType = kRenderTarget_GrPlatformSurfaceType;
desc.fWidth = SkScalarRound(win->width());
desc.fHeight = SkScalarRound(win->height());
desc.fConfig = kRGBA_8888_GrPixelConfig;
desc.fStencilBits = 8;
GrGLint buffer;
GR_GL_GetIntegerv(GR_GL_FRAMEBUFFER_BINDING, &buffer);
desc.fPlatformRenderTarget = buffer;
SkSafeUnref(fGrRenderTarget);
fGrRenderTarget = static_cast<GrRenderTarget*>(
fGrContext->createPlatformSurface(desc));
}
}
virtual GrContext* getGrContext() {
return fGrContext;
}
private:
GrContext* fGrContext;
GrRenderTarget* fGrRenderTarget;
};
///////////////
static const char view_inval_msg[] = "view-inval-msg";
void SampleWindow::postInvalDelay() {
SkEvent* evt = new SkEvent(view_inval_msg);
evt->post(this->getSinkID(), 1);
}
static bool isInvalEvent(const SkEvent& evt) {
return evt.isType(view_inval_msg);
}
//////////////////
SkViewRegister* SkViewRegister::gHead;
SkViewRegister::SkViewRegister(SkViewFactory fact) : fFact(fact) {
static bool gOnce;
if (!gOnce) {
gHead = NULL;
gOnce = true;
}
fChain = gHead;
gHead = this;
}
#if 0
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFURLAccess.h>
static void testpdf() {
CFStringRef path = CFStringCreateWithCString(NULL, "/test.pdf",
kCFStringEncodingUTF8);
CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path,
kCFURLPOSIXPathStyle,
false);
CFRelease(path);
CGRect box = CGRectMake(0, 0, 8*72, 10*72);
CGContextRef cg = CGPDFContextCreateWithURL(url, &box, NULL);
CFRelease(url);
CGContextBeginPage(cg, &box);
CGRect r = CGRectMake(10, 10, 40 + 0.5, 50 + 0.5);
CGContextFillEllipseInRect(cg, r);
CGContextEndPage(cg);
CGContextRelease(cg);
if (false) {
SkBitmap bm;
bm.setConfig(SkBitmap::kA8_Config, 64, 64);
bm.allocPixels();
bm.eraseColor(0);
SkCanvas canvas(bm);
}
}
#endif
//////////////////////////////////////////////////////////////////////////////
enum FlipAxisEnum {
kFlipAxis_X = (1 << 0),
kFlipAxis_Y = (1 << 1)
};
static SkTriState cycle_tristate(SkTriState state) {
static const SkTriState gCycle[] = {
/* kFalse_SkTriState -> */ kUnknown_SkTriState,
/* kTrue_SkTriState -> */ kFalse_SkTriState,
/* kUnknown_SkTriState -> */ kTrue_SkTriState,
};
return gCycle[state];
}
#include "SkDrawFilter.h"
class FlagsDrawFilter : public SkDrawFilter {
public:
FlagsDrawFilter(SkTriState lcd, SkTriState aa, SkTriState filter,
SkTriState hinting) :
fLCDState(lcd), fAAState(aa), fFilterState(filter), fHintingState(hinting) {}
virtual void filter(SkPaint* paint, Type t) {
if (kText_Type == t && kUnknown_SkTriState != fLCDState) {
paint->setLCDRenderText(kTrue_SkTriState == fLCDState);
}
if (kUnknown_SkTriState != fAAState) {
paint->setAntiAlias(kTrue_SkTriState == fAAState);
}
if (kUnknown_SkTriState != fFilterState) {
paint->setFilterBitmap(kTrue_SkTriState == fFilterState);
}
if (kUnknown_SkTriState != fHintingState) {
paint->setHinting(kTrue_SkTriState == fHintingState ?
SkPaint::kNormal_Hinting :
SkPaint::kSlight_Hinting);
}
}
private:
SkTriState fLCDState;
SkTriState fAAState;
SkTriState fFilterState;
SkTriState fHintingState;
};
//////////////////////////////////////////////////////////////////////////////
#define MAX_ZOOM_LEVEL 8
#define MIN_ZOOM_LEVEL -8
static const char gCharEvtName[] = "SampleCode_Char_Event";
static const char gKeyEvtName[] = "SampleCode_Key_Event";
static const char gTitleEvtName[] = "SampleCode_Title_Event";
static const char gPrefSizeEvtName[] = "SampleCode_PrefSize_Event";
static const char gFastTextEvtName[] = "SampleCode_FastText_Event";
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;
}
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);
}
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);
}
bool SampleCode::FastTextQ(const SkEvent& evt) {
return evt.isType(gFastTextEvtName, sizeof(gFastTextEvtName) - 1);
}
///////////////////////////////////////////////////////////////////////////////
static SkMSec gAnimTime;
static SkMSec gAnimTimePrev;
SkMSec SampleCode::GetAnimTime() { return gAnimTime; }
SkMSec SampleCode::GetAnimTimeDelta() { return gAnimTime - gAnimTimePrev; }
SkScalar SampleCode::GetAnimSecondsDelta() {
return SkDoubleToScalar(GetAnimTimeDelta() / 1000.0);
}
SkScalar SampleCode::GetAnimScalar(SkScalar speed, SkScalar period) {
// since gAnimTime can be up to 32 bits, we can't convert it to a float
// or we'll lose the low bits. Hence we use doubles for the intermediate
// calculations
double seconds = (double)gAnimTime / 1000.0;
double value = SkScalarToDouble(speed) * seconds;
if (period) {
value = ::fmod(value, SkScalarToDouble(period));
}
return SkDoubleToScalar(value);
}
GrContext* SampleCode::GetGr() {
return gSampleWindow ? gSampleWindow->getGrContext() : NULL;
}
//////////////////////////////////////////////////////////////////////////////
static SkView* curr_view(SkWindow* wind) {
SkView::F2BIter iter(wind);
return iter.next();
}
void SampleWindow::setZoomCenter(float x, float y)
{
fZoomCenterX = SkFloatToScalar(x);
fZoomCenterY = SkFloatToScalar(y);
}
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::toggleZoomer()
{
fShowZoomer = !fShowZoomer;
this->inval(NULL);
}
void SampleWindow::updatePointer(int x, int y)
{
fMouseX = x;
fMouseY = y;
if (fShowZoomer) {
this->inval(NULL);
}
}
static inline SampleWindow::DeviceType cycle_devicetype(SampleWindow::DeviceType ct) {
static const SampleWindow::DeviceType gCT[] = {
SampleWindow::kPicture_DeviceType,
SampleWindow::kGPU_DeviceType,
SampleWindow::kRaster_DeviceType
};
return gCT[ct];
}
SampleWindow::SampleWindow(void* hwnd, int argc, char** argv, DeviceManager* devManager) : INHERITED(hwnd) {
gSampleWindow = this;
#ifdef PIPE_FILE
//Clear existing file or create file if it doesn't exist
FILE* f = fopen(FILE_PATH, "wb");
fclose(f);
#endif
fPicture = NULL;
#ifdef DEFAULT_TO_GPU
fDeviceType = kGPU_DeviceType;
#else
fDeviceType = kRaster_DeviceType;
#endif
fUseClip = false;
fNClip = false;
fRepeatDrawing = false;
fAnimating = false;
fRotate = false;
fScale = false;
fRequestGrabImage = false;
fUsePipe = false;
fMeasureFPS = false;
fLCDState = kUnknown_SkTriState;
fAAState = kUnknown_SkTriState;
fFilterState = kUnknown_SkTriState;
fHintingState = kUnknown_SkTriState;
fFlipAxis = 0;
fScrollTestX = fScrollTestY = 0;
fMouseX = fMouseY = 0;
fFatBitsScale = 8;
fTypeface = SkTypeface::CreateFromTypeface(NULL, SkTypeface::kBold);
fShowZoomer = false;
fZoomLevel = 0;
fZoomScale = SK_Scalar1;
fSaveToPdf = false;
fPdfCanvas = NULL;
// this->setConfig(SkBitmap::kRGB_565_Config);
this->setConfig(SkBitmap::kARGB_8888_Config);
this->setVisibleP(true);
this->setClipToBounds(false);
{
const SkViewRegister* reg = SkViewRegister::Head();
while (reg) {
*fSamples.append() = reg->factory();
reg = reg->next();
}
}
fCurrIndex = 0;
if (argc > 1) {
int i, count = fSamples.count();
for (i = 0; i < count; i++) {
SkString title = getSampleTitle(i);
if (title.equals(argv[1])) {
fCurrIndex = i;
break;
}
}
if (i == count) {
fprintf(stderr, "Unknown sample \"%s\"\n", argv[1]);
}
}
this->loadView(fSamples[fCurrIndex]());
fPDFData = NULL;
if (NULL == devManager) {
fDevManager = new DefaultDeviceManager();
} else {
devManager->ref();
fDevManager = devManager;
}
fDevManager->init(this);
// If another constructor set our dimensions, ensure that our
// onSizeChange gets called.
if (this->height() && this->width()) {
this->onSizeChange();
}
}
SampleWindow::~SampleWindow() {
delete fPicture;
delete fPdfCanvas;
fTypeface->unref();
SkSafeUnref(fDevManager);
}
static SkBitmap capture_bitmap(SkCanvas* canvas) {
SkBitmap bm;
const SkBitmap& src = canvas->getDevice()->accessBitmap(false);
src.copyTo(&bm, src.config());
return bm;
}
static bool bitmap_diff(SkCanvas* canvas, const SkBitmap& orig,
SkBitmap* diff) {
const SkBitmap& src = canvas->getDevice()->accessBitmap(false);
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;
}
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);
}
#define XCLIP_N 8
#define YCLIP_N 8
void SampleWindow::draw(SkCanvas* canvas) {
if (!fDevManager->prepareCanvas(fDeviceType, canvas, this)) {
return;
}
// update the animation time
gAnimTimePrev = gAnimTime;
gAnimTime = SkTime::GetMSecs();
SkScalar cx = fZoomCenterX;
SkScalar cy = fZoomCenterY;
if (fZoomLevel) {
SkMatrix m;
SkPoint center;
m = canvas->getTotalMatrix();//.invert(&m);
m.mapXY(cx, cy, &center);
cx = center.fX;
cy = center.fY;
m.setTranslate(-cx, -cy);
m.postScale(fZoomScale, fZoomScale);
m.postTranslate(cx, cy);
canvas->concat(m);
}
if (fFlipAxis) {
SkMatrix m;
m.setTranslate(cx, cy);
if (fFlipAxis & kFlipAxis_X) {
m.preScale(-SK_Scalar1, SK_Scalar1);
}
if (fFlipAxis & kFlipAxis_Y) {
m.preScale(SK_Scalar1, -SK_Scalar1);
}
m.preTranslate(-cx, -cy);
canvas->concat(m);
}
// Apply any gesture matrix
if (true) {
const SkMatrix& localM = fGesture.localM();
if (localM.getType() & SkMatrix::kScale_Mask) {
canvas->setExternalMatrix(&localM);
}
canvas->concat(localM);
canvas->concat(fGesture.globalM());
if (fGesture.isActive()) {
this->inval(NULL);
}
}
if (fNClip) {
this->INHERITED::draw(canvas);
SkBitmap orig = capture_bitmap(canvas);
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++) {
SkRect r;
r.fTop = y * ch;
r.fBottom = (y + 1) * ch;
if (y == YCLIP_N - 1) {
r.fBottom = h;
}
for (int x = 0; x < XCLIP_N; x++) {
SkAutoCanvasRestore acr(canvas, true);
r.fLeft = x * cw;
r.fRight = (x + 1) * cw;
if (x == XCLIP_N - 1) {
r.fRight = w;
}
canvas->clipRect(r);
this->INHERITED::draw(canvas);
}
}
SkBitmap diff;
if (bitmap_diff(canvas, orig, &diff)) {
}
} else {
this->INHERITED::draw(canvas);
}
if (fShowZoomer && !fSaveToPdf) {
showZoomer(canvas);
}
// do this last
fDevManager->publishCanvas(fDeviceType, canvas, this);
}
void SampleWindow::showZoomer(SkCanvas* canvas) {
int count = canvas->save();
canvas->resetMatrix();
// Ensure the mouse position is on screen.
int width = SkScalarRound(this->width());
int height = SkScalarRound(this->height());
if (fMouseX >= width) fMouseX = width - 1;
else if (fMouseX < 0) fMouseX = 0;
if (fMouseY >= height) fMouseY = height - 1;
else if (fMouseY < 0) fMouseY = 0;
SkBitmap bitmap = capture_bitmap(canvas);
bitmap.lockPixels();
// Find the size of the zoomed in view, forced to be odd, so the examined pixel is in the middle.
int zoomedWidth = (width >> 1) | 1;
int zoomedHeight = (height >> 1) | 1;
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);
}
void SampleWindow::onDraw(SkCanvas* canvas) {
if (fRepeatDrawing) {
this->inval(NULL);
}
}
#include "SkColorPriv.h"
static void reverseRedAndBlue(const SkBitmap& bm) {
SkASSERT(bm.config() == SkBitmap::kARGB_8888_Config);
uint8_t* p = (uint8_t*)bm.getPixels();
uint8_t* stop = p + bm.getSize();
while (p < stop) {
// swap red/blue (to go from ARGB(int) to RGBA(memory) and premultiply
unsigned scale = SkAlpha255To256(p[3]);
unsigned r = p[2];
unsigned b = p[0];
p[0] = SkAlphaMul(r, scale);
p[1] = SkAlphaMul(p[1], scale);
p[2] = SkAlphaMul(b, scale);
p += 4;
}
}
void SampleWindow::saveToPdf()
{
fSaveToPdf = true;
this->inval(NULL);
}
SkCanvas* SampleWindow::beforeChildren(SkCanvas* canvas) {
if (fSaveToPdf) {
const SkBitmap& bmp = canvas->getDevice()->accessBitmap(false);
SkISize size = SkISize::Make(bmp.width(), bmp.height());
SkPDFDevice* pdfDevice = new SkPDFDevice(size, size,
canvas->getTotalMatrix());
fPdfCanvas = new SkCanvas(pdfDevice);
pdfDevice->unref();
canvas = fPdfCanvas;
} else {
switch (fDeviceType) {
case kRaster_DeviceType:
case kGPU_DeviceType:
canvas = this->INHERITED::beforeChildren(canvas);
break;
case kPicture_DeviceType:
fPicture = new SkPicture;
canvas = fPicture->beginRecording(9999, 9999);
break;
}
}
if (fUseClip) {
canvas->drawColor(0xFFFF88FF);
canvas->clipPath(fClipPath);
}
return canvas;
}
static void paint_rgn(const SkBitmap& bm, const SkIRect& r,
const SkRegion& rgn) {
SkCanvas canvas(bm);
SkRegion inval(rgn);
inval.translate(r.fLeft, r.fTop);
canvas.clipRegion(inval);
canvas.drawColor(0xFFFF8080);
}
#include "SkData.h"
void SampleWindow::afterChildren(SkCanvas* orig) {
if (fSaveToPdf) {
fSaveToPdf = false;
if (fShowZoomer) {
showZoomer(fPdfCanvas);
}
SkString name;
name.printf("%s.pdf", this->getTitle());
SkPDFDocument doc;
SkPDFDevice* device = static_cast<SkPDFDevice*>(fPdfCanvas->getDevice());
doc.appendPage(device);
#ifdef ANDROID
name.prepend("/sdcard/");
#endif
#ifdef SK_BUILD_FOR_IOS
SkDynamicMemoryWStream mstream;
doc.emitPDF(&mstream);
fPDFData = mstream.copyToData();
#endif
SkFILEWStream stream(name.c_str());
if (stream.isValid()) {
doc.emitPDF(&stream);
const char* desc = "File saved from Skia SampleApp";
this->onPDFSaved(this->getTitle(), desc, name.c_str());
}
delete fPdfCanvas;
fPdfCanvas = NULL;
// We took over the draw calls in order to create the PDF, so we need
// to redraw.
this->inval(NULL);
return;
}
if (fRequestGrabImage) {
fRequestGrabImage = false;
SkDevice* device = orig->getDevice();
SkBitmap bmp;
if (device->accessBitmap(false).copyTo(&bmp, SkBitmap::kARGB_8888_Config)) {
static int gSampleGrabCounter;
SkString name;
name.printf("sample_grab_%d", gSampleGrabCounter++);
SkImageEncoder::EncodeFile(name.c_str(), bmp,
SkImageEncoder::kPNG_Type, 100);
}
}
if (kPicture_DeviceType == fDeviceType) {
if (true) {
SkPicture* pict = new SkPicture(*fPicture);
fPicture->unref();
orig->drawPicture(*pict);
pict->unref();
} else if (true) {
SkDynamicMemoryWStream ostream;
fPicture->serialize(&ostream);
fPicture->unref();
SkAutoDataUnref data(ostream.copyToData());
SkMemoryStream istream(data.data(), data.size());
SkPicture pict(&istream);
orig->drawPicture(pict);
} else {
fPicture->draw(orig);
fPicture->unref();
}
fPicture = NULL;
}
// Do this after presentGL and other finishing, rather than in afterChild
if (fMeasureFPS && fMeasureFPS_Time) {
fMeasureFPS_Time = SkTime::GetMSecs() - fMeasureFPS_Time;
this->updateTitle();
this->postInvalDelay();
}
// if ((fScrollTestX | fScrollTestY) != 0)
if (false) {
const SkBitmap& bm = orig->getDevice()->accessBitmap(true);
int dx = fScrollTestX * 7;
int dy = fScrollTestY * 7;
SkIRect r;
SkRegion inval;
r.set(50, 50, 50+100, 50+100);
bm.scrollRect(&r, dx, dy, &inval);
paint_rgn(bm, r, inval);
}
}
void SampleWindow::beforeChild(SkView* child, SkCanvas* canvas) {
if (fScale) {
SkScalar scale = SK_Scalar1 * 7 / 10;
SkScalar cx = this->width() / 2;
SkScalar cy = this->height() / 2;
canvas->translate(cx, cy);
canvas->scale(scale, scale);
canvas->translate(-cx, -cy);
}
if (fRotate) {
SkScalar cx = this->width() / 2;
SkScalar cy = this->height() / 2;
canvas->translate(cx, cy);
canvas->rotate(SkIntToScalar(30));
canvas->translate(-cx, -cy);
}
canvas->setDrawFilter(new FlagsDrawFilter(fLCDState, fAAState,
fFilterState, fHintingState))->unref();
if (fMeasureFPS) {
fMeasureFPS_Time = 0; // 0 means the child is not aware of repeat-draw
if (SampleView::SetRepeatDraw(child, FPS_REPEAT_COUNT)) {
fMeasureFPS_Time = SkTime::GetMSecs();
}
} else {
(void)SampleView::SetRepeatDraw(child, 1);
}
(void)SampleView::SetUsePipe(child, fUsePipe);
}
void SampleWindow::afterChild(SkView* child, SkCanvas* canvas) {
canvas->setDrawFilter(NULL);
}
static SkBitmap::Config gConfigCycle[] = {
SkBitmap::kNo_Config, // none -> none
SkBitmap::kNo_Config, // a1 -> none
SkBitmap::kNo_Config, // a8 -> none
SkBitmap::kNo_Config, // index8 -> none
SkBitmap::kARGB_4444_Config, // 565 -> 4444
SkBitmap::kARGB_8888_Config, // 4444 -> 8888
SkBitmap::kRGB_565_Config // 8888 -> 565
};
static SkBitmap::Config cycle_configs(SkBitmap::Config c) {
return gConfigCycle[c];
}
void SampleWindow::changeZoomLevel(float delta) {
fZoomLevel += SkFloatToScalar(delta);
if (fZoomLevel > 0) {
fZoomLevel = SkMinScalar(fZoomLevel, MAX_ZOOM_LEVEL);
fZoomScale = fZoomLevel + SK_Scalar1;
} else if (fZoomLevel < 0) {
fZoomLevel = SkMaxScalar(fZoomLevel, MIN_ZOOM_LEVEL);
fZoomScale = SK_Scalar1 / (SK_Scalar1 - fZoomLevel);
} else {
fZoomScale = SK_Scalar1;
}
this->updateTitle();
this->inval(NULL);
}
bool SampleWindow::previousSample() {
fCurrIndex = (fCurrIndex - 1 + fSamples.count()) % fSamples.count();
this->loadView(fSamples[fCurrIndex]());
return true;
}
bool SampleWindow::nextSample() {
fCurrIndex = (fCurrIndex + 1) % fSamples.count();
this->loadView(fSamples[fCurrIndex]());
return true;
}
bool SampleWindow::goToSample(int i) {
fCurrIndex = (i) % fSamples.count();
this->loadView(fSamples[fCurrIndex]());
return true;
}
SkString SampleWindow::getSampleTitle(int i) {
SkView* view = fSamples[i]();
SkString title;
SkEvent evt(gTitleEvtName);
if (view->doQuery(&evt)) {
title.set(evt.findString(gTitleEvtName));
}
view->unref();
return title;
}
int SampleWindow::sampleCount() {
return fSamples.count();
}
void SampleWindow::postAnimatingEvent() {
if (fAnimating) {
SkEvent* evt = new SkEvent(ANIMATING_EVENTTYPE);
evt->post(this->getSinkID(), ANIMATING_DELAY);
}
}
bool SampleWindow::onEvent(const SkEvent& evt) {
if (evt.isType(ANIMATING_EVENTTYPE)) {
if (fAnimating) {
this->nextSample();
this->postAnimatingEvent();
}
return true;
}
if (evt.isType("set-curr-index")) {
fCurrIndex = evt.getFast32() % fSamples.count();
this->loadView(fSamples[fCurrIndex]());
return true;
}
if (isInvalEvent(evt)) {
this->inval(NULL);
return true;
}
return this->INHERITED::onEvent(evt);
}
bool SampleWindow::onQuery(SkEvent* query) {
if (query->isType("get-slide-count")) {
query->setFast32(fSamples.count());
return true;
}
if (query->isType("get-slide-title")) {
SkView* view = fSamples[query->getFast32()]();
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);
}
if (query->isType("ignore-window-bitmap")) {
query->setFast32(this->getGrContext() != NULL);
return true;
}
return this->INHERITED::onQuery(query);
}
static void cleanup_for_filename(SkString* name) {
char* str = name->writable_str();
for (size_t i = 0; i < name->size(); i++) {
switch (str[i]) {
case ':': str[i] = '-'; break;
case '/': str[i] = '-'; break;
case ' ': str[i] = '_'; break;
default: break;
}
}
}
bool SampleWindow::onHandleChar(SkUnichar uni) {
{
SkView* view = curr_view(this);
if (view) {
SkEvent evt(gCharEvtName);
evt.setFast32(uni);
if (view->doQuery(&evt)) {
return true;
}
}
}
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;
default:
break;
}
if (0xFF != dx && 0xFF != dy) {
if ((dx | dy) == 0) {
fScrollTestX = fScrollTestY = 0;
} else {
fScrollTestX += dx;
fScrollTestY += dy;
}
this->inval(NULL);
return true;
}
switch (uni) {
case 'a':
this->toggleSlideshow();
return true;
case 'b':
fAAState = cycle_tristate(fAAState);
this->updateTitle();
this->inval(NULL);
break;
case 'c':
fUseClip = !fUseClip;
this->inval(NULL);
this->updateTitle();
return true;
case 'd':
SkGraphics::SetFontCacheUsed(0);
return true;
case 'e':
this->saveToPdf();
break;
case 'f':
this->toggleFPS();
break;
case 'g':
fRequestGrabImage = true;
this->inval(NULL);
break;
case 'h':
fHintingState = cycle_tristate(fHintingState);
this->updateTitle();
this->inval(NULL);
break;
case 'i':
this->zoomIn();
break;
case 'l':
fLCDState = cycle_tristate(fLCDState);
this->updateTitle();
this->inval(NULL);
break;
case 'n':
fFilterState = cycle_tristate(fFilterState);
this->updateTitle();
this->inval(NULL);
break;
case 'o':
this->zoomOut();
break;
case 'p':
fUsePipe = !fUsePipe;
this->updateTitle();
this->inval(NULL);
break;
case 'r':
fRotate = !fRotate;
this->inval(NULL);
this->updateTitle();
return true;
case 's':
fScale = !fScale;
this->inval(NULL);
this->updateTitle();
return true;
case 'x':
fFlipAxis ^= kFlipAxis_X;
this->updateTitle();
this->inval(NULL);
break;
case 'y':
fFlipAxis ^= kFlipAxis_Y;
this->updateTitle();
this->inval(NULL);
break;
case 'z':
this->toggleZoomer();
break;
default:
break;
}
return this->INHERITED::onHandleChar(uni);
}
void SampleWindow::toggleFPS() {
fMeasureFPS = !fMeasureFPS;
this->inval(NULL);
this->updateTitle();
}
void SampleWindow::toggleSlideshow() {
fAnimating = !fAnimating;
this->postAnimatingEvent();
this->updateTitle();
}
void SampleWindow::toggleRendering() {
DeviceType origDevType = fDeviceType;
do {
fDeviceType = cycle_devicetype(fDeviceType);
} while (origDevType != fDeviceType &&
!fDevManager->supportsDeviceType(fDeviceType));
this->updateTitle();
this->inval(NULL);
}
#include "SkDumpCanvas.h"
bool SampleWindow::onHandleKey(SkKey key) {
{
SkView* view = curr_view(this);
if (view) {
SkEvent evt(gKeyEvtName);
evt.setFast32(key);
if (view->doQuery(&evt)) {
return true;
}
}
}
switch (key) {
case kRight_SkKey:
if (this->nextSample()) {
return true;
}
break;
case kLeft_SkKey:
toggleRendering();
return true;
case kUp_SkKey:
if (USE_ARROWS_FOR_ZOOM) {
this->changeZoomLevel(1.f);
} else {
fNClip = !fNClip;
this->inval(NULL);
this->updateTitle();
}
return true;
case kDown_SkKey:
if (USE_ARROWS_FOR_ZOOM) {
this->changeZoomLevel(-1.f);
} else {
this->setConfig(cycle_configs(this->getBitmap().config()));
this->updateTitle();
}
return true;
case kOK_SkKey:
if (false) {
SkDebugfDumper dumper;
SkDumpCanvas dc(&dumper);
this->draw(&dc);
} else {
fRepeatDrawing = !fRepeatDrawing;
if (fRepeatDrawing) {
this->inval(NULL);
}
}
return true;
case kBack_SkKey:
this->loadView(NULL);
return true;
default:
break;
}
return this->INHERITED::onHandleKey(key);
}
///////////////////////////////////////////////////////////////////////////////
static const char gGestureClickType[] = "GestureClickType";
bool SampleWindow::onDispatchClick(int x, int y, Click::State state,
void* owner) {
if (Click::kMoved_State == state) {
updatePointer(x, y);
}
int w = SkScalarRound(this->width());
int h = SkScalarRound(this->height());
// check for the resize-box
if (w - x < 16 && h - y < 16) {
return false; // let the OS handle the click
} else {
return this->INHERITED::onDispatchClick(x, y, state, owner);
}
}
class GestureClick : public SkView::Click {
public:
GestureClick(SkView* target) : SkView::Click(target) {
this->setType(gGestureClickType);
}
static bool IsGesture(Click* click) {
return click->isType(gGestureClickType);
}
};
SkView::Click* SampleWindow::onFindClickHandler(SkScalar x, SkScalar y) {
return new GestureClick(this);
}
bool SampleWindow::onClick(Click* click) {
if (GestureClick::IsGesture(click)) {
float x = SkScalarToFloat(click->fCurr.fX);
float y = SkScalarToFloat(click->fCurr.fY);
switch (click->fState) {
case SkView::Click::kDown_State:
fGesture.touchBegin(click->fOwner, x, y);
break;
case SkView::Click::kMoved_State:
fGesture.touchMoved(click->fOwner, x, y);
this->inval(NULL);
break;
case SkView::Click::kUp_State:
fGesture.touchEnd(click->fOwner);
this->inval(NULL);
break;
}
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
void SampleWindow::loadView(SkView* view) {
SkView::F2BIter iter(this);
SkView* prev = iter.next();
if (prev) {
prev->detachFromParent();
}
if (NULL == view) {
view = create_overview(fSamples.count(), fSamples.begin());
}
view->setVisibleP(true);
view->setClipToBounds(false);
this->attachChildToFront(view)->unref();
view->setSize(this->width(), this->height());
this->updateTitle();
}
static const char* gConfigNames[] = {
"unknown config",
"A1",
"A8",
"Index8",
"565",
"4444",
"8888"
};
static const char* configToString(SkBitmap::Config c) {
return gConfigNames[c];
}
static const char* gDeviceTypePrefix[] = {
"raster: ",
"picture: ",
"opengl: "
};
static const char* trystate_str(SkTriState state,
const char trueStr[], const char falseStr[]) {
if (kTrue_SkTriState == state) {
return trueStr;
} else if (kFalse_SkTriState == state) {
return falseStr;
}
return NULL;
}
void SampleWindow::updateTitle() {
SkString title;
SkView::F2BIter iter(this);
SkView* view = iter.next();
SkEvent evt(gTitleEvtName);
if (view->doQuery(&evt)) {
title.set(evt.findString(gTitleEvtName));
}
if (title.size() == 0) {
title.set("<unknown>");
}
title.prepend(gDeviceTypePrefix[fDeviceType]);
title.prepend(" ");
title.prepend(configToString(this->getBitmap().config()));
if (fAnimating) {
title.prepend("<A> ");
}
if (fScale) {
title.prepend("<S> ");
}
if (fRotate) {
title.prepend("<R> ");
}
if (fNClip) {
title.prepend("<C> ");
}
title.prepend(trystate_str(fLCDState, "LCD ", "lcd "));
title.prepend(trystate_str(fAAState, "AA ", "aa "));
title.prepend(trystate_str(fFilterState, "H ", "h "));
title.prepend(fFlipAxis & kFlipAxis_X ? "X " : NULL);
title.prepend(fFlipAxis & kFlipAxis_Y ? "Y " : NULL);
if (fZoomLevel) {
title.prependf("{%.2f} ", SkScalarToFloat(fZoomLevel));
}
if (fMeasureFPS) {
title.appendf(" %6.1f ms", fMeasureFPS_Time / (float)FPS_REPEAT_MULTIPLIER);
}
if (fUsePipe && SampleView::IsSampleView(view)) {
title.prepend("<P> ");
}
if (SampleView::IsSampleView(view)) {
title.prepend("! ");
}
this->setTitle(title.c_str());
}
void SampleWindow::onSizeChange() {
this->INHERITED::onSizeChange();
SkView::F2BIter iter(this);
SkView* view = iter.next();
view->setSize(this->width(), this->height());
// rebuild our clippath
{
const SkScalar W = this->width();
const SkScalar H = this->height();
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
}
fZoomCenterX = SkScalarHalf(this->width());
fZoomCenterY = SkScalarHalf(this->height());
#ifdef ANDROID
// FIXME: The first draw after a size change does not work on Android, so
// we post an invalidate.
this->postInvalDelay();
#endif
this->updateTitle(); // to refresh our config
fDevManager->windowSizeChanged(this);
}
///////////////////////////////////////////////////////////////////////////////
static const char is_sample_view_tag[] = "sample-is-sample-view";
static const char repeat_count_tag[] = "sample-set-repeat-count";
static const char set_use_pipe_tag[] = "sample-set-use-pipe";
bool SampleView::IsSampleView(SkView* view) {
SkEvent evt(is_sample_view_tag);
return view->doQuery(&evt);
}
bool SampleView::SetRepeatDraw(SkView* view, int count) {
SkEvent evt(repeat_count_tag);
evt.setFast32(count);
return view->doEvent(evt);
}
bool SampleView::SetUsePipe(SkView* view, bool pred) {
SkEvent evt(set_use_pipe_tag);
evt.setFast32(pred);
return view->doEvent(evt);
}
bool SampleView::onEvent(const SkEvent& evt) {
if (evt.isType(repeat_count_tag)) {
fRepeatCount = evt.getFast32();
return true;
}
if (evt.isType(set_use_pipe_tag)) {
fUsePipe = !!evt.getFast32();
return true;
}
return this->INHERITED::onEvent(evt);
}
bool SampleView::onQuery(SkEvent* evt) {
if (evt->isType(is_sample_view_tag)) {
return true;
}
return this->INHERITED::onQuery(evt);
}
#ifdef TEST_GPIPE
#include "SkGPipe.h"
class SimplePC : public SkGPipeController {
public:
SimplePC(SkCanvas* target);
~SimplePC();
virtual void* requestBlock(size_t minRequest, size_t* actual);
virtual void notifyWritten(size_t bytes);
private:
SkGPipeReader fReader;
void* fBlock;
size_t fBlockSize;
size_t fBytesWritten;
int fAtomsWritten;
SkGPipeReader::Status fStatus;
size_t fTotalWritten;
};
SimplePC::SimplePC(SkCanvas* target) : fReader(target) {
fBlock = NULL;
fBlockSize = fBytesWritten = 0;
fStatus = SkGPipeReader::kDone_Status;
fTotalWritten = 0;
fAtomsWritten = 0;
}
SimplePC::~SimplePC() {
// SkASSERT(SkGPipeReader::kDone_Status == fStatus);
sk_free(fBlock);
if (fTotalWritten) {
SkDebugf("--- %d bytes %d atoms, status %d\n", fTotalWritten,
fAtomsWritten, fStatus);
}
}
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);
#ifdef PIPE_FILE
//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);
#endif
fStatus = fReader.playback((const char*)fBlock + fBytesWritten, bytes);
SkASSERT(SkGPipeReader::kError_Status != fStatus);
fBytesWritten += bytes;
fTotalWritten += bytes;
fAtomsWritten += 1;
}
#endif
void SampleView::onDraw(SkCanvas* canvas) {
#ifdef TEST_GPIPE
SimplePC controller(canvas);
SkGPipeWriter writer;
if (fUsePipe) {
uint32_t flags = SkGPipeWriter::kCrossProcess_Flag;
// flags = 0;
canvas = writer.startRecording(&controller, flags);
}
#endif
this->onDrawBackground(canvas);
for (int i = 0; i < fRepeatCount; i++) {
SkAutoCanvasRestore acr(canvas, true);
this->onDrawContent(canvas);
}
}
void SampleView::onDrawBackground(SkCanvas* canvas) {
canvas->drawColor(fBGColor);
}
///////////////////////////////////////////////////////////////////////////////
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;
}
}
for (int k = 0; k < count - 1; k++) {
SkASSERT(!(array[k+1] < array[k]));
}
}
#include "SkRandom.h"
static void rand_rect(SkIRect* rect, SkRandom& rand) {
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);
}
SkRandom rand;
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);
}
}
SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv) {
// test();
return new SampleWindow(hwnd, argc, argv, NULL);
}
void get_preferred_size(int* x, int* y, int* width, int* height) {
*x = 10;
*y = 50;
*width = 640;
*height = 480;
}
void application_init() {
// setenv("ANDROID_ROOT", "../../../data", 0);
#ifdef SK_BUILD_FOR_MAC
setenv("ANDROID_ROOT", "/android/device/data", 0);
#endif
SkGraphics::Init();
SkEvent::Init();
}
void application_term() {
SkEvent::Term();
SkGraphics::Term();
}