Allow image filters to require a translation-only layer transform
Will be used for first version of runtime image filters. Bug: skia:12074 Change-Id: I2d7fc3af77d5df8182fd3f3d8da888e20ee05b34 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/416778 Commit-Queue: Brian Osman <brianosman@google.com> Reviewed-by: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
parent
4f4b5212d5
commit
03f1d12b64
@ -315,21 +315,21 @@ skif::FilterResult<For::kOutput> SkImageFilter_Base::onFilterImage(const skif::C
|
||||
return skif::FilterResult<For::kOutput>(std::move(image), skif::LayerSpace<SkIPoint>(origin));
|
||||
}
|
||||
|
||||
bool SkImageFilter_Base::canHandleComplexCTM() const {
|
||||
SkImageFilter_Base::MatrixCapability SkImageFilter_Base::getCTMCapability() const {
|
||||
MatrixCapability result = this->onGetCTMCapability();
|
||||
// CropRects need to apply in the source coordinate system, but are not aware of complex CTMs
|
||||
// when performing clipping. For a simple fix, any filter with a crop rect set cannot support
|
||||
// complex CTMs until that's updated.
|
||||
if (this->cropRectIsSet() || !this->onCanHandleComplexCTM()) {
|
||||
return false;
|
||||
// more than scale+translate CTMs until that's updated.
|
||||
if (this->cropRectIsSet()) {
|
||||
result = std::min(result, MatrixCapability::kScaleTranslate);
|
||||
}
|
||||
const int count = this->countInputs();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
const SkImageFilter_Base* input = as_IFB(this->getInput(i));
|
||||
if (input && !input->canHandleComplexCTM()) {
|
||||
return false;
|
||||
if (const SkImageFilter_Base* input = as_IFB(this->getInput(i))) {
|
||||
result = std::min(result, input->getCTMCapability());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
void SkImageFilter_Base::CropRect::applyTo(const SkIRect& imageBounds, const SkMatrix& ctm,
|
||||
|
@ -30,9 +30,16 @@ Mapping Mapping::DecomposeCTM(const SkMatrix& ctm, const SkImageFilter* filter,
|
||||
const skif::ParameterSpace<SkPoint>& representativePoint) {
|
||||
SkMatrix remainder, layer;
|
||||
SkSize scale;
|
||||
if (!filter || ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) {
|
||||
// It doesn't matter what type of matrix ctm is, we can have layer space be equivalent to
|
||||
// device space.
|
||||
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;
|
||||
layer = SkMatrix::I();
|
||||
} else if (ctm.isScaleTranslate() || capability == MatrixCapability::kComplex) {
|
||||
// Either layer space can be anything (kComplex) - or - it can be scale+translate, and the
|
||||
// ctm is. In both cases, the layer space can be equivalent to device space.
|
||||
remainder = SkMatrix::I();
|
||||
layer = ctm;
|
||||
} else if (ctm.decomposeScale(&scale, &remainder)) {
|
||||
|
@ -648,11 +648,11 @@ public:
|
||||
// layer space where the image filters are evaluated, as well as the remaining transformation
|
||||
// from the layer space to the final device space. The layer space defined by the returned
|
||||
// Mapping may be the same as the root device space, or be an intermediate space that is
|
||||
// supported by the image filter DAG (depending on what it returns from canHandleComplexCTM()).
|
||||
// If a node returns false from canHandleComplexCTM(), the layer matrix of the mapping will be
|
||||
// at most a scale + translate, and the remaining matrix will be appropriately set to transform
|
||||
// the layer space to the final device space (applied by the SkCanvas when filtering is
|
||||
// finished).
|
||||
// supported by the image filter DAG (depending on what it returns from getCTMCapability()).
|
||||
// If a node returns something other than kComplex from getCTMCapability(), the layer matrix of
|
||||
// the mapping will respect that return value, and the remaining matrix will be appropriately
|
||||
// set to transform the layer space to the final device space (applied by the SkCanvas when
|
||||
// filtering is finished).
|
||||
const Mapping& mapping() const { return fMapping; }
|
||||
// DEPRECATED: Use mapping() and its coordinate-space types instead
|
||||
const SkMatrix& ctm() const { return fMapping.layerMatrix(); }
|
||||
|
@ -108,11 +108,16 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* ImageFilters can natively handle scaling and translate components in the CTM. Only some of
|
||||
* them can handle affine (or more complex) matrices. This call returns true iff the filter
|
||||
* and all of its (non-null) inputs can handle these more complex matrices.
|
||||
* 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.
|
||||
*/
|
||||
bool canHandleComplexCTM() const;
|
||||
enum class MatrixCapability {
|
||||
kTranslate,
|
||||
kScaleTranslate,
|
||||
kComplex,
|
||||
};
|
||||
MatrixCapability getCTMCapability() const;
|
||||
|
||||
uint32_t uniqueID() const { return fUniqueID; }
|
||||
|
||||
@ -350,11 +355,14 @@ private:
|
||||
virtual bool onIsColorFilterNode(SkColorFilter** /*filterPtr*/) const { return false; }
|
||||
|
||||
/**
|
||||
* Return true if this filter can map from its parameter space to a layer space described by an
|
||||
* arbitrary transformation matrix. If this returns false, the filter only needs to worry about
|
||||
* mapping from parameter to layer using a scale+translate matrix.
|
||||
* Return the most complex matrix type this filter can support (mapping from its parameter
|
||||
* space to a layer space). If this returns anything less than kComplex, the filter only needs
|
||||
* to worry about mapping from parameter to layer using a matrix that is constrained in that
|
||||
* way (eg, scale+translate).
|
||||
*/
|
||||
virtual bool onCanHandleComplexCTM() const { return false; }
|
||||
virtual MatrixCapability onGetCTMCapability() const {
|
||||
return MatrixCapability::kScaleTranslate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this filter would transform transparent black pixels to a color other than
|
||||
|
@ -18,7 +18,9 @@ sk_sp<SkImageFilter> SkLocalMatrixImageFilter::Make(const SkMatrix& localM,
|
||||
if (localM.isIdentity()) {
|
||||
return input;
|
||||
}
|
||||
if (!as_IFB(input)->canHandleComplexCTM() && !localM.isScaleTranslate()) {
|
||||
MatrixCapability inputCapability = as_IFB(input)->getCTMCapability();
|
||||
if ((inputCapability == MatrixCapability::kTranslate && !localM.isTranslate()) ||
|
||||
(inputCapability == MatrixCapability::kScaleTranslate && !localM.isScaleTranslate())) {
|
||||
// Nothing we can do at this point
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ protected:
|
||||
SkIRect onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
|
||||
MapDirection, const SkIRect* inputRect) const override;
|
||||
|
||||
bool onCanHandleComplexCTM() const override { return true; }
|
||||
MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
|
||||
|
||||
private:
|
||||
SK_FLATTENABLE_HOOKS(SkLocalMatrixImageFilter)
|
||||
|
@ -28,7 +28,7 @@ protected:
|
||||
void flatten(SkWriteBuffer&) const override;
|
||||
sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
|
||||
bool onIsColorFilterNode(SkColorFilter**) const override;
|
||||
bool onCanHandleComplexCTM() const override { return true; }
|
||||
MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
|
||||
bool affectsTransparentBlack() const override;
|
||||
|
||||
private:
|
||||
|
@ -27,7 +27,7 @@ protected:
|
||||
sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
|
||||
SkIRect onFilterBounds(const SkIRect&, const SkMatrix& ctm,
|
||||
MapDirection, const SkIRect* inputRect) const override;
|
||||
bool onCanHandleComplexCTM() const override { return true; }
|
||||
MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
|
||||
|
||||
private:
|
||||
friend void ::SkRegisterComposeImageFilterFlattenable();
|
||||
|
@ -38,7 +38,7 @@ protected:
|
||||
SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm,
|
||||
MapDirection, const SkIRect* inputRect) const override;
|
||||
|
||||
bool onCanHandleComplexCTM() const override { return true; }
|
||||
MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
|
||||
|
||||
private:
|
||||
friend void ::SkRegisterImageImageFilterFlattenable();
|
||||
|
@ -26,7 +26,7 @@ public:
|
||||
|
||||
protected:
|
||||
sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
|
||||
bool onCanHandleComplexCTM() const override { return true; }
|
||||
MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
|
||||
|
||||
private:
|
||||
friend void ::SkRegisterMergeImageFilterFlattenable();
|
||||
|
@ -1786,26 +1786,27 @@ DEF_TEST(ImageFilterComplexCTM, reporter) {
|
||||
sk_sp<SkColorFilter> cf = SkColorFilters::Blend(SK_ColorRED, SkBlendMode::kSrcATop);
|
||||
sk_sp<SkImageFilter> cfif = SkImageFilters::ColorFilter(cf, nullptr); // can handle
|
||||
sk_sp<SkImageFilter> blif = SkImageFilters::Blur(3, 3, nullptr); // cannot handle
|
||||
using MatrixCapability = SkImageFilter_Base::MatrixCapability;
|
||||
|
||||
struct {
|
||||
sk_sp<SkImageFilter> fFilter;
|
||||
bool fExpectCanHandle;
|
||||
MatrixCapability fExpectCapability;
|
||||
} recs[] = {
|
||||
{ cfif, true },
|
||||
{ SkImageFilters::ColorFilter(cf, cfif), true },
|
||||
{ SkImageFilters::Merge(cfif, cfif), true },
|
||||
{ SkImageFilters::Compose(cfif, cfif), true },
|
||||
{ cfif, MatrixCapability::kComplex },
|
||||
{ SkImageFilters::ColorFilter(cf, cfif), MatrixCapability::kComplex },
|
||||
{ SkImageFilters::Merge(cfif, cfif), MatrixCapability::kComplex },
|
||||
{ SkImageFilters::Compose(cfif, cfif), MatrixCapability::kComplex },
|
||||
|
||||
{ blif, false },
|
||||
{ SkImageFilters::Blur(3, 3, cfif), false },
|
||||
{ SkImageFilters::ColorFilter(cf, blif), false },
|
||||
{ SkImageFilters::Merge(cfif, blif), false },
|
||||
{ SkImageFilters::Compose(blif, cfif), false },
|
||||
{ blif, MatrixCapability::kScaleTranslate },
|
||||
{ SkImageFilters::Blur(3, 3, cfif), MatrixCapability::kScaleTranslate },
|
||||
{ SkImageFilters::ColorFilter(cf, blif), MatrixCapability::kScaleTranslate },
|
||||
{ SkImageFilters::Merge(cfif, blif), MatrixCapability::kScaleTranslate },
|
||||
{ SkImageFilters::Compose(blif, cfif), MatrixCapability::kScaleTranslate },
|
||||
};
|
||||
|
||||
for (const auto& rec : recs) {
|
||||
const bool canHandle = as_IFB(rec.fFilter)->canHandleComplexCTM();
|
||||
REPORTER_ASSERT(reporter, canHandle == rec.fExpectCanHandle);
|
||||
const MatrixCapability capability = as_IFB(rec.fFilter)->getCTMCapability();
|
||||
REPORTER_ASSERT(reporter, capability == rec.fExpectCapability);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user