Fix SkAlphaThresholdFilter bounds handling.
authorsenorblanco <senorblanco@chromium.org>
Tue, 19 Jan 2016 16:50:18 +0000 (08:50 -0800)
committerCommit bot <commit-bot@chromium.org>
Tue, 19 Jan 2016 16:50:18 +0000 (08:50 -0800)
SkAlphaThresholdFilter was always allocating a mask texture
of the same size as the source texture. In addition to
potentially wasting VRAM, this could cause the mask to be
offset from the source texture, if the resulting bounds
were a different size than the source texture.

The fix is to allocate a mask texture only as large as the
bounds, and to offset it to the bounds origin on draw.

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

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

gm/imagealphathreshold.cpp
src/effects/SkAlphaThresholdFilter.cpp

index 13a0ffa..ea7185b 100644 (file)
@@ -8,10 +8,39 @@
 #include "gm.h"
 #include "SkAlphaThresholdFilter.h"
 #include "SkRandom.h"
+#include "SkSurface.h"
 
 #define WIDTH 500
 #define HEIGHT 500
 
+namespace {
+
+void draw_rects(SkCanvas* canvas) {
+    SkPaint rectPaint;
+    rectPaint.setColor(0xFF0000FF);
+    canvas->drawRect(SkRect::MakeXYWH(0, 0, WIDTH / 2, HEIGHT / 2), rectPaint);
+    rectPaint.setColor(0xBFFF0000);
+    canvas->drawRect(SkRect::MakeXYWH(WIDTH / 2, 0, WIDTH / 2, HEIGHT / 2), rectPaint);
+    rectPaint.setColor(0x3F00FF00);
+    canvas->drawRect(SkRect::MakeXYWH(0, HEIGHT / 2, WIDTH / 2, HEIGHT / 2), rectPaint);
+    rectPaint.setColor(0x00000000);
+    canvas->drawRect(SkRect::MakeXYWH(WIDTH / 2, HEIGHT / 2, WIDTH / 2, HEIGHT / 2), rectPaint);
+}
+
+SkPaint create_filter_paint() {
+    SkIRect rects[2];
+    rects[0] = SkIRect::MakeXYWH(0, 150, WIDTH, HEIGHT - 300);
+    rects[1] = SkIRect::MakeXYWH(150, 0, WIDTH - 300, HEIGHT);
+    SkRegion region;
+    region.setRects(rects, 2);
+
+    SkPaint paint;
+    paint.setImageFilter(SkAlphaThresholdFilter::Create(region, 0.2f, 0.7f))->unref();
+    return paint;
+}
+
+};
+
 namespace skiagm {
 
 class ImageAlphaThresholdGM : public GM {
@@ -31,12 +60,6 @@ protected:
     }
 
     void onDraw(SkCanvas* canvas) override {
-        SkIRect rects[2];
-        rects[0] = SkIRect::MakeXYWH(0, 150, WIDTH, HEIGHT - 300);
-        rects[1] = SkIRect::MakeXYWH(150, 0, WIDTH - 300, HEIGHT);
-        SkRegion region;
-        region.setRects(rects, 2);
-
         SkMatrix matrix;
         matrix.reset();
         matrix.setTranslate(WIDTH * .1f, HEIGHT * .1f);
@@ -44,25 +67,9 @@ protected:
 
         canvas->concat(matrix);
 
-        SkPaint paint;
-        paint.setImageFilter(
-            SkAlphaThresholdFilter::Create(region, 0.2f, 0.7f))->unref();
+        SkPaint paint = create_filter_paint();
         canvas->saveLayer(nullptr, &paint);
-        paint.setAntiAlias(true);
-
-        SkPaint rect_paint;
-        rect_paint.setColor(0xFF0000FF);
-        canvas->drawRect(SkRect::MakeXYWH(0, 0, WIDTH / 2, HEIGHT / 2),
-                         rect_paint);
-        rect_paint.setColor(0xBFFF0000);
-        canvas->drawRect(SkRect::MakeXYWH(WIDTH / 2, 0, WIDTH / 2, HEIGHT / 2),
-                         rect_paint);
-        rect_paint.setColor(0x3F00FF00);
-        canvas->drawRect(SkRect::MakeXYWH(0, HEIGHT / 2, WIDTH / 2, HEIGHT / 2),
-                         rect_paint);
-        rect_paint.setColor(0x00000000);
-        canvas->drawRect(SkRect::MakeXYWH(WIDTH / 2, HEIGHT / 2, WIDTH / 2, HEIGHT / 2),
-                         rect_paint);
+        draw_rects(canvas);
 
         canvas->restore();
     }
@@ -71,9 +78,43 @@ private:
     typedef GM INHERITED;
 };
 
