From 403d154591275b631464854e63c6050adf083686 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Mon, 26 Sep 2016 18:51:24 -0400 Subject: Revert "Revert "replace Arithmetic xfermode with imagefilter"" This reverts commit 10ff5bfa789b6b602464e8511fdf676c6f5b1bd4. BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2680 TBR= Change-Id: Iac2f4d48d227ff426ccf02b7eae280f382ad3580 Reviewed-on: https://skia-review.googlesource.com/2680 Reviewed-by: Mike Reed Commit-Queue: Mike Reed --- src/effects/SkArithmeticMode.cpp | 2 +- src/effects/SkArithmeticModePriv.h | 42 +++++ src/effects/SkXfermodeImageFilter.cpp | 257 ++++++++++++++++++++++++--- src/ports/SkGlobalInitialization_default.cpp | 2 +- 4 files changed, 272 insertions(+), 31 deletions(-) create mode 100644 src/effects/SkArithmeticModePriv.h (limited to 'src') diff --git a/src/effects/SkArithmeticMode.cpp b/src/effects/SkArithmeticMode.cpp index b8aa36853a..876f34b430 100644 --- a/src/effects/SkArithmeticMode.cpp +++ b/src/effects/SkArithmeticMode.cpp @@ -5,7 +5,7 @@ * found in the LICENSE file. */ -#include "SkArithmeticMode.h" +#include "SkArithmeticModePriv.h" #include "SkColorPriv.h" #include "SkNx.h" #include "SkRasterPipeline.h" diff --git a/src/effects/SkArithmeticModePriv.h b/src/effects/SkArithmeticModePriv.h new file mode 100644 index 0000000000..04bd90eebe --- /dev/null +++ b/src/effects/SkArithmeticModePriv.h @@ -0,0 +1,42 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkArithmeticModePriv_DEFINED +#define SkArithmeticModePriv_DEFINED + +#include "SkArithmeticMode.h" + +#ifndef SK_SUPPORT_LEGACY_ARITHMETICMODE + +class SK_API SkArithmeticMode { +public: + /** + * result = clamp[k1 * src * dst + k2 * src + k3 * dst + k4] + * + * k1=k2=k3=0, k4=1.0 results in returning opaque white + * k1=k3=k4=0, k2=1.0 results in returning the src + * k1=k2=k4=0, k3=1.0 results in returning the dst + */ + static sk_sp Make(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4, + bool enforcePMColor = true); +#ifdef SK_SUPPORT_LEGACY_XFERMODE_PTR + static SkXfermode* Create(SkScalar k1, SkScalar k2, + SkScalar k3, SkScalar k4, + bool enforcePMColor = true) { + return Make(k1, k2, k3, k4, enforcePMColor).release(); + } +#endif + + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP(); + +private: + SkArithmeticMode(); // can't be instantiated +}; + +#endif + +#endif diff --git a/src/effects/SkXfermodeImageFilter.cpp b/src/effects/SkXfermodeImageFilter.cpp index c602a4a1aa..f832503512 100644 --- a/src/effects/SkXfermodeImageFilter.cpp +++ b/src/effects/SkXfermodeImageFilter.cpp @@ -20,11 +20,12 @@ #include "effects/GrConstColorProcessor.h" #include "effects/GrTextureDomain.h" #include "effects/GrSimpleTextureEffect.h" +#include "SkArithmeticMode_gpu.h" #include "SkGr.h" #include "SkGrPriv.h" #endif -class SK_API SkXfermodeImageFilter_Base : public SkImageFilter { +class SkXfermodeImageFilter_Base : public SkImageFilter { public: SkXfermodeImageFilter_Base(sk_sp mode, sk_sp inputs[2], const CropRect* cropRect); @@ -48,6 +49,10 @@ protected: void flatten(SkWriteBuffer&) const override; + virtual void drawForeground(SkCanvas* canvas, SkSpecialImage*, const SkIRect&) const; +#if SK_SUPPORT_GPU + virtual sk_sp makeFGFrag(sk_sp bgFP) const; +#endif private: sk_sp fMode; @@ -138,31 +143,33 @@ sk_sp SkXfermodeImageFilter_Base::onFilterImage(SkSpecialImage* SkASSERT(canvas); canvas->clear(0x0); // can't count on background to fully clear the background - canvas->translate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); - SkPaint paint; - paint.setXfermodeMode(SkXfermode::kSrc_Mode); - if (background) { + SkPaint paint; + paint.setXfermodeMode(SkXfermode::kSrc_Mode); background->draw(canvas, SkIntToScalar(backgroundOffset.fX), SkIntToScalar(backgroundOffset.fY), &paint); } - paint.setXfermode(fMode); + this->drawForeground(canvas, foreground.get(), foregroundBounds); - if (foreground) { - foreground->draw(canvas, - SkIntToScalar(foregroundOffset.fX), SkIntToScalar(foregroundOffset.fY), - &paint); + return surf->makeImageSnapshot(); +} + +void SkXfermodeImageFilter_Base::drawForeground(SkCanvas* canvas, SkSpecialImage* img, + const SkIRect& fgBounds) const { + SkPaint paint; + paint.setXfermode(fMode); + if (img) { + img->draw(canvas, SkIntToScalar(fgBounds.fLeft), SkIntToScalar(fgBounds.fTop), &paint); } - canvas->clipRect(SkRect::Make(foregroundBounds), SkCanvas::kDifference_Op); - paint.setColor(SK_ColorTRANSPARENT); + SkAutoCanvasRestore acr(canvas, true); + canvas->clipRect(SkRect::Make(fgBounds), SkCanvas::kDifference_Op); + paint.setColor(0); canvas->drawPaint(paint); - - return surf->makeImageSnapshot(); } #ifndef SK_IGNORE_TO_STRING @@ -249,21 +256,7 @@ sk_sp SkXfermodeImageFilter_Base::filterImageGPU( paint.addColorFragmentProcessor(std::move(foregroundFP)); - // A null fMode is interpreted to mean kSrcOver_Mode (to match raster). - SkAutoTUnref mode(SkSafeRef(fMode.get())); - if (!mode) { - // It would be awesome to use SkXfermode::Create here but it knows better - // than us and won't return a kSrcOver_Mode SkXfermode. That means we - // have to get one the hard way. - struct ProcCoeff rec; - rec.fProc = SkXfermode::GetProc(SkXfermode::kSrcOver_Mode); - SkXfermode::ModeAsCoeff(SkXfermode::kSrcOver_Mode, &rec.fSC, &rec.fDC); - - mode.reset(new SkProcCoeffXfermode(rec, SkXfermode::kSrcOver_Mode)); - } - - sk_sp xferFP( - mode->makeFragmentProcessorForImageFilter(std::move(bgFP))); + sk_sp xferFP = this->makeFGFrag(bgFP); // A null 'xferFP' here means kSrc_Mode was used in which case we can just proceed if (xferFP) { @@ -294,13 +287,219 @@ sk_sp SkXfermodeImageFilter_Base::filterImageGPU( sk_ref_sp(drawContext->getColorSpace())); } +sk_sp +SkXfermodeImageFilter_Base::makeFGFrag(sk_sp bgFP) const { + // A null fMode is interpreted to mean kSrcOver_Mode (to match raster). + SkAutoTUnref mode(SkSafeRef(fMode.get())); + if (!mode) { + // It would be awesome to use SkXfermode::Create here but it knows better + // than us and won't return a kSrcOver_Mode SkXfermode. That means we + // have to get one the hard way. + struct ProcCoeff rec; + rec.fProc = SkXfermode::GetProc(SkXfermode::kSrcOver_Mode); + SkXfermode::ModeAsCoeff(SkXfermode::kSrcOver_Mode, &rec.fSC, &rec.fDC); + + mode.reset(new SkProcCoeffXfermode(rec, SkXfermode::kSrcOver_Mode)); + } + return mode->makeFragmentProcessorForImageFilter(std::move(bgFP)); +} + #endif /////////////////////////////////////////////////////////////////////////////////////////////////// +class SkArithmeticImageFilter : public SkXfermodeImageFilter_Base { +public: + SkArithmeticImageFilter(float k1, float k2, float k3, float k4, bool enforcePMColor, + sk_sp inputs[2], const CropRect* cropRect) + : SkXfermodeImageFilter_Base(nullptr, inputs, cropRect) + , fK{ k1, k2, k3, k4 } + , fEnforcePMColor(enforcePMColor) + {} + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkArithmeticImageFilter) + +protected: + void flatten(SkWriteBuffer& buffer) const override { + this->INHERITED::flatten(buffer); + for (int i = 0; i < 4; ++i) { + buffer.writeScalar(fK[i]); + } + buffer.writeBool(fEnforcePMColor); + } + void drawForeground(SkCanvas* canvas, SkSpecialImage*, const SkIRect&) const override; +#if SK_SUPPORT_GPU + sk_sp makeFGFrag(sk_sp bgFP) const override { + return GrArithmeticFP::Make(fK[0], fK[1], fK[2], fK[3], fEnforcePMColor, std::move(bgFP)); + } +#endif + +private: + const float fK[4]; + const bool fEnforcePMColor; + + friend class SkXfermodeImageFilter; + + typedef SkXfermodeImageFilter_Base INHERITED; +}; + +sk_sp SkArithmeticImageFilter::CreateProc(SkReadBuffer& buffer) { + SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2); + + // skip the mode (srcover) our parent-class wrote + sk_sp mode(buffer.readXfermode()); + SkASSERT(nullptr == mode); + + float k[4]; + for (int i = 0; i < 4; ++i) { + k[i] = buffer.readScalar(); + } + const bool enforcePMColor = buffer.readBool(); + return SkXfermodeImageFilter::MakeArithmetic(k[0], k[1], k[2], k[3], enforcePMColor, + common.getInput(0), common.getInput(1), + &common.cropRect()); +} + +#include "SkNx.h" + +static Sk4f pin(float min, const Sk4f& val, float max) { + return Sk4f::Max(min, Sk4f::Min(val, max)); +} + +template void arith_span(const float k[], SkPMColor dst[], + const SkPMColor src[], int count) { + const Sk4f k1 = k[0] * (1/255.0f), + k2 = k[1], + k3 = k[2], + k4 = k[3] * 255.0f + 0.5f; + + for (int i = 0; i < count; i++) { + Sk4f s = SkNx_cast(Sk4b::Load(src+i)), + d = SkNx_cast(Sk4b::Load(dst+i)), + r = pin(0, k1*s*d + k2*s + k3*d + k4, 255); + if (EnforcePMColor) { + Sk4f a = SkNx_shuffle<3,3,3,3>(r); + r = Sk4f::Min(a, r); + } + SkNx_cast(r).store(dst+i); + } +} + +// apply mode to src==transparent (0) +template void arith_transparent(const float k[], SkPMColor dst[], int count) { + const Sk4f k3 = k[2], + k4 = k[3] * 255.0f + 0.5f; + + for (int i = 0; i < count; i++) { + Sk4f d = SkNx_cast(Sk4b::Load(dst+i)), + r = pin(0, k3*d + k4, 255); + if (EnforcePMColor) { + Sk4f a = SkNx_shuffle<3,3,3,3>(r); + r = Sk4f::Min(a, r); + } + SkNx_cast(r).store(dst+i); + } +} + +static bool intersect(SkPixmap* dst, SkPixmap* src, SkIPoint srcOffset) { + SkIRect dstR = SkIRect::MakeWH(dst->width(), dst->height()); + SkIRect srcR = SkIRect::MakeXYWH(srcOffset.x(), srcOffset.y(), src->width(), src->height()); + SkIRect sect; + if (!sect.intersect(dstR, srcR)) { + return false; + } + *dst = SkPixmap(dst->info().makeWH(sect.width(), sect.height()), + dst->addr(sect.fLeft, sect.fTop), + dst->rowBytes()); + *src = SkPixmap(src->info().makeWH(sect.width(), sect.height()), + src->addr(SkTMax(0, -srcOffset.x()), SkTMax(0, -srcOffset.y())), + src->rowBytes()); + return true; +} + +void SkArithmeticImageFilter::drawForeground(SkCanvas* canvas, SkSpecialImage* img, + const SkIRect& fgBounds) const { + SkPixmap dst; + if (!canvas->peekPixels(&dst)) { + return; + } + + if (img) { + SkBitmap srcBM; + SkPixmap src; + if (!img->getROPixels(&srcBM)) { + return; + } + srcBM.lockPixels(); + if (!srcBM.peekPixels(&src)) { + return; + } + + auto proc = fEnforcePMColor ? arith_span : arith_span; + const SkMatrix& ctm = canvas->getTotalMatrix(); + SkASSERT(ctm.getType() <= SkMatrix::kTranslate_Mask); + SkIPoint offset { + fgBounds.fLeft + SkScalarRoundToInt(ctm.getTranslateX()), + fgBounds.fTop + SkScalarRoundToInt(ctm.getTranslateY()), + }; + SkPixmap tmpDst = dst; + if (intersect(&tmpDst, &src, offset)) { + for (int y = 0; y < tmpDst.height(); ++y) { + proc(fK, tmpDst.writable_addr32(0, y), src.addr32(0, y), tmpDst.width()); + } + } + } + + // Now apply the mode with transparent-color to the outside of the fg image + SkRegion outside(SkIRect::MakeWH(dst.width(), dst.height())); + outside.op(fgBounds, SkRegion::kDifference_Op); + auto proc = fEnforcePMColor ? arith_transparent : arith_transparent; + for (SkRegion::Iterator iter(outside); !iter.done(); iter.next()) { + const SkIRect r = iter.rect(); + for (int y = r.fTop; y < r.fBottom; ++y) { + proc(fK, dst.writable_addr32(r.fLeft, y), r.width()); + } + } +} + +sk_sp SkXfermodeImageFilter::MakeArithmetic(float k1, float k2, float k3, float k4, + bool enforcePMColor, + sk_sp background, + sk_sp foreground, + const SkImageFilter::CropRect* crop) { + if (!SkScalarIsFinite(k1) || !SkScalarIsFinite(k2) || + !SkScalarIsFinite(k3) || !SkScalarIsFinite(k4)) { + return nullptr; + } + + // are we nearly some other "std" mode? + int mode = -1; // illegal mode + if (SkScalarNearlyZero(k1) && SkScalarNearlyEqual(k2, SK_Scalar1) && + SkScalarNearlyZero(k3) && SkScalarNearlyZero(k4)) { + mode = SkXfermode::kSrc_Mode; + } else if (SkScalarNearlyZero(k1) && SkScalarNearlyZero(k2) && + SkScalarNearlyEqual(k3, SK_Scalar1) && SkScalarNearlyZero(k4)) { + mode = SkXfermode::kDst_Mode; + } else if (SkScalarNearlyZero(k1) && SkScalarNearlyZero(k2) && + SkScalarNearlyZero(k3) && SkScalarNearlyZero(k4)) { + mode = SkXfermode::kClear_Mode; + } + if (mode >= 0) { + return SkXfermodeImageFilter::Make(SkXfermode::Make((SkXfermode::Mode)mode), + std::move(background), std::move(foreground), crop); + } + + sk_sp inputs[2] = { std::move(background), std::move(foreground) }; + return sk_sp(new SkArithmeticImageFilter(k1, k2, k3, k4, enforcePMColor, + inputs, crop)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkXfermodeImageFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkXfermodeImageFilter_Base) // manually register the legacy serialized name "SkXfermodeImageFilter" SkFlattenable::Register("SkXfermodeImageFilter", SkXfermodeImageFilter_Base::CreateProc, SkFlattenable::kSkImageFilter_Type); + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkArithmeticImageFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp index be41422f16..499e9bfed3 100644 --- a/src/ports/SkGlobalInitialization_default.cpp +++ b/src/ports/SkGlobalInitialization_default.cpp @@ -8,7 +8,7 @@ #include "Sk1DPathEffect.h" #include "Sk2DPathEffect.h" #include "SkAlphaThresholdFilter.h" -#include "SkArithmeticMode.h" +#include "../../src/effects/SkArithmeticModePriv.h" #include "SkArcToPathEffect.h" #include "SkBitmapSourceDeserializer.h" #include "SkBlurDrawLooper.h" -- cgit v1.2.3