Initial implementation of GPU no filter NinePatch

TBR=bsalomon@google.com
BUG=skia:

Review URL: https://codereview.chromium.org/1454933002
diff --git a/gm/ninepatchstretch.cpp b/gm/ninepatchstretch.cpp
index 4074add..218b452 100644
--- a/gm/ninepatchstretch.cpp
+++ b/gm/ninepatchstretch.cpp
@@ -66,7 +66,7 @@
     }
 
     SkISize onISize() override {
-        return SkISize::Make(760, 400);
+        return SkISize::Make(760, 800);
     }
 
     void onDraw(SkCanvas* canvas) override {
@@ -91,16 +91,17 @@
         SkScalar y = SkIntToScalar(100);
 
         SkPaint paint;
-        paint.setFilterQuality(kLow_SkFilterQuality);
-
-        for (int iy = 0; iy < 2; ++iy) {
-            for (int ix = 0; ix < 2; ++ix) {
-                int i = ix * 2 + iy;
-                SkRect r = SkRect::MakeXYWH(x + ix * fixed, y + iy * fixed,
-                                            size[i].width(), size[i].height());
-                canvas->drawBitmapNine(fBitmap, fCenter, r, &paint);
-                canvas->drawImageNine(fImage, fCenter, r.makeOffset(360, 0), &paint);
-
+        for (int filter = 0; filter < 2; filter++) {
+            paint.setFilterQuality(filter == 0 ? kLow_SkFilterQuality : kNone_SkFilterQuality);
+            canvas->translate(0, filter * SkIntToScalar(400));
+            for (int iy = 0; iy < 2; ++iy) {
+                for (int ix = 0; ix < 2; ++ix) {
+                    int i = ix * 2 + iy;
+                    SkRect r = SkRect::MakeXYWH(x + ix * fixed, y + iy * fixed,
+                                                size[i].width(), size[i].height());
+                    canvas->drawBitmapNine(fBitmap, fCenter, r, &paint);
+                    canvas->drawImageNine(fImage, fCenter, r.makeOffset(360, 0), &paint);
+                }
             }
         }
     }
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 3d2d9f2..f93e935 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -232,6 +232,8 @@
       '<(skia_src_path)/gpu/batches/GrNonAAFillRectBatch.cpp',
       '<(skia_src_path)/gpu/batches/GrNonAAStrokeRectBatch.cpp',
       '<(skia_src_path)/gpu/batches/GrNonAAStrokeRectBatch.h',
+      '<(skia_src_path)/gpu/batches/GrNinePatch.cpp',
+      '<(skia_src_path)/gpu/batches/GrNinePatch.h',
       '<(skia_src_path)/gpu/batches/GrRectBatchFactory.h',
       '<(skia_src_path)/gpu/batches/GrRectBatchFactory.cpp',
       '<(skia_src_path)/gpu/batches/GrStencilAndCoverPathRenderer.cpp',
diff --git a/include/gpu/GrDrawContext.h b/include/gpu/GrDrawContext.h
index d55818b..f051ebd 100644
--- a/include/gpu/GrDrawContext.h
+++ b/include/gpu/GrDrawContext.h
@@ -242,6 +242,27 @@
                   const SkRect& oval,
                   const GrStrokeInfo& strokeInfo);
 
+    /**
+     *  Draw the image stretched differentially to fit into dst.
+     *  center is a rect within the image, and logically divides the image
+     *  into 9 sections (3x3). For example, if the middle pixel of a [5x5]
+     *  image is the "center", then the center-rect should be [2, 2, 3, 3].
+     *
+     *  If the dst is >= the image size, then...
+     *  - The 4 corners are not stretched at all.
+     *  - The sides are stretched in only one axis.
+     *  - The center is stretched in both axes.
+     * Else, for each axis where dst < image,
+     *  - The corners shrink proportionally
+     *  - The sides (along the shrink axis) and center are not drawn
+     */
+    void drawImageNine(const GrClip&,
+                       const GrPaint& paint,
+                       const SkMatrix& viewMatrix,
+                       int imageWidth,
+                       int imageHeight,
+                       const SkIRect& center,
+                       const SkRect& dst);
 
     /**
      * Draws a batch
diff --git a/src/gpu/GrDrawContext.cpp b/src/gpu/GrDrawContext.cpp
index 2613407..6718699 100644
--- a/src/gpu/GrDrawContext.cpp
+++ b/src/gpu/GrDrawContext.cpp
@@ -23,6 +23,7 @@
 #include "batches/GrDrawAtlasBatch.h"
 #include "batches/GrDrawVerticesBatch.h"
 #include "batches/GrRectBatchFactory.h"
+#include "batches/GrNinePatch.h" // TODO Factory
 
 #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == fDrawingManager->getContext())
 #define RETURN_IF_ABANDONED        if (fDrawingManager->abandoned()) { return; }
@@ -568,6 +569,27 @@
     }
 }
 
+void GrDrawContext::drawImageNine(const GrClip& clip,
+                                  const GrPaint& paint,
+                                  const SkMatrix& viewMatrix,
+                                  int imageWidth,
+                                  int imageHeight,
+                                  const SkIRect& center,
+                                  const SkRect& dst) {
+    RETURN_IF_ABANDONED
+    SkDEBUGCODE(this->validate();)
+
+    AutoCheckFlush acf(fDrawingManager);
+
+    SkAutoTUnref<GrDrawBatch> batch(GrNinePatch::CreateNonAA(paint.getColor(), viewMatrix,
+                                                             imageWidth, imageHeight,
+                                                             center, dst));
+
+    GrPipelineBuilder pipelineBuilder(paint, fRenderTarget, clip);
+    this->getDrawTarget()->drawBatch(pipelineBuilder, batch);
+}
+
+
 // Can 'path' be drawn as a pair of filled nested rectangles?
 static bool is_nested_rects(const SkMatrix& viewMatrix,
                             const SkPath& path,
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 7dfeba6..ccfed2c 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -30,6 +30,7 @@
 #include "SkImageFilter.h"
 #include "SkLayerInfo.h"
 #include "SkMaskFilter.h"
+#include "SkNinePatchIter.h"
 #include "SkPathEffect.h"
 #include "SkPicture.h"
 #include "SkPictureData.h"
@@ -1020,7 +1021,6 @@
     }
 }
 
-
 /*
  *  This is called by drawBitmap(), which has to handle images that may be too
  *  large to be represented by a single texture.
@@ -1074,7 +1074,6 @@
 
     // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring
     // the rest from the SkPaint.
-    GrPaint grPaint;
     SkAutoTUnref<const GrFragmentProcessor> fp;
 
     if (needsTextureDomain && (SkCanvas::kStrict_SrcRectConstraint == constraint)) {
@@ -1112,27 +1111,9 @@
         fp.reset(GrSimpleTextureEffect::Create(texture, texMatrix, params));
     }
 
-    SkAutoTUnref<const GrFragmentProcessor> shaderFP;
-
-    if (kAlpha_8_SkColorType == bitmap.colorType()) {
-        if (const SkShader* shader = paint.getShader()) {
-            shaderFP.reset(shader->asFragmentProcessor(this->context(),
-                                                       viewMatrix,
-                                                       nullptr,
-                                                       paint.getFilterQuality()));
-            if (!shaderFP) {
-                return;
-            }
-            const GrFragmentProcessor* fpSeries[] = { shaderFP.get(), fp.get() };
-            fp.reset(GrFragmentProcessor::RunInSeries(fpSeries, 2));
-        } else {
-            fp.reset(GrFragmentProcessor::MulOutputByInputUnpremulColor(fp));
-        }
-    } else {
-        fp.reset(GrFragmentProcessor::MulOutputByInputAlpha(fp));
-    }
-
-    if (!SkPaintToGrPaintReplaceShader(this->context(), paint, fp, &grPaint)) {
+    GrPaint grPaint;
+    if (!SkPaintToGrPaintWithTexture(this->context(), paint, viewMatrix, fp,
+                                     kAlpha_8_SkColorType == bitmap.colorType(), &grPaint)) {
         return;
     }
 
@@ -1495,6 +1476,64 @@
     this->drawBitmapRect(draw, bm, src, dst, paint, constraint);
 }
 
+void SkGpuDevice::drawImageNine(const SkDraw& draw, const SkImage* image,
+                                const SkIRect& center, const SkRect& dst, const SkPaint& paint) {
+    // TODO write native implementation
+    SkBitmap bitmap;
+    if (!wrap_as_bm(this->context(), image, &bitmap)) {
+        return;
+    }
+    return this->drawBitmapNine(draw, bitmap, center, dst, paint);
+}
+
+void SkGpuDevice::drawBitmapNine(const SkDraw& draw, const SkBitmap& bitmap, const SkIRect& center,
+                                 const SkRect& dst, const SkPaint& paint) {
+    GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice::drawBitmapNine", fContext);
+
+    CHECK_FOR_ANNOTATION(paint);
+    CHECK_SHOULD_DRAW(draw);
+
+    bool useFallback = paint.getMaskFilter() || paint.isAntiAlias();
+    bool doBicubic;
+    GrTextureParams::FilterMode textureFilterMode =
+            GrSkFilterQualityToGrFilterMode(paint.getFilterQuality(), *draw.fMatrix, SkMatrix::I(),
+                                            &doBicubic);
+
+    // TODO handle bilerp
+    if (useFallback || doBicubic || GrTextureParams::kNone_FilterMode != textureFilterMode) {
+        SkNinePatchIter iter(bitmap.width(), bitmap.height(), center, dst);
+
+        SkRect srcR, dstR;
+        while (iter.next(&srcR, &dstR)) {
+            this->drawBitmapRect(draw, bitmap, &srcR, dstR, paint,
+                                 SkCanvas::kStrict_SrcRectConstraint);
+        }
+        return;
+    }
+
+    GrTextureParams params = GrTextureParams::ClampNoFilter();
+
+    GrTexture* texture(GrRefCachedBitmapTexture(this->context(), bitmap, params));
+    if (nullptr == texture) {
+        return;
+    }
+
+    SkMatrix texMatrix;
+    texMatrix.setIDiv(texture->width(), texture->height());
+
+    SkAutoTUnref<const GrFragmentProcessor> fp(GrSimpleTextureEffect::Create(texture, texMatrix,
+                                                                             params));
+
+    GrPaint grPaint;
+    if (!SkPaintToGrPaintWithTexture(this->context(), paint, *draw.fMatrix, fp,
+                                     kAlpha_8_SkColorType == bitmap.colorType(), &grPaint)) {
+        return;
+    }
+
+    fDrawContext->drawImageNine(fClip, grPaint, *draw.fMatrix, bitmap.width(), bitmap.height(),
+                                center, dst);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 // must be in SkCanvas::VertexMode order
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index 0c0987a..1408a71 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -119,6 +119,11 @@
     void drawImageRect(const SkDraw&, const SkImage*, const SkRect* src, const SkRect& dst,
                        const SkPaint&, SkCanvas::SrcRectConstraint) override;
 
+    void drawImageNine(const SkDraw& draw, const SkImage* image, const SkIRect& center,
+                       const SkRect& dst, const SkPaint& paint) override;
+    void drawBitmapNine(const SkDraw& draw, const SkBitmap& bitmap, const SkIRect& center,
+                        const SkRect& dst, const SkPaint& paint) override;
+
     void flush() override;
 
     void onAttachToCanvas(SkCanvas* canvas) override;
diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp
index 5344145..6a43d37 100644
--- a/src/gpu/SkGpuDevice_drawTexture.cpp
+++ b/src/gpu/SkGpuDevice_drawTexture.cpp
@@ -23,35 +23,6 @@
     return textureIsAlphaOnly && paint.getShader();
 }
 
-/** Determines how to combine the texture FP with the paint's color and SkShader, if any. */
-static const GrFragmentProcessor* mix_texture_fp_with_paint_color_and_shader(
-                                                            const GrFragmentProcessor* textureFP,
-                                                            bool textureIsAlphaOnly,
-                                                            GrContext* context,
-                                                            const SkMatrix& viewMatrix,
-                                                            const SkPaint& paint) {
-    // According to the SkCanvas API, we only consider the shader if the bitmap or image being
-    // rendered is alpha-only.
-    if (textureIsAlphaOnly) {
-        if (const SkShader* shader = paint.getShader()) {
-            SkAutoTUnref<const GrFragmentProcessor> shaderFP(
-                shader->asFragmentProcessor(context,
-                                            viewMatrix,
-                                            nullptr,
-                                            paint.getFilterQuality()));
-            if (!shaderFP) {
-                return nullptr;
-            }
-            const GrFragmentProcessor* fpSeries[] = { shaderFP, textureFP };
-            return GrFragmentProcessor::RunInSeries(fpSeries, 2);
-        } else {
-            return GrFragmentProcessor::MulOutputByInputUnpremulColor(textureFP);
-        }
-    } else {
-        return GrFragmentProcessor::MulOutputByInputAlpha(textureFP);
-    }
-}
-
 //////////////////////////////////////////////////////////////////////////////
 //  Helper functions for dropping src rect constraint in bilerp mode.
 
@@ -231,10 +202,9 @@
     if (!fp) {
         return;
     }
-    fp.reset(mix_texture_fp_with_paint_color_and_shader(fp, alphaTexture, this->context(),
-                                                        viewMatrix, paint));
+
     GrPaint grPaint;
-    if (!SkPaintToGrPaintReplaceShader(fContext, paint, fp, &grPaint)) {
+    if (!SkPaintToGrPaintWithTexture(fContext, paint, viewMatrix, fp, alphaTexture, &grPaint)) {
         return;
     }
 
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index affd6cd..411b5b2 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -545,6 +545,34 @@
                                    grPaint);
 }
 
+bool SkPaintToGrPaintWithTexture(GrContext* context,
+                                 const SkPaint& paint,
+                                 const SkMatrix& viewM,
+                                 const GrFragmentProcessor* fp,
+                                 bool textureIsAlphaOnly,
+                                 GrPaint* grPaint) {
+    SkAutoTUnref<const GrFragmentProcessor> shaderFP;
+    if (textureIsAlphaOnly) {
+        if (const SkShader* shader = paint.getShader()) {
+            shaderFP.reset(shader->asFragmentProcessor(context,
+                                                       viewM,
+                                                       nullptr,
+                                                       paint.getFilterQuality()));
+            if (!shaderFP) {
+                return false;
+            }
+            const GrFragmentProcessor* fpSeries[] = { shaderFP.get(), fp };
+            shaderFP.reset(GrFragmentProcessor::RunInSeries(fpSeries, 2));
+        } else {
+            shaderFP.reset(GrFragmentProcessor::MulOutputByInputUnpremulColor(fp));
+        }
+    } else {
+        shaderFP.reset(GrFragmentProcessor::MulOutputByInputAlpha(fp));
+    }
+
+    return SkPaintToGrPaintReplaceShader(context, paint, shaderFP.get(), grPaint);
+}
+
 
 ////////////////////////////////////////////////////////////////////////////////////////////////
 
