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