From 5234075b1c6bcada4ad17ed5a83bfcb53df66b7f Mon Sep 17 00:00:00 2001 From: Justin Novosad Date: Tue, 2 Dec 2014 14:50:56 -0500 Subject: [PATCH] Adding a PictureResolution option to SkPictureImageFilter 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 | 3 ++ gm/pictureimagefilter.cpp | 28 ++++++++++--- include/core/SkPicture.h | 3 +- include/effects/SkPictureImageFilter.h | 40 +++++++++++++++--- src/core/SkReadBuffer.h | 1 + src/effects/SkPictureImageFilter.cpp | 74 +++++++++++++++++++++++++++++----- 6 files changed, 129 insertions(+), 20 deletions(-) diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt index 77651cc..eec9c97 100644 --- a/expectations/gm/ignored-tests.txt +++ b/expectations/gm/ignored-tests.txt @@ -63,3 +63,6 @@ multipicturedraw_sierpinski_tiled #derekf drawbitmapmatrix + +#junov skbug.com/3176 +pictureimagefilter diff --git a/gm/pictureimagefilter.cpp b/gm/pictureimagefilter.cpp index 4e169f7..712e059 100644 --- a/gm/pictureimagefilter.cpp +++ b/gm/pictureimagefilter.cpp @@ -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 pictureSource(SkPictureImageFilter::Create(fPicture)); - SkAutoTUnref pictureSourceSrcRect(SkPictureImageFilter::Create(fPicture, srcRect)); - SkAutoTUnref pictureSourceEmptyRect(SkPictureImageFilter::Create(fPicture, emptyRect)); - + SkAutoTUnref pictureSource( + SkPictureImageFilter::Create(fPicture)); + SkAutoTUnref pictureSourceSrcRect( + SkPictureImageFilter::Create(fPicture, srcRect)); + SkAutoTUnref pictureSourceEmptyRect( + SkPictureImageFilter::Create(fPicture, emptyRect)); + SkAutoTUnref 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); } } diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h index b3bdda1..f764d34 100644 --- a/include/core/SkPicture.h +++ b/include/core/SkPicture.h @@ -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); diff --git a/include/effects/SkPictureImageFilter.h b/include/effects/SkPictureImageFilter.h index f4f1fff..8c3c9c4 100644 --- a/include/effects/SkPictureImageFilter.h +++ b/include/effects/SkPictureImageFilter.h @@ -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; }; diff --git a/src/core/SkReadBuffer.h b/src/core/SkReadBuffer.h index 4b14117..9056b07 100644 --- a/src/core/SkReadBuffer.h +++ b/src/core/SkReadBuffer.h @@ -53,6 +53,7 @@ public: */ kRemoveColorTableAlpha_Version = 36, kDropShadowMode_Version = 37, + kPictureImageFilterResolution_Version = 38, }; /** diff --git a/src/effects/SkPictureImageFilter.cpp b/src/effects/SkPictureImageFilter.cpp index 5399ea7..e18600d 100644 --- a/src/effects/SkPictureImageFilter.cpp +++ b/src/effects/SkPictureImageFilter.cpp @@ -16,14 +16,16 @@ 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 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); } -- 2.7.4