Flutter Engine
The Flutter Engine
SkImageFilter_Base.h
Go to the documentation of this file.
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
16
18
19#include <optional>
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.
24public:
25 /**
26 * Request a new filtered image to be created from the src image. The returned skif::Image
27 * provides both the pixel data and the origin point that it should be drawn at, relative to
28 * the layer space defined by the provided context.
29 *
30 * If the result image cannot be created, or the result would be transparent black, returns
31 * a skif::Image that has a null special image, in which its origin should be ignored.
32 *
33 * TODO: Right now the imagefilters sometimes return empty result bitmaps/
34 * specialimages. That doesn't seem quite right.
35 */
36 skif::FilterResult filterImage(const skif::Context& context) const;
37
38 /**
39 * Create a filtered version of the 'src' image using this filter. This is basically a wrapper
40 * around filterImage that prepares the skif::Context to filter the 'src' image directly,
41 * for implementing the SkImages::MakeWithFilter API calls.
42 */
45 const SkIRect& subset,
46 const SkIRect& clipBounds,
47 SkIRect* outSubset,
48 SkIPoint* offset) const;
49
50 /**
51 * Calculate the smallest-possible required layer bounds that would provide sufficient
52 * information to correctly compute the image filter for every pixel in the desired output
53 * bounds. The 'desiredOutput' is intended to represent either the root render target bounds,
54 * or the device-space bounds of the current clip. If the bounds of the content that will be
55 * drawn into the layer is known, 'knownContentBounds' should be provided, since it can be
56 * used to restrict the size of the layer if the image filter DAG does not affect transparent
57 * black.
58 *
59 * The returned rect is in the layer space defined by 'mapping', so it directly represents
60 * the size and location of the SkDevice created to rasterize the content prior to invoking the
61 * image filter (assuming its CTM and basis matrix are configured to match 'mapping').
62 *
63 * While this operation transforms an device-space output bounds to a layer-space input bounds,
64 * it is not necessarily the inverse of getOutputBounds(). For instance, a blur needs to have
65 * an outset margin when reading pixels at the edge (to satisfy its kernel), thus it expands
66 * its required input rect to include every pixel that contributes to the desired output rect.
67
68 * @param mapping The coordinate space mapping that defines both the transformation
69 * between local and layer, and layer to root device space, that will be
70 * used when the filter is later invoked.
71 * @param desiredOutput The desired output boundary that needs to be covered by the filter's
72 * output (assuming that the filter is then invoked with a suitable input)
73 * @param knownContentBounds
74 * Optional, the known layer-space bounds of the non-transparent content
75 * that would be rasterized in the source input image. Assumes unbounded
76 * content when not provided.
77 *
78 * @return The layer-space bounding box to use for an SkDevice when drawing the source image.
79 */
81 const skif::Mapping& mapping,
82 const skif::DeviceSpace<SkIRect>& desiredOutput,
83 std::optional<skif::ParameterSpace<SkRect>> knownContentBounds) const;
84
85 /**
86 * Calculate the device-space bounds of the output of this filter DAG, if it were to process
87 * an image layer covering the 'contentBounds'. The 'mapping' defines how the content will be
88 * transformed to layer space when it is drawn, and how the output filter image is then
89 * transformed to the final device space (i.e. it specifies the mapping between the root device
90 * space and the parameter space of the initially provided content).
91 *
92 * While this operation transforms a parameter-space input bounds to an device-space output
93 * bounds, it is not necessarily the inverse of getInputBounds(). For instance, a blur needs to
94 * have an outset margin when reading pixels at the edge (to satisfy its kernel), so it will
95 * generate a result larger than its input (so that the blur is visible) and, thus, expands its
96 * output to include every pixel that it will touch.
97 *
98 * If the returned optional does not have a value, the caller should interpret this to mean
99 * that the output of the image filter will fill the entirety of whatever clipped device it's
100 * drawn into.
101 *
102 * @param mapping The coordinate space mapping that defines both the transformation
103 * between local and layer, and layer to root device space, that will be
104 * used when the filter is later invoked.
105 * @param contentBounds The local-space bounds of the non-transparent content that would be
106 * drawn into the source image prior to filtering with this DAG, i.e.
107 * the same as 'knownContentBounds' in getInputBounds().
108 *
109 * @return The root device-space bounding box of the filtered image, were it applied to
110 * content contained by 'contentBounds' and then drawn with 'mapping' to the root
111 * device (w/o any additional clipping).
112 */
113 std::optional<skif::DeviceSpace<SkIRect>> getOutputBounds(
114 const skif::Mapping& mapping,
115 const skif::ParameterSpace<SkRect>& contentBounds) const;
116
117 // Returns true if this image filter graph transforms a source transparent black pixel to a
118 // color other than transparent black.
119 bool affectsTransparentBlack() const;
120
121 // Returns true if this image filter graph references the Context's source image.
122 bool usesSource() const { return fUsesSrcInput; }
123
124 /**
125 * This call returns the maximum "kind" of CTM for a filter and all of its (non-null) inputs.
126 */
129
130 uint32_t uniqueID() const { return fUniqueID; }
131
133 return kSkImageFilter_Type;
134 }
135
137 return kSkImageFilter_Type;
138 }
139
140 // TODO: CreateProcs for now-removed image filter subclasses need to hook into
141 // SK_IMAGEFILTER_UNFLATTEN_COMMON, so this temporarily exposes it for the case where there's a
142 // single input filter, and can be removed when the legacy CreateProcs are deleted.
143 static std::pair<sk_sp<SkImageFilter>, std::optional<SkRect>>
145
146protected:
147 class Common {
148 public:
149 /**
150 * Attempt to unflatten the expected number of input filters.
151 * If any number of input filters is valid, pass -1.
152 * If this fails (i.e. corrupt buffer or contents) then return false and common will
153 * be left uninitialized.
154 * If this returns true, then inputCount() is the number of found input filters, each
155 * of which may be NULL or a valid imagefilter.
156 */
157 bool unflatten(SkReadBuffer&, int expectedInputs);
158
159 std::optional<SkRect> cropRect() const { return fCropRect; }
160
161 int inputCount() const { return fInputs.size(); }
162 sk_sp<SkImageFilter>* inputs() { return fInputs.begin(); }
163
164 sk_sp<SkImageFilter> getInput(int index) { return fInputs[index]; }
165
166 private:
167 // Old SKPs (version less than kRemoveDeprecatedCropRect may have this set).
168 std::optional<SkRect> fCropRect;
169
170 // most filters accept at most 2 input-filters
172 };
173
174 SkImageFilter_Base(sk_sp<SkImageFilter> const* inputs, int inputCount,
175 std::optional<bool> usesSrc = {});
176
177 ~SkImageFilter_Base() override;
178
179 void flatten(SkWriteBuffer&) const override;
180
181 // Helper function to calculate the required input/output of a specific child filter,
182 // automatically handling if the child filter is null.
184 int index,
185 const skif::Mapping& mapping,
186 const skif::LayerSpace<SkIRect>& desiredOutput,
187 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const;
188 std::optional<skif::LayerSpace<SkIRect>> getChildOutputLayerBounds(
189 int index,
190 const skif::Mapping& mapping,
191 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const;
192
193 // Helper function for recursing through the filter DAG. It automatically evaluates the input
194 // image filter at 'index' using the given context. If the input image filter is null, it
195 // returns the context's dynamic source image.
196 //
197 // When an image filter requires a different output than what is requested in it's own Context
198 // passed to onFilterImage(), it should explicitly pass in an updated Context via
199 // `withNewDesiredOutput`.
200 skif::FilterResult getChildOutput(int index, const skif::Context& ctx) const;
201
202private:
203 friend class SkImageFilter;
204 // For PurgeCache()
205 friend class SkGraphics;
206
207 static void PurgeCache();
208
209 // Configuration points for the filter implementation, marked private since they should not
210 // need to be invoked by the subclasses. These refer to the node's specific behavior and are
211 // not responsible for aggregating the behavior of the entire filter DAG.
212
213 /**
214 * Return true (and returns a ref'd colorfilter) if this node in the DAG is just a colorfilter
215 * w/o cropping constraints.
216 */
217 virtual bool onIsColorFilterNode(SkColorFilter** /*filterPtr*/) const { return false; }
218
219 /**
220 * Return the most complex matrix type this filter can support (mapping from its parameter
221 * space to a layer space). If this returns anything less than kComplex, the filter only needs
222 * to worry about mapping from parameter to layer using a matrix that is constrained in that
223 * way (eg, scale+translate).
224 */
227 }
228
229 /**
230 * Return true if this filter would transform transparent black pixels to a color other than
231 * transparent black. When false, optimizations can be taken to discard regions known to be
232 * transparent black and thus process fewer pixels.
233 */
234 virtual bool onAffectsTransparentBlack() const { return false; }
235
236 /**
237 * Return true if `affectsTransparentBlack()` should only be based on
238 * `onAffectsTransparentBlack()` and ignore the transparency behavior of child input filters.
239 */
240 virtual bool ignoreInputsAffectsTransparentBlack() const { return false; }
241
242 /**
243 * This is the virtual which should be overridden by the derived class to perform image
244 * filtering. Subclasses are responsible for recursing to their input filters, although the
245 * filterInput() function is provided to handle all necessary details of this.
246 *
247 * If the image cannot be created (either because of an error or if the result would be empty
248 * because it was clipped out), this should return a filtered Image with a null SkSpecialImage.
249 * In these situations, callers that do not affect transparent black can end early, since the
250 * "transparent" implicit image would be unchanged. Callers that affect transparent black need
251 * to safely handle these null and empty images and return an image filling the context's clip
252 * bounds as if its input filtered image were transparent black.
253 */
254 virtual skif::FilterResult onFilterImage(const skif::Context& context) const = 0;
255
256 /**
257 * Calculates the necessary input layer size in order for the final output of the filter to
258 * cover the desired output bounds. The provided 'desiredOutput' represents the requested
259 * input bounds for this node's parent filter node, i.e. this function answers "what does this
260 * node require for input in order to satisfy (as its own output), the input needs of its
261 * parent?".
262 *
263 * 'contentBounds' represents the bounds of the non-transparent content that will form the
264 * source image when the filter graph is invoked. If it's not instantiated, implementations
265 * should treat the content as extending infinitely. However, since the output is known and
266 * bounded, implementations should still be able to determine a finite input bounds under these
267 * circumstances.
268 *
269 * Unlike the public getInputBounds(), all internal bounds calculations are done in the shared
270 * layer space defined by 'mapping'.
271 */
273 const skif::Mapping& mapping,
274 const skif::LayerSpace<SkIRect>& desiredOutput,
275 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const = 0;
276
277 /**
278 * Calculates the output bounds that this filter node would touch when processing an input
279 * sized to 'contentBounds'. This function is responsible for recursing to its child image
280 * filters and accounting for what they output. It is up to the filter to determine how to
281 * aggregate the outputs of its children.
282 *
283 * 'contentBounds' represents the bounds of the non-transparent content that will form the
284 * source image when the filter graph is invoked. If it's not instantiated, implementations
285 * should treat the content as extending infinitely. However, since the output is known and
286 * bounded, implementations should still be able to determine a finite input bounds under these
287 * circumstances.
288 *
289 * If the non-transparent output extends infinitely, subclasses should return an uninstantiated
290 * optional. Implementations must also be able to handle when their children return such
291 * unbounded "outputs" and react accordingly.
292 *
293 * Unlike the public getOutputBounds(), all internal bounds calculations are done in the
294 * shared layer space defined by 'mapping'.
295 */
296 // TODO (michaelludwig) - When layerMatrix = I, this function could be used to implement
297 // onComputeFastBounds() instead of making filters implement the essentially the same calcs x2
298 virtual std::optional<skif::LayerSpace<SkIRect>> onGetOutputLayerBounds(
299 const skif::Mapping& mapping,
300 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const = 0;
301
303
304 bool fUsesSrcInput;
305 uint32_t fUniqueID; // Globally unique
306
307 using INHERITED = SkImageFilter;
308};
309
310static inline SkImageFilter_Base* as_IFB(SkImageFilter* filter) {
311 return static_cast<SkImageFilter_Base*>(filter);
312}
313
314static inline SkImageFilter_Base* as_IFB(const sk_sp<SkImageFilter>& filter) {
315 return static_cast<SkImageFilter_Base*>(filter.get());
316}
317
318static inline const SkImageFilter_Base* as_IFB(const SkImageFilter* filter) {
319 return static_cast<const SkImageFilter_Base*>(filter);
320}
321
322/**
323 * Helper to unflatten the common data, and return nullptr if we fail.
324 */
325#define SK_IMAGEFILTER_UNFLATTEN_COMMON(localVar, expectedCount) \
326 Common localVar; \
327 do { \
328 if (!localVar.unflatten(buffer, expectedCount)) { \
329 return nullptr; \
330 } \
331 } while (0)
332
333
334/**
335 * All image filter implementations defined for the include/effects/SkImageFilters.h factories
336 * are entirely encapsulated within their own CPP files. SkFlattenable deserialization needs a hook
337 * into these types, so their registration functions are exposed here.
338 */
355
356// TODO(michaelludwig): These filters no longer have dedicated implementations, so their
357// SkFlattenable create procs only need to remain to support old SkPictures.
359
360#endif // SkImageFilter_Base_DEFINED
const char * backend
void SkRegisterMorphologyImageFilterFlattenables()
void SkRegisterCropImageFilterFlattenable()
void SkRegisterRuntimeImageFilterFlattenable()
void SkRegisterMagnifierImageFilterFlattenable()
void SkRegisterImageImageFilterFlattenable()
void SkRegisterLegacyDropShadowImageFilterFlattenable()
static SkImageFilter_Base * as_IFB(SkImageFilter *filter)
void SkRegisterColorFilterImageFilterFlattenable()
void SkRegisterComposeImageFilterFlattenable()
void SkRegisterBlurImageFilterFlattenable()
void SkRegisterMatrixConvolutionImageFilterFlattenable()
void SkRegisterLightingImageFilterFlattenables()
void SkRegisterDisplacementMapImageFilterFlattenable()
void SkRegisterMatrixTransformImageFilterFlattenable()
void SkRegisterPictureImageFilterFlattenable()
void SkRegisterMergeImageFilterFlattenable()
void SkRegisterBlendImageFilterFlattenable()
void SkRegisterShaderImageFilterFlattenable()
sk_sp< SkImageFilter > * inputs()
bool unflatten(SkReadBuffer &, int expectedInputs)
sk_sp< SkImageFilter > getInput(int index)
std::optional< SkRect > cropRect() const
virtual bool onIsColorFilterNode(SkColorFilter **) const
~SkImageFilter_Base() override
virtual skif::LayerSpace< SkIRect > onGetInputLayerBounds(const skif::Mapping &mapping, const skif::LayerSpace< SkIRect > &desiredOutput, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const =0
std::optional< skif::DeviceSpace< SkIRect > > getOutputBounds(const skif::Mapping &mapping, const skif::ParameterSpace< SkRect > &contentBounds) const
friend class SkImageFilter
virtual bool onAffectsTransparentBlack() const
static SkFlattenable::Type GetFlattenableType()
skif::LayerSpace< SkIRect > getChildInputLayerBounds(int index, const skif::Mapping &mapping, const skif::LayerSpace< SkIRect > &desiredOutput, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const
void flatten(SkWriteBuffer &) const override
virtual bool ignoreInputsAffectsTransparentBlack() const
virtual std::optional< skif::LayerSpace< SkIRect > > onGetOutputLayerBounds(const skif::Mapping &mapping, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const =0
virtual skif::FilterResult onFilterImage(const skif::Context &context) const =0
skif::LayerSpace< SkIRect > getInputBounds(const skif::Mapping &mapping, const skif::DeviceSpace< SkIRect > &desiredOutput, std::optional< skif::ParameterSpace< SkRect > > knownContentBounds) const
skif::FilterResult getChildOutput(int index, const skif::Context &ctx) const
sk_sp< SkImage > makeImageWithFilter(sk_sp< skif::Backend > backend, sk_sp< SkImage > src, const SkIRect &subset, const SkIRect &clipBounds, SkIRect *outSubset, SkIPoint *offset) const
SkFlattenable::Type getFlattenableType() const override
skif::FilterResult filterImage(const skif::Context &context) const
uint32_t uniqueID() const
bool affectsTransparentBlack() const
std::optional< skif::LayerSpace< SkIRect > > getChildOutputLayerBounds(int index, const skif::Mapping &mapping, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const
MatrixCapability getCTMCapability() const
static std::pair< sk_sp< SkImageFilter >, std::optional< SkRect > > Unflatten(SkReadBuffer &buffer)
virtual MatrixCapability onGetCTMCapability() const
friend class SkImageFilter_Base
T * get() const
Definition: SkRefCnt.h:303
int size() const
Definition: SkTArray.h:421
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
SeparatedVector2 offset
Definition: SkRect.h:32