• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef SkImageFilterTypes_DEFINED
9 #define SkImageFilterTypes_DEFINED
10 
11 #include "include/core/SkMatrix.h"
12 #include "src/core/SkSpecialImage.h"
13 #include "src/core/SkSpecialSurface.h"
14 
15 class GrRecordingContext;
16 class SkImageFilter;
17 class SkImageFilterCache;
18 class SkSpecialSurface;
19 class SkSurfaceProps;
20 
21 // The skif (SKI[mage]F[ilter]) namespace contains types that are used for filter implementations.
22 // The defined types come in two groups: users of internal Skia types, and templates to help with
23 // readability. Image filters cannot be implemented without access to key internal types, such as
24 // SkSpecialImage. It is possible to avoid the use of the readability templates, although they are
25 // strongly encouraged.
26 namespace skif {
27 
28 // skif::IVector and skif::Vector represent plain-old-data types for storing direction vectors, so
29 // that the coordinate-space templating system defined below can have a separate type id for
30 // directions vs. points, and specialize appropriately. As such, all operations with direction
31 // vectors are defined on the LayerSpace specialization, since that is the intended point of use.
32 struct IVector {
33     int32_t fX;
34     int32_t fY;
35 
36     IVector() = default;
IVectorIVector37     IVector(int32_t x, int32_t y) : fX(x), fY(y) {}
IVectorIVector38     explicit IVector(const SkIVector& v) : fX(v.fX), fY(v.fY) {}
39 };
40 
41 struct Vector {
42     SkScalar fX;
43     SkScalar fY;
44 
45     Vector() = default;
VectorVector46     Vector(SkScalar x, SkScalar y) : fX(x), fY(y) {}
VectorVector47     explicit Vector(const SkVector& v) : fX(v.fX), fY(v.fY) {}
48 };
49 
50 ///////////////////////////////////////////////////////////////////////////////////////////////////
51 // Coordinate Space Tagging
52 // - In order to enforce correct coordinate spaces in image filter implementations and use,
53 //   geometry is wrapped by templated structs to declare in the type system what coordinate space
54 //   the coordinates are defined in.
55 // - Currently there is ParameterSpace and DeviceSpace that are data-only wrappers around
56 //   coordinates, and the primary LayerSpace that provides all operative functionality for image
57 //   filters. It is intended that all logic about image bounds and access be conducted in the shared
58 //   layer space.
59 // - The LayerSpace struct has type-safe specializations for SkIRect, SkRect, SkIPoint, SkPoint,
60 //   skif::IVector (to distinguish SkIVector from SkIPoint), skif::Vector, SkISize, and SkSize.
61 // - A Mapping object provides type safe coordinate conversions between these spaces, and
62 //   automatically does the "right thing" for each geometric type.
63 ///////////////////////////////////////////////////////////////////////////////////////////////////
64 
65 // ParameterSpace is a data-only wrapper around Skia's geometric types such as SkIPoint, and SkRect.
66 // Parameter space is the same as the local coordinate space of an SkShader, or the coordinates
67 // passed into SkCanvas::drawX calls, but "local" is avoided due to the alliteration with layer
68 // space. SkImageFilters are defined in terms of ParameterSpace<T> geometry and must use the Mapping
69 // on Context to transform the parameters into LayerSpace to evaluate the filter in the shared
70 // coordinate space of the entire filter DAG.
71 //
72 // A value of ParameterSpace<SkIRect> implies that its wrapped SkIRect is defined in the local
73 // parameter space.
74 template<typename T>
75 class ParameterSpace {
76 public:
77     ParameterSpace() = default;
ParameterSpace(const T & data)78     explicit ParameterSpace(const T& data) : fData(data) {}
ParameterSpace(T && data)79     explicit ParameterSpace(T&& data) : fData(std::move(data)) {}
80 
81     explicit operator const T&() const { return fData; }
82 
Optional(const T * ptr)83     static const ParameterSpace<T>* Optional(const T* ptr) {
84         return static_cast<const ParameterSpace<T>*>(reinterpret_cast<const void*>(ptr));
85     }
86 private:
87     T fData;
88 };
89 
90 // DeviceSpace is a data-only wrapper around Skia's geometric types. It is similar to
91 // 'ParameterSpace' except that it is used to represent geometry that has been transformed or
92 // defined in the root device space (i.e. the final pixels of drawn content). Much of what SkCanvas
93 // tracks, such as its clip bounds are defined in this space and DeviceSpace provides a
94 // type-enforced mechanism for the canvas to pass that information into the image filtering system,
95 // using the Mapping of the filtering context.
96 template<typename T>
97 class DeviceSpace {
98 public:
99     DeviceSpace() = default;
DeviceSpace(const T & data)100     explicit DeviceSpace(const T& data) : fData(data) {}
DeviceSpace(T && data)101     explicit DeviceSpace(T&& data) : fData(std::move(data)) {}
102 
103     explicit operator const T&() const { return fData; }
104 
105 private:
106     T fData;
107 };
108 
109 // LayerSpace is a geometric wrapper that specifies the geometry is defined in the shared layer
110 // space where image filters are evaluated. For a given Context (and its Mapping), the image filter
111 // DAG operates in the same coordinate space. This space may be different from the local coordinate
112 // space that defined the image filter parameters (such as blur sigma), and it may be different
113 // from the total CTM of the SkCanvas.
114 //
115 // To encourage correct filter use and implementation, the bulk of filter logic should be performed
116 // in layer space (e.g. determining what portion of an input image to read, or what the output
117 // region is). LayerSpace specializations for the six common Skia math types (Sk[I]Rect, Sk[I]Point,
118 // and Sk[I]Size), and skif::[I]Vector (to allow vectors to be specialized separately from points))
119 // are provided that mimic their APIs but preserve the coordinate space and enforce type semantics.
120 template<typename T>
121 class LayerSpace {};
122 
123 // Layer-space specialization for integerized direction vectors.
124 template<>
125 class LayerSpace<IVector> {
126 public:
127     LayerSpace() = default;
LayerSpace(const IVector & geometry)128     explicit LayerSpace(const IVector& geometry) : fData(geometry) {}
LayerSpace(IVector && geometry)129     explicit LayerSpace(IVector&& geometry) : fData(std::move(geometry)) {}
130     explicit operator const IVector&() const { return fData; }
131 
SkIVector()132     explicit operator SkIVector() const { return SkIVector::Make(fData.fX, fData.fY); }
133 
x()134     int32_t x() const { return fData.fX; }
y()135     int32_t y() const { return fData.fY; }
136 
137     LayerSpace<IVector> operator-() const { return LayerSpace<IVector>({-fData.fX, -fData.fY}); }
138 
139     LayerSpace<IVector> operator+(const LayerSpace<IVector>& v) const {
140         LayerSpace<IVector> sum = *this;
141         sum += v;
142         return sum;
143     }
144     LayerSpace<IVector> operator-(const LayerSpace<IVector>& v) const {
145         LayerSpace<IVector> diff = *this;
146         diff -= v;
147         return diff;
148     }
149 
150     void operator+=(const LayerSpace<IVector>& v) {
151         fData.fX += v.fData.fX;
152         fData.fY += v.fData.fY;
153     }
154     void operator-=(const LayerSpace<IVector>& v) {
155         fData.fX -= v.fData.fX;
156         fData.fY -= v.fData.fY;
157     }
158 
159 private:
160     IVector fData;
161 };
162 
163 // Layer-space specialization for floating point direction vectors.
164 template<>
165 class LayerSpace<Vector> {
166 public:
167     LayerSpace() = default;
LayerSpace(const Vector & geometry)168     explicit LayerSpace(const Vector& geometry) : fData(geometry) {}
LayerSpace(Vector && geometry)169     explicit LayerSpace(Vector&& geometry) : fData(std::move(geometry)) {}
170     explicit operator const Vector&() const { return fData; }
171 
SkVector()172     explicit operator SkVector() const { return SkVector::Make(fData.fX, fData.fY); }
173 
x()174     SkScalar x() const { return fData.fX; }
y()175     SkScalar y() const { return fData.fY; }
176 
length()177     SkScalar length() const { return SkVector::Length(fData.fX, fData.fY); }
178 
179     LayerSpace<Vector> operator-() const { return LayerSpace<Vector>({-fData.fX, -fData.fY}); }
180 
181     LayerSpace<Vector> operator*(SkScalar s) const {
182         LayerSpace<Vector> scaled = *this;
183         scaled *= s;
184         return scaled;
185     }
186 
187     LayerSpace<Vector> operator+(const LayerSpace<Vector>& v) const {
188         LayerSpace<Vector> sum = *this;
189         sum += v;
190         return sum;
191     }
192     LayerSpace<Vector> operator-(const LayerSpace<Vector>& v) const {
193         LayerSpace<Vector> diff = *this;
194         diff -= v;
195         return diff;
196     }
197 
198     void operator*=(SkScalar s) {
199         fData.fX *= s;
200         fData.fY *= s;
201     }
202     void operator+=(const LayerSpace<Vector>& v) {
203         fData.fX += v.fData.fX;
204         fData.fY += v.fData.fY;
205     }
206     void operator-=(const LayerSpace<Vector>& v) {
207         fData.fX -= v.fData.fX;
208         fData.fY -= v.fData.fY;
209     }
210 
211     friend LayerSpace<Vector> operator*(SkScalar s, const LayerSpace<Vector>& b) {
212         return b * s;
213     }
214 
215 private:
216     Vector fData;
217 };
218 
219 // Layer-space specialization for integer 2D coordinates (treated as positions, not directions).
220 template<>
221 class LayerSpace<SkIPoint> {
222 public:
223     LayerSpace() = default;
LayerSpace(const SkIPoint & geometry)224     explicit LayerSpace(const SkIPoint& geometry)  : fData(geometry) {}
LayerSpace(SkIPoint && geometry)225     explicit LayerSpace(SkIPoint&& geometry) : fData(std::move(geometry)) {}
226     explicit operator const SkIPoint&() const { return fData; }
227 
228     // Parrot the SkIPoint API while preserving coordinate space.
x()229     int32_t x() const { return fData.fX; }
y()230     int32_t y() const { return fData.fY; }
231 
232     // Offsetting by direction vectors produce more points
233     LayerSpace<SkIPoint> operator+(const LayerSpace<IVector>& v) {
234         return LayerSpace<SkIPoint>(fData + SkIVector(v));
235     }
236     LayerSpace<SkIPoint> operator-(const LayerSpace<IVector>& v) {
237         return LayerSpace<SkIPoint>(fData - SkIVector(v));
238     }
239 
240     void operator+=(const LayerSpace<IVector>& v) {
241         fData += SkIVector(v);
242     }
243     void operator-=(const LayerSpace<IVector>& v) {
244         fData -= SkIVector(v);
245     }
246 
247     // Subtracting another point makes a direction between them
248     LayerSpace<IVector> operator-(const LayerSpace<SkIPoint>& p) {
249         return LayerSpace<IVector>(IVector(fData - p.fData));
250     }
251 
252     LayerSpace<IVector> operator-() const { return LayerSpace<IVector>({-fData.fX, -fData.fY}); }
253 
254 private:
255     SkIPoint fData;
256 };
257 
258 // Layer-space specialization for floating point 2D coordinates (treated as positions)
259 template<>
260 class LayerSpace<SkPoint> {
261 public:
262     LayerSpace() = default;
LayerSpace(const SkPoint & geometry)263     explicit LayerSpace(const SkPoint& geometry) : fData(geometry) {}
LayerSpace(SkPoint && geometry)264     explicit LayerSpace(SkPoint&& geometry) : fData(std::move(geometry)) {}
265     explicit operator const SkPoint&() const { return fData; }
266 
267     // Parrot the SkPoint API while preserving coordinate space.
x()268     SkScalar x() const { return fData.fX; }
y()269     SkScalar y() const { return fData.fY; }
270 
distanceToOrigin()271     SkScalar distanceToOrigin() const { return fData.distanceToOrigin(); }
272 
273     // Offsetting by direction vectors produce more points
274     LayerSpace<SkPoint> operator+(const LayerSpace<Vector>& v) {
275         return LayerSpace<SkPoint>(fData + SkVector(v));
276     }
277     LayerSpace<SkPoint> operator-(const LayerSpace<Vector>& v) {
278         return LayerSpace<SkPoint>(fData - SkVector(v));
279     }
280 
281     void operator+=(const LayerSpace<Vector>& v) {
282         fData += SkVector(v);
283     }
284     void operator-=(const LayerSpace<Vector>& v) {
285         fData -= SkVector(v);
286     }
287 
288     // Subtracting another point makes a direction between them
289     LayerSpace<Vector> operator-(const LayerSpace<SkPoint>& p) {
290         return LayerSpace<Vector>(Vector(fData - p.fData));
291     }
292 
293     LayerSpace<Vector> operator-() const { return LayerSpace<Vector>({-fData.fX, -fData.fY}); }
294 
295 private:
296     SkPoint fData;
297 };
298 
299 // Layer-space specialization for integer dimensions
300 template<>
301 class LayerSpace<SkISize> {
302 public:
303     LayerSpace() = default;
LayerSpace(const SkISize & geometry)304     explicit LayerSpace(const SkISize& geometry) : fData(geometry) {}
LayerSpace(SkISize && geometry)305     explicit LayerSpace(SkISize&& geometry) : fData(std::move(geometry)) {}
306     explicit operator const SkISize&() const { return fData; }
307 
width()308     int32_t width() const { return fData.width(); }
height()309     int32_t height() const { return fData.height(); }
310 
isEmpty()311     bool isEmpty() const { return fData.isEmpty(); }
312 
313 private:
314     SkISize fData;
315 };
316 
317 // Layer-space specialization for floating point dimensions
318 template<>
319 class LayerSpace<SkSize> {
320 public:
321     LayerSpace() = default;
LayerSpace(const SkSize & geometry)322     explicit LayerSpace(const SkSize& geometry) : fData(geometry) {}
LayerSpace(SkSize && geometry)323     explicit LayerSpace(SkSize&& geometry) : fData(std::move(geometry)) {}
324     explicit operator const SkSize&() const { return fData; }
325 
width()326     SkScalar width() const { return fData.width(); }
height()327     SkScalar height() const { return fData.height(); }
328 
isEmpty()329     bool isEmpty() const { return fData.isEmpty(); }
isZero()330     bool isZero() const { return fData.isZero(); }
331 
round()332     LayerSpace<SkISize> round() const { return LayerSpace<SkISize>(fData.toRound()); }
ceil()333     LayerSpace<SkISize> ceil() const { return LayerSpace<SkISize>(fData.toCeil()); }
floor()334     LayerSpace<SkISize> floor() const { return LayerSpace<SkISize>(fData.toFloor()); }
335 
336 private:
337     SkSize fData;
338 };
339 
340 // Layer-space specialization for axis-aligned integer bounding boxes.
341 template<>
342 class LayerSpace<SkIRect> {
343 public:
344     LayerSpace() = default;
LayerSpace(const SkIRect & geometry)345     explicit LayerSpace(const SkIRect& geometry) : fData(geometry) {}
LayerSpace(SkIRect && geometry)346     explicit LayerSpace(SkIRect&& geometry) : fData(std::move(geometry)) {}
347     explicit operator const SkIRect&() const { return fData; }
348 
Empty()349     static LayerSpace<SkIRect> Empty() { return LayerSpace<SkIRect>(SkIRect::MakeEmpty()); }
350 
351     // Parrot the SkIRect API while preserving coord space
isEmpty()352     bool isEmpty() const { return fData.isEmpty(); }
353 
left()354     int32_t left() const { return fData.fLeft; }
top()355     int32_t top() const { return fData.fTop; }
right()356     int32_t right() const { return fData.fRight; }
bottom()357     int32_t bottom() const { return fData.fBottom; }
358 
width()359     int32_t width() const { return fData.width(); }
height()360     int32_t height() const { return fData.height(); }
361 
topLeft()362     LayerSpace<SkIPoint> topLeft() const { return LayerSpace<SkIPoint>(fData.topLeft()); }
size()363     LayerSpace<SkISize> size() const { return LayerSpace<SkISize>(fData.size()); }
364 
intersect(const LayerSpace<SkIRect> & r)365     bool intersect(const LayerSpace<SkIRect>& r) { return fData.intersect(r.fData); }
join(const LayerSpace<SkIRect> & r)366     void join(const LayerSpace<SkIRect>& r) { fData.join(r.fData); }
offset(const LayerSpace<IVector> & v)367     void offset(const LayerSpace<IVector>& v) { fData.offset(SkIVector(v)); }
outset(const LayerSpace<SkISize> & delta)368     void outset(const LayerSpace<SkISize>& delta) { fData.outset(delta.width(), delta.height()); }
369 
370 private:
371     SkIRect fData;
372 };
373 
374 // Layer-space specialization for axis-aligned float bounding boxes.
375 template<>
376 class LayerSpace<SkRect> {
377 public:
378     LayerSpace() = default;
LayerSpace(const SkRect & geometry)379     explicit LayerSpace(const SkRect& geometry) : fData(geometry) {}
LayerSpace(SkRect && geometry)380     explicit LayerSpace(SkRect&& geometry) : fData(std::move(geometry)) {}
381     explicit operator const SkRect&() const { return fData; }
382 
Empty()383     static LayerSpace<SkRect> Empty() { return LayerSpace<SkRect>(SkRect::MakeEmpty()); }
384 
385     // Parrot the SkRect API while preserving coord space and usage
isEmpty()386     bool isEmpty() const { return fData.isEmpty(); }
387 
left()388     SkScalar left() const { return fData.fLeft; }
top()389     SkScalar top() const { return fData.fTop; }
right()390     SkScalar right() const { return fData.fRight; }
bottom()391     SkScalar bottom() const { return fData.fBottom; }
392 
width()393     SkScalar width() const { return fData.width(); }
height()394     SkScalar height() const { return fData.height(); }
395 
topLeft()396     LayerSpace<SkPoint> topLeft() const {
397         return LayerSpace<SkPoint>(SkPoint::Make(fData.fLeft, fData.fTop));
398     }
size()399     LayerSpace<SkSize> size() const {
400         return LayerSpace<SkSize>(SkSize::Make(fData.width(), fData.height()));
401     }
402 
round()403     LayerSpace<SkIRect> round() const { return LayerSpace<SkIRect>(fData.round()); }
roundIn()404     LayerSpace<SkIRect> roundIn() const { return LayerSpace<SkIRect>(fData.roundIn()); }
roundOut()405     LayerSpace<SkIRect> roundOut() const { return LayerSpace<SkIRect>(fData.roundOut()); }
406 
intersect(const LayerSpace<SkRect> & r)407     bool intersect(const LayerSpace<SkRect>& r) { return fData.intersect(r.fData); }
join(const LayerSpace<SkRect> & r)408     void join(const LayerSpace<SkRect>& r) { fData.join(r.fData); }
offset(const LayerSpace<Vector> & v)409     void offset(const LayerSpace<Vector>& v) { fData.offset(SkVector(v)); }
outset(const LayerSpace<SkSize> & delta)410     void outset(const LayerSpace<SkSize>& delta) { fData.outset(delta.width(), delta.height()); }
411 
412 private:
413     SkRect fData;
414 };
415 
416 // Mapping is the primary definition of the shared layer space used when evaluating an image filter
417 // DAG. It encapsulates any needed decomposition of the total CTM into the parameter-to-layer matrix
418 // (that filters use to map their parameters to the layer space), and the layer-to-device matrix
419 // (that canvas uses to map the output layer-space image into its root device space). Mapping
420 // defines functions to transform ParameterSpace and DeviceSpace types to and from their LayerSpace
421 // variants, which can then be used and reasoned about by SkImageFilter implementations.
422 class Mapping {
423 public:
424     Mapping() = default;
425 
426     // Helper constructor that equates device and layer space to the same coordinate space.
Mapping(const SkMatrix & paramToLayer)427     explicit Mapping(const SkMatrix& paramToLayer)
428             : fLayerToDevMatrix(SkMatrix::I())
429             , fParamToLayerMatrix(paramToLayer)
430             , fDevToLayerMatrix(SkMatrix::I()) {}
431 
432     // This constructor allows the decomposition to be explicitly provided, assumes that
433     // 'layerToDev's inverse has already been calculated in 'devToLayer'
Mapping(const SkMatrix & layerToDev,const SkMatrix & devToLayer,const SkMatrix & paramToLayer)434     Mapping(const SkMatrix& layerToDev, const SkMatrix& devToLayer, const SkMatrix& paramToLayer)
435             : fLayerToDevMatrix(layerToDev)
436             , fParamToLayerMatrix(paramToLayer)
437             , fDevToLayerMatrix(devToLayer) {}
438 
439     // Sets this Mapping to the default decomposition of the canvas's total transform, given the
440     // requirements of the 'filter'. Returns false if the decomposition failed or would produce an
441     // invalid device matrix. Assumes 'ctm' is invertible.
442     bool SK_WARN_UNUSED_RESULT decomposeCTM(const SkMatrix& ctm,
443                                             const SkImageFilter* filter,
444                                             const skif::ParameterSpace<SkPoint>& representativePt);
445 
446     // Update the mapping's parameter-to-layer matrix to be pre-concatenated with the specified
447     // local space transformation. This changes the definition of parameter space, any
448     // skif::ParameterSpace<> values are interpreted anew. Layer space and device space are
449     // unchanged.
concatLocal(const SkMatrix & local)450     void concatLocal(const SkMatrix& local) { fParamToLayerMatrix.preConcat(local); }
451 
452     // Update the mapping's layer space coordinate system by post-concatenating the given matrix
453     // to it's parameter-to-layer transform, and pre-concatenating the inverse of the matrix with
454     // it's layer-to-device transform. The net effect is that neither the parameter nor device
455     // coordinate systems are changed, but skif::LayerSpace is adjusted.
456     //
457     // Returns false if the layer matrix cannot be inverted, and this mapping is left unmodified.
458     bool adjustLayerSpace(const SkMatrix& layer);
459 
460     // Update the mapping's layer space so that the point 'origin' in the current layer coordinate
461     // space maps to (0, 0) in the adjusted coordinate space.
applyOrigin(const LayerSpace<SkIPoint> & origin)462     void applyOrigin(const LayerSpace<SkIPoint>& origin) {
463         SkAssertResult(this->adjustLayerSpace(SkMatrix::Translate(-origin.x(), -origin.y())));
464     }
465 
deviceMatrix()466     const SkMatrix& deviceMatrix() const { return fLayerToDevMatrix; }
layerMatrix()467     const SkMatrix& layerMatrix() const { return fParamToLayerMatrix; }
totalMatrix()468     SkMatrix totalMatrix() const {
469         return SkMatrix::Concat(fLayerToDevMatrix, fParamToLayerMatrix);
470     }
471 
472     template<typename T>
paramToLayer(const ParameterSpace<T> & paramGeometry)473     LayerSpace<T> paramToLayer(const ParameterSpace<T>& paramGeometry) const {
474         return LayerSpace<T>(map(static_cast<const T&>(paramGeometry), fParamToLayerMatrix));
475     }
476 
477     template<typename T>
deviceToLayer(const DeviceSpace<T> & devGeometry)478     LayerSpace<T> deviceToLayer(const DeviceSpace<T>& devGeometry) const {
479         return LayerSpace<T>(map(static_cast<const T&>(devGeometry), fDevToLayerMatrix));
480     }
481 
482     template<typename T>
layerToDevice(const LayerSpace<T> & layerGeometry)483     DeviceSpace<T> layerToDevice(const LayerSpace<T>& layerGeometry) const {
484         return DeviceSpace<T>(map(static_cast<const T&>(layerGeometry), fLayerToDevMatrix));
485     }
486 
487 private:
488     // The image filter process decomposes the total CTM into layerToDev * paramToLayer and uses the
489     // param-to-layer matrix to define the layer-space coordinate system. Depending on how it's
490     // decomposed, either the layer matrix or the device matrix could be the identity matrix (but
491     // sometimes neither).
492     SkMatrix fLayerToDevMatrix;
493     SkMatrix fParamToLayerMatrix;
494 
495     // Cached inverse of fLayerToDevMatrix
496     SkMatrix fDevToLayerMatrix;
497 
498     // Actual geometric mapping operations that work on coordinates and matrices w/o the type
499     // safety of the coordinate space wrappers (hence these are private).
500     template<typename T>
501     static T map(const T& geom, const SkMatrix& matrix);
502 };
503 
504 // Wraps an SkSpecialImage and its location in the shared layer coordinate space. This origin is
505 // used to draw the image in the correct location. The 'layerBounds' rectangle of the filtered image
506 // is the layer-space bounding box of the image. It has its top left corner at 'origin' and has the
507 // same dimensions as the underlying special image subset. Transforming 'layerBounds' by the
508 // Context's layer-to-device matrix and painting it with the subset rectangle will display the
509 // filtered results in the appropriate device-space region.
510 //
511 // When filter implementations are processing intermediate FilterResult results, it can be assumed
512 // that all FilterResult' layerBounds are in the same layer coordinate space defined by the shared
513 // skif::Context.
514 //
515 // NOTE: This is named FilterResult since most instances will represent the output of an image
516 // filter (even if that is then used as an input to the next filter). The main exception is the
517 // source input used when an input filter is null, but from a data-standpoint it is the same since
518 // it is equivalent to the result of an identity filter.
519 class FilterResult {
520 public:
FilterResult()521     FilterResult() : fImage(nullptr), fOrigin(SkIPoint::Make(0, 0)) {}
522 
FilterResult(sk_sp<SkSpecialImage> image,const LayerSpace<SkIPoint> & origin)523     FilterResult(sk_sp<SkSpecialImage> image, const LayerSpace<SkIPoint>& origin)
524             : fImage(std::move(image))
525             , fOrigin(origin) {}
FilterResult(sk_sp<SkSpecialImage> image)526     explicit FilterResult(sk_sp<SkSpecialImage> image)
527             : fImage(std::move(image))
528             , fOrigin{{0, 0}} {}
529 
image()530     const SkSpecialImage* image() const { return fImage.get(); }
refImage()531     sk_sp<SkSpecialImage> refImage() const { return fImage; }
532 
533     // Get the layer-space bounds of the result. This will have the same dimensions as the
534     // image and its top left corner will be 'origin()'.
layerBounds()535     LayerSpace<SkIRect> layerBounds() const {
536         return LayerSpace<SkIRect>(SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(),
537                                                      fImage->width(), fImage->height()));
538     }
539 
540     // Get the layer-space coordinate of this image's top left pixel.
layerOrigin()541     const LayerSpace<SkIPoint>& layerOrigin() const { return fOrigin; }
542 
543     // Produce a new FilterResult that can correctly cover 'newBounds' when it's drawn with its
544     // tile mode at its origin. When possible, the returned FilterResult can reuse the same image
545     // data and adjust its access subset, origin, and tile mode. If 'forcePad' is true or if the
546     // 'newTileMode' that applies at the 'newBounds' geometry is incompatible with the current
547     // bounds and tile mode, then a new image is created that resolves this image's data and tiling.
548     // TODO (michaelludwig): All FilterResults are decal mode and there are no current usages that
549     // require force-padding a decal FilterResult so these arguments aren't implemented yet.
550     FilterResult resolveToBounds(const LayerSpace<SkIRect>& newBounds) const;
551                                 //  SkTileMode newTileMode=SkTileMode::kDecal,
552                                 //  bool forcePad=false) const;
553 
554     // Extract image and origin, safely when the image is null.
555     // TODO (michaelludwig) - This is intended for convenience until all call sites of
556     // SkImageFilter_Base::filterImage() have been updated to work in the new type system
557     // (which comes later as SkDevice, SkCanvas, etc. need to be modified, and coordinate space
558     // tagging needs to be added).
imageAndOffset(SkIPoint * offset)559     sk_sp<SkSpecialImage> imageAndOffset(SkIPoint* offset) const {
560         if (fImage) {
561             *offset = SkIPoint(fOrigin);
562             return fImage;
563         } else {
564             *offset = {0, 0};
565             return nullptr;
566         }
567     }
568 
569 private:
570     sk_sp<SkSpecialImage> fImage;
571     LayerSpace<SkIPoint>  fOrigin;
572     // SkTileMode         fTileMode = SkTileMode::kDecal;
573 };
574 
575 // The context contains all necessary information to describe how the image filter should be
576 // computed (i.e. the current layer matrix and clip), and the color information of the output of a
577 // filter DAG. For now, this is just the color space (of the original requesting device). This is
578 // used when constructing intermediate rendering surfaces, so that we ensure we land in a surface
579 // that's similar/compatible to the final consumer of the DAG's output.
580 class Context {
581 public:
582     // Creates a context with the given layer matrix and destination clip, reading from 'source'
583     // with an origin of (0,0).
Context(const SkMatrix & layerMatrix,const SkIRect & clipBounds,SkImageFilterCache * cache,SkColorType colorType,SkColorSpace * colorSpace,const SkSpecialImage * source)584     Context(const SkMatrix& layerMatrix, const SkIRect& clipBounds, SkImageFilterCache* cache,
585             SkColorType colorType, SkColorSpace* colorSpace, const SkSpecialImage* source)
586         : fMapping(layerMatrix)
587         , fDesiredOutput(clipBounds)
588         , fCache(cache)
589         , fColorType(colorType)
590         , fColorSpace(colorSpace)
591         , fSource(sk_ref_sp(source), LayerSpace<SkIPoint>({0, 0})) {}
592 
Context(const Mapping & mapping,const LayerSpace<SkIRect> & desiredOutput,SkImageFilterCache * cache,SkColorType colorType,SkColorSpace * colorSpace,const FilterResult & source)593     Context(const Mapping& mapping, const LayerSpace<SkIRect>& desiredOutput,
594             SkImageFilterCache* cache, SkColorType colorType, SkColorSpace* colorSpace,
595             const FilterResult& source)
596         : fMapping(mapping)
597         , fDesiredOutput(desiredOutput)
598         , fCache(cache)
599         , fColorType(colorType)
600         , fColorSpace(colorSpace)
601         , fSource(source) {}
602 
603     // The mapping that defines the transformation from local parameter space of the filters to the
604     // layer space where the image filters are evaluated, as well as the remaining transformation
605     // from the layer space to the final device space. The layer space defined by the returned
606     // Mapping may be the same as the root device space, or be an intermediate space that is
607     // supported by the image filter DAG (depending on what it returns from getCTMCapability()).
608     // If a node returns something other than kComplex from getCTMCapability(), the layer matrix of
609     // the mapping will respect that return value, and the remaining matrix will be appropriately
610     // set to transform the layer space to the final device space (applied by the SkCanvas when
611     // filtering is finished).
mapping()612     const Mapping& mapping() const { return fMapping; }
613     // DEPRECATED: Use mapping() and its coordinate-space types instead
ctm()614     const SkMatrix& ctm() const { return fMapping.layerMatrix(); }
615     // The bounds, in the layer space, that the filtered image will be clipped to. The output
616     // from filterImage() must cover these clip bounds, except in areas where it will just be
617     // transparent black, in which case a smaller output image can be returned.
desiredOutput()618     const LayerSpace<SkIRect>& desiredOutput() const { return fDesiredOutput; }
619     // DEPRECATED: Use desiredOutput() instead
clipBounds()620     const SkIRect& clipBounds() const { return static_cast<const SkIRect&>(fDesiredOutput); }
621     // The cache to use when recursing through the filter DAG, in order to avoid repeated
622     // calculations of the same image.
cache()623     SkImageFilterCache* cache() const { return fCache; }
624     // The output device's color type, which can be used for intermediate images to be
625     // compatible with the eventual target of the filtered result.
colorType()626     SkColorType colorType() const { return fColorType; }
627 #if SK_SUPPORT_GPU
grColorType()628     GrColorType grColorType() const { return SkColorTypeToGrColorType(fColorType); }
629 #endif
630     // The output device's color space, so intermediate images can match, and so filtering can
631     // be performed in the destination color space.
colorSpace()632     SkColorSpace* colorSpace() const { return fColorSpace; }
refColorSpace()633     sk_sp<SkColorSpace> refColorSpace() const { return sk_ref_sp(fColorSpace); }
634     // The default surface properties to use when making transient surfaces during filtering.
surfaceProps()635     const SkSurfaceProps& surfaceProps() const { return fSource.image()->props(); }
636 
637     // This is the image to use whenever an expected input filter has been set to null. In the
638     // majority of cases, this is the original source image for the image filter DAG so it comes
639     // from the SkDevice that holds either the saveLayer or the temporary rendered result. The
640     // exception is composing two image filters (via SkImageFilters::Compose), which must use
641     // the output of the inner DAG as the "source" for the outer DAG.
source()642     const FilterResult& source() const { return fSource; }
643     // DEPRECATED: Use source() instead to get both the image and its origin.
sourceImage()644     const SkSpecialImage* sourceImage() const { return fSource.image(); }
645 
646     // True if image filtering should occur on the GPU if possible.
gpuBacked()647     bool gpuBacked() const { return fSource.image()->isTextureBacked(); }
648     // The recording context to use when computing the filter with the GPU.
getContext()649     GrRecordingContext* getContext() const { return fSource.image()->getContext(); }
650 
651     /**
652      *  Since a context can be built directly, its constructor has no chance to "return null" if
653      *  it's given invalid or unsupported inputs. Call this to know of the the context can be
654      *  used.
655      *
656      *  The SkImageFilterCache Key, for example, requires a finite ctm (no infinities or NaN),
657      *  so that test is part of isValid.
658      */
isValid()659     bool isValid() const { return fSource.image() != nullptr && fMapping.layerMatrix().isFinite(); }
660 
661     // Create a surface of the given size, that matches the context's color type and color space
662     // as closely as possible, and uses the same backend of the device that produced the source
663     // image.
664     sk_sp<SkSpecialSurface> makeSurface(const SkISize& size,
665                                         const SkSurfaceProps* props = nullptr) const {
666         if (!props) {
667              props = &this->surfaceProps();
668         }
669         return fSource.image()->makeSurface(fColorType, fColorSpace, size,
670                                             kPremul_SkAlphaType, *props);
671     }
672 
673     // Create a new context that matches this context, but with an overridden layer space.
withNewMapping(const Mapping & mapping)674     Context withNewMapping(const Mapping& mapping) const {
675         return Context(mapping, fDesiredOutput, fCache, fColorType, fColorSpace, fSource);
676     }
677     // Create a new context that matches this context, but with an overridden desired output rect.
withNewDesiredOutput(const LayerSpace<SkIRect> & desiredOutput)678     Context withNewDesiredOutput(const LayerSpace<SkIRect>& desiredOutput) const {
679         return Context(fMapping, desiredOutput, fCache, fColorType, fColorSpace, fSource);
680     }
681 
682 private:
683     Mapping             fMapping;
684     LayerSpace<SkIRect> fDesiredOutput;
685     SkImageFilterCache* fCache;
686     SkColorType         fColorType;
687     // The pointed-to object is owned by the device controlling the filter process, and our lifetime
688     // is bounded by the device, so this can be a bare pointer.
689     SkColorSpace*       fColorSpace;
690     FilterResult        fSource;
691 };
692 
693 } // end namespace skif
694 
695 #endif // SkImageFilterTypes_DEFINED
696