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:
parent
9fdcc517b2
commit
3b39b7f5c6
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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; }
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user