aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Mike Reed <reed@google.com>2016-09-26 18:51:24 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2016-09-27 10:33:06 +0000
commit403d154591275b631464854e63c6050adf083686 (patch)
tree1398cb50f863394c3f96b14f703f9a8e0e6c0065 /src
parented924ceffb07d6f383aeb63eab336df4f13df0f1 (diff)
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 <reed@google.com> Commit-Queue: Mike Reed <reed@google.com>
Diffstat (limited to 'src')
-rw-r--r--src/effects/SkArithmeticMode.cpp2
-rw-r--r--src/effects/SkArithmeticModePriv.h42
-rw-r--r--src/effects/SkXfermodeImageFilter.cpp257
-rw-r--r--src/ports/SkGlobalInitialization_default.cpp2
4 files changed, 272 insertions, 31 deletions
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<SkXfermode> 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<SkXfermode> mode, sk_sp<SkImageFilter> 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<GrFragmentProcessor> makeFGFrag(sk_sp<GrFragmentProcessor> bgFP) const;
+#endif
private:
sk_sp<SkXfermode> fMode;
@@ -138,31 +143,33 @@ sk_sp<SkSpecialImage> 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<SkSpecialImage> SkXfermodeImageFilter_Base::filterImageGPU(
paint.addColorFragmentProcessor(std::move(foregroundFP));
- // A null fMode is interpreted to mean kSrcOver_Mode (to match raster).
- SkAutoTUnref<SkXfermode> 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<GrFragmentProcessor> xferFP(
- mode->makeFragmentProcessorForImageFilter(std::move(bgFP)));
+ sk_sp<GrFragmentProcessor> 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<SkSpecialImage> SkXfermodeImageFilter_Base::filterImageGPU(
sk_ref_sp(drawContext->getColorSpace()));
}
+sk_sp<GrFragmentProcessor>
+SkXfermodeImageFilter_Base::makeFGFrag(sk_sp<GrFragmentProcessor> bgFP) const {
+ // A null fMode is interpreted to mean kSrcOver_Mode (to match raster).
+ SkAutoTUnref<SkXfermode> 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<SkImageFilter> 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<GrFragmentProcessor> makeFGFrag(sk_sp<GrFragmentProcessor> 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<SkFlattenable> SkArithmeticImageFilter::CreateProc(SkReadBuffer& buffer) {
+ SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 2);
+
+ // skip the mode (srcover) our parent-class wrote
+ sk_sp<SkXfermode> 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<bool EnforcePMColor> 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<float>(Sk4b::Load(src+i)),
+ d = SkNx_cast<float>(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<uint8_t>(r).store(dst+i);
+ }
+}
+
+// apply mode to src==transparent (0)
+template<bool EnforcePMColor> 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<float>(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<uint8_t>(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<true> : arith_span<false>;
+ 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<true> : arith_transparent<false>;
+ 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<SkImageFilter> SkXfermodeImageFilter::MakeArithmetic(float k1, float k2, float k3, float k4,
+ bool enforcePMColor,
+ sk_sp<SkImageFilter> background,
+ sk_sp<SkImageFilter> 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<SkImageFilter> inputs[2] = { std::move(background), std::move(foreground) };
+ return sk_sp<SkImageFilter>(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"