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