| #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" | 
 |  | 
 | 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_pts(SkPoint pts[], int R, int C, SkBoundaryPatch* patch) { | 
 |     SkScalar invR = SkScalarInvert(SkIntToScalar(R - 1)); | 
 |     SkScalar invC = SkScalarInvert(SkIntToScalar(C - 1)); | 
 |  | 
 |     for (int y = 0; y < C; y++) { | 
 |         SkScalar yy = y * invC; | 
 |         for (int x = 0; x < R; x++) { | 
 |             *pts++ = patch->evaluate(x * invR, yy); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | 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 draw_texture(SkCanvas* canvas, const SkPoint verts[], int R, int C, | 
 |                          const SkBitmap& texture) { | 
 |     int vertCount = R * C; | 
 |     const int rows = R - 1; | 
 |     const int cols = C - 1; | 
 |     int idxCount = rows * cols * 6; | 
 |  | 
 |     SkAutoTArray<SkPoint> texStorage(vertCount); | 
 |     SkPoint* tex = texStorage.get(); | 
 |     SkAutoTArray<uint16_t> idxStorage(idxCount); | 
 |     uint16_t* idx = idxStorage.get(); | 
 |  | 
 |  | 
 |     const SkScalar dtx = texture.width() / rows; | 
 |     const SkScalar dty = texture.height() / cols; | 
 |     int index = 0; | 
 |     for (int y = 0; y <= cols; y++) { | 
 |         for (int x = 0; x <= rows; x++) { | 
 |             tex->set(x*dtx, 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; | 
 |     } | 
 |  | 
 |     SkPaint paint; | 
 |     paint.setShader(SkShader::CreateBitmapShader(texture, | 
 |                                                  SkShader::kClamp_TileMode, | 
 |                                                  SkShader::kClamp_TileMode))->unref(); | 
 |  | 
 |     canvas->drawVertices(SkCanvas::kTriangles_VertexMode, vertCount, verts, | 
 |                          texStorage.get(), NULL, NULL, idxStorage.get(), | 
 |                          idxCount, paint);                          | 
 | } | 
 |  | 
 | static void test_patch(SkCanvas* canvas, const SkBitmap& bm, SkScalar scale) { | 
 |     SkCubicBoundaryCurve L, T, R, B; | 
 |      | 
 |     set_cubic(L.fPts, 0, 0, 0, 100, scale); | 
 |     set_cubic(T.fPts, 0, 0, 100, 0, scale); | 
 |     set_cubic(R.fPts, 100, 0, 100, 100, -scale); | 
 |     set_cubic(B.fPts, 0, 100, 100, 100, 0); | 
 |  | 
 |     SkBoundaryPatch patch; | 
 |     patch.setCurve(SkBoundaryPatch::kLeft, &L); | 
 |     patch.setCurve(SkBoundaryPatch::kTop, &T); | 
 |     patch.setCurve(SkBoundaryPatch::kRight, &R); | 
 |     patch.setCurve(SkBoundaryPatch::kBottom, &B); | 
 |  | 
 |     const int Rows = 25; | 
 |     const int Cols = 25; | 
 |     SkPoint pts[Rows * Cols]; | 
 |     set_pts(pts, Rows, Cols, &patch); | 
 |      | 
 |     SkPaint paint; | 
 |     paint.setAntiAlias(true); | 
 |     paint.setStrokeWidth(1); | 
 |     paint.setStrokeCap(SkPaint::kRound_Cap); | 
 |  | 
 |     canvas->translate(50, 50); | 
 |     canvas->scale(3, 3); | 
 |  | 
 |     draw_texture(canvas, pts, Rows, Cols, bm); | 
 | //    canvas->drawPoints(SkCanvas::kPoints_PointMode, SK_ARRAY_COUNT(pts), | 
 | //                       pts, 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); | 
 | } | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | static SkScalar gScale = 0; | 
 | static SkScalar gDScale = 0.01; | 
 |  | 
 | class WarpView : public SkView { | 
 |     Mesh        fMesh, fOrig; | 
 |     SkBitmap    fBitmap; | 
 | public: | 
 | 	WarpView() { | 
 |         SkBitmap bm; | 
 |    //     SkImageDecoder::DecodeFile("/skimages/beach.jpg", &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, 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); | 
 |          | 
 |         test_patch(canvas, fBitmap, gScale); | 
 |         gScale += gDScale; | 
 |         if (gScale > 2) { | 
 |             gDScale = -gDScale; | 
 |         } else if (gScale < -2) { | 
 |             gDScale = -gDScale; | 
 |         } | 
 |         this->inval(NULL); | 
 |     } | 
 |      | 
 |     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); | 
 |  |