Line data Source code
1 : /*
2 : * Copyright 2011 Google Inc.
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_DEFINED
9 : #define SkImageFilter_DEFINED
10 :
11 : #include "../private/SkTArray.h"
12 : #include "../private/SkTemplates.h"
13 : #include "../private/SkMutex.h"
14 : #include "SkColorSpace.h"
15 : #include "SkFilterQuality.h"
16 : #include "SkFlattenable.h"
17 : #include "SkMatrix.h"
18 : #include "SkRect.h"
19 :
20 : class GrContext;
21 : class GrFragmentProcessor;
22 : class SkColorFilter;
23 : class SkColorSpaceXformer;
24 : struct SkIPoint;
25 : class SkSpecialImage;
26 : class SkImageFilterCache;
27 : struct SkImageFilterCacheKey;
28 :
29 : /**
30 : * Base class for image filters. If one is installed in the paint, then
31 : * all drawing occurs as usual, but it is as if the drawing happened into an
32 : * offscreen (before the xfermode is applied). This offscreen bitmap will
33 : * then be handed to the imagefilter, who in turn creates a new bitmap which
34 : * is what will finally be drawn to the device (using the original xfermode).
35 : */
36 : class SK_API SkImageFilter : public SkFlattenable {
37 : public:
38 : // Extra information about the output of a filter DAG. For now, this is just the color space
39 : // (of the original requesting device). This is used when constructing intermediate rendering
40 : // surfaces, so that we ensure we land in a surface that's similar/compatible to the final
41 : // consumer of the DAG's output.
42 : class OutputProperties {
43 : public:
44 0 : explicit OutputProperties(SkColorSpace* colorSpace) : fColorSpace(colorSpace) {}
45 :
46 0 : SkColorSpace* colorSpace() const { return fColorSpace; }
47 :
48 : private:
49 : // This will be a pointer to the device's color space, and our lifetime is bounded by
50 : // the device, so we can store a bare pointer.
51 : SkColorSpace* fColorSpace;
52 : };
53 :
54 : class Context {
55 : public:
56 0 : Context(const SkMatrix& ctm, const SkIRect& clipBounds, SkImageFilterCache* cache,
57 : const OutputProperties& outputProperties)
58 0 : : fCTM(ctm)
59 : , fClipBounds(clipBounds)
60 : , fCache(cache)
61 0 : , fOutputProperties(outputProperties)
62 0 : {}
63 :
64 0 : const SkMatrix& ctm() const { return fCTM; }
65 0 : const SkIRect& clipBounds() const { return fClipBounds; }
66 0 : SkImageFilterCache* cache() const { return fCache; }
67 0 : const OutputProperties& outputProperties() const { return fOutputProperties; }
68 :
69 : private:
70 : SkMatrix fCTM;
71 : SkIRect fClipBounds;
72 : SkImageFilterCache* fCache;
73 : OutputProperties fOutputProperties;
74 : };
75 :
76 : class CropRect {
77 : public:
78 : enum CropEdge {
79 : kHasLeft_CropEdge = 0x01,
80 : kHasTop_CropEdge = 0x02,
81 : kHasWidth_CropEdge = 0x04,
82 : kHasHeight_CropEdge = 0x08,
83 : kHasAll_CropEdge = 0x0F,
84 : };
85 0 : CropRect() {}
86 0 : explicit CropRect(const SkRect& rect, uint32_t flags = kHasAll_CropEdge)
87 0 : : fRect(rect), fFlags(flags) {}
88 0 : uint32_t flags() const { return fFlags; }
89 0 : const SkRect& rect() const { return fRect; }
90 : #ifndef SK_IGNORE_TO_STRING
91 : void toString(SkString* str) const;
92 : #endif
93 :
94 : /**
95 : * Apply this cropRect to the imageBounds. If a given edge of the cropRect is not
96 : * set, then the corresponding edge from imageBounds will be used. If "embiggen"
97 : * is true, the crop rect is allowed to enlarge the size of the rect, otherwise
98 : * it may only reduce the rect. Filters that can affect transparent black should
99 : * pass "true", while all other filters should pass "false".
100 : *
101 : * Note: imageBounds is in "device" space, as the output cropped rectangle will be,
102 : * so the matrix is ignored for those. It is only applied the croprect's bounds.
103 : */
104 : void applyTo(const SkIRect& imageBounds, const SkMatrix&, bool embiggen,
105 : SkIRect* cropped) const;
106 :
107 : private:
108 : SkRect fRect;
109 : uint32_t fFlags;
110 : };
111 :
112 : enum TileUsage {
113 : kPossible_TileUsage, //!< the created device may be drawn tiled
114 : kNever_TileUsage, //!< the created device will never be drawn tiled
115 : };
116 :
117 : /**
118 : * Request a new filtered image to be created from the src image.
119 : *
120 : * The context contains the environment in which the filter is occurring.
121 : * It includes the clip bounds, CTM and cache.
122 : *
123 : * Offset is the amount to translate the resulting image relative to the
124 : * src when it is drawn. This is an out-param.
125 : *
126 : * If the result image cannot be created, or the result would be
127 : * transparent black, return null, in which case the offset parameter
128 : * should be ignored by the caller.
129 : *
130 : * TODO: Right now the imagefilters sometimes return empty result bitmaps/
131 : * specialimages. That doesn't seem quite right.
132 : */
133 : sk_sp<SkSpecialImage> filterImage(SkSpecialImage* src, const Context&, SkIPoint* offset) const;
134 :
135 : enum MapDirection {
136 : kForward_MapDirection,
137 : kReverse_MapDirection
138 : };
139 : /**
140 : * Map a device-space rect recursively forward or backward through the
141 : * filter DAG. kForward_MapDirection is used to determine which pixels of
142 : * the destination canvas a source image rect would touch after filtering.
143 : * kReverse_MapDirection is used to determine which rect of the source
144 : * image would be required to fill the given rect (typically, clip bounds).
145 : * Used for clipping and temp-buffer allocations, so the result need not
146 : * be exact, but should never be smaller than the real answer. The default
147 : * implementation recursively unions all input bounds, or returns the
148 : * source rect if no inputs.
149 : */
150 : SkIRect filterBounds(const SkIRect& src, const SkMatrix& ctm,
151 : MapDirection = kReverse_MapDirection) const;
152 :
153 : #if SK_SUPPORT_GPU
154 : static sk_sp<SkSpecialImage> DrawWithFP(GrContext* context,
155 : sk_sp<GrFragmentProcessor> fp,
156 : const SkIRect& bounds,
157 : const OutputProperties& outputProperties);
158 : #endif
159 :
160 : /**
161 : * Returns whether this image filter is a color filter and puts the color filter into the
162 : * "filterPtr" parameter if it can. Does nothing otherwise.
163 : * If this returns false, then the filterPtr is unchanged.
164 : * If this returns true, then if filterPtr is not null, it must be set to a ref'd colorfitler
165 : * (i.e. it may not be set to NULL).
166 : */
167 0 : bool isColorFilterNode(SkColorFilter** filterPtr) const {
168 0 : return this->onIsColorFilterNode(filterPtr);
169 : }
170 :
171 : // DEPRECATED : use isColorFilterNode() instead
172 : bool asColorFilter(SkColorFilter** filterPtr) const {
173 : return this->isColorFilterNode(filterPtr);
174 : }
175 :
176 : static sk_sp<SkImageFilter> MakeBlur(SkScalar sigmaX, SkScalar sigmaY,
177 : sk_sp<SkImageFilter> input,
178 : const CropRect* cropRect = nullptr);
179 :
180 : /**
181 : * Returns true (and optionally returns a ref'd filter) if this imagefilter can be completely
182 : * replaced by the returned colorfilter. i.e. the two effects will affect drawing in the
183 : * same way.
184 : */
185 : bool asAColorFilter(SkColorFilter** filterPtr) const;
186 :
187 : /**
188 : * Returns the number of inputs this filter will accept (some inputs can
189 : * be NULL).
190 : */
191 0 : int countInputs() const { return fInputs.count(); }
192 :
193 : /**
194 : * Returns the input filter at a given index, or NULL if no input is
195 : * connected. The indices used are filter-specific.
196 : */
197 0 : SkImageFilter* getInput(int i) const {
198 0 : SkASSERT(i < fInputs.count());
199 0 : return fInputs[i].get();
200 : }
201 :
202 : /**
203 : * Returns whether any edges of the crop rect have been set. The crop
204 : * rect is set at construction time, and determines which pixels from the
205 : * input image will be processed, and which pixels in the output image will be allowed.
206 : * The size of the crop rect should be
207 : * used as the size of the destination image. The origin of this rect
208 : * should be used to offset access to the input images, and should also
209 : * be added to the "offset" parameter in onFilterImage.
210 : */
211 0 : bool cropRectIsSet() const { return fCropRect.flags() != 0x0; }
212 :
213 0 : CropRect getCropRect() const { return fCropRect; }
214 :
215 : // Default impl returns union of all input bounds.
216 : virtual SkRect computeFastBounds(const SkRect&) const;
217 :
218 : // Can this filter DAG compute the resulting bounds of an object-space rectangle?
219 : bool canComputeFastBounds() const;
220 :
221 : /**
222 : * If this filter can be represented by another filter + a localMatrix, return that filter,
223 : * else return null.
224 : */
225 : sk_sp<SkImageFilter> makeWithLocalMatrix(const SkMatrix&) const;
226 :
227 : /**
228 : * ImageFilters can natively handle scaling and translate components in the CTM. Only some of
229 : * them can handle affine (or more complex) matrices. This call returns true iff the filter
230 : * and all of its (non-null) inputs can handle these more complex matrices.
231 : */
232 : bool canHandleComplexCTM() const;
233 :
234 : /**
235 : * Return an imagefilter which transforms its input by the given matrix.
236 : */
237 : static sk_sp<SkImageFilter> MakeMatrixFilter(const SkMatrix& matrix,
238 : SkFilterQuality quality,
239 : sk_sp<SkImageFilter> input);
240 :
241 : SK_TO_STRING_PUREVIRT()
242 0 : SK_DEFINE_FLATTENABLE_TYPE(SkImageFilter)
243 : SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
244 :
245 : protected:
246 0 : class Common {
247 : public:
248 : /**
249 : * Attempt to unflatten the cropRect and the expected number of input filters.
250 : * If any number of input filters is valid, pass -1.
251 : * If this fails (i.e. corrupt buffer or contents) then return false and common will
252 : * be left uninitialized.
253 : * If this returns true, then inputCount() is the number of found input filters, each
254 : * of which may be NULL or a valid imagefilter.
255 : */
256 : bool unflatten(SkReadBuffer&, int expectedInputs);
257 :
258 0 : const CropRect& cropRect() const { return fCropRect; }
259 0 : int inputCount() const { return fInputs.count(); }
260 0 : sk_sp<SkImageFilter>* inputs() const { return fInputs.get(); }
261 :
262 0 : sk_sp<SkImageFilter> getInput(int index) const { return fInputs[index]; }
263 :
264 : private:
265 : CropRect fCropRect;
266 : // most filters accept at most 2 input-filters
267 : SkAutoSTArray<2, sk_sp<SkImageFilter>> fInputs;
268 :
269 : void allocInputs(int count);
270 : };
271 :
272 : SkImageFilter(sk_sp<SkImageFilter>* inputs, int inputCount, const CropRect* cropRect);
273 :
274 : ~SkImageFilter() override;
275 :
276 : /**
277 : * Constructs a new SkImageFilter read from an SkReadBuffer object.
278 : *
279 : * @param inputCount The exact number of inputs expected for this SkImageFilter object.
280 : * -1 can be used if the filter accepts any number of inputs.
281 : * @param rb SkReadBuffer object from which the SkImageFilter is read.
282 : */
283 : explicit SkImageFilter(int inputCount, SkReadBuffer& rb);
284 :
285 : void flatten(SkWriteBuffer&) const override;
286 :
287 0 : const CropRect* getCropRectIfSet() const {
288 0 : return this->cropRectIsSet() ? &fCropRect : nullptr;
289 : }
290 :
291 : /**
292 : * This is the virtual which should be overridden by the derived class
293 : * to perform image filtering.
294 : *
295 : * src is the original primitive bitmap. If the filter has a connected
296 : * input, it should recurse on that input and use that in place of src.
297 : *
298 : * The matrix is the current matrix on the canvas.
299 : *
300 : * Offset is the amount to translate the resulting image relative to the
301 : * src when it is drawn. This is an out-param.
302 : *
303 : * If the result image cannot be created (either because of error or if, say, the result
304 : * is entirely clipped out), this should return nullptr.
305 : * Callers that affect transparent black should explicitly handle nullptr
306 : * results and press on. In the error case this behavior will produce a better result
307 : * than nothing and is necessary for the clipped out case.
308 : * If the return value is nullptr then offset should be ignored.
309 : */
310 : virtual sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* src, const Context&,
311 : SkIPoint* offset) const = 0;
312 :
313 : /**
314 : * This function recurses into its inputs with the given rect (first
315 : * argument), calls filterBounds() with the given map direction on each,
316 : * and returns the union of those results. If a derived class has special
317 : * recursion requirements (e.g., it has an input which does not participate
318 : * in bounds computation), it can be overridden here.
319 : *
320 : * Note that this function is *not* responsible for mapping the rect for
321 : * this node's filter bounds requirements (i.e., calling
322 : * onFilterNodeBounds()); that is handled by filterBounds().
323 : */
324 : virtual SkIRect onFilterBounds(const SkIRect&, const SkMatrix&, MapDirection) const;
325 :
326 : /**
327 : * Performs a forwards or reverse mapping of the given rect to accommodate
328 : * this filter's margin requirements. kForward_MapDirection is used to
329 : * determine the destination pixels which would be touched by filtering
330 : * the given given source rect (e.g., given source bitmap bounds,
331 : * determine the optimal bounds of the filtered offscreen bitmap).
332 : * kReverse_MapDirection is used to determine which pixels of the
333 : * input(s) would be required to fill the given destination rect
334 : * (e.g., clip bounds). NOTE: these operations may not be the
335 : * inverse of the other. For example, blurring expands the given rect
336 : * in both forward and reverse directions. Unlike
337 : * onFilterBounds(), this function is non-recursive.
338 : */
339 : virtual SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const;
340 :
341 : // Helper function which invokes filter processing on the input at the
342 : // specified "index". If the input is null, it returns "src" and leaves
343 : // "offset" untouched. If the input is non-null, it
344 : // calls filterImage() on that input, and returns the result.
345 : sk_sp<SkSpecialImage> filterInput(int index,
346 : SkSpecialImage* src,
347 : const Context&,
348 : SkIPoint* offset) const;
349 :
350 : /**
351 : * Return true (and return a ref'd colorfilter) if this node in the DAG is just a
352 : * colorfilter w/o CropRect constraints.
353 : */
354 0 : virtual bool onIsColorFilterNode(SkColorFilter** /*filterPtr*/) const {
355 0 : return false;
356 : }
357 :
358 : /**
359 : * Override this to describe the behavior of your subclass - as a leaf node. The caller will
360 : * take care of calling your inputs (and return false if any of them could not handle it).
361 : */
362 0 : virtual bool onCanHandleComplexCTM() const { return false; }
363 :
364 : /** Given a "srcBounds" rect, computes destination bounds for this filter.
365 : * "dstBounds" are computed by transforming the crop rect by the context's
366 : * CTM, applying it to the initial bounds, and intersecting the result with
367 : * the context's clip bounds. "srcBounds" (if non-null) are computed by
368 : * intersecting the initial bounds with "dstBounds", to ensure that we never
369 : * sample outside of the crop rect (this restriction may be relaxed in the
370 : * future).
371 : */
372 : bool applyCropRect(const Context&, const SkIRect& srcBounds, SkIRect* dstBounds) const;
373 :
374 : /** A variant of the above call which takes the original source bitmap and
375 : * source offset. If the resulting crop rect is not entirely contained by
376 : * the source bitmap's bounds, it creates a new bitmap in "result" and
377 : * pads the edges with transparent black. In that case, the srcOffset is
378 : * modified to be the same as the bounds, since no further adjustment is
379 : * needed by the caller. This version should only be used by filters
380 : * which are not capable of processing a smaller source bitmap into a
381 : * larger destination.
382 : */
383 : sk_sp<SkSpecialImage> applyCropRect(const Context&, SkSpecialImage* src, SkIPoint* srcOffset,
384 : SkIRect* bounds) const;
385 :
386 : /**
387 : * Creates a modified Context for use when recursing up the image filter DAG.
388 : * The clip bounds are adjusted to accommodate any margins that this
389 : * filter requires by calling this node's
390 : * onFilterNodeBounds(..., kReverse_MapDirection).
391 : */
392 : Context mapContext(const Context& ctx) const;
393 :
394 : #if SK_SUPPORT_GPU
395 : /**
396 : * Returns a version of the passed-in image (possibly the original), that is in a colorspace
397 : * with the same gamut as the one from the OutputProperties. This allows filters that do many
398 : * texture samples to guarantee that any color space conversion has happened before running.
399 : */
400 : static sk_sp<SkSpecialImage> ImageToColorSpace(SkSpecialImage* src, const OutputProperties&);
401 : #endif
402 :
403 : /**
404 : * Returns an image filter transformed into a new color space via the |xformer|.
405 : */
406 0 : sk_sp<SkImageFilter> makeColorSpace(SkColorSpaceXformer* xformer) const {
407 0 : return this->onMakeColorSpace(xformer);
408 : }
409 0 : virtual sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const {
410 0 : return sk_ref_sp(const_cast<SkImageFilter*>(this));
411 : }
412 :
413 : private:
414 : // For makeColorSpace().
415 : friend class ArithmeticImageFilterImpl;
416 : friend class SkAlphaThresholdFilterImpl;
417 : friend class SkBlurImageFilterImpl;
418 : friend class SkColorFilterImageFilter;
419 : friend class SkColorSpaceXformer;
420 : friend class SkComposeImageFilter;
421 : friend class SkDisplacementMapEffect;
422 : friend class SkDropShadowImageFilter;
423 : friend class SkImageSource;
424 : friend class SkMagnifierImageFilter;
425 : friend class SkMatrixConvolutionImageFilter;
426 : friend class SkMergeImageFilter;
427 : friend class SkMorphologyImageFilter;
428 : friend class SkOffsetImageFilter;
429 : friend class SkTileImageFilter;
430 : friend class SkXfermodeImageFilter_Base;
431 :
432 : friend class SkGraphics;
433 :
434 : static void PurgeCache();
435 :
436 : void init(sk_sp<SkImageFilter>* inputs, int inputCount, const CropRect* cropRect);
437 :
438 0 : bool usesSrcInput() const { return fUsesSrcInput; }
439 0 : virtual bool affectsTransparentBlack() const { return false; }
440 :
441 : SkAutoSTArray<2, sk_sp<SkImageFilter>> fInputs;
442 :
443 : bool fUsesSrcInput;
444 : CropRect fCropRect;
445 : uint32_t fUniqueID; // Globally unique
446 : mutable SkTArray<SkImageFilterCacheKey> fCacheKeys;
447 : mutable SkMutex fMutex;
448 : typedef SkFlattenable INHERITED;
449 : };
450 :
451 : /**
452 : * Helper to unflatten the common data, and return NULL if we fail.
453 : */
454 : #define SK_IMAGEFILTER_UNFLATTEN_COMMON(localVar, expectedCount) \
455 : Common localVar; \
456 : do { \
457 : if (!localVar.unflatten(buffer, expectedCount)) { \
458 : return NULL; \
459 : } \
460 : } while (0)
461 :
462 : #endif
|