Initial clip-mask-layer support

SkBitmapDevice-only implementation.

Will add A8 fast path specializations in a follow-up.

Change-Id: I2ccb1ffba3689e92ac90a23e94737471dfb121a1
BUG=skia:6005

Change-Id: I2ccb1ffba3689e92ac90a23e94737471dfb121a1
Reviewed-on: https://skia-review.googlesource.com/14183
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
This commit is contained in:
Florin Malita 2017-04-28 13:48:37 -04:00 committed by Skia Commit-Bot
parent 57061eea44
commit 53f77bd4fd
30 changed files with 329 additions and 77 deletions

View File

@ -123,7 +123,7 @@ static void draw_set(SkCanvas* canvas, sk_sp<SkImageFilter> filters[], int count
canvas->save();
SkRRect rr = SkRRect::MakeRectXY(r.makeOffset(dx, dy), 20, 20);
canvas->clipRRect(rr, true);
canvas->saveLayer({ &rr.getBounds(), nullptr, filters[i].get(), 0 });
canvas->saveLayer({ &rr.getBounds(), nullptr, filters[i].get(), nullptr, nullptr, 0 });
canvas->drawColor(0x40FFFFFF);
canvas->restore();
canvas->restore();

View File

@ -8,7 +8,6 @@
#include "gm.h"
#include "sk_tool_utils.h"
static const uint32_t SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag = 1U << 31;
// This GM tests out the deprecated Android-specific unclipped saveLayer "feature".
@ -17,7 +16,8 @@ static const uint32_t SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag = 1U << 31;
static void save_layer_unclipped(SkCanvas* canvas,
SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
SkRect rect = SkRect::MakeLTRB(l, t, r, b);
canvas->saveLayer({ &rect, nullptr, nullptr, SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag });
canvas->saveLayer({ &rect, nullptr, nullptr, nullptr, nullptr,
SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag });
}
static void do_draw(SkCanvas* canvas) {
@ -94,8 +94,8 @@ DEF_SIMPLE_GM(picture_savelayer, canvas, 320, 640) {
for(int i = 1; i < 2; ++i) {
canvas->translate(100 * i, 0);
auto flag = i ? SkCanvas_kDontClipToLayer_PrivateSaveLayerFlag : 0;
canvas->saveLayer({ &rect1, &paint1, nullptr, flag});
canvas->saveLayer({ &rect2, &paint2, nullptr, flag});
canvas->saveLayer({ &rect1, &paint1, nullptr, nullptr, nullptr, flag});
canvas->saveLayer({ &rect2, &paint2, nullptr, nullptr, nullptr, flag});
canvas->drawRect(rect3, paint3);
canvas->restore();
canvas->restore();
@ -122,3 +122,118 @@ DEF_SIMPLE_GM(savelayer_initfromprev, canvas, 256, 256) {
canvas->restore();
};
#include "SkBlurImageFilter.h"
#include "SkGradientShader.h"
#include "SkPicture.h"
#include "SkPictureRecorder.h"
#include "SkSurface.h"
static void draw_mask(SkCanvas* canvas, int size) {
const SkScalar cx = size * SK_ScalarHalf,
cy = cx;
const SkColor colors[] = { 0x00000000, 0xffff0000, 0x00000000, 0xffff0000, 0x00000000,
0xffff0000, 0x00000000, 0xffff0000, 0x00000000 };
SkPaint paint;
paint.setAntiAlias(true);
paint.setShader(SkGradientShader::MakeSweep(cx, cy, colors, nullptr, SK_ARRAY_COUNT(colors)));
canvas->drawPaint(paint);
paint.setShader(SkGradientShader::MakeRadial({cx, cy}, size / 4, colors, nullptr, 2,
SkShader::kClamp_TileMode));
canvas->drawCircle(cx, cy, size / 4, paint);
}
DEF_SIMPLE_GM(savelayer_clipmask, canvas, 1200, 1200) {
static constexpr int kSize = 100;
static constexpr SkRect kLayerBounds = { kSize * 0.25f, kSize * 0.25f,
kSize * 0.75f, kSize * 0.75f };
static constexpr struct {
const SkRect* bounds;
const SkScalar matrix[9];
} kConfigs[] = {
{ nullptr, { 1 , 0 , 0, 0 , 1 , 0, 0, 0, 1 } },
{ nullptr, { 2 , 0 , 0, 0 , 2 , 0, 0, 0, 1 } },
{ nullptr, { 2 , 0 , -50, 0 , 2 , -50, 0, 0, 1 } },
{ nullptr, { 0.707f, -0.707f, 50, 0.707f, 0.707f, -20, 0, 0, 1 } },
{ nullptr, { 0.5f , 0 , 25, 0 , 0.5f , 25, 0, 0, 1 } },
{ &kLayerBounds, { 1 , 0 , 0, 0 , 1 , 0, 0, 0, 1 } },
{ &kLayerBounds, { 2 , 0 , 0, 0 , 2 , 0, 0, 0, 1 } },
{ &kLayerBounds, { 2 , 0 , -50, 0 , 2 , -50, 0, 0, 1 } },
{ &kLayerBounds, { 0.707f, -0.707f, 50, 0.707f, 0.707f, -20, 0, 0, 1 } },
{ &kLayerBounds, { 0.5f , 0 , 25, 0 , 0.5f , 25, 0, 0, 1 } },
};
using MaskMakerFunc = sk_sp<SkImage> (*)(int size);
static const MaskMakerFunc kMaskMakers[] = {
[](int size) -> sk_sp<SkImage> {
auto surf = SkSurface::MakeRaster(SkImageInfo::MakeA8(size, size));
draw_mask(surf->getCanvas(), size);
return surf->makeImageSnapshot();
},
[](int size) -> sk_sp<SkImage> {
auto surf = SkSurface::MakeRasterN32Premul(size, size);
draw_mask(surf->getCanvas(), size);
return surf->makeImageSnapshot();
},
[](int size) -> sk_sp<SkImage> {
SkPictureRecorder recorder;
draw_mask(recorder.beginRecording(size, size), size);
return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(),
SkISize::Make(size, size),
nullptr, nullptr,
SkImage::BitDepth::kU8,
SkColorSpace::MakeSRGB());
}
};
using PaintMakerFunc = SkPaint (*)();
static const PaintMakerFunc kPaintMakers[] = {
[]() -> SkPaint { return SkPaint(); },
[]() -> SkPaint {
SkPaint p; p.setImageFilter(SkBlurImageFilter::Make(2, 2, nullptr)); return p;
},
[]() -> SkPaint { SkPaint p; p.setBlendMode(SkBlendMode::kSrcOut); return p; },
};
canvas->drawColor(0xffcccccc);
SkMatrix clipMatrix;
SkCanvas::SaveLayerRec rec;
rec.fClipMatrix = &clipMatrix;
for (const auto& paintMaker : kPaintMakers) {
auto layerPaint = paintMaker();
rec.fPaint = &layerPaint;
for (const auto& maskMaker : kMaskMakers) {
maskMaker(kSize);
rec.fClipMask = maskMaker(kSize);
canvas->save();
for (const auto cfg : kConfigs) {
rec.fBounds = cfg.bounds;
clipMatrix.set9(cfg.matrix);
canvas->saveLayer(rec);
SkPaint paint;
paint.setColor(0xff0000ff);
canvas->drawRect(SkRect::MakeWH(50, 50), paint);
paint.setColor(0xffff0000);
canvas->drawRect(SkRect::MakeXYWH(50, 0, 50, 50), paint);
paint.setColor(0xff00ff00);
canvas->drawRect(SkRect::MakeXYWH(50, 50, 50, 50), paint);
paint.setColor(0xffffff00);
canvas->drawRect(SkRect::MakeXYWH(0, 50, 50, 50), paint);
canvas->restore();
canvas->translate(120, 0);
}
canvas->restore();
canvas->translate(0, 120);
}
}
}

View File

@ -323,27 +323,29 @@ public:
typedef uint32_t SaveLayerFlags;
struct SaveLayerRec {
SaveLayerRec()
: fBounds(nullptr), fPaint(nullptr), fBackdrop(nullptr), fSaveLayerFlags(0)
{}
SaveLayerRec() {}
SaveLayerRec(const SkRect* bounds, const SkPaint* paint, SaveLayerFlags saveLayerFlags = 0)
: fBounds(bounds)
, fPaint(paint)
, fBackdrop(nullptr)
, fSaveLayerFlags(saveLayerFlags)
{}
SaveLayerRec(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop,
sk_sp<SkImage> clipMask, const SkMatrix* clipMatrix,
SaveLayerFlags saveLayerFlags)
: fBounds(bounds)
, fPaint(paint)
, fBackdrop(backdrop)
, fClipMask(std::move(clipMask))
, fClipMatrix(clipMatrix)
, fSaveLayerFlags(saveLayerFlags)
{}
const SkRect* fBounds; // optional
const SkPaint* fPaint; // optional
const SkImageFilter* fBackdrop; // optional
SaveLayerFlags fSaveLayerFlags;
const SkRect* fBounds = nullptr; // optional
const SkPaint* fPaint = nullptr; // optional
const SkImageFilter* fBackdrop = nullptr; // optional
sk_sp<SkImage> fClipMask; // optional
const SkMatrix* fClipMatrix = nullptr; // optional -- only used with fClipMask
SaveLayerFlags fSaveLayerFlags = 0;
};
int saveLayer(const SaveLayerRec&);
@ -1489,7 +1491,7 @@ private:
enum {
kMCRecSize = 128, // most recent measurement
kMCRecCount = 32, // common depth for save/restores
kDeviceCMSize = 184, // most recent measurement
kDeviceCMSize = 224, // most recent measurement
};
intptr_t fMCRecStorage[kMCRecSize * kMCRecCount / sizeof(intptr_t)];
intptr_t fDeviceCMStorage[kDeviceCMSize / sizeof(intptr_t)];
@ -1561,7 +1563,8 @@ private:
SrcRectConstraint);
void internalDrawPaint(const SkPaint& paint);
void internalSaveLayer(const SaveLayerRec&, SaveLayerStrategy);
void internalDrawDevice(SkBaseDevice*, int x, int y, const SkPaint*);
void internalDrawDevice(SkBaseDevice*, int x, int y, const SkPaint*, SkImage* clipImage,
const SkMatrix& clipMatrix);
// shared by save() and saveLayer()
void internalSave();

View File

@ -200,10 +200,11 @@ private:
// V50: SkXfermode -> SkBlendMode
// V51: more SkXfermode -> SkBlendMode
// V52: Remove SkTextBlob::fRunCount
// V53: SaveLayerRec clip mask
// 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 = 52;
static const uint32_t CURRENT_PICTURE_VERSION = 53;
static_assert(MIN_PICTURE_VERSION <= 41,
"Remove kFontFileName and related code from SkFontDescriptor.cpp.");

View File

@ -180,6 +180,8 @@ RECORD(SaveLayer, kHasPaint_Tag,
Optional<SkRect> bounds;
Optional<SkPaint> paint;
sk_sp<const SkImageFilter> backdrop;
sk_sp<SkImage> clipMask;
Optional<SkMatrix> clipMatrix;
SkCanvas::SaveLayerFlags saveLayerFlags);
RECORD(SetMatrix, 0,

View File

@ -260,7 +260,7 @@ protected:
SkPaint paint;
paint.setAlpha(0xCC);
canvas->saveLayer({ &bounds, &paint, fFilter.get(), 0 });
canvas->saveLayer({ &bounds, &paint, fFilter.get(), nullptr, nullptr, 0 });
canvas->restore();
}

View File

@ -20,6 +20,7 @@
#include "SkShader.h"
#include "SkSpecialImage.h"
#include "SkSurface.h"
#include "SkTLazy.h"
#include "SkVertices.h"
class SkColorTable;
@ -387,35 +388,71 @@ void SkBitmapDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPain
///////////////////////////////////////////////////////////////////////////////
void SkBitmapDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y,
const SkPaint& paint) {
SkASSERT(!srcImg->isTextureBacked());
void SkBitmapDevice::drawSpecial(SkSpecialImage* src, int x, int y, const SkPaint& origPaint,
SkImage* clipImage, const SkMatrix& clipMatrix) {
SkASSERT(!src->isTextureBacked());
SkBitmap resultBM;
sk_sp<SkSpecialImage> filteredImage;
SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
SkImageFilter* filter = paint.getImageFilter();
if (filter) {
if (SkImageFilter* filter = paint->getImageFilter()) {
SkIPoint offset = SkIPoint::Make(0, 0);
SkMatrix matrix = this->ctm();
matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
const SkMatrix matrix = SkMatrix::Concat(
SkMatrix::MakeTrans(SkIntToScalar(-x), SkIntToScalar(-y)), this->ctm());
const SkIRect clipBounds = fRCStack.rc().getBounds().makeOffset(-x, -y);
sk_sp<SkImageFilterCache> cache(this->getImageFilterCache());
SkImageFilter::OutputProperties outputProperties(fBitmap.colorSpace());
SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), outputProperties);
sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg, ctx, &offset));
if (resultImg) {
SkPaint tmpUnfiltered(paint);
tmpUnfiltered.setImageFilter(nullptr);
if (resultImg->getROPixels(&resultBM)) {
this->drawSprite(resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered);
}
}
} else {
if (srcImg->getROPixels(&resultBM)) {
this->drawSprite(resultBM, x, y, paint);
filteredImage = filter->filterImage(src, ctx, &offset);
if (!filteredImage) {
return;
}
src = filteredImage.get();
paint.writable()->setImageFilter(nullptr);
x += offset.x();
y += offset.y();
}
if (!clipImage) {
SkBitmap resultBM;
if (src->getROPixels(&resultBM)) {
this->drawSprite(resultBM, x, y, *paint);
}
return;
}
// Clip image case.
sk_sp<SkImage> srcImage(src->asImage());
if (!srcImage) {
return;
}
const SkMatrix totalMatrix = SkMatrix::Concat(this->ctm(), clipMatrix);
SkRect clipBounds;
totalMatrix.mapRect(&clipBounds, SkRect::Make(clipImage->bounds()));
const SkIRect srcBounds = srcImage->bounds().makeOffset(x, y);
SkIRect maskBounds = fRCStack.rc().getBounds();
if (!maskBounds.intersect(clipBounds.roundOut()) || !maskBounds.intersect(srcBounds)) {
return;
}
sk_sp<SkSurface> surf = SkSurface::MakeRaster(SkImageInfo::MakeA8(maskBounds.width(),
maskBounds.height()));
SkCanvas* canvas = surf->getCanvas();
canvas->translate(-maskBounds.x(), -maskBounds.y());
canvas->concat(totalMatrix);
canvas->drawImage(clipImage, 0, 0);
sk_sp<SkImage> mask = surf->makeImageSnapshot();
const SkMatrix m = SkMatrix::MakeTrans(x - maskBounds.x(), y - maskBounds.y());
paint.writable()->setShader(srcImage->makeShader(&m));
SkAutoDeviceCTMRestore adctmr(this, SkMatrix::I());
this->drawImage(mask.get(), maskBounds.x(), maskBounds.y(), *paint);
}
sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkBitmap& bitmap) {

View File

@ -112,7 +112,8 @@ protected:
///////////////////////////////////////////////////////////////////////////
void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&) override;
void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
SkImage*, const SkMatrix&) override;
sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
sk_sp<SkSpecialImage> snapSpecial() override;

View File

@ -253,12 +253,17 @@ struct DeviceCM {
SkRasterClip fClip;
std::unique_ptr<const SkPaint> fPaint; // may be null (in the future)
SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer
sk_sp<SkImage> fClipImage;
SkMatrix fClipMatrix;
DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed)
DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed,
sk_sp<SkImage> clipImage, const SkMatrix* clipMatrix)
: fNext(nullptr)
, fDevice(std::move(device))
, fPaint(paint ? skstd::make_unique<SkPaint>(*paint) : nullptr)
, fStashedMatrix(stashed)
, fClipImage(std::move(clipImage))
, fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I())
{}
void reset(const SkIRect& bounds) {
@ -649,7 +654,7 @@ SkBaseDevice* SkCanvas::init(SkBaseDevice* device, InitFlags flags) {
SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage));
fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage;
new (fDeviceCMStorage) DeviceCM(sk_ref_sp(device), nullptr, fMCRec->fMatrix);
new (fDeviceCMStorage) DeviceCM(sk_ref_sp(device), nullptr, fMCRec->fMatrix, nullptr, nullptr);
fMCRec->fTopLayer = fMCRec->fLayer;
@ -1010,13 +1015,14 @@ int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPai
}
int SkCanvas::saveLayer(const SaveLayerRec& origRec) {
SaveLayerRec rec(origRec);
SkTCopyOnFirstWrite<SaveLayerRec> rec(origRec);
if (gIgnoreSaveLayerBounds) {
rec.fBounds = nullptr;
rec.writable()->fBounds = nullptr;
}
SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec);
SaveLayerStrategy strategy = this->getSaveLayerStrategy(*rec);
fSaveCount += 1;
this->internalSaveLayer(rec, strategy);
this->internalSaveLayer(*rec, strategy);
return this->getSaveCount() - 1;
}
@ -1041,7 +1047,7 @@ void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filt
int y = src->getOrigin().y() - dstOrigin.y();
auto special = src->snapSpecial();
if (special) {
dst->drawSpecial(special.get(), x, y, p);
dst->drawSpecial(special.get(), x, y, p, nullptr, SkMatrix::I());
}
}
@ -1148,7 +1154,7 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra
return;
}
}
DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix);
DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix);
// only have a "next" if this new layer doesn't affect the clip (rare)
layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer;
@ -1209,7 +1215,8 @@ void SkCanvas::internalRestore() {
if (fMCRec) {
const SkIPoint& origin = layer->fDevice->getOrigin();
this->internalDrawDevice(layer->fDevice.get(), origin.x(), origin.y(),
layer->fPaint.get());
layer->fPaint.get(),
layer->fClipImage.get(), layer->fClipMatrix);
// restore what we smashed in internalSaveLayer
fMCRec->fMatrix = layer->fStashedMatrix;
// reset this, since internalDrawDevice will have set it to true
@ -1302,7 +1309,8 @@ bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) {
/////////////////////////////////////////////////////////////////////////////
void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint) {
void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPaint* paint,
SkImage* clipImage, const SkMatrix& clipMatrix) {
SkPaint tmp;
if (nullptr == paint) {
paint = &tmp;
@ -1315,10 +1323,11 @@ void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, const SkPa
paint = &looper.paint();
SkImageFilter* filter = paint->getImageFilter();
SkIPoint pos = { x - iter.getX(), y - iter.getY() };
if (filter) {
if (filter || clipImage) {
sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial();
if (specialImage) {
dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint);
dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint,
clipImage, clipMatrix);
}
} else {
dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint);
@ -2219,7 +2228,8 @@ void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const S
iter.fDevice->ctm().mapXY(x, y, &pt);
iter.fDevice->drawSpecial(special.get(),
SkScalarRoundToInt(pt.fX),
SkScalarRoundToInt(pt.fY), pnt);
SkScalarRoundToInt(pt.fY), pnt,
nullptr, SkMatrix::I());
} else {
iter.fDevice->drawImage(image, x, y, pnt);
}
@ -2300,7 +2310,8 @@ void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, cons
iter.fDevice->ctm().mapXY(x, y, &pt);
iter.fDevice->drawSpecial(special.get(),
SkScalarRoundToInt(pt.fX),
SkScalarRoundToInt(pt.fY), pnt);
SkScalarRoundToInt(pt.fY), pnt,
nullptr, SkMatrix::I());
} else {
iter.fDevice->drawBitmap(bitmap, matrix, looper.paint());
}

