Flutter Engine
The Flutter Engine
Classes | Public Member Functions | List of all members
skif::FilterResult::Builder Class Reference

#include <SkImageFilterTypes.h>

Public Member Functions

 Builder (const Context &context)
 
 ~Builder ()
 
Builderadd (const FilterResult &input, std::optional< LayerSpace< SkIRect > > sampleBounds={}, SkEnumBitMask< ShaderFlags > inputFlags=ShaderFlags::kNone, const SkSamplingOptions &inputSampling=kDefaultSampling)
 
FilterResult merge ()
 
FilterResult blur (const LayerSpace< SkSize > &sigma)
 
template<typename ShaderFn >
FilterResult eval (ShaderFn shaderFn, std::optional< LayerSpace< SkIRect > > explicitOutput={}, bool evaluateInParameterSpace=false)
 

Detailed Description

Definition at line 995 of file SkImageFilterTypes.h.

Constructor & Destructor Documentation

◆ Builder()

skif::FilterResult::Builder::Builder ( const Context context)

Definition at line 1955 of file SkImageFilterTypes.cpp.

1955: fContext(context) {}

◆ ~Builder()

skif::FilterResult::Builder::~Builder ( )
default

Member Function Documentation

◆ add()

Builder & skif::FilterResult::Builder::add ( const FilterResult input,
std::optional< LayerSpace< SkIRect > >  sampleBounds = {},
SkEnumBitMask< ShaderFlags inputFlags = ShaderFlags::kNone,
const SkSamplingOptions inputSampling = kDefaultSampling 
)
inline

Definition at line 1012 of file SkImageFilterTypes.h.

1013 {},
1015 const SkSamplingOptions& inputSampling = kDefaultSampling) {
1016 fInputs.push_back({input, sampleBounds, inputFlags, inputSampling});
1017 return *this;
1018 }
static constexpr SkSamplingOptions kDefaultSampling

◆ blur()

FilterResult skif::FilterResult::Builder::blur ( const LayerSpace< SkSize > &  sigma)

Definition at line 2053 of file SkImageFilterTypes.cpp.