+class ImageAlphaThresholdSurfaceGM : public GM {
+public:
+    ImageAlphaThresholdSurfaceGM() {
+        this->setBGColor(0xFFFFFFFF);
+    }
+
+protected:
+    SkString onShortName() override {
+        return SkString("imagealphathreshold_surface");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(WIDTH, HEIGHT);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        SkImageInfo info = SkImageInfo::MakeN32(WIDTH, HEIGHT, kOpaque_SkAlphaType);
+        SkAutoTUnref<SkSurface> surface(canvas->newSurface(info));
+        if (nullptr == surface) {
+            surface.reset(SkSurface::NewRaster(info));
+        }
+        surface->getCanvas()->clear(SK_ColorWHITE);
+        draw_rects(surface->getCanvas());
+
+        SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
+        SkPaint paint = create_filter_paint();
+        canvas->clipRect(SkRect::MakeLTRB(100, 100, WIDTH - 100, HEIGHT - 100));
+        canvas->drawImage(image, 0, 0, &paint);
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
 //////////////////////////////////////////////////////////////////////////////
 
-static GM* MyFactory(void*) { return new ImageAlphaThresholdGM; }
-static GMRegistry reg(MyFactory);
+DEF_GM(return new ImageAlphaThresholdGM();)
+DEF_GM(return new ImageAlphaThresholdSurfaceGM();)
 
 }
index b6be944..6cadbc3 100644 (file)
@@ -68,14 +68,26 @@ SkImageFilter* SkAlphaThresholdFilter::Create(const SkRegion& region,
 #include "glsl/GrGLSLProgramDataManager.h"
 #include "glsl/GrGLSLUniformHandler.h"
 
+namespace {
+
+SkMatrix make_div_and_translate_matrix(GrTexture* texture, int x, int y) {
+    SkMatrix matrix = GrCoordTransform::MakeDivByTextureWHMatrix(texture);
+    matrix.preTranslate(SkIntToScalar(x), SkIntToScalar(y));
+    return matrix;
+}
+
+};
+
 class AlphaThresholdEffect : public GrFragmentProcessor {
 
 public:
     static GrFragmentProcessor* Create(GrTexture* texture,
                                        GrTexture* maskTexture,
                                        float innerThreshold,
-                                       float outerThreshold) {
-        return new AlphaThresholdEffect(texture, maskTexture, innerThreshold, outerThreshold);
+                                       float outerThreshold,
+                                       const SkIRect& bounds) {
+        return new AlphaThresholdEffect(texture, maskTexture, innerThreshold, outerThreshold,
+                                        bounds);
     }
 
     virtual ~AlphaThresholdEffect() {};
@@ -89,7 +101,8 @@ private:
     AlphaThresholdEffect(GrTexture* texture,
                          GrTexture* maskTexture,
                          float innerThreshold,
-                         float outerThreshold)
+                         float outerThreshold,
+                         const SkIRect& bounds)
         : fInnerThreshold(innerThreshold)
         , fOuterThreshold(outerThreshold)
         , fImageCoordTransform(kLocal_GrCoordSet,
@@ -97,7 +110,8 @@ private:
                                GrTextureParams::kNone_FilterMode)
         , fImageTextureAccess(texture)
         , fMaskCoordTransform(kLocal_GrCoordSet,
-                              GrCoordTransform::MakeDivByTextureWHMatrix(maskTexture), maskTexture,
+                              make_div_and_translate_matrix(maskTexture, -bounds.x(), -bounds.y()),
+                              maskTexture,
                               GrTextureParams::kNone_FilterMode)
         , fMaskTextureAccess(maskTexture) {
         this->initClassID<AlphaThresholdEffect>();
@@ -205,7 +219,14 @@ const GrFragmentProcessor* AlphaThresholdEffect::TestCreate(GrProcessorTestData*
     GrTexture* maskTex = d->fTextures[GrProcessorUnitTest::kAlphaTextureIdx];
     float innerThresh = d->fRandom->nextUScalar1();
     float outerThresh = d->fRandom->nextUScalar1();
-    return AlphaThresholdEffect::Create(bmpTex, maskTex, innerThresh, outerThresh);
+    const int kMaxWidth = 1000;
+    const int kMaxHeight = 1000;
+    uint32_t width = d->fRandom->nextULessThan(kMaxWidth);
+    uint32_t height = d->fRandom->nextULessThan(kMaxHeight);
+    uint32_t x = d->fRandom->nextULessThan(kMaxWidth - width);
+    uint32_t y = d->fRandom->nextULessThan(kMaxHeight - height);
+    SkIRect bounds = SkIRect::MakeXYWH(x, y, width, height);
+    return AlphaThresholdEffect::Create(bmpTex, maskTex, innerThresh, outerThresh, bounds);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -260,7 +281,7 @@ SkAlphaThresholdFilterImpl::SkAlphaThresholdFilterImpl(const SkRegion& region,
 bool SkAlphaThresholdFilterImpl::asFragmentProcessor(GrFragmentProcessor** fp,
                                                      GrTexture* texture,
                                                      const SkMatrix& inMatrix,
-                                                     const SkIRect&) const {
+                                                     const SkIRect& bounds) const {
     if (fp) {
         GrContext* context = texture->getContext();
         GrSurfaceDesc maskDesc;
@@ -272,8 +293,8 @@ bool SkAlphaThresholdFilterImpl::asFragmentProcessor(GrFragmentProcessor** fp,
         maskDesc.fFlags = kRenderTarget_GrSurfaceFlag;
         // Add one pixel of border to ensure that clamp mode will be all zeros
         // the outside.
-        maskDesc.fWidth = texture->width();
-        maskDesc.fHeight = texture->height();
+        maskDesc.fWidth = bounds.width();
+        maskDesc.fHeight = bounds.height();
         SkAutoTUnref<GrTexture> maskTexture(
             context->textureProvider()->createApproxTexture(maskDesc));
         if (!maskTexture) {
@@ -288,9 +309,10 @@ bool SkAlphaThresholdFilterImpl::asFragmentProcessor(GrFragmentProcessor** fp,
             SkRegion::Iterator iter(fRegion);
             drawContext->clear(nullptr, 0x0, true);
 
+            GrClip clip(SkRect::Make(SkIRect::MakeWH(bounds.width(), bounds.height())));
             while (!iter.done()) {
                 SkRect rect = SkRect::Make(iter.rect());
-                drawContext->drawRect(GrClip::WideOpen(), grPaint, inMatrix, rect);
+                drawContext->drawRect(clip, grPaint, inMatrix, rect);
                 iter.next();
             }
         }
@@ -298,7 +320,8 @@ bool SkAlphaThresholdFilterImpl::asFragmentProcessor(GrFragmentProcessor** fp,
         *fp = AlphaThresholdEffect::Create(texture,
                                            maskTexture,
                                            fInnerThreshold,
-                                           fOuterThreshold);
+                                           fOuterThreshold,
+                                           bounds);
     }
     return true;
 }