/*
* Copyright 2011 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 "SkGradientShader.h"
#include "SkPath.h"
#include "SkRegion.h"
#include "SkShader.h"
#include "SkUtils.h"
#include "SkImageDecoder.h"
#include "SkBlurMaskFilter.h"
#include "SkTableMaskFilter.h"
#define kNearlyZero (SK_Scalar1 / 8092)
static void test_bigblur(SkCanvas* canvas) {
canvas->drawColor(SK_ColorBLACK);
SkBitmap orig, mask;
SkImageDecoder::DecodeFile("/skimages/app_icon.png", &orig);
SkMaskFilter* mf = SkBlurMaskFilter::Create(8, SkBlurMaskFilter::kNormal_BlurStyle);
SkPaint paint;
paint.setMaskFilter(mf)->unref();
SkIPoint offset;
orig.extractAlpha(&mask, &paint, &offset);
paint.setColor(0xFFBB8800);
paint.setColor(SK_ColorWHITE);
int i;
canvas->save();
float gamma = 0.8;
for (i = 0; i < 5; i++) {
paint.setMaskFilter(SkTableMaskFilter::CreateGamma(gamma))->unref();
canvas->drawBitmap(mask, 0, 0, &paint);
paint.setMaskFilter(NULL);
canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
gamma -= 0.1;
canvas->translate(120, 0);
}
canvas->restore();
canvas->translate(0, 160);
for (i = 0; i < 5; i++) {
paint.setMaskFilter(SkTableMaskFilter::CreateClip(i*30, 255 - 20))->unref();
canvas->drawBitmap(mask, 0, 0, &paint);
paint.setMaskFilter(NULL);
canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
canvas->translate(120, 0);
}
#if 0
paint.setColor(0xFFFFFFFF);
canvas->drawBitmap(mask, 0, 0, &paint);
paint.setMaskFilter(NULL);
canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
canvas->translate(120, 0);
canvas->drawBitmap(mask, 0, 0, &paint);
canvas->drawBitmap(mask, 0, 0, &paint);
canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
canvas->translate(120, 0);
canvas->drawBitmap(mask, 0, 0, &paint);
canvas->drawBitmap(mask, 0, 0, &paint);
canvas->drawBitmap(mask, 0, 0, &paint);
canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
canvas->translate(120, 0);
canvas->drawBitmap(mask, 0, 0, &paint);
canvas->drawBitmap(mask, 0, 0, &paint);
canvas->drawBitmap(mask, 0, 0, &paint);
canvas->drawBitmap(mask, 0, 0, &paint);
canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
canvas->translate(120, 0);
canvas->drawBitmap(mask, 0, 0, &paint);
canvas->drawBitmap(mask, 0, 0, &paint);
canvas->drawBitmap(mask, 0, 0, &paint);
canvas->drawBitmap(mask, 0, 0, &paint);
canvas->drawBitmap(mask, 0, 0, &paint);
canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
#endif
}
#include "SkMeshUtils.h"
static SkPoint SkMakePoint(SkScalar x, SkScalar y) {
SkPoint pt;
pt.set(x, y);
return pt;
}
static SkPoint SkPointInterp(const SkPoint& a, const SkPoint& b, SkScalar t) {
return SkMakePoint(SkScalarInterp(a.fX, b.fX, t),
SkScalarInterp(a.fY, b.fY, t));
}
#include "SkBoundaryPatch.h"
static void set_cubic(SkPoint pts[4], SkScalar x0, SkScalar y0,
SkScalar x3, SkScalar y3, SkScalar scale = 1) {
SkPoint tmp, tmp2;
pts[0].set(x0, y0);
pts[3].set(x3, y3);
tmp = SkPointInterp(pts[0], pts[3], SK_Scalar1/3);
tmp2 = pts[0] - tmp;
tmp2.rotateCW();
tmp2.scale(scale);
pts[1] = tmp + tmp2;
tmp = SkPointInterp(pts[0], pts[3], 2*SK_Scalar1/3);
tmp2 = pts[3] - tmp;
tmp2.rotateCW();
tmp2.scale(scale);
pts[2] = tmp + tmp2;
}
static void test_patch(SkCanvas* canvas, const SkBitmap& bm, SkScalar scale) {
SkCubicBoundary cubic;
set_cubic(cubic.fPts + 0, 0, 0, 100, 0, scale);
set_cubic(cubic.fPts + 3, 100, 0, 100, 100, scale);
set_cubic(cubic.fPts + 6, 100, 100, 0, 100, -scale);
set_cubic(cubic.fPts + 9, 0, 100, 0, 0, 0);
SkBoundaryPatch patch;
patch.setBoundary(&cubic);
const int Rows = 16;
const int Cols = 16;
SkPoint pts[Rows * Cols];
patch.evalPatch(pts, Rows, Cols);
SkPaint paint;
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setStrokeWidth(1);
paint.setStrokeCap(SkPaint::kRound_Cap);
canvas->translate(50, 50);
canvas->scale(3, 3);
SkMeshUtils::Draw(canvas, bm, Rows, Cols, pts, NULL, paint);
}
static void test_drag(SkCanvas* canvas, const SkBitmap& bm,
const SkPoint& p0, const SkPoint& p1) {
SkCubicBoundary cubic;
set_cubic(cubic.fPts + 0, 0, 0, 100, 0, 0);
set_cubic(cubic.fPts + 3, 100, 0, 100, 100, 0);
set_cubic(cubic.fPts + 6, 100, 100, 0, 100, 0);
set_cubic(cubic.fPts + 9, 0, 100, 0, 0, 0);
#if 0
cubic.fPts[1] += p1 - p0;
cubic.fPts[2] += p1 - p0;
#else
SkScalar dx = p1.fX - p0.fX;
if (dx > 0) dx = 0;
SkScalar dy = p1.fY - p0.fY;
if (dy > 0) dy = 0;
cubic.fPts[1].fY += dy;
cubic.fPts[2].fY += dy;
cubic.fPts[10].fX += dx;
cubic.fPts[11].fX += dx;
#endif
SkBoundaryPatch patch;
patch.setBoundary(&cubic);
const int Rows = 16;
const int Cols = 16;
SkPoint pts[Rows * Cols];
patch.evalPatch(pts, Rows, Cols);
SkPaint paint;
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setStrokeWidth(1);
paint.setStrokeCap(SkPaint::kRound_Cap);
canvas->translate(50, 50);
canvas->scale(3, 3);
SkAutoCanvasRestore acr(canvas, true);
SkRect r = { 0, 0, 100, 100 };
canvas->clipRect(r);
SkMeshUtils::Draw(canvas, bm, Rows, Cols, pts, NULL, paint);
}
///////////////////////////////////////////////////////////////////////////////
class Mesh {
public:
Mesh();
~Mesh();
Mesh& operator=(const Mesh& src);
void init(const SkRect& bounds, int rows, int cols,
const SkRect& texture);
const SkRect& bounds() const { return fBounds; }
int rows() const { return fRows; }
int cols() const { return fCols; }
SkPoint& pt(int row, int col) {
return fPts[row * (fRows + 1) + col];
}
void draw(SkCanvas*, const SkPaint&);
void drawWireframe(SkCanvas* canvas, const SkPaint& paint);
private:
SkRect fBounds;
int fRows, fCols;
SkPoint* fPts;
SkPoint* fTex; // just points into fPts, not separately allocated
int fCount;
uint16_t* fIndices;
int fIndexCount;
};
Mesh::Mesh() : fPts(NULL), fCount(0), fIndices(NULL), fIndexCount(0) {}
Mesh::~Mesh() {
delete[] fPts;
delete[] fIndices;
}
Mesh& Mesh::operator=(const Mesh& src) {
delete[] fPts;
delete[] fIndices;
fBounds = src.fBounds;
fRows = src.fRows;
fCols = src.fCols;
fCount = src.fCount;
fPts = new SkPoint[fCount * 2];
fTex = fPts + fCount;
memcpy(fPts, src.fPts, fCount * 2 * sizeof(SkPoint));
delete[] fIndices;
fIndexCount = src.fIndexCount;
fIndices = new uint16_t[fIndexCount];
memcpy(fIndices, src.fIndices, fIndexCount * sizeof(uint16_t));
return *this;
}
void Mesh::init(const SkRect& bounds, int rows, int cols,
const SkRect& texture) {
SkASSERT(rows > 0 && cols > 0);
fBounds = bounds;
fRows = rows;
fCols = cols;
delete[] fPts;
fCount = (rows + 1) * (cols + 1);
fPts = new SkPoint[fCount * 2];
fTex = fPts + fCount;
delete[] fIndices;
fIndexCount = rows * cols * 6;
fIndices = new uint16_t[fIndexCount];
SkPoint* pts = fPts;
const SkScalar dx = bounds.width() / rows;
const SkScalar dy = bounds.height() / cols;
SkPoint* tex = fTex;
const SkScalar dtx = texture.width() / rows;
const SkScalar dty = texture.height() / cols;
uint16_t* idx = fIndices;
int index = 0;
for (int y = 0; y <= cols; y++) {
for (int x = 0; x <= rows; x++) {
pts->set(bounds.fLeft + x*dx, bounds.fTop + y*dy);
pts += 1;
tex->set(texture.fLeft + x*dtx, texture.fTop + y*dty);
tex += 1;
if (y < cols && x < rows) {
*idx++ = index;
*idx++ = index + rows + 1;
*idx++ = index + 1;
*idx++ = index + 1;
*idx++ = index + rows + 1;
*idx++ = index + rows + 2;
index += 1;
}
}
index += 1;
}
}
void Mesh::draw(SkCanvas* canvas, const SkPaint& paint) {
canvas->drawVertices(SkCanvas::kTriangles_VertexMode, fCount,
fPts, fTex, NULL, NULL, fIndices, fIndexCount,
paint);
}
void Mesh::drawWireframe(SkCanvas* canvas, const SkPaint& paint) {
canvas->drawVertices(SkCanvas::kTriangles_VertexMode, fCount,
fPts, NULL, NULL, NULL, fIndices, fIndexCount,
paint);
}
///////////////////////////////////////////////////////////////////////////////
class WarpView : public SkView {
Mesh fMesh, fOrig;
SkBitmap fBitmap;
SkMatrix fMatrix, fInverse;
public:
WarpView() {
SkBitmap bm;
// SkImageDecoder::DecodeFile("/skimages/marker.png", &bm);
SkImageDecoder::DecodeFile("/skimages/logo.gif", &bm);
// SkImageDecoder::DecodeFile("/beach_shot.JPG", &bm);
fBitmap = bm;
SkRect bounds, texture;
texture.set(0, 0, SkIntToScalar(fBitmap.width()),
SkIntToScalar(fBitmap.height()));
bounds = texture;
// fMesh.init(bounds, fBitmap.width() / 40, fBitmap.height() / 40, texture);
fMesh.init(bounds, fBitmap.width()/16, fBitmap.height()/16, texture);
fOrig = fMesh;
fP0.set(0, 0);
fP1 = fP0;
fMatrix.setScale(2, 2);
fMatrix.invert(&fInverse);
}
protected:
// overrides from SkEventSink
virtual bool onQuery(SkEvent* evt) {
if (SampleCode::TitleQ(*evt)) {
SampleCode::TitleR(evt, "Warp");
return true;
}
return this->INHERITED::onQuery(evt);
}
static SkPoint apply_warp(const SkVector& drag, SkScalar dragLength,
const SkPoint& dragStart, const SkPoint& dragCurr,
const SkPoint& orig) {
SkVector delta = orig - dragCurr;
SkScalar length = SkPoint::Normalize(&delta);
if (length <= kNearlyZero) {
return orig;
}
const SkScalar period = 20;
const SkScalar mag = dragLength / 3;
SkScalar d = length / (period);
d = mag * SkScalarSin(d) / d;
SkScalar dx = delta.fX * d;
SkScalar dy = delta.fY * d;
SkScalar px = orig.fX + dx;
SkScalar py = orig.fY + dy;
return SkPoint::Make(px, py);
}
static SkPoint apply_warp2(const SkVector& drag, SkScalar dragLength,
const SkPoint& dragStart, const SkPoint& dragCurr,
const SkPoint& orig) {
SkVector delta = orig - dragCurr;
SkScalar length = SkPoint::Normalize(&delta);
if (length <= kNearlyZero) {
return orig;
}
const SkScalar period = 10 + dragLength/4;
const SkScalar mag = dragLength / 3;
SkScalar d = length / (period);
if (d > SK_ScalarPI) {
d = SK_ScalarPI;
}
d = -mag * SkScalarSin(d);
SkScalar dx = delta.fX * d;
SkScalar dy = delta.fY * d;
SkScalar px = orig.fX + dx;
SkScalar py = orig.fY + dy;
return SkPoint::Make(px, py);
}
typedef SkPoint (*WarpProc)(const SkVector& drag, SkScalar dragLength,
const SkPoint& dragStart, const SkPoint& dragCurr,
const SkPoint& orig);
void warp(const SkPoint& p0, const SkPoint& p1) {
WarpProc proc = apply_warp2;
SkPoint delta = p1 - p0;
SkScalar length = SkPoint::Normalize(&delta);
for (int y = 0; y < fMesh.rows(); y++) {
for (int x = 0; x < fMesh.cols(); x++) {
fMesh.pt(x, y) = proc(delta, length, p0, p1, fOrig.pt(x, y));
}
}
fP0 = p0;
fP1 = p1;
}
virtual void onDraw(SkCanvas* canvas) {
canvas->drawColor(SK_ColorLTGRAY);
// test_bigblur(canvas); return;
canvas->concat(fMatrix);
SkPaint paint;
paint.setFilterBitmap(true);
paint.setShader(SkShader::CreateBitmapShader(fBitmap,
SkShader::kClamp_TileMode,
SkShader::kClamp_TileMode))->unref();
fMesh.draw(canvas, paint); //return;
paint.setShader(NULL);
paint.setColor(SK_ColorRED);
fMesh.draw(canvas, paint);
// test_drag(canvas, fBitmap, fP0, fP1);
}
virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
return new Click(this);
}
virtual bool onClick(Click* click) {
SkPoint pts[2] = { click->fOrig, click->fCurr };
fInverse.mapPoints(pts, 2);
this->warp(pts[0], pts[1]);
this->inval(NULL);
return true;
}
private:
SkIRect fBase, fRect;
SkPoint fP0, fP1;
typedef SkView INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
static SkView* MyFactory() { return new WarpView; }
static SkViewRegister reg(MyFactory);