2053 {
2054 SkASSERT(fInputs.size() == 1);
2055
2056 // TODO: The blur functor is only supported for GPU contexts; SkBlurImageFilter should have
2057 // detected this.
2058 const SkBlurEngine* blurEngine = fContext.backend()->getBlurEngine();
2059 SkASSERT(blurEngine);
2060
2061 const SkBlurEngine::Algorithm* algorithm = blurEngine->findAlgorithm(
2062 SkSize(sigma), fContext.backend()->colorType());
2063 if (!algorithm) {
2064 return {};
2065 }
2066
2067 // TODO: De-duplicate this logic between SkBlurImageFilter, here, and skgpu::BlurUtils.
2068 LayerSpace<SkISize> radii =
2069 LayerSpace<SkSize>({3.f*sigma.width(), 3.f*sigma.height()}).ceil();
2070 auto maxOutput = fInputs[0].fImage.layerBounds();
2071 maxOutput.outset(radii);
2072
2073 auto outputBounds = this->outputBounds(maxOutput);
2074 if (outputBounds.isEmpty()) {
2075 return {};
2076 }
2077
2078 // These are the source pixels that will be read from the input image, which can be calculated
2079 // internally because the blur's access pattern is well defined (vs. needing it to be provided
2080 // in Builder::add()).
2081 auto sampleBounds = outputBounds;
2082 sampleBounds.outset(radii);
2083
2084 if (fContext.backend()->useLegacyFilterResultBlur()) {
2085 SkASSERT(sigma.width() <= algorithm->maxSigma() && sigma.height() <= algorithm->maxSigma());
2086
2087 FilterResult resolved = fInputs[0].fImage.resolve(fContext, sampleBounds);
2088 if (!resolved) {
2089 return {};
2090 }
2091 auto srcRelativeOutput = outputBounds;
2092 srcRelativeOutput.offset(-resolved.layerBounds().topLeft());
2093 resolved = {algorithm->blur(SkSize(sigma),
2094 resolved.fImage,
2095 SkIRect::MakeSize(resolved.fImage->dimensions()),
2097 SkIRect(srcRelativeOutput)),
2098 outputBounds.topLeft()};
2099 return resolved;
2100 }
2101
2102 float sx = sigma.width() > algorithm->maxSigma() ? algorithm->maxSigma()/sigma.width() : 1.f;
2103 float sy = sigma.height() > algorithm->maxSigma() ? algorithm->maxSigma()/sigma.height() : 1.f;
2104
2105 // For identity scale factors, this rescale() is a no-op when possible, but otherwise it will
2106 // also handle resolving any color filters or transform similar to a resolve() except that it
2107 // can defer the tile mode.
2108 FilterResult lowResImage = fInputs[0].fImage.rescale(
2109 fContext.withNewDesiredOutput(sampleBounds),
2110 LayerSpace<SkSize>({sx, sy}),
2111 algorithm->supportsOnlyDecalTiling());
2112 if (!lowResImage) {
2113 return {};
2114 }
2115 SkASSERT(lowResImage.tileMode() == SkTileMode::kDecal ||
2116 !algorithm->supportsOnlyDecalTiling());
2117
2118 // Map 'sigma' into the low-res image's pixel space to determine the low-res blur params to pass
2119 // into the blur engine.
2120 PixelSpace<SkMatrix> layerToLowRes;
2121 SkAssertResult(lowResImage.fTransform.invert(&layerToLowRes));
2122 PixelSpace<SkSize> lowResSigma = layerToLowRes.mapSize(sigma);
2123 // The layerToLowRes mapped size should be <= maxSigma, but clamp it just in case floating point
2124 // error made it slightly higher.
2125 lowResSigma = PixelSpace<SkSize>{{std::min(algorithm->maxSigma(), lowResSigma.width()),
2126 std::min(algorithm->maxSigma(), lowResSigma.height())}};
2127 PixelSpace<SkIRect> lowResMaxOutput{SkISize{lowResImage.fImage->width(),
2128 lowResImage.fImage->height()}};
2129
2130 PixelSpace<SkIRect> srcRelativeOutput;
2131 if (lowResImage.tileMode() == SkTileMode::kRepeat ||
2132 lowResImage.tileMode() == SkTileMode::kMirror) {
2133 // The periodic tiling was deferred when down-sampling; we can further defer it to after the
2134 // blur. The low-res output is 1-to-1 with the low res image.
2135 srcRelativeOutput = lowResMaxOutput;
2136 } else {
2137 // For decal and clamp tiling, the blurred image stops being interesting outside the radii
2138 // outset, so redo the max output analysis with the 'outputBounds' mapped into pixel space.
2139 srcRelativeOutput = layerToLowRes.mapRect(outputBounds);
2140
2141 // NOTE: Since 'lowResMaxOutput' is based on the actual image and deferred tiling, this can
2142 // be smaller than the pessimistic filling for a clamp-tiled blur.
2143 lowResMaxOutput.outset(PixelSpace<SkSize>({3.f * lowResSigma.width(),
2144 3.f * lowResSigma.height()}).ceil());
2145 srcRelativeOutput = lowResMaxOutput.relevantSubset(srcRelativeOutput,
2146 lowResImage.tileMode());
2147 // Clamp won't return empty from relevantSubset() and a non-intersecting decal should have
2148 // been caught earlier.
2149 SkASSERT(!srcRelativeOutput.isEmpty());
2150
2151 // Include 1px of blur output so that it can be sampled during the upscale, which is needed
2152 // to correctly seam large blurs across crop/raster tiles (crbug.com/1500021).
2153 srcRelativeOutput.outset(PixelSpace<SkISize>({1, 1}));
2154 }
2155
2156 sk_sp<SkSpecialImage> lowResBlur = lowResImage.refImage();
2157 SkIRect blurOutputBounds = SkIRect(srcRelativeOutput);
2158 SkTileMode tileMode = lowResImage.tileMode();
2159 if (lowResImage.canClampToTransparentBoundary(BoundsAnalysis::kSimple)) {
2160 // Have to manage this manually since the BlurEngine isn't aware of the known pixel padding.
2161 lowResBlur = lowResBlur->makePixelOutset();
2162 blurOutputBounds.offset(1, 1);
2164 }
2165
2166 lowResBlur = algorithm->blur(SkSize(lowResSigma),
2167 lowResBlur,
2168 SkIRect::MakeSize(lowResBlur->dimensions()),
2169 tileMode,
2170 blurOutputBounds);
2171
2172 FilterResult result{std::move(lowResBlur), srcRelativeOutput.topLeft()};
2173 if (lowResImage.tileMode() == SkTileMode::kClamp ||
2174 lowResImage.tileMode() == SkTileMode::kDecal) {
2175 // Undo the outset padding that was added to srcRelativeOutput before invoking the blur
2176 result = result.insetByPixel();
2177 }
2178
2179 result.fTransform.postConcat(lowResImage.fTransform);
2180 if (lowResImage.tileMode() == SkTileMode::kDecal) {
2181 // Recalculate the output bounds based on the blur output; with rounding the final image may
2182 // be slightly larger than the original, which would unnecessarily add cropping to the layer
2183 // bounds. But so long as the `outputBounds` had been constrained by the input's own layer,
2184 // that crop is unnecessary. The result is still restricted to the desired output bounds,
2185 // which will induce clipping as needed for a rounded-out image.
2186 outputBounds = this->outputBounds(
2187 result.fTransform.mapRect(LayerSpace<SkIRect>(result.fImage->dimensions())));
2188 }
2189 result.fLayerBounds = outputBounds;
2190 result.fTileMode = lowResImage.tileMode();
2191 return result;
2192}
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkTileMode
Definition: SkTileMode.h:13
virtual float maxSigma() const =0
virtual bool supportsOnlyDecalTiling() const =0
virtual sk_sp< SkSpecialImage > blur(SkSize sigma, sk_sp< SkSpecialImage > src, const SkIRect &srcRect, SkTileMode tileMode, const SkIRect &dstRect) const =0
virtual const Algorithm * findAlgorithm(SkSize sigma, SkColorType colorType) const =0
sk_sp< SkSpecialImage > makePixelOutset() const
SkISize dimensions() const
int size() const
Definition: SkTArray.h:421
virtual const SkBlurEngine * getBlurEngine() const =0
virtual bool useLegacyFilterResultBlur() const
SkColorType colorType() const
const Backend * backend() const
Context withNewDesiredOutput(const LayerSpace< SkIRect > &desiredOutput) const
SkTileMode tileMode() const
LayerSpace< SkIPoint > topLeft() const
void outset(const LayerSpace< SkISize > &delta)
void offset(const LayerSpace< IVector > &v)
GAsyncResult * result
static float min(float r, float g, float b)
Definition: hsl.cpp:48
SIN Vec< N, float > ceil(const Vec< N, float > &x)
Definition: SkVx.h:702
Definition: SkRect.h:32
static constexpr SkIRect MakeSize(const SkISize &size)
Definition: SkRect.h:66
void offset(int32_t dx, int32_t dy)
Definition: SkRect.h:367
Definition: SkSize.h:16
constexpr int32_t width() const
Definition: SkSize.h:36
Definition: SkSize.h:52

