• 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 SkImageFilter_Base_DEFINED
9 #define SkImageFilter_Base_DEFINED
10 
11 #include "include/core/SkColorSpace.h"
12 #include "include/core/SkImageFilter.h"
13 #include "include/core/SkImageInfo.h"
14 #include "include/private/SkTArray.h"
15 
16 #include "src/core/SkImageFilterTypes.h"
17 
18 class GrFragmentProcessor;
19 class GrRecordingContext;
20 
21 // True base class that all SkImageFilter implementations need to extend from. This provides the
22 // actual API surface that Skia will use to compute the filtered images.
23 class SkImageFilter_Base : public SkImageFilter {
24 public:
25     // DEPRECATED - Use skif::Context directly.
26     using Context = skif::Context;
27 
28     /**
29      *  Request a new filtered image to be created from the src image.
30      *
31      *  The context contains the environment in which the filter is occurring.
32      *  It includes the clip bounds, CTM and cache.
33      *
34      *  Offset is the amount to translate the resulting image relative to the
35      *  src when it is drawn. This is an out-param.
36      *
37      *  If the result image cannot be created, or the result would be
38      *  transparent black, return null, in which case the offset parameter
39      *  should be ignored by the caller.
40      *
41      *  TODO: Right now the imagefilters sometimes return empty result bitmaps/
42      *        specialimages. That doesn't seem quite right.
43      */
44     sk_sp<SkSpecialImage> filterImage(const skif::Context& context, SkIPoint* offset) const;
45 
46     /**
47      *  Returns whether any edges of the crop rect have been set. The crop
48      *  rect is set at construction time, and determines which pixels from the
49      *  input image will be processed, and which pixels in the output image will be allowed.
50      *  The size of the crop rect should be
51      *  used as the size of the destination image. The origin of this rect
52      *  should be used to offset access to the input images, and should also
53      *  be added to the "offset" parameter in onFilterImage.
54      */
cropRectIsSet()55     bool cropRectIsSet() const { return fCropRect.flags() != 0x0; }
56 
getCropRect()57     CropRect getCropRect() const { return fCropRect; }
58 
59     // Expose isolated node bounds behavior for SampleImageFilterDAG and debugging
filterNodeBounds(const SkIRect & srcRect,const SkMatrix & ctm,MapDirection dir,const SkIRect * inputRect)60     SkIRect filterNodeBounds(const SkIRect& srcRect, const SkMatrix& ctm,
61                              MapDirection dir, const SkIRect* inputRect) const {
62         return this->onFilterNodeBounds(srcRect, ctm, dir, inputRect);
63     }
64 
65     /**
66      *  ImageFilters can natively handle scaling and translate components in the CTM. Only some of
67      *  them can handle affine (or more complex) matrices. This call returns true iff the filter
68      *  and all of its (non-null) inputs can handle these more complex matrices.
69      */
70     bool canHandleComplexCTM() const;
71 
72     /**
73      * Return an image filter representing this filter applied with the given ctm. This will modify
74      * the DAG as needed if this filter does not support complex CTMs and 'ctm' is not simple. The
75      * ctm matrix will be decomposed such that ctm = A*B; B will be incorporated directly into the
76      * DAG and A must be the ctm set on the context passed to filterImage(). 'remainder' will be set
77      * to A.
78      *
79      * If this filter supports complex ctms, or 'ctm' is not complex, then A = ctm and B = I. When
80      * the filter does not support complex ctms, and the ctm is complex, then A represents the
81      * extracted simple portion of the ctm, and the complex portion is baked into a new DAG using a
82      * matrix filter.
83      *
84      * This will never return null.
85      */
86     sk_sp<SkImageFilter> applyCTM(const SkMatrix& ctm, SkMatrix* remainder) const;
87     /**
88      * Similar to SkApplyCTMToFilter except this assumes the input content is an existing backdrop
89      * image to be filtered. As such,  the input to this filter will also be transformed by B^-1 if
90      * the filter can't support complex CTMs, since backdrop content is already in device space and
91      * must be transformed back into the CTM's local space.
92      */
93     sk_sp<SkImageFilter> applyCTMForBackdrop(const SkMatrix& ctm, SkMatrix* remainder) const;
94 
uniqueID()95     uint32_t uniqueID() const { return fUniqueID; }
96 
97 protected:
98     class Common {
99     public:
100         /**
101          *  Attempt to unflatten the cropRect and the expected number of input filters.
102          *  If any number of input filters is valid, pass -1.
103          *  If this fails (i.e. corrupt buffer or contents) then return false and common will
104          *  be left uninitialized.
105          *  If this returns true, then inputCount() is the number of found input filters, each
106          *  of which may be NULL or a valid imagefilter.
107          */
108         bool unflatten(SkReadBuffer&, int expectedInputs);
109 
cropRect()110         const CropRect& cropRect() const { return fCropRect; }
inputCount()111         int inputCount() const { return fInputs.count(); }
inputs()112         sk_sp<SkImageFilter>* inputs() { return fInputs.begin(); }
113 
getInput(int index)114         sk_sp<SkImageFilter> getInput(int index) { return fInputs[index]; }
115 
116     private:
117         CropRect fCropRect;
118         // most filters accept at most 2 input-filters
119         SkSTArray<2, sk_sp<SkImageFilter>, true> fInputs;
120     };
121 
122     SkImageFilter_Base(sk_sp<SkImageFilter> const* inputs, int inputCount,
123                        const CropRect* cropRect);
124 
125     ~SkImageFilter_Base() override;
126 
127     void flatten(SkWriteBuffer&) const override;
128 
affectsTransparentBlack()129     virtual bool affectsTransparentBlack() const { return false; }
130 
131     /**
132      *  This is the virtual which should be overridden by the derived class
133      *  to perform image filtering.
134      *
135      *  src is the original primitive bitmap. If the filter has a connected
136      *  input, it should recurse on that input and use that in place of src.
137      *
138      *  The matrix is the matrix used to draw the geometry into the current
139      *  layer that produced the 'src' image. This may be the total canvas'
140      *  matrix, or part of its decomposition (depending on what the filter DAG
141      *  is able to support).
142      *
143      *  Offset is the amount to translate the resulting image relative to the
144      *  src when it is drawn. This is an out-param.
145      *
146      *  If the result image cannot be created (either because of error or if, say, the result
147      *  is entirely clipped out), this should return nullptr.
148      *  Callers that affect transparent black should explicitly handle nullptr
149      *  results and press on. In the error case this behavior will produce a better result
150      *  than nothing and is necessary for the clipped out case.
151      *  If the return value is nullptr then offset should be ignored.
152      */
153     virtual sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const = 0;
154 
155     /**
156      * This function recurses into its inputs with the given rect (first
157      * argument), calls filterBounds() with the given map direction on each,
158      * and returns the union of those results. If a derived class has special
159      * recursion requirements (e.g., it has an input which does not participate
160      * in bounds computation), it can be overridden here.
161      * In kReverse mode, 'inputRect' is the device-space bounds of the input pixels. In kForward
162      * mode it should always be null. If 'inputRect' is null in kReverse mode the resulting
163      * answer may be incorrect.
164      *
165      * Note that this function is *not* responsible for mapping the rect for
166      * this node's filter bounds requirements (i.e., calling
167      * onFilterNodeBounds()); that is handled by filterBounds().
168      */
169     virtual SkIRect onFilterBounds(const SkIRect&, const SkMatrix& ctm,
170                                    MapDirection, const SkIRect* inputRect) const;
171 
172     /**
173      * Performs a forwards or reverse mapping of the given rect to accommodate
174      * this filter's margin requirements. kForward_MapDirection is used to
175      * determine the destination pixels which would be touched by filtering
176      * the given source rect (e.g., given source bitmap bounds,
177      * determine the optimal bounds of the filtered offscreen bitmap).
178      * kReverse_MapDirection is used to determine which pixels of the
179      * input(s) would be required to fill the given destination rect
180      * (e.g., clip bounds). NOTE: these operations may not be the
181      * inverse of the other. For example, blurring expands the given rect
182      * in both forward and reverse directions. Unlike
183      * onFilterBounds(), this function is non-recursive.
184      * In kReverse mode, 'inputRect' will be the device space bounds of the input pixels. In
185      * kForward mode, 'inputRect' should always be null. If 'inputRect' is null in kReverse mode
186      * the resulting answer may be incorrect.
187      */
188     virtual SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm,
189                                        MapDirection, const SkIRect* inputRect) const;
190 
191     // Helper function which invokes filter processing on the input at the specified "index". If the
192     // input is null, it returns the Context's source image "src" and leaves "offset" untouched. If
193     // the input is non-null, it calls filterImage() on that input, and returns the result.
194     sk_sp<SkSpecialImage> filterInput(int index,
195                                       const Context&,
196                                       SkIPoint* offset) const;
197 
198     /**
199      *  Return true (and returns a ref'd colorfilter) if this node in the DAG is just a
200      *  colorfilter w/o CropRect constraints.
201      */
onIsColorFilterNode(SkColorFilter **)202     virtual bool onIsColorFilterNode(SkColorFilter** /*filterPtr*/) const {
203         return false;
204     }
205 
206     /**
207      *  Override this to describe the behavior of your subclass - as a leaf node. The caller will
208      *  take care of calling your inputs (and return false if any of them could not handle it).
209      */
onCanHandleComplexCTM()210     virtual bool onCanHandleComplexCTM() const { return false; }
211 
getCropRectIfSet()212     const CropRect* getCropRectIfSet() const {
213         return this->cropRectIsSet() ? &fCropRect : nullptr;
214     }
215 
216     /** Given a "srcBounds" rect, computes destination bounds for this filter.
217      *  "dstBounds" are computed by transforming the crop rect by the context's
218      *  CTM, applying it to the initial bounds, and intersecting the result with
219      *  the context's clip bounds.  "srcBounds" (if non-null) are computed by
220      *  intersecting the initial bounds with "dstBounds", to ensure that we never
221      *  sample outside of the crop rect (this restriction may be relaxed in the
222      *  future).
223      */
224     bool applyCropRect(const Context&, const SkIRect& srcBounds, SkIRect* dstBounds) const;
225 
226     /** A variant of the above call which takes the original source bitmap and
227      *  source offset. If the resulting crop rect is not entirely contained by
228      *  the source bitmap's bounds, it creates a new bitmap in "result" and
229      *  pads the edges with transparent black. In that case, the srcOffset is
230      *  modified to be the same as the bounds, since no further adjustment is
231      *  needed by the caller. This version should only be used by filters
232      *  which are not capable of processing a smaller source bitmap into a
233      *  larger destination.
234      */
235     sk_sp<SkSpecialImage> applyCropRectAndPad(const Context&, SkSpecialImage* src,
236                                               SkIPoint* srcOffset, SkIRect* bounds) const;
237 
238     /**
239      *  Creates a modified Context for use when recursing up the image filter DAG.
240      *  The clip bounds are adjusted to accommodate any margins that this
241      *  filter requires by calling this node's
242      *  onFilterNodeBounds(..., kReverse_MapDirection).
243      */
244     Context mapContext(const Context& ctx) const;
245 
246 #if SK_SUPPORT_GPU
247     static sk_sp<SkSpecialImage> DrawWithFP(GrRecordingContext* context,
248                                             std::unique_ptr<GrFragmentProcessor> fp,
249                                             const SkIRect& bounds,
250                                             SkColorType colorType,
251                                             const SkColorSpace* colorSpace,
252                                             GrProtected isProtected = GrProtected::kNo);
253 
254     /**
255      *  Returns a version of the passed-in image (possibly the original), that is in a colorspace
256      *  with the same gamut as the one from the OutputProperties. This allows filters that do many
257      *  texture samples to guarantee that any color space conversion has happened before running.
258      */
259     static sk_sp<SkSpecialImage> ImageToColorSpace(SkSpecialImage* src,
260                                                    SkColorType colorType,
261                                                    SkColorSpace* colorSpace);
262 #endif
263 
264     // If 'srcBounds' will sample outside the border of 'originalSrcBounds' (i.e., the sample
265     // will wrap around to the other side) we must preserve the far side of the src along that
266     // axis (e.g., if we will sample beyond the left edge of the src, the right side must be
267     // preserved for the repeat sampling to work).
268     static SkIRect DetermineRepeatedSrcBound(const SkIRect& srcBounds,
269                                              const SkIVector& filterOffset,
270                                              const SkISize& filterSize,
271                                              const SkIRect& originalSrcBounds);
272 
273 private:
274     friend class SkImageFilter;
275     // For PurgeCache()
276     friend class SkGraphics;
277 
278     static void PurgeCache();
279 
280     void init(sk_sp<SkImageFilter> const* inputs, int inputCount, const CropRect* cropRect);
281 
282     SkAutoSTArray<2, sk_sp<SkImageFilter>> fInputs;
283 
284     bool fUsesSrcInput;
285     CropRect fCropRect;
286     uint32_t fUniqueID; // Globally unique
287 
288     typedef SkImageFilter INHERITED;
289 };
290 
as_IFB(SkImageFilter * filter)291 static inline SkImageFilter_Base* as_IFB(SkImageFilter* filter) {
292     return static_cast<SkImageFilter_Base*>(filter);
293 }
294 
as_IFB(const sk_sp<SkImageFilter> & filter)295 static inline SkImageFilter_Base* as_IFB(const sk_sp<SkImageFilter>& filter) {
296     return static_cast<SkImageFilter_Base*>(filter.get());
297 }
298 
as_IFB(const SkImageFilter * filter)299 static inline const SkImageFilter_Base* as_IFB(const SkImageFilter* filter) {
300     return static_cast<const SkImageFilter_Base*>(filter);
301 }
302 
303 /**
304  *  Helper to unflatten the common data, and return nullptr if we fail.
305  */
306 #define SK_IMAGEFILTER_UNFLATTEN_COMMON(localVar, expectedCount)    \
307     Common localVar;                                                \
308     do {                                                            \
309         if (!localVar.unflatten(buffer, expectedCount)) {           \
310             return nullptr;                                         \
311         }                                                           \
312     } while (0)
313 
314 #endif // SkImageFilter_Base_DEFINED
315