Tile large bitmaps that are clipped.
authorbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 24 Oct 2013 17:52:07 +0000 (17:52 +0000)
committerbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 24 Oct 2013 17:52:07 +0000 (17:52 +0000)
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

expectations/gm/ignored-tests.txt
gm/verylargebitmap.cpp
include/gpu/SkGpuDevice.h
src/gpu/SkGpuDevice.cpp

index 72c9d09..1fe8d07 100644 (file)
@@ -39,3 +39,6 @@ pdf-poppler
 
 # Added by bsalomon, test case added in https://codereview.chromium.org/30593003
 imagefiltersgraph
+
+# Added by bsalomon in https://codereview.chromium.org/31033002
+verylargebitmap
index d448d2d..a9ae46f 100644 (file)
@@ -7,18 +7,27 @@
 
 #include "gm.h"
 #include "SkCanvas.h"
+#include "SkGradientShader.h"
 #include "SkPath.h"
 
-static void make_bm(SkBitmap* bm, int width, int height, SkColor color) {
+static void make_bm(SkBitmap* bm, int width, int height, SkColor colors[2]) {
     bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
     bm->allocPixels();
-    bm->eraseColor(color);
+    SkCanvas canvas(*bm);
+    SkPoint center = {SkIntToScalar(width)/2, SkIntToScalar(height)/2};
+    SkScalar radius = 40;
+    SkShader* shader = SkGradientShader::CreateRadial(center, radius, colors, NULL, 2,
+                                                      SkShader::kMirror_TileMode);
+    SkPaint paint;
+    paint.setShader(shader)->unref();
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    canvas.drawPaint(paint);
     bm->setImmutable();
 }
 
-static void show_bm(SkCanvas* canvas, int width, int height, SkColor color) {
+static void show_bm(SkCanvas* canvas, int width, int height, SkColor colors[2]) {
     SkBitmap bm;
-    make_bm(&bm, width, height, color);
+    make_bm(&bm, width, height, colors);
 
     SkPaint paint;
     SkRect r;
@@ -56,24 +65,41 @@ protected:
     }
 
     virtual SkISize onISize() SK_OVERRIDE {
-        return SkISize::Make(640, 480);
+        return SkISize::Make(500, 600);
     }
 
     virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
         int veryBig = 70*1024; // 64K < size
-        int big = 60*1024;      // 32K < size < 64K
+        int big = 60*1024;     // 32K < size < 64K
+        // smaller than many max texture sizes, but large enough to gpu-tile for memory reasons.
+        int medium = 7*1024;
         int small = 150;
 
+        SkColor colors[2];
+
         canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
-        show_bm(canvas, small, small, SK_ColorRED);
+        colors[0] = SK_ColorRED;
+        colors[1] = SK_ColorGREEN;
+        show_bm(canvas, small, small, colors);
+        canvas->translate(0, SkIntToScalar(150));
+
+        colors[0] = SK_ColorBLUE;
+        colors[1] = SK_ColorMAGENTA;
+        show_bm(canvas, big, small, colors);
         canvas->translate(0, SkIntToScalar(150));
 
-        show_bm(canvas, big, small, SK_ColorBLUE);
+        colors[0] = SK_ColorMAGENTA;
+        colors[1] = SK_ColorYELLOW;
+        // as of this writing, the raster code will fail to draw the scaled version
+        // since it has a 64K limit on x,y coordinates... (but gpu should succeed)
+        show_bm(canvas, medium, medium, colors);
         canvas->translate(0, SkIntToScalar(150));
 
+        colors[0] = SK_ColorGREEN;
+        colors[1] = SK_ColorYELLOW;
         // as of this writing, the raster code will fail to draw the scaled version
         // since it has a 64K limit on x,y coordinates... (but gpu should succeed)
-        show_bm(canvas, veryBig, small, SK_ColorGREEN);
+        show_bm(canvas, veryBig, small, colors);
     }
 
 private:
index debfd37..81e0324 100644 (file)
@@ -188,7 +188,9 @@ private:
      */
     bool shouldTileBitmap(const SkBitmap& bitmap,
                           const GrTextureParams& sampler,
-                          const SkRect* srcRectPtr) const;
+                          const SkRect* srcRectPtr,
+                          int maxTileSize,
+                          int* tileSize) const;
     void internalDrawBitmap(const SkBitmap&,
                             const SkRect&,
                             const GrTextureParams& params,
@@ -198,7 +200,8 @@ private:
                          const SkRect& srcRect,
                          const GrTextureParams& params,
                          const SkPaint& paint,
-                         SkCanvas::DrawBitmapRectFlags flags);
+                         SkCanvas::DrawBitmapRectFlags flags,
+                         int tileSize);
 
     /**
      * Returns non-initialized instance.
index d80d0f5..db440c1 100644 (file)
@@ -978,68 +978,84 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
     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;
-
-    SkIRect iSrc;
-    src.roundOut(&iSrc);
-
-    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);
+    size_t maxTileTotalTileSize = get_tile_count(src, maxTileSize);
+    size_t smallTotalTileSize = get_tile_count(src, kBmpSmallTileSize);
 
-    maxTexTotalTileSize *= maxTextureSize * maxTextureSize;
-    smallTotalTileSize *= kSmallTileSize * kSmallTileSize;
+    maxTileTotalTileSize *= maxTileSize * maxTileSize;
+    smallTotalTileSize *= kBmpSmallTileSize * kBmpSmallTileSize;
 
-    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 @@ bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap,
         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 @@ void SkGpuDevice::drawBitmapCommon(const SkDraw& draw,
 
     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 @@ void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap,
                                   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;