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