/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SampleCode.h"
#include "SkView.h"
#include "SkCanvas.h"
#include "SkPath.h"
#include "SkRegion.h"
#include "SkShader.h"
#include "SkUtils.h"
#include "SkImage.h"
#include "SkSurface.h"
#define FAT_PIXEL_COLOR SK_ColorBLACK
#define PIXEL_CENTER_SIZE 3
#define WIRE_FRAME_COLOR 0xFFFF0000 /*0xFF00FFFF*/
#define WIRE_FRAME_SIZE 1.5f
static SkScalar apply_grid(SkScalar x) {
const SkScalar grid = 2;
return SkScalarRoundToScalar(x * grid) / grid;
}
static void apply_grid(SkPoint pts[], int count) {
for (int i = 0; i < count; ++i) {
pts[i].set(apply_grid(pts[i].fX), apply_grid(pts[i].fY));
}
}
static void erase(SkSurface* surface) {
surface->getCanvas()->clear(SK_ColorTRANSPARENT);
}
static SkShader* createChecker(const SkMatrix& localMatrix) {
// SkColor colors[] = { 0xFFFDFDFD, 0xFFF4F4F4 };
SkColor colors[] = { 0xFFFFFFFF, 0xFFFFFFFF };
SkBitmap bm;
bm.allocN32Pixels(2, 2);
bm.lockPixels();
*bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = SkPreMultiplyColor(colors[0]);
*bm.getAddr32(0, 1) = *bm.getAddr32(1, 0) = SkPreMultiplyColor(colors[1]);
return SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
SkShader::kRepeat_TileMode, &localMatrix);
}
class FatBits {
public:
FatBits() {
fAA = false;
fStyle = kHair_Style;
fGrid = true;
fShowSkeleton = true;
fUseGPU = false;
fUseClip = false;
fRectAsOval = false;
fUseTriangle = false;
fClipRect.set(2, 2, 11, 8 );
}
int getZoom() const { return fZoom; }
bool getAA() const { return fAA; }
void setAA(bool aa) { fAA = aa; }
bool getGrid() const { return fGrid; }
void setGrid(bool g) { fGrid = g; }
bool getShowSkeleton() const { return fShowSkeleton; }
void setShowSkeleton(bool ss) { fShowSkeleton = ss; }
bool getUseGPU() const { return fUseGPU; }
void setUseGPU(bool ug) { fUseGPU = ug; }
bool getTriangle() const { return fUseTriangle; }
void setTriangle(bool ut) { fUseTriangle = ut; }
void toggleRectAsOval() { fRectAsOval = !fRectAsOval; }
bool getUseClip() const { return fUseClip; }
void setUseClip(bool uc) { fUseClip = uc; }
enum Style {
kHair_Style,
kStroke_Style,
};
Style getStyle() const { return fStyle; }
void setStyle(Style s) { fStyle = s; }
void setWHZ(int width, int height, int zoom) {
fW = width;
fH = height;
fZoom = zoom;
fBounds.set(0, 0, SkIntToScalar(width * zoom), SkIntToScalar(height * zoom));
fMatrix.setScale(SkIntToScalar(zoom), SkIntToScalar(zoom));
fInverse.setScale(SK_Scalar1 / zoom, SK_Scalar1 / zoom);
fShader.reset(createChecker(fMatrix));
SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
fMinSurface.reset(SkSurface::NewRaster(info));
info.fWidth *= zoom;
info.fHeight *= zoom;
fMaxSurface.reset(SkSurface::NewRaster(info));
}
void drawBG(SkCanvas*);
void drawFG(SkCanvas*);
void drawLine(SkCanvas*, SkPoint pts[2]);
void drawRect(SkCanvas* canvas, SkPoint pts[2]);
void drawTriangle(SkCanvas* canvas, SkPoint pts[3]);
private:
bool fAA, fGrid, fShowSkeleton, fUseGPU, fUseClip, fRectAsOval, fUseTriangle;
Style fStyle;
int fW, fH, fZoom;
SkMatrix fMatrix, fInverse;
SkRect fBounds, fClipRect;
SkAutoTUnref<SkShader> fShader;
SkAutoTUnref<SkSurface> fMinSurface;
SkAutoTUnref<SkSurface> fMaxSurface;
void setupPaint(SkPaint* paint) {
bool aa = this->getAA();
switch (fStyle) {
case kHair_Style:
paint->setStrokeWidth(0);
break;
case kStroke_Style:
paint->setStrokeWidth(SK_Scalar1);
break;
}
paint->setAntiAlias(aa);
}
void setupSkeletonPaint(SkPaint* paint) {
paint->setStyle(SkPaint::kStroke_Style);
paint->setStrokeWidth(WIRE_FRAME_SIZE);
paint->setColor(fShowSkeleton ? WIRE_FRAME_COLOR : 0);
paint->setAntiAlias(true);
}
void drawTriangleSkeleton(SkCanvas* max, const SkPoint pts[]);
void drawLineSkeleton(SkCanvas* max, const SkPoint pts[]);
void drawRectSkeleton(SkCanvas* max, const SkRect& r) {
SkPaint paint;
this->setupSkeletonPaint(&paint);
SkPath path;
if (fUseGPU && fAA) {
SkRect rr = r;
rr.inset(SkIntToScalar(fZoom)/2, SkIntToScalar(fZoom)/2);
path.addRect(rr);
path.moveTo(rr.fLeft, rr.fTop);
path.lineTo(rr.fRight, rr.fBottom);
rr = r;
rr.inset(-SkIntToScalar(fZoom)/2, -SkIntToScalar(fZoom)/2);
path.addRect(rr);
} else {
fRectAsOval ? path.addOval(r) : path.addRect(r);
if (fUseGPU) {
path.moveTo(r.fLeft, r.fTop);
path.lineTo(r.fRight, r.fBottom);
}
}
max->drawPath(path, paint);
}
void copyMinToMax() {
erase(fMaxSurface);
SkCanvas* canvas = fMaxSurface->getCanvas();
canvas->save();
canvas->concat(fMatrix);
fMinSurface->draw(canvas, 0, 0, NULL);
canvas->restore();
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kClear_Mode);
for (int iy = 1; iy < fH; ++iy) {
SkScalar y = SkIntToScalar(iy * fZoom);
canvas->drawLine(0, y - SK_ScalarHalf, 999, y - SK_ScalarHalf, paint);
}
for (int ix = 1; ix < fW; ++ix) {
SkScalar x = SkIntToScalar(ix * fZoom);
canvas->drawLine(x - SK_ScalarHalf, 0, x - SK_ScalarHalf, 999, paint);
}
}
};
void FatBits::drawBG(SkCanvas* canvas) {
SkPaint paint;
paint.setShader(fShader);
canvas->drawRect(fBounds, paint);
paint.setShader(NULL);
}
void FatBits::drawFG(SkCanvas* canvas) {
SkPaint inner, outer;
inner.setAntiAlias(true);
inner.setColor(SK_ColorBLACK);
inner.setStrokeWidth(PIXEL_CENTER_SIZE);
outer.setAntiAlias(true);
outer.setColor(SK_ColorWHITE);
outer.setStrokeWidth(PIXEL_CENTER_SIZE + 2);
SkScalar half = SkIntToScalar(fZoom) / 2;
for (int iy = 0; iy < fH; ++iy) {
SkScalar y = SkIntToScalar(iy * fZoom) + half;
for (int ix = 0; ix < fW; ++ix) {
SkScalar x = SkIntToScalar(ix * fZoom) + half;
canvas->drawPoint(x, y, outer);
canvas->drawPoint(x, y, inner);
}
}
if (fUseClip) {
SkPaint p;
p.setStyle(SkPaint::kStroke_Style);
p.setColor(SK_ColorLTGRAY);
SkRect r = {
fClipRect.fLeft * fZoom,
fClipRect.fTop * fZoom,
fClipRect.fRight * fZoom,
fClipRect.fBottom * fZoom
};
canvas->drawRect(r, p);
}
}
void FatBits::drawLineSkeleton(SkCanvas* max, const SkPoint pts[]) {
SkPaint paint;
this->setupSkeletonPaint(&paint);
SkPath path;
path.moveTo(pts[0]);
path.lineTo(pts[1]);
switch (fStyle) {
case kHair_Style:
if (fUseGPU) {
SkPaint p;
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(SK_Scalar1 * fZoom);
SkPath dst;
p.getFillPath(path, &dst);
path.addPath(dst);
}
break;
case kStroke_Style: {
SkPaint p;
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(SK_Scalar1 * fZoom);
SkPath dst;
p.getFillPath(path, &dst);
path = dst;
if (fUseGPU) {
path.moveTo(dst.getPoint(0));
path.lineTo(dst.getPoint(2));
}
} break;
}
max->drawPath(path, paint);
}
void FatBits::drawLine(SkCanvas* canvas, SkPoint pts[]) {
SkPaint paint;
fInverse.mapPoints(pts, 2);
if (fGrid) {
apply_grid(pts, 2);
}
erase(fMinSurface);
this->setupPaint(&paint);
paint.setColor(FAT_PIXEL_COLOR);
if (fUseClip) {
fMinSurface->getCanvas()->save();
SkRect r = fClipRect;
r.inset(SK_Scalar1/3, SK_Scalar1/3);
fMinSurface->getCanvas()->clipRect(r, SkRegion::kIntersect_Op, true);
}
fMinSurface->getCanvas()->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
if (fUseClip) {
fMinSurface->getCanvas()->restore();
}
this->copyMinToMax();
SkCanvas* max = fMaxSurface->getCanvas();
fMatrix.mapPoints(pts, 2);
this->drawLineSkeleton(max, pts);
fMaxSurface->draw(canvas, 0, 0, NULL);
}
void FatBits::drawRect(SkCanvas* canvas, SkPoint pts[2]) {
SkPaint paint;
fInverse.mapPoints(pts, 2);
if (fGrid) {
apply_grid(pts, 2);
}
SkRect r;
r.set(pts, 2);
erase(fMinSurface);
this->setupPaint(&paint);
paint.setColor(FAT_PIXEL_COLOR);
{
SkCanvas* c = fMinSurface->getCanvas();
fRectAsOval ? c->drawOval(r, paint) : c->drawRect(r, paint);
}
this->copyMinToMax();
SkCanvas* max = fMaxSurface->getCanvas();
fMatrix.mapPoints(pts, 2);
r.set(pts, 2);
this->drawRectSkeleton(max, r);
fMaxSurface->draw(canvas, 0, 0, NULL);
}
void FatBits::drawTriangleSkeleton(SkCanvas* max, const SkPoint pts[]) {
SkPaint paint;
this->setupSkeletonPaint(&paint);
SkPath path;
path.moveTo(pts[0]);
path.lineTo(pts[1]);
path.lineTo(pts[2]);
path.close();
max->drawPath(path, paint);
}
void FatBits::drawTriangle(SkCanvas* canvas, SkPoint pts[3]) {
SkPaint paint;
fInverse.mapPoints(pts, 3);
if (fGrid) {
apply_grid(pts, 3);
}
SkPath path;
path.moveTo(pts[0]);
path.lineTo(pts[1]);
path.lineTo(pts[2]);
path.close();
erase(fMinSurface);
this->setupPaint(&paint);
paint.setColor(FAT_PIXEL_COLOR);
fMinSurface->getCanvas()->drawPath(path, paint);
this->copyMinToMax();
SkCanvas* max = fMaxSurface->getCanvas();
fMatrix.mapPoints(pts, 3);
this->drawTriangleSkeleton(max, pts);
fMaxSurface->draw(canvas, 0, 0, NULL);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
class IndexClick : public SkView::Click {
int fIndex;
public:
IndexClick(SkView* v, int index) : SkView::Click(v), fIndex(index) {}
static int GetIndex(SkView::Click* click) {
return ((IndexClick*)click)->fIndex;
}
};
class DrawLineView : public SampleView {
FatBits fFB;
SkPoint fPts[3];
bool fIsRect;
public:
DrawLineView() {
fFB.setWHZ(24, 16, 48);
fPts[0].set(48, 48);
fPts[1].set(48 * 5, 48 * 4);
fPts[2].set(48 * 2, 48 * 6);
fIsRect = false;
}
void setStyle(FatBits::Style s) {
fFB.setStyle(s);
this->inval(NULL);
}
protected:
virtual bool onQuery(SkEvent* evt) SK_OVERRIDE {
if (SampleCode::TitleQ(*evt)) {
SampleCode::TitleR(evt, "FatBits");
return true;
}
SkUnichar uni;
if (SampleCode::CharQ(*evt, &uni)) {
switch (uni) {
case 'c':
fFB.setUseClip(!fFB.getUseClip());
this->inval(NULL);
return true;
case 'r':
fIsRect = !fIsRect;
this->inval(NULL);
return true;
case 'o':
fFB.toggleRectAsOval();
this->inval(NULL);
return true;
case 'x':
fFB.setGrid(!fFB.getGrid());
this->inval(NULL);
return true;
case 's':
if (FatBits::kStroke_Style == fFB.getStyle()) {
this->setStyle(FatBits::kHair_Style);
} else {
this->setStyle(FatBits::kStroke_Style);
}
return true;
case 'a':
fFB.setAA(!fFB.getAA());
this->inval(NULL);
return true;
case 'w':
fFB.setShowSkeleton(!fFB.getShowSkeleton());
this->inval(NULL);
return true;
case 'g':
fFB.setUseGPU(!fFB.getUseGPU());
this->inval(NULL);
return true;
case 't':
fFB.setTriangle(!fFB.getTriangle());
this->inval(NULL);
return true;
}
}
return this->INHERITED::onQuery(evt);
}
virtual void onDrawContent(SkCanvas* canvas) {
fFB.drawBG(canvas);
if (fFB.getTriangle()) {
fFB.drawTriangle(canvas, fPts);
}
else if (fIsRect) {
fFB.drawRect(canvas, fPts);
} else {
fFB.drawLine(canvas, fPts);
}
fFB.drawFG(canvas);
{
SkString str;
str.printf("%s %s %s %s",
fFB.getAA() ? "AA" : "BW",
FatBits::kHair_Style == fFB.getStyle() ? "Hair" : "Stroke",
fFB.getUseGPU() ? "GPU" : "CPU",
fFB.getUseClip() ? "clip" : "noclip");
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(16);
paint.setColor(SK_ColorBLUE);
canvas->drawText(str.c_str(), str.size(), 10, 16, paint);
}
}
virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y,
unsigned modi) SK_OVERRIDE {
SkPoint pt = { x, y };
int index = -1;
int count = fFB.getTriangle() ? 3 : 2;
SkScalar tol = 12;
for (int i = 0; i < count; ++i) {
if (fPts[i].equalsWithinTolerance(pt, tol)) {
index = i;
break;
}
}
return new IndexClick(this, index);
}
virtual bool onClick(Click* click) SK_OVERRIDE {
int index = IndexClick::GetIndex(click);
if (index >= 0 && index <= 2) {
fPts[index] = click->fCurr;
} else {
SkScalar dx = click->fCurr.fX - click->fPrev.fX;
SkScalar dy = click->fCurr.fY - click->fPrev.fY;
fPts[0].offset(dx, dy);
fPts[1].offset(dx, dy);
fPts[2].offset(dx, dy);
}
this->inval(NULL);
return true;
}
private:
typedef SampleView INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
static SkView* MyFactory() { return new DrawLineView; }
static SkViewRegister reg(MyFactory);