update on boundary patches
authorreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 28 Oct 2009 14:25:34 +0000 (14:25 +0000)
committerreed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 28 Oct 2009 14:25:34 +0000 (14:25 +0000)
git-svn-id: http://skia.googlecode.com/svn/trunk@409 2bbb7eff-a529-9590-31e7-b0007b416f81

experimental/SkBoundaryPatch.cpp
experimental/SkBoundaryPatch.h
include/utils/SkMeshUtils.h [new file with mode: 0644]
samplecode/SampleAnimator.cpp
samplecode/SampleWarp.cpp
src/utils/SkMeshUtils.cpp [new file with mode: 0644]

index 1644f72..cdbc877 100644 (file)
@@ -1,20 +1,14 @@
 #include "SkBoundaryPatch.h"
 
-SkBoundaryPatch::SkBoundaryPatch() {
-    sk_bzero(fCurve, sizeof(fCurve));
-};
+SkBoundaryPatch::SkBoundaryPatch() : fBoundary(NULL) {}
 
 SkBoundaryPatch::~SkBoundaryPatch() {
-    for (int i = 0; i < 4; i++) {
-        SkSafeUnref(fCurve[i]);
-    }
+    SkSafeUnref(fBoundary);
 }
 
-SkBoundaryCurve* SkBoundaryPatch::setCurve(Edge e, SkBoundaryCurve* curve) {
-    SkASSERT((unsigned)e < 4);
-
-    SkRefCnt_SafeAssign(fCurve[e], curve);
-    return curve;
+SkBoundary* SkBoundaryPatch::setBoundary(SkBoundary* b) {
+    SkRefCnt_SafeAssign(fBoundary, b);
+    return b;
 }
 
 static SkPoint SkMakePoint(SkScalar x, SkScalar y) {
@@ -28,27 +22,52 @@ static SkPoint SkPointInterp(const SkPoint& a, const SkPoint& b, SkScalar t) {
                        SkScalarInterp(a.fY, b.fY, t));
 }
 
-SkPoint SkBoundaryPatch::evaluate(SkScalar unitU, SkScalar unitV) {
-    SkPoint u = SkPointInterp(fCurve[kLeft]->evaluate(unitV),
-                              fCurve[kRight]->evaluate(unitV), unitU);
-    SkPoint v = SkPointInterp(fCurve[kTop]->evaluate(unitU),
-                              fCurve[kBottom]->evaluate(unitU), unitV);
+SkPoint SkBoundaryPatch::eval(SkScalar unitU, SkScalar unitV) {
+    SkBoundary* b = fBoundary;
+    SkPoint u = SkPointInterp(b->eval(SkBoundary::kLeft, SK_Scalar1 - unitV),
+                              b->eval(SkBoundary::kRight, unitV),
+                              unitU);
+    SkPoint v = SkPointInterp(b->eval(SkBoundary::kTop, unitU),
+                              b->eval(SkBoundary::kBottom, SK_Scalar1 - unitU),
+                              unitV);
     return SkMakePoint(SkScalarAve(u.fX, v.fX),
                        SkScalarAve(u.fY, v.fY));
 }
 
+bool SkBoundaryPatch::evalPatch(SkPoint verts[], int rows, int cols) {
+    if (rows < 2 || cols < 2) {
+        return false;
+    }
+
+    const SkScalar invR = SkScalarInvert(SkIntToScalar(rows - 1));
+    const SkScalar invC = SkScalarInvert(SkIntToScalar(cols - 1));
+    
+    for (int y = 0; y < cols; y++) {
+        SkScalar yy = y * invC;
+        for (int x = 0; x < rows; x++) {
+            *verts++ = this->eval(x * invR, yy);
+        }
+    }
+    return true;
+}
+
 ////////////////////////////////////////////////////////////////////////
 
 #include "SkGeometry.h"
 
-SkPoint SkLineBoundaryCurve::evaluate(SkScalar t) {
-    return SkPointInterp(fPts[0], fPts[1], t);
+SkPoint SkLineBoundary::eval(Edge e, SkScalar t) {
+    SkASSERT((unsigned)e < 4);
+    return SkPointInterp(fPts[e], fPts[(e + 1) & 3], t);
 }
 
-SkPoint SkCubicBoundaryCurve::evaluate(SkScalar t) {
+SkPoint SkCubicBoundary::eval(Edge e, SkScalar t) {
+    SkASSERT((unsigned)e < 4);
+
+    // ensure our 4th cubic wraps to the start of the first
+    fPts[12] = fPts[0];
+
     SkPoint loc;
-    SkEvalCubicAt(fPts, t, &loc, NULL, NULL);
+    SkEvalCubicAt(&fPts[e * 3], t, &loc, NULL, NULL);
     return loc;
 }
 
-
index 264ee29..835fc3e 100644 (file)
@@ -4,9 +4,17 @@
 #include "SkPoint.h"
 #include "SkRefCnt.h"
 
-class SkBoundaryCurve : public SkRefCnt {
+class SkBoundary : public SkRefCnt {
 public:
-    virtual SkPoint evaluate(SkScalar unitInterval) = 0;
+    // These must be 0, 1, 2, 3 for efficiency in the subclass implementations
+    enum Edge {
+        kTop    = 0,
+        kRight  = 1,
+        kBottom = 2,
+        kLeft   = 3
+    };
+    // Edge index goes clockwise around the boundary, beginning at the "top"
+    virtual SkPoint eval(Edge, SkScalar unitInterval) = 0;
 };
 
 class SkBoundaryPatch {
@@ -14,38 +22,33 @@ public:
     SkBoundaryPatch();
     ~SkBoundaryPatch();
 
-    enum Edge {
-        kLeft,
-        kTop,
-        kRight,
-        kBottom
-    };
-    
-    SkBoundaryCurve* getCurve(Edge e) const { return fCurve[e]; }
-    SkBoundaryCurve* setCurve(Edge e, SkBoundaryCurve*);
+    SkBoundary* getBoundary() const { return fBoundary; }
+    SkBoundary* setBoundary(SkBoundary*);
 
-    SkPoint evaluate(SkScalar unitU, SkScalar unitV);
+    SkPoint eval(SkScalar unitU, SkScalar unitV);
+    bool evalPatch(SkPoint verts[], int rows, int cols);
 
 private:
-    SkBoundaryCurve* fCurve[4];
+    SkBoundary* fBoundary;
 };
 
 ////////////////////////////////////////////////////////////////////////
 
-class SkLineBoundaryCurve : public SkBoundaryCurve {
+class SkLineBoundary : public SkBoundary {
 public:
-    SkPoint fPts[2];
+    SkPoint fPts[4];
     
     // override
-    virtual SkPoint evaluate(SkScalar);
+    virtual SkPoint eval(Edge, SkScalar);
 };
 
-class SkCubicBoundaryCurve : public SkBoundaryCurve {
+class SkCubicBoundary : public SkBoundary {
 public:
-    SkPoint fPts[4];
+    // the caller sets the first 12 entries. The 13th is used by the impl.
+    SkPoint fPts[13];
     
     // override
-    virtual SkPoint evaluate(SkScalar);
+    virtual SkPoint eval(Edge, SkScalar);
 };
 
 #endif
diff --git a/include/utils/SkMeshUtils.h b/include/utils/SkMeshUtils.h
new file mode 100644 (file)
index 0000000..1235485
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef SkMeshUtils_DEFINED
+#define SkMeshUtils_DEFINED
+
+#include "SkPoint.h"
+#include "SkColor.h"
+
+class SkBitmap;
+class SkCanvas;
+class SkPaint;
+
+class SkMeshIndices {
+public:
+    SkMeshIndices();
+    ~SkMeshIndices();
+    
+    bool init(int texW, int texH, int rows, int cols) {
+        return this->init(NULL, NULL, texW, texH, rows, cols);
+    }
+
+    bool init(SkPoint tex[], uint16_t indices[],
+              int texW, int texH, int rows, int cols);
+
+    size_t          indexCount() const { return fIndexCount; }
+    const uint16_t* indices() const { return fIndices; }
+
+    size_t          texCount() const { return fTexCount; }
+    const SkPoint*  tex() const { return fTex; }
+
+private:
+    size_t      fIndexCount, fTexCount;
+    SkPoint*    fTex;
+    uint16_t*   fIndices;
+    void*       fStorage; // may be null
+};
+
+class SkMeshUtils {
+public:
+    static void Draw(SkCanvas*, const SkBitmap&, int rows, int cols,
+                     const SkPoint verts[], const SkColor colors[],
+                     const SkPaint& paint);
+};
+
+#endif
index 2909ebc..4ba8872 100644 (file)
@@ -5,6 +5,50 @@
 #include "SkAnimator.h"
 #include "SkStream.h"
 
+#include "SkColorPriv.h"
+static inline void Filter_32_opaque_portable(unsigned x, unsigned y,
+                                             SkPMColor a00, SkPMColor a01,
+                                             SkPMColor a10, SkPMColor a11,
+                                             SkPMColor* dstColor) {
+    SkASSERT((unsigned)x <= 0xF);
+    SkASSERT((unsigned)y <= 0xF);
+    
+    int xy = x * y;
+    uint32_t mask = gMask_00FF00FF; //0xFF00FF;
+    
+    int scale = 256 - 16*y - 16*x + xy;
+    uint32_t lo = (a00 & mask) * scale;
+    uint32_t hi = ((a00 >> 8) & mask) * scale;
+    
+    scale = 16*x - xy;
+    lo += (a01 & mask) * scale;
+    hi += ((a01 >> 8) & mask) * scale;
+    
+    scale = 16*y - xy;
+    lo += (a10 & mask) * scale;
+    hi += ((a10 >> 8) & mask) * scale;
+    
+    lo += (a11 & mask) * xy;
+    hi += ((a11 >> 8) & mask) * xy;
+    
+    *dstColor = ((lo >> 8) & mask) | (hi & ~mask);
+}
+
+static void test_filter() {
+    for (int r = 0; r <= 0xFF; r++) {
+        SkPMColor c = SkPackARGB32(0xFF, r, r, r);
+        for (int y = 0; y <= 0xF; y++) {
+            for (int x = 0; x <= 0xF; x++) {
+                SkPMColor dst;
+                Filter_32_opaque_portable(x, y, c, c, c, c, &dst);
+                SkASSERT(SkGetPackedA32(dst) == 255);
+            }
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 class SkAnimatorView : public SkView {
 public:
     SkAnimatorView();
@@ -30,6 +74,7 @@ private:
 };
 
 SkAnimatorView::SkAnimatorView() : fAnimator(NULL) {
+    test_filter();
 }
 
 SkAnimatorView::~SkAnimatorView() {
index c7adad9..16c0bc8 100644 (file)
@@ -8,6 +8,8 @@
 #include "SkUtils.h"
 #include "SkImageDecoder.h"
 
+#include "SkMeshUtils.h"
+
 static SkPoint SkMakePoint(SkScalar x, SkScalar y) {
     SkPoint pt;
     pt.set(x, y);
@@ -21,18 +23,6 @@ static SkPoint SkPointInterp(const SkPoint& a, const SkPoint& b, SkScalar 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;
@@ -53,82 +43,78 @@ static void set_cubic(SkPoint pts[4], SkScalar x0, SkScalar y0,
     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;
-    }
-
+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.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);                         
+    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_patch(SkCanvas* canvas, const SkBitmap& bm, SkScalar scale) {
-    SkCubicBoundaryCurve L, T, R, B;
+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);
     
-    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);
+#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.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;
+    patch.setBoundary(&cubic);
+    
+    const int Rows = 16;
+    const int Cols = 16;
     SkPoint pts[Rows * Cols];
-    set_pts(pts, Rows, Cols, &patch);
+    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);
 
-    draw_texture(canvas, pts, Rows, Cols, bm);
-//    canvas->drawPoints(SkCanvas::kPoints_PointMode, SK_ARRAY_COUNT(pts),
-//                       pts, paint);
+    SkRect r = { 0, 0, 100, 100 };
+    canvas->clipRect(r);
+    SkMeshUtils::Draw(canvas, bm, Rows, Cols, pts, NULL, paint);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -255,7 +241,7 @@ void Mesh::drawWireframe(SkCanvas* canvas, const SkPaint& paint) {
 ///////////////////////////////////////////////////////////////////////////////
 
 static SkScalar gScale = 0;
-static SkScalar gDScale = 0.01;
+static SkScalar gDScale = 0.02;
 
 class WarpView : public SkView {
     Mesh        fMesh, fOrig;
@@ -263,8 +249,8 @@ class WarpView : public SkView {
 public:
        WarpView() {
         SkBitmap bm;
-   //     SkImageDecoder::DecodeFile("/skimages/beach.jpg", &bm);
-        SkImageDecoder::DecodeFile("/beach_shot.JPG", &bm);
+        SkImageDecoder::DecodeFile("/skimages/beach.jpg", &bm);
+   //     SkImageDecoder::DecodeFile("/beach_shot.JPG", &bm);
         fBitmap = bm;
         
         SkRect bounds, texture;
@@ -275,6 +261,9 @@ public:
 //        fMesh.init(bounds, fBitmap.width() / 40, fBitmap.height() / 40, texture);
         fMesh.init(bounds, 30, 30, texture);
         fOrig = fMesh;
+        
+        fP0.set(0, 0);
+        fP1 = fP0;
     }
 
 protected:
@@ -287,56 +276,9 @@ protected:
         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));
-                }
-            }
-        }
+        fP0 = p0;
+        fP1 = p1;
     }
     
     virtual void onDraw(SkCanvas* canvas) {
@@ -352,7 +294,8 @@ protected:
         paint.setShader(NULL);
         paint.setColor(SK_ColorRED);
     //    fMesh.draw(canvas, paint);
-        
+
+#if 0
         test_patch(canvas, fBitmap, gScale);
         gScale += gDScale;
         if (gScale > 2) {
@@ -361,6 +304,9 @@ protected:
             gDScale = -gDScale;
         }
         this->inval(NULL);
+#else
+        test_drag(canvas, fBitmap, fP0, fP1);
+#endif
     }
     
     virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
@@ -375,7 +321,7 @@ protected:
     
 private:
     SkIRect    fBase, fRect;
-    
+    SkPoint     fP0, fP1;
     typedef SkView INHERITED;
 };
 
diff --git a/src/utils/SkMeshUtils.cpp b/src/utils/SkMeshUtils.cpp
new file mode 100644 (file)
index 0000000..0385b99
--- /dev/null
@@ -0,0 +1,96 @@
+#include "SkMeshUtils.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+
+SkMeshIndices::SkMeshIndices() {
+    sk_bzero(this, sizeof(*this));
+}
+
+SkMeshIndices::~SkMeshIndices() {
+    sk_free(fStorage);
+}
+
+bool SkMeshIndices::init(SkPoint tex[], uint16_t indices[],
+                         int texW, int texH, int rows, int cols) {
+    if (rows < 2 || cols < 2) {
+        sk_free(fStorage);
+        fStorage = NULL;
+        fTex = NULL;
+        fIndices = NULL;
+        fTexCount = fIndexCount = 0;
+        return false;
+    }
+
+    sk_free(fStorage);
+    fStorage = NULL;
+
+    fTexCount = rows * cols;
+    rows -= 1;
+    cols -= 1;
+    fIndexCount = rows * cols * 6;
+
+    if (tex) {
+        fTex = tex;
+        fIndices = indices;
+    } else {
+        fStorage = sk_malloc_throw(fTexCount * sizeof(SkPoint) +
+                                   fIndexCount * sizeof(uint16_t));
+        fTex = (SkPoint*)fStorage;
+        fIndices = (uint16_t*)(fTex + fTexCount);
+    }
+
+    // compute the indices
+    {
+        uint16_t* idx = fIndices;
+        int index = 0;
+        for (int y = 0; y < cols; y++) {
+            for (int x = 0; x < rows; x++) {
+                *idx++ = index;
+                *idx++ = index + rows + 1;
+                *idx++ = index + 1;
+                
+                *idx++ = index + 1;
+                *idx++ = index + rows + 1;
+                *idx++ = index + rows + 2;
+                
+                index += 1;
+            }
+            index += 1;
+        }
+    }
+
+    // compute texture coordinates
+    {
+        SkPoint* tex = fTex;
+        const SkScalar dx = SkIntToScalar(texW) / rows;
+        const SkScalar dy = SkIntToScalar(texH) / cols;
+        for (int y = 0; y <= cols; y++) {
+            for (int x = 0; x <= rows; x++) {
+                tex->set(x*dx, y*dy);
+                tex += 1;
+            }
+        }
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkShader.h"
+
+void SkMeshUtils::Draw(SkCanvas* canvas, const SkBitmap& bitmap,
+                       int rows, int cols, const SkPoint verts[],
+                       const SkColor colors[], const SkPaint& paint) {
+    SkMeshIndices idx;
+    
+    if (idx.init(bitmap.width(), bitmap.height(), rows, cols)) {
+        SkPaint p(paint);
+        p.setShader(SkShader::CreateBitmapShader(bitmap,
+                                         SkShader::kClamp_TileMode,
+                                         SkShader::kClamp_TileMode))->unref();
+        canvas->drawVertices(SkCanvas::kTriangles_VertexMode,
+                             rows * cols, verts, idx.tex(), colors, NULL,
+                             idx.indices(), idx.indexCount(), p);
+    }
+}
+