| /* |
| * Copyright 2019 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #ifndef SkImageFilter_Base_DEFINED |
| #define SkImageFilter_Base_DEFINED |
| |
| #include "include/core/SkColorSpace.h" |
| #include "include/core/SkImageFilter.h" |
| #include "include/core/SkImageInfo.h" |
| #include "include/private/SkTArray.h" |
| |
| #include "src/core/SkImageFilterTypes.h" |
| |
| class GrFragmentProcessor; |
| class GrRecordingContext; |
| |
| // True base class that all SkImageFilter implementations need to extend from. This provides the |
| // actual API surface that Skia will use to compute the filtered images. |
| class SkImageFilter_Base : public SkImageFilter { |
| public: |
| SK_USE_FLUENT_IMAGE_FILTER_TYPES_IN_CLASS |
| |
| // DEPRECATED - Use skif::Context directly. |
| using Context = skif::Context; |
| |
| /** |
| * Request a new filtered image to be created from the src image. The returned skif::Image |
| * provides both the pixel data and the origin point that it should be drawn at, relative to |
| * the layer space defined by the provided context. |
| * |
| * If the result image cannot be created, or the result would be transparent black, returns |
| * a skif::Image that has a null special image, in which its origin should be ignored. |
| * |
| * TODO: Right now the imagefilters sometimes return empty result bitmaps/ |
| * specialimages. That doesn't seem quite right. |
| */ |
| skif::FilterResult<For::kOutput> filterImage(const skif::Context& context) const; |
| |
| /** |
| * Calculate the smallest-possible required layer bounds that would provide sufficient |
| * information to correctly compute the image filter for every pixel in the desired output |
| * bounds. The 'desiredOutput' is intended to represent either the root render target bounds, |
| * or the device-space bounds of the current clip. If the bounds of the content that will be |
| * drawn into the layer is known, 'knownContentBounds' should be provided, since it can be |
| * used to restrict the size of the layer if the image filter DAG does not affect transparent |
| * black. |
| * |
| * The returned rect is in the layer space defined by 'mapping', so it directly represents |
| * the size and location of the SkDevice created to rasterize the content prior to invoking the |
| * image filter (assuming its CTM and basis matrix are configured to match 'mapping'). |
| * |
| * While this operation transforms an device-space output bounds to a layer-space input bounds, |
| * it is not necessarily the inverse of getOutputBounds(). For instance, a blur needs to have |
| * an outset margin when reading pixels at the edge (to satisfy its kernel), thus it expands |
| * its required input rect to include every pixel that contributes to the desired output rect. |
| |
| * @param mapping The coordinate space mapping that defines both the transformation |
| * between local and layer, and layer to root device space, that will be |
| * used when the filter is later invoked. |
| * @param desiredOutput The desired output boundary that needs to be covered by the filter's |
| * output (assuming that the filter is then invoked with a suitable input) |
| * @param knownContentBounds |
| * Optional, the known layer-space bounds of the non-transparent content |
| * that would be rasterized in the source input image. |
| * |
| * @return The layer-space bounding box to use for an SkDevice when drawing the source image. |
| */ |
| skif::LayerSpace<SkIRect> getInputBounds( |
| const skif::Mapping& mapping, const skif::DeviceSpace<SkRect>& desiredOutput, |
| const skif::ParameterSpace<SkRect>* knownContentBounds) const; |
| |
| /** |
| * Calculate the device-space bounds of the output of this filter DAG, if it were to process |
| * an image layer covering the 'contentBounds'. The 'mapping' defines how the content will be |
| * transformed to layer space when it is drawn, and how the output filter image is then |
| * transformed to the final device space (i.e. it specifies the mapping between the root device |
| * space and the parameter space of the initially provided content). |
| * |
| * While this operation transforms a parameter-space input bounds to an device-space output |
| * bounds, it is not necessarily the inverse of getInputBounds(). For instance, a blur needs to |
| * have an outset margin when reading pixels at the edge (to satisfy its kernel), so it will |
| * generate a result larger than its input (so that the blur is visible) and, thus, expands its |
| * output to include every pixel that it will touch. |
| * |
| * @param mapping The coordinate space mapping that defines both the transformation |
| * between local and layer, and layer to root device space, that will be |
| * used when the filter is later invoked. |
| * @param contentBounds The local-space bounds of the non-transparent content that would be |
| * drawn into the source image prior to filtering with this DAG, i.e. |
| * the same as 'knownContentBounds' in getInputBounds(). |
| * |
| * @return The root device-space bounding box of the filtered image, were it applied to |
| * content contained by 'contentBounds' and then drawn with 'mapping' to the root |
| * device (w/o any additional clipping). |
| */ |
| skif::DeviceSpace<SkIRect> getOutputBounds( |
| const skif::Mapping& mapping, const skif::ParameterSpace<SkRect>& contentBounds) const; |
| |
| /** |
| * Returns whether any edges of the crop rect have been set. The crop |
| * rect is set at construction time, and determines which pixels from the |
| * input image will be processed, and which pixels in the output image will be allowed. |
| * The size of the crop rect should be |
| * used as the size of the destination image. The origin of this rect |
| * should be used to offset access to the input images, and should also |
| * be added to the "offset" parameter in onFilterImage. |
| * |
| * DEPRECATED - Remove once cropping is handled by a separate filter |
| */ |
| bool cropRectIsSet() const { return fCropRect.flags() != 0x0; } |
| |
| // DEPRECATED - Remove once cropping is handled by a separate filter |
| CropRect getCropRect() const { return fCropRect; } |
| |
| // Expose isolated node bounds behavior for SampleImageFilterDAG and debugging |
| SkIRect filterNodeBounds(const SkIRect& srcRect, const SkMatrix& ctm, |
| MapDirection dir, const SkIRect* inputRect) const { |
| return this->onFilterNodeBounds(srcRect, ctm, dir, inputRect); |
| } |
| |
| /** |
| * ImageFilters can natively handle scaling and translate components in the CTM. Only some of |
| * them can handle affine (or more complex) matrices. This call returns true iff the filter |
| * and all of its (non-null) inputs can handle these more complex matrices. |
| */ |
| bool canHandleComplexCTM() const; |
| |
| /** |
| * Return an image filter representing this filter applied with the given ctm. This will modify |
| * the DAG as needed if this filter does not support complex CTMs and 'ctm' is not simple. The |
| * ctm matrix will be decomposed such that ctm = A*B; B will be incorporated directly into the |
| * DAG and A must be the ctm set on the context passed to filterImage(). 'remainder' will be set |
| * to A. |
| * |
| * If this filter supports complex ctms, or 'ctm' is not complex, then A = ctm and B = I. When |
| * the filter does not support complex ctms, and the ctm is complex, then A represents the |
| * extracted simple portion of the ctm, and the complex portion is baked into a new DAG using a |
| * matrix filter. |
| * |
| * This will never return null. |
| * |
| * DEPRECATED - Should draw the results of filterImage() directly with the remainder matrix. |
| */ |
| sk_sp<SkImageFilter> applyCTM(const SkMatrix& ctm, SkMatrix* remainder) const; |
| |
| uint32_t uniqueID() const { return fUniqueID; } |
| |
| protected: |
| class Common { |
| public: |
| /** |
| * Attempt to unflatten the cropRect and the expected number of input filters. |
| * If any number of input filters is valid, pass -1. |
| * If this fails (i.e. corrupt buffer or contents) then return false and common will |
| * be left uninitialized. |
| * If this returns true, then inputCount() is the number of found input filters, each |
| * of which may be NULL or a valid imagefilter. |
| */ |
| bool unflatten(SkReadBuffer&, int expectedInputs); |
| |
| const CropRect& cropRect() const { return fCropRect; } |
| int inputCount() const { return fInputs.count(); } |
| sk_sp<SkImageFilter>* inputs() { return fInputs.begin(); } |
| |
| sk_sp<SkImageFilter> getInput(int index) { return fInputs[index]; } |
| |
| private: |
| CropRect fCropRect; |
| // most filters accept at most 2 input-filters |
| SkSTArray<2, sk_sp<SkImageFilter>, true> fInputs; |
| }; |
| |
| // Whether or not to recurse to child input filters for certain operations that walk the DAG. |
| enum class VisitChildren : bool { |
| kNo = false, |
| kYes = true |
| }; |
| |
| SkImageFilter_Base(sk_sp<SkImageFilter> const* inputs, int inputCount, |
| const CropRect* cropRect); |
| |
| ~SkImageFilter_Base() override; |
| |
| void flatten(SkWriteBuffer&) const override; |
| |
| // DEPRECATED - Use the private context-only variant |
| virtual sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const = 0; |
| |
| // DEPRECATED - Override onGetOutputLayerBounds and onGetInputLayerBounds instead. The |
| // node-specific and aggregation functions are no longer separated in the current API. A helper |
| // function is provided to do the default recursion for the common filter case. |
| virtual SkIRect onFilterBounds(const SkIRect&, const SkMatrix& ctm, |
| MapDirection, const SkIRect* inputRect) const; |
| virtual SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm, |
| MapDirection, const SkIRect* inputRect) const; |
| |
| // DEPRECRATED - Call the Context-only getInputFilteredImage() |
| sk_sp<SkSpecialImage> filterInput(int index, const Context& ctx, SkIPoint* offset) const { |
| return this->getInputFilteredImage(index, ctx).imageAndOffset(offset); |
| } |
| |
| // Helper function to visit each of this filter's child filters and call their |
| // onGetInputLayerBounds with the provided 'desiredOutput' and 'contentBounds'. Automatically |
| // handles null input filters. Returns the union of all of the children's input bounds. |
| skif::LayerSpace<SkIRect> visitInputLayerBounds( |
| const skif::Mapping& mapping, const skif::LayerSpace<SkIRect>& desiredOutput, |
| const skif::LayerSpace<SkIRect>& contentBounds) const; |
| // Helper function to visit each of this filter's child filters and call their |
| // onGetOutputLayerBounds with the provided 'contentBounds'. Automatically handles null input |
| // filters. |
| skif::LayerSpace<SkIRect> visitOutputLayerBounds( |
| const skif::Mapping& mapping, const skif::LayerSpace<SkIRect>& contentBounds) const; |
| |
| // Helper function to help with recursing through the filter DAG. It invokes filter processing |
| // set to null, it returns the dynamic source image on the Context instead. |
| // |
| // Implementations must handle cases when the input filter was unable to compute an image and |
| // the returned skif::Image has a null SkSpecialImage. If the filter affect transparent black |
| // should explicitly handle nullptr results and press on. In the error case this behavior will |
| // produce a better result than nothing and is necessary for the clipped out case. |
| skif::FilterResult<For::kInput> getInputFilteredImage(int index, |
| const skif::Context& context) const { |
| return this->filterInput<For::kInput>(index, context); |
| } |
| // Convenience that calls filterInput with index = 0 and the most specific usage. |
| skif::FilterResult<For::kInput0> getInputFilteredImage0(const skif::Context& context) const { |
| return this->filterInput<For::kInput0>(0, context); |
| } |
| // Convenience that calls filterInput with index = 1 and the most specific usage. |
| skif::FilterResult<For::kInput1> getInputFilteredImage1(const skif::Context& context) const { |
| return this->filterInput<For::kInput1>(1, context); |
| } |
| |
| // DEPRECATED - Remove once cropping is handled by a separate filter |
| const CropRect* getCropRectIfSet() const { |
| return this->cropRectIsSet() ? &fCropRect : nullptr; |
| } |
| |
| /** Given a "srcBounds" rect, computes destination bounds for this filter. |
| * "dstBounds" are computed by transforming the crop rect by the context's |
| * CTM, applying it to the initial bounds, and intersecting the result with |
| * the context's clip bounds. "srcBounds" (if non-null) are computed by |
| * intersecting the initial bounds with "dstBounds", to ensure that we never |
| * sample outside of the crop rect (this restriction may be relaxed in the |
| * future). |
| * |
| * DEPRECATED - Remove once cropping is handled by a separate filter, although it may be |
| * necessary to provide a similar convenience function to compute the output bounds given the |
| * images returned by filterInput(). |
| */ |
| bool applyCropRect(const Context&, const SkIRect& srcBounds, SkIRect* dstBounds) const; |
| |
| /** A variant of the above call which takes the original source bitmap and |
| * source offset. If the resulting crop rect is not entirely contained by |
| * the source bitmap's bounds, it creates a new bitmap in "result" and |
| * pads the edges with transparent black. In that case, the srcOffset is |
| * modified to be the same as the bounds, since no further adjustment is |
| * needed by the caller. This version should only be used by filters |
| * which are not capable of processing a smaller source bitmap into a |
| * larger destination. |
| * |
| * DEPRECATED - Remove once cropping is handled by a separate filter. |
| */ |
| sk_sp<SkSpecialImage> applyCropRectAndPad(const Context&, SkSpecialImage* src, |
| SkIPoint* srcOffset, SkIRect* bounds) const; |
| |
| /** |
| * Creates a modified Context for use when recursing up the image filter DAG. |
| * The clip bounds are adjusted to accommodate any margins that this |
| * filter requires by calling this node's |
| * onFilterNodeBounds(..., kReverse_MapDirection). |
| */ |
| // TODO (michaelludwig) - I don't think this is necessary to keep as protected. Other than the |
| // real use case in recursing through the DAG for filterInput(), it feels wrong for blur and |
| // other filters to need to call it. |
| Context mapContext(const Context& ctx) const; |
| |
| #if SK_SUPPORT_GPU |
| static sk_sp<SkSpecialImage> DrawWithFP(GrRecordingContext* context, |
| std::unique_ptr<GrFragmentProcessor> fp, |
| const SkIRect& bounds, |
| SkColorType colorType, |
| const SkColorSpace* colorSpace, |
| GrProtected isProtected = GrProtected::kNo); |
| |
| /** |
| * Returns a version of the passed-in image (possibly the original), that is in a colorspace |
| * with the same gamut as the one from the OutputProperties. This allows filters that do many |
| * texture samples to guarantee that any color space conversion has happened before running. |
| */ |
| static sk_sp<SkSpecialImage> ImageToColorSpace(SkSpecialImage* src, |
| SkColorType colorType, |
| SkColorSpace* colorSpace); |
| #endif |
| |
| // If 'srcBounds' will sample outside the border of 'originalSrcBounds' (i.e., the sample |
| // will wrap around to the other side) we must preserve the far side of the src along that |
| // axis (e.g., if we will sample beyond the left edge of the src, the right side must be |
| // preserved for the repeat sampling to work). |
| // DEPRECATED - Remove once cropping is handled by a separate filter, that can also handle all |
| // tile modes (including repeat) properly |
| static SkIRect DetermineRepeatedSrcBound(const SkIRect& srcBounds, |
| const SkIVector& filterOffset, |
| const SkISize& filterSize, |
| const SkIRect& originalSrcBounds); |
| |
| private: |
| friend class SkImageFilter; |
| // For PurgeCache() |
| friend class SkGraphics; |
| |
| static void PurgeCache(); |
| |
| void init(sk_sp<SkImageFilter> const* inputs, int inputCount, const CropRect* cropRect); |
| |
| // Configuration points for the filter implementation, marked private since they should not |
| // need to be invoked by the subclasses. These refer to the node's specific behavior and are |
| // not responsible for aggregating the behavior of the entire filter DAG. |
| |
| /** |
| * Return true (and returns a ref'd colorfilter) if this node in the DAG is just a colorfilter |
| * w/o CropRect constraints. |
| */ |
| virtual bool onIsColorFilterNode(SkColorFilter** /*filterPtr*/) const { return false; } |
| |
| /** |
| * Return true if this filter can map from its parameter space to a layer space described by an |
| * arbitrary transformation matrix. If this returns false, the filter only needs to worry about |
| * mapping from parameter to layer using a scale+translate matrix. |
| */ |
| virtual bool onCanHandleComplexCTM() const { return false; } |
| |
| /** |
| * Return true if this filter would transform transparent black pixels to a color other than |
| * transparent black. When false, optimizations can be taken to discard regions known to be |
| * transparent black and thus process fewer pixels. |
| */ |
| virtual bool affectsTransparentBlack() const { return false; } |
| |
| /** |
| * This is the virtual which should be overridden by the derived class to perform image |
| * filtering. Subclasses are responsible for recursing to their input filters, although the |
| * getFilteredInputX() functions are provided to handle all necessary details of this. If the |
| * filter has a fixed number of inputs, the getFilterInput0() and getFilteredInput1() functions |
| * ensure the returned filtered Images have the most specific input usage. |
| * |
| * If the image cannot be created (either because of an error or if the result would be empty |
| * because it was clipped out), this should return a filtered Image with a null SkSpecialImage. |
| * In these situations, callers that do not affect transparent black can end early, since the |
| * "transparent" implicit image would be unchanged. Callers that affect transparent black need |
| * to safely handle these null and empty images and return an image filling the context's clip |
| * bounds as if its input filtered image were transparent black. |
| */ |
| virtual skif::FilterResult<For::kOutput> onFilterImage(const skif::Context& context) const; |
| |
| /** |
| * Calculates the necessary input layer size in order for the final output of the filter to |
| * cover the desired output bounds. The provided 'desiredOutput' represents the requested |
| * input bounds for this node's parent filter node, i.e. this function answers "what does this |
| * node require for input in order to satisfy (as its own output), the input needs of its |
| * parent?". |
| * |
| * If 'recurse' is true, this function is responsible for recursing to its child image filters |
| * and accounting for what they require to meet this filter's input requirements. It is up to |
| * the filter to determine how to aggregate these inputs, but a helper function is provided for |
| * the common case where the final required layer size is the union of the child filters' |
| * required inputs, evaluated on what this filter requires for itself. 'recurse' is kNo |
| * when mapping Contexts while actually filtering images, since the child recursion is |
| * happening at a higher level. |
| * |
| * Unlike the public getInputBounds(), all internal bounds calculations are done in the shared |
| * layer space defined by 'mapping'. |
| * |
| * The default implementation assumes that current filter requires an input equal to |
| * 'desiredOutputBounds', and passes this down to its child filters, and returns the union of |
| * their required inputs. |
| */ |
| virtual skif::LayerSpace<SkIRect> onGetInputLayerBounds( |
| const skif::Mapping& mapping, const skif::LayerSpace<SkIRect>& desiredOutput, |
| const skif::LayerSpace<SkIRect>& contentBounds, |
| VisitChildren recurse = VisitChildren::kYes) const; |
| |
| /** |
| * Calculates the output bounds that this filter node would touch when processing an input |
| * sized to 'contentBounds'. This function is responsible for recursing to its child image |
| * filters and accounting for what they output. It is up to the filter to determine how to |
| * aggregate the outputs of its children, but a helper function is provided for the common |
| * case where the filter output is the union of its child outputs. |
| * |
| * Unlike the public getOutputBounds(), all internal bounds calculations are done in the |
| * shared layer space defined by 'mapping'. |
| * |
| * The default implementation assumes that the output of this filter is equal to the union of |
| * the outputs of its child filters evaluated with 'contentBounds'. |
| */ |
| // TODO (michaelludwig) - When layerMatrix = I, this function could be used to implement |
| // onComputeFastBounds() instead of making filters implement the essentially the same calcs x2 |
| virtual skif::LayerSpace<SkIRect> onGetOutputLayerBounds( |
| const skif::Mapping& mapping, const skif::LayerSpace<SkIRect>& contentBounds) const; |
| |
| // The actual implementation of the protected getFilterInputX() functions, but don't expose the |
| // flexible templating to subclasses so it can't be abused. |
| template<skif::Usage kU> |
| skif::FilterResult<kU> filterInput(int index, const skif::Context& ctx) const; |
| |
| SkAutoSTArray<2, sk_sp<SkImageFilter>> fInputs; |
| |
| bool fUsesSrcInput; |
| CropRect fCropRect; |
| uint32_t fUniqueID; // Globally unique |
| |
| typedef SkImageFilter INHERITED; |
| }; |
| |
| static inline SkImageFilter_Base* as_IFB(SkImageFilter* filter) { |
| return static_cast<SkImageFilter_Base*>(filter); |
| } |
| |
| static inline SkImageFilter_Base* as_IFB(const sk_sp<SkImageFilter>& filter) { |
| return static_cast<SkImageFilter_Base*>(filter.get()); |
| } |
| |
| static inline const SkImageFilter_Base* as_IFB(const SkImageFilter* filter) { |
| return static_cast<const SkImageFilter_Base*>(filter); |
| } |
| |
| /** |
| * Helper to unflatten the common data, and return nullptr if we fail. |
| */ |
| #define SK_IMAGEFILTER_UNFLATTEN_COMMON(localVar, expectedCount) \ |
| Common localVar; \ |
| do { \ |
| if (!localVar.unflatten(buffer, expectedCount)) { \ |
| return nullptr; \ |
| } \ |
| } while (0) |
| |
| #endif // SkImageFilter_Base_DEFINED |