Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
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 */
226 return MatrixCapability::kScaleTranslate;
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
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:416
static const uint8_t buffer[]
Point offset