◆ eval()

template<typename ShaderFn >
FilterResult skif::FilterResult::Builder::eval ( ShaderFn  shaderFn,
std::optional< LayerSpace< SkIRect > >  explicitOutput = {},
bool  evaluateInParameterSpace = false 
)
inline

Definition at line 1049 of file SkImageFilterTypes.h.

1050 {},
1051 bool evaluateInParameterSpace=false) {
1052 auto outputBounds = this->outputBounds(explicitOutput);
1053 if (outputBounds.isEmpty()) {
1054 return {};
1055 }
1056
1057 auto inputShaders = this->createInputShaders(outputBounds, evaluateInParameterSpace);
1058 return this->drawShader(shaderFn(inputShaders), outputBounds, evaluateInParameterSpace);
1059 }

◆ merge()

FilterResult skif::FilterResult::Builder::merge ( )

Definition at line 2024 of file SkImageFilterTypes.cpp.

2024 {
2025 // merge() could return an empty image on 0 added inputs, but this should have been caught
2026 // earlier and routed to SkImageFilters::Empty() instead.
2027 SkASSERT(!fInputs.empty());
2028 if (fInputs.size() == 1) {
2029 SkASSERT(!fInputs[0].fSampleBounds.has_value() &&
2030 fInputs[0].fSampling == kDefaultSampling &&
2031 fInputs[0].fFlags == ShaderFlags::kNone);
2032 return fInputs[0].fImage;
2033 }
2034
2035 const auto mergedBounds = LayerSpace<SkIRect>::Union(
2036 (int) fInputs.size(),
2037 [this](int i) { return fInputs[i].fImage.layerBounds(); });
2038 const auto outputBounds = this->outputBounds(mergedBounds);
2039
2040 AutoSurface surface{fContext, outputBounds, PixelBoundary::kTransparent,
2041 /*renderInParameterSpace=*/false};
2042 if (surface) {
2043 for (const SampledFilterResult& input : fInputs) {
2044 SkASSERT(!input.fSampleBounds.has_value() &&
2045 input.fSampling == kDefaultSampling &&
2046 input.fFlags == ShaderFlags::kNone);
2047 input.fImage.draw(fContext, surface.device(), /*preserveDeviceState=*/true);
2048 }
2049 }
2050 return surface.snap();
2051}
static void Union(SkRegion *rgn, const SkIRect &rect)
Definition: RegionTest.cpp:27
bool empty() const
Definition: SkTArray.h:199
VkSurfaceKHR surface
Definition: main.cc:49

The documentation for this class was generated from the following files: