Reimplement drawBitmapRectToRect to correctly handle fraction srcRect.
authorreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 25 Sep 2012 15:37:50 +0000 (15:37 +0000)
committerreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 25 Sep 2012 15:37:50 +0000 (15:37 +0000)
The prev impl relied on drawBitmap "deducing" the destination rect by applying
the computed matrix to the bitmap's bounds. This cannot be done if the srcRect
is fractional, and therefore not representable w/ a bitmap.

The new impl computes the same matrix, but calls down to the device via drawRect
+ a bitmap_shader. This allows us to specfiy the dstRect explicitly.

The possible down-side is that we now rely on the device subclass to efficiently
handle draRect+shader, instead of calling its drawBitmap entry-point.

To give the device the chance to handle this differently, I now call through to
a new device virtual: drawBitmapRect. The default impl is to create the shader
and call drawRect, but a subclass can intercept that.

For now, the GPU override of drawBitmapRect is mimicing the old behavior (by
rounding the srcRect to an iRect). This preserves its ability to call drawBitmap
which handles very-large textures, but shows some gittering/imprecision, due to
the rounding. ... this is the same GPU behavior we have before this CL.
Review URL: https://codereview.appspot.com/6542065

git-svn-id: http://skia.googlecode.com/svn/trunk@5663 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkDevice.h
include/gpu/SkGpuDevice.h
samplecode/SampleBitmapRect.cpp
src/core/SkCanvas.cpp
src/core/SkDevice.cpp
src/gpu/SkGpuDevice.cpp

index da7842b..851a167 100644 (file)
@@ -249,6 +249,15 @@ protected:
                             const SkMatrix& matrix, const SkPaint& paint);
     virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
                             int x, int y, const SkPaint& paint);
