Skip to content

Commit 3818fcc

Browse files
authored
Reland "render BackdropFilter layers directly to DisplayListBuilder" (flutter#34381)
1 parent 9e67369 commit 3818fcc

File tree

6 files changed

+473
-112
lines changed

6 files changed

+473
-112
lines changed

display_list/display_list_image_filter.h

Lines changed: 207 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,10 @@ class DlImageFilter
102102
// based on the supplied input bounds where both are measured in the local
103103
// (untransformed) coordinate space.
104104
//
105-
// The output bounds parameter must be supplied and the method will either
106-
// return a pointer to it with the result filled in, or it will return a
107-
// nullptr if it cannot determine the results.
105+
// The method will return a pointer to the output_bounds parameter if it
106+
// can successfully compute the output bounds of the filter, otherwise the
107+
// method will return a nullptr and the output_bounds will be filled with
108+
// a best guess for the answer, even if just a copy of the input_bounds.
108109
virtual SkRect* map_local_bounds(const SkRect& input_bounds,
109110
SkRect& output_bounds) const = 0;
110111

@@ -115,12 +116,108 @@ class DlImageFilter
115116
// is used in a rendering operation (for example, the blur radius of a
116117
// Blur filter will expand based on the ctm).
117118
//
118-
// The output bounds parameter must be supplied and the method will either
119-
// return a pointer to it with the result filled in, or it will return a
120-
// nullptr if it cannot determine the results.
119+
// The method will return a pointer to the output_bounds parameter if it
120+
// can successfully compute the output bounds of the filter, otherwise the
121+
// method will return a nullptr and the output_bounds will be filled with
122+
// a best guess for the answer, even if just a copy of the input_bounds.
121123
virtual SkIRect* map_device_bounds(const SkIRect& input_bounds,
122124
const SkMatrix& ctm,
123125
SkIRect& output_bounds) const = 0;
126+
127+
// Return the input bounds that will be needed in order for the filter to
128+
// properly fill the indicated output_bounds under the specified
129+
// transformation matrix. Both output_bounds and input_bounds are taken to
130+
// be relative to the transformed coordinate space of the provided |ctm|.
131+
//
132+
// The method will return a pointer to the input_bounds parameter if it
133+
// can successfully compute the required input bounds, otherwise the
134+
// method will return a nullptr and the input_bounds will be filled with
135+
// a best guess for the answer, even if just a copy of the output_bounds.
136+
virtual SkIRect* get_input_device_bounds(const SkIRect& output_bounds,
137+
const SkMatrix& ctm,
138+
SkIRect& input_bounds) const = 0;
139+
140+
protected:
141+
static SkVector map_vectors_affine(const SkMatrix& ctm,
142+
SkScalar x,
143+
SkScalar y) {
144+
FML_DCHECK(SkScalarIsFinite(x) && x >= 0);
145+
FML_DCHECK(SkScalarIsFinite(y) && y >= 0);
146+
FML_DCHECK(ctm.isFinite() && !ctm.hasPerspective());
147+
148+
// The x and y scalars would have been used to expand a local space
149+
// rectangle which is then transformed by ctm. In order to do the
150+
// expansion correctly, we should look at the relevant math. The
151+
// 4 corners will be moved outward by the following vectors:
152+
// (UL,UR,LR,LL) = ((-x, -y), (+x, -y), (+x, +y), (-x, +y))
153+
// After applying the transform, each of these vectors could be
154+
// pointing in any direction so we need to examine each transformed
155+
// delta vector and how it affected the bounds.
156+
// Looking at just the affine 2x3 entries of the CTM we can delta
157+
// transform these corner offsets and get the following:
158+
// UL = dCTM(-x, -y) = (- x*m00 - y*m01, - x*m10 - y*m11)
159+
// UR = dCTM(+x, -y) = ( x*m00 - y*m01, x*m10 - y*m11)
160+
// LR = dCTM(+x, +y) = ( x*m00 + y*m01, x*m10 + y*m11)
161+
// LL = dCTM(-x, +y) = (- x*m00 + y*m01, - x*m10 + y*m11)
162+
// The X vectors are all some variation of adding or subtracting
163+
// the sum of x*m00 and y*m01 or their difference. Similarly the Y
164+
// vectors are +/- the associated sum/difference of x*m10 and y*m11.
165+
// The largest displacements, both left/right or up/down, will
166+
// happen when the signs of the m00/m01/m10/m11 matrix entries
167+
// coincide with the signs of the scalars, i.e. are all positive.
168+
return {x * abs(ctm[0]) + y * abs(ctm[1]),
169+
x * abs(ctm[3]) + y * abs(ctm[4])};
170+
}
171+
172+
static SkIRect* inset_device_bounds(const SkIRect& input_bounds,
173+
SkScalar radius_x,
174+
SkScalar radius_y,
175+
const SkMatrix& ctm,
176+
SkIRect& output_bounds) {
177+
if (ctm.isFinite()) {
178+
if (ctm.hasPerspective()) {
179+
SkMatrix inverse;
180+
if (ctm.invert(&inverse)) {
181+
SkRect local_bounds = inverse.mapRect(SkRect::Make(input_bounds));
182+
local_bounds.inset(radius_x, radius_y);
183+
output_bounds = ctm.mapRect(local_bounds).roundOut();
184+
return &output_bounds;
185+
}
186+
} else {
187+
SkVector device_radius = map_vectors_affine(ctm, radius_x, radius_y);
188+
output_bounds = input_bounds.makeInset(floor(device_radius.fX), //
189+
floor(device_radius.fY));
190+
return &output_bounds;
191+
}
192+
}
193+
output_bounds = input_bounds;
194+
return nullptr;
195+
}
196+
197+
static SkIRect* outset_device_bounds(const SkIRect& input_bounds,
198+
SkScalar radius_x,
199+
SkScalar radius_y,
200+
const SkMatrix& ctm,
201+
SkIRect& output_bounds) {
202+
if (ctm.isFinite()) {
203+
if (ctm.hasPerspective()) {
204+
SkMatrix inverse;
205+
if (ctm.invert(&inverse)) {
206+
SkRect local_bounds = inverse.mapRect(SkRect::Make(input_bounds));
207+
local_bounds.outset(radius_x, radius_y);
208+
output_bounds = ctm.mapRect(local_bounds).roundOut();
209+
return &output_bounds;
210+
}
211+
} else {
212+
SkVector device_radius = map_vectors_affine(ctm, radius_x, radius_y);
213+
output_bounds = input_bounds.makeOutset(ceil(device_radius.fX), //
214+
ceil(device_radius.fY));
215+
return &output_bounds;
216+
}
217+
}
218+
output_bounds = input_bounds;
219+
return nullptr;
220+
}
124221
};
125222

