Tile large bitmaps that are clipped.

R=robertphillips@google.com

Review URL: https://codereview.chromium.org/31033002

git-svn-id: http://skia.googlecode.com/svn/trunk@11951 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index d80d0f5..db440c1 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -978,68 +978,84 @@
     fContext->drawPath(grPaint, *pathPtr, stroke);
 }
 
-namespace {
+static const int kBmpSmallTileSize = 1 << 10;
 
-inline int get_tile_count(int l, int t, int r, int b, int tileSize)  {
-    int tilesX = (r / tileSize) - (l / tileSize) + 1;
-    int tilesY = (b / tileSize) - (t / tileSize) + 1;
+static inline int get_tile_count(const SkIRect& srcRect, int tileSize)  {
+    int tilesX = (srcRect.fRight / tileSize) - (srcRect.fLeft / tileSize) + 1;
+    int tilesY = (srcRect.fBottom / tileSize) - (srcRect.fTop / tileSize) + 1;
     return tilesX * tilesY;
 }
 
-inline int determine_tile_size(const SkBitmap& bitmap,
-                               const SkRect& src,
-                               int maxTextureSize) {
-    static const int kSmallTileSize = 1 << 10;
-    if (maxTextureSize <= kSmallTileSize) {
-        return maxTextureSize;
+static int determine_tile_size(const SkBitmap& bitmap, const SkIRect& src, int maxTileSize) {
+    if (maxTileSize <= kBmpSmallTileSize) {
+        return maxTileSize;
     }
 
-    size_t maxTexTotalTileSize;
-    size_t smallTotalTileSize;
+    size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize);
+    size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize);
 
-    SkIRect iSrc;
-    src.roundOut(&iSrc);
+    maxTileTotalTileSize *= maxTileSize * maxTileSize;
+    smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize;
 
-    maxTexTotalTileSize = get_tile_count(iSrc.fLeft,
-                                         iSrc.fTop,
-                                         iSrc.fRight,
-                                         iSrc.fBottom,
-                                         maxTextureSize);
-    smallTotalTileSize = get_tile_count(iSrc.fLeft,
-                                        iSrc.fTop,
-                                        iSrc.fRight,
-                                        iSrc.fBottom,
-                                        kSmallTileSize);
-
-    maxTexTotalTileSize *= maxTextureSize * maxTextureSize;
-    smallTotalTileSize *= kSmallTileSize * kSmallTileSize;
-
-    if (maxTexTotalTileSize > 2 * smallTotalTileSize) {
-        return kSmallTileSize;
+    if (maxTileTotalTileSize > 2 * smallTotalTileSize) {
+        return kBmpSmallTileSize;
     } else {
-        return maxTextureSize;
+        return maxTileSize;
     }
 }
+
+// Given a bitmap, an optional src rect, and a context with a clip and matrix determine what
+// pixels from the bitmap are necessary.
+static void determine_clipped_src_rect(const GrContext* context,
+                                       const SkBitmap& bitmap,
+                                       const SkRect* srcRectPtr,
+                                       SkIRect* clippedSrcIRect) {
+    const GrClipData* clip = context->getClip();
+    clip->getConservativeBounds(context->getRenderTarget(), clippedSrcIRect, NULL);
+    SkMatrix inv;
+    if (!context->getMatrix().invert(&inv)) {
+        clippedSrcIRect->setEmpty();
+        return;
+    }
+    SkRect clippedSrcRect = SkRect::MakeFromIRect(*clippedSrcIRect);
+    inv.mapRect(&clippedSrcRect);
+    if (NULL != srcRectPtr) {
+        if (!clippedSrcRect.intersect(*srcRectPtr)) {
+            clippedSrcIRect->setEmpty();
+            return;
+        }
+    }
+    clippedSrcRect.roundOut(clippedSrcIRect);
+    SkIRect bmpBounds = SkIRect::MakeWH(bitmap.width(), bitmap.height());
+    if (!clippedSrcIRect->intersect(bmpBounds)) {
+        clippedSrcIRect->setEmpty();
+    }
 }
 
+
 bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap,
                                    const GrTextureParams& params,
-                                   const SkRect* srcRectPtr) const {
+                                   const SkRect* srcRectPtr,
+                                   int maxTileSize,
+                                   int* tileSize) const {
     // if bitmap is explictly texture backed then just use the texture
     if (NULL != bitmap.getTexture()) {
         return false;
     }
-    // if it's larger than the max texture size, then we have no choice but
-    // tiling
-    const int maxTextureSize = fContext->getMaxTextureSize();
-    if (bitmap.width() > maxTextureSize ||
-        bitmap.height() > maxTextureSize) {
+
+    SkIRect clippedSrcRect;
+
+    // if it's larger than the max tile size, then we have no choice but tiling.
+    if (bitmap.width() > maxTileSize || bitmap.height() > maxTileSize) {
+        determine_clipped_src_rect(fContext, bitmap, srcRectPtr, &clippedSrcRect);
+        *tileSize = determine_tile_size(bitmap, clippedSrcRect, maxTileSize);
         return true;
     }
-    // if we are going to have to draw the whole thing, then don't tile
-    if (NULL == srcRectPtr) {
+
+    if (bitmap.width() * bitmap.height() < 4 * kBmpSmallTileSize * kBmpSmallTileSize) {
         return false;
     }
+
     // if the entire texture is already in our cache then no reason to tile it
     if (GrIsBitmapInCache(fContext, bitmap, &params)) {
         return false;
@@ -1059,13 +1075,13 @@
         return false;
     }
 
-    SkScalar fracUsed = SkScalarMul(srcRectPtr->width() / bitmap.width(),
-                                    srcRectPtr->height() / bitmap.height());
-    if (fracUsed <= SK_ScalarHalf) {
-        return true;
-    } else {
-        return false;
-    }
+    // Figure out how much of the src we will need based on the src rect and clipping.
+    determine_clipped_src_rect(fContext, bitmap, srcRectPtr, &clippedSrcRect);
+    *tileSize = kBmpSmallTileSize; // already know whole bitmap fits in one max sized tile.
+    size_t usedTileBytes = get_tile_count(clippedSrcRect, kBmpSmallTileSize) *
+                           kBmpSmallTileSize * kBmpSmallTileSize;
+
+    return usedTileBytes < 2 * bmpSize;
 }
 
 void SkGpuDevice::drawBitmap(const SkDraw& draw,
@@ -1195,11 +1211,19 @@
 
     params.setFilterMode(textureFilterMode);
 
-    if (!this->shouldTileBitmap(bitmap, params, srcRectPtr)) {
+    int maxTileSize = fContext->getMaxTextureSize();
+    if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) {
+        // We may need a skosh more room if we have to bump out the tile
+        // by 1 pixel all around
+        maxTileSize -= 2;
+    }
+    int tileSize;
+
+    if (this->shouldTileBitmap(bitmap, params, srcRectPtr, maxTileSize, &tileSize)) {
+        this->drawTiledBitmap(bitmap, srcRect, params, paint, flags, tileSize);
+    } else {
         // take the simple case
         this->internalDrawBitmap(bitmap, srcRect, params, paint, flags);
-    } else {
-        this->drawTiledBitmap(bitmap, srcRect, params, paint, flags);
     }
 }
 
@@ -1209,15 +1233,8 @@
                                   const SkRect& srcRect,
                                   const GrTextureParams& params,
                                   const SkPaint& paint,
-                                  SkCanvas::DrawBitmapRectFlags flags) {
-    int maxTextureSize = fContext->getMaxTextureSize();
-    if (SkPaint::kNone_FilterLevel != paint.getFilterLevel()) {
-        // We may need a skosh more room if we have to bump out the tile
-        // by 1 pixel all around
-        maxTextureSize -= 2;
-    }
-
-    int tileSize = determine_tile_size(bitmap, srcRect, maxTextureSize);
+                                  SkCanvas::DrawBitmapRectFlags flags,
+                                  int tileSize) {
 
     // compute clip bounds in local coordinates
     SkRect clipRect;