+
+    /**
+     *  The default impl. will create a bitmap-shader from the bitmap,
+     *  and call drawRect with it.
+     */
+    virtual void drawBitmapRect(const SkDraw&, const SkBitmap&,
+                                const SkRect* srcOrNull, const SkRect& dst,
+                                const SkPaint& paint);
+
     /**
      *  Does not handle text decoration.
      *  Decorations (underline and stike-thru) will be handled by SkCanvas.
index 04c4e0f..45ae05a 100644 (file)
@@ -79,6 +79,9 @@ public:
     virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
                             const SkIRect* srcRectOrNull,
                             const SkMatrix&, const SkPaint&) SK_OVERRIDE;
+    virtual void drawBitmapRect(const SkDraw&, const SkBitmap&,
+                                const SkRect* srcOrNull, const SkRect& dst,
+                                const SkPaint& paint) SK_OVERRIDE;
     virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
                             int x, int y, const SkPaint& paint);
     virtual void drawText(const SkDraw&, const void* text, size_t len,
index ba7efbf..18208fb 100644 (file)
 class GrContext;
 #endif
 
+#define INT_SIZE        64
+#define SCALAR_SIZE     SkIntToScalar(INT_SIZE)
 
-static void make_bitmap(SkBitmap* bitmap, GrContext* ctx) {
-    SkCanvas canvas;
-
-#if SK_SUPPORT_GPU
-    if (ctx) {
-        SkDevice* dev = new SkGpuDevice(ctx, SkBitmap::kARGB_8888_Config, 64, 64);
-        canvas.setDevice(dev)->unref();
-        *bitmap = dev->accessBitmap(false);
-    } else
-#endif
-    {
-        bitmap->setConfig(SkBitmap::kARGB_8888_Config, 64, 64);
-        bitmap->allocPixels();
-        canvas.setBitmapDevice(*bitmap);
-    }
+static void make_bitmap(SkBitmap* bitmap) {
+    bitmap->setConfig(SkBitmap::kARGB_8888_Config, INT_SIZE, INT_SIZE);
+    bitmap->allocPixels();
+    SkCanvas canvas(*bitmap);
 
     canvas.drawColor(SK_ColorRED);
     SkPaint paint;
     paint.setAntiAlias(true);
-    const SkPoint pts[] = { { 0, 0 }, { 64, 64 } };
+    const SkPoint pts[] = { { 0, 0 }, { SCALAR_SIZE, SCALAR_SIZE } };
     const SkColor colors[] = { SK_ColorWHITE, SK_ColorBLUE };
     paint.setShader(SkGradientShader::CreateLinear(pts, colors, NULL, 2,
                                                    SkShader::kClamp_TileMode))->unref();
-    canvas.drawCircle(32, 32, 32, paint);
+    canvas.drawCircle(SCALAR_SIZE/2, SCALAR_SIZE/2, SCALAR_SIZE/2, paint);
+}
+
+static SkPoint unit_vec(int degrees) {
+    SkScalar rad = SkDegreesToRadians(SkIntToScalar(degrees));
+    SkScalar s, c;
+    s = SkScalarSinCos(rad, &c);
+    return SkPoint::Make(c, s);
+}
+
+static void bounce(SkScalar* value, SkScalar* delta, SkScalar min, SkScalar max) {
+    *value += *delta;
+    if (*value < min) {
+        *value = min;
+        *delta = - *delta;
+    } else if (*value > max) {
+        *value = max;
+        *delta = - *delta;
+    }
+}
+
+static void bounce_pt(SkPoint* pt, SkVector* vec, const SkRect& limit) {
+    bounce(&pt->fX, &vec->fX, limit.fLeft, limit.fRight);
+    bounce(&pt->fY, &vec->fY, limit.fTop, limit.fBottom);
 }
 
 class BitmapRectView : public SampleView {
+    SkPoint fSrcPts[2];
+    SkPoint fSrcVec[2];
+    SkRect  fSrcLimit;
+    SkRect  fDstR[2];
+
+    void bounce() {
+        bounce_pt(&fSrcPts[0], &fSrcVec[0], fSrcLimit);
+        bounce_pt(&fSrcPts[1], &fSrcVec[1], fSrcLimit);
+    }
+
 public:
     BitmapRectView() {
         this->setBGColor(SK_ColorGRAY);
+
+        fSrcPts[0].set(0, 0);
+        fSrcPts[1].set(SCALAR_SIZE, SCALAR_SIZE);
+
+        fSrcVec[0] = unit_vec(30);
+        fSrcVec[1] = unit_vec(107);
+
+        fSrcLimit.set(-SCALAR_SIZE/4, -SCALAR_SIZE/4,
+                      SCALAR_SIZE*5/4, SCALAR_SIZE*5/4);
+
+        fDstR[0] = SkRect::MakeXYWH(SkIntToScalar(10), SkIntToScalar(100),
+                                       SkIntToScalar(250), SkIntToScalar(300));
+        fDstR[1] = fDstR[0];
+        fDstR[1].offset(fDstR[0].width() * 5/4, 0);
+
+        fSrcPts[0].set(32, 32);
+        fSrcPts[1].set(90, 90);
     }
 
 protected:
@@ -74,45 +115,134 @@ protected:
     }
 
     virtual void onDrawContent(SkCanvas* canvas) {
-        GrContext* ctx = SampleCode::GetGr();
-
-        const SkIRect src[] = {
-            { 0, 0, 32, 32 },
-            { 0, 0, 80, 80 },
-            { 32, 32, 96, 96 },
-            { -32, -32, 32, 32, }
-        };
+        SkRect srcR;
+        srcR.set(fSrcPts[0], fSrcPts[1]);
+        srcR = SkRect::MakeXYWH(fSrcPts[0].fX, fSrcPts[0].fY, 32, 32);
+        srcR.offset(-srcR.width()/2, -srcR.height()/2);
 
         SkPaint paint;
         paint.setStyle(SkPaint::kStroke_Style);
-        paint.setColor(ctx ? SK_ColorGREEN : SK_ColorYELLOW);
+        paint.setColor(SK_ColorYELLOW);
 
         SkBitmap bitmap;
-        make_bitmap(&bitmap, ctx);
+        make_bitmap(&bitmap);
+
+        canvas->translate(20, 20);
+
+        canvas->drawBitmap(bitmap, 0, 0, &paint);
+        canvas->drawRect(srcR, paint);
+
+        for (int i = 0; i < 2; ++i) {
+            paint.setFilterBitmap(1 == i);
+            canvas->drawBitmapRectToRect(bitmap, &srcR, fDstR[i], &paint);
+            canvas->drawRect(fDstR[i], paint);
+        }
+
+        this->bounce();
+        this->inval(NULL);
+    }
+
+private:
+    typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void make_big_bitmap(SkBitmap* bm) {
+    static const char gText[] =
+        "We the people, in order to form a more perfect union, establish justice,"
+        " ensure domestic tranquility, provide for the common defense, promote the"
+        " general welfare and ensure the blessings of liberty to ourselves and our"
+        " posterity, do ordain and establish this constitution for the United"
+        " States of America.";
+
+    const int BIG_H = 120;
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setTextSize(SkIntToScalar(BIG_H));
+
+    const int BIG_W = SkScalarRoundToInt(paint.measureText(gText, strlen(gText)));
+
+    bm->setConfig(SkBitmap::kARGB_8888_Config, BIG_W, BIG_H);
+    bm->allocPixels();
+    bm->eraseColor(SK_ColorWHITE);
+
+    SkCanvas canvas(*bm);
+
+    canvas.drawText(gText, strlen(gText), 0, paint.getTextSize()*4/5, paint);
+}
+
+class BitmapRectView2 : public SampleView {
+    SkBitmap fBitmap;
+
+    SkRect  fSrcR;
+    SkRect  fLimitR;
+    SkScalar fDX;
+    SkRect  fDstR[2];
+    
+    void bounceMe() {
+        SkScalar width = fSrcR.width();
+        bounce(&fSrcR.fLeft, &fDX, fLimitR.fLeft, fLimitR.fRight - width);
+        fSrcR.fRight = fSrcR.fLeft + width;
+    }
+    
+public:
+    BitmapRectView2() {
+        make_big_bitmap(&fBitmap);
 
-        SkRect dstR = { 0, 200, 128, 380 };
+        this->setBGColor(SK_ColorGRAY);
 
-        canvas->translate(16, 40);
-        for (size_t i = 0; i < SK_ARRAY_COUNT(src); i++) {
-            SkRect srcR;
-            srcR.set(src[i]);
+        fSrcR.fLeft = 0;
+        fSrcR.fTop = 0;
+        fSrcR.fRight = SkIntToScalar(fBitmap.height()) * 3;
+        fSrcR.fBottom = SkIntToScalar(fBitmap.height());
 
-            canvas->drawBitmap(bitmap, 0, 0, &paint);
-            canvas->drawBitmapRect(bitmap, &src[i], dstR, &paint);
+        fLimitR.set(0, 0,
+                    SkIntToScalar(fBitmap.width()),
+                    SkIntToScalar(fBitmap.height()));
 
-            canvas->drawRect(dstR, paint);
-            canvas->drawRect(srcR, paint);
+        fDX = SK_Scalar1;
 
-            canvas->translate(160, 0);
+        fDstR[0] = SkRect::MakeXYWH(SkIntToScalar(20), SkIntToScalar(20),
+                                    SkIntToScalar(600), SkIntToScalar(200));
+        fDstR[1] = fDstR[0];
+        fDstR[1].offset(0, fDstR[0].height() * 5/4);
+    }
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "BigBitmapRect");
+            return true;
         }
+        return this->INHERITED::onQuery(evt);
     }
+    
+    virtual void onDrawContent(SkCanvas* canvas) { 
+        SkPaint paint;
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setColor(SK_ColorYELLOW);
 
+        for (int i = 0; i < 2; ++i) {
+            paint.setFilterBitmap(1 == i);
+            canvas->drawBitmapRectToRect(fBitmap, &fSrcR, fDstR[i], &paint);
+            canvas->drawRect(fDstR[i], paint);
+        }
+        
+        this->bounceMe();
+        this->inval(NULL);
+    }
+    
 private:
     typedef SkView INHERITED;
 };
 
 //////////////////////////////////////////////////////////////////////////////
 
-static SkView* MyFactory() { return new BitmapRectView; }
-static SkViewRegister reg(MyFactory);
+static SkView* F0() { return new BitmapRectView; }
+static SkView* F1() { return new BitmapRectView2; }
+static SkViewRegister gR0(F0);
+static SkViewRegister gR1(F1);
 
index 9ef9c25..1bb3a45 100644 (file)
@@ -1593,41 +1593,18 @@ void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
         }
     }
 
-    SkMatrix matrix;
-    // Compute matrix from the two rectangles
-    {
-        SkRect tmpSrc;
-        if (src) {
-            tmpSrc = *src;
-            // if the extract process clipped off the top or left of the
-            // original, we adjust for that here to get the position right.
-            if (tmpSrc.fLeft > 0) {
-                tmpSrc.fRight -= tmpSrc.fLeft;
-                tmpSrc.fLeft = 0;
-            }
-            if (tmpSrc.fTop > 0) {
-                tmpSrc.fBottom -= tmpSrc.fTop;
-                tmpSrc.fTop = 0;
-            }
-        } else {
-            tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
-                       SkIntToScalar(bitmap.height()));
-        }
-        matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
+    SkLazyPaint lazy;
+    if (NULL == paint) {
+        paint = lazy.init();
     }
-
-    // ensure that src is "valid" before we pass it to our internal routines
-    // and to SkDevice. i.e. sure it is contained inside the original bitmap.
-    SkIRect isrcStorage;
-    SkIRect* isrcPtr = NULL;
-    if (src) {
-        src->roundOut(&isrcStorage);
-        if (!isrcStorage.intersect(0, 0, bitmap.width(), bitmap.height())) {
-            return;
-        }
-        isrcPtr = &isrcStorage;
+    
+    LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
+    
+    while (iter.next()) {
+        iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint());
     }
-    this->internalDrawBitmap(bitmap, isrcPtr, matrix, paint);
+    
+    LOOPER_END
 }
 
 void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
index 10d89d5..fb126db 100644 (file)
@@ -11,6 +11,7 @@
 #include "SkMetaData.h"
 #include "SkRasterClip.h"
 #include "SkRect.h"
+#include "SkShader.h"
 
 SK_DEFINE_INST_COUNT(SkDevice)
 
@@ -348,6 +349,80 @@ void SkDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
     draw.drawBitmap(*bitmapPtr, matrix, paint);
 }
 
+void SkDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
+                              const SkRect* src, const SkRect& dst,
+                              const SkPaint& paint) {
+    SkMatrix    matrix;
+    SkRect      bitmapBounds, tmpSrc, tmpDst;
+    SkBitmap    tmpBitmap;
+    
+    bitmapBounds.set(0, 0,
+                     SkIntToScalar(bitmap.width()),
+                     SkIntToScalar(bitmap.height()));
+    
+    // Compute matrix from the two rectangles
+    if (src) {
+        tmpSrc = *src;
+    } else {
+        tmpSrc = bitmapBounds;
+    }
+    matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
+    
+    const SkRect* dstPtr = &dst;
+    const SkBitmap* bitmapPtr = &bitmap;
+    
+    // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
+    // needed (if the src was clipped). No check needed if src==null.
+    if (src) {
+        if (!bitmapBounds.contains(*src)) {
+            if (!tmpSrc.intersect(bitmapBounds)) {
+                return; // nothing to draw
+            }
+            // recompute dst, based on the smaller tmpSrc
+            matrix.mapRect(&tmpDst, tmpSrc);
+            dstPtr = &tmpDst;
+        }
+        
+        // since we may need to clamp to the borders of the src rect within
+        // the bitmap, we extract a subset.
+        SkIRect srcIR;
+        tmpSrc.roundOut(&srcIR);
+        if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
+            return;
+        }
+        bitmapPtr = &tmpBitmap;
+        
+        // Since we did an extract, we need to adjust the matrix accordingly
+        SkScalar dx = 0, dy = 0;
+        if (srcIR.fLeft > 0) {
+            dx = SkIntToScalar(srcIR.fLeft);
+        }
+        if (srcIR.fTop > 0) {
+            dy = SkIntToScalar(srcIR.fTop);
+        }
+        if (dx || dy) {
+            matrix.preTranslate(dx, dy);
+        }
+    }
+    
+    // construct a shader, so we can call drawRect with the dst
+    SkShader* s = SkShader::CreateBitmapShader(*bitmapPtr,
+                                               SkShader::kClamp_TileMode,
+                                               SkShader::kClamp_TileMode);
+    if (NULL == s) {
+        return;
+    }
+    s->setLocalMatrix(matrix);
+
+    SkPaint paintWithShader(paint);
+    paintWithShader.setStyle(SkPaint::kFill_Style);
+    paintWithShader.setShader(s)->unref();
+
+    // Call ourself, in case the subclass wanted to share this setup code
+    // but handle the drawRect code themselves.
+    this->drawRect(draw, *dstPtr, paintWithShader);
+}
+
 void SkDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
                               int x, int y, const SkPaint& paint) {
     draw.drawSprite(bitmap, x, y, paint);
index 2149d11..bcda7d8 100644 (file)
@@ -1585,6 +1585,47 @@ void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
                                         GR_Scalar1 * h / texture->height()));
 }
 
+void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
+                                 const SkRect* src, const SkRect& dst,
+                                 const SkPaint& paint) {
+    SkMatrix matrix;
+    // Compute matrix from the two rectangles
+    {
+        SkRect tmpSrc;
+        if (src) {
+            tmpSrc = *src;
+            // if the extract process clipped off the top or left of the
+            // original, we adjust for that here to get the position right.
+            if (tmpSrc.fLeft > 0) {
+                tmpSrc.fRight -= tmpSrc.fLeft;
+                tmpSrc.fLeft = 0;
+            }
+            if (tmpSrc.fTop > 0) {
+                tmpSrc.fBottom -= tmpSrc.fTop;
+                tmpSrc.fTop = 0;
+            }
+        } else {
+            tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
+                       SkIntToScalar(bitmap.height()));
+        }
+        matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
+    }
+    
+    // ensure that src is "valid" before we pass it to our internal routines
+    // and to SkDevice. i.e. sure it is contained inside the original bitmap.
+    SkIRect isrcStorage;
+    SkIRect* isrcPtr = NULL;
+    if (src) {
+        src->roundOut(&isrcStorage);
+        if (!isrcStorage.intersect(0, 0, bitmap.width(), bitmap.height())) {
+            return;
+        }
+        isrcPtr = &isrcStorage;
+    }
+
+    this->drawBitmap(draw, bitmap, isrcPtr, matrix, paint);
+}
+
 void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* device,
                             int x, int y, const SkPaint& paint) {
     // clear of the source device must occur before CHECK_SHOULD_DRAW