View File

@ -229,10 +229,13 @@ public:
SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
sk_sp<SkImageFilter> backdrop = rec.fBackdrop ? fXformer->apply(rec.fBackdrop) : nullptr;
sk_sp<SkImage> clipMask = rec.fClipMask ? fXformer->apply(rec.fClipMask.get()) : nullptr;
fTarget->saveLayer({
rec.fBounds,
MaybePaint(rec.fPaint, fXformer.get()),
backdrop.get(),
std::move(clipMask),
rec.fClipMatrix,
rec.fSaveLayerFlags,
});
return kNoLayer_SaveLayerStrategy;

View File

@ -282,7 +282,8 @@ void SkBaseDevice::drawAtlas(const SkImage* atlas, const SkRSXform xform[],
///////////////////////////////////////////////////////////////////////////////////////////////////
void SkBaseDevice::drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&) {}
void SkBaseDevice::drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
SkImage*, const SkMatrix&) {}
sk_sp<SkSpecialImage> SkBaseDevice::makeSpecial(const SkBitmap&) { return nullptr; }
sk_sp<SkSpecialImage> SkBaseDevice::makeSpecial(const SkImage*) { return nullptr; }
sk_sp<SkSpecialImage> SkBaseDevice::snapSpecial() { return nullptr; }

