From af562b437e43a99f5371585ba50643b1d88f09e0 Mon Sep 17 00:00:00 2001 From: "bsalomon@google.com" Date: Thu, 24 Oct 2013 17:52:07 +0000 Subject: [PATCH] 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 --- expectations/gm/ignored-tests.txt | 3 + gm/verylargebitmap.cpp | 44 ++++++++++--- include/gpu/SkGpuDevice.h | 7 +- src/gpu/SkGpuDevice.cpp | 133 +++++++++++++++++++++----------------- 4 files changed, 118 insertions(+), 69 deletions(-) diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt index 72c9d09..1fe8d07 100644 --- a/expectations/gm/ignored-tests.txt +++ b/expectations/gm/ignored-tests.txt @@ -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 diff --git a/gm/verylargebitmap.cpp b/gm/verylargebitmap.cpp index d448d2d..a9ae46f 100644 --- a/gm/verylargebitmap.cpp +++ b/gm/verylargebitmap.cpp @@ -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: diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h index debfd37..81e0324 100644 --- a/include/gpu/SkGpuDevice.h +++ b/include/gpu/SkGpuDevice.h @@ -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. 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 @@ 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, ¶ms)) { 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; -- 2.7.4