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