• 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 #include "include/private/SkTemplates.h"
16 
17 #include "src/core/SkImageFilterTypes.h"
18 
19 class GrFragmentProcessor;
20 class GrRecordingContext;
21 
22 // True base class that all SkImageFilter implementations need to extend from. This provides the
23 // actual API surface that Skia will use to compute the filtered images.
24 class SkImageFilter_Base : public SkImageFilter {
25 public:
26     SK_USE_FLUENT_IMAGE_FILTER_TYPES_IN_CLASS
27 
28     // DEPRECATED - Use skif::Context directly.
29     using Context = skif::Context;
30 
31     /**
32      *  Request a new filtered image to be created from the src image. The returned skif::Image
33      *  provides both the pixel data and the origin point that it should be drawn at, relative to
34      *  the layer space defined by the provided context.
35      *
36      *  If the result image cannot be created, or the result would be transparent black, returns
37      *  a skif::Image that has a null special image, in which its origin should be ignored.
38      *
39      *  TODO: Right now the imagefilters sometimes return empty result bitmaps/
40      *        specialimages. That doesn't seem quite right.
41      */
42     skif::FilterResult<For::kOutput> filterImage(const skif::Context& context) const;
43 
44     /**
45      *  Calculate the smallest-possible required layer bounds that would provide sufficient
46      *  information to correctly compute the image filter for every pixel in the desired output
47      *  bounds. The 'desiredOutput' is intended to represent either the root render target bounds,
48      *  or the device-space bounds of the current clip. If the bounds of the content that will be
49      *  drawn into the layer is known, 'knownContentBounds' should be provided, since it can be
50      *  used to restrict the size of the layer if the image filter DAG does not affect transparent
51      *  black.
52      *
53      *  The returned rect is in the layer space defined by 'mapping', so it directly represents
54      *  the size and location of the SkDevice created to rasterize the content prior to invoking the
55      *  image filter (assuming its CTM and basis matrix are configured to match 'mapping').
56      *
57      *  While this operation transforms an device-space output bounds to a layer-space input bounds,
58      *  it is not necessarily the inverse of getOutputBounds(). For instance, a blur needs to have
59      *  an outset margin when reading pixels at the edge (to satisfy its kernel), thus it expands
60      *  its required input rect to include every pixel that contributes to the desired output rect.
61 
62      *  @param mapping       The coordinate space mapping that defines both the transformation
63      *                       between local and layer, and layer to root device space, that will be
64      *                       used when the filter is later invoked.
65      *  @param desiredOutput The desired output boundary that needs to be covered by the filter's
66      *                       output (assuming that the filter is then invoked with a suitable input)
67      *  @param knownContentBounds
68      *                       Optional, the known layer-space bounds of the non-transparent content
69      *                       that would be rasterized in the source input image.
70      *
71      * @return The layer-space bounding box to use for an SkDevice when drawing the source image.
72      */
73     skif::LayerSpace<SkIRect> getInputBounds(
74             const skif::Mapping& mapping, const skif::DeviceSpace<SkIRect>& desiredOutput,
75             const skif::ParameterSpace<SkRect>* knownContentBounds) const;
76 
77     /**
78      *  Calculate the device-space bounds of the output of this filter DAG, if it were to process
79      *  an image layer covering the 'contentBounds'. The 'mapping' defines how the content will be
80      *  transformed to layer space when it is drawn, and how the output filter image is then
81      *  transformed to the final device space (i.e. it specifies the mapping between the root device
82      *  space and the parameter space of the initially provided content).
83      *
84      *  While this operation transforms a parameter-space input bounds to an device-space output
85      *  bounds, it is not necessarily the inverse of getInputBounds(). For instance, a blur needs to
86      *  have an outset margin when reading pixels at the edge (to satisfy its kernel), so it will
87      *  generate a result larger than its input (so that the blur is visible) and, thus, expands its
88      *  output to include every pixel that it will touch.
89      *
90      *  @param mapping       The coordinate space mapping that defines both the transformation
91      *                       between local and layer, and layer to root device space, that will be
92      *                       used when the filter is later invoked.
93      *  @param contentBounds The local-space bounds of the non-transparent content that would be
94      *                       drawn into the source image prior to filtering with this DAG,  i.e.
95      *                       the same as 'knownContentBounds' in getInputBounds().
96      *
97      *  @return The root device-space bounding box of the filtered image, were it applied to
98      *          content contained by 'contentBounds' and then drawn with 'mapping' to the root
99      *          device (w/o any additional clipping).
100      */
101     skif::DeviceSpace<SkIRect> getOutputBounds(
102             const skif::Mapping& mapping, const skif::ParameterSpace<SkRect>& contentBounds) const;
103 
104     // Expose isolated node bounds behavior for SampleImageFilterDAG and debugging
filterNodeBounds(const SkIRect & srcRect,const SkMatrix & ctm,MapDirection dir,const SkIRect * inputRect)105     SkIRect filterNodeBounds(const SkIRect& srcRect, const SkMatrix& ctm,
106                              MapDirection dir, const SkIRect* inputRect) const {
107         return this->onFilterNodeBounds(srcRect, ctm, dir, inputRect);
108     }
109 
110     /**
111      *  ImageFilters can natively handle scaling and translate components in the CTM. Only some of
112      *  them can handle affine (or more complex) matrices. This call returns true iff the filter
113      *  and all of its (non-null) inputs can handle these more complex matrices.
114      */
115     bool canHandleComplexCTM() const;
116 
117     /**
118      * Return an image filter representing this filter applied with the given ctm. This will modify
119      * the DAG as needed if this filter does not support complex CTMs and 'ctm' is not simple. The
120      * ctm matrix will be decomposed such that ctm = A*B; B will be incorporated directly into the
121      * DAG and A must be the ctm set on the context passed to filterImage(). 'remainder' will be set
122      * to A.
123      *
124      * If this filter supports complex ctms, or 'ctm' is not complex, then A = ctm and B = I. When
125      * the filter does not support complex ctms, and the ctm is complex, then A represents the
126      * extracted simple portion of the ctm, and the complex portion is baked into a new DAG using a
127      * matrix filter.
128      *
129      * This will never return null.
130      *
131      * DEPRECATED - Should draw the results of filterImage() directly with the remainder matrix.
132      */
133     sk_sp<SkImageFilter> applyCTM(const SkMatrix& ctm, SkMatrix* remainder) const;
134 
uniqueID()135     uint32_t uniqueID() const { return fUniqueID; }
136 
137 protected:
138     // DEPRECATED: Will be removed once cropping is handled by a standalone image filter
139     class CropRect {
140     public:
141         enum CropEdge {
142             kHasLeft_CropEdge   = 0x01,
143             kHasTop_CropEdge    = 0x02,
144             kHasWidth_CropEdge  = 0x04,
145             kHasHeight_CropEdge = 0x08,
146             kHasAll_CropEdge    = 0x0F,
147         };
CropRect()148         CropRect() : fFlags(0) {}
CropRect(const SkRect * rect)149         explicit CropRect(const SkRect* rect)
150             : fRect(rect ? *rect : SkRect::MakeEmpty()), fFlags(rect ? kHasAll_CropEdge : 0x0) {}
151 
152         // CropRect(const CropRect&) = default;
153 
flags()154         uint32_t flags() const { return fFlags; }
rect()155         const SkRect& rect() const { return fRect; }
156 
157         /**
158          *  Apply this cropRect to the imageBounds. If a given edge of the cropRect is not set, then
159          *  the corresponding edge from imageBounds will be used. If "embiggen" is true, the crop
160          *  rect is allowed to enlarge the size of the rect, otherwise it may only reduce the rect.
161          *  Filters that can affect transparent black should pass "true", while all other filters
162          *  should pass "false".
163          *
164          *  Note: imageBounds is in "device" space, as the output cropped rectangle will be, so the
165          *  matrix is ignored for those. It is only applied to the cropRect's bounds.
166          */
167         void applyTo(const SkIRect& imageBounds, const SkMatrix& matrix, bool embiggen,
168                      SkIRect* cropped) const;
169 
170     private:
171         SkRect fRect;
172         uint32_t fFlags;
173     };
174 
175     class Common {
176     public:
177         /**
178          *  Attempt to unflatten the cropRect and the expected number of input filters.
179          *  If any number of input filters is valid, pass -1.
180          *  If this fails (i.e. corrupt buffer or contents) then return false and common will
181          *  be left uninitialized.
182          *  If this returns true, then inputCount() is the number of found input filters, each
183          *  of which may be NULL or a valid imagefilter.
184          */
185         bool unflatten(SkReadBuffer&, int expectedInputs);
186 
cropRect()187         const SkRect* cropRect() const {
188             return fCropRect.flags() != 0x0 ? &fCropRect.rect() : nullptr;
189         }
inputCount()190         int inputCount() const { return fInputs.count(); }
inputs()191         sk_sp<SkImageFilter>* inputs() { return fInputs.begin(); }
192 
getInput(int index)193         sk_sp<SkImageFilter> getInput(int index) { return fInputs[index]; }
194 
195     private:
196         CropRect fCropRect;
197         // most filters accept at most 2 input-filters
198         SkSTArray<2, sk_sp<SkImageFilter>, true> fInputs;
199     };
200 
201     // Whether or not to recurse to child input filters for certain operations that walk the DAG.
202     enum class VisitChildren : bool {
203         kNo  = false,
204         kYes = true
205     };
206 
207     SkImageFilter_Base(sk_sp<SkImageFilter> const* inputs, int inputCount,
208                        const SkRect* cropRect);
209 
210     ~SkImageFilter_Base() override;
211 
212     void flatten(SkWriteBuffer&) const override;
213 
214     // DEPRECATED - Use the private context-only variant
215     virtual sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const = 0;
216 
217     // DEPRECATED - Override onGetOutputLayerBounds and onGetInputLayerBounds instead. The
218     // node-specific and aggregation functions are no longer separated in the current API. A helper
219     // function is provided to do the default recursion for the common filter case.
220     virtual SkIRect onFilterBounds(const SkIRect&, const SkMatrix& ctm,
221                                    MapDirection, const SkIRect* inputRect) const;
222     virtual SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm,
223                                        MapDirection, const SkIRect* inputRect) const;
224 
225     // DEPRECRATED - Call the Context-only getInputFilteredImage()
filterInput(int index,const Context & ctx,SkIPoint * offset)226     sk_sp<SkSpecialImage> filterInput(int index, const Context& ctx, SkIPoint* offset) const {
227         return this->getInputFilteredImage(index, ctx).imageAndOffset(offset);
228     }
229 
230     // Helper function to visit each of this filter's child filters and call their
231     // onGetInputLayerBounds with the provided 'desiredOutput' and 'contentBounds'. Automatically
232     // handles null input filters. Returns the union of all of the children's input bounds.
233     skif::LayerSpace<SkIRect> visitInputLayerBounds(
234             const skif::Mapping& mapping, const skif::LayerSpace<SkIRect>& desiredOutput,
235             const skif::LayerSpace<SkIRect>& contentBounds) const;
236     // Helper function to visit each of this filter's child filters and call their
237     // onGetOutputLayerBounds with the provided 'contentBounds'. Automatically handles null input
238     // filters.
239     skif::LayerSpace<SkIRect> visitOutputLayerBounds(
240             const skif::Mapping& mapping, const skif::LayerSpace<SkIRect>& contentBounds) const;
241 
242     // Helper function to help with recursing through the filter DAG. It invokes filter processing
243     // set to null, it returns the dynamic source image on the Context instead.
244     //
245     // Implementations must handle cases when the input filter was unable to compute an image and
246     // the returned skif::Image has a null SkSpecialImage. If the filter affect transparent black
247     // should explicitly handle nullptr results and press on. In the error case this behavior will
248     // produce a better result than nothing and is necessary for the clipped out case.
getInputFilteredImage(int index,const skif::Context & context)249     skif::FilterResult<For::kInput> getInputFilteredImage(int index,
250                                                           const skif::Context& context) const {
251         return this->filterInput<For::kInput>(index, context);
252     }
253     // Convenience that calls filterInput with index = 0 and the most specific usage.
getInputFilteredImage0(const skif::Context & context)254     skif::FilterResult<For::kInput0> getInputFilteredImage0(const skif::Context& context) const {
255         return this->filterInput<For::kInput0>(0, context);
256     }
257     // Convenience that calls filterInput with index = 1 and the most specific usage.
getInputFilteredImage1(const skif::Context & context)258     skif::FilterResult<For::kInput1> getInputFilteredImage1(const skif::Context& context) const {
259         return this->filterInput<For::kInput1>(1, context);
260     }
261 
262     /**
263      *  Returns whether any edges of the crop rect have been set. The crop
264      *  rect is set at construction time, and determines which pixels from the
265      *  input image will be processed, and which pixels in the output image will be allowed.
266      *  The size of the crop rect should be
267      *  used as the size of the destination image. The origin of this rect
268      *  should be used to offset access to the input images, and should also
269      *  be added to the "offset" parameter in onFilterImage.
270      *
271      *  DEPRECATED - Remove once cropping is handled by a separate filter
272      */
cropRectIsSet()273     bool cropRectIsSet() const { return fCropRect.flags() != 0x0; }
274 
275     // DEPRECATED - Remove once cropping is handled by a separate filter
getCropRect()276     CropRect getCropRect() const { return fCropRect; }
277 
278     // DEPRECATED - Remove once cropping is handled by a separate filter
getCropRectIfSet()279     const CropRect* getCropRectIfSet() const {
280         return this->cropRectIsSet() ? &fCropRect : nullptr;
281     }
282 
283     /** Given a "srcBounds" rect, computes destination bounds for this filter.
284      *  "dstBounds" are computed by transforming the crop rect by the context's
285      *  CTM, applying it to the initial bounds, and intersecting the result with
286      *  the context's clip bounds.  "srcBounds" (if non-null) are computed by
287      *  intersecting the initial bounds with "dstBounds", to ensure that we never
288      *  sample outside of the crop rect (this restriction may be relaxed in the
289      *  future).
290      *
291      *  DEPRECATED - Remove once cropping is handled by a separate filter, although it may be
292      *  necessary to provide a similar convenience function to compute the output bounds given the
293      *  images returned by filterInput().
294      */
295     bool applyCropRect(const Context&, const SkIRect& srcBounds, SkIRect* dstBounds) const;
296 
297     /** A variant of the above call which takes the original source bitmap and
298      *  source offset. If the resulting crop rect is not entirely contained by
299      *  the source bitmap's bounds, it creates a new bitmap in "result" and
300      *  pads the edges with transparent black. In that case, the srcOffset is
301      *  modified to be the same as the bounds, since no further adjustment is
302      *  needed by the caller. This version should only be used by filters
303      *  which are not capable of processing a smaller source bitmap into a
304      *  larger destination.
305      *
306      *  DEPRECATED - Remove once cropping is handled by a separate filter.
307      */
308     sk_sp<SkSpecialImage> applyCropRectAndPad(const Context&, SkSpecialImage* src,
309                                               SkIPoint* srcOffset, SkIRect* bounds) const;
310 
311     /**
312      *  Creates a modified Context for use when recursing up the image filter DAG.
313      *  The clip bounds are adjusted to accommodate any margins that this
314      *  filter requires by calling this node's
315      *  onFilterNodeBounds(..., kReverse_MapDirection).
316      */
317     // TODO (michaelludwig) - I don't think this is necessary to keep as protected. Other than the
318     // real use case in recursing through the DAG for filterInput(), it feels wrong for blur and
319     // other filters to need to call it.
320     Context mapContext(const Context& ctx) const;
321 
322 #if SK_SUPPORT_GPU
323     static sk_sp<SkSpecialImage> DrawWithFP(GrRecordingContext* context,
324                                             std::unique_ptr<GrFragmentProcessor> fp,
325                                             const SkIRect& bounds,
326                                             SkColorType colorType,
327                                             const SkColorSpace* colorSpace,
328                                             const SkSurfaceProps&,
329                                             GrProtected isProtected = GrProtected::kNo);
330 
331     /**
332      *  Returns a version of the passed-in image (possibly the original), that is in a colorspace
333      *  with the same gamut as the one from the OutputProperties. This allows filters that do many
334      *  texture samples to guarantee that any color space conversion has happened before running.
335      */
336     static sk_sp<SkSpecialImage> ImageToColorSpace(SkSpecialImage* src,
337                                                    SkColorType colorType,
338                                                    SkColorSpace* colorSpace,
339                                                    const SkSurfaceProps&);
340 #endif
341 
342     // If 'srcBounds' will sample outside the border of 'originalSrcBounds' (i.e., the sample
343     // will wrap around to the other side) we must preserve the far side of the src along that
344     // axis (e.g., if we will sample beyond the left edge of the src, the right side must be
345     // preserved for the repeat sampling to work).
346     // DEPRECATED - Remove once cropping is handled by a separate filter, that can also handle all
347     // tile modes (including repeat) properly
348     static SkIRect DetermineRepeatedSrcBound(const SkIRect& srcBounds,
349                                              const SkIVector& filterOffset,
350                                              const SkISize& filterSize,
351                                              const SkIRect& originalSrcBounds);
352 
353 private:
354     friend class SkImageFilter;
355     // For PurgeCache()
356     friend class SkGraphics;
357 
358     static void PurgeCache();
359 
360     // Configuration points for the filter implementation, marked private since they should not
361     // need to be invoked by the subclasses. These refer to the node's specific behavior and are
362     // not responsible for aggregating the behavior of the entire filter DAG.
363 
364     /**
365      *  Return true (and returns a ref'd colorfilter) if this node in the DAG is just a colorfilter
366      *  w/o CropRect constraints.
367      */
onIsColorFilterNode(SkColorFilter **)368     virtual bool onIsColorFilterNode(SkColorFilter** /*filterPtr*/) const { return false; }
369 
370     /**
371      *  Return true if this filter can map from its parameter space to a layer space described by an
372      *  arbitrary transformation matrix. If this returns false, the filter only needs to worry about
373      *  mapping from parameter to layer using a scale+translate matrix.
374      */
onCanHandleComplexCTM()375     virtual bool onCanHandleComplexCTM() const { return false; }
376 
377     /**
378      *  Return true if this filter would transform transparent black pixels to a color other than
379      *  transparent black. When false, optimizations can be taken to discard regions known to be
380      *  transparent black and thus process fewer pixels.
381      */
affectsTransparentBlack()382     virtual bool affectsTransparentBlack() const { return false; }
383 
384     /**
385      *  This is the virtual which should be overridden by the derived class to perform image
386      *  filtering. Subclasses are responsible for recursing to their input filters, although the
387      *  getFilteredInputX() functions are provided to handle all necessary details of this. If the
388      *  filter has a fixed number of inputs, the getFilterInput0() and getFilteredInput1() functions
389      *  ensure the returned filtered Images have the most specific input usage.
390      *
391      *  If the image cannot be created (either because of an error or if the result would be empty
392      *  because it was clipped out), this should return a filtered Image with a null SkSpecialImage.
393      *  In these situations, callers that do not affect transparent black can end early, since the
394      *  "transparent" implicit image would be unchanged. Callers that affect transparent black need
395      *  to safely handle these null and empty images and return an image filling the context's clip
396      *  bounds as if its input filtered image were transparent black.
397      */
398     virtual skif::FilterResult<For::kOutput> onFilterImage(const skif::Context& context) const;
399 
400     /**
401      *  Calculates the necessary input layer size in order for the final output of the filter to
402      *  cover the desired output bounds. The provided 'desiredOutput' represents the requested
403      *  input bounds for this node's parent filter node, i.e. this function answers "what does this
404      *  node require for input in order to satisfy (as its own output), the input needs of its
405      *  parent?".
406      *
407      *  If 'recurse' is true, this function is responsible for recursing to its child image filters
408      *  and accounting for what they require to meet this filter's input requirements. It is up to
409      *  the filter to determine how to aggregate these inputs, but a helper function is provided for
410      *  the common case where the final required layer size is the union of the child filters'
411      *  required inputs, evaluated on what this filter requires for itself. 'recurse' is kNo
412      *  when mapping Contexts while actually filtering images, since the child recursion is
413      *  happening at a higher level.
414      *
415      *  Unlike the public getInputBounds(), all internal bounds calculations are done in the shared
416      *  layer space defined by 'mapping'.
417      *
418      *  The default implementation assumes that current filter requires an input equal to
419      *  'desiredOutputBounds', and passes this down to its child filters, and returns the union of
420      *  their required inputs.
421      */
422     virtual skif::LayerSpace<SkIRect> onGetInputLayerBounds(
423             const skif::Mapping& mapping, const skif::LayerSpace<SkIRect>& desiredOutput,
424             const skif::LayerSpace<SkIRect>& contentBounds,
425             VisitChildren recurse = VisitChildren::kYes) const;
426 
427     /**
428      *  Calculates the output bounds that this filter node would touch when processing an input
429      *  sized to 'contentBounds'. This function is responsible for recursing to its child image
430      *  filters and accounting for what they output. It is up to the filter to determine how to
431      *  aggregate the outputs of its children, but a helper function is provided for the common
432      *  case where the filter output is the union of its child outputs.
433      *
434      *  Unlike the public getOutputBounds(), all internal bounds calculations are done in the
435      *  shared layer space defined by 'mapping'.
436      *
437      *  The default implementation assumes that the output of this filter is equal to the union of
438      *  the outputs of its child filters evaluated with 'contentBounds'.
439      */
440     // TODO (michaelludwig) - When layerMatrix = I, this function could be used to implement
441     // onComputeFastBounds() instead of making filters implement the essentially the same calcs x2
442     virtual skif::LayerSpace<SkIRect> onGetOutputLayerBounds(
443             const skif::Mapping& mapping, const skif::LayerSpace<SkIRect>& contentBounds) const;
444 
445     // The actual implementation of the protected getFilterInputX() functions, but don't expose the
446     // flexible templating to subclasses so it can't be abused.
447     template<skif::Usage kU>
448     skif::FilterResult<kU> filterInput(int index, const skif::Context& ctx) const;
449 
450     SkAutoSTArray<2, sk_sp<SkImageFilter>> fInputs;
451 
452     bool fUsesSrcInput;
453     CropRect fCropRect;
454     uint32_t fUniqueID; // Globally unique
455 
456     using INHERITED = SkImageFilter;
457 };
458 
as_IFB(SkImageFilter * filter)459 static inline SkImageFilter_Base* as_IFB(SkImageFilter* filter) {
460     return static_cast<SkImageFilter_Base*>(filter);
461 }
462 
as_IFB(const sk_sp<SkImageFilter> & filter)463 static inline SkImageFilter_Base* as_IFB(const sk_sp<SkImageFilter>& filter) {
464     return static_cast<SkImageFilter_Base*>(filter.get());
465 }
466 
as_IFB(const SkImageFilter * filter)467 static inline const SkImageFilter_Base* as_IFB(const SkImageFilter* filter) {
468     return static_cast<const SkImageFilter_Base*>(filter);
469 }
470 
471 /**
472  *  Helper to unflatten the common data, and return nullptr if we fail.
473  */
474 #define SK_IMAGEFILTER_UNFLATTEN_COMMON(localVar, expectedCount)    \
475     Common localVar;                                                \
476     do {                                                            \
477         if (!localVar.unflatten(buffer, expectedCount)) {           \
478             return nullptr;                                         \
479         }                                                           \
480     } while (0)
481 
482 
483 /**
484  * All image filter implementations defined for the include/effects/SkImageFilters.h factories
485  * are entirely encapsulated within their own CPP files. SkFlattenable deserialization needs a hook
486  * into these types, so their registration functions are exposed here.
487  */
488 void SkRegisterAlphaThresholdImageFilterFlattenable();
489 void SkRegisterArithmeticImageFilterFlattenable();
490 void SkRegisterBlendImageFilterFlattenable();
491 void SkRegisterBlurImageFilterFlattenable();
492 void SkRegisterColorFilterImageFilterFlattenable();
493 void SkRegisterComposeImageFilterFlattenable();
494 void SkRegisterDisplacementMapImageFilterFlattenable();
495 void SkRegisterDropShadowImageFilterFlattenable();
496 void SkRegisterImageImageFilterFlattenable();
497 void SkRegisterLightingImageFilterFlattenables();
498 void SkRegisterMagnifierImageFilterFlattenable();
499 void SkRegisterMatrixConvolutionImageFilterFlattenable();
500 void SkRegisterMergeImageFilterFlattenable();
501 void SkRegisterMorphologyImageFilterFlattenables();
502 void SkRegisterOffsetImageFilterFlattenable();
503 void SkRegisterPictureImageFilterFlattenable();
504 void SkRegisterShaderImageFilterFlattenable();
505 void SkRegisterTileImageFilterFlattenable();
506 
507 #endif // SkImageFilter_Base_DEFINED
508