@@ -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
126223class 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