Explicit tile bounds for SkPictureShader

The integer picture size is not granular enough to allow precise tiling
in arbitrary coordinate systems. This CL adds an optional tile bounds
float rect param to control the tile size and location.

(this also allows tile spacing emulation for picture
shaders).

R=reed@google.com, robertphillips@google.com

Author: fmalita@chromium.org

Review URL: https://codereview.chromium.org/437393003
diff --git a/src/core/SkPictureShader.cpp b/src/core/SkPictureShader.cpp
index 6ea67f8..2a6aae5 100644
--- a/src/core/SkPictureShader.cpp
+++ b/src/core/SkPictureShader.cpp
@@ -19,16 +19,20 @@
 #endif
 
 SkPictureShader::SkPictureShader(const SkPicture* picture, TileMode tmx, TileMode tmy,
-                                 const SkMatrix* localMatrix)
+                                 const SkMatrix* localMatrix, const SkRect* tile)
     : INHERITED(localMatrix)
     , fPicture(SkRef(picture))
     , fTmx(tmx)
-    , fTmy(tmy) { }
+    , fTmy(tmy) {
+    fTile = tile ? *tile : SkRect::MakeWH(SkIntToScalar(picture->width()),
+                                          SkIntToScalar(picture->height()));
+}
 
 SkPictureShader::SkPictureShader(SkReadBuffer& buffer)
         : INHERITED(buffer) {
     fTmx = static_cast<SkShader::TileMode>(buffer.read32());
     fTmy = static_cast<SkShader::TileMode>(buffer.read32());
+    buffer.readRect(&fTile);
     fPicture = SkPicture::CreateFromBuffer(buffer);
 }
 
@@ -37,11 +41,12 @@
 }
 
 SkPictureShader* SkPictureShader::Create(const SkPicture* picture, TileMode tmx, TileMode tmy,
-                                         const SkMatrix* localMatrix) {
-    if (!picture || 0 == picture->width() || 0 == picture->height()) {
+                                         const SkMatrix* localMatrix, const SkRect* tile) {
+    if (!picture || 0 == picture->width() || 0 == picture->height()
+        || (NULL != tile && tile->isEmpty())) {
         return NULL;
     }
-    return SkNEW_ARGS(SkPictureShader, (picture, tmx, tmy, localMatrix));
+    return SkNEW_ARGS(SkPictureShader, (picture, tmx, tmy, localMatrix, tile));
 }
 
 void SkPictureShader::flatten(SkWriteBuffer& buffer) const {
@@ -49,6 +54,7 @@
 
     buffer.write32(fTmx);
     buffer.write32(fTmy);
+    buffer.writeRect(fTile);
     fPicture->flatten(buffer);
 }
 
@@ -68,7 +74,7 @@
         scale.set(SkScalarSqrt(m.getScaleX() * m.getScaleX() + m.getSkewX() * m.getSkewX()),
                   SkScalarSqrt(m.getScaleY() * m.getScaleY() + m.getSkewY() * m.getSkewY()));
     }
-    SkSize scaledSize = SkSize::Make(scale.x() * fPicture->width(), scale.y() * fPicture->height());
+    SkSize scaledSize = SkSize::Make(scale.x() * fTile.width(), scale.y() * fTile.height());
 
     SkISize tileSize = scaledSize.toRound();
     if (tileSize.isEmpty()) {
@@ -76,8 +82,8 @@
     }
 
     // The actual scale, compensating for rounding.
-    SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fPicture->width(),
-                                    SkIntToScalar(tileSize.height()) / fPicture->height());
+    SkSize tileScale = SkSize::Make(SkIntToScalar(tileSize.width()) / fTile.width(),
+                                    SkIntToScalar(tileSize.height()) / fTile.height());
 
     SkAutoMutexAcquire ama(fCachedBitmapShaderMutex);
 
@@ -90,6 +96,7 @@
 
         SkCanvas canvas(bm);
         canvas.scale(tileScale.width(), tileScale.height());
+        canvas.translate(fTile.x(), fTile.y());
         canvas.drawPicture(fPicture);
 
         fCachedTileScale = tileScale;
diff --git a/src/core/SkPictureShader.h b/src/core/SkPictureShader.h
index 00aee9b..99c70c9 100644
--- a/src/core/SkPictureShader.h
+++ b/src/core/SkPictureShader.h
@@ -21,7 +21,8 @@
  */
 class SkPictureShader : public SkShader {
 public:
-    static SkPictureShader* Create(const SkPicture*, TileMode, TileMode, const SkMatrix* = NULL);
+    static SkPictureShader* Create(const SkPicture*, TileMode, TileMode, const SkMatrix*,
+                                   const SkRect*);
     virtual ~SkPictureShader();
 
     virtual size_t contextSize() const SK_OVERRIDE;
@@ -38,11 +39,12 @@
     virtual Context* onCreateContext(const ContextRec&, void* storage) const SK_OVERRIDE;
 
 private:
-    SkPictureShader(const SkPicture*, TileMode, TileMode, const SkMatrix* = NULL);
+    SkPictureShader(const SkPicture*, TileMode, TileMode, const SkMatrix*, const SkRect*);
 
     SkShader* refBitmapShader(const SkMatrix&, const SkMatrix* localMatrix) const;
 
     const SkPicture*  fPicture;
+    SkRect            fTile;
     TileMode          fTmx, fTmy;
 
     mutable SkMutex                 fCachedBitmapShaderMutex;
diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp
index d533cd6..305cf06 100644
--- a/src/core/SkShader.cpp
+++ b/src/core/SkShader.cpp
@@ -228,8 +228,8 @@
 }
 
 SkShader* SkShader::CreatePictureShader(SkPicture* src, TileMode tmx, TileMode tmy,
-                                       const SkMatrix* localMatrix) {
-    return SkPictureShader::Create(src, tmx, tmy, localMatrix);
+                                        const SkMatrix* localMatrix, const SkRect* tile) {
+    return SkPictureShader::Create(src, tmx, tmy, localMatrix, tile);
 }
 
 #ifndef SK_IGNORE_TO_STRING