Fix subset behavior in makeWithFilter.
Most image filters were fixed with just the changes to SkImage::makeWithFilter and the changes to SkSpecialImage's subset handling (particularly the raster backend that could read from a bitmap view, or ganesh impls that relied SkSpecialImage::draw). The gpu implementation for alpha threshold, blurs, matrix convolutions, and displacement maps have been updated to account for the special image's offset when it reads from the backing texture. Change-Id: I8778aa373e60e9268961305057b2bf6da2bdb3af Reviewed-on: https://skia-review.googlesource.com/c/skia/+/221121 Reviewed-by: Robert Phillips <robertphillips@google.com> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
parent
9b06f2168d
commit
b4580351c7
@ -421,6 +421,10 @@ private:
|
||||
|
||||
typedef GM INHERITED;
|
||||
};
|
||||
// The different strategies should all look the same, with the exception of filters that affect
|
||||
// transparent black (i.e. the lighting filter). In the save layer case, the filter affects the
|
||||
// transparent pixels outside of the drawn subset, whereas the makeWithFilter is restricted. This
|
||||
// works as intended.
|
||||
DEF_GM( return new ImageMakeWithFilterGM(Strategy::kMakeWithFilter); )
|
||||
DEF_GM( return new ImageMakeWithFilterGM(Strategy::kSaveLayer); )
|
||||
// Test with crop rects on the image filters; should look identical to above if working correctly
|
||||
|
@ -884,6 +884,7 @@ sk_sp<GrTextureProxy> SkBlurMaskFilterImpl::filterMaskGPU(GrRecordingContext* co
|
||||
sk_sp<GrRenderTargetContext> renderTargetContext(
|
||||
SkGpuBlurUtils::GaussianBlur(context,
|
||||
srcProxy,
|
||||
SkIPoint::Make(0, 0),
|
||||
nullptr,
|
||||
clipRect,
|
||||
SkIRect::EmptyIRect(),
|
||||
|
@ -152,8 +152,16 @@ static sk_sp<GrRenderTargetContext> convolve_gaussian_2d(GrRecordingContext* con
|
||||
return renderTargetContext;
|
||||
}
|
||||
|
||||
// NOTE: Both convolve_gaussian or decimate accept a proxyOffset. This is separate from the
|
||||
// srcBounds and srcOffset, which are relative to the content rect of the image, whereas proxyOffset
|
||||
// maps from the content rect to the proxy's coordinate space. Due to how the destination bounds are
|
||||
// calculated, it is more convenient to have the proxy offset kept separate from the logical bounds
|
||||
// (which do impact destination decisions). Both functions incorporate the proxy offset into the
|
||||
// geometry they submit or before calling convolve_gaussian_1d.
|
||||
|
||||
static sk_sp<GrRenderTargetContext> convolve_gaussian(GrRecordingContext* context,
|
||||
sk_sp<GrTextureProxy> proxy,
|
||||
const SkIPoint& proxyOffset,
|
||||
const SkIRect& srcRect,
|
||||
const SkIPoint& srcOffset,
|
||||
Direction direction,
|
||||
@ -189,20 +197,22 @@ static sk_sp<GrRenderTargetContext> convolve_gaussian(GrRecordingContext* contex
|
||||
|
||||
int bounds[2] = { 0, 0 };
|
||||
SkIRect dstRect = SkIRect::MakeWH(srcRect.width(), srcRect.height());
|
||||
SkIPoint netOffset = srcOffset - proxyOffset;
|
||||
if (GrTextureDomain::kIgnore_Mode == mode) {
|
||||
*contentRect = dstRect;
|
||||
convolve_gaussian_1d(dstRenderTargetContext.get(), clip, dstRect, srcOffset,
|
||||
convolve_gaussian_1d(dstRenderTargetContext.get(), clip, dstRect, netOffset,
|
||||
std::move(proxy), direction, radius, sigma,
|
||||
GrTextureDomain::kIgnore_Mode, bounds);
|
||||
return dstRenderTargetContext;
|
||||
}
|
||||
|
||||
// These destination rects need to be adjusted by srcOffset, but should *not* be adjusted by
|
||||
// the proxyOffset, which is why keeping them separate is convenient.
|
||||
SkIRect midRect = *contentRect, leftRect, rightRect;
|
||||
midRect.offset(srcOffset);
|
||||
SkIRect topRect, bottomRect;
|
||||
if (Direction::kX == direction) {
|
||||
bounds[0] = contentRect->left();
|
||||
bounds[1] = contentRect->right();
|
||||
bounds[0] = contentRect->left() + proxyOffset.x();
|
||||
bounds[1] = contentRect->right() + proxyOffset.x();
|
||||
topRect = SkIRect::MakeLTRB(0, 0, dstRect.right(), midRect.top());
|
||||
bottomRect = SkIRect::MakeLTRB(0, midRect.bottom(), dstRect.right(), dstRect.bottom());
|
||||
midRect.inset(radius, 0);
|
||||
@ -217,8 +227,8 @@ static sk_sp<GrRenderTargetContext> convolve_gaussian(GrRecordingContext* contex
|
||||
contentRect->fRight = dstRect.fRight;
|
||||
contentRect->fBottom = midRect.fBottom;
|
||||
} else {
|
||||
bounds[0] = contentRect->top();
|
||||
bounds[1] = contentRect->bottom();
|
||||
bounds[0] = contentRect->top() + proxyOffset.y();
|
||||
bounds[1] = contentRect->bottom() + proxyOffset.y();
|
||||
topRect = SkIRect::MakeLTRB(0, 0, midRect.left(), dstRect.bottom());
|
||||
bottomRect = SkIRect::MakeLTRB(midRect.right(), 0, dstRect.right(), dstRect.bottom());
|
||||
midRect.inset(0, radius);
|
||||
@ -245,15 +255,15 @@ static sk_sp<GrRenderTargetContext> convolve_gaussian(GrRecordingContext* contex
|
||||
|
||||
if (midRect.isEmpty()) {
|
||||
// Blur radius covers srcBounds; use bounds over entire draw
|
||||
convolve_gaussian_1d(dstRenderTargetContext.get(), clip, dstRect, srcOffset,
|
||||
convolve_gaussian_1d(dstRenderTargetContext.get(), clip, dstRect, netOffset,
|
||||
std::move(proxy), direction, radius, sigma, mode, bounds);
|
||||
} else {
|
||||
// Draw right and left margins with bounds; middle without.
|
||||
convolve_gaussian_1d(dstRenderTargetContext.get(), clip, leftRect, srcOffset,
|
||||
convolve_gaussian_1d(dstRenderTargetContext.get(), clip, leftRect, netOffset,
|
||||
proxy, direction, radius, sigma, mode, bounds);
|
||||
convolve_gaussian_1d(dstRenderTargetContext.get(), clip, rightRect, srcOffset,
|
||||
convolve_gaussian_1d(dstRenderTargetContext.get(), clip, rightRect, netOffset,
|
||||
proxy, direction, radius, sigma, mode, bounds);
|
||||
convolve_gaussian_1d(dstRenderTargetContext.get(), clip, midRect, srcOffset,
|
||||
convolve_gaussian_1d(dstRenderTargetContext.get(), clip, midRect, netOffset,
|
||||
std::move(proxy), direction, radius, sigma,
|
||||
GrTextureDomain::kIgnore_Mode, bounds);
|
||||
}
|
||||
@ -263,6 +273,7 @@ static sk_sp<GrRenderTargetContext> convolve_gaussian(GrRecordingContext* contex
|
||||
|
||||
static sk_sp<GrTextureProxy> decimate(GrRecordingContext* context,
|
||||
sk_sp<GrTextureProxy> src,
|
||||
const SkIPoint& proxyOffset,
|
||||
SkIPoint* srcOffset,
|
||||
SkIRect* contentRect,
|
||||
int scaleFactorX, int scaleFactorY,
|
||||
@ -328,6 +339,7 @@ static sk_sp<GrTextureProxy> decimate(GrRecordingContext* context,
|
||||
if (domain.fBottom < domain.fTop) {
|
||||
domain.fTop = domain.fBottom = SkScalarAve(domain.fTop, domain.fBottom);
|
||||
}
|
||||
domain.offset(proxyOffset.x(), proxyOffset.y());
|
||||
auto fp = GrTextureDomainEffect::Make(std::move(src),
|
||||
SkMatrix::I(),
|
||||
domain,
|
||||
@ -344,10 +356,10 @@ static sk_sp<GrTextureProxy> decimate(GrRecordingContext* context,
|
||||
}
|
||||
paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
|
||||
|
||||
GrFixedClip clip(dstRect);
|
||||
dstRenderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo,
|
||||
dstRenderTargetContext->fillRectToRect(GrFixedClip::Disabled(), std::move(paint), GrAA::kNo,
|
||||
SkMatrix::I(), SkRect::Make(dstRect),
|
||||
SkRect::Make(srcRect));
|
||||
SkRect::Make(srcRect.makeOffset(proxyOffset.x(),
|
||||
proxyOffset.y())));
|
||||
|
||||
src = dstRenderTargetContext->asTextureProxyRef();
|
||||
if (!src) {
|
||||
@ -381,7 +393,9 @@ static sk_sp<GrTextureProxy> decimate(GrRecordingContext* context,
|
||||
return dstRenderTargetContext->asTextureProxyRef();
|
||||
}
|
||||
|
||||
// Expand the contents of 'srcRenderTargetContext' to fit in 'dstII'.
|
||||
// Expand the contents of 'srcRenderTargetContext' to fit in 'dstII'. At this point, we are
|
||||
// expanding an intermediate image, so there's no need to account for a proxy offset from the
|
||||
// original input.
|
||||
static sk_sp<GrRenderTargetContext> reexpand(GrRecordingContext* context,
|
||||
sk_sp<GrRenderTargetContext> srcRenderTargetContext,
|
||||
const SkIRect& localSrcBounds,
|
||||
@ -460,6 +474,7 @@ namespace SkGpuBlurUtils {
|
||||
|
||||
sk_sp<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
|
||||
sk_sp<GrTextureProxy> srcProxy,
|
||||
const SkIPoint& proxyOffset,
|
||||
sk_sp<SkColorSpace> colorSpace,
|
||||
const SkIRect& dstBounds,
|
||||
const SkIRect& srcBounds,
|
||||
@ -487,6 +502,8 @@ sk_sp<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
|
||||
SkASSERT(sigmaX || sigmaY);
|
||||
|
||||
SkIPoint srcOffset = SkIPoint::Make(-dstBounds.x(), -dstBounds.y());
|
||||
SkIRect localSrcBounds = srcBounds;
|
||||
SkIPoint localProxyOffset = proxyOffset;
|
||||
|
||||
// For really small blurs (certainly no wider than 5x5 on desktop gpus) it is faster to just
|
||||
// launch a single non separable kernel vs two launches
|
||||
@ -494,10 +511,11 @@ sk_sp<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
|
||||
(2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) {
|
||||
// We shouldn't be scaling because this is a small size blur
|
||||
SkASSERT((1 == scaleFactorX) && (1 == scaleFactorY));
|
||||
|
||||
return convolve_gaussian_2d(context, std::move(srcProxy), srcBounds, srcOffset,
|
||||
radiusX, radiusY, sigmaX, sigmaY,
|
||||
mode, finalDestII, fit);
|
||||
// Apply the proxy offset to src bounds and offset directly
|
||||
srcOffset -= proxyOffset;
|
||||
localSrcBounds.offset(proxyOffset);
|
||||
return convolve_gaussian_2d(context, std::move(srcProxy), localSrcBounds, srcOffset,
|
||||
radiusX, radiusY, sigmaX, sigmaY, mode, finalDestII, fit);
|
||||
}
|
||||
|
||||
// Only the last rendered renderTargetContext needs to match the supplied 'fit'
|
||||
@ -508,12 +526,11 @@ sk_sp<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
|
||||
xFit = SkBackingFit::kApprox; // the y-pass will be last
|
||||
}
|
||||
|
||||
SkIRect localSrcBounds = srcBounds;
|
||||
|
||||
if (scaleFactorX > 1 || scaleFactorY > 1) {
|
||||
srcProxy = decimate(context, std::move(srcProxy), &srcOffset, &localSrcBounds,
|
||||
scaleFactorX, scaleFactorY, sigmaX > 0.0f, sigmaY > 0.0f,
|
||||
radiusX, radiusY, mode, finalDestII);
|
||||
srcProxy = decimate(context, std::move(srcProxy), localProxyOffset, &srcOffset,
|
||||
&localSrcBounds, scaleFactorX, scaleFactorY, sigmaX > 0.0f,
|
||||
sigmaY > 0.0f, radiusX, radiusY, mode, finalDestII);
|
||||
localProxyOffset.set(0, 0);
|
||||
if (!srcProxy) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -524,9 +541,9 @@ sk_sp<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
|
||||
SkIRect srcRect = finalDestII.bounds();
|
||||
scale_irect_roundout(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
|
||||
if (sigmaX > 0.0f) {
|
||||
dstRenderTargetContext = convolve_gaussian(context, std::move(srcProxy), srcRect, srcOffset,
|
||||
Direction::kX, radiusX, sigmaX, &localSrcBounds,
|
||||
mode, finalDestII, xFit);
|
||||
dstRenderTargetContext = convolve_gaussian(
|
||||
context, std::move(srcProxy), localProxyOffset, srcRect, srcOffset, Direction::kX,
|
||||
radiusX, sigmaX, &localSrcBounds, mode, finalDestII, xFit);
|
||||
if (!dstRenderTargetContext) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -546,12 +563,13 @@ sk_sp<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
|
||||
|
||||
srcRect.offsetTo(0, 0);
|
||||
srcOffset.set(0, 0);
|
||||
localProxyOffset.set(0, 0);
|
||||
}
|
||||
|
||||
if (sigmaY > 0.0f) {
|
||||
dstRenderTargetContext = convolve_gaussian(context, std::move(srcProxy), srcRect, srcOffset,
|
||||
Direction::kY, radiusY, sigmaY, &localSrcBounds,
|
||||
mode, finalDestII, yFit);
|
||||
dstRenderTargetContext = convolve_gaussian(
|
||||
context, std::move(srcProxy), localProxyOffset, srcRect, srcOffset, Direction::kY,
|
||||
radiusY, sigmaY, &localSrcBounds, mode, finalDestII, yFit);
|
||||
if (!dstRenderTargetContext) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -563,10 +581,12 @@ sk_sp<GrRenderTargetContext> GaussianBlur(GrRecordingContext* context,
|
||||
|
||||
srcRect.offsetTo(0, 0);
|
||||
srcOffset.set(0, 0);
|
||||
localProxyOffset.set(0, 0);
|
||||
}
|
||||
|
||||
SkASSERT(dstRenderTargetContext);
|
||||
SkASSERT(srcProxy.get() == dstRenderTargetContext->asTextureProxy());
|
||||
SkASSERT(localProxyOffset.x() == 0 && localProxyOffset.y() == 0);
|
||||
|
||||
if (scaleFactorX > 1 || scaleFactorY > 1) {
|
||||
dstRenderTargetContext = reexpand(context, std::move(dstRenderTargetContext),
|
||||
|
@ -21,14 +21,21 @@ namespace SkGpuBlurUtils {
|
||||
/**
|
||||
* Applies a 2D Gaussian blur to a given texture. The blurred result is returned
|
||||
* as a renderTargetContext in case the caller wishes to future draw into the result.
|
||||
*
|
||||
* The 'proxyOffset' is kept separate form 'srcBounds' because they exist in different
|
||||
* coordinate spaces. 'srcBounds' exists in the content space of the special image, and
|
||||
* 'proxyOffset' maps from the content space to the proxy's space.
|
||||
*
|
||||
* Note: one of sigmaX and sigmaY should be non-zero!
|
||||
* @param context The GPU context
|
||||
* @param src The source to be blurred.
|
||||
* @param proxyOffset The offset from the top-left corner to valid texels in 'src', which
|
||||
* should come from the subset of the owning SkSpecialImage.
|
||||
* @param colorSpace Color space of the source (used for the renderTargetContext result,
|
||||
* too).
|
||||
* @param dstBounds The destination bounds, relative to the source texture.
|
||||
* @param srcBounds The source bounds, relative to the source texture. If non-null,
|
||||
* no pixels will be sampled outside of this rectangle.
|
||||
* @param srcBounds The source bounds, relative to the source texture's offset. No pixels
|
||||
* will be sampled outside of this rectangle.
|
||||
* @param sigmaX The blur's standard deviation in X.
|
||||
* @param sigmaY The blur's standard deviation in Y.
|
||||
* @param mode The mode to handle samples outside bounds.
|
||||
@ -38,6 +45,7 @@ namespace SkGpuBlurUtils {
|
||||
sk_sp<GrRenderTargetContext> GaussianBlur(
|
||||
GrRecordingContext* context,
|
||||
sk_sp<GrTextureProxy> src,
|
||||
const SkIPoint& proxyOffset,
|
||||
sk_sp<SkColorSpace> colorSpace,
|
||||
const SkIRect& dstBounds,
|
||||
const SkIRect& srcBounds,
|
||||
|
@ -53,12 +53,16 @@ public:
|
||||
virtual sk_sp<GrTextureProxy> onAsTextureProxyRef(GrRecordingContext* context) const = 0;
|
||||
#endif
|
||||
|
||||
// This subset is relative to the backing store's coordinate frame, it has already been mapped
|
||||
// from the content rect by the non-virtual makeSubset().
|
||||
virtual sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const = 0;
|
||||
|
||||
virtual sk_sp<SkSpecialSurface> onMakeSurface(const SkImageFilter::OutputProperties& outProps,
|
||||
const SkISize& size, SkAlphaType at,
|
||||
const SkSurfaceProps* = nullptr) const = 0;
|
||||
|
||||
// This subset (when not null) is relative to the backing store's coordinate frame, it has
|
||||
// already been mapped from the content rect by the non-virtual asImage().
|
||||
virtual sk_sp<SkImage> onAsImage(const SkIRect* subset) const = 0;
|
||||
|
||||
virtual sk_sp<SkSurface> onMakeTightSurface(const SkImageFilter::OutputProperties& outProps,
|
||||
@ -165,11 +169,17 @@ sk_sp<SkSurface> SkSpecialImage::makeTightSurface(const SkImageFilter::OutputPro
|
||||
}
|
||||
|
||||
sk_sp<SkSpecialImage> SkSpecialImage::makeSubset(const SkIRect& subset) const {
|
||||
return as_SIB(this)->onMakeSubset(subset);
|
||||
SkIRect absolute = subset.makeOffset(this->subset().x(), this->subset().y());
|
||||
return as_SIB(this)->onMakeSubset(absolute);
|
||||
}
|
||||
|
||||
sk_sp<SkImage> SkSpecialImage::asImage(const SkIRect* subset) const {
|
||||
return as_SIB(this)->onAsImage(subset);
|
||||
if (subset) {
|
||||
SkIRect absolute = subset->makeOffset(this->subset().x(), this->subset().y());
|
||||
return as_SIB(this)->onAsImage(&absolute);
|
||||
} else {
|
||||
return as_SIB(this)->onAsImage(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(SK_DEBUG) || SK_SUPPORT_GPU
|
||||
@ -236,8 +246,7 @@ public:
|
||||
}
|
||||
|
||||
bool onGetROPixels(SkBitmap* bm) const override {
|
||||
*bm = fBitmap;
|
||||
return true;
|
||||
return fBitmap.extractSubset(bm, this->subset());
|
||||
}
|
||||
|
||||
SkColorSpace* onGetColorSpace() const override {
|
||||
@ -265,15 +274,8 @@ public:
|
||||
}
|
||||
|
||||
sk_sp<SkSpecialImage> onMakeSubset(const SkIRect& subset) const override {
|
||||
SkBitmap subsetBM;
|
||||
|
||||
if (!fBitmap.extractSubset(&subsetBM, subset)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(subset.width(), subset.height()),
|
||||
subsetBM,
|
||||
&this->props());
|
||||
// No need to extract subset, onGetROPixels handles that when needed
|
||||
return SkSpecialImage::MakeFromRaster(subset, fBitmap, &this->props());
|
||||
}
|
||||
|
||||
sk_sp<SkImage> onAsImage(const SkIRect* subset) const override {
|
||||
|
@ -65,7 +65,7 @@ public:
|
||||
sk_sp<SkSpecialImage> makeTextureImage(GrRecordingContext*);
|
||||
|
||||
/**
|
||||
* Draw this SpecialImage into the canvas.
|
||||
* Draw this SpecialImage into the canvas, automatically taking into account the image's subset
|
||||
*/
|
||||
void draw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const;
|
||||
|
||||
@ -107,7 +107,8 @@ public:
|
||||
|
||||
/**
|
||||
* Extract a subset of this special image and return it as a special image.
|
||||
* It may or may not point to the same backing memory.
|
||||
* It may or may not point to the same backing memory. The input 'subset' is relative to the
|
||||
* special image's content rect.
|
||||
*/
|
||||
sk_sp<SkSpecialImage> makeSubset(const SkIRect& subset) const;
|
||||
|
||||
@ -117,7 +118,7 @@ public:
|
||||
* Note: when no 'subset' parameter is specified the the entire SkSpecialImage will be
|
||||
* returned - including whatever extra padding may have resulted from a loose fit!
|
||||
* When the 'subset' parameter is specified the returned image will be tight even if that
|
||||
* entails a copy!
|
||||
* entails a copy! The 'subset' is relative to this special image's content rect.
|
||||
*/
|
||||
sk_sp<SkImage> asImage(const SkIRect* subset = nullptr) const;
|
||||
|
||||
@ -133,17 +134,18 @@ public:
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
/**
|
||||
* Regardless of the underlying backing store, return the contents as a GrTextureProxy.
|
||||
* The active portion of the texture can be retrieved via 'subset'.
|
||||
* Regardless of how the underlying backing data is stored, returns the contents as a
|
||||
* GrTextureProxy. The returned proxy represents the entire backing image, so texture
|
||||
* coordinates must be mapped from the content rect (e.g. relative to 'subset()') to the proxy's
|
||||
* space (offset by subset().topLeft()).
|
||||
*/
|
||||
sk_sp<GrTextureProxy> asTextureProxyRef(GrRecordingContext*) const;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Regardless of the underlying backing store, return the contents as an SkBitmap
|
||||
*
|
||||
* The returned ImageInfo represents the backing memory. Use 'subset'
|
||||
* to get the active portion's dimensions.
|
||||
* Regardless of the underlying backing store, return the contents as an SkBitmap.
|
||||
* The returned bitmap represents the subset accessed by this image, thus (0,0) refers to the
|
||||
* top-left corner of 'subset'.
|
||||
*/
|
||||
bool getROPixels(SkBitmap*) const;
|
||||
|
||||
|
@ -180,7 +180,9 @@ sk_sp<SkSpecialImage> SkAlphaThresholdFilterImpl::onFilterImage(SkSpecialImage*
|
||||
}
|
||||
|
||||
const OutputProperties& outProps = ctx.outputProperties();
|
||||
auto textureFP = GrSimpleTextureEffect::Make(std::move(inputProxy), SkMatrix::I());
|
||||
auto textureFP = GrSimpleTextureEffect::Make(std::move(inputProxy),
|
||||
SkMatrix::MakeTrans(input->subset().x(),
|
||||
input->subset().y()));
|
||||
textureFP = GrColorSpaceXformEffect::Make(std::move(textureFP), input->getColorSpace(),
|
||||
input->alphaType(), outProps.colorSpace());
|
||||
if (!textureFP) {
|
||||
|
@ -599,7 +599,6 @@ sk_sp<SkSpecialImage> SkBlurImageFilterImpl::onFilterImage(SkSpecialImage* sourc
|
||||
// Ensure the input is in the destination's gamut. This saves us from having to do the
|
||||
// xform during the filter itself.
|
||||
input = ImageToColorSpace(input.get(), ctx.outputProperties());
|
||||
|
||||
result = this->gpuFilter(source, sigma, input, inputBounds, dstBounds, inputOffset,
|
||||
ctx.outputProperties(), &resultOffset);
|
||||
} else
|
||||
@ -637,6 +636,7 @@ sk_sp<SkSpecialImage> SkBlurImageFilterImpl::gpuFilter(
|
||||
sk_sp<GrRenderTargetContext> renderTargetContext(SkGpuBlurUtils::GaussianBlur(
|
||||
context,
|
||||
std::move(inputTexture),
|
||||
input->subset().topLeft(),
|
||||
outProps.colorSpace() ? sk_ref_sp(input->getColorSpace()) : nullptr,
|
||||
dstBounds,
|
||||
inputBounds,
|
||||
|
@ -156,11 +156,11 @@ public:
|
||||
static std::unique_ptr<GrFragmentProcessor> Make(
|
||||
SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
|
||||
SkDisplacementMapEffect::ChannelSelectorType yChannelSelector, SkVector scale,
|
||||
sk_sp<GrTextureProxy> displacement, const SkMatrix& offsetMatrix,
|
||||
sk_sp<GrTextureProxy> color, const SkISize& colorDimensions) {
|
||||
sk_sp<GrTextureProxy> displacement, const SkIRect& displSubset,
|
||||
const SkMatrix& offsetMatrix, sk_sp<GrTextureProxy> color, const SkIRect& colorSubset) {
|
||||
return std::unique_ptr<GrFragmentProcessor>(new GrDisplacementMapEffect(
|
||||
xChannelSelector, yChannelSelector, scale, std::move(displacement), offsetMatrix,
|
||||
std::move(color), colorDimensions));
|
||||
xChannelSelector, yChannelSelector, scale, std::move(displacement), displSubset,
|
||||
offsetMatrix, std::move(color), colorSubset));
|
||||
}
|
||||
|
||||
~GrDisplacementMapEffect() override;
|
||||
@ -189,9 +189,9 @@ private:
|
||||
|
||||
GrDisplacementMapEffect(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
|
||||
SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
|
||||
const SkVector& scale,
|
||||
sk_sp<GrTextureProxy> displacement, const SkMatrix& offsetMatrix,
|
||||
sk_sp<GrTextureProxy> color, const SkISize& colorDimensions);
|
||||
const SkVector& scale, sk_sp<GrTextureProxy> displacement,
|
||||
const SkIRect& displSubset, const SkMatrix& offsetMatrix,
|
||||
sk_sp<GrTextureProxy> color, const SkIRect& colorSubset);
|
||||
|
||||
const TextureSampler& onTextureSampler(int i) const override {
|
||||
return IthTextureSampler(i, fDisplacementSampler, fColorSampler);
|
||||
@ -288,9 +288,10 @@ sk_sp<SkSpecialImage> SkDisplacementMapEffect::onFilterImage(SkSpecialImage* sou
|
||||
fYChannelSelector,
|
||||
scale,
|
||||
std::move(displProxy),
|
||||
displ->subset(),
|
||||
offsetMatrix,
|
||||
std::move(colorProxy),
|
||||
SkISize::Make(color->width(), color->height()));
|
||||
color->subset());
|
||||
fp = GrColorSpaceXformEffect::Make(std::move(fp), color->getColorSpace(),
|
||||
color->alphaType(), colorSpace);
|
||||
|
||||
@ -418,16 +419,20 @@ GrDisplacementMapEffect::GrDisplacementMapEffect(
|
||||
SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
|
||||
const SkVector& scale,
|
||||
sk_sp<GrTextureProxy> displacement,
|
||||
const SkIRect& displSubset,
|
||||
const SkMatrix& offsetMatrix,
|
||||
sk_sp<GrTextureProxy> color,
|
||||
const SkISize& colorDimensions)
|
||||
const SkIRect& colorSubset)
|
||||
: INHERITED(kGrDisplacementMapEffect_ClassID,
|
||||
GrFragmentProcessor::kNone_OptimizationFlags)
|
||||
, fDisplacementTransform(offsetMatrix, displacement.get())
|
||||
, fDisplacementTransform(
|
||||
SkMatrix::Concat(SkMatrix::MakeTrans(displSubset.x(), displSubset.y()),
|
||||
offsetMatrix),
|
||||
displacement.get())
|
||||
, fDisplacementSampler(displacement)
|
||||
, fColorTransform(color.get())
|
||||
, fColorTransform(SkMatrix::MakeTrans(colorSubset.x(), colorSubset.y()), color.get())
|
||||
, fDomain(color.get(),
|
||||
GrTextureDomain::MakeTexelDomain(SkIRect::MakeSize(colorDimensions),
|
||||
GrTextureDomain::MakeTexelDomain(colorSubset,
|
||||
GrTextureDomain::kDecal_Mode),
|
||||
GrTextureDomain::kDecal_Mode, GrTextureDomain::kDecal_Mode)
|
||||
, fColorSampler(color)
|
||||
@ -491,9 +496,12 @@ std::unique_ptr<GrFragmentProcessor> GrDisplacementMapEffect::TestCreate(GrProce
|
||||
SkISize colorDimensions;
|
||||
colorDimensions.fWidth = d->fRandom->nextRangeU(0, colorProxy->width());
|
||||
colorDimensions.fHeight = d->fRandom->nextRangeU(0, colorProxy->height());
|
||||
SkIRect dispRect = SkIRect::MakeWH(dispProxy->width(), dispProxy->height());
|
||||
return GrDisplacementMapEffect::Make(xChannelSelector, yChannelSelector, scale,
|
||||
std::move(dispProxy), SkMatrix::I(),
|
||||
std::move(colorProxy), colorDimensions);
|
||||
std::move(dispProxy),
|
||||
dispRect,
|
||||
SkMatrix::I(),
|
||||
std::move(colorProxy), SkIRect::MakeSize(colorDimensions));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -353,6 +353,8 @@ sk_sp<SkSpecialImage> SkMatrixConvolutionImageFilter::onFilterImage(SkSpecialIma
|
||||
offset->fY = dstBounds.top();
|
||||
dstBounds.offset(-inputOffset);
|
||||
srcBounds.offset(-inputOffset);
|
||||
// Map srcBounds from input's logical image domain to that of the proxy
|
||||
srcBounds.offset(input->subset().x(), input->subset().y());
|
||||
|
||||
auto fp = GrMatrixConvolutionEffect::Make(std::move(inputProxy),
|
||||
srcBounds,
|
||||
@ -372,7 +374,6 @@ sk_sp<SkSpecialImage> SkMatrixConvolutionImageFilter::onFilterImage(SkSpecialIma
|
||||
#endif
|
||||
|
||||
SkBitmap inputBM;
|
||||
|
||||
if (!input->getROPixels(&inputBM)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -78,6 +78,7 @@ uniform half blurRadius;
|
||||
sk_sp<GrRenderTargetContext> rtc2(
|
||||
SkGpuBlurUtils::GaussianBlur(context,
|
||||
std::move(srcProxy),
|
||||
SkIPoint::Make(0, 0),
|
||||
nullptr,
|
||||
SkIRect::MakeWH(size.fWidth, size.fHeight),
|
||||
SkIRect::EmptyIRect(),
|
||||
|
@ -79,6 +79,7 @@ public:
|
||||
sk_sp<GrRenderTargetContext> rtc2(
|
||||
SkGpuBlurUtils::GaussianBlur(context,
|
||||
std::move(srcProxy),
|
||||
SkIPoint::Make(0, 0),
|
||||
nullptr,
|
||||
SkIRect::MakeWH(size.fWidth, size.fHeight),
|
||||
SkIRect::EmptyIRect(),
|
||||
|
@ -283,22 +283,40 @@ sk_sp<SkImage> SkImage::makeWithFilter(GrContext* grContext,
|
||||
sk_sp<SkImageFilterCache> cache(
|
||||
SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize));
|
||||
SkImageFilter::OutputProperties outputProperties(fInfo.colorType(), fInfo.colorSpace());
|
||||
SkImageFilter::Context context(SkMatrix::I(), clipBounds, cache.get(), outputProperties);
|
||||
|
||||
// The filters operate in the local space of the src image, where (0,0) corresponds to the
|
||||
// subset's top left corner. But the clip bounds and any crop rects on the filters are in the
|
||||
// original coordinate system, so configure the CTM to correct crop rects and explicitly adjust
|
||||
// the clip bounds (since it is assumed to already be in image space).
|
||||
SkImageFilter::Context context(SkMatrix::MakeTrans(-subset.x(), -subset.y()),
|
||||
clipBounds.makeOffset(-subset.x(), -subset.y()),
|
||||
cache.get(), outputProperties);
|
||||
|
||||
sk_sp<SkSpecialImage> result = filter->filterImage(srcSpecialImage.get(), context, offset);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*outSubset = SkIRect::MakeWH(result->width(), result->height());
|
||||
if (!outSubset->intersect(clipBounds.makeOffset(-offset->x(), -offset->y()))) {
|
||||
// The output image and offset are relative to the subset rectangle, so the offset needs to
|
||||
// be shifted to put it in the correct spot with respect to the original coordinate system
|
||||
offset->fX += subset.x();
|
||||
offset->fY += subset.y();
|
||||
|
||||
// Final clip against the exact clipBounds (the clip provided in the context gets adjusted
|
||||
// to account for pixel-moving filters so doesn't always exactly match when finished). The
|
||||
// clipBounds are translated into the clippedDstRect coordinate space, including the
|
||||
// result->subset() ensures that the result's image pixel origin does not affect results.
|
||||
SkIRect dstRect = result->subset();
|
||||
SkIRect clippedDstRect = dstRect;
|
||||
if (!clippedDstRect.intersect(clipBounds.makeOffset(result->subset().x() - offset->x(),
|
||||
result->subset().y() - offset->y()))) {
|
||||
return nullptr;
|
||||
}
|
||||
offset->fX += outSubset->x();
|
||||
offset->fY += outSubset->y();
|
||||
|
||||
// Note that here we're returning the special image's entire backing store, loose padding
|
||||
// and all!
|
||||
// Adjust the geometric offset if the top-left corner moved as well
|
||||
offset->fX += (clippedDstRect.x() - dstRect.x());
|
||||
offset->fY += (clippedDstRect.y() - dstRect.y());
|
||||
*outSubset = clippedDstRect;
|
||||
return result->asImage();
|
||||
}
|
||||
|
||||
|
@ -54,11 +54,10 @@ static SkBitmap create_bm() {
|
||||
|
||||
// Basic test of the SkSpecialImage public API (e.g., peekTexture, peekPixels & draw)
|
||||
static void test_image(const sk_sp<SkSpecialImage>& img, skiatest::Reporter* reporter,
|
||||
GrContext* context, bool isGPUBacked,
|
||||
int offset, int size) {
|
||||
GrContext* context, bool isGPUBacked) {
|
||||
const SkIRect subset = img->subset();
|
||||
REPORTER_ASSERT(reporter, offset == subset.left());
|
||||
REPORTER_ASSERT(reporter, offset == subset.top());
|
||||
REPORTER_ASSERT(reporter, kPad == subset.left());
|
||||
REPORTER_ASSERT(reporter, kPad == subset.top());
|
||||
REPORTER_ASSERT(reporter, kSmallerSize == subset.width());
|
||||
REPORTER_ASSERT(reporter, kSmallerSize == subset.height());
|
||||
|
||||
@ -77,13 +76,8 @@ static void test_image(const sk_sp<SkSpecialImage>& img, skiatest::Reporter* rep
|
||||
// Test getROPixels - this should always succeed regardless of backing store
|
||||
SkBitmap bitmap;
|
||||
REPORTER_ASSERT(reporter, img->getROPixels(&bitmap));
|
||||
if (context) {
|
||||
REPORTER_ASSERT(reporter, kSmallerSize == bitmap.width());
|
||||
REPORTER_ASSERT(reporter, kSmallerSize == bitmap.height());
|
||||
} else {
|
||||
REPORTER_ASSERT(reporter, size == bitmap.width());
|
||||
REPORTER_ASSERT(reporter, size == bitmap.height());
|
||||
}
|
||||
REPORTER_ASSERT(reporter, kSmallerSize == bitmap.width());
|
||||
REPORTER_ASSERT(reporter, kSmallerSize == bitmap.height());
|
||||
|
||||
//--------------
|
||||
// Test that draw restricts itself to the subset
|
||||
@ -148,12 +142,12 @@ DEF_TEST(SpecialImage_Raster, reporter) {
|
||||
|
||||
{
|
||||
sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeFromRaster(subset, bm));
|
||||
test_image(subSImg1, reporter, nullptr, false, kPad, kFullSize);
|
||||
test_image(subSImg1, reporter, nullptr, false);
|
||||
}
|
||||
|
||||
{
|
||||
sk_sp<SkSpecialImage> subSImg2(fullSImage->makeSubset(subset));
|
||||
test_image(subSImg2, reporter, nullptr, false, 0, kSmallerSize);
|
||||
test_image(subSImg2, reporter, nullptr, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,12 +165,12 @@ static void test_specialimage_image(skiatest::Reporter* reporter) {
|
||||
|
||||
{
|
||||
sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeFromImage(nullptr, subset, fullImage));
|
||||
test_image(subSImg1, reporter, nullptr, false, kPad, kFullSize);
|
||||
test_image(subSImg1, reporter, nullptr, false);
|
||||
}
|
||||
|
||||
{
|
||||
sk_sp<SkSpecialImage> subSImg2(fullSImage->makeSubset(subset));
|
||||
test_image(subSImg2, reporter, nullptr, false, 0, kSmallerSize);
|
||||
test_image(subSImg2, reporter, nullptr, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,12 +273,12 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_Gpu, reporter, ctxInfo) {
|
||||
context, subset,
|
||||
kNeedNewImageUniqueID_SpecialImage,
|
||||
std::move(proxy), nullptr));
|
||||
test_image(subSImg1, reporter, context, true, kPad, kFullSize);
|
||||
test_image(subSImg1, reporter, context, true);
|
||||
}
|
||||
|
||||
{
|
||||
sk_sp<SkSpecialImage> subSImg2(fullSImg->makeSubset(subset));
|
||||
test_image(subSImg2, reporter, context, true, kPad, kFullSize);
|
||||
test_image(subSImg2, reporter, context, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user