skia2/samplecode/SampleWarp.cpp

253 lines
7.1 KiB
C++
Raw Normal View History

#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"
///////////////////////////////////////////////////////////////////////////////
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;
public:
WarpView() {
SkBitmap bm;
SkImageDecoder::DecodeFile("/skimages/nytimes.png", &bm);
SkIRect subset = { 0, 0, 420, 420 };
bm.extractSubset(&fBitmap, subset);
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, 30, 30, texture);
fOrig = fMesh;
}
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 make_pt(SkScalar x, SkScalar y) {
SkPoint pt;
pt.set(x, y);
return pt;
}
static SkScalar mapx0(SkScalar min, SkScalar max, SkScalar x0, SkScalar x1,
SkScalar x) {
if (x < x0) {
SkASSERT(x0 > min);
return x1 - SkScalarMulDiv(x1 - min, x0 - x, x0 - min);
} else {
SkASSERT(max > x0);
return x1 + SkScalarMulDiv(max - x1, x - x0, max - x0);
}
}
static SkScalar mapx1(SkScalar min, SkScalar max, SkScalar x0, SkScalar x1,
SkScalar x) {
SkScalar newx;
if (x < x0) {
SkASSERT(x0 > min);
newx = x1 - SkScalarMulDiv(x1 - min, x0 - x, x0 - min);
} else {
SkASSERT(max > x0);
newx = x1 + SkScalarMulDiv(max - x1, x - x0, max - x0);
}
return x + (newx - x) * 0.5f;
}
static SkPoint mappt(const SkRect& r, const SkPoint& p0, const SkPoint& p1,
const SkPoint& pt) {
return make_pt(mapx0(r.fLeft, r.fRight, p0.fX, p1.fX, pt.fX),
mapx0(r.fTop, r.fBottom, p0.fY, p1.fY, pt.fY));
}
void warp(const SkPoint& p0, const SkPoint& p1) {
const SkRect& bounds = fOrig.bounds();
int rows = fMesh.rows();
int cols = fMesh.cols();
SkRect r = bounds;
r.inset(bounds.width() / 256, bounds.height() / 256);
if (r.contains(p0)) {
for (int y = 1; y < cols; y++) {
for (int x = 1; x < rows; x++) {
fMesh.pt(x, y) = mappt(bounds, p0, p1, fOrig.pt(x, y));
}
}
}
}
virtual void onDraw(SkCanvas* canvas) {
canvas->drawColor(SK_ColorGRAY);
SkPaint paint;
paint.setFilterBitmap(true);
paint.setShader(SkShader::CreateBitmapShader(fBitmap,
SkShader::kClamp_TileMode,
SkShader::kClamp_TileMode))->unref();
fMesh.draw(canvas, paint);
paint.setShader(NULL);
paint.setColor(SK_ColorRED);
// fMesh.draw(canvas, paint);
}
virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
return new Click(this);
}
virtual bool onClick(Click* click) {
this->warp(click->fOrig, click->fCurr);
this->inval(NULL);
return true;
}
private:
SkIRect fBase, fRect;
typedef SkView INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
static SkView* MyFactory() { return new WarpView; }
static SkViewRegister reg(MyFactory);