Alias canComputeFastBounds to affectsTransparentBlack

This moves SkImageFilter's API closer to SkColorFilter's, and also
updates SkRecordDraw's PaintMayAffectTransparentBlack to be less
conservative.

Originally:
-canComputeFastBounds() handled aggregating behavior of the entire graph
  but it's a public API
-affectsTransparentBlack() was the private virtual for a specific node

Now:
-affectsTransparentBlack() handles aggregating behavior of the graph and
  is part of SkImageFilter_Base (mirroring SkColorFilter_Base).
-added onAffectsTransparentBlack() to clarify the per-node virtual that
  they can override.
-canComputeFastBounds() simply returns !affectsTransparentBlack().

There are some usages in our code that I kept using canComputeFastBounds
(e.g. SkPaint's computefastBounds) because it kept naming consistent.
In other places I updated to use affectsTransparentBlack since I thought
that made it clearer why we were checking that behavior.

Bug: skia:12282
Change-Id: I7b58372c127b4d8d9097d6c0de64486e822d2342
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/436296
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Michael Ludwig 2021-08-04 13:13:05 -04:00 committed by SkCQ
parent 9fdcc517b2
commit 3b39b7f5c6
10 changed files with 37 additions and 32 deletions

View File

@ -82,7 +82,7 @@ SkIRect SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
// Manually apply the crop rect for now, until cropping is performed by a dedicated SkIF.
SkIRect dst;
as_IFB(this)->getCropRect().applyTo(
SkIRect(output), ctm, as_IFB(this)->affectsTransparentBlack(), &dst);
SkIRect(output), ctm, as_IFB(this)->onAffectsTransparentBlack(), &dst);
return dst;
}
}
@ -104,16 +104,20 @@ SkRect SkImageFilter::computeFastBounds(const SkRect& src) const {
}
bool SkImageFilter::canComputeFastBounds() const {
if (as_IFB(this)->affectsTransparentBlack()) {
return false;
return !as_IFB(this)->affectsTransparentBlack();
}
bool SkImageFilter_Base::affectsTransparentBlack() const {
if (this->onAffectsTransparentBlack()) {
return true;
}
for (int i = 0; i < this->countInputs(); i++) {
const SkImageFilter* input = this->getInput(i);
if (input && !input->canComputeFastBounds()) {
return false;
if (input && as_IFB(input)->affectsTransparentBlack()) {
return true;
}
}
return true;
return false;
}
bool SkImageFilter::asAColorFilter(SkColorFilter** filterPtr) const {
@ -280,7 +284,7 @@ skif::LayerSpace<SkIRect> SkImageFilter_Base::getInputBounds(
mapping, desiredBounds, contentBounds);
// If we know what's actually going to be drawn into the layer, and we don't change transparent
// black, then we can further restrict the layer to what the known content is
if (knownContentBounds && this->canComputeFastBounds()) {
if (knownContentBounds && !this->affectsTransparentBlack()) {
if (!requiredInput.intersect(contentBounds)) {
// Nothing would be output by the filter, so return empty rect
return skif::LayerSpace<SkIRect>(SkIRect::MakeEmpty());
@ -301,7 +305,7 @@ skif::DeviceSpace<SkIRect> SkImageFilter_Base::getOutputBounds(
SkIRect dst;
as_IFB(this)->getCropRect().applyTo(
SkIRect(filterOutput), mapping.layerMatrix(),
as_IFB(this)->affectsTransparentBlack(), &dst);
as_IFB(this)->onAffectsTransparentBlack(), &dst);
// Map all the way to device space
return mapping.layerToDevice(skif::LayerSpace<SkIRect>(dst));
@ -371,7 +375,7 @@ void SkImageFilter_Base::CropRect::applyTo(const SkIRect& imageBounds, const SkM
bool SkImageFilter_Base::applyCropRect(const Context& ctx, const SkIRect& srcBounds,
SkIRect* dstBounds) const {
SkIRect tmpDst = this->onFilterNodeBounds(srcBounds, ctx.ctm(), kForward_MapDirection, nullptr);
fCropRect.applyTo(tmpDst, ctx.ctm(), this->affectsTransparentBlack(), dstBounds);
fCropRect.applyTo(tmpDst, ctx.ctm(), this->onAffectsTransparentBlack(), dstBounds);
// Intersect against the clip bounds, in case the crop rect has
// grown the bounds beyond the original clip. This can happen for
// example in tiling, where the clip is much smaller than the filtered

View File

@ -101,11 +101,9 @@ public:
skif::DeviceSpace<SkIRect> getOutputBounds(
const skif::Mapping& mapping, const skif::ParameterSpace<SkRect>& contentBounds) const;
// Expose isolated node bounds behavior for SampleImageFilterDAG and debugging
SkIRect filterNodeBounds(const SkIRect& srcRect, const SkMatrix& ctm,
MapDirection dir, const SkIRect* inputRect) const {
return this->onFilterNodeBounds(srcRect, ctm, dir, inputRect);
}
// Returns true if this image filter graph transforms a source transparent black pixel to a
// color other than transparent black.
bool affectsTransparentBlack() const;
/**
* Most ImageFilters can natively handle scaling and translate components in the CTM. Only
@ -377,7 +375,7 @@ private:
* transparent black. When false, optimizations can be taken to discard regions known to be
* transparent black and thus process fewer pixels.
*/
virtual bool affectsTransparentBlack() const { return false; }
virtual bool onAffectsTransparentBlack() const { return false; }
/**
* This is the virtual which should be overridden by the derived class to perform image

View File

@ -9,6 +9,7 @@
#include "include/core/SkImage.h"
#include "src/core/SkCanvasPriv.h"
#include "src/core/SkColorFilterBase.h"
#include "src/core/SkImageFilter_Base.h"
#include "src/core/SkRecordDraw.h"
#include "src/utils/SkPatchUtils.h"
@ -313,7 +314,8 @@ private:
static bool PaintMayAffectTransparentBlack(const SkPaint* paint) {
if (paint) {
// FIXME: this is very conservative
if (paint->getImageFilter() ||
if ((paint->getImageFilter() &&
as_IFB(paint->getImageFilter())->affectsTransparentBlack()) ||
(paint->getColorFilter() &&
as_CFB(paint->getColorFilter())->affectsTransparentBlack())) {
return true;

View File

@ -60,7 +60,7 @@ private:
friend void ::SkRegisterArithmeticImageFilterFlattenable();
SK_FLATTENABLE_HOOKS(SkArithmeticImageFilter)
bool affectsTransparentBlack() const override { return !SkScalarNearlyZero(fK[3]); }
bool onAffectsTransparentBlack() const override { return !SkScalarNearlyZero(fK[3]); }
SkV4 fK;
bool fEnforcePMColor;

View File

@ -29,7 +29,7 @@ protected:
sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
bool onIsColorFilterNode(SkColorFilter**) const override;
MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
bool affectsTransparentBlack() const override;
bool onAffectsTransparentBlack() const override;
private:
friend void ::SkRegisterColorFilterImageFilterFlattenable();
@ -152,6 +152,6 @@ bool SkColorFilterImageFilter::onIsColorFilterNode(SkColorFilter** filter) const
return false;
}
bool SkColorFilterImageFilter::affectsTransparentBlack() const {
bool SkColorFilterImageFilter::onAffectsTransparentBlack() const {
return as_CFB(fColorFilter)->affectsTransparentBlack();
}

View File

@ -418,7 +418,7 @@ protected:
buffer.writeScalar(fSurfaceScale * 255);
}
bool affectsTransparentBlack() const override { return true; }
bool onAffectsTransparentBlack() const override { return true; }
const SkImageFilterLight* light() const { return fLight.get(); }
inline sk_sp<const SkImageFilterLight> refLight() const { return fLight; }

View File

@ -58,7 +58,7 @@ protected:
sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm,
MapDirection, const SkIRect* inputRect) const override;
bool affectsTransparentBlack() const override;
bool onAffectsTransparentBlack() const override;
private:
friend void ::SkRegisterMatrixConvolutionImageFilterFlattenable();
@ -493,7 +493,7 @@ SkIRect SkMatrixConvolutionImageFilter::onFilterNodeBounds(
return dst;
}
bool SkMatrixConvolutionImageFilter::affectsTransparentBlack() const {
bool SkMatrixConvolutionImageFilter::onAffectsTransparentBlack() const {
// It seems that the only rational way for repeat sample mode to work is if the caller
// explicitly restricts the input in which case the input range is explicitly known and
// specified.

View File

@ -30,7 +30,7 @@ public:
, fEffect(std::move(effect))
, fUniforms(std::move(uniforms)) {}
bool affectsTransparentBlack() const override { return true; }
bool onAffectsTransparentBlack() const override { return true; }
MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kTranslate; }
protected:

View File

@ -26,8 +26,6 @@ public:
return sk_sp<SkImageFilter>(new SkShaderImageFilter(paint, rect));
}
bool affectsTransparentBlack() const override;
protected:
void flatten(SkWriteBuffer&) const override;
sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
@ -36,6 +34,8 @@ private:
friend void ::SkRegisterShaderImageFilterFlattenable();
SK_FLATTENABLE_HOOKS(SkShaderImageFilter)
bool onAffectsTransparentBlack() const override { return true; }
// This filter only applies the shader and dithering policy of the paint.
SkPaint fPaint;
@ -113,7 +113,3 @@ sk_sp<SkSpecialImage> SkShaderImageFilter::onFilterImage(const Context& ctx,
offset->fY = bounds.fTop;
return surf->makeImageSnapshot();
}
bool SkShaderImageFilter::affectsTransparentBlack() const {
return true;
}

View File

@ -199,13 +199,18 @@ DEF_TEST(RecordDraw_SaveLayerAffectsClipBounds, r) {
//
// The second bug showed up as adjusting the picture bounds (0,0,50,50) by the drop shadow too.
// The saveLayer, clipRect, and restore bounds were incorrectly (0,0,70,50).
//
// Now, all recorded bounds should be (0,0,40,40), representing the union of the original
// draw/clip (0,0,20,40) with the 20px offset drop shadow along the x-axis (20,0,40,40).
// The saveLayer and restore match the output bounds of the drop shadow filter, instead of
// expanding to fill the entire picture.
SkAutoTMalloc<SkRect> bounds(record.count());
SkAutoTMalloc<SkBBoxHierarchy::Metadata> meta(record.count());
SkRecordFillBounds(SkRect::MakeWH(50, 50), record, bounds, meta);
REPORTER_ASSERT(r, sloppy_rect_eq(bounds[0], SkRect::MakeLTRB(0, 0, 50, 50)));
REPORTER_ASSERT(r, sloppy_rect_eq(bounds[1], SkRect::MakeLTRB(0, 0, 50, 50)));
REPORTER_ASSERT(r, sloppy_rect_eq(bounds[0], SkRect::MakeLTRB(0, 0, 40, 40)));
REPORTER_ASSERT(r, sloppy_rect_eq(bounds[1], SkRect::MakeLTRB(0, 0, 40, 40)));
REPORTER_ASSERT(r, sloppy_rect_eq(bounds[2], SkRect::MakeLTRB(0, 0, 40, 40)));
REPORTER_ASSERT(r, sloppy_rect_eq(bounds[3], SkRect::MakeLTRB(0, 0, 50, 50)));
REPORTER_ASSERT(r, sloppy_rect_eq(bounds[3], SkRect::MakeLTRB(0, 0, 40, 40)));
}
DEF_TEST(RecordDraw_Metadata, r) {