diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index 828b92331d..dcbff3f60b 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -1257,7 +1257,7 @@ private: MCRec* fMCRec; // the first N recs that can fit here mean we won't call malloc enum { - kMCRecSize = 128, // most recent measurement + kMCRecSize = 136, // most recent measurement kMCRecCount = 8, // common depth for save/restores }; intptr_t fMCRecStorage[kMCRecSize * kMCRecCount / sizeof(intptr_t)]; @@ -1330,7 +1330,7 @@ private: const SkRect& dst, const SkPaint* paint); void internalDrawPaint(const SkPaint& paint); void internalSaveLayer(const SkRect* bounds, const SkPaint*, SaveFlags, SaveLayerStrategy); - void internalDrawDevice(SkBaseDevice*, int x, int y, const SkPaint*); + void internalDrawDevice(SkBaseDevice*, int x, int y, const SkPaint*, bool isBitmapDevice); // shared by save() and saveLayer() void internalSave(); diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h index ec57711754..8a219d7053 100644 --- a/include/core/SkDevice.h +++ b/include/core/SkDevice.h @@ -348,6 +348,13 @@ protected: /** * Create a new device based on CreateInfo. If the paint is not null, then it represents a * preview of how the new device will be composed with its creator device (this). + * + * The subclass may be handed this device in drawDevice(), so it must always return + * a device that it knows how to draw, and that it knows how to identify if it is not of the + * same subclass (since drawDevice is passed a SkBaseDevice*). If the subclass cannot fulfill + * that contract (e.g. PDF cannot support some settings on the paint) it should return NULL, + * and the caller may then decide to explicitly create a bitmapdevice, knowing that later + * it could not call drawDevice with it (but it could call drawSprite or drawBitmap). */ virtual SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) { return NULL; diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index 514a45460c..fbf6c9e83b 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -111,13 +111,16 @@ struct DeviceCM { DeviceCM* fNext; SkBaseDevice* fDevice; SkRasterClip fClip; - const SkMatrix* fMatrix; SkPaint* fPaint; // may be null (in the future) + const SkMatrix* fMatrix; + SkMatrix fMatrixStorage; + const bool fDeviceIsBitmapDevice; DeviceCM(SkBaseDevice* device, const SkPaint* paint, SkCanvas* canvas, - bool conservativeRasterClip) + bool conservativeRasterClip, bool deviceIsBitmapDevice) : fNext(NULL) , fClip(conservativeRasterClip) + , fDeviceIsBitmapDevice(deviceIsBitmapDevice) { if (NULL != device) { device->ref(); @@ -180,9 +183,6 @@ struct DeviceCM { } #endif } - -private: - SkMatrix fMatrixStorage; }; /* This is the record we keep for each save/restore level in the stack. @@ -486,7 +486,7 @@ SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) { SkASSERT(sizeof(DeviceCM) <= sizeof(fBaseLayerStorage)); fMCRec->fLayer = (DeviceCM*)fBaseLayerStorage; - new (fBaseLayerStorage) DeviceCM(NULL, NULL, NULL, fConservativeRasterClip); + new (fBaseLayerStorage) DeviceCM(NULL, NULL, NULL, fConservativeRasterClip, false); fMCRec->fTopLayer = fMCRec->fLayer; @@ -985,16 +985,27 @@ void SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint, Sav return; } - SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage; - device = device->onCreateDevice(SkBaseDevice::CreateInfo(info, usage, geo), paint); - if (NULL == device) { - SkErrorInternals::SetError( kInternalError_SkError, - "Unable to create device for layer."); - return; + bool forceSpriteOnRestore = false; + { + const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage; + const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo); + SkBaseDevice* newDev = device->onCreateDevice(createInfo, paint); + if (NULL == newDev) { + // If onCreateDevice didn't succeed, try raster (e.g. PDF couldn't handle the paint) + newDev = SkBitmapDevice::Create(createInfo.fInfo); + if (NULL == newDev) { + SkErrorInternals::SetError(kInternalError_SkError, + "Unable to create device for layer."); + return; + } + forceSpriteOnRestore = true; + } + device = newDev; } device->setOrigin(ir.fLeft, ir.fTop); - DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, paint, this, fConservativeRasterClip)); + DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, paint, this, fConservativeRasterClip, + forceSpriteOnRestore)); device->unref(); layer->fNext = fMCRec->fTopLayer; @@ -1043,7 +1054,7 @@ void SkCanvas::internalRestore() { if (layer->fNext) { const SkIPoint& origin = layer->fDevice->getOrigin(); this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(), - layer->fPaint); + layer->fPaint, layer->fDeviceIsBitmapDevice); // reset this, since internalDrawDevice will have set it to true fDeviceCMDirty = true; SkDELETE(layer); @@ -1155,7 +1166,7 @@ void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap, } void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, - const SkPaint* paint) { + const SkPaint* paint, bool deviceIsBitmapDevice) { SkPaint tmp; if (NULL == paint) { paint = &tmp; @@ -1183,6 +1194,9 @@ void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(), tmpUnfiltered); } + } else if (deviceIsBitmapDevice) { + const SkBitmap& src = srcDev->accessBitmap(false); + dstDev->drawSprite(iter, src, pos.x(), pos.y(), *paint); } else { dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint); } diff --git a/src/core/SkDeviceImageFilterProxy.h b/src/core/SkDeviceImageFilterProxy.h index dcf12f7c40..79ff957564 100644 --- a/src/core/SkDeviceImageFilterProxy.h +++ b/src/core/SkDeviceImageFilterProxy.h @@ -8,6 +8,7 @@ #ifndef SkDeviceImageFilterProxy_DEFINED #define SkDeviceImageFilterProxy_DEFINED +#include "SkBitmapDevice.h" #include "SkDevice.h" #include "SkImageFilter.h" #include "SkSurfaceProps.h" @@ -24,7 +25,11 @@ public: SkBaseDevice::kNever_TileUsage, kUnknown_SkPixelGeometry, true /*forImageFilter*/); - return fDevice->onCreateDevice(cinfo, NULL); + SkBaseDevice* dev = fDevice->onCreateDevice(cinfo, NULL); + if (NULL == dev) { + dev = SkBitmapDevice::Create(cinfo.fInfo); + } + return dev; } bool canHandleImageFilter(const SkImageFilter* filter) override { return fDevice->canHandleImageFilter(filter); diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index d8b0ee0b64..3434671d6a 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1959,7 +1959,7 @@ SkBaseDevice* SkGpuDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint texture->asRenderTarget(), cinfo.fInfo.width(), cinfo.fInfo.height(), &props, flags); } else { SkErrorInternals::SetError( kInternalError_SkError, - "---- failed to create compatible device texture [%d %d]\n", + "---- failed to create gpu device texture [%d %d]\n", cinfo.fInfo.width(), cinfo.fInfo.height()); return NULL; } diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index 88f9b810b6..2dbad5f411 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -8,7 +8,6 @@ #include "SkPDFDevice.h" #include "SkAnnotation.h" -#include "SkBitmapDevice.h" #include "SkColor.h" #include "SkClipStack.h" #include "SkData.h" @@ -579,7 +578,7 @@ static bool not_supported_for_layers(const SkPaint& layerPaint) { SkBaseDevice* SkPDFDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint* layerPaint) { if (cinfo.fForImageFilter || (layerPaint && not_supported_for_layers(*layerPaint))) { - return SkBitmapDevice::Create(cinfo.fInfo); + return NULL; } SkISize size = SkISize::Make(cinfo.fInfo.width(), cinfo.fInfo.height()); return SkPDFDevice::Create(size, fRasterDpi, fCanon);