Revert "Support multiple image filters in SaveLayerRec"
This reverts commit 7a97b766b14bc1d839643b9edd71a7b56ac14718.
Reason for revert: Fuzzer bug - abundance of caution
Original change's description:
> Support multiple image filters in SaveLayerRec
>
> The new API tries to match the WHATWG spec behavior: the layer contents
> are rendered once, then filtered and composited (using the layer's
> paint) in order.
>
> Bug: chromium:1418141
> Change-Id: Ia67f11ab622831d7f968bfa364fe54226b5d1d8c
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/784549
> Commit-Queue: Brian Osman <brianosman@google.com>
> Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Bug: chromium:1418141
Change-Id: I18ee6f9f84023913f8e4e47ba15d78d9691dcdff
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/790339
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Auto-Submit: Brian Osman <brianosman@google.com>
diff --git a/gm/imagefilters.cpp b/gm/imagefilters.cpp
index d98e4de..6004852 100644
--- a/gm/imagefilters.cpp
+++ b/gm/imagefilters.cpp
@@ -29,7 +29,6 @@
#include "include/effects/SkShaderMaskFilter.h"
#include "include/gpu/GrDirectContext.h"
#include "include/gpu/ganesh/SkImageGanesh.h"
-#include "src/core/SkCanvasPriv.h"
#include "tools/DecodeUtils.h"
#include "tools/Resources.h"
#include "tools/ToolUtils.h"
@@ -283,47 +282,3 @@
canvas->drawImage(image, 0, 0, SkSamplingOptions(), &testMaskPaint);
canvas->restore();
}
-
-DEF_SIMPLE_GM(multiple_filters, canvas, 415, 210) {
- ToolUtils::draw_checkerboard(canvas);
- canvas->translate(5, 5);
-
- auto drawFilteredLayer = [=](SkCanvas::FilterSpan filters) {
- SkPaint restorePaint;
- restorePaint.setAlphaf(0.5f);
- SkCanvas::SaveLayerRec rec = SkCanvasPriv::ScaledBackdropLayer(
- /*bounds=*/nullptr,
- &restorePaint,
- /*backdrop=*/nullptr,
- /*backdropScale=*/1,
- /*saveLayerFlags=*/0,
- filters);
- canvas->save();
- canvas->clipRect({0, 0, 200, 200});
- canvas->saveLayer(rec);
-
- SkPaint paint;
- paint.setStyle(SkPaint::kStroke_Style);
- paint.setStrokeWidth(20);
- paint.setColor(SK_ColorGREEN);
- canvas->drawCircle(100, 100, 70, paint);
-
- canvas->restore();
- canvas->restore();
- canvas->translate(205, 0);
- };
-
- {
- // Test with two non-null filters that each change bounds in a different way:
- sk_sp<SkImageFilter> filters[2] = {SkImageFilters::Dilate(5, 5, nullptr),
- SkImageFilters::Erode(5, 5, nullptr)};
- drawFilteredLayer(filters);
- }
-
- {
- // Test with one null filter, to more closely mimic the canvas2D layers use-case:
- sk_sp<SkImageFilter> filters[2] = {
- SkImageFilters::DropShadowOnly(7, 7, 5, 5, SK_ColorBLUE, nullptr), nullptr};
- drawFilteredLayer(filters);
- }
-}
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index f9d034c..32a9de8 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -24,13 +24,11 @@
#include "include/core/SkSamplingOptions.h"
#include "include/core/SkScalar.h"
#include "include/core/SkSize.h"
-#include "include/core/SkSpan.h"
#include "include/core/SkString.h"
#include "include/core/SkSurfaceProps.h"
#include "include/core/SkTypes.h"
#include "include/private/base/SkCPUTypes.h"
#include "include/private/base/SkDeque.h"
-#include "include/private/base/SkTArray.h"
#include <cstdint>
#include <cstring>
@@ -672,8 +670,7 @@
kF16ColorType = 1 << 4,
};
- using SaveLayerFlags = uint32_t;
- using FilterSpan = SkSpan<sk_sp<SkImageFilter>>;
+ typedef uint32_t SaveLayerFlags;
/** \struct SkCanvas::SaveLayerRec
SaveLayerRec contains the state used to create the layer.
@@ -693,7 +690,7 @@
@return SaveLayerRec with empty fBackdrop
*/
SaveLayerRec(const SkRect* bounds, const SkPaint* paint, SaveLayerFlags saveLayerFlags = 0)
- : SaveLayerRec(bounds, paint, nullptr, 1.f, saveLayerFlags, /*filters=*/{}) {}
+ : SaveLayerRec(bounds, paint, nullptr, 1.f, saveLayerFlags) {}
/** Sets fBounds, fPaint, fBackdrop, and fSaveLayerFlags.
@@ -709,7 +706,7 @@
*/
SaveLayerRec(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop,
SaveLayerFlags saveLayerFlags)
- : SaveLayerRec(bounds, paint, backdrop, 1.f, saveLayerFlags, /*filters=*/{}) {}
+ : SaveLayerRec(bounds, paint, backdrop, 1.f, saveLayerFlags) {}
/** hints at layer size limit */
const SkRect* fBounds = nullptr;
@@ -717,8 +714,6 @@
/** modifies overlay */
const SkPaint* fPaint = nullptr;
- FilterSpan fFilters = {};
-
/**
* If not null, this triggers the same initialization behavior as setting
* kInitWithPrevious_SaveLayerFlag on fSaveLayerFlags: the current layer is copied into
@@ -734,21 +729,13 @@
friend class SkCanvas;
friend class SkCanvasPriv;
- SaveLayerRec(const SkRect* bounds,
- const SkPaint* paint,
- const SkImageFilter* backdrop,
- SkScalar backdropScale,
- SaveLayerFlags saveLayerFlags,
- FilterSpan filters)
- : fBounds(bounds)
- , fPaint(paint)
- , fFilters(filters)
- , fBackdrop(backdrop)
- , fSaveLayerFlags(saveLayerFlags)
- , fExperimentalBackdropScale(backdropScale) {
- // We only allow the paint's image filter or the side-car list of filters -- not both.
- SkASSERT(fFilters.empty() || !paint || !paint->getImageFilter());
- }
+ SaveLayerRec(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop,
+ SkScalar backdropScale, SaveLayerFlags saveLayerFlags)
+ : fBounds(bounds)
+ , fPaint(paint)
+ , fBackdrop(backdrop)
+ , fSaveLayerFlags(saveLayerFlags)
+ , fExperimentalBackdropScale(backdropScale) {}
// Relative scale factor that the image content used to initialize the layer when the
// kInitFromPrevious flag or a backdrop filter is used.
@@ -2342,14 +2329,14 @@
// clip, and matrix commands. There is a layer per call to saveLayer() using the
// kFullLayer_SaveLayerStrategy.
struct Layer {
- sk_sp<SkDevice> fDevice;
- skia_private::STArray<1, sk_sp<SkImageFilter>> fImageFilters;
- SkPaint fPaint;
- bool fIsCoverage;
- bool fDiscard;
+ sk_sp<SkDevice> fDevice;
+ sk_sp<SkImageFilter> fImageFilter; // applied to layer *before* being drawn by paint
+ SkPaint fPaint;
+ bool fIsCoverage;
+ bool fDiscard;
Layer(sk_sp<SkDevice> device,
- FilterSpan imageFilters,
+ sk_sp<SkImageFilter> imageFilter,
const SkPaint& paint,
bool isCoverage);
};
@@ -2387,7 +2374,7 @@
~MCRec();
void newLayer(sk_sp<SkDevice> layerDevice,
- FilterSpan filters,
+ sk_sp<SkImageFilter> filter,
const SkPaint& restorePaint,
bool layerIsCoverage);
@@ -2523,7 +2510,7 @@
* relative transforms between the devices).
*/
void internalDrawDeviceWithFilter(SkDevice* src, SkDevice* dst,
- FilterSpan filters, const SkPaint& paint,
+ const SkImageFilter* filter, const SkPaint& paint,
DeviceCompatibleWithFilter compat,
SkScalar scaleFactor = 1.f,
bool srcIsCoverageLayer = false);
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 6289c0d..8ee0d51 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -24,7 +24,6 @@
#include "include/core/SkRRect.h"
#include "include/core/SkRSXform.h"
#include "include/core/SkRasterHandleAllocator.h"
-#include "include/core/SkRefCnt.h"
#include "include/core/SkRegion.h"
#include "include/core/SkShader.h"
#include "include/core/SkSurface.h"
@@ -34,6 +33,7 @@
#include "include/core/SkVertices.h"
#include "include/private/base/SkDebug.h"
#include "include/private/base/SkSafe32.h"
+#include "include/private/base/SkSpan_impl.h"
#include "include/private/base/SkTPin.h"
#include "include/private/base/SkTemplates.h"
#include "include/private/base/SkTo.h"
@@ -177,11 +177,11 @@
///////////////////////////////////////////////////////////////////////////////
SkCanvas::Layer::Layer(sk_sp<SkDevice> device,
- FilterSpan imageFilters,
+ sk_sp<SkImageFilter> imageFilter,
const SkPaint& paint,
bool isCoverage)
: fDevice(std::move(device))
- , fImageFilters(imageFilters.data(), imageFilters.size())
+ , fImageFilter(std::move(imageFilter))
, fPaint(paint)
, fIsCoverage(isCoverage)
, fDiscard(false) {
@@ -209,12 +209,12 @@
SkCanvas::MCRec::~MCRec() {}
void SkCanvas::MCRec::newLayer(sk_sp<SkDevice> layerDevice,
- FilterSpan filters,
+ sk_sp<SkImageFilter> filter,
const SkPaint& restorePaint,
bool layerIsCoverage) {
SkASSERT(!fBackImage);
- fLayer =
- std::make_unique<Layer>(std::move(layerDevice), filters, restorePaint, layerIsCoverage);
+ fLayer = std::make_unique<Layer>(std::move(layerDevice), std::move(filter),
+ restorePaint, layerIsCoverage);
fDevice = fLayer->fDevice.get();
}
@@ -553,17 +553,6 @@
return skif::ParameterSpace<SkPoint>(center);
}
-// Helper when we need to upgrade a single filter to a FilterSpan
-struct FilterToSpan {
- FilterToSpan(const SkImageFilter* filter) : fFilter(sk_ref_sp(filter)) {}
-
- operator SkCanvas::FilterSpan() {
- return fFilter ? SkCanvas::FilterSpan{&fFilter, 1} : SkCanvas::FilterSpan{};
- }
-
- sk_sp<SkImageFilter> fFilter;
-};
-
// Compute suitable transformations and layer bounds for a new layer that will be used as the source
// input into 'filter' before being drawn into 'dst' via the returned skif::Mapping.
// Null filters are permitted and act as the identity. The returned mapping will be compatible with
@@ -574,7 +563,7 @@
// if the image filter doesn't require an input image to produce a valid output.
static std::optional<std::pair<skif::Mapping, skif::LayerSpace<SkIRect>>>
get_layer_mapping_and_bounds(
- SkCanvas::FilterSpan filters,
+ const SkImageFilter* filter,
const SkMatrix& localToDst,
const skif::DeviceSpace<SkIRect>& targetOutput,
std::optional<skif::ParameterSpace<SkRect>> contentBounds = {},
@@ -601,13 +590,7 @@
// Determine initial mapping and a reasonable maximum dimension to prevent layer-to-device
// transforms with perspective and skew from triggering excessive buffer allocations.
skif::Mapping mapping;
- skif::MatrixCapability capability = skif::MatrixCapability::kComplex;
- for (const sk_sp<SkImageFilter>& filter : filters) {
- if (filter) {
- capability = std::min(capability, as_IFB(filter)->getCTMCapability());
- }
- }
- if (!mapping.decomposeCTM(localToDst, capability, center)) {
+ if (!mapping.decomposeCTM(localToDst, filter, center)) {
return {};
}
// Push scale factor into layer matrix and device matrix (net no change, but the layer will have
@@ -627,42 +610,28 @@
SkIRect(targetOutput).height64())),
kMinDimThreshold);
- auto baseLayerBounds = mapping.deviceToLayer(targetOutput);
- if (contentBounds) {
- // For better or for worse, user bounds currently act as a hard clip on the layer's
- // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
- skif::LayerSpace<SkIRect> knownBounds = mapping.paramToLayer(*contentBounds).roundOut();
- if (!baseLayerBounds.intersect(knownBounds)) {
- baseLayerBounds = skif::LayerSpace<SkIRect>::Empty();
- }
- }
-
skif::LayerSpace<SkIRect> layerBounds;
- if (!filters.empty()) {
- layerBounds = skif::LayerSpace<SkIRect>::Union(filters.size(), [&](int i) {
- return filters[i] ? as_IFB(filters[i])
- ->getInputBounds(mapping, targetOutput, contentBounds)
- : baseLayerBounds;
- });
+ if (filter) {
+ layerBounds = as_IFB(filter)->getInputBounds(mapping, targetOutput, contentBounds);
// When a filter is involved, the layer size may be larger than the default maxLayerDim due
// to required inputs for filters (e.g. a displacement map with a large radius).
if (layerBounds.width() > maxLayerDim || layerBounds.height() > maxLayerDim) {
skif::Mapping idealMapping{mapping.layerMatrix()};
- for (const sk_sp<SkImageFilter>& filter : filters) {
- if (filter) {
- auto idealLayerBounds = as_IFB(filter)->getInputBounds(
- idealMapping, targetOutput, contentBounds);
- maxLayerDim = std::max(std::max(idealLayerBounds.width(),
- idealLayerBounds.height()),
- maxLayerDim);
- }
- }
+ auto idealLayerBounds = as_IFB(filter)->getInputBounds(idealMapping, targetOutput,
+ contentBounds);
+ maxLayerDim = std::max(std::max(idealLayerBounds.width(), idealLayerBounds.height()),
+ maxLayerDim);
}
} else {
- if (baseLayerBounds.isEmpty()) {
- return {};
+ layerBounds = mapping.deviceToLayer(targetOutput);
+ if (contentBounds) {
+ // For better or for worse, user bounds currently act as a hard clip on the layer's
+ // extent (i.e., they implement the CSS filter-effects 'filter region' feature).
+ skif::LayerSpace<SkIRect> knownBounds = mapping.paramToLayer(*contentBounds).roundOut();
+ if (!layerBounds.intersect(knownBounds)) {
+ return {};
+ }
}
- layerBounds = baseLayerBounds;
}
if (layerBounds.width() > maxLayerDim || layerBounds.height() > maxLayerDim) {
@@ -717,7 +686,7 @@
void SkCanvas::internalDrawDeviceWithFilter(SkDevice* src,
SkDevice* dst,
- FilterSpan filters,
+ const SkImageFilter* filter,
const SkPaint& paint,
DeviceCompatibleWithFilter compat,
SkScalar scaleFactor,
@@ -775,7 +744,7 @@
// Compute the image filter mapping by decomposing the local->device matrix of dst and
// re-determining the required input.
auto mappingAndBounds = get_layer_mapping_and_bounds(
- filters, dst->localToDevice(), outputBounds, {}, SkTPin(scaleFactor, 0.f, 1.f));
+ filter, dst->localToDevice(), outputBounds, {}, SkTPin(scaleFactor, 0.f, 1.f));
if (!mappingAndBounds) {
return;
}
@@ -859,28 +828,24 @@
ctx = ctx.withNewDesiredOutput(mapping.deviceToLayer(outputBounds))
.withNewSource(source);
- // Here, we allow a single-element FilterSpan with a null entry, to simplify the loop:
- sk_sp<SkImageFilter> nullFilter;
- FilterSpan filtersOrNull = filters.empty() ? FilterSpan{&nullFilter, 1} : filters;
+ auto result = filter ? as_IFB(filter)->filterImage(ctx) : source;
- for (const sk_sp<SkImageFilter>& filter : filtersOrNull) {
- auto result = filter ? as_IFB(filter)->filterImage(ctx) : source;
-
- if (srcIsCoverageLayer) {
- SkASSERT(dst->useDrawCoverageMaskForMaskFilters());
- // TODO: Can FilterResult optimize this in any meaningful way if it still has to go
- // through drawCoverageMask that requires an image (vs a coverage shader)?
- auto [coverageMask, origin] = result.imageAndOffset(ctx);
- if (coverageMask) {
- SkMatrix deviceMatrixWithOffset = mapping.layerToDevice();
- deviceMatrixWithOffset.preTranslate(origin.x(), origin.y());
- dst->drawCoverageMask(
- coverageMask.get(), deviceMatrixWithOffset, result.sampling(), paint);
- }
- } else {
- result = apply_alpha_and_colorfilter(ctx, result, paint);
- result.draw(ctx, dst, paint.getBlender());
+ if (srcIsCoverageLayer) {
+ SkASSERT(dst->useDrawCoverageMaskForMaskFilters());
+ // TODO: Can FilterResult optimize this in any meaningful way if it still has to go through
+ // drawCoverageMask that requires an image (vs a coverage shader)?
+ auto [coverageMask, origin] = result.imageAndOffset(ctx);
+ if (coverageMask) {
+ SkMatrix deviceMatrixWithOffset = mapping.layerToDevice();
+ deviceMatrixWithOffset.preTranslate(origin.x(), origin.y());
+ dst->drawCoverageMask(coverageMask.get(),
+ deviceMatrixWithOffset,
+ result.sampling(),
+ paint);
}
+ } else {
+ result = apply_alpha_and_colorfilter(ctx, result, paint);
+ result.draw(ctx, dst, paint.getBlender());
}
}
@@ -897,13 +862,11 @@
void SkCanvas::internalDrawDeviceWithFilter(SkDevice* src,
SkDevice* dst,
- FilterSpan filters,
+ const SkImageFilter* filter,
const SkPaint& paint,
DeviceCompatibleWithFilter compat,
SkScalar scaleFactor,
bool srcIsCoverageLayer) {
- const SkImageFilter* filter = filters.empty() ? nullptr : filters.front().get();
-
// coverage image filters won't be supported in the old filter rendering code path
(void) srcIsCoverageLayer;
@@ -952,7 +915,7 @@
// Compute the image filter mapping by decomposing the local->device matrix of dst and
// re-determining the required input.
auto mappingAndBounds = get_layer_mapping_and_bounds(
- filters, dst->localToDevice(), skif::DeviceSpace<SkIRect>(dst->devClipBounds()),
+ filter, dst->localToDevice(), skif::DeviceSpace<SkIRect>(dst->devClipBounds()),
{}, true, SkTPin(scaleFactor, 0.f, 1.f));
if (!mappingAndBounds) {
return;
@@ -1170,12 +1133,8 @@
restorePaint.setAntiAlias(true);
#if defined(SK_RESOLVE_FILTERS_BEFORE_RESTORE)
- sk_sp<SkImageFilter> paintFilter = sk_ref_sp(optimize_layer_filter(
- rec.fPaint ? rec.fPaint->getImageFilter() : nullptr, &restorePaint));
-
- // Don't support multiple filters while using the old code path
- SkASSERT(rec.fFilters.empty());
- FilterSpan filters = paintFilter ? FilterSpan{&paintFilter, 1} : FilterSpan{};
+ const SkImageFilter* filter = optimize_layer_filter(
+ rec.fPaint ? rec.fPaint->getImageFilter() : nullptr, &restorePaint);
// Size the new layer relative to the prior device, which may already be aligned for filters.
SkDevice* priorDevice = this->topDevice();
@@ -1185,15 +1144,14 @@
if (rec.fBounds) {
contentBounds = skif::ParameterSpace<SkRect>(*rec.fBounds);
}
-
auto mappingAndBounds = get_layer_mapping_and_bounds(
- filters, priorDevice->localToDevice(),
+ filter, priorDevice->localToDevice(),
skif::DeviceSpace<SkIRect>(priorDevice->devClipBounds()),
contentBounds,
must_cover_prior_device(rec.fBackdrop, restorePaint));
#else
- sk_sp<SkImageFilter> paintFilter = rec.fPaint ? rec.fPaint->refImageFilter() : nullptr;
- FilterSpan filters = paintFilter ? FilterSpan{&paintFilter, 1} : rec.fFilters;
+
+ const SkImageFilter* filter = rec.fPaint ? rec.fPaint->getImageFilter() : nullptr;
const SkColorFilter* cf = restorePaint.getColorFilter();
const SkBlender* blender = restorePaint.getBlender();
@@ -1207,7 +1165,7 @@
// TODO(b/314968012): Chrome needs to be updated to clip saveAlphaLayer bounds explicitly when
// it uses kInitWithPrevious and LCD text.
filtersPriorDevice |= ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) &&
- (!filters.empty() || cf || blender || restorePaint.getAlphaf() < 1.f));
+ (filter || cf || blender || restorePaint.getAlphaf() < 1.f));
#endif
// If the restorePaint has a transparency-affecting colorfilter or blender, the output is
// unbounded during restore(). `internalDrawDeviceWithFilter` automatically applies these
@@ -1215,7 +1173,7 @@
// not apply effects beyond the layer's image so we mark `trivialRestore` as false too.
// TODO: drawDevice() could be updated to apply transparency-affecting effects to a content-
// clipped image, but this is the simplest solution when considering document-based SkDevices.
- const bool drawDeviceMustFillClip = filters.empty() &&
+ const bool drawDeviceMustFillClip = !filter &&
((cf && as_CFB(cf)->affectsTransparentBlack()) ||
(blender && as_BB(blender)->affectsTransparentBlack()));
const bool trivialRestore = !filtersPriorDevice && !drawDeviceMustFillClip;
@@ -1233,7 +1191,7 @@
}
auto mappingAndBounds = get_layer_mapping_and_bounds(
- filters, priorDevice->localToDevice(), outputBounds, contentBounds);
+ filter, priorDevice->localToDevice(), outputBounds, contentBounds);
#endif
auto abortLayer = [this]() {
@@ -1257,9 +1215,8 @@
// produce output, or that the filter DAG has no references to the dynamic source image.
// In this case it still has an output that we need to render, but do so now since there is
// no new layer pushed on the stack and the paired restore() will be a no-op.
- if (!filters.empty() && !priorDevice->isNoPixelsDevice()) {
+ if (filter && !priorDevice->isNoPixelsDevice()) {
#if defined(SK_RESOLVE_FILTERS_BEFORE_RESTORE)
- const SkImageFilter* filter = filters.empty() ? nullptr : filters.front().get();
skif::ParameterSpace<SkRect> emptyInput{SkRect::MakeEmpty()};
std::optional<skif::DeviceSpace<SkIRect>> output =
as_IFB(filter)->getOutputBounds(newLayerMapping, emptyInput);
@@ -1278,7 +1235,7 @@
restorePaint);
}
#else
- this->internalDrawDeviceWithFilter(/*src=*/nullptr, priorDevice, filters, restorePaint,
+ this->internalDrawDeviceWithFilter(/*src=*/nullptr, priorDevice, filter, restorePaint,
DeviceCompatibleWithFilter::kUnknown);
#endif
}
@@ -1344,23 +1301,22 @@
#else
const SkImageFilter* backdropFilter = rec.fBackdrop;
#endif
- FilterToSpan backdropAsSpan(backdropFilter);
// The new device was constructed to be compatible with 'filter', not necessarily
// 'rec.fBackdrop', so allow DrawDeviceWithFilter to transform the prior device contents
// if necessary to evaluate the backdrop filter. If no filters are involved, then the
// devices differ by integer translations and are always compatible.
bool scaleBackdrop = rec.fExperimentalBackdropScale != 1.0f;
- auto compat = (!filters.empty() || backdropFilter || scaleBackdrop)
+ auto compat = (filter || backdropFilter || scaleBackdrop)
? DeviceCompatibleWithFilter::kUnknown : DeviceCompatibleWithFilter::kYes;
this->internalDrawDeviceWithFilter(priorDevice, // src
newDevice.get(), // dst
- backdropAsSpan,
+ backdropFilter,
backdropPaint,
compat,
rec.fExperimentalBackdropScale);
}
- fMCRec->newLayer(std::move(newDevice), filters, restorePaint, coverageOnly);
+ fMCRec->newLayer(std::move(newDevice), sk_ref_sp(filter), restorePaint, coverageOnly);
fQuickRejectBounds = this->computeDeviceClipBounds();
}
@@ -1451,10 +1407,10 @@
// internalSaveLayer and internalRestore.
if (this->predrawNotify()) {
SkDevice* dstDev = this->topDevice();
- if (!layer->fImageFilters.empty()) {
+ if (layer->fImageFilter) {
this->internalDrawDeviceWithFilter(layer->fDevice.get(), // src
dstDev, // dst
- layer->fImageFilters,
+ layer->fImageFilter.get(),
layer->fPaint,
DeviceCompatibleWithFilter::kYes,
/*scaleFactor=*/1.0f,
@@ -2544,8 +2500,7 @@
skif::ParameterSpace<SkRect> imageBounds{dst};
skif::DeviceSpace<SkIRect> outputBounds{device->devClipBounds()};
- FilterToSpan filterAsSpan(realPaint.getImageFilter());
- auto mappingAndBounds = get_layer_mapping_and_bounds(filterAsSpan,
+ auto mappingAndBounds = get_layer_mapping_and_bounds(realPaint.getImageFilter(),
device->localToDevice(),
outputBounds,
imageBounds);
diff --git a/src/core/SkCanvasPriv.h b/src/core/SkCanvasPriv.h
index 43df100..35c9226b 100644
--- a/src/core/SkCanvasPriv.h
+++ b/src/core/SkCanvasPriv.h
@@ -80,10 +80,8 @@
const SkPaint* paint,
const SkImageFilter* backdrop,
SkScalar backdropScale,
- SkCanvas::SaveLayerFlags saveLayerFlags,
- SkCanvas::FilterSpan filters = {}) {
- return SkCanvas::SaveLayerRec(
- bounds, paint, backdrop, backdropScale, saveLayerFlags, filters);
+ SkCanvas::SaveLayerFlags saveLayerFlags) {
+ return SkCanvas::SaveLayerRec(bounds, paint, backdrop, backdropScale, saveLayerFlags);
}
static SkScalar GetBackdropScaleFactor(const SkCanvas::SaveLayerRec& rec) {
diff --git a/src/core/SkImageFilterTypes.cpp b/src/core/SkImageFilterTypes.cpp
index 3935a54..386bdbf 100644
--- a/src/core/SkImageFilterTypes.cpp
+++ b/src/core/SkImageFilterTypes.cpp
@@ -418,9 +418,12 @@
SkIRect RoundIn(SkRect r) { return r.makeOutset(kRoundEpsilon, kRoundEpsilon).roundIn(); }
-bool Mapping::decomposeCTM(const SkMatrix& ctm, MatrixCapability capability,
+bool Mapping::decomposeCTM(const SkMatrix& ctm, const SkImageFilter* filter,
const skif::ParameterSpace<SkPoint>& representativePt) {
SkMatrix remainder, layer;
+ using MatrixCapability = SkImageFilter_Base::MatrixCapability;
+ MatrixCapability capability =
+ filter ? as_IFB(filter)->getCTMCapability() : MatrixCapability::kComplex;
if (capability == MatrixCapability::kTranslate) {
// Apply the entire CTM post-filtering
remainder = ctm;
@@ -451,15 +454,6 @@
}
}
-bool Mapping::decomposeCTM(const SkMatrix& ctm,
- const SkImageFilter* filter,
- const skif::ParameterSpace<SkPoint>& representativePt) {
- return this->decomposeCTM(
- ctm,
- filter ? as_IFB(filter)->getCTMCapability() : MatrixCapability::kComplex,
- representativePt);
-}
-
bool Mapping::adjustLayerSpace(const SkMatrix& layer) {
SkMatrix invLayer;
if (!layer.invert(&invLayer)) {
diff --git a/src/core/SkImageFilterTypes.h b/src/core/SkImageFilterTypes.h
index 1b9ff5c..1233c72 100644
--- a/src/core/SkImageFilterTypes.h
+++ b/src/core/SkImageFilterTypes.h
@@ -533,16 +533,6 @@
SkMatrix fData;
};
-/**
- * Most ImageFilters can natively handle scaling and translate components in the CTM. Only some of
- * them can handle affine (or more complex) matrices. Some may only handle translation.
- */
-enum class MatrixCapability {
- kTranslate,
- kScaleTranslate,
- kComplex,
-};
-
// Mapping is the primary definition of the shared layer space used when evaluating an image filter
// DAG. It encapsulates any needed decomposition of the total CTM into the parameter-to-layer matrix
// (that filters use to map their parameters to the layer space), and the layer-to-device matrix
@@ -572,9 +562,6 @@
[[nodiscard]] bool decomposeCTM(const SkMatrix& ctm,
const SkImageFilter* filter,
const skif::ParameterSpace<SkPoint>& representativePt);
- [[nodiscard]] bool decomposeCTM(const SkMatrix& ctm,
- MatrixCapability,
- const skif::ParameterSpace<SkPoint>& representativePt);
// Update the mapping's parameter-to-layer matrix to be pre-concatenated with the specified
// local space transformation. This changes the definition of parameter space, any
diff --git a/src/core/SkImageFilter_Base.h b/src/core/SkImageFilter_Base.h
index 64becbb..ef2fce0 100644
--- a/src/core/SkImageFilter_Base.h
+++ b/src/core/SkImageFilter_Base.h
@@ -122,9 +122,15 @@
bool usesSource() const { return fUsesSrcInput; }
/**
+ * Most ImageFilters can natively handle scaling and translate components in the CTM. Only
+ * some of them can handle affine (or more complex) matrices. Some may only handle translation.
* This call returns the maximum "kind" of CTM for a filter and all of its (non-null) inputs.
*/
- using MatrixCapability = skif::MatrixCapability;
+ enum class MatrixCapability {
+ kTranslate,
+ kScaleTranslate,
+ kComplex,
+ };
MatrixCapability getCTMCapability() const;
uint32_t uniqueID() const { return fUniqueID; }
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index 9519557..99f1ce2 100644
--- a/src/core/SkPictureFlat.h
+++ b/src/core/SkPictureFlat.h
@@ -145,14 +145,13 @@
};
enum SaveLayerRecFlatFlags {
- SAVELAYERREC_HAS_BOUNDS = 1 << 0,
- SAVELAYERREC_HAS_PAINT = 1 << 1,
- SAVELAYERREC_HAS_BACKDROP = 1 << 2,
- SAVELAYERREC_HAS_FLAGS = 1 << 3,
+ SAVELAYERREC_HAS_BOUNDS = 1 << 0,
+ SAVELAYERREC_HAS_PAINT = 1 << 1,
+ SAVELAYERREC_HAS_BACKDROP = 1 << 2,
+ SAVELAYERREC_HAS_FLAGS = 1 << 3,
SAVELAYERREC_HAS_CLIPMASK_OBSOLETE = 1 << 4, // 6/13/2020
SAVELAYERREC_HAS_CLIPMATRIX_OBSOLETE = 1 << 5, // 6/13/2020
- SAVELAYERREC_HAS_BACKDROP_SCALE = 1 << 6,
- SAVELAYERREC_HAS_MULTIPLE_FILTERS = 1 << 7,
+ SAVELAYERREC_HAS_BACKDROP_SCALE = 1 << 6
};
enum SaveBehindFlatFlags {
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index 7f1f204..b6526e2 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -12,7 +12,6 @@
#include "include/core/SkColor.h"
#include "include/core/SkData.h"
#include "include/core/SkImage.h"
-#include "include/core/SkImageFilter.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPoint.h"
@@ -672,7 +671,6 @@
SkCanvas::SaveLayerRec rec(nullptr, nullptr, nullptr, 0);
const uint32_t flatFlags = reader->readInt();
SkRect bounds;
- skia_private::AutoSTArray<2, sk_sp<SkImageFilter>> filters;
if (flatFlags & SAVELAYERREC_HAS_BOUNDS) {
reader->readRect(&bounds);
rec.fBounds = &bounds;
@@ -698,16 +696,6 @@
(flatFlags & SAVELAYERREC_HAS_BACKDROP_SCALE)) {
SkCanvasPriv::SetBackdropScaleFactor(&rec, reader->readScalar());
}
- if (!reader->isVersionLT(SkPicturePriv::Version::kMultipleFiltersOnSaveLayer) &&
- (flatFlags & SAVELAYERREC_HAS_MULTIPLE_FILTERS)) {
- uint32_t filterCount = reader->readUInt();
- filters.reset(filterCount);
- for (uint32_t i = 0; i < filterCount; ++i) {
- const SkPaint& paint = fPictureData->requiredPaint(reader);
- filters[i] = paint.refImageFilter();
- }
- rec.fFilters = filters;
- }
BREAK_ON_READ_ERROR(reader);
canvas->saveLayer(rec);
diff --git a/src/core/SkPicturePriv.h b/src/core/SkPicturePriv.h
index 7371b63..0d09909 100644
--- a/src/core/SkPicturePriv.h
+++ b/src/core/SkPicturePriv.h
@@ -114,7 +114,6 @@
// V101: Crop image filter supports all SkTileModes instead of just kDecal
// V102: Convolution image filter uses ::Crop to apply tile mode
// V103: Remove deprecated per-image filter crop rect
- // v104: SaveLayer supports multiple image filters
enum Version {
kPictureShaderFilterParam_Version = 82,
@@ -139,7 +138,6 @@
kCropImageFilterSupportsTiling = 101,
kConvolutionImageFilterTilingUpdate = 102,
kRemoveDeprecatedCropRect = 103,
- kMultipleFiltersOnSaveLayer = 104,
// Only SKPs within the min/current picture version range (inclusive) can be read.
//
@@ -164,7 +162,7 @@
//
// Contact the Infra Gardener if the above steps do not work for you.
kMin_Version = kPictureShaderFilterParam_Version,
- kCurrent_Version = kMultipleFiltersOnSaveLayer
+ kCurrent_Version = kRemoveDeprecatedCropRect
};
};
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index cdb137d..d9d8391 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -111,7 +111,6 @@
// op + flatflags
size_t size = 2 * kUInt32Size;
uint32_t flatFlags = 0;
- uint32_t filterCount = SkToU32(rec.fFilters.size());
if (rec.fBounds) {
flatFlags |= SAVELAYERREC_HAS_BOUNDS;
@@ -133,11 +132,6 @@
flatFlags |= SAVELAYERREC_HAS_BACKDROP_SCALE;
size += sizeof(SkScalar);
}
- if (filterCount) {
- flatFlags |= SAVELAYERREC_HAS_MULTIPLE_FILTERS;
- size += sizeof(uint32_t); // count
- size += sizeof(uint32_t) * filterCount; // N (paint) indices
- }
const size_t initialOffset = this->addDraw(SAVE_LAYER_SAVELAYERREC, &size);
this->addInt(flatFlags);
@@ -159,15 +153,6 @@
if (flatFlags & SAVELAYERREC_HAS_BACKDROP_SCALE) {
this->addScalar(SkCanvasPriv::GetBackdropScaleFactor(rec));
}
- if (flatFlags & SAVELAYERREC_HAS_MULTIPLE_FILTERS) {
- this->addInt(filterCount);
- for (uint32_t i = 0; i < filterCount; ++i) {
- // overkill to store a paint, oh well.
- SkPaint paint;
- paint.setImageFilter(rec.fFilters[i]);
- this->addPaint(paint);
- }
- }
this->validate(initialOffset, size);
}
diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp
index 4f13602..8627070 100644
--- a/src/core/SkRecordDraw.cpp
+++ b/src/core/SkRecordDraw.cpp
@@ -40,8 +40,6 @@
#include <optional>
#include <vector>
-class SkImageFilter;
-
void SkRecordDraw(const SkRecord& record,
SkCanvas* canvas,
SkPicture const* const drawablePicts[],
@@ -95,15 +93,11 @@
#define DRAW(T, call) template <> void Draw::draw(const T& r) { fCanvas->call; }
DRAW(Restore, restore())
DRAW(Save, save())
-DRAW(SaveLayer,
- saveLayer(SkCanvasPriv::ScaledBackdropLayer(
- r.bounds,
- r.paint,
- r.backdrop.get(),
- r.backdropScale,
- r.saveLayerFlags,
- SkCanvas::FilterSpan{const_cast<sk_sp<SkImageFilter>*>(r.filters.data()),
- r.filters.size()})))
+DRAW(SaveLayer, saveLayer(SkCanvasPriv::ScaledBackdropLayer(r.bounds,
+ r.paint,
+ r.backdrop.get(),
+ r.backdropScale,
+ r.saveLayerFlags)))
template <> void Draw::draw(const SaveBehind& r) {
SkCanvasPriv::SaveBehind(fCanvas, r.subset);
diff --git a/src/core/SkRecordOpts.cpp b/src/core/SkRecordOpts.cpp
index 3d87e52..630ef7e 100644
--- a/src/core/SkRecordOpts.cpp
+++ b/src/core/SkRecordOpts.cpp
@@ -13,7 +13,6 @@
#include "include/core/SkPaint.h"
#include "include/core/SkRefCnt.h"
#include "include/private/base/SkMath.h"
-#include "include/private/base/SkTemplates.h"
#include "src/core/SkRecord.h"
#include "src/core/SkRecordPattern.h"
#include "src/core/SkRecords.h"
@@ -165,11 +164,6 @@
return false;
}
- if (!match->first<SaveLayer>()->filters.empty()) {
- // Our optimizations don't handle the filter list correctly - don't bother trying
- return false;
- }
-
// A SaveLayer's bounds field is just a hint, so we should be free to ignore it.
SkPaint* layerPaint = match->first<SaveLayer>()->paint;
SkPaint* drawPaint = match->second<SkPaint>();
@@ -223,12 +217,6 @@
return false;
}
- if (!match->first<SaveLayer>()->filters.empty() ||
- !match->fourth<SaveLayer>()->filters.empty()) {
- // Our optimizations don't handle the filter list correctly - don't bother trying
- return false;
- }
-
SkPaint* opacityPaint = match->first<SaveLayer>()->paint;
if (nullptr == opacityPaint) {
// There wasn't really any point to this SaveLayer at all.
diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp
index 04d73a0..b7b08da 100644
--- a/src/core/SkRecorder.cpp
+++ b/src/core/SkRecorder.cpp
@@ -11,7 +11,6 @@
#include "include/core/SkData.h"
#include "include/core/SkDrawable.h"
#include "include/core/SkImage.h"
-#include "include/core/SkImageFilter.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPaint.h"
@@ -341,17 +340,11 @@
}
SkCanvas::SaveLayerStrategy SkRecorder::getSaveLayerStrategy(const SaveLayerRec& rec) {
- AutoTArray<sk_sp<SkImageFilter>> filters(rec.fFilters.size());
- for (size_t i = 0; i < rec.fFilters.size(); ++i) {
- filters[i] = rec.fFilters[i];
- }
-
- this->append<SkRecords::SaveLayer>(this->copy(rec.fBounds),
- this->copy(rec.fPaint),
- sk_ref_sp(rec.fBackdrop),
- rec.fSaveLayerFlags,
- SkCanvasPriv::GetBackdropScaleFactor(rec),
- std::move(filters));
+ this->append<SkRecords::SaveLayer>(this->copy(rec.fBounds)
+ , this->copy(rec.fPaint)
+ , sk_ref_sp(rec.fBackdrop)
+ , rec.fSaveLayerFlags
+ , SkCanvasPriv::GetBackdropScaleFactor(rec));
return SkCanvas::kNoLayer_SaveLayerStrategy;
}
diff --git a/src/core/SkRecords.h b/src/core/SkRecords.h
index de9a9a8..840f127 100644
--- a/src/core/SkRecords.h
+++ b/src/core/SkRecords.h
@@ -184,8 +184,7 @@
Optional<SkPaint> paint;
sk_sp<const SkImageFilter> backdrop;
SkCanvas::SaveLayerFlags saveLayerFlags;
- SkScalar backdropScale;
- skia_private::AutoTArray<sk_sp<SkImageFilter>> filters)
+ SkScalar backdropScale)
RECORD(SaveBehind, 0,
Optional<SkRect> subset)