LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkBlurImageFilter.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 145 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 15 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright 2011 The Android Open Source Project
       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             : #include "SkAutoPixmapStorage.h"
       9             : #include "SkColorPriv.h"
      10             : #include "SkGpuBlurUtils.h"
      11             : #include "SkOpts.h"
      12             : #include "SkReadBuffer.h"
      13             : #include "SkSpecialImage.h"
      14             : #include "SkWriteBuffer.h"
      15             : 
      16             : #if SK_SUPPORT_GPU
      17             : #include "GrContext.h"
      18             : #include "GrTextureProxy.h"
      19             : #include "SkGr.h"
      20             : #endif
      21             : 
      22           0 : class SkBlurImageFilterImpl : public SkImageFilter {
      23             : public:
      24             :     SkBlurImageFilterImpl(SkScalar sigmaX,
      25             :                       SkScalar sigmaY,
      26             :                       sk_sp<SkImageFilter> input,
      27             :                       const CropRect* cropRect);
      28             : 
      29             :     SkRect computeFastBounds(const SkRect&) const override;
      30             : 
      31             :     SK_TO_STRING_OVERRIDE()
      32           0 :     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurImageFilterImpl)
      33             : 
      34             : protected:
      35             :     void flatten(SkWriteBuffer&) const override;
      36             :     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
      37             :                                         SkIPoint* offset) const override;
      38             :     sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
      39             :     SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override;
      40             : 
      41             : private:
      42             :     SkSize   fSigma;
      43             :     typedef SkImageFilter INHERITED;
      44             : 
      45             :     friend class SkImageFilter;
      46             : };
      47             : 
      48           0 : SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkImageFilter)
      49           0 :     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurImageFilterImpl)
      50           0 : SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
      51             : 
      52             : ///////////////////////////////////////////////////////////////////////////////
      53             : 
      54           0 : sk_sp<SkImageFilter> SkImageFilter::MakeBlur(SkScalar sigmaX, SkScalar sigmaY,
      55             :                                              sk_sp<SkImageFilter> input,
      56             :                                              const CropRect* cropRect) {
      57           0 :     if (0 == sigmaX && 0 == sigmaY && !cropRect) {
      58           0 :         return input;
      59             :     }
      60           0 :     return sk_sp<SkImageFilter>(new SkBlurImageFilterImpl(sigmaX, sigmaY, input, cropRect));
      61             : }
      62             : 
      63             : // This rather arbitrary-looking value results in a maximum box blur kernel size
      64             : // of 1000 pixels on the raster path, which matches the WebKit and Firefox
      65             : // implementations. Since the GPU path does not compute a box blur, putting
      66             : // the limit on sigma ensures consistent behaviour between the GPU and
      67             : // raster paths.
      68             : #define MAX_SIGMA SkIntToScalar(532)
      69             : 
      70           0 : static SkVector map_sigma(const SkSize& localSigma, const SkMatrix& ctm) {
      71           0 :     SkVector sigma = SkVector::Make(localSigma.width(), localSigma.height());
      72           0 :     ctm.mapVectors(&sigma, 1);
      73           0 :     sigma.fX = SkMinScalar(SkScalarAbs(sigma.fX), MAX_SIGMA);
      74           0 :     sigma.fY = SkMinScalar(SkScalarAbs(sigma.fY), MAX_SIGMA);
      75           0 :     return sigma;
      76             : }
      77             : 
      78           0 : SkBlurImageFilterImpl::SkBlurImageFilterImpl(
      79           0 :         SkScalar sigmaX, SkScalar sigmaY, sk_sp<SkImageFilter> input, const CropRect* cropRect)
      80           0 :         : INHERITED(&input, 1, cropRect), fSigma{sigmaX, sigmaY} {}
      81             : 
      82           0 : sk_sp<SkFlattenable> SkBlurImageFilterImpl::CreateProc(SkReadBuffer& buffer) {
      83           0 :     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
      84           0 :     SkScalar sigmaX = buffer.readScalar();
      85           0 :     SkScalar sigmaY = buffer.readScalar();
      86           0 :     return SkImageFilter::MakeBlur(sigmaX, sigmaY, common.getInput(0), &common.cropRect());
      87             : }
      88             : 
      89           0 : void SkBlurImageFilterImpl::flatten(SkWriteBuffer& buffer) const {
      90           0 :     this->INHERITED::flatten(buffer);
      91           0 :     buffer.writeScalar(fSigma.fWidth);
      92           0 :     buffer.writeScalar(fSigma.fHeight);
      93           0 : }
      94             : 
      95           0 : static void get_box3_params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset,
      96             :                             int *highOffset) {
      97           0 :     float pi = SkScalarToFloat(SK_ScalarPI);
      98           0 :     int d = static_cast<int>(floorf(SkScalarToFloat(s) * 3.0f * sqrtf(2.0f * pi) / 4.0f + 0.5f));
      99           0 :     *kernelSize = d;
     100           0 :     if (d % 2 == 1) {
     101           0 :         *lowOffset = *highOffset = (d - 1) / 2;
     102           0 :         *kernelSize3 = d;
     103             :     } else {
     104           0 :         *highOffset = d / 2;
     105           0 :         *lowOffset = *highOffset - 1;
     106           0 :         *kernelSize3 = d + 1;
     107             :     }
     108           0 : }
     109             : 
     110           0 : sk_sp<SkSpecialImage> SkBlurImageFilterImpl::onFilterImage(SkSpecialImage* source,
     111             :                                                            const Context& ctx,
     112             :                                                            SkIPoint* offset) const {
     113           0 :     SkIPoint inputOffset = SkIPoint::Make(0, 0);
     114             : 
     115           0 :     sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
     116           0 :     if (!input) {
     117           0 :         return nullptr;
     118             :     }
     119             : 
     120             :     SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.fX, inputOffset.fY,
     121           0 :                                             input->width(), input->height());
     122             : 
     123             :     SkIRect dstBounds;
     124           0 :     if (!this->applyCropRect(this->mapContext(ctx), inputBounds, &dstBounds)) {
     125           0 :         return nullptr;
     126             :     }
     127           0 :     if (!inputBounds.intersect(dstBounds)) {
     128           0 :         return nullptr;
     129             :     }
     130             : 
     131           0 :     const SkVector sigma = map_sigma(fSigma, ctx.ctm());
     132             : 
     133             : #if SK_SUPPORT_GPU
     134           0 :     if (source->isTextureBacked()) {
     135           0 :         GrContext* context = source->getContext();
     136             : 
     137             :         // Ensure the input is in the destination's gamut. This saves us from having to do the
     138             :         // xform during the filter itself.
     139           0 :         input = ImageToColorSpace(input.get(), ctx.outputProperties());
     140             : 
     141           0 :         sk_sp<GrTextureProxy> inputTexture(input->asTextureProxyRef(context));
     142           0 :         if (!inputTexture) {
     143           0 :             return nullptr;
     144             :         }
     145             : 
     146           0 :         if (0 == sigma.x() && 0 == sigma.y()) {
     147           0 :             offset->fX = inputBounds.x();
     148           0 :             offset->fY = inputBounds.y();
     149           0 :             return input->makeSubset(inputBounds.makeOffset(-inputOffset.x(),
     150           0 :                                                             -inputOffset.y()));
     151             :         }
     152             : 
     153           0 :         offset->fX = dstBounds.fLeft;
     154           0 :         offset->fY = dstBounds.fTop;
     155           0 :         inputBounds.offset(-inputOffset);
     156           0 :         dstBounds.offset(-inputOffset);
     157             :         // Typically, we would create the RTC with the output's color space (from ctx), but we
     158             :         // always blur in the PixelConfig of the *input*. Those might not be compatible (if they
     159             :         // have different transfer functions). We've already guaranteed that those color spaces
     160             :         // have the same gamut, so in this case, we do everything in the input's color space.
     161             :         sk_sp<GrRenderTargetContext> renderTargetContext(SkGpuBlurUtils::GaussianBlur(
     162             :                                                                 context,
     163           0 :                                                                 std::move(inputTexture),
     164           0 :                                                                 sk_ref_sp(input->getColorSpace()),
     165             :                                                                 dstBounds,
     166             :                                                                 &inputBounds,
     167             :                                                                 sigma.x(),
     168           0 :                                                                 sigma.y()));
     169           0 :         if (!renderTargetContext) {
     170           0 :             return nullptr;
     171             :         }
     172             : 
     173             :         return SkSpecialImage::MakeDeferredFromGpu(context,
     174           0 :                                                    SkIRect::MakeWH(dstBounds.width(),
     175           0 :                                                                    dstBounds.height()),
     176             :                                                    kNeedNewImageUniqueID_SpecialImage,
     177           0 :                                                    renderTargetContext->asTextureProxyRef(),
     178           0 :                                                    renderTargetContext->refColorSpace(),
     179           0 :                                                    &source->props());
     180             :     }
     181             : #endif
     182             : 
     183             :     int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
     184             :     int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
     185           0 :     get_box3_params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX);
     186           0 :     get_box3_params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY);
     187             : 
     188           0 :     if (kernelSizeX < 0 || kernelSizeY < 0) {
     189           0 :         return nullptr;
     190             :     }
     191             : 
     192           0 :     if (kernelSizeX == 0 && kernelSizeY == 0) {
     193           0 :         offset->fX = inputBounds.x();
     194           0 :         offset->fY = inputBounds.y();
     195           0 :         return input->makeSubset(inputBounds.makeOffset(-inputOffset.x(),
     196           0 :                                                         -inputOffset.y()));
     197             :     }
     198             : 
     199           0 :     SkBitmap inputBM;
     200             : 
     201           0 :     if (!input->getROPixels(&inputBM)) {
     202           0 :         return nullptr;
     203             :     }
     204             : 
     205           0 :     if (inputBM.colorType() != kN32_SkColorType) {
     206           0 :         return nullptr;
     207             :     }
     208             : 
     209             :     SkImageInfo info = SkImageInfo::Make(dstBounds.width(), dstBounds.height(),
     210           0 :                                          inputBM.colorType(), inputBM.alphaType());
     211             : 
     212           0 :     SkBitmap tmp, dst;
     213           0 :     if (!tmp.tryAllocPixels(info) || !dst.tryAllocPixels(info)) {
     214           0 :         return nullptr;
     215             :     }
     216             : 
     217           0 :     SkAutoLockPixels inputLock(inputBM), tmpLock(tmp), dstLock(dst);
     218             : 
     219           0 :     offset->fX = dstBounds.fLeft;
     220           0 :     offset->fY = dstBounds.fTop;
     221           0 :     SkPMColor* t = tmp.getAddr32(0, 0);
     222           0 :     SkPMColor* d = dst.getAddr32(0, 0);
     223           0 :     int w = dstBounds.width(), h = dstBounds.height();
     224           0 :     const SkPMColor* s = inputBM.getAddr32(inputBounds.x() - inputOffset.x(),
     225           0 :                                            inputBounds.y() - inputOffset.y());
     226           0 :     inputBounds.offset(-dstBounds.x(), -dstBounds.y());
     227           0 :     dstBounds.offset(-dstBounds.x(), -dstBounds.y());
     228             :     SkIRect inputBoundsT = SkIRect::MakeLTRB(inputBounds.top(), inputBounds.left(),
     229           0 :                                              inputBounds.bottom(), inputBounds.right());
     230           0 :     SkIRect dstBoundsT = SkIRect::MakeWH(dstBounds.height(), dstBounds.width());
     231           0 :     int sw = int(inputBM.rowBytes() >> 2);
     232             : 
     233             :     /**
     234             :      *
     235             :      * In order to make memory accesses cache-friendly, we reorder the passes to
     236             :      * use contiguous memory reads wherever possible.
     237             :      *
     238             :      * For example, the 6 passes of the X-and-Y blur case are rewritten as
     239             :      * follows. Instead of 3 passes in X and 3 passes in Y, we perform
     240             :      * 2 passes in X, 1 pass in X transposed to Y on write, 2 passes in X,
     241             :      * then 1 pass in X transposed to Y on write.
     242             :      *
     243             :      * +----+       +----+       +----+        +---+       +---+       +---+        +----+
     244             :      * + AB + ----> | AB | ----> | AB | -----> | A | ----> | A | ----> | A | -----> | AB |
     245             :      * +----+ blurX +----+ blurX +----+ blurXY | B | blurX | B | blurX | B | blurXY +----+
     246             :      *                                         +---+       +---+       +---+
     247             :      *
     248             :      * In this way, two of the y-blurs become x-blurs applied to transposed
     249             :      * images, and all memory reads are contiguous.
     250             :      */
     251           0 :     if (kernelSizeX > 0 && kernelSizeY > 0) {
     252           0 :         SkOpts::box_blur_xx(s, sw,  inputBounds,  t, kernelSizeX,  lowOffsetX,  highOffsetX, w, h);
     253           0 :         SkOpts::box_blur_xx(t,  w,  dstBounds,    d, kernelSizeX,  highOffsetX, lowOffsetX,  w, h);
     254           0 :         SkOpts::box_blur_xy(d,  w,  dstBounds,    t, kernelSizeX3, highOffsetX, highOffsetX, w, h);
     255           0 :         SkOpts::box_blur_xx(t,  h,  dstBoundsT,   d, kernelSizeY,  lowOffsetY,  highOffsetY, h, w);
     256           0 :         SkOpts::box_blur_xx(d,  h,  dstBoundsT,   t, kernelSizeY,  highOffsetY, lowOffsetY,  h, w);
     257           0 :         SkOpts::box_blur_xy(t,  h,  dstBoundsT,   d, kernelSizeY3, highOffsetY, highOffsetY, h, w);
     258           0 :     } else if (kernelSizeX > 0) {
     259           0 :         SkOpts::box_blur_xx(s, sw,  inputBounds,  d, kernelSizeX,  lowOffsetX,  highOffsetX, w, h);
     260           0 :         SkOpts::box_blur_xx(d,  w,  dstBounds,    t, kernelSizeX,  highOffsetX, lowOffsetX,  w, h);
     261           0 :         SkOpts::box_blur_xx(t,  w,  dstBounds,    d, kernelSizeX3, highOffsetX, highOffsetX, w, h);
     262           0 :     } else if (kernelSizeY > 0) {
     263           0 :         SkOpts::box_blur_yx(s, sw,  inputBoundsT, d, kernelSizeY,  lowOffsetY,  highOffsetY, h, w);
     264           0 :         SkOpts::box_blur_xx(d,  h,  dstBoundsT,   t, kernelSizeY,  highOffsetY, lowOffsetY,  h, w);
     265           0 :         SkOpts::box_blur_xy(t,  h,  dstBoundsT,   d, kernelSizeY3, highOffsetY, highOffsetY, h, w);
     266             :     }
     267             : 
     268           0 :     return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(dstBounds.width(),
     269           0 :                                                           dstBounds.height()),
     270           0 :                                           dst, &source->props());
     271             : }
     272             : 
     273           0 : sk_sp<SkImageFilter> SkBlurImageFilterImpl::onMakeColorSpace(SkColorSpaceXformer* xformer)
     274             : const {
     275           0 :     SkASSERT(1 == this->countInputs());
     276           0 :     if (!this->getInput(0)) {
     277           0 :         return sk_ref_sp(const_cast<SkBlurImageFilterImpl*>(this));
     278             :     }
     279             : 
     280           0 :     sk_sp<SkImageFilter> input = this->getInput(0)->makeColorSpace(xformer);
     281           0 :     return SkImageFilter::MakeBlur(fSigma.width(), fSigma.height(), std::move(input),
     282           0 :                                    this->getCropRectIfSet());
     283             : }
     284             : 
     285           0 : SkRect SkBlurImageFilterImpl::computeFastBounds(const SkRect& src) const {
     286           0 :     SkRect bounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src;
     287           0 :     bounds.outset(fSigma.width() * 3, fSigma.height() * 3);
     288           0 :     return bounds;
     289             : }
     290             : 
     291           0 : SkIRect SkBlurImageFilterImpl::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
     292             :                                               MapDirection) const {
     293           0 :     SkVector sigma = map_sigma(fSigma, ctm);
     294           0 :     return src.makeOutset(SkScalarCeilToInt(sigma.x() * 3), SkScalarCeilToInt(sigma.y() * 3));
     295             : }
     296             : 
     297             : #ifndef SK_IGNORE_TO_STRING
     298           0 : void SkBlurImageFilterImpl::toString(SkString* str) const {
     299           0 :     str->appendf("SkBlurImageFilterImpl: (");
     300           0 :     str->appendf("sigma: (%f, %f) input (", fSigma.fWidth, fSigma.fHeight);
     301             : 
     302           0 :     if (this->getInput(0)) {
     303           0 :         this->getInput(0)->toString(str);
     304             :     }
     305             : 
     306           0 :     str->append("))");
     307           0 : }
     308             : #endif

Generated by: LCOV version 1.13