Fix image filters for PDF backend.

Currently, the PDF backend does not support image filters (since PDF
does not have that functionality), so it simply removes them. This is
causing Chrome print preview to render incorrectly (see bug). The fix
here is to fall back to a raster device for image filters, as we used
to do in Blink. The resulting bitmap will be drawn to the destination
device as a normal main-memory-backed bitmap.

Note: this change invalidates the PDF results of all GMs containing
image filters (since they'll actually be rendered).

BUG=422144

Review URL: https://codereview.chromium.org/644323006
This commit is contained in:
senorblanco 2014-10-20 14:03:12 -07:00 committed by Commit bot
parent 1d932663e1
commit b0e89dcc1d
8 changed files with 103 additions and 7 deletions

View File

@ -57,3 +57,34 @@ imagefiltersbase
# jvanverth - adding color emoji to df text
dftext
# senorblanco https://codereview.chromium.org/644323006/
# PDF backend now supports image filters
bitmapsource
colorfilterimagefilter
displacement
dropshadowimagefilter
imagealphathreshold
imageblur
imageblur2
imageblur_large
imageblurtiled
imagefiltersbase
imagefiltersclipped
imagefilterscropexpand
imagefilterscropped
imagefiltersgraph
imagefiltersscaled
imagemagnifier
imageresizetiled
lighting
matrixconvolution
matriximagefilter
morphology
offsetimagefilter
pictureimagefilter
resizeimagefilter
spritebitmap
testimagefilters
tileimagefilter
xfermodeimagefilter

View File

@ -121,7 +121,8 @@ public:
protected:
enum Usage {
kGeneral_Usage,
kSaveLayer_Usage // <! internal use only
kSaveLayer_Usage, // <! internal use only
kImageFilter_Usage // <! internal use only
};
struct TextFlags {
@ -368,6 +369,8 @@ private:
void setOrigin(int x, int y) { fOrigin.set(x, y); }
// just called by SkCanvas for saveLayer
SkBaseDevice* createCompatibleDeviceForSaveLayer(const SkImageInfo&);
// just called by SkCanvas for imagefilter
SkBaseDevice* createCompatibleDeviceForImageFilter(const SkImageInfo&);
virtual SkBaseDevice* onCreateDevice(const SkImageInfo&, Usage) {
return NULL;

View File

@ -213,10 +213,6 @@ protected:
return fLegacyBitmap;
}
virtual bool allowImageFilter(const SkImageFilter*) SK_OVERRIDE {
return false;
}
virtual SkSurface* newSurface(const SkImageInfo&, const SkSurfaceProps&) SK_OVERRIDE;
private:

View File

@ -933,7 +933,7 @@ int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, Save
if (paint && paint->getImageFilter()) {
device = this->getDevice();
if (device) {
device = device->createCompatibleDevice(info);
device = device->createCompatibleDeviceForImageFilter(info);
}
} else {
device = this->createLayerDevice(info);

View File

@ -36,6 +36,10 @@ SkBaseDevice* SkBaseDevice::createCompatibleDeviceForSaveLayer(const SkImageInfo
return this->onCreateDevice(info, kSaveLayer_Usage);
}
SkBaseDevice* SkBaseDevice::createCompatibleDeviceForImageFilter(const SkImageInfo& info) {
return this->onCreateDevice(info, kImageFilter_Usage);
}
SkMetaData& SkBaseDevice::getMetaData() {
// metadata users are rare, so we lazily allocate it. If that changes we
// can decide to just make it a field in the device (rather than a ptr)

View File

@ -15,7 +15,7 @@ public:
SkDeviceImageFilterProxy(SkBaseDevice* device) : fDevice(device) {}
virtual SkBaseDevice* createDevice(int w, int h) SK_OVERRIDE {
return fDevice->createCompatibleDevice(SkImageInfo::MakeN32Premul(w, h));
return fDevice->createCompatibleDeviceForImageFilter(SkImageInfo::MakeN32Premul(w, h));
}
virtual bool canHandleImageFilter(const SkImageFilter* filter) SK_OVERRIDE {
return fDevice->canHandleImageFilter(filter);

View File

@ -8,6 +8,7 @@
#include "SkPDFDevice.h"
#include "SkAnnotation.h"
#include "SkBitmapDevice.h"
#include "SkColor.h"
#include "SkClipStack.h"
#include "SkData.h"
@ -567,6 +568,15 @@ void GraphicStackState::updateDrawingState(const GraphicStateEntry& state) {
}
SkBaseDevice* SkPDFDevice::onCreateDevice(const SkImageInfo& info, Usage usage) {
// PDF does not support image filters, so render them on CPU.
// Note that this rendering is done at "screen" resolution (100dpi), not
// printer resolution.
// FIXME: It may be possible to express some filters natively using PDF
// to improve quality and file size (http://skbug.com/3043)
if (kImageFilter_Usage == usage) {
return SkBitmapDevice::Create(info);
}
SkMatrix initialTransform;
initialTransform.reset();
SkISize size = SkISize::Make(info.width(), info.height());

View File

@ -15,6 +15,7 @@
#include "SkPDFDevice.h"
#include "SkPDFStream.h"
#include "SkPDFTypes.h"
#include "SkReadBuffer.h"
#include "SkScalar.h"
#include "SkStream.h"
#include "SkTypes.h"
@ -428,3 +429,54 @@ DEF_TEST(PDFPrimitives, reporter) {
TestImages(reporter);
}
namespace {
class DummyImageFilter : public SkImageFilter {
public:
DummyImageFilter(bool visited = false) : SkImageFilter(0, NULL), fVisited(visited) {}
virtual ~DummyImageFilter() SK_OVERRIDE {}
virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&,
SkBitmap* result, SkIPoint* offset) const {
fVisited = true;
offset->fX = offset->fY = 0;
*result = src;
return true;
}
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(DummyImageFilter)
#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
explicit DummyImageFilter(SkReadBuffer& buffer) : SkImageFilter(0, NULL) {
fVisited = buffer.readBool();
}
#endif
bool visited() const { return fVisited; }
private:
mutable bool fVisited;
};
SkFlattenable* DummyImageFilter::CreateProc(SkReadBuffer& buffer) {
SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0);
bool visited = buffer.readBool();
return SkNEW_ARGS(DummyImageFilter, (visited));
}
};
// Check that PDF rendering of image filters successfully falls back to
// CPU rasterization.
DEF_TEST(PDFImageFilter, reporter) {
SkISize pageSize = SkISize::Make(100, 100);
SkAutoTUnref<SkPDFDevice> device(new SkPDFDevice(pageSize, pageSize, SkMatrix::I()));
SkCanvas canvas(device.get());
SkAutoTUnref<DummyImageFilter> filter(new DummyImageFilter());
// Filter just created; should be unvisited.
REPORTER_ASSERT(reporter, !filter->visited());
SkPaint paint;
paint.setImageFilter(filter.get());
canvas.drawRect(SkRect::MakeWH(100, 100), paint);
// Filter was used in rendering; should be visited.
REPORTER_ASSERT(reporter, filter->visited());
}