126223
class DlBlurImageFilter final : public DlImageFilter {
@@ -154,16 +251,15 @@ class DlBlurImageFilter final : public DlImageFilter {
154251
SkIRect* map_device_bounds(const SkIRect& input_bounds,
155252
const SkMatrix& ctm,
156253
SkIRect& output_bounds) const override {
157-
SkVector device_sigma = ctm.mapVector(sigma_x_ * 3, sigma_y_ * 3);
158-
if (!SkScalarIsFinite(device_sigma.fX)) {
159-
device_sigma.fX = 0;
160-
}
161-
if (!SkScalarIsFinite(device_sigma.fY)) {
162-
device_sigma.fY = 0;
163-
}
164-
output_bounds = input_bounds.makeOutset(ceil(abs(device_sigma.fX)),
165-
ceil(abs(device_sigma.fY)));
166-
return &output_bounds;
254+
return outset_device_bounds(input_bounds, sigma_x_ * 3.0, sigma_y_ * 3.0,
255+
ctm, output_bounds);
256+
}
257+
258+
SkIRect* get_input_device_bounds(const SkIRect& output_bounds,
259+
const SkMatrix& ctm,
260+
SkIRect& input_bounds) const override {
261+
// Blurs are symmetric in terms of output-for-input and input-for-output
262+
return map_device_bounds(output_bounds, ctm, input_bounds);
167263
}
168264

169265
SkScalar sigma_x() const { return sigma_x_; }
@@ -217,16 +313,15 @@ class DlDilateImageFilter final : public DlImageFilter {
217313
SkIRect* map_device_bounds(const SkIRect& input_bounds,
218314
const SkMatrix& ctm,
219315
SkIRect& output_bounds) const override {
220-
SkVector device_radius = ctm.mapVector(radius_x_, radius_y_);
221-
if (!SkScalarIsFinite(device_radius.fX)) {
222-
device_radius.fX = 0;
223-
}
224-
if (!SkScalarIsFinite(device_radius.fY)) {
225-
device_radius.fY = 0;
226-
}
227-
output_bounds = input_bounds.makeOutset(ceil(abs(device_radius.fX)),
228-
ceil(abs(device_radius.fY)));
229-
return &output_bounds;
316+
return outset_device_bounds(input_bounds, radius_x_, radius_y_, ctm,
317+
output_bounds);
318+
}
319+
320+
SkIRect* get_input_device_bounds(const SkIRect& output_bounds,
321+
const SkMatrix& ctm,
322+
SkIRect& input_bounds) const override {
323+
return inset_device_bounds(output_bounds, radius_x_, radius_y_, ctm,
324+
input_bounds);
230325
}
231326

232327
SkScalar radius_x() const { return radius_x_; }
@@ -270,23 +365,22 @@ class DlErodeImageFilter final : public DlImageFilter {
270365

271366
SkRect* map_local_bounds(const SkRect& input_bounds,
272367
SkRect& output_bounds) const override {
273-
output_bounds = input_bounds.makeOutset(radius_x_, radius_y_);
368+
output_bounds = input_bounds.makeInset(radius_x_, radius_y_);
274369
return &output_bounds;
275370
}
276371

277372
SkIRect* map_device_bounds(const SkIRect& input_bounds,
278373
const SkMatrix& ctm,
279374
SkIRect& output_bounds) const override {
280-
SkVector device_radius = ctm.mapVector(radius_x_, radius_y_);
281-
if (!SkScalarIsFinite(device_radius.fX)) {
282-
device_radius.fX = 0;
283-
}
284-
if (!SkScalarIsFinite(device_radius.fY)) {
285-
device_radius.fY = 0;
286-
}
287-
output_bounds = input_bounds.makeOutset(ceil(abs(device_radius.fX)),
288-
ceil(abs(device_radius.fY)));
289-
return &output_bounds;
375+
return inset_device_bounds(input_bounds, radius_x_, radius_y_, ctm,
376+
output_bounds);
377+
}
378+
379+
SkIRect* get_input_device_bounds(const SkIRect& output_bounds,
380+
const SkMatrix& ctm,
381+
SkIRect& input_bounds) const override {
382+
return outset_device_bounds(output_bounds, radius_x_, radius_y_, ctm,
383+
input_bounds);
290384
}
291385

292386
SkScalar radius_x() const { return radius_x_; }
@@ -353,6 +447,23 @@ class DlMatrixImageFilter final : public DlImageFilter {
353447
return &output_bounds;
354448
}
355449

450+
SkIRect* get_input_device_bounds(const SkIRect& output_bounds,
451+
const SkMatrix& ctm,
452+
SkIRect& input_bounds) const override {
453+
SkMatrix matrix = SkMatrix::Concat(ctm, matrix_);
454+
SkMatrix inverse;
455+
if (!matrix.invert(&inverse)) {
456+
input_bounds = output_bounds;
457+
return nullptr;
458+
}
459+
inverse.postConcat(ctm);
460+
SkRect bounds;
461+
bounds.set(output_bounds);
462+
inverse.mapRect(&bounds);
463+
input_bounds = bounds.roundOut();
464+
return &input_bounds;
465+
}
466+
356467
sk_sp<SkImageFilter> skia_object() const override {
357468
return SkImageFilters::MatrixTransform(matrix_, ToSk(sampling_), nullptr);
358469
}
@@ -409,41 +520,61 @@ class DlComposeImageFilter final : public DlImageFilter {
409520

410521
SkRect* map_local_bounds(const SkRect& input_bounds,
411522
SkRect& output_bounds) const override {
412-
SkRect* ret = &output_bounds;
523+
SkRect cur_bounds = input_bounds;
524+
// We set this result in case neither filter is present.
525+
output_bounds = input_bounds;
413526
if (inner_) {
414-
if (!inner_->map_local_bounds(input_bounds, output_bounds)) {
415-
ret = nullptr;
527+
if (!inner_->map_local_bounds(cur_bounds, output_bounds)) {
528+
return nullptr;
416529
}
530+
cur_bounds = output_bounds;
417531
}
418-
if (ret && outer_) {
419-
if (!outer_->map_local_bounds(input_bounds, output_bounds)) {
420-
ret = nullptr;
532+
if (outer_) {
533+
if (!outer_->map_local_bounds(cur_bounds, output_bounds)) {
534+
return nullptr;
421535
}
422536
}
423-
if (!ret) {
424-
output_bounds = input_bounds;
425-
}
426-
return ret;
537+
return &output_bounds;
427538
}
428539

429540
SkIRect* map_device_bounds(const SkIRect& input_bounds,
430541
const SkMatrix& ctm,
431542
SkIRect& output_bounds) const override {
432-
SkIRect* ret = &output_bounds;
543+
SkIRect cur_bounds = input_bounds;
544+
// We set this result in case neither filter is present.
545+
output_bounds = input_bounds;
433546
if (inner_) {
434-
if (!inner_->map_device_bounds(input_bounds, ctm, output_bounds)) {
435-
ret = nullptr;
547+
if (!inner_->map_device_bounds(cur_bounds, ctm, output_bounds)) {
548+
return nullptr;
436549
}
550+
cur_bounds = output_bounds;
437551
}
438-
if (ret && outer_) {
439-
if (!outer_->map_device_bounds(input_bounds, ctm, output_bounds)) {
440-
ret = nullptr;
552+
if (outer_) {
553+
if (!outer_->map_device_bounds(cur_bounds, ctm, output_bounds)) {
554+
return nullptr;
441555
}
442556
}
443-
if (!ret) {
444-
output_bounds = input_bounds;
557+
return &output_bounds;
558+
}
559+
560+
SkIRect* get_input_device_bounds(const SkIRect& output_bounds,
561+
const SkMatrix& ctm,
562+
SkIRect& input_bounds) const override {
563+
SkIRect cur_bounds = output_bounds;
564+
// We set this result in case neither filter is present.
565+
input_bounds = output_bounds;
566+
if (outer_) {
567+
if (!outer_->get_input_device_bounds(cur_bounds, ctm, input_bounds)) {
568+
return nullptr;
569+
}
570+
cur_bounds = output_bounds;
445571
}
446-
return ret;
572+
if (inner_) {
573+
if (!inner_->get_input_device_bounds(cur_bounds, ctm, input_bounds)) {
574+
return nullptr;
575+
}
576+
}
577+
return &input_bounds;
447578
}
448579

449580
sk_sp<SkImageFilter> skia_object() const override {
@@ -513,6 +644,12 @@ class DlColorFilterImageFilter final : public DlImageFilter {
513644
return modifies_transparent_black() ? nullptr : &output_bounds;
514645
}
515646

647+
SkIRect* get_input_device_bounds(const SkIRect& output_bounds,
648+
const SkMatrix& ctm,
649+
SkIRect& input_bounds) const override {
650+
return map_device_bounds(output_bounds, ctm, input_bounds);
651+
}
652+
516653
sk_sp<SkImageFilter> skia_object() const override {
517654
return SkImageFilters::ColorFilter(color_filter_->skia_object(), nullptr);
518655
}
@@ -564,8 +701,8 @@ class DlUnknownImageFilter final : public DlImageFilter {
564701

565702
SkRect* map_local_bounds(const SkRect& input_bounds,
566703
SkRect& output_bounds) const override {
567-
output_bounds = input_bounds;
568704
if (modifies_transparent_black()) {
705+
output_bounds = input_bounds;
569706
return nullptr;
570707
}
571708
output_bounds = sk_filter_->computeFastBounds(input_bounds);
@@ -575,15 +712,27 @@ class DlUnknownImageFilter final : public DlImageFilter {
575712
SkIRect* map_device_bounds(const SkIRect& input_bounds,
576713
const SkMatrix& ctm,
577714
SkIRect& output_bounds) const override {
578-
output_bounds = input_bounds;
579715
if (modifies_transparent_black()) {
716+
output_bounds = input_bounds;
580717
return nullptr;
581718
}
582719
output_bounds = sk_filter_->filterBounds(
583720
input_bounds, ctm, SkImageFilter::kForward_MapDirection);
584721
return &output_bounds;
585722
}
586723

724+
SkIRect* get_input_device_bounds(const SkIRect& output_bounds,
725+
const SkMatrix& ctm,
726+
SkIRect& input_bounds) const override {
727+
if (modifies_transparent_black()) {
728+
input_bounds = output_bounds;
729+
return nullptr;
730+
}
731+
input_bounds = sk_filter_->filterBounds(
732+
output_bounds, ctm, SkImageFilter::kReverse_MapDirection);
733+
return &input_bounds;
734+
}
735+
587736
sk_sp<SkImageFilter> skia_object() const override { return sk_filter_; }
588737

589738
virtual ~DlUnknownImageFilter() = default;

0 commit comments

Comments
 (0)