Fix SkTileImageFilter when srcRect is a superset of bitmap bounds.
authorsenorblanco <senorblanco@chromium.org>
Mon, 11 Jan 2016 22:09:09 +0000 (14:09 -0800)
committerCommit bot <commit-bot@chromium.org>
Mon, 11 Jan 2016 22:09:09 +0000 (14:09 -0800)
If the input bitmap passed to SkTileImageFilter does not fill the
srcRect, we were tiling this incorrectly (see the first sample
from tileimage filter -- it draws from a srcRect of 12,12 50x50
to a dstRect of 0,0 50x50. There should be no tiling at all
in this case!)

In order to fix this, we need to pad the bitmap out to srcRect,
and tile with that. In order to tile correctly in the GPU case,
we need to request a tileable texture.

NOTE: this will change the results of the tileimagefilter GM (correctness,
and added src / dest rects).

BUG=skia:4774
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1570133003

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

gm/tileimagefilter.cpp
include/core/SkImageFilter.h
src/core/SkImageFilter.cpp
src/effects/SkTileImageFilter.cpp

index 10086c9c17ed734fd9abf113c9c15725b8d39eaa..eb0ad09f4be4cbfa6015532ee67ccd78d8b38e8e 100644 (file)
@@ -47,6 +47,12 @@ protected:
 
     void onDraw(SkCanvas* canvas) override {
         canvas->clear(SK_ColorBLACK);
+        SkPaint red;
+        red.setColor(SK_ColorRED);
+        red.setStyle(SkPaint::kStroke_Style);
+        SkPaint blue;
+        blue.setColor(SK_ColorBLUE);
+        blue.setStyle(SkPaint::kStroke_Style);
 
         int x = 0, y = 0;
         for (size_t i = 0; i < 4; i++) {
@@ -67,6 +73,8 @@ protected:
             SkPaint paint;
             paint.setImageFilter(filter);
             canvas->drawImage(fBitmap, 0, 0, &paint);
+            canvas->drawRect(srcRect, red);
+            canvas->drawRect(dstRect, blue);
             canvas->restore();
             x += image->width() + MARGIN;
             if (x + image->width() > WIDTH) {
@@ -96,6 +104,8 @@ protected:
         canvas->saveLayer(&dstRect, &paint);
         canvas->drawImage(fBitmap, 0, 0);
         canvas->restore();
+        canvas->drawRect(srcRect, red);
+        canvas->drawRect(dstRect, blue);
         canvas->restore();
     }
 private:
index 13fe6530c71306ab5bbbdb2f697c0482f9c7087d..eb5d2a1b062c25ae4e49487036f2a2d5bdc5895b 100644 (file)
@@ -98,11 +98,17 @@ public:
         uint32_t fFlags;
     };
 
+    enum TileUsage {
+        kPossible_TileUsage,    //!< the created device may be drawn tiled
+        kNever_TileUsage,       //!< the created device will never be drawn tiled
+    };
+
     class Proxy {
     public:
         virtual ~Proxy() {}
 
-        virtual SkBaseDevice* createDevice(int width, int height) = 0;
+        virtual SkBaseDevice* createDevice(int width, int height,
+                                           TileUsage usage = kNever_TileUsage) = 0;
 
         // Returns true if the proxy handled the filter itself. If this returns
         // false then the filter's code will be called.
@@ -115,7 +121,8 @@ public:
     public:
         DeviceProxy(SkBaseDevice* device) : fDevice(device) {}
 
-        SkBaseDevice* createDevice(int width, int height) override;
+        SkBaseDevice* createDevice(int width, int height,
+                                   TileUsage usage = kNever_TileUsage) override;
 
         // Returns true if the proxy handled the filter itself. If this returns
         // false then the filter's code will be called.
index e64cf7200aca22df518d245b4f97be45691db513..b068644d556c6fb42a8a9b0ef399bdc1ca8ddafa 100644 (file)
@@ -663,9 +663,10 @@ void SkImageFilter::PurgeCache() {
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-SkBaseDevice* SkImageFilter::DeviceProxy::createDevice(int w, int h) {
+SkBaseDevice* SkImageFilter::DeviceProxy::createDevice(int w, int h, TileUsage usage) {
     SkBaseDevice::CreateInfo cinfo(SkImageInfo::MakeN32Premul(w, h),
-                                   SkBaseDevice::kNever_TileUsage,
+                                   kPossible_TileUsage == usage ? SkBaseDevice::kPossible_TileUsage
+                                                                : SkBaseDevice::kNever_TileUsage,
                                    kUnknown_SkPixelGeometry,
                                    false,   /* preserveLCDText */
                                    true /*forImageFilter*/);
index 8d158013879a7bf23097c75a2ce241f49816bad6..ca4c15d4191900fc644cdb3961096bcb8b4bf125 100644 (file)
@@ -54,15 +54,31 @@ bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
     srcRect.roundOut(&srcIRect);
     srcIRect.offset(-srcOffset);
     SkBitmap subset;
-    SkIRect bounds;
-    source.getBounds(&bounds);
+    SkIRect srcBounds;
+    source.getBounds(&srcBounds);
 
-    if (!srcIRect.intersect(bounds)) {
+    if (!SkIRect::Intersects(srcIRect, srcBounds)) {
         offset->fX = offset->fY = 0;
         return true;
-    } else if (!source.extractSubset(&subset, srcIRect)) {
-        return false;
     }
+    if (srcBounds.contains(srcIRect)) {
+        if (!source.extractSubset(&subset, srcIRect)) {
+            return false;
+        }
+    } else {
+        SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(srcIRect.width(),
+                                                              srcIRect.height(),
+                                                              kPossible_TileUsage));
+        if (!device) {
+            return false;
+        }
+        SkCanvas canvas(device);
+        canvas.drawBitmap(src, SkIntToScalar(srcOffset.x()),
+                               SkIntToScalar(srcOffset.y()));
+        subset = device->accessBitmap(false);
+    }
+    SkASSERT(subset.width() == srcIRect.width());
+    SkASSERT(subset.height() == srcIRect.height());
 
     SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(w, h));
     if (nullptr == device.get()) {
@@ -72,12 +88,8 @@ bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
     SkPaint paint;
     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
 
-    SkMatrix shaderMatrix;
-    shaderMatrix.setTranslate(SkIntToScalar(srcOffset.fX),
-                              SkIntToScalar(srcOffset.fY));
     SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(subset,
-                                  SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode,
-                                  &shaderMatrix));
+                                  SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
     paint.setShader(shader);
     canvas.translate(-dstRect.fLeft, -dstRect.fTop);
     canvas.drawRect(dstRect, paint);