• 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:
ParameterSpace(const T & data)77     explicit ParameterSpace(const T& data) : fData(data) {}
ParameterSpace(T && data)78     explicit ParameterSpace(T&& data) : fData(std::move(data)) {}
79 
80     explicit operator const T&() const { return fData; }
81 
82 private:
83     T fData;
84 };
85 
86 // DeviceSpace is a data-only wrapper around Skia's geometric types. It is similar to
87 // 'ParameterSpace' except that it is used to represent geometry that has been transformed or
88 // defined in the root device space (i.e. the final pixels of drawn content). Much of what SkCanvas
89 // tracks, such as its clip bounds are defined in this space and DeviceSpace provides a
90 // type-enforced mechanism for the canvas to pass that information into the image filtering system,
91 // using the Mapping of the filtering context.
92 template<typename T>
93 class DeviceSpace {
94 public:
DeviceSpace(const T & data)95     explicit DeviceSpace(const T& data) : fData(data) {}
DeviceSpace(T && data)96     explicit DeviceSpace(T&& data) : fData(std::move(data)) {}
97 
98     explicit operator const T&() const { return fData; }
99 
100 private:
101     T fData;
102 };
103 
104 // LayerSpace is a geometric wrapper that specifies the geometry is defined in the shared layer
105 // space where image filters are evaluated. For a given Context (and its Mapping), the image filter
106 // DAG operates in the same coordinate space. This space may be different from the local coordinate
107 // space that defined the image filter parameters (such as blur sigma), and it may be different
108 // from the total CTM of the SkCanvas.
109 //
110 // To encourage correct filter use and implementation, the bulk of filter logic should be performed
111 // in layer space (e.g. determining what portion of an input image to read, or what the output
112 // region is). LayerSpace specializations for the six common Skia math types (Sk[I]Rect, Sk[I]Point,
113 // and Sk[I]Size), and skif::[I]Vector (to allow vectors to be specialized separately from points))
114 // are provided that mimic their APIs but preserve the coordinate space and enforce type semantics.
115 template<typename T>
116 class LayerSpace {};
117 
118 // Layer-space specialization for integerized direction vectors.
119 template<>
120 class LayerSpace<IVector> {
121 public:
122     LayerSpace() = default;
LayerSpace(const IVector & geometry)123     explicit LayerSpace(const IVector& geometry) : fData(geometry) {}
LayerSpace(IVector && geometry)124     explicit LayerSpace(IVector&& geometry) : fData(std::move(geometry)) {}
125     explicit operator const IVector&() const { return fData; }
126 
SkIVector()127     explicit operator SkIVector() const { return SkIVector::Make(fData.fX, fData.fY); }
128 
x()129     int32_t x() const { return fData.fX; }
y()130     int32_t y() const { return fData.fY; }
131 
132     LayerSpace<IVector> operator-() const { return LayerSpace<IVector>({-fData.fX, -fData.fY}); }
133 
134     LayerSpace<IVector> operator+(const LayerSpace<IVector>& v) const {
135         LayerSpace<IVector> sum = *this;
136         sum += v;
137         return sum;
138     }
139     LayerSpace<IVector> operator-(const LayerSpace<IVector>& v) const {
140         LayerSpace<IVector> diff = *this;
141         diff -= v;
142         return diff;
143     }
144 
145     void operator+=(const LayerSpace<IVector>& v) {
146         fData.fX += v.fData.fX;
147         fData.fY += v.fData.fY;
148     }
149     void operator-=(const LayerSpace<IVector>& v) {
150         fData.fX -= v.fData.fX;
151         fData.fY -= v.fData.fY;
152     }
153 
154 private:
155     IVector fData;
156 };
157 
158 // Layer-space specialization for floating point direction vectors.
159 template<>
160 class LayerSpace<Vector> {
161 public:
162     LayerSpace() = default;
LayerSpace(const Vector & geometry)163     explicit LayerSpace(const Vector& geometry) : fData(geometry) {}
LayerSpace(Vector && geometry)164     explicit LayerSpace(Vector&& geometry) : fData(std::move(geometry)) {}
165     explicit operator const Vector&() const { return fData; }
166 
SkVector()167     explicit operator SkVector() const { return SkVector::Make(fData.fX, fData.fY); }
168 
x()169     SkScalar x() const { return fData.fX; }
y()170     SkScalar y() const { return fData.fY; }
171 
length()172     SkScalar length() const { return SkVector::Length(fData.fX, fData.fY); }
173 
174     LayerSpace<Vector> operator-() const { return LayerSpace<Vector>({-fData.fX, -fData.fY}); }
175 
176     LayerSpace<Vector> operator*(SkScalar s) const {
177         LayerSpace<Vector> scaled = *this;
178         scaled *= s;
179         return scaled;
180     }
181 
182     LayerSpace<Vector> operator+(const LayerSpace<Vector>& v) const {
183         LayerSpace<Vector> sum = *this;
184         sum += v;
185         return sum;
186     }
187     LayerSpace<Vector> operator-(const LayerSpace<Vector>& v) const {
188         LayerSpace<Vector> diff = *this;
189         diff -= v;
190         return diff;
191     }
192 
193     void operator*=(SkScalar s) {
194         fData.fX *= s;
195         fData.fY *= s;
196     }
197     void operator+=(const LayerSpace<Vector>& v) {
198         fData.fX += v.fData.fX;
199         fData.fY += v.fData.fY;
200     }
201     void operator-=(const LayerSpace<Vector>& v) {
202         fData.fX -= v.fData.fX;
203         fData.fY -= v.fData.fY;
204     }
205 
206     friend LayerSpace<Vector> operator*(SkScalar s, const LayerSpace<Vector>& b) {
207         return b * s;
208     }
209 
210 private:
211     Vector fData;
212 };
213 
214 // Layer-space specialization for integer 2D coordinates (treated as positions, not directions).
215 template<>
216 class LayerSpace<SkIPoint> {
217 public:
218     LayerSpace() = default;
LayerSpace(const SkIPoint & geometry)219     explicit LayerSpace(const SkIPoint& geometry)  : fData(geometry) {}
LayerSpace(SkIPoint && geometry)220     explicit LayerSpace(SkIPoint&& geometry) : fData(std::move(geometry)) {}
221     explicit operator const SkIPoint&() const { return fData; }
222 
223     // Parrot the SkIPoint API while preserving coordinate space.
x()224     int32_t x() const { return fData.fX; }
y()225     int32_t y() const { return fData.fY; }
226 
227     // Offsetting by direction vectors produce more points
228     LayerSpace<SkIPoint> operator+(const LayerSpace<IVector>& v) {
229         return LayerSpace<SkIPoint>(fData + SkIVector(v));
230     }
231     LayerSpace<SkIPoint> operator-(const LayerSpace<IVector>& v) {
232         return LayerSpace<SkIPoint>(fData - SkIVector(v));
233     }
234 
235     void operator+=(const LayerSpace<IVector>& v) {
236         fData += SkIVector(v);
237     }
238     void operator-=(const LayerSpace<IVector>& v) {
239         fData -= SkIVector(v);
240     }
241 
242     // Subtracting another point makes a direction between them
243     LayerSpace<IVector> operator-(const LayerSpace<SkIPoint>& p) {
244         return LayerSpace<IVector>(IVector(fData - p.fData));
245     }
246 
247 private:
248     SkIPoint fData;
249 };
250 
251 // Layer-space specialization for floating point 2D coordinates (treated as positions)
252 template<>
253 class LayerSpace<SkPoint> {
254 public:
255     LayerSpace() = default;
LayerSpace(const SkPoint & geometry)256     explicit LayerSpace(const SkPoint& geometry) : fData(geometry) {}
LayerSpace(SkPoint && geometry)257     explicit LayerSpace(SkPoint&& geometry) : fData(std::move(geometry)) {}
258     explicit operator const SkPoint&() const { return fData; }
259 
260     // Parrot the SkPoint API while preserving coordinate space.
x()261     SkScalar x() const { return fData.fX; }
y()262     SkScalar y() const { return fData.fY; }
263 
distanceToOrigin()264     SkScalar distanceToOrigin() const { return fData.distanceToOrigin(); }
265 
266     // Offsetting by direction vectors produce more points
267     LayerSpace<SkPoint> operator+(const LayerSpace<Vector>& v) {
268         return LayerSpace<SkPoint>(fData + SkVector(v));
269     }
270     LayerSpace<SkPoint> operator-(const LayerSpace<Vector>& v) {
271         return LayerSpace<SkPoint>(fData - SkVector(v));
272     }
273 
274     void operator+=(const LayerSpace<Vector>& v) {
275         fData += SkVector(v);
276     }
277     void operator-=(const LayerSpace<Vector>& v) {
278         fData -= SkVector(v);
279     }
280 
281     // Subtracting another point makes a direction between them
282     LayerSpace<Vector> operator-(const LayerSpace<SkPoint>& p) {
283         return LayerSpace<Vector>(Vector(fData - p.fData));
284     }
285 
286 private:
287     SkPoint fData;
288 };
289 
290 // Layer-space specialization for integer dimensions
291 template<>
292 class LayerSpace<SkISize> {
293 public:
294     LayerSpace() = default;
LayerSpace(const SkISize & geometry)295     explicit LayerSpace(const SkISize& geometry) : fData(geometry) {}
LayerSpace(SkISize && geometry)296     explicit LayerSpace(SkISize&& geometry) : fData(std::move(geometry)) {}
297     explicit operator const SkISize&() const { return fData; }
298 
width()299     int32_t width() const { return fData.width(); }
height()300     int32_t height() const { return fData.height(); }
301 
isEmpty()302     bool isEmpty() const { return fData.isEmpty(); }
303 
304 private:
305     SkISize fData;
306 };
307 
308 // Layer-space specialization for floating point dimensions
309 template<>
310 class LayerSpace<SkSize> {
311 public:
312     LayerSpace() = default;
LayerSpace(const SkSize & geometry)313     explicit LayerSpace(const SkSize& geometry) : fData(geometry) {}
LayerSpace(SkSize && geometry)314     explicit LayerSpace(SkSize&& geometry) : fData(std::move(geometry)) {}
315     explicit operator const SkSize&() const { return fData; }
316 
width()317     SkScalar width() const { return fData.width(); }
height()318     SkScalar height() const { return fData.height(); }
319 
isEmpty()320     bool isEmpty() const { return fData.isEmpty(); }
isZero()321     bool isZero() const { return fData.isZero(); }
322 
round()323     LayerSpace<SkISize> round() const { return LayerSpace<SkISize>(fData.toRound()); }
ceil()324     LayerSpace<SkISize> ceil() const { return LayerSpace<SkISize>(fData.toCeil()); }
floor()325     LayerSpace<SkISize> floor() const { return LayerSpace<SkISize>(fData.toFloor()); }
326 
327 private:
328     SkSize fData;
329 };
330 
331 // Layer-space specialization for axis-aligned integer bounding boxes.
332 template<>
333 class LayerSpace<SkIRect> {
334 public:
335     LayerSpace() = default;
LayerSpace(const SkIRect & geometry)336     explicit LayerSpace(const SkIRect& geometry) : fData(geometry) {}
LayerSpace(SkIRect && geometry)337     explicit LayerSpace(SkIRect&& geometry) : fData(std::move(geometry)) {}
338     explicit operator const SkIRect&() const { return fData; }
339 
340     // Parrot the SkIRect API while preserving coord space
left()341     int32_t left() const { return fData.fLeft; }
top()342     int32_t top() const { return fData.fTop; }
right()343     int32_t right() const { return fData.fRight; }
bottom()344     int32_t bottom() const { return fData.fBottom; }
345 
width()346     int32_t width() const { return fData.width(); }
height()347     int32_t height() const { return fData.height(); }
348 
topLeft()349     LayerSpace<SkIPoint> topLeft() const { return LayerSpace<SkIPoint>(fData.topLeft()); }
size()350     LayerSpace<SkISize> size() const { return LayerSpace<SkISize>(fData.size()); }
351 
intersect(const LayerSpace<SkIRect> & r)352     bool intersect(const LayerSpace<SkIRect>& r) { return fData.intersect(r.fData); }
join(const LayerSpace<SkIRect> & r)353     void join(const LayerSpace<SkIRect>& r) { fData.join(r.fData); }
offset(const LayerSpace<IVector> & v)354     void offset(const LayerSpace<IVector>& v) { fData.offset(SkIVector(v)); }
outset(const LayerSpace<SkISize> & delta)355     void outset(const LayerSpace<SkISize>& delta) { fData.outset(delta.width(), delta.height()); }
356 
357 private:
358     SkIRect fData;
359 };
360 
361 // Layer-space specialization for axis-aligned float bounding boxes.
362 template<>
363 class LayerSpace<SkRect> {
364 public:
365     LayerSpace() = default;
LayerSpace(const SkRect & geometry)366     explicit LayerSpace(const SkRect& geometry) : fData(geometry) {}
LayerSpace(SkRect && geometry)367     explicit LayerSpace(SkRect&& geometry) : fData(std::move(geometry)) {}
368     explicit operator const SkRect&() const { return fData; }
369 
370     // Parrot the SkRect API while preserving coord space and usage
left()371     SkScalar left() const { return fData.fLeft; }
top()372     SkScalar top() const { return fData.fTop; }
right()373     SkScalar right() const { return fData.fRight; }
bottom()374     SkScalar bottom() const { return fData.fBottom; }
375 
width()376     SkScalar width() const { return fData.width(); }
height()377     SkScalar height() const { return fData.height(); }
378 
topLeft()379     LayerSpace<SkPoint> topLeft() const {
380         return LayerSpace<SkPoint>(SkPoint::Make(fData.fLeft, fData.fTop));
381     }
size()382     LayerSpace<SkSize> size() const {
383         return LayerSpace<SkSize>(SkSize::Make(fData.width(), fData.height()));
384     }
roundOut()385     LayerSpace<SkIRect> roundOut() const { return LayerSpace<SkIRect>(fData.roundOut()); }
386 
intersect(const LayerSpace<SkRect> & r)387     bool intersect(const LayerSpace<SkRect>& r) { return fData.intersect(r.fData); }
join(const LayerSpace<SkRect> & r)388     void join(const LayerSpace<SkRect>& r) { fData.join(r.fData); }
offset(const LayerSpace<Vector> & v)389     void offset(const LayerSpace<Vector>& v) { fData.offset(SkVector(v)); }
outset(const LayerSpace<SkSize> & delta)390     void outset(const LayerSpace<SkSize>& delta) { fData.outset(delta.width(), delta.height()); }
391 
392 private:
393     SkRect fData;
394 };
395 
396 // Mapping is the primary definition of the shared layer space used when evaluating an image filter
397 // DAG. It encapsulates any needed decomposition of the total CTM into the parameter-to-layer matrix
398 // (that filters use to map their parameters to the layer space), and the layer-to-device matrix
399 // (that canvas uses to map the output layer-space image into its root device space). Mapping
400 // defines functions to transform ParameterSpace and DeviceSpace types to and from their LayerSpace
401 // variants, which can then be used and reasoned about by SkImageFilter implementations.
402 class Mapping {
403 public:
404     // This constructor allows the decomposition to be explicitly provided
Mapping(const SkMatrix & layerToDev,const SkMatrix & paramToLayer)405     Mapping(const SkMatrix& layerToDev, const SkMatrix& paramToLayer)
406             : fLayerToDevMatrix(layerToDev)
407             , fParamToLayerMatrix(paramToLayer) {}
408 
409     // Make the default decomposition Mapping, given the total CTM and the root image filter.
410     static Mapping DecomposeCTM(const SkMatrix& ctm, const SkImageFilter* filter,
411                                 const skif::ParameterSpace<SkPoint>& representativePoint);
412 
413     // Return a new Mapping object whose parameter-to-layer matrix is equal to this->layerMatrix() *
414     // local, but both share the same layer-to-device matrix.
concatLocal(const SkMatrix & local)415     Mapping concatLocal(const SkMatrix& local) const {
416         return Mapping(fLayerToDevMatrix, SkMatrix::Concat(fParamToLayerMatrix, local));
417     }
418 
deviceMatrix()419     const SkMatrix& deviceMatrix() const { return fLayerToDevMatrix; }
layerMatrix()420     const SkMatrix& layerMatrix() const { return fParamToLayerMatrix; }
totalMatrix()421     SkMatrix totalMatrix() const {
422         return SkMatrix::Concat(fLayerToDevMatrix, fParamToLayerMatrix);
423     }
424 
425     template<typename T>
paramToLayer(const ParameterSpace<T> & paramGeometry)426     LayerSpace<T> paramToLayer(const ParameterSpace<T>& paramGeometry) const {
427         return LayerSpace<T>(map(static_cast<const T&>(paramGeometry), fParamToLayerMatrix));
428     }
429 
430     template<typename T>
deviceToLayer(const DeviceSpace<T> & devGeometry)431     LayerSpace<T> deviceToLayer(const DeviceSpace<T>& devGeometry) const {
432         // The mapping from device space to layer space is defined by the inverse of the
433         // layer-to-device matrix
434         SkMatrix devToLayerMatrix;
435         if (!fLayerToDevMatrix.invert(&devToLayerMatrix)) {
436             // Punt and just pass through the geometry unmodified...
437             return LayerSpace<T>(static_cast<const T&>(devGeometry));
438         } else {
439             return LayerSpace<T>(map(static_cast<const T&>(devGeometry), devToLayerMatrix));
440         }
441     }
442 
443     template<typename T>
layerToDevice(const LayerSpace<T> & layerGeometry)444     DeviceSpace<T> layerToDevice(const LayerSpace<T>& layerGeometry) const {
445         return DeviceSpace<T>(map(static_cast<const T&>(layerGeometry), fLayerToDevMatrix));
446     }
447 
448 private:
449     // The image filter process decomposes the total CTM into layerToDev * paramToLayer and uses the
450     // param-to-layer matrix to define the layer-space coordinate system. Depending on how it's
451     // decomposed, either the layer matrix or the device matrix could be the identity matrix (but
452     // sometimes neither).
453     SkMatrix fLayerToDevMatrix;
454     SkMatrix fParamToLayerMatrix;
455 
456     // Actual geometric mapping operations that work on coordinates and matrices w/o the type
457     // safety of the coordinate space wrappers (hence these are private).
458     template<typename T>
459     static T map(const T& geom, const SkMatrix& matrix);
460 };
461 
462 // Usage is a template tag to improve the readability of filter implementations. It is attached to
463 // images and geometry to group a collection of related variables and ensure that moving from one
464 // use case to another is explicit.
465 // NOTE: This can be aliased as 'For' when using the fluent type names.
466 // TODO (michaelludwig) - If the primary motivation for Usage--enforcing layer to image space
467 // transformations safely when multiple images are involved--can be handled entirely by helper
468 // functions on FilterResult, then Usage can go away and FilterResult will not need to be templated
469 enum class Usage {
470     // Designates the semantic purpose of the bounds, coordinate, or image as being an input
471     // to the image filter calculations. When this usage is used, it denotes a generic input,
472     // such as the current input in a dynamic loop, or some aggregate of all inputs. Because
473     // most image filters consume 1 or 2 filters only, the related kInput0 and kInput1 are
474     // also defined.
475     kInput,
476     // A more specific version of kInput, this marks the tagged variable as attached to the
477     // image filter of SkImageFilter_Base::getInput(0).
478     kInput0,
479     // A more specific version of kInput, this marks the tagged variable as attached to the
480     // image filter of SkImageFilter_Base::getInput(1).
481     kInput1,
482     // Designates the purpose of the bounds, coordinate, or image as being the output of the
483     // current image filter calculation. There is only ever one output for an image filter.
484     kOutput,
485 };
486 
487 // Convenience macros to add 'using' declarations that rename the above enums to provide a more
488 // fluent and readable API. This should only be used in a private or local scope to prevent leakage
489 // of the names. Use the IN_CLASS variant at the start of a class declaration in those scenarios.
490 // These macros enable the following simpler type names:
491 //   skif::Image<skif::Usage::kInput> -> Image<For::kInput>
492 #define SK_USE_FLUENT_IMAGE_FILTER_TYPES \
493     using For = skif::Usage;
494 
495 #define SK_USE_FLUENT_IMAGE_FILTER_TYPES_IN_CLASS \
496     protected: SK_USE_FLUENT_IMAGE_FILTER_TYPES public:
497 
498 // Wraps an SkSpecialImage and tags it with a corresponding usage, either as generic input (e.g. the
499 // source image), or a specific input image from a filter's connected inputs. It also includes the
500 // origin of the image in the layer space. This origin is used to draw the image in the correct
501 // location. The 'layerBounds' rectangle of the filtered image is the layer-space bounding box of
502 // the image. It has its top left corner at 'origin' and has the same dimensions as the underlying
503 // special image subset. Transforming 'layerBounds' by the Context's layer matrix and painting it
504 // with the subset rectangle will display the filtered results in the appropriate device-space
505 // region.
506 //
507 // When filter implementations are processing intermediate FilterResult results, it can be assumed
508 // that all FilterResult' layerBounds are in the same layer coordinate space defined by the shared
509 // skif::Context.
510 //
511 // NOTE: This is named FilterResult since most instances will represent the output of an image
512 // filter (even if that is then cast to be the input to the next filter). The main exception is the
513 // source input used when an input filter is null, but from a data-standpoint it is the same since
514 // it is equivalent to the result of an identity filter.
515 template<Usage kU>
516 class FilterResult {
517 public:
FilterResult()518     FilterResult() : fImage(nullptr), fOrigin(SkIPoint::Make(0, 0)) {}
519 
FilterResult(sk_sp<SkSpecialImage> image,const LayerSpace<SkIPoint> & origin)520     FilterResult(sk_sp<SkSpecialImage> image, const LayerSpace<SkIPoint>& origin)
521             : fImage(std::move(image))
522             , fOrigin(origin) {}
FilterResult(sk_sp<SkSpecialImage> image)523     explicit FilterResult(sk_sp<SkSpecialImage> image)
524             : fImage(std::move(image))
525             , fOrigin{{0, 0}} {}
526 
527     // Allow explicit moves/copies in order to cast from one use type to another, except kInput0
528     // and kInput1 can only be cast to kOutput (e.g. as part of a noop image filter).
529     template<Usage kI>
FilterResult(FilterResult<kI> && image)530     explicit FilterResult(FilterResult<kI>&& image)
531             : fImage(std::move(image.fImage))
532             , fOrigin(image.fOrigin) {
533         static_assert((kU != Usage::kInput) || (kI != Usage::kInput0 && kI != Usage::kInput1),
534                       "kInput0 and kInput1 cannot be moved to more generic kInput usage.");
535         static_assert((kU != Usage::kInput0 && kU != Usage::kInput1) ||
536                       (kI == kU || kI == Usage::kInput || kI == Usage::kOutput),
537                       "Can only move to specific input from the generic kInput or kOutput usage.");
538     }
539 
540     template<Usage kI>
FilterResult(const FilterResult<kI> & image)541     explicit FilterResult(const FilterResult<kI>& image)
542             : fImage(image.fImage)
543             , fOrigin(image.fOrigin) {
544         static_assert((kU != Usage::kInput) || (kI != Usage::kInput0 && kI != Usage::kInput1),
545                       "kInput0 and kInput1 cannot be copied to more generic kInput usage.");
546         static_assert((kU != Usage::kInput0 && kU != Usage::kInput1) ||
547                       (kI == kU || kI == Usage::kInput || kI == Usage::kOutput),
548                       "Can only copy to specific input from the generic kInput usage.");
549     }
550 
image()551     const SkSpecialImage* image() const { return fImage.get(); }
refImage()552     sk_sp<SkSpecialImage> refImage() const { return fImage; }
553 
554     // Get the layer-space bounds of the result. This will have the same dimensions as the
555     // image and its top left corner will be 'origin()'.
layerBounds()556     LayerSpace<SkIRect> layerBounds() const {
557         return LayerSpace<SkIRect>(SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(),
558                                                      fImage->width(), fImage->height()));
559     }
560 
561     // Get the layer-space coordinate of this image's top left pixel.
layerOrigin()562     const LayerSpace<SkIPoint>& layerOrigin() const { return fOrigin; }
563 
564     // Extract image and origin, safely when the image is null.
565     // TODO (michaelludwig) - This is intended for convenience until all call sites of
566     // SkImageFilter_Base::filterImage() have been updated to work in the new type system
567     // (which comes later as SkDevice, SkCanvas, etc. need to be modified, and coordinate space
568     // tagging needs to be added).
imageAndOffset(SkIPoint * offset)569     sk_sp<SkSpecialImage> imageAndOffset(SkIPoint* offset) const {
570         if (fImage) {
571             *offset = SkIPoint(fOrigin);
572             return fImage;
573         } else {
574             *offset = {0, 0};
575             return nullptr;
576         }
577     }
578 
579 private:
580     // Allow all FilterResult templates access to each others members
581     template<Usage kO>
582     friend class FilterResult;
583 
584     sk_sp<SkSpecialImage> fImage;
585     LayerSpace<SkIPoint> fOrigin;
586 };
587 
588 // The context contains all necessary information to describe how the image filter should be
589 // computed (i.e. the current layer matrix and clip), and the color information of the output of a
590 // filter DAG. For now, this is just the color space (of the original requesting device). This is
591 // used when constructing intermediate rendering surfaces, so that we ensure we land in a surface
592 // that's similar/compatible to the final consumer of the DAG's output.
593 class Context {
594 public:
595     SK_USE_FLUENT_IMAGE_FILTER_TYPES_IN_CLASS
596 
597     // Creates a context with the given layer matrix and destination clip, reading from 'source'
598     // with an origin of (0,0).
Context(const SkMatrix & layerMatrix,const SkIRect & clipBounds,SkImageFilterCache * cache,SkColorType colorType,SkColorSpace * colorSpace,const SkSpecialImage * source)599     Context(const SkMatrix& layerMatrix, const SkIRect& clipBounds, SkImageFilterCache* cache,
600             SkColorType colorType, SkColorSpace* colorSpace, const SkSpecialImage* source)
601         : fMapping(SkMatrix::I(), layerMatrix)
602         , fDesiredOutput(clipBounds)
603         , fCache(cache)
604         , fColorType(colorType)
605         , fColorSpace(colorSpace)
606         , fSource(sk_ref_sp(source), LayerSpace<SkIPoint>({0, 0})) {}
607 
Context(const Mapping & mapping,const LayerSpace<SkIRect> & desiredOutput,SkImageFilterCache * cache,SkColorType colorType,SkColorSpace * colorSpace,const FilterResult<For::kInput> & source)608     Context(const Mapping& mapping, const LayerSpace<SkIRect>& desiredOutput,
609             SkImageFilterCache* cache, SkColorType colorType, SkColorSpace* colorSpace,
610             const FilterResult<For::kInput>& source)
611         : fMapping(mapping)
612         , fDesiredOutput(desiredOutput)
613         , fCache(cache)
614         , fColorType(colorType)
615         , fColorSpace(colorSpace)
616         , fSource(source) {}
617 
618     // The mapping that defines the transformation from local parameter space of the filters to the
619     // layer space where the image filters are evaluated, as well as the remaining transformation
620     // from the layer space to the final device space. The layer space defined by the returned
621     // Mapping may be the same as the root device space, or be an intermediate space that is
622     // supported by the image filter DAG (depending on what it returns from canHandleComplexCTM()).
623     // If a node returns false from canHandleComplexCTM(), the layer matrix of the mapping will be
624     // at most a scale + translate, and the remaining matrix will be appropriately set to transform
625     // the layer space to the final device space (applied by the SkCanvas when filtering is
626     // finished).
mapping()627     const Mapping& mapping() const { return fMapping; }
628     // DEPRECATED: Use mapping() and its coordinate-space types instead
ctm()629     const SkMatrix& ctm() const { return fMapping.layerMatrix(); }
630     // The bounds, in the layer space, that the filtered image will be clipped to. The output
631     // from filterImage() must cover these clip bounds, except in areas where it will just be
632     // transparent black, in which case a smaller output image can be returned.
desiredOutput()633     const LayerSpace<SkIRect>& desiredOutput() const { return fDesiredOutput; }
634     // DEPRECATED: Use desiredOutput() instead
clipBounds()635     const SkIRect& clipBounds() const { return static_cast<const SkIRect&>(fDesiredOutput); }
636     // The cache to use when recursing through the filter DAG, in order to avoid repeated
637     // calculations of the same image.
cache()638     SkImageFilterCache* cache() const { return fCache; }
639     // The output device's color type, which can be used for intermediate images to be
640     // compatible with the eventual target of the filtered result.
colorType()641     SkColorType colorType() const { return fColorType; }
642 #if SK_SUPPORT_GPU
grColorType()643     GrColorType grColorType() const { return SkColorTypeToGrColorType(fColorType); }
644 #endif
645     // The output device's color space, so intermediate images can match, and so filtering can
646     // be performed in the destination color space.
colorSpace()647     SkColorSpace* colorSpace() const { return fColorSpace; }
refColorSpace()648     sk_sp<SkColorSpace> refColorSpace() const { return sk_ref_sp(fColorSpace); }
649     // The default surface properties to use when making transient surfaces during filtering.
surfaceProps()650     const SkSurfaceProps& surfaceProps() const { return fSource.image()->props(); }
651 
652     // This is the image to use whenever an expected input filter has been set to null. In the
653     // majority of cases, this is the original source image for the image filter DAG so it comes
654     // from the SkDevice that holds either the saveLayer or the temporary rendered result. The
655     // exception is composing two image filters (via SkImageFilters::Compose), which must use
656     // the output of the inner DAG as the "source" for the outer DAG.
source()657     const FilterResult<For::kInput>& source() const { return fSource; }
658     // DEPRECATED: Use source() instead to get both the image and its origin.
sourceImage()659     const SkSpecialImage* sourceImage() const { return fSource.image(); }
660 
661     // True if image filtering should occur on the GPU if possible.
gpuBacked()662     bool gpuBacked() const { return fSource.image()->isTextureBacked(); }
663     // The recording context to use when computing the filter with the GPU.
getContext()664     GrRecordingContext* getContext() const { return fSource.image()->getContext(); }
665 
666     /**
667      *  Since a context can be built directly, its constructor has no chance to "return null" if
668      *  it's given invalid or unsupported inputs. Call this to know of the the context can be
669      *  used.
670      *
671      *  The SkImageFilterCache Key, for example, requires a finite ctm (no infinities or NaN),
672      *  so that test is part of isValid.
673      */
isValid()674     bool isValid() const { return fSource.image() != nullptr && fMapping.layerMatrix().isFinite(); }
675 
676     // Create a surface of the given size, that matches the context's color type and color space
677     // as closely as possible, and uses the same backend of the device that produced the source
678     // image.
679     sk_sp<SkSpecialSurface> makeSurface(const SkISize& size,
680                                         const SkSurfaceProps* props = nullptr) const {
681         if (!props) {
682              props = &this->surfaceProps();
683         }
684         return fSource.image()->makeSurface(fColorType, fColorSpace, size,
685                                             kPremul_SkAlphaType, *props);
686     }
687 
688     // Create a new context that matches this context, but with an overridden layer space.
withNewMapping(const Mapping & mapping)689     Context withNewMapping(const Mapping& mapping) const {
690         return Context(mapping, fDesiredOutput, fCache, fColorType, fColorSpace, fSource);
691     }
692     // Create a new context that matches this context, but with an overridden desired output rect.
withNewDesiredOutput(const LayerSpace<SkIRect> & desiredOutput)693     Context withNewDesiredOutput(const LayerSpace<SkIRect>& desiredOutput) const {
694         return Context(fMapping, desiredOutput, fCache, fColorType, fColorSpace, fSource);
695     }
696 
697 private:
698     Mapping                   fMapping;
699     LayerSpace<SkIRect>       fDesiredOutput;
700     SkImageFilterCache*       fCache;
701     SkColorType               fColorType;
702     // The pointed-to object is owned by the device controlling the filter process, and our lifetime
703     // is bounded by the device, so this can be a bare pointer.
704     SkColorSpace*             fColorSpace;
705     FilterResult<For::kInput> fSource;
706 };
707 
708 } // end namespace skif
709 
710 #endif // SkImageFilterTypes_DEFINED
711