/* * 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);