diff --git a/src/gpu/SkGrPriv.h b/src/gpu/SkGrPriv.h
index 5924ea6..77a464c 100644
--- a/src/gpu/SkGrPriv.h
+++ b/src/gpu/SkGrPriv.h
@@ -84,6 +84,15 @@
                                         false, grPaint);
 }
 
+/** This is used when there may or may not be a shader, and the caller wants to plugin a texture
+    lookup.  If there is a shader, then its output will only be used if the texture is alpha8. */
+bool SkPaintToGrPaintWithTexture(GrContext* context,
+                                 const SkPaint& paint,
+                                 const SkMatrix& viewM,
+                                 const GrFragmentProcessor* fp,
+                                 bool textureIsAlphaOnly,
+                                 GrPaint* grPaint);
+
 //////////////////////////////////////////////////////////////////////////////
 
 GrSurfaceDesc GrImageInfoToSurfaceDesc(const SkImageInfo&);
diff --git a/src/gpu/batches/GrNinePatch.cpp b/src/gpu/batches/GrNinePatch.cpp
new file mode 100644
index 0000000..4c6310c
--- /dev/null
+++ b/src/gpu/batches/GrNinePatch.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrNinePatch.h"
+
+#include "GrBatchFlushState.h"
+#include "GrDefaultGeoProcFactory.h"
+#include "GrResourceProvider.h"
+#include "GrVertexBatch.h"
+#include "SkBitmap.h"
+#include "SkNinePatchIter.h"
+#include "SkRect.h"
+
+static const GrGeometryProcessor* create_gp(bool readsCoverage) {
+    using namespace GrDefaultGeoProcFactory;
+    Color color(Color::kAttribute_Type);
+    Coverage coverage(readsCoverage ? Coverage::kSolid_Type : Coverage::kNone_Type);
+    LocalCoords localCoords(LocalCoords::kHasExplicit_Type);
+    return GrDefaultGeoProcFactory::Create(color, coverage, localCoords, SkMatrix::I());
+}
+
+class GrNonAANinePatchBatch : public GrVertexBatch {
+public:
+    DEFINE_BATCH_CLASS_ID
+
+    static const int kVertsPerRect = 4;
+    static const int kIndicesPerRect = 6;
+    static const int kRectsPerInstance = 9; // We could skip empty rects
+
+    struct Geometry {
+        SkMatrix fViewMatrix;
+        SkIRect fCenter;
+        SkRect fDst;
+        GrColor fColor;
+    };
+
+    GrNonAANinePatchBatch(GrColor color, const SkMatrix& viewMatrix, int imageWidth,
+                          int imageHeight, const SkIRect& center, const SkRect &dst)
+        : INHERITED(ClassID()) {
+        Geometry& geo = fGeoData.push_back();
+        geo.fViewMatrix = viewMatrix;
+        geo.fColor = color;
+        geo.fCenter = center;
+        geo.fDst = dst;
+
+        fImageWidth = imageWidth;
+        fImageHeight = imageHeight;
+
+        // setup bounds
+        geo.fViewMatrix.mapRect(&fBounds, geo.fDst);
+    }
+
+    const char* name() const override { return "GrNonAANinePatchBatch"; }
+
+    void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
+        out->setUnknownFourComponents();
+    }
+
+    void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
+        out->setKnownSingleComponent(0xff);
+    }
+
+    SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
+
+private:
+    void onPrepareDraws(Target* target) override {
+        SkAutoTUnref<const GrGeometryProcessor> gp(create_gp(fOpts.readsCoverage()));
+        if (!gp) {
+            SkDebugf("Couldn't create GrGeometryProcessor\n");
+            return;
+        }
+
+        target->initDraw(gp, this->pipeline());
+
+        size_t vertexStride = gp->getVertexStride();
+        int instanceCount = fGeoData.count();
+
+        SkAutoTUnref<const GrIndexBuffer> indexBuffer(
+                target->resourceProvider()->refQuadIndexBuffer());
+        InstancedHelper helper;
+        void* vertices = helper.init(target, kTriangles_GrPrimitiveType, vertexStride,
+                                     indexBuffer, kVertsPerRect,
+                                     kIndicesPerRect, instanceCount * kRectsPerInstance);
+        if (!vertices || !indexBuffer) {
+            SkDebugf("Could not allocate vertices\n");
+            return;
+        }
+
+        for (int i = 0; i < instanceCount; i++) {
+            intptr_t verts = reinterpret_cast<intptr_t>(vertices) +
+                             i * kRectsPerInstance * kVertsPerRect * vertexStride;
+
+            Geometry& geo = fGeoData[i];
+            SkNinePatchIter iter(fImageWidth, fImageHeight, geo.fCenter, geo.fDst);
+
+            SkRect srcR, dstR;
+            while (iter.next(&srcR, &dstR)) {
+                SkPoint* positions = reinterpret_cast<SkPoint*>(verts);
+
+                positions->setRectFan(dstR.fLeft, dstR.fTop,
+                                      dstR.fRight, dstR.fBottom, vertexStride);
+
+                SkASSERT(!geo.fViewMatrix.hasPerspective());
+                geo.fViewMatrix.mapPointsWithStride(positions, vertexStride, kVertsPerRect);
+
+                // Setup local coords
+                static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
+                SkPoint* coords = reinterpret_cast<SkPoint*>(verts + kLocalOffset);
+                coords->setRectFan(srcR.fLeft, srcR.fTop, srcR.fRight, srcR.fBottom, vertexStride);
+
+                static const int kColorOffset = sizeof(SkPoint);
+                GrColor* vertColor = reinterpret_cast<GrColor*>(verts + kColorOffset);
+                for (int j = 0; j < 4; ++j) {
+                    *vertColor = geo.fColor;
+                    vertColor = (GrColor*) ((intptr_t) vertColor + vertexStride);
+                }
+                verts += kVertsPerRect * vertexStride;
+            }
+        }
+        helper.recordDraw(target);
+    }
+
+    void initBatchTracker(const GrPipelineOptimizations& opt) override {
+        opt.getOverrideColorIfSet(&fGeoData[0].fColor);
+        fOpts = opt;
+    }
+
+    bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
+        GrNonAANinePatchBatch* that = t->cast<GrNonAANinePatchBatch>();
+        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
+                                    that->bounds(), caps)) {
+            return false;
+        }
+
+        SkASSERT(this->fImageWidth == that->fImageWidth &&
+                 this->fImageHeight == that->fImageHeight);
+
+        // In the event of two batches, one who can tweak, one who cannot, we just fall back to
+        // not tweaking
+        if (fOpts.canTweakAlphaForCoverage() && !that->fOpts.canTweakAlphaForCoverage()) {
+            fOpts = that->fOpts;
+        }
+
+        fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
+        this->joinBounds(that->bounds());
+        return true;
+    }
+
+    GrPipelineOptimizations fOpts;
+    int fImageWidth;
+    int fImageHeight;
+    SkSTArray<1, Geometry, true> fGeoData;
+
+    typedef GrVertexBatch INHERITED;
+};
+
+namespace GrNinePatch {
+GrDrawBatch* CreateNonAA(GrColor color, const SkMatrix& viewMatrix, int imageWidth, int imageHeight,
+                         const SkIRect& center, const SkRect& dst) {
+    return new GrNonAANinePatchBatch(color, viewMatrix, imageWidth, imageHeight, center, dst);
+}
+};
diff --git a/src/gpu/batches/GrNinePatch.h b/src/gpu/batches/GrNinePatch.h
new file mode 100644
index 0000000..0a4ffd6
--- /dev/null
+++ b/src/gpu/batches/GrNinePatch.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrNinePatch_DEFINED
+#define GrNinePatch_DEFINED
+
+#include "GrColor.h"
+
+class GrDrawBatch;
+class SkBitmap;
+class SkMatrix;
+struct SkIRect;
+struct SkRect;
+
+namespace GrNinePatch {
+GrDrawBatch* CreateNonAA(GrColor color, const SkMatrix& viewMatrix, int imageWidth, int imageHeight,
+                         const SkIRect& center, const SkRect& dst);
+};
+
+#endif