-
Notifications
You must be signed in to change notification settings - Fork 6k
Create DlLocalMatrixImageFilter #34878
Create DlLocalMatrixImageFilter #34878
Conversation
flar
left a comment
There was a problem hiding this 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); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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_)
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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) {} |
There was a problem hiding this comment.
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.
I will add some tests to test it. Current version some method maybe is wrong |
display_list/display_list_builder.cc
Outdated
| filter->asLocalMatrix(); | ||
| FML_DCHECK(local_matrix_filter); | ||
| void* pod = Push<SetPodImageFilterOp>(local_matrix_filter->size(), 0); | ||
| new (pod) DlLocalMatrixImageFilter(local_matrix_filter); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
1f91b6f to
56557ed
Compare
display_list/display_list_builder.cc
Outdated
| Push<SetSharedImageFilterOp>(0, 0, filter); | ||
| break; | ||
| } | ||
| case DlImageFilterType::kLocalMatrixFilter: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
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->...).
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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}; } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
56557ed to
a730139
Compare
| return MatrixCapability::kScaleTranslate; | ||
| } | ||
|
|
||
| virtual std::vector<const DlImageFilter*> filters() const { return {this}; } |
There was a problem hiding this comment.
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}; } |
There was a problem hiding this comment.
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.
a137693 to
abd475b
Compare
flar
left a comment
There was a problem hiding this 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...
|
I'm having trouble understanding this implementation. Do the bounds mapping functions correctly mimic Skia's behavior? The 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 |
|
@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 |
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