Home | History | Annotate | Download | only in effects
      1 /*
      2  * Copyright 2013 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 "SkXfermodeImageFilter.h"
      9 #include "SkCanvas.h"
     10 #include "SkDevice.h"
     11 #include "SkColorPriv.h"
     12 #include "SkReadBuffer.h"
     13 #include "SkWriteBuffer.h"
     14 #include "SkXfermode.h"
     15 #if SK_SUPPORT_GPU
     16 #include "GrContext.h"
     17 #include "effects/GrTextureDomain.h"
     18 #include "SkGr.h"
     19 #endif
     20 
     21 ///////////////////////////////////////////////////////////////////////////////
     22 
     23 SkXfermodeImageFilter::SkXfermodeImageFilter(SkXfermode* mode,
     24                                              SkImageFilter* inputs[2],
     25                                              const CropRect* cropRect)
     26   : INHERITED(2, inputs, cropRect), fMode(mode) {
     27     SkSafeRef(fMode);
     28 }
     29 
     30 SkXfermodeImageFilter::~SkXfermodeImageFilter() {
     31     SkSafeUnref(fMode);
     32 }
     33 
     34 SkFlattenable* SkXfermodeImageFilter::CreateProc(SkReadBuffer& buffer) {
     35     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
     36     SkAutoTUnref<SkXfermode> mode(buffer.readXfermode());
     37     return Create(mode, common.getInput(0), common.getInput(1), &common.cropRect());
     38 }
     39 
     40 void SkXfermodeImageFilter::flatten(SkWriteBuffer& buffer) const {
     41     this->INHERITED::flatten(buffer);
     42     buffer.writeFlattenable(fMode);
     43 }
     44 
     45 bool SkXfermodeImageFilter::onFilterImage(Proxy* proxy,
     46                                             const SkBitmap& src,
     47                                             const Context& ctx,
     48                                             SkBitmap* dst,
     49                                             SkIPoint* offset) const {
     50     SkBitmap background = src, foreground = src;
     51     SkImageFilter* backgroundInput = getInput(0);
     52     SkImageFilter* foregroundInput = getInput(1);
     53     SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
     54     if (backgroundInput &&
     55         !backgroundInput->filterImage(proxy, src, ctx, &background, &backgroundOffset)) {
     56         background.reset();
     57     }
     58     SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
     59     if (foregroundInput &&
     60         !foregroundInput->filterImage(proxy, src, ctx, &foreground, &foregroundOffset)) {
     61         foreground.reset();
     62     }
     63 
     64     SkIRect bounds, foregroundBounds;
     65     if (!applyCropRect(ctx, foreground, foregroundOffset, &foregroundBounds)) {
     66         foregroundBounds.setEmpty();
     67         foreground.reset();
     68     }
     69     if (!applyCropRect(ctx, background, backgroundOffset, &bounds)) {
     70         bounds.setEmpty();
     71         background.reset();
     72     }
     73     bounds.join(foregroundBounds);
     74     if (bounds.isEmpty()) {
     75         return false;
     76     }
     77 
     78     SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height()));
     79     if (NULL == device.get()) {
     80         return false;
     81     }
     82     SkCanvas canvas(device);
     83     canvas.translate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top()));
     84     SkPaint paint;
     85     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
     86     canvas.drawBitmap(background, SkIntToScalar(backgroundOffset.fX),
     87                       SkIntToScalar(backgroundOffset.fY), &paint);
     88     paint.setXfermode(fMode);
     89     canvas.drawBitmap(foreground, SkIntToScalar(foregroundOffset.fX),
     90                       SkIntToScalar(foregroundOffset.fY), &paint);
     91     canvas.clipRect(SkRect::Make(foregroundBounds), SkRegion::kDifference_Op);
     92     paint.setColor(SK_ColorTRANSPARENT);
     93     canvas.drawPaint(paint);
     94     *dst = device->accessBitmap(false);
     95     offset->fX = bounds.left();
     96     offset->fY = bounds.top();
     97     return true;
     98 }
     99 
    100 #ifndef SK_IGNORE_TO_STRING
    101 void SkXfermodeImageFilter::toString(SkString* str) const {
    102     str->appendf("SkXfermodeImageFilter: (");
    103     str->appendf("xfermode: (");
    104     if (fMode) {
    105         fMode->toString(str);
    106     }
    107     str->append("))");
    108 }
    109 #endif
    110 
    111 #if SK_SUPPORT_GPU
    112 
    113 bool SkXfermodeImageFilter::canFilterImageGPU() const {
    114     return fMode && fMode->asFragmentProcessor(NULL, NULL) && !cropRectIsSet();
    115 }
    116 
    117 bool SkXfermodeImageFilter::filterImageGPU(Proxy* proxy,
    118                                            const SkBitmap& src,
    119                                            const Context& ctx,
    120                                            SkBitmap* result,
    121                                            SkIPoint* offset) const {
    122     SkBitmap background = src;
    123     SkIPoint backgroundOffset = SkIPoint::Make(0, 0);
    124     if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &background,
    125                                                        &backgroundOffset)) {
    126         return onFilterImage(proxy, src, ctx, result, offset);
    127     }
    128     GrTexture* backgroundTex = background.getTexture();
    129 
    130     if (NULL == backgroundTex) {
    131         SkASSERT(false);
    132         return false;
    133     }
    134 
    135     SkBitmap foreground = src;
    136     SkIPoint foregroundOffset = SkIPoint::Make(0, 0);
    137     if (getInput(1) && !getInput(1)->getInputResultGPU(proxy, src, ctx, &foreground,
    138                                                        &foregroundOffset)) {
    139         return onFilterImage(proxy, src, ctx, result, offset);
    140     }
    141     GrTexture* foregroundTex = foreground.getTexture();
    142     GrContext* context = foregroundTex->getContext();
    143 
    144     GrFragmentProcessor* xferProcessor = NULL;
    145 
    146     GrSurfaceDesc desc;
    147     desc.fFlags = kRenderTarget_GrSurfaceFlag;
    148     desc.fWidth = src.width();
    149     desc.fHeight = src.height();
    150     desc.fConfig = kSkia8888_GrPixelConfig;
    151     SkAutoTUnref<GrTexture> dst(context->textureProvider()->refScratchTexture(
    152         desc, GrTextureProvider::kApprox_ScratchTexMatch));
    153     if (!dst) {
    154         return false;
    155     }
    156 
    157     if (!fMode || !fMode->asFragmentProcessor(&xferProcessor, backgroundTex)) {
    158         // canFilterImageGPU() should've taken care of this
    159         SkASSERT(false);
    160         return false;
    161     }
    162 
    163     SkMatrix foregroundMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(foregroundTex);
    164     foregroundMatrix.preTranslate(SkIntToScalar(backgroundOffset.fX-foregroundOffset.fX),
    165                                   SkIntToScalar(backgroundOffset.fY-foregroundOffset.fY));
    166 
    167 
    168     SkRect srcRect;
    169     src.getBounds(&srcRect);
    170 
    171     GrPaint paint;
    172     SkAutoTUnref<GrFragmentProcessor> foregroundDomain(GrTextureDomainEffect::Create(
    173         foregroundTex, foregroundMatrix,
    174         GrTextureDomain::MakeTexelDomain(foregroundTex, foreground.bounds()),
    175         GrTextureDomain::kDecal_Mode,
    176         GrTextureParams::kNone_FilterMode)
    177     );
    178 
    179     paint.addColorProcessor(foregroundDomain.get());
    180     paint.addColorProcessor(xferProcessor)->unref();
    181     context->drawRect(dst->asRenderTarget(), GrClip::WideOpen(), paint, SkMatrix::I(), srcRect);
    182 
    183     offset->fX = backgroundOffset.fX;
    184     offset->fY = backgroundOffset.fY;
    185     WrapTexture(dst, src.width(), src.height(), result);
    186     return true;
    187 }
    188 
    189 #endif
    190 
    191