Home | History | Annotate | Download | only in effects
      1 /*
      2  * Copyright 2013 Google Inc.
      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 "SkAlphaThresholdFilter.h"
      9 
     10 #include "SkBitmap.h"
     11 #include "SkColorSpaceXformer.h"
     12 #include "SkImageFilterPriv.h"
     13 #include "SkReadBuffer.h"
     14 #include "SkSpecialImage.h"
     15 #include "SkWriteBuffer.h"
     16 #include "SkRegion.h"
     17 
     18 #if SK_SUPPORT_GPU
     19 #include "GrAlphaThresholdFragmentProcessor.h"
     20 #include "GrColorSpaceXform.h"
     21 #include "GrContext.h"
     22 #include "GrFixedClip.h"
     23 #include "GrRenderTargetContext.h"
     24 #include "GrTextureProxy.h"
     25 #include "effects/GrSimpleTextureEffect.h"
     26 #endif
     27 
     28 class SkAlphaThresholdFilterImpl : public SkImageFilter {
     29 public:
     30     SkAlphaThresholdFilterImpl(const SkRegion& region, SkScalar innerThreshold,
     31                                SkScalar outerThreshold, sk_sp<SkImageFilter> input,
     32                                const CropRect* cropRect = nullptr);
     33 
     34     SK_TO_STRING_OVERRIDE()
     35     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAlphaThresholdFilterImpl)
     36     friend void SkAlphaThresholdFilter::InitializeFlattenables();
     37 
     38 protected:
     39     void flatten(SkWriteBuffer&) const override;
     40 
     41     sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&,
     42                                         SkIPoint* offset) const override;
     43 
     44     sk_sp<SkImageFilter> onMakeColorSpace(SkColorSpaceXformer*) const override;
     45 
     46 #if SK_SUPPORT_GPU
     47     sk_sp<GrTextureProxy> createMaskTexture(GrContext*,
     48                                             const SkMatrix&,
     49                                             const SkIRect& bounds) const;
     50 #endif
     51 
     52 private:
     53     SkRegion fRegion;
     54     SkScalar fInnerThreshold;
     55     SkScalar fOuterThreshold;
     56     typedef SkImageFilter INHERITED;
     57 };
     58 
     59 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkAlphaThresholdFilter)
     60     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAlphaThresholdFilterImpl)
     61 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
     62 
     63 static SkScalar pin_0_1(SkScalar x) {
     64     return SkMinScalar(SkMaxScalar(x, 0), 1);
     65 }
     66 
     67 sk_sp<SkImageFilter> SkAlphaThresholdFilter::Make(const SkRegion& region,
     68                                                   SkScalar innerThreshold,
     69                                                   SkScalar outerThreshold,
     70                                                   sk_sp<SkImageFilter> input,
     71                                                   const SkImageFilter::CropRect* cropRect) {
     72     innerThreshold = pin_0_1(innerThreshold);
     73     outerThreshold = pin_0_1(outerThreshold);
     74     if (!SkScalarIsFinite(innerThreshold) || !SkScalarIsFinite(outerThreshold)) {
     75         return nullptr;
     76     }
     77     return sk_sp<SkImageFilter>(new SkAlphaThresholdFilterImpl(region, innerThreshold,
     78                                                                outerThreshold,
     79                                                                std::move(input),
     80                                                                cropRect));
     81 }
     82 
     83 sk_sp<SkFlattenable> SkAlphaThresholdFilterImpl::CreateProc(SkReadBuffer& buffer) {
     84     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
     85     SkScalar inner = buffer.readScalar();
     86     SkScalar outer = buffer.readScalar();
     87     SkRegion rgn;
     88     buffer.readRegion(&rgn);
     89     return SkAlphaThresholdFilter::Make(rgn, inner, outer, common.getInput(0),
     90                                         &common.cropRect());
     91 }
     92 
     93 SkAlphaThresholdFilterImpl::SkAlphaThresholdFilterImpl(const SkRegion& region,
     94                                                        SkScalar innerThreshold,
     95                                                        SkScalar outerThreshold,
     96                                                        sk_sp<SkImageFilter> input,
     97                                                        const CropRect* cropRect)
     98     : INHERITED(&input, 1, cropRect)
     99     , fRegion(region)
    100     , fInnerThreshold(innerThreshold)
    101     , fOuterThreshold(outerThreshold) {
    102 }
    103 
    104 #if SK_SUPPORT_GPU
    105 sk_sp<GrTextureProxy> SkAlphaThresholdFilterImpl::createMaskTexture(GrContext* context,
    106                                                                     const SkMatrix& inMatrix,
    107                                                                     const SkIRect& bounds) const {
    108 
    109     sk_sp<GrRenderTargetContext> rtContext(context->makeDeferredRenderTargetContextWithFallback(
    110         SkBackingFit::kApprox, bounds.width(), bounds.height(), kAlpha_8_GrPixelConfig, nullptr));
    111     if (!rtContext) {
    112         return nullptr;
    113     }
    114 
    115     GrPaint paint;
    116     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
    117     SkRegion::Iterator iter(fRegion);
    118     rtContext->clear(nullptr, 0x0, GrRenderTargetContext::CanClearFullscreen::kYes);
    119 
    120     GrFixedClip clip(SkIRect::MakeWH(bounds.width(), bounds.height()));
    121     while (!iter.done()) {
    122         SkRect rect = SkRect::Make(iter.rect());
    123         rtContext->drawRect(clip, std::move(paint), GrAA::kNo, inMatrix, rect);
    124         iter.next();
    125     }
    126 
    127     return rtContext->asTextureProxyRef();
    128 }
    129 #endif
    130 
    131 void SkAlphaThresholdFilterImpl::flatten(SkWriteBuffer& buffer) const {
    132     this->INHERITED::flatten(buffer);
    133     buffer.writeScalar(fInnerThreshold);
    134     buffer.writeScalar(fOuterThreshold);
    135     buffer.writeRegion(fRegion);
    136 }
    137 
    138 sk_sp<SkSpecialImage> SkAlphaThresholdFilterImpl::onFilterImage(SkSpecialImage* source,
    139                                                                 const Context& ctx,
    140                                                                 SkIPoint* offset) const {
    141     SkIPoint inputOffset = SkIPoint::Make(0, 0);
    142     sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
    143     if (!input) {
    144         return nullptr;
    145     }
    146 
    147     const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(),
    148                                                   input->width(), input->height());
    149 
    150     SkIRect bounds;
    151     if (!this->applyCropRect(ctx, inputBounds, &bounds)) {
    152         return nullptr;
    153     }
    154 
    155 #if SK_SUPPORT_GPU
    156     if (source->isTextureBacked()) {
    157         GrContext* context = source->getContext();
    158 
    159         sk_sp<GrTextureProxy> inputProxy(input->asTextureProxyRef(context));
    160         SkASSERT(inputProxy);
    161 
    162         offset->fX = bounds.left();
    163         offset->fY = bounds.top();
    164 
    165         bounds.offset(-inputOffset);
    166 
    167         SkMatrix matrix(ctx.ctm());
    168         matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
    169 
    170         sk_sp<GrTextureProxy> maskProxy(this->createMaskTexture(context, matrix, bounds));
    171         if (!maskProxy) {
    172             return nullptr;
    173         }
    174 
    175         const OutputProperties& outProps = ctx.outputProperties();
    176         GrPixelConfig inputConfig = inputProxy->config();
    177         auto textureFP = GrSimpleTextureEffect::Make(std::move(inputProxy), SkMatrix::I());
    178         textureFP = GrColorSpaceXformEffect::Make(std::move(textureFP), input->getColorSpace(),
    179                                                   inputConfig, outProps.colorSpace());
    180         if (!textureFP) {
    181             return nullptr;
    182         }
    183 
    184         auto thresholdFP = GrAlphaThresholdFragmentProcessor::Make(std::move(maskProxy),
    185                                                                    fInnerThreshold,
    186                                                                    fOuterThreshold,
    187                                                                    bounds);
    188         if (!thresholdFP) {
    189             return nullptr;
    190         }
    191 
    192         std::unique_ptr<GrFragmentProcessor> fpSeries[] = { std::move(textureFP),
    193                                                             std::move(thresholdFP) };
    194         auto fp = GrFragmentProcessor::RunInSeries(fpSeries, 2);
    195 
    196         return DrawWithFP(context, std::move(fp), bounds, outProps);
    197     }
    198 #endif
    199 
    200     SkBitmap inputBM;
    201 
    202     if (!input->getROPixels(&inputBM)) {
    203         return nullptr;
    204     }
    205 
    206     if (inputBM.colorType() != kN32_SkColorType) {
    207         return nullptr;
    208     }
    209 
    210     if (!inputBM.getPixels() || inputBM.width() <= 0 || inputBM.height() <= 0) {
    211         return nullptr;
    212     }
    213 
    214 
    215     SkMatrix localInverse;
    216     if (!ctx.ctm().invert(&localInverse)) {
    217         return nullptr;
    218     }
    219 
    220     SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(),
    221                                             kPremul_SkAlphaType);
    222 
    223     SkBitmap dst;
    224     if (!dst.tryAllocPixels(info)) {
    225         return nullptr;
    226     }
    227 
    228     U8CPU innerThreshold = (U8CPU)(fInnerThreshold * 0xFF);
    229     U8CPU outerThreshold = (U8CPU)(fOuterThreshold * 0xFF);
    230     SkColor* dptr = dst.getAddr32(0, 0);
    231     int dstWidth = dst.width(), dstHeight = dst.height();
    232     SkIPoint srcOffset = { bounds.fLeft - inputOffset.fX, bounds.fTop - inputOffset.fY };
    233     for (int y = 0; y < dstHeight; ++y) {
    234         const SkColor* sptr = inputBM.getAddr32(srcOffset.fX, srcOffset.fY+y);
    235 
    236         for (int x = 0; x < dstWidth; ++x) {
    237             const SkColor& source = sptr[x];
    238             SkColor outputColor(source);
    239             SkPoint position;
    240             localInverse.mapXY((SkScalar)x + bounds.fLeft, (SkScalar)y + bounds.fTop, &position);
    241             if (fRegion.contains((int32_t)position.x(), (int32_t)position.y())) {
    242                 if (SkColorGetA(source) < innerThreshold) {
    243                     U8CPU alpha = SkColorGetA(source);
    244                     if (alpha == 0) {
    245                         alpha = 1;
    246                     }
    247                     float scale = (float)innerThreshold / alpha;
    248                     outputColor = SkColorSetARGB(innerThreshold,
    249                                                   (U8CPU)(SkColorGetR(source) * scale),
    250                                                   (U8CPU)(SkColorGetG(source) * scale),
    251                                                   (U8CPU)(SkColorGetB(source) * scale));
    252                 }
    253             } else {
    254                 if (SkColorGetA(source) > outerThreshold) {
    255                     float scale = (float)outerThreshold / SkColorGetA(source);
    256                     outputColor = SkColorSetARGB(outerThreshold,
    257                                                   (U8CPU)(SkColorGetR(source) * scale),
    258                                                   (U8CPU)(SkColorGetG(source) * scale),
    259                                                   (U8CPU)(SkColorGetB(source) * scale));
    260                 }
    261             }
    262             dptr[y * dstWidth + x] = outputColor;
    263         }
    264     }
    265 
    266     offset->fX = bounds.left();
    267     offset->fY = bounds.top();
    268     return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()),
    269                                           dst);
    270 }
    271 
    272 sk_sp<SkImageFilter> SkAlphaThresholdFilterImpl::onMakeColorSpace(SkColorSpaceXformer* xformer)
    273 const {
    274     SkASSERT(1 == this->countInputs());
    275     sk_sp<SkImageFilter> input = xformer->apply(this->getInput(0));
    276     if (input.get() != this->getInput(0)) {
    277         return SkAlphaThresholdFilter::Make(fRegion, fInnerThreshold, fOuterThreshold,
    278                                             std::move(input), this->getCropRectIfSet());
    279     }
    280     return this->refMe();
    281 }
    282 
    283 #ifndef SK_IGNORE_TO_STRING
    284 void SkAlphaThresholdFilterImpl::toString(SkString* str) const {
    285     str->appendf("SkAlphaThresholdImageFilter: (");
    286     str->appendf("inner: %f outer: %f", fInnerThreshold, fOuterThreshold);
    287     str->append(")");
    288 }
    289 #endif
    290