Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

@JsouLiang
Copy link
Contributor

Currently this technique relies on SkImageFilter::makeWithLocalMatrix, but that operation is not supported in DlImageFilter.
This pr is try to implement the DlLocalMatrixImageFilter
cc @flar

@JsouLiang JsouLiang requested a review from flar July 25, 2022 08:44
Copy link
Contributor

@flar flar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you run tests on how SkImageFilter deals with local matrices? How is the filtered output affected? The bounds methods?


SkRect* map_local_bounds(const SkRect& input_bounds,
SkRect& output_bounds) const override {
output_bounds = matrix_.mapRect(input_bounds);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should just be return image_filter_->map_local_bounds(...);

const SkMatrix& ctm,
SkIRect& output_bounds) const override {
return image_filter_->map_device_bounds(
input_bounds, SkMatrix::Concat(ctm, matrix_), output_bounds);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The input bounds are relative to ctm, but not to matrix_. You need to return bounds relative to only ctm.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I'm wondering what Skia does with this. Have you run tests to see how the bounds methods behave with a local matrix?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a test case:

auto blur_filter = SkImageFilters::Blur(5.0, 6.0, SkTileMode::kRepeat, nullptr);
  auto local_filter = blur_filter->makeWithLocalMatrix(SkMatrix::Scale(2, 2));
  auto inputBounds = SkIRect::MakeLTRB(20, 20, 80, 80);
  auto rect = local_filter->filterBounds(inputBounds, SkMatrix::I(), SkImageFilter::MapDirection::kForward_MapDirection);

  auto dl_color_filter = std::make_shared<DlBlurImageFilter>(5.0, 6.0, DlTileMode::kRepeat);
  auto local_matrix_filter = DlLocalMatrixImageFilter(SkMatrix::Scale(2, 2), dl_color_filter);
  SkIRect out_bounds;
  local_matrix_filter.map_device_bounds(inputBounds, SkMatrix::I(),out_bounds);
  ASSERT_EQ(out_bounds, rect);

the out_bouns is equal the rect when we use SkMatrix::Concat(ctm, matrix_)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very basic test of some of the cases that have the fewest quirks in them.

Try something like:

For each filter type:
  filter = instantiate that type;
  For each transform type local_matrix:
    transformed_filter = filter->makeWithLocal(local_matrix)
    For each transform type map_matrix:
      compare output of transformed_filter.map(..., map_matrix, ...);

Transform types should be:

  • translate
  • scale + translate
  • rotate + translate
  • perspective
  • any others?

SkIRect* get_input_device_bounds(const SkIRect& output_bounds,
const SkMatrix& ctm,
SkIRect& input_bounds) const override {
return image_filter_->get_input_device_bounds(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as map_device_bounds - which coordinate system should the returned bounds be relative to?

explicit DlLocalMatrixImageFilter(const DlLocalMatrixImageFilter* filter)
: DlLocalMatrixImageFilter(filter->matrix_, filter->image_filter_) {}
DlLocalMatrixImageFilter(const DlLocalMatrixImageFilter& filter)
: DlLocalMatrixImageFilter(&filter) {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no protection against a null filter in the constructors so everywhere image_filter_ is used here has to protect against null.

@JsouLiang
Copy link
Contributor Author

Did you run tests on how SkImageFilter deals with local matrices? How is the filtered output affected? The bounds methods?

I will add some tests to test it. Current version some method maybe is wrong

@JsouLiang JsouLiang requested a review from flar July 27, 2022 02:08
filter->asLocalMatrix();
FML_DCHECK(local_matrix_filter);
void* pod = Push<SetPodImageFilterOp>(local_matrix_filter->size(), 0);
new (pod) DlLocalMatrixImageFilter(local_matrix_filter);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pod allocations must be raw data, no pointers. This filter is incompatible with pod storage.

const SkMatrix& ctm,
SkIRect& output_bounds) const override {
return image_filter_->map_device_bounds(
input_bounds, SkMatrix::Concat(ctm, matrix_), output_bounds);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very basic test of some of the cases that have the fewest quirks in them.

Try something like:

For each filter type:
  filter = instantiate that type;
  For each transform type local_matrix:
    transformed_filter = filter->makeWithLocal(local_matrix)
    For each transform type map_matrix:
      compare output of transformed_filter.map(..., map_matrix, ...);

Transform types should be:

  • translate
  • scale + translate
  • rotate + translate
  • perspective
  • any others?

SkIRect& input_bounds) const = 0;

virtual MatrixCapability get_matrix_capability() const {
return MatrixCapability::kScaleTranslate;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did you get information about the compatibility here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Skia, the ImageFilter has a method onGetCTMCapability that will return the MatrixCapability.

@JsouLiang JsouLiang requested a review from flar July 27, 2022 12:25
@JsouLiang JsouLiang force-pushed the Create-DlLocalMatrixImageFilter branch from 1f91b6f to 56557ed Compare July 28, 2022 08:45
Push<SetSharedImageFilterOp>(0, 0, filter);
break;
}
case DlImageFilterType::kLocalMatrixFilter:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be added to the previous set of cases in this switch so we add it with a shared pointer rather than converting to an opaque Skia object.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(See SetSharedImageFilterOp implementation)

// until that's updated. if (this->cropRectIsSet()) {
// result = std::min(result, MatrixCapability::kScaleTranslate);
// }
for (auto* filter : filters()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels a little like overkill for our one type that has sub-filters. I would just make this virtual and have Compose return "min(outer->..., inner->...).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.
Another question is the Skia has a logic is check the cropRectIsSet, sorry I don't get what it means.

Should we need to add this check?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's the code I think you are referring to, I think they have a way to inject a crop rectangle on the inputs that we don't necessarily have (?yet?)?

return MatrixCapability::kScaleTranslate;
}

virtual std::vector<const DlImageFilter*> filters() const { return {this}; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually unnecessary for child-less filters - their own method was already called before this vector is considered so this default implementation just forces their own method to be called twice. An empty vector would be less redundant.

But, really, I think this vector solution is overkill for our current minimal tree.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now unused, please delete it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this method still here? It is unused. Please delete it.

@JsouLiang JsouLiang force-pushed the Create-DlLocalMatrixImageFilter branch from 56557ed to a730139 Compare July 29, 2022 07:46
@JsouLiang JsouLiang requested a review from flar July 29, 2022 07:47
return MatrixCapability::kScaleTranslate;
}

virtual std::vector<const DlImageFilter*> filters() const { return {this}; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now unused, please delete it.

return MatrixCapability::kScaleTranslate;
}

virtual std::vector<const DlImageFilter*> filters() const { return {this}; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this method still here? It is unused. Please delete it.

@JsouLiang JsouLiang force-pushed the Create-DlLocalMatrixImageFilter branch from a137693 to abd475b Compare August 9, 2022 03:49
@JsouLiang JsouLiang requested a review from flar August 9, 2022 03:49
Copy link
Contributor

@flar flar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to go. Make sure we have appropriate Skia bug(s) and Flutter Issue(s) for the problem in the unit tests and add them to the code before pushing...

@JsouLiang JsouLiang added the autosubmit Merge PR when tree becomes green via auto submit App label Aug 11, 2022
@auto-submit auto-submit bot merged commit f2fce02 into flutter:main Aug 11, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Aug 11, 2022
emilyabest pushed a commit to emilyabest/engine that referenced this pull request Aug 12, 2022
@bdero
Copy link
Member

bdero commented Sep 12, 2022

I'm having trouble understanding this implementation. Do the bounds mapping functions correctly mimic Skia's behavior?

The skia_object() implementation returns skia_object->makeWithLocalMatrix(matrix_);.
The device bounds mapping routines look like the intent is for the matrix to be applied within the CTM space before drawing takes place. In practice, the matrix seems to be applied in the parent space: https://fiddle.skia.org/c/338f53b8b36d4ba30e38cd91f0564c9d

If the bounds mapping code in this implementation was correct, I would expect the Skia fiddle above to translate the blurred image 600 pixels to the right -- but in practice, it only translates 100 pixels. Swapping over to the SkImageFilters::MatrixTransform implementation changes nothing about the result.

@JsouLiang
Copy link
Contributor Author

JsouLiang commented Sep 13, 2022

@bdero I found this issue when I wrote the unit test cases, and I have put forward an issus, there has been some discussion with @flar in the issus context
Maybe they're related.
This is the issue: flutter/flutter#108693

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

autosubmit Merge PR when tree becomes green via auto submit App

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants