Adding a PictureResolution option to SkPictureImageFilter
authorJustin Novosad <junov@chromium.org>
Tue, 2 Dec 2014 19:50:56 +0000 (14:50 -0500)
committerJustin Novosad <junov@chromium.org>
Tue, 2 Dec 2014 19:50:56 +0000 (14:50 -0500)
This change adds an option to SkPictureImageFilter to make it
rasterize SkPicture in a resolution that matches the local coordinate
space (equivalent to the record-time device space).

BUG=skia:3176
R=reed@google.com, senorblanco@chromium.org

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

expectations/gm/ignored-tests.txt
gm/pictureimagefilter.cpp
include/core/SkPicture.h
include/effects/SkPictureImageFilter.h
src/core/SkReadBuffer.h
src/effects/SkPictureImageFilter.cpp

index 77651cc..eec9c97 100644 (file)
@@ -63,3 +63,6 @@ multipicturedraw_sierpinski_tiled
 
 #derekf
 drawbitmapmatrix
+
+#junov skbug.com/3176
+pictureimagefilter
index 4e169f7..712e059 100644 (file)
@@ -36,7 +36,7 @@ protected:
         fPicture.reset(recorder.endRecording());
     }
 
-    virtual SkISize onISize() SK_OVERRIDE { return SkISize::Make(500, 150); }
+    virtual SkISize onISize() SK_OVERRIDE { return SkISize::Make(400, 300); }
 
     virtual void onOnceBeforeDraw() SK_OVERRIDE {
         this->makePicture();
@@ -57,10 +57,16 @@ protected:
             SkRect srcRect = SkRect::MakeXYWH(20, 20, 30, 30);
             SkRect emptyRect = SkRect::MakeXYWH(20, 20, 0, 0);
             SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
-            SkAutoTUnref<SkImageFilter> pictureSource(SkPictureImageFilter::Create(fPicture));
-            SkAutoTUnref<SkImageFilter> pictureSourceSrcRect(SkPictureImageFilter::Create(fPicture, srcRect));
-            SkAutoTUnref<SkImageFilter> pictureSourceEmptyRect(SkPictureImageFilter::Create(fPicture, emptyRect));
-
+            SkAutoTUnref<SkPictureImageFilter> pictureSource(
+                SkPictureImageFilter::Create(fPicture));
+            SkAutoTUnref<SkPictureImageFilter> pictureSourceSrcRect(
+                SkPictureImageFilter::Create(fPicture, srcRect));
+            SkAutoTUnref<SkPictureImageFilter> pictureSourceEmptyRect(
+                SkPictureImageFilter::Create(fPicture, emptyRect));
+            SkAutoTUnref<SkPictureImageFilter> pictureSourceResampled(
+                SkPictureImageFilter::CreateForLocalSpace(fPicture, fPicture->cullRect()));
+
+            canvas->save();
             // Draw the picture unscaled.
             fillRectFiltered(canvas, bounds, pictureSource);
             canvas->translate(SkIntToScalar(100), 0);
@@ -72,6 +78,18 @@ protected:
             // Draw the picture to an empty rect (should draw nothing).
             fillRectFiltered(canvas, bounds, pictureSourceEmptyRect);
             canvas->translate(SkIntToScalar(100), 0);
+
+            canvas->restore();
+
+            // Draw the picture scaled
+            canvas->translate(0, SkIntToScalar(100));
+            canvas->scale(200 / srcRect.width(), 200 / srcRect.height());
+            canvas->translate(-srcRect.fLeft, -srcRect.fTop);
+            fillRectFiltered(canvas, srcRect, pictureSource);
+
+            // Draw the picture scaled, but rasterized at original resolution
+            canvas->translate(srcRect.width(), 0);
+            fillRectFiltered(canvas, srcRect, pictureSourceResampled);
         }
     }
 
index b3bdda1..f764d34 100644 (file)
@@ -246,13 +246,14 @@ private:
     // V35: Store SkRect (rather then width & height) in header
     // V36: Remove (obsolete) alphatype from SkColorTable
     // V37: Added shadow only option to SkDropShadowImageFilter (last version to record CLEAR)
+    // V37: Added PictureResolution and FilterLevel options to SkPictureImageFilter
 
     // Note: If the picture version needs to be increased then please follow the
     // steps to generate new SKPs in (only accessible to Googlers): http://goo.gl/qATVcw
 
     // Only SKPs within the min/current picture version range (inclusive) can be read.
     static const uint32_t MIN_PICTURE_VERSION = 35;     // Produced by Chrome M39.
-    static const uint32_t CURRENT_PICTURE_VERSION = 37;
+    static const uint32_t CURRENT_PICTURE_VERSION = 38;
 
     void createHeader(SkPictInfo* info) const;
     static bool IsValidPictInfo(const SkPictInfo& info);
