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 "SkArithmeticImageFilter.h" 10#include "SkArithmeticModePriv.h" 11#include "SkCanvas.h" 12#include "SkColorPriv.h" 13#include "SkReadBuffer.h" 14#include "SkSpecialImage.h" 15#include "SkSpecialSurface.h" 16#include "SkWriteBuffer.h" 17#if SK_SUPPORT_GPU 18#include "GrClip.h" 19#include "GrContext.h" 20#include "GrRenderTargetContext.h" 21#include "GrTextureProxy.h" 22 23#include "effects/GrConstColorProcessor.h" 24#include "effects/GrTextureDomain.h" 25#include "effects/GrSimpleTextureEffect.h" 26#include "SkGr.h" 27#endif 28#include "SkClipOpPriv.h" 29 30class SkXfermodeImageFilter_Base : public SkImageFilter { 31public: 32 SkXfermodeImageFilter_Base(SkBlendMode mode, sk_sp<SkImageFilter> inputs[2], 33 const CropRect* cropRect); 34 35 SK_TO_STRING_OVERRIDE() 36 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkXfermodeImageFilter_Base) 37 38protected: 39 sk_sp<SkSpecialImage> onFilterImage(SkSpecialImage* source, const Context&, 40 SkIPoint* offset) const override; 41 42#if SK_SUPPORT_GPU 43 sk_sp<SkSpecialImage> filterImageGPU(SkSpecialImage* source, 44 sk_sp<SkSpecialImage> background, 45 const SkIPoint& backgroundOffset, 46 sk_sp<SkSpecialImage> foreground, 47 const SkIPoint& foregroundOffset, 48 const SkIRect& bounds, 49 const OutputProperties& outputProperties) const; 50#endif 51 52 void flatten(SkWriteBuffer&) const override; 53 54 void drawForeground(SkCanvas* canvas, SkSpecialImage*, const SkIRect&) const; 55#if SK_SUPPORT_GPU 56 sk_sp<GrFragmentProcessor> makeFGFrag(sk_sp<GrFragmentProcessor> bgFP) const; 57#endif 58 59private: 60 static sk_sp<SkFlattenable> LegacyArithmeticCreateProc(SkReadBuffer& buffer); 61 62 SkBlendMode fMode; 63 64 friend class SkXfermodeImageFilter; 65 66 typedef SkImageFilter INHERITED; 67}; 68 69/////////////////////////////////////////////////////////////////////////////// 70 71sk_sp<SkImageFilter> SkXfermodeImageFilter::Make(SkBlendMode mode, 72 sk_sp<SkImageFilter> background, 73 sk_sp<SkImageFilter> foreground, 74 const SkImageFilter::CropRect* cropRect) { 75 sk_sp<SkImageFilter> inputs[2] = { std::move(background), std::move(foreground) }; 76 return sk_sp<SkImageFilter>(new SkXfermodeImageFilter_Base(mode, inputs, cropRect)); 77} 78 79SkXfermodeImageFilter_Base::SkXfermodeImageFilter_Base(SkBlendMode mode, 80 sk_sp<SkImageFilter> inputs[2], 81 const CropRect* cropRect) 82 : INHERITED(inputs, 2, cropRect) 83 , fMode(mode) 84{} 85 86static int unflatten_blendmode(SkReadBuffer& buffer, SkArithmeticParams* arith) { 87 if (buffer.isVersionLT(SkReadBuffer::kXfermodeToBlendMode_Version)) { 88 sk_sp<SkXfermode> xfer = buffer.readXfermode(); 89 if (xfer) { 90 if (xfer->isArithmetic(arith)) { 91 return -1; 92 } 93 return (int)xfer->blend(); 94 } else { 95 return (int)SkBlendMode::kSrcOver; 96 } 97 } else { 98 uint32_t mode = buffer.read32(); 99 (void)buffer.validate(mode <= (unsigned)SkBlendMode::kLastMode); 100 return mode; 101 } 102} 103 104sk_sp<SkFlattenable> SkXfermodeImageFilter_Base::CreateProc(SkReadBuffer& buffer) { 105 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2); 106 SkArithmeticParams arith; 107 int mode = unflatten_blendmode(buffer, &arith); 108 if (mode >= 0) { 109 return SkXfermodeImageFilter::Make((SkBlendMode)mode, common.getInput(0), 110 common.getInput(1), &common.cropRect()); 111 } else { 112 return SkArithmeticImageFilter::Make(arith.fK[0], arith.fK[1], arith.fK[2], arith.fK[3], 113 arith.fEnforcePMColor, common.getInput(0), 114 common.getInput(1), &common.cropRect()); 115 } 116} 117 118void SkXfermodeImageFilter_Base::flatten(SkWriteBuffer& buffer) const { 119 this->INHERITED::flatten(buffer); 120 buffer.write32((unsigned)fMode); 121} 122 123sk_sp<SkSpecialImage> SkXfermodeImageFilter_Base::onFilterImage(SkSpecialImage* source, 124 const Context& ctx, 125 SkIPoint* offset) const { 126 SkIPoint backgroundOffset = SkIPoint::Make(0, 0); 127 sk_sp<SkSpecialImage> background(this->filterInput(0, source, ctx, &backgroundOffset)); 128 129 SkIPoint foregroundOffset = SkIPoint::Make(0, 0); 130 sk_sp<SkSpecialImage> foreground(this->filterInput(1, source, ctx, &foregroundOffset)); 131 132 SkIRect foregroundBounds = SkIRect::EmptyIRect(); 133 if (foreground) { 134 foregroundBounds = SkIRect::MakeXYWH(foregroundOffset.x(), foregroundOffset.y(), 135 foreground->width(), foreground->height()); 136 } 137 138 SkIRect srcBounds = SkIRect::EmptyIRect(); 139 if (background) { 140 srcBounds = SkIRect::MakeXYWH(backgroundOffset.x(), backgroundOffset.y(), 141 background->width(), background->height()); 142 } 143 144 srcBounds.join(foregroundBounds); 145 if (srcBounds.isEmpty()) { 146 return nullptr; 147 } 148 149 SkIRect bounds; 150 if (!this->applyCropRect(ctx, srcBounds, &bounds)) { 151 return nullptr; 152 } 153 154 offset->fX = bounds.left(); 155 offset->fY = bounds.top(); 156 157#if SK_SUPPORT_GPU 158 if (source->isTextureBacked()) { 159 return this->filterImageGPU(source, 160 background, backgroundOffset, 161 foreground, foregroundOffset, 162 bounds, ctx.outputProperties()); 163 } 164#endif 165 166 sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), bounds.size())); 167 if (!surf) { 168 return nullptr; 169 } 170 171 SkCanvas* canvas = surf->getCanvas(); 172 SkASSERT(canvas); 173 174 canvas->clear(0x0); // can't count on background to fully clear the background 175 canvas->translate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); 176 177 if (background) { 178 SkPaint paint; 179 paint.setBlendMode(SkBlendMode::kSrc); 180 background->draw(canvas, 181 SkIntToScalar(backgroundOffset.fX), SkIntToScalar(backgroundOffset.fY), 182 &paint); 183 } 184 185 this->drawForeground(canvas, foreground.get(), foregroundBounds); 186 187 return surf->makeImageSnapshot(); 188} 189 190void SkXfermodeImageFilter_Base::drawForeground(SkCanvas* canvas, SkSpecialImage* img, 191 const SkIRect& fgBounds) const { 192 SkPaint paint; 193 paint.setBlendMode(fMode); 194 if (img) { 195 img->draw(canvas, SkIntToScalar(fgBounds.fLeft), SkIntToScalar(fgBounds.fTop), &paint); 196 } 197 198 SkAutoCanvasRestore acr(canvas, true); 199 canvas->clipRect(SkRect::Make(fgBounds), kDifference_SkClipOp); 200 paint.setColor(0); 201 canvas->drawPaint(paint); 202} 203 204#ifndef SK_IGNORE_TO_STRING 205void SkXfermodeImageFilter_Base::toString(SkString* str) const { 206 str->appendf("SkXfermodeImageFilter: ("); 207 str->appendf("blendmode: (%d)", (int)fMode); 208 if (this->getInput(0)) { 209 str->appendf("foreground: ("); 210 this->getInput(0)->toString(str); 211 str->appendf(")"); 212 } 213 if (this->getInput(1)) { 214 str->appendf("background: ("); 215 this->getInput(1)->toString(str); 216 str->appendf(")"); 217 } 218 str->append(")"); 219} 220#endif 221 222#if SK_SUPPORT_GPU 223 224#include "SkXfermode_proccoeff.h" 225 226sk_sp<SkSpecialImage> SkXfermodeImageFilter_Base::filterImageGPU( 227 SkSpecialImage* source, 228 sk_sp<SkSpecialImage> background, 229 const SkIPoint& backgroundOffset, 230 sk_sp<SkSpecialImage> foreground, 231 const SkIPoint& foregroundOffset, 232 const SkIRect& bounds, 233 const OutputProperties& outputProperties) const { 234 SkASSERT(source->isTextureBacked()); 235 236 GrContext* context = source->getContext(); 237 238 sk_sp<GrTextureProxy> backgroundProxy, foregroundProxy; 239 240 if (background) { 241 backgroundProxy = background->asTextureProxyRef(context); 242 } 243 244 if (foreground) { 245 foregroundProxy = foreground->asTextureProxyRef(context); 246 } 247 248 GrPaint paint; 249 sk_sp<GrFragmentProcessor> bgFP; 250 251 if (backgroundProxy) { 252 SkMatrix bgMatrix = SkMatrix::MakeTrans(-SkIntToScalar(backgroundOffset.fX), 253 -SkIntToScalar(backgroundOffset.fY)); 254 sk_sp<GrColorSpaceXform> bgXform = GrColorSpaceXform::Make(background->getColorSpace(), 255 outputProperties.colorSpace()); 256 bgFP = GrTextureDomainEffect::Make( 257 context->resourceProvider(), std::move(backgroundProxy), 258 std::move(bgXform), bgMatrix, 259 GrTextureDomain::MakeTexelDomain(background->subset()), 260 GrTextureDomain::kDecal_Mode, 261 GrSamplerParams::kNone_FilterMode); 262 } else { 263 bgFP = GrConstColorProcessor::Make(GrColor4f::TransparentBlack(), 264 GrConstColorProcessor::kIgnore_InputMode); 265 } 266 267 if (foregroundProxy) { 268 SkMatrix fgMatrix = SkMatrix::MakeTrans(-SkIntToScalar(foregroundOffset.fX), 269 -SkIntToScalar(foregroundOffset.fY)); 270 sk_sp<GrColorSpaceXform> fgXform = GrColorSpaceXform::Make(foreground->getColorSpace(), 271 outputProperties.colorSpace()); 272 sk_sp<GrFragmentProcessor> foregroundFP; 273 274 foregroundFP = GrTextureDomainEffect::Make( 275 context->resourceProvider(), std::move(foregroundProxy), 276 std::move(fgXform), fgMatrix, 277 GrTextureDomain::MakeTexelDomain(foreground->subset()), 278 GrTextureDomain::kDecal_Mode, 279 GrSamplerParams::kNone_FilterMode); 280 281 paint.addColorFragmentProcessor(std::move(foregroundFP)); 282 283 sk_sp<GrFragmentProcessor> xferFP = this->makeFGFrag(bgFP); 284 285 // A null 'xferFP' here means kSrc_Mode was used in which case we can just proceed 286 if (xferFP) { 287 paint.addColorFragmentProcessor(std::move(xferFP)); 288 } 289 } else { 290 paint.addColorFragmentProcessor(std::move(bgFP)); 291 } 292 293 paint.setPorterDuffXPFactory(SkBlendMode::kSrc); 294 295 sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext( 296 SkBackingFit::kApprox, bounds.width(), bounds.height(), 297 GrRenderableConfigForColorSpace(outputProperties.colorSpace()), 298 sk_ref_sp(outputProperties.colorSpace()))); 299 if (!renderTargetContext) { 300 return nullptr; 301 } 302 paint.setGammaCorrect(renderTargetContext->isGammaCorrect()); 303 304 SkMatrix matrix; 305 matrix.setTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); 306 renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, matrix, 307 SkRect::Make(bounds)); 308 309 return SkSpecialImage::MakeDeferredFromGpu(context, 310 SkIRect::MakeWH(bounds.width(), bounds.height()), 311 kNeedNewImageUniqueID_SpecialImage, 312 renderTargetContext->asTextureProxyRef(), 313 renderTargetContext->refColorSpace()); 314} 315 316sk_sp<GrFragmentProcessor> 317SkXfermodeImageFilter_Base::makeFGFrag(sk_sp<GrFragmentProcessor> bgFP) const { 318 // A null fMode is interpreted to mean kSrcOver_Mode (to match raster). 319 SkXfermode* xfer = SkXfermode::Peek(fMode); 320 sk_sp<SkXfermode> srcover; 321 if (!xfer) { 322 // It would be awesome to use SkXfermode::Create here but it knows better 323 // than us and won't return a kSrcOver_Mode SkXfermode. That means we 324 // have to get one the hard way. 325 struct ProcCoeff rec; 326 rec.fProc = SkXfermode::GetProc(SkBlendMode::kSrcOver); 327 SkXfermode::ModeAsCoeff(SkBlendMode::kSrcOver, &rec.fSC, &rec.fDC); 328 329 srcover.reset(new SkProcCoeffXfermode(rec, SkBlendMode::kSrcOver)); 330 xfer = srcover.get(); 331 332 } 333 return xfer->makeFragmentProcessorForImageFilter(std::move(bgFP)); 334} 335 336#endif 337/////////////////////////////////////////////////////////////////////////////////////////////////// 338 339sk_sp<SkFlattenable> SkXfermodeImageFilter_Base::LegacyArithmeticCreateProc(SkReadBuffer& buffer) { 340 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2); 341 // skip the unused mode (srcover) field 342 SkDEBUGCODE(int mode =) unflatten_blendmode(buffer, nullptr); 343 if (!buffer.isValid()) { 344 return nullptr; 345 } 346 SkASSERT(SkBlendMode::kSrcOver == (SkBlendMode)mode); 347 float k[4]; 348 for (int i = 0; i < 4; ++i) { 349 k[i] = buffer.readScalar(); 350 } 351 const bool enforcePMColor = buffer.readBool(); 352 return SkArithmeticImageFilter::Make(k[0], k[1], k[2], k[3], enforcePMColor, common.getInput(0), 353 common.getInput(1), &common.cropRect()); 354} 355 356/////////////////////////////////////////////////////////////////////////////////////////////////// 357 358SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkXfermodeImageFilter) 359 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkXfermodeImageFilter_Base) 360 // manually register the legacy serialized name "SkXfermodeImageFilter" 361 SkFlattenable::Register("SkXfermodeImageFilter", SkXfermodeImageFilter_Base::CreateProc, 362 SkFlattenable::kSkImageFilter_Type); 363 // manually register the legacy serialized name "SkArithmeticImageFilter" from when that filter 364 // was implemented as a xfermode image filter. 365 SkFlattenable::Register("SkArithmeticImageFilter", 366 SkXfermodeImageFilter_Base::LegacyArithmeticCreateProc, 367 SkFlattenable::kSkImageFilter_Type); 368SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END 369