View File

@ -258,7 +258,8 @@ protected:
virtual void drawTextRSXform(const void* text, size_t len, const SkRSXform[],
const SkPaint&);
virtual void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&);
virtual void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
SkImage* clipImage, const SkMatrix& clipMatrix);
virtual sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&);
virtual sk_sp<SkSpecialImage> makeSpecial(const SkImage*);
virtual sk_sp<SkSpecialImage> snapSpecial();

View File

@ -96,6 +96,8 @@ public:
SkMask* mask, SkMask::CreateMode mode,
SkStrokeRec::InitStyle style);
void drawDevMask(const SkMask& mask, const SkPaint&) const;
enum RectType {
kHair_RectType,
kFill_RectType,
@ -122,7 +124,6 @@ public:
const SkPaint&, const SkSurfaceProps*) const;
static SkScalar ComputeResScaleForStroking(const SkMatrix& );
private:
void drawDevMask(const SkMask& mask, const SkPaint&) const;
void drawBitmapAsMask(const SkBitmap&, const SkPaint&) const;
void drawPath(const SkPath&, const SkPaint&, const SkMatrix* preMatrix,

View File

@ -91,18 +91,24 @@ namespace {
struct SaveLayer final : Op {
static const auto kType = Type::SaveLayer;
SaveLayer(const SkRect* bounds, const SkPaint* paint,
const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags) {
const SkImageFilter* backdrop, sk_sp<SkImage> clipMask,
const SkMatrix* clipMatrix, SkCanvas::SaveLayerFlags flags) {
if (bounds) { this->bounds = *bounds; }
if (paint) { this->paint = *paint; }
this->backdrop = sk_ref_sp(backdrop);
this->clipMask = std::move(clipMask);
this->clipMatrix = clipMatrix ? *clipMatrix : SkMatrix::I();
this->flags = flags;
}
SkRect bounds = kUnset;
SkPaint paint;
sk_sp<const SkImageFilter> backdrop;
sk_sp<SkImage> clipMask;
SkMatrix clipMatrix;
SkCanvas::SaveLayerFlags flags;
void draw(SkCanvas* c, const SkMatrix&) const {
c->saveLayer({ maybe_unset(bounds), &paint, backdrop.get(), flags });
c->saveLayer({ maybe_unset(bounds), &paint, backdrop.get(), clipMask,
clipMatrix.isIdentity() ? nullptr : &clipMatrix, flags });
}
};
@ -544,8 +550,9 @@ void SkLiteDL::setDrawFilter(SkDrawFilter* df) {
void SkLiteDL:: save() { this->push <Save>(0); }
void SkLiteDL::restore() { this->push<Restore>(0); }
void SkLiteDL::saveLayer(const SkRect* bounds, const SkPaint* paint,
const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags) {
this->push<SaveLayer>(0, bounds, paint, backdrop, flags);
const SkImageFilter* backdrop, sk_sp<SkImage> clipMask,
const SkMatrix* clipMatrix, SkCanvas::SaveLayerFlags flags) {
this->push<SaveLayer>(0, bounds, paint, backdrop, std::move(clipMask), clipMatrix, flags);
}
void SkLiteDL:: concat(const SkMatrix& matrix) { this->push <Concat>(0, matrix); }

View File

@ -29,7 +29,8 @@ public:
#endif
void save();
void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, SkCanvas::SaveLayerFlags);
void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, sk_sp<SkImage>,
const SkMatrix*, SkCanvas::SaveLayerFlags);
void restore();
void concat (const SkMatrix&);

View File

@ -31,7 +31,8 @@ SkDrawFilter* SkLiteRecorder::setDrawFilter(SkDrawFilter* df) {
void SkLiteRecorder::willSave() { fDL->save(); }
SkCanvas::SaveLayerStrategy SkLiteRecorder::getSaveLayerStrategy(const SaveLayerRec& rec) {
fDL->saveLayer(rec.fBounds, rec.fPaint, rec.fBackdrop, rec.fSaveLayerFlags);
fDL->saveLayer(rec.fBounds, rec.fPaint, rec.fBackdrop, rec.fClipMask, rec.fClipMatrix,
rec.fSaveLayerFlags);
return SkCanvas::kNoLayer_SaveLayerStrategy;
}
void SkLiteRecorder::willRestore() { fDL->restore(); }

View File

@ -122,6 +122,8 @@ enum SaveLayerRecFlatFlags {
SAVELAYERREC_HAS_PAINT = 1 << 1,
SAVELAYERREC_HAS_BACKDROP = 1 << 2,
SAVELAYERREC_HAS_FLAGS = 1 << 3,
SAVELAYERREC_HAS_CLIPMASK = 1 << 4,
SAVELAYERREC_HAS_CLIPMATRIX = 1 << 5,
};
///////////////////////////////////////////////////////////////////////////////

View File

@ -724,7 +724,8 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader,
canvas->saveLayer(SkCanvas::SaveLayerRec(boundsPtr, paint, flags));
} break;
case SAVE_LAYER_SAVELAYERREC: {
SkCanvas::SaveLayerRec rec(nullptr, nullptr, nullptr, 0);
SkCanvas::SaveLayerRec rec(nullptr, nullptr, nullptr, nullptr, nullptr, 0);
SkMatrix clipMatrix;
const uint32_t flatFlags = reader->readInt();
SkRect bounds;
if (flatFlags & SAVELAYERREC_HAS_BOUNDS) {
@ -742,6 +743,13 @@ void SkPicturePlayback::handleOp(SkReadBuffer* reader,
if (flatFlags & SAVELAYERREC_HAS_FLAGS) {
rec.fSaveLayerFlags = reader->readInt();
}
if (flatFlags & SAVELAYERREC_HAS_CLIPMASK) {
rec.fClipMask = sk_ref_sp(const_cast<SkImage*>(fPictureData->getImage(reader)));
}
if (flatFlags & SAVELAYERREC_HAS_CLIPMATRIX) {
reader->readMatrix(&clipMatrix);
rec.fClipMatrix = &clipMatrix;
}
BREAK_ON_READ_ERROR(reader);
canvas->saveLayer(rec);

View File

@ -98,6 +98,14 @@ void SkPictureRecord::recordSaveLayer(const SaveLayerRec& rec) {
flatFlags |= SAVELAYERREC_HAS_FLAGS;
size += sizeof(uint32_t);
}
if (rec.fClipMask) {
flatFlags |= SAVELAYERREC_HAS_CLIPMASK;
size += sizeof(uint32_t); // clip image index
}
if (rec.fClipMatrix) {
flatFlags |= SAVELAYERREC_HAS_CLIPMATRIX;
size += rec.fClipMatrix->writeToMemory(nullptr);
}
const size_t initialOffset = this->addDraw(SAVE_LAYER_SAVELAYERREC, &size);
this->addInt(flatFlags);
@ -116,6 +124,12 @@ void SkPictureRecord::recordSaveLayer(const SaveLayerRec& rec) {
if (flatFlags & SAVELAYERREC_HAS_FLAGS) {
this->addInt(rec.fSaveLayerFlags);
}
if (flatFlags & SAVELAYERREC_HAS_CLIPMASK) {
this->addImage(rec.fClipMask.get());
}
if (flatFlags & SAVELAYERREC_HAS_CLIPMATRIX) {
this->addMatrix(*rec.fClipMatrix);
}
this->validate(initialOffset, size);
}

View File

@ -77,6 +77,8 @@ DRAW(Save, save());
DRAW(SaveLayer, saveLayer(SkCanvas::SaveLayerRec(r.bounds,
r.paint,
r.backdrop.get(),
r.clipMask,
r.clipMatrix,
r.saveLayerFlags)));
DRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix)));
DRAW(Concat, concat(r.matrix));

View File

@ -188,8 +188,8 @@ struct SaveLayerDrawRestoreNooper {
typedef Pattern<Is<SaveLayer>, IsDraw, Is<Restore>> Match;
bool onMatch(SkRecord* record, Match* match, int begin, int end) {
if (match->first<SaveLayer>()->backdrop) {
// can't throw away the layer if we have a backdrop
if (match->first<SaveLayer>()->backdrop || match->first<SaveLayer>()->clipMask) {
// can't throw away the layer if we have a backdrop or clip mask
return false;
}

View File

@ -366,6 +366,8 @@ SkCanvas::SaveLayerStrategy SkRecorder::getSaveLayerStrategy(const SaveLayerRec&
APPEND(SaveLayer, this->copy(rec.fBounds)
, this->copy(rec.fPaint)
, sk_ref_sp(rec.fBackdrop)
, rec.fClipMask
, this->copy(rec.fClipMatrix)
, rec.fSaveLayerFlags);
return SkCanvas::kNoLayer_SaveLayerStrategy;
}

View File

@ -1125,17 +1125,18 @@ void SkGpuDevice::drawSprite(const SkBitmap& bitmap,
return;
}
this->drawSpecial(srcImg.get(), left, top, paint);
this->drawSpecial(srcImg.get(), left, top, paint, nullptr, SkMatrix::I());
}
void SkGpuDevice::drawSpecial(SkSpecialImage* special1,
int left, int top,
const SkPaint& paint) {
void SkGpuDevice::drawSpecial(SkSpecialImage* special1, int left, int top, const SkPaint& paint,
SkImage* clipImage,const SkMatrix& clipMatrix) {
ASSERT_SINGLE_OWNER
CHECK_SHOULD_DRAW();
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawSpecial", fContext.get());
// TODO: clipImage support.
SkIPoint offset = { 0, 0 };
sk_sp<SkSpecialImage> result;
@ -1354,7 +1355,7 @@ void SkGpuDevice::drawDevice(SkBaseDevice* device,
return;
}
this->drawSpecial(srcImg.get(), left, top, paint);
this->drawSpecial(srcImg.get(), left, top, paint, nullptr, SkMatrix::I());
}
void SkGpuDevice::drawImage(const SkImage* image, SkScalar x, SkScalar y,

View File

@ -110,8 +110,8 @@ public:
void drawBitmapLattice(const SkBitmap&, const SkCanvas::Lattice&,
const SkRect& dst, const SkPaint&) override;
void drawSpecial(SkSpecialImage*,
int left, int top, const SkPaint& paint) override;
void drawSpecial(SkSpecialImage*, int left, int top, const SkPaint& paint,
SkImage*, const SkMatrix&) override;
sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
sk_sp<SkSpecialImage> snapSpecial() override;

View File

@ -2202,10 +2202,12 @@ void SkPDFDevice::internalDrawImage(const SkMatrix& origMatrix,
#include "SkSpecialImage.h"
#include "SkImageFilter.h"
void SkPDFDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y,
const SkPaint& paint) {
void SkPDFDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y, const SkPaint& paint,
SkImage* clipImage, const SkMatrix& clipMatrix) {
SkASSERT(!srcImg->isTextureBacked());
//TODO: clipImage support
SkBitmap resultBM;
SkImageFilter* filter = paint.getImageFilter();

View File

@ -170,7 +170,8 @@ protected:
void drawAnnotation(const SkRect&, const char key[], SkData* value) override;
void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&) override;
void drawSpecial(SkSpecialImage*, int x, int y, const SkPaint&,
SkImage*, const SkMatrix&) override;
sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
sk_sp<SkSpecialImage> snapSpecial() override;

View File

@ -240,6 +240,12 @@ SkCanvas::SaveLayerStrategy SkPipeCanvas::getSaveLayerStrategy(const SaveLayerRe
if (rec.fBackdrop) {
extra |= kHasBackdrop_SaveLayerMask;
}
if (rec.fClipMask) {
extra |= kHasClipMask_SaveLayerMask;
}
if (rec.fClipMatrix) {
extra |= kHasClipMatrix_SaveLayerMask;
}
writer.write32(pack_verb(SkPipeVerb::kSaveLayer, extra));
if (rec.fBounds) {
@ -251,6 +257,13 @@ SkCanvas::SaveLayerStrategy SkPipeCanvas::getSaveLayerStrategy(const SaveLayerRe
if (rec.fBackdrop) {
writer.writeFlattenable(rec.fBackdrop);
}
if (rec.fClipMask) {
writer.writeImage(rec.fClipMask.get());
}
if (rec.fClipMatrix) {
writer.writeMatrix(*rec.fClipMatrix);
}
return kNoLayer_SaveLayerStrategy;
}

View File

@ -107,6 +107,8 @@ enum {
kHasPaint_SaveLayerMask = 1 << 9,
kHasBackdrop_SaveLayerMask = 1 << 10,
kDontClipToLayer_SaveLayerMask = 1 << 11,
kHasClipMask_SaveLayerMask = 1 << 12,
kHasClipMatrix_SaveLayerMask = 1 << 13,
};
enum {

View File

@ -244,6 +244,14 @@ static void saveLayer_handler(SkPipeReader& reader, uint32_t packedVerb, SkCanva
if (extra & kHasBackdrop_SaveLayerMask) {
backdrop = reader.readImageFilter();
}
sk_sp<SkImage> clipMask;
if (extra & kHasClipMask_SaveLayerMask) {
clipMask = reader.readImage();
}
SkMatrix clipMatrix;
if (extra & kHasClipMatrix_SaveLayerMask) {
reader.readMatrix(&clipMatrix);
}
SkCanvas::SaveLayerFlags flags = (SkCanvas::SaveLayerFlags)(extra & kFlags_SaveLayerMask);
// unremap this wacky flag
@ -251,7 +259,8 @@ static void saveLayer_handler(SkPipeReader& reader, uint32_t packedVerb, SkCanva
flags |= (1 << 31);//SkCanvas::kDontClipToLayer_PrivateSaveLayerFlag;
}
canvas->saveLayer(SkCanvas::SaveLayerRec(bounds, paint, backdrop.get(), flags));
canvas->saveLayer(SkCanvas::SaveLayerRec(bounds, paint, backdrop.get(), std::move(clipMask),
(extra & kHasClipMatrix_SaveLayerMask) ? &clipMatrix : nullptr, flags));
}
static void restore_handler(SkPipeReader& reader, uint32_t packedVerb, SkCanvas* canvas) {

View File

@ -190,10 +190,19 @@ DEF_TEST(RecordOpts_NoopSaveLayerDrawRestore, r) {
// saveLayer w/ backdrop should NOT go away
sk_sp<SkImageFilter> filter(SkBlurImageFilter::Make(3, 3, nullptr));
recorder.saveLayer({ nullptr, nullptr, filter.get(), 0});
recorder.saveLayer({ nullptr, nullptr, filter.get(), nullptr, nullptr, 0});
recorder.drawRect(draw, opaqueDrawPaint);
recorder.restore();
assert_savelayer_draw_restore(r, &record, 18, false);
// saveLayer w/ clip mask should also NOT go away
{
sk_sp<SkSurface> surface(SkSurface::MakeRasterN32Premul(10, 10));
recorder.saveLayer({ nullptr, nullptr, nullptr, surface->makeImageSnapshot(), nullptr, 0});
recorder.drawRect(draw, opaqueDrawPaint);
recorder.restore();
assert_savelayer_draw_restore(r, &record, 21, false);
}
}
#endif
@ -265,10 +274,12 @@ DEF_TEST(RecordOpts_MergeSvgOpacityAndFilterLayers, r) {
for (size_t m = 0; m < SK_ARRAY_COUNT(secondPaints); ++m) {
bool innerNoOped = !secondBounds[k] && !secondPaints[m] && !innerF;
recorder.saveLayer({firstBounds[i], firstPaints[j], outerF, 0});
recorder.saveLayer({firstBounds[i], firstPaints[j], outerF,
nullptr, nullptr, 0});
recorder.save();
recorder.clipRect(clip);
recorder.saveLayer({secondBounds[k], secondPaints[m], innerF, 0});
recorder.saveLayer({secondBounds[k], secondPaints[m], innerF,
nullptr, nullptr, 0});
recorder.restore();
recorder.restore();
recorder.restore();