index f4f1fff..8c3c9c4 100644 (file)
@@ -24,15 +24,37 @@ public:
      *  Refs the passed-in picture. cropRect can be used to crop or expand the destination rect when
      *  the picture is drawn. (No scaling is implied by the dest rect; only the CTM is applied.)
      */
-    static SkPictureImageFilter* Create(const SkPicture* picture, const SkRect& cropRect, uint32_t uniqueID = 0) {
-        return SkNEW_ARGS(SkPictureImageFilter, (picture, cropRect, uniqueID));
+    static SkPictureImageFilter* Create(const SkPicture* picture, const SkRect& cropRect,
+                                        uint32_t uniqueID = 0) {
+        return SkNEW_ARGS(SkPictureImageFilter, (picture, cropRect, uniqueID,
+                                                 kDeviceSpace_PictureResolution));
+    }
+
+    /**
+     *  Refs the passed-in picture. The picture is rasterized at a resolution that matches the
+     *  local coordinate space. If the picture needs to be resampled for drawing it into the
+     *  destination canvas, bilinear filtering will be used. cropRect can be used to crop or
+     *  expand the destination rect when the picture is drawn. (No scaling is implied by the
+     *  dest rect; only the CTM is applied.)
+     */
+    static SkPictureImageFilter* CreateForLocalSpace(const SkPicture* picture,
+                                                     const SkRect& cropRect,
+                                                     uint32_t uniqueID = 0) {
+        return SkNEW_ARGS(SkPictureImageFilter, (picture, cropRect, uniqueID,
+                                                 kLocalSpace_PictureResolution));
     }
 
     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPictureImageFilter)
 
 protected:
+    enum PictureResolution {
+        kDeviceSpace_PictureResolution,
+        kLocalSpace_PictureResolution
+    };
+
     explicit SkPictureImageFilter(const SkPicture* picture, uint32_t uniqueID);
-    SkPictureImageFilter(const SkPicture* picture, const SkRect& cropRect, uint32_t uniqueID);
+    SkPictureImageFilter(const SkPicture* picture, const SkRect& cropRect, uint32_t uniqueID,
+                         PictureResolution);
     virtual ~SkPictureImageFilter();
     /*  Constructs an SkPictureImageFilter object from an SkReadBuffer.
      *  Note: If the SkPictureImageFilter object construction requires bitmap
@@ -45,8 +67,16 @@ protected:
                                SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE;
 
 private:
-    const SkPicture* fPicture;
-    SkRect           fCropRect;
+
+
+    void drawPictureAtDeviceResolution(Proxy*, SkBaseDevice*, const SkIRect& deviceBounds,
+                                       const Context&) const;
+    void drawPictureAtLocalResolution(Proxy*, SkBaseDevice*, const SkIRect& deviceBounds,
+                                      const Context&) const;
+
+    const SkPicture*      fPicture;
+    SkRect                fCropRect;
+    PictureResolution     fPictureResolution;
     typedef SkImageFilter INHERITED;
 };
 
index 4b14117..9056b07 100644 (file)
@@ -53,6 +53,7 @@ public:
         */
         kRemoveColorTableAlpha_Version     = 36,
         kDropShadowMode_Version            = 37,
+        kPictureImageFilterResolution_Version = 38,
     };
 
     /**
index 5399ea7..e18600d 100644 (file)
 SkPictureImageFilter::SkPictureImageFilter(const SkPicture* picture, uint32_t uniqueID)
     : INHERITED(0, 0, NULL, uniqueID)
     , fPicture(SkSafeRef(picture))
-    , fCropRect(picture ? picture->cullRect() : SkRect::MakeEmpty()) {
+    , fCropRect(picture ? picture->cullRect() : SkRect::MakeEmpty())
+    , fPictureResolution(kDeviceSpace_PictureResolution) {
 }
 
 SkPictureImageFilter::SkPictureImageFilter(const SkPicture* picture, const SkRect& cropRect,
-                                           uint32_t uniqueID)
+                                           uint32_t uniqueID, PictureResolution pictureResolution)
     : INHERITED(0, 0, NULL, uniqueID)
     , fPicture(SkSafeRef(picture))
-    , fCropRect(cropRect) {
+    , fCropRect(cropRect)
+    , fPictureResolution(pictureResolution) {
 }
 
 SkPictureImageFilter::~SkPictureImageFilter() {
@@ -42,7 +44,16 @@ SkFlattenable* SkPictureImageFilter::CreateProc(SkReadBuffer& buffer) {
         buffer.validate(!buffer.readBool());
     }
     buffer.readRect(&cropRect);
+    PictureResolution pictureResolution;
+    if (buffer.isVersionLT(SkReadBuffer::kPictureImageFilterResolution_Version)) {
+        pictureResolution = kDeviceSpace_PictureResolution;
+    } else {
+        pictureResolution = (PictureResolution)buffer.readInt();
+    } 
 
+    if (pictureResolution == kLocalSpace_PictureResolution) {
+        return CreateForLocalSpace(picture, cropRect);
+    }
     return Create(picture, cropRect);
 }
 
@@ -57,6 +68,7 @@ void SkPictureImageFilter::flatten(SkWriteBuffer& buffer) const {
         buffer.writeBool(false);
     }
     buffer.writeRect(fCropRect);
+    buffer.writeInt(fPictureResolution);
 }
 
 bool SkPictureImageFilter::onFilterImage(Proxy* proxy, const SkBitmap&, const Context& ctx,
@@ -83,17 +95,61 @@ bool SkPictureImageFilter::onFilterImage(Proxy* proxy, const SkBitmap&, const Co
         return false;
     }
 
+    if (kLocalSpace_PictureResolution == fPictureResolution && 
+        (ctx.ctm().getType() & ~SkMatrix::kTranslate_Mask)) {
+        drawPictureAtLocalResolution(proxy, device.get(), bounds, ctx);
+    } else {
+        drawPictureAtDeviceResolution(proxy, device.get(), bounds, ctx);
+    }
+
+    *result = device.get()->accessBitmap(false);
+    offset->fX = bounds.fLeft;
+    offset->fY = bounds.fTop;
+    return true;
+}
+
+void SkPictureImageFilter::drawPictureAtDeviceResolution(Proxy* proxy, SkBaseDevice* device,
+                                                         const SkIRect& deviceBounds,
+                                                         const Context& ctx) const {
     // Pass explicit surface props, as the simplified canvas constructor discards device properties.
     // FIXME: switch back to the public constructor (and unfriend) after
     //        https://code.google.com/p/skia/issues/detail?id=3142 is fixed.
-    SkCanvas canvas(device.get(), proxy->surfaceProps(), SkCanvas::kDefault_InitFlags);
+    SkCanvas canvas(device, proxy->surfaceProps(), SkCanvas::kDefault_InitFlags);
 
-    canvas.translate(-SkIntToScalar(bounds.fLeft), -SkIntToScalar(bounds.fTop));
+    canvas.translate(-SkIntToScalar(deviceBounds.fLeft), -SkIntToScalar(deviceBounds.fTop));
     canvas.concat(ctx.ctm());
     canvas.drawPicture(fPicture);
+}
 
-    *result = device.get()->accessBitmap(false);
-    offset->fX = bounds.fLeft;
-    offset->fY = bounds.fTop;
-    return true;
+void SkPictureImageFilter::drawPictureAtLocalResolution(Proxy* proxy, SkBaseDevice* device,
+                                                        const SkIRect& deviceBounds,
+                                                        const Context& ctx) const {
+    SkMatrix inverseCtm;
+    if (!ctx.ctm().invert(&inverseCtm))
+        return;
+    SkRect localBounds = SkRect::Make(ctx.clipBounds());
+    inverseCtm.mapRect(&localBounds);
+    if (!localBounds.intersect(fCropRect))
+        return;
+    SkIRect localIBounds = localBounds.roundOut();
+    SkAutoTUnref<SkBaseDevice> localDevice(proxy->createDevice(localIBounds.width(), localIBounds.height()));
+
+    // Pass explicit surface props, as the simplified canvas constructor discards device properties.
+    // FIXME: switch back to the public constructor (and unfriend) after
+    //        https://code.google.com/p/skia/issues/detail?id=3142 is fixed.
+    SkCanvas localCanvas(localDevice, proxy->surfaceProps(), SkCanvas::kDefault_InitFlags);
+    localCanvas.translate(-SkIntToScalar(localIBounds.fLeft), -SkIntToScalar(localIBounds.fTop));
+    localCanvas.drawPicture(fPicture);
+
+    // Pass explicit surface props, as the simplified canvas constructor discards device properties.
+    // FIXME: switch back to the public constructor (and unfriend) after
+    //        https://code.google.com/p/skia/issues/detail?id=3142 is fixed.
+    SkCanvas canvas(device, proxy->surfaceProps(), SkCanvas::kDefault_InitFlags);
+
+    canvas.translate(-SkIntToScalar(deviceBounds.fLeft), -SkIntToScalar(deviceBounds.fTop));
+    canvas.concat(ctx.ctm());
+    SkPaint paint;
+    paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+    canvas.drawBitmap(localDevice.get()->accessBitmap(false), SkIntToScalar(localIBounds.fLeft), SkIntToScalar(localIBounds.fTop), &paint);
+    //canvas.drawPicture(fPicture);
 }