diff options
-rw-r--r-- | include/effects/SkMatrixConvolutionImageFilter.h | 11 | ||||
-rw-r--r-- | src/effects/SkMatrixConvolutionImageFilter.cpp | 175 | ||||
-rw-r--r-- | tests/ImageFilterTest.cpp | 45 |
3 files changed, 148 insertions, 83 deletions
diff --git a/include/effects/SkMatrixConvolutionImageFilter.h b/include/effects/SkMatrixConvolutionImageFilter.h index 7a36c84af2..6c559675d6 100644 --- a/include/effects/SkMatrixConvolutionImageFilter.h +++ b/include/effects/SkMatrixConvolutionImageFilter.h @@ -29,7 +29,7 @@ public: kMax_TileMode = kClampToBlack_TileMode }; - virtual ~SkMatrixConvolutionImageFilter(); + ~SkMatrixConvolutionImageFilter() override; /** Construct a matrix convolution image filter. @param kernelSize The kernel size in pixels, in each dimension (N by M). @@ -92,16 +92,11 @@ protected: const CropRect* cropRect); void flatten(SkWriteBuffer&) const override; - bool onFilterImageDeprecated(Proxy*, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* loc) const override; + sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&, + SkIPoint* offset) const override; SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const override; bool affectsTransparentBlack() const override; -#if SK_SUPPORT_GPU - bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&, - const SkIRect& bounds) const override; -#endif - private: SkISize fKernelSize; SkScalar* fKernel; diff --git a/src/effects/SkMatrixConvolutionImageFilter.cpp b/src/effects/SkMatrixConvolutionImageFilter.cpp index 3104336628..51e3b6faa7 100644 --- a/src/effects/SkMatrixConvolutionImageFilter.cpp +++ b/src/effects/SkMatrixConvolutionImageFilter.cpp @@ -8,13 +8,16 @@ #include "SkMatrixConvolutionImageFilter.h" #include "SkBitmap.h" #include "SkColorPriv.h" -#include "SkDevice.h" #include "SkReadBuffer.h" +#include "SkSpecialImage.h" +#include "SkSpecialSurface.h" #include "SkWriteBuffer.h" #include "SkRect.h" #include "SkUnPreMultiply.h" #if SK_SUPPORT_GPU +#include "GrContext.h" +#include "GrDrawContext.h" #include "effects/GrMatrixConvolutionEffect.h" #endif @@ -242,18 +245,19 @@ void SkMatrixConvolutionImageFilter::filterBorderPixels(const SkBitmap& src, // FIXME: This should be refactored to SkImageFilterUtils for // use by other filters. For now, we assume the input is always // premultiplied and unpremultiply it -static SkBitmap unpremultiplyBitmap(SkImageFilter::Proxy* proxy, const SkBitmap& src) +static SkBitmap unpremultiply_bitmap(const SkBitmap& src) { SkAutoLockPixels alp(src); if (!src.getPixels()) { return SkBitmap(); } - SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(src.width(), src.height())); - if (!device) { + + const SkImageInfo info = SkImageInfo::MakeN32(src.width(), src.height(), src.alphaType()); + SkBitmap result; + if (!result.tryAllocPixels(info)) { return SkBitmap(); } - SkBitmap result = device->accessBitmap(false); - SkAutoLockPixels alp_result(result); + SkAutoLockPixels resultLock(result); for (int y = 0; y < src.height(); ++y) { const uint32_t* srcRow = src.getAddr32(0, y); uint32_t* dstRow = result.getAddr32(0, y); @@ -264,46 +268,103 @@ static SkBitmap unpremultiplyBitmap(SkImageFilter::Proxy* proxy, const SkBitmap& return result; } -bool SkMatrixConvolutionImageFilter::onFilterImageDeprecated(Proxy* proxy, - const SkBitmap& source, - const Context& ctx, - SkBitmap* result, - SkIPoint* offset) const { - SkBitmap src = source; - SkIPoint srcOffset = SkIPoint::Make(0, 0); - if (!this->filterInputDeprecated(0, proxy, source, ctx, &src, &srcOffset)) { - return false; +#if SK_SUPPORT_GPU + +static GrTextureDomain::Mode convert_tilemodes(SkMatrixConvolutionImageFilter::TileMode tileMode) { + switch (tileMode) { + case SkMatrixConvolutionImageFilter::kClamp_TileMode: + return GrTextureDomain::kClamp_Mode; + case SkMatrixConvolutionImageFilter::kRepeat_TileMode: + return GrTextureDomain::kRepeat_Mode; + case SkMatrixConvolutionImageFilter::kClampToBlack_TileMode: + return GrTextureDomain::kDecal_Mode; + default: + SkASSERT(false); } + return GrTextureDomain::kIgnore_Mode; +} - if (src.colorType() != kN32_SkColorType) { - return false; +#endif + +sk_sp<SkSpecialImage> SkMatrixConvolutionImageFilter::onFilterImage(SkSpecialImage* source, + const Context& ctx, + SkIPoint* offset) const { + SkIPoint inputOffset = SkIPoint::Make(0, 0); + sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset)); + if (!input) { + return nullptr; } SkIRect bounds; - if (!this->applyCropRectDeprecated(this->mapContext(ctx), proxy, src, &srcOffset, - &bounds, &src)) { - return false; + input = this->applyCropRect(this->mapContext(ctx), input.get(), &inputOffset, &bounds); + if (!input) { + return nullptr; } - if (!fConvolveAlpha && !src.isOpaque()) { - src = unpremultiplyBitmap(proxy, src); +#if SK_SUPPORT_GPU + // Note: if the kernel is too big, the GPU path falls back to SW + if (source->isTextureBacked() && + fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE) { + GrContext* context = source->getContext(); + + sk_sp<GrTexture> inputTexture(input->asTextureRef(context)); + SkASSERT(inputTexture); + + offset->fX = bounds.left(); + offset->fY = bounds.top(); + bounds.offset(-inputOffset); + + // SRGBTODO: handle sRGB here + sk_sp<GrFragmentProcessor> fp(GrMatrixConvolutionEffect::Create( + inputTexture.get(), + bounds, + fKernelSize, + fKernel, + fGain, + fBias, + fKernelOffset, + convert_tilemodes(fTileMode), + fConvolveAlpha)); + if (!fp) { + return nullptr; + } + + return DrawWithFP(context, std::move(fp), bounds, source->internal_getProxy()); } +#endif - SkAutoLockPixels alp(src); - if (!src.getPixels()) { - return false; + SkBitmap inputBM; + + if (!input->getROPixels(&inputBM)) { + return nullptr; } - SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height())); - if (!device) { - return false; + if (inputBM.colorType() != kN32_SkColorType) { + return nullptr; } - *result = device->accessBitmap(false); - SkAutoLockPixels alp_result(*result); + + if (!fConvolveAlpha && !inputBM.isOpaque()) { + inputBM = unpremultiply_bitmap(inputBM); + } + + SkAutoLockPixels alp(inputBM); + if (!inputBM.getPixels()) { + return nullptr; + } + + const SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(), + inputBM.alphaType()); + + SkBitmap dst; + if (!dst.tryAllocPixels(info)) { + return nullptr; + } + + SkAutoLockPixels dstLock(dst); offset->fX = bounds.fLeft; offset->fY = bounds.fTop; - bounds.offset(-srcOffset); + bounds.offset(-inputOffset); SkIRect interior = SkIRect::MakeXYWH(bounds.left() + fKernelOffset.fX, bounds.top() + fKernelOffset.fY, bounds.width() - fKernelSize.fWidth + 1, @@ -315,12 +376,14 @@ bool SkMatrixConvolutionImageFilter::onFilterImageDeprecated(Proxy* proxy, interior.left(), interior.bottom()); SkIRect right = SkIRect::MakeLTRB(interior.right(), interior.top(), bounds.right(), interior.bottom()); - filterBorderPixels(src, result, top, bounds); - filterBorderPixels(src, result, left, bounds); - filterInteriorPixels(src, result, interior, bounds); - filterBorderPixels(src, result, right, bounds); - filterBorderPixels(src, result, bottom, bounds); - return true; + this->filterBorderPixels(inputBM, &dst, top, bounds); + this->filterBorderPixels(inputBM, &dst, left, bounds); + this->filterInteriorPixels(inputBM, &dst, interior, bounds); + this->filterBorderPixels(inputBM, &dst, right, bounds); + this->filterBorderPixels(inputBM, &dst, bottom, bounds); + return SkSpecialImage::MakeFromRaster(source->internal_getProxy(), + SkIRect::MakeWH(bounds.width(), bounds.height()), + dst); } SkIRect SkMatrixConvolutionImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, @@ -343,44 +406,6 @@ bool SkMatrixConvolutionImageFilter::affectsTransparentBlack() const { return true; } -#if SK_SUPPORT_GPU - -static GrTextureDomain::Mode convert_tilemodes( - SkMatrixConvolutionImageFilter::TileMode tileMode) { - switch (tileMode) { - case SkMatrixConvolutionImageFilter::kClamp_TileMode: - return GrTextureDomain::kClamp_Mode; - case SkMatrixConvolutionImageFilter::kRepeat_TileMode: - return GrTextureDomain::kRepeat_Mode; - case SkMatrixConvolutionImageFilter::kClampToBlack_TileMode: - return GrTextureDomain::kDecal_Mode; - default: - SkASSERT(false); - } - return GrTextureDomain::kIgnore_Mode; -} - -bool SkMatrixConvolutionImageFilter::asFragmentProcessor(GrFragmentProcessor** fp, - GrTexture* texture, - const SkMatrix&, - const SkIRect& bounds) const { - if (!fp) { - return fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE; - } - SkASSERT(fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE); - *fp = GrMatrixConvolutionEffect::Create(texture, - bounds, - fKernelSize, - fKernel, - fGain, - fBias, - fKernelOffset, - convert_tilemodes(fTileMode), - fConvolveAlpha); - return true; -} -#endif - #ifndef SK_IGNORE_TO_STRING void SkMatrixConvolutionImageFilter::toString(SkString* str) const { str->appendf("SkMatrixConvolutionImageFilter: ("); diff --git a/tests/ImageFilterTest.cpp b/tests/ImageFilterTest.cpp index a6ff0875b7..114c342ada 100644 --- a/tests/ImageFilterTest.cpp +++ b/tests/ImageFilterTest.cpp @@ -1093,6 +1093,51 @@ DEF_TEST(ImageFilterMatrixConvolutionBorder, reporter) { canvas.restore(); } +static void test_big_kernel(SkImageFilter::Proxy* proxy, + skiatest::Reporter* reporter, + GrContext* context) { + // Check that a kernel that is too big for the GPU still works + SkScalar identityKernel[49] = { + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 + }; + SkISize kernelSize = SkISize::Make(7, 7); + SkScalar gain = SK_Scalar1, bias = 0; + SkIPoint kernelOffset = SkIPoint::Make(0, 0); + + sk_sp<SkImageFilter> filter(SkMatrixConvolutionImageFilter::Make( + kernelSize, identityKernel, gain, bias, kernelOffset, + SkMatrixConvolutionImageFilter::kClamp_TileMode, + true, nullptr)); + + sk_sp<SkSpecialImage> srcImg(create_empty_special_image(context, proxy, 100)); + SkASSERT(srcImg); + + SkIPoint offset; + SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeWH(100, 100), nullptr); + sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg.get(), ctx, &offset)); + REPORTER_ASSERT(reporter, resultImg); + REPORTER_ASSERT(reporter, SkToBool(context) == resultImg->isTextureBacked()); + REPORTER_ASSERT(reporter, resultImg->width() == 100 && resultImg->height() == 100); + REPORTER_ASSERT(reporter, offset.fX == 0 && offset.fY == 0); +} + +DEF_TEST(ImageFilterMatrixConvolutionBigKernel, reporter) { + run_raster_test(reporter, 100, test_big_kernel); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ImageFilterMatrixConvolutionBigKernel_Gpu, + reporter, ctxInfo) { + run_gpu_test(reporter, ctxInfo.fGrContext, 100, test_big_kernel); +} +#endif + DEF_TEST(ImageFilterCropRect, reporter) { run_raster_test(reporter, 100, test_crop_rects); } |