detect when we can filter bitmaps/images directly, w/o a tmp layer
authorreed <reed@google.com>
Sat, 5 Dec 2015 21:07:27 +0000 (13:07 -0800)
committerCommit bot <commit-bot@chromium.org>
Sat, 5 Dec 2015 21:07:27 +0000 (13:07 -0800)
visual bench run on Mac Pro

curr/maxrss loops min median mean max stddev samples    config bench
 100/100 MB 16 412µs 413µs 413µs 414µs 0% ▄▁▇▄▄▄▄█▄▃▅ gpu warmupbench
 101/102 MB 32 547µs 548µs 611µs 1.24ms 34% █▁▁▁▁▁▁▁▁▁▁ gpu image-filter-sprite-draw-image
 102/103 MB 32 547µs 548µs 721µs 1.23ms 41% █▁▇▁▁█▁▁▁▁▁ gpu image-filter-sprite-draw-bitmap
 103/103 MB 64 546µs 546µs 546µs 547µs 0% ▆▄▂▁▇█▅▇▅▇▃ gpu image-filter-sprite-draw-sprite

Should have no effect on Chrome while SK_SUPPORT_LEGACY_LAYER_BITMAP_IMAGEFILTERS is defined (which it is in chrome)

BUG=skia:1073

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

include/core/SkCanvas.h
include/core/SkDevice.h
src/core/SkCanvas.cpp
src/core/SkDevice.cpp
src/image/SkImage_Base.h
src/image/SkImage_Gpu.cpp
src/image/SkImage_Gpu.h

index 53f6dda88b090c5734a7e7104925ae0749bb76d9..cd6421beb184c75c7879aac2ecc9e10e1504a6e1 100644 (file)
@@ -1428,6 +1428,10 @@ private:
      */
     bool wouldOverwriteEntireSurface(const SkRect*, const SkPaint*, ShaderOverrideOpacity) const;
 
+    /**
+     *  Returns true if the paint's imagefilter can be invoked directly, without needed a layer.
+     */
+    bool canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint&);
 
     /*  These maintain a cache of the clip bounds in local coordinates,
         (converted to 2s-compliment if floats are slow).
index c52c579424a2ad7e450b501585052d0aa8eea050..a7a3895db4fe81936735fed1d41e825fc0a4612c 100644 (file)
@@ -374,9 +374,13 @@ private:
     friend class SkDeviceFilteredPaint;
     friend class SkImageFilter::DeviceProxy;
     friend class SkNoPixelsBitmapDevice;
-
     friend class SkSurface_Raster;
 
+    /**
+     *  Calls through to drawSprite, processing imagefilter as needed.
+     */
+    void drawBitmapAsSprite(const SkDraw&, const SkBitmap&, int x, int y, const SkPaint&);
+
     // used to change the backend's pixels (and possibly config/rowbytes)
     // but cannot change the width/height, so there should be no change to
     // any clip information.
index 1e49fe272df04a9b8af1e8afd50cb7642488c7c5..88dddf412a7ae663c1e34d343a8ddd953f8ca1dc 100644 (file)
@@ -17,6 +17,8 @@
 #include "SkDrawLooper.h"
 #include "SkErrorInternals.h"
 #include "SkImage.h"
+#include "SkImage_Base.h"
+#include "SkMatrixUtils.h"
 #include "SkMetaData.h"
 #include "SkNinePatchIter.h"
 #include "SkPaintPriv.h"
@@ -592,6 +594,13 @@ bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
 
 ////////// macros to place around the internal draw calls //////////////////
 
+#define LOOPER_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds)          \
+    this->predrawNotify();                                                  \
+    AutoDrawLooper looper(this, fProps, paint, skipLayerForFilter, bounds); \
+    while (looper.next(SkDrawFilter::kBitmap_Type)) {                       \
+        SkDrawIter iter(this);
+
+
 #define LOOPER_BEGIN_DRAWDEVICE(paint, type)                        \
     this->predrawNotify();                                          \
     AutoDrawLooper  looper(this, fProps, paint, true);              \
@@ -1404,28 +1413,8 @@ void SkCanvas::onDrawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint*
     LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
 
     while (iter.next()) {
-        paint = &looper.paint();
-        SkImageFilter* filter = paint->getImageFilter();
-        SkIPoint pos = { x - iter.getX(), y - iter.getY() };
-        if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
-            SkImageFilter::DeviceProxy proxy(iter.fDevice);
-            SkBitmap dst;
-            SkIPoint offset = SkIPoint::Make(0, 0);
-            SkMatrix matrix = *iter.fMatrix;
-            matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y()));
-            const SkIRect clipBounds = bitmap.bounds();
-            SkAutoTUnref<SkImageFilter::Cache> cache(iter.fDevice->getImageFilterCache());
-            SkImageFilter::Context ctx(matrix, clipBounds, cache.get(),
-                                       SkImageFilter::kApprox_SizeConstraint);
-            if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
-                SkPaint tmpUnfiltered(*paint);
-                tmpUnfiltered.setImageFilter(nullptr);
-                iter.fDevice->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(),
-                                         tmpUnfiltered);
-            }
-        } else {
-            iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
-        }
+        const SkIPoint pos = {  x - iter.getX(), y - iter.getY() };
+        iter.fDevice->drawBitmapAsSprite(iter, bitmap, pos.x(), pos.y(), looper.paint());
     }
     LOOPER_END
 }
@@ -2197,6 +2186,32 @@ void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
     LOOPER_END
 }
 
+bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
+#ifdef SK_SUPPORT_LEGACY_LAYER_BITMAP_IMAGEFILTERS
+    return false;
+#endif
+
+    if (!paint.getImageFilter()) {
+        return false;
+    }
+
+    const SkMatrix& ctm = this->getTotalMatrix();
+    const unsigned kSubpixelBits = 0;   // matching SkDraw::drawBitmap()
+    if (!SkTreatAsSprite(ctm, w, h, kSubpixelBits)) {
+        return false;
+    }
+
+    // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds.
+    // Once we can filter and the filter will return a result larger than itself, we should be
+    // able to remove this constraint.
+    // skbug.com/4526
+    //
+    SkPoint pt;
+    ctm.mapXY(x, y, &pt);
+    SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h);
+    return ir.contains(fMCRec->fRasterClip.getBounds());
+}
+
 void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
     TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
     SkRect bounds = SkRect::MakeXYWH(x, y,
@@ -2215,11 +2230,25 @@ void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const S
     if (nullptr == paint) {
         paint = lazy.init();
     }
-    
-    LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &bounds)
-    
+
+    const bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(),
+                                                          *paint);
+    LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds)
+
     while (iter.next()) {
-        iter.fDevice->drawImage(iter, image, x, y, looper.paint());
+        const SkPaint& pnt = looper.paint();
+        if (drawAsSprite && pnt.getImageFilter()) {
+            SkBitmap bitmap;
+            if (as_IB(image)->asBitmapForImageFilters(&bitmap)) {
+                SkPoint pt;
+                iter.fMatrix->mapXY(x, y, &pt);
+                iter.fDevice->drawBitmapAsSprite(iter, bitmap,
+                                                 SkScalarRoundToInt(pt.fX),
+                                                 SkScalarRoundToInt(pt.fY), pnt);
+            }
+        } else {
+            iter.fDevice->drawImage(iter, image, x, y, pnt);
+        }
     }
     
     LOOPER_END
@@ -2281,12 +2310,23 @@ void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, cons
         bounds = &storage;
     }
 
-    LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
+    const bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(),
+                                                                    bitmap.height(), *paint);
+    LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds)
 
     while (iter.next()) {
-        iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
+        const SkPaint& pnt = looper.paint();
+        if (drawAsSprite && pnt.getImageFilter()) {
+            SkPoint pt;
+            iter.fMatrix->mapXY(x, y, &pt);
+            iter.fDevice->drawBitmapAsSprite(iter, bitmap,
+                                             SkScalarRoundToInt(pt.fX),
+                                             SkScalarRoundToInt(pt.fY), pnt);
+        } else {
+            iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint());
+        }
     }
-    
+
     LOOPER_END
 }
 
index 34c171db9071109cb668619964b037dbee95ad21..cf4a279b355523be1a982af8a989e014beaf7938 100644 (file)
@@ -402,6 +402,29 @@ void SkBaseDevice::drawTextOnPath(const SkDraw& draw, const void* text, size_t b
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
+void SkBaseDevice::drawBitmapAsSprite(const SkDraw& draw, const SkBitmap& bitmap, int x, int y,
+                                      const SkPaint& paint) {
+    SkImageFilter* filter = paint.getImageFilter();
+    if (filter && !this->canHandleImageFilter(filter)) {
+        SkImageFilter::DeviceProxy proxy(this);
+        SkBitmap dst;
+        SkIPoint offset = SkIPoint::Make(0, 0);
+        SkMatrix matrix = *draw.fMatrix;
+        matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
+        const SkIRect clipBounds = bitmap.bounds();
+        SkAutoTUnref<SkImageFilter::Cache> cache(this->getImageFilterCache());
+        SkImageFilter::Context ctx(matrix, clipBounds, cache.get(),
+                                   SkImageFilter::kApprox_SizeConstraint);
+        if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) {
+            SkPaint tmpUnfiltered(paint);
+            tmpUnfiltered.setImageFilter(nullptr);
+            this->drawSprite(draw, dst, x + offset.x(), y + offset.y(), tmpUnfiltered);
+        }
+    } else {
+        this->drawSprite(draw, bitmap, x, y, paint);
+    }
+}
+
 uint32_t SkBaseDevice::filterTextFlags(const SkPaint& paint) const {
     uint32_t flags = paint.getFlags();
 
index c91430e5bd4ca88b9dfcbb414bb09ec85ea6cb29..5cb6a48132c997746623c58bcba5dcbad565076f 100644 (file)
@@ -55,6 +55,12 @@ public:
 
     virtual bool onIsLazyGenerated() const { return false; }
 
+    // Return a bitmap suitable for passing to image-filters
+    // For now, that means wrapping textures into SkGrPixelRefs...
+    virtual bool asBitmapForImageFilters(SkBitmap* bitmap) const {
+        return this->getROPixels(bitmap, kAllow_CachingHint);
+    }
+
     // Call when this image is part of the key to a resourcecache entry. This allows the cache
     // to know automatically those entries can be purged when this SkImage deleted.
     void notifyAddedToCache() const {
index 346f7983b71eff2838ebf132c44111554fd24048..55a82bba8701b86e059fd7283f73941bf55d4911 100644 (file)
@@ -5,16 +5,18 @@
  * found in the LICENSE file.
  */
 
-#include "SkBitmapCache.h"
-#include "SkImage_Gpu.h"
 #include "GrCaps.h"
 #include "GrContext.h"
 #include "GrDrawContext.h"
 #include "GrTextureParamsAdjuster.h"
 #include "effects/GrYUVtoRGBEffect.h"
 #include "SkCanvas.h"
+#include "SkBitmapCache.h"
 #include "SkGpuDevice.h"
+#include "SkGrPixelRef.h"
 #include "SkGrPriv.h"
+#include "SkImageFilter.h"
+#include "SkImage_Gpu.h"
 #include "SkPixelRef.h"
 
 SkImage_Gpu::SkImage_Gpu(int w, int h, uint32_t uniqueID, SkAlphaType at, GrTexture* tex,
@@ -69,6 +71,13 @@ bool SkImage_Gpu::getROPixels(SkBitmap* dst, CachingHint chint) const {
     return true;
 }
 
+bool SkImage_Gpu::asBitmapForImageFilters(SkBitmap* bitmap) const {
+    bitmap->setInfo(make_info(this->width(), this->height(), this->isOpaque()));
+    bitmap->setPixelRef(new SkGrPixelRef(bitmap->info(), fTexture))->unref();
+    bitmap->pixelRef()->setImmutableWithID(this->uniqueID());
+    return true;
+}
+
 class GpuImage_GrTextureAdjuster : public GrTextureAdjuster {
 public:
     GpuImage_GrTextureAdjuster(const SkImage_Gpu* image)
@@ -166,9 +175,6 @@ SkImage* SkImage_Gpu::onNewSubset(const SkIRect& subset) const {
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-#include "SkGrPixelRef.h"
-#include "SkImageFilter.h"
-
 class SkGpuImageFilterProxy : public SkImageFilter::Proxy {
     GrContext* fCtx;
 
index ff3b12e668d4170f6553dc662afc627d759c1860..bd8abd24e234b51b563f65663aa7c9f53fe541ef 100644 (file)
@@ -50,6 +50,8 @@ public:
         return SkSurface::NewRenderTarget(fTexture->getContext(), SkSurface::kNo_Budgeted, info);
     }
 
+    bool asBitmapForImageFilters(SkBitmap* bitmap) const override;
+
 private:
     SkAutoTUnref<GrTexture>     fTexture;
     const SkAlphaType           fAlphaType;