Index: src/core/Sk4pxXfermode.h |
diff --git a/src/core/Sk4pxXfermode.h b/src/core/Sk4pxXfermode.h |
index 09490dc9904b9eab82652b93ecbbed24d68073fb..b58718304610d7ff2decfa08de4db2e291660887 100644 |
--- a/src/core/Sk4pxXfermode.h |
+++ b/src/core/Sk4pxXfermode.h |
@@ -9,11 +9,13 @@ |
#define Sk4pxXfermode_DEFINED |
#include "Sk4px.h" |
+#include "SkPMFloat.h" |
// This file is possibly included into multiple .cpp files. |
// Each gets its own independent instantiation by wrapping in an anonymous namespace. |
namespace { |
+// Most xfermodes can be done most efficiently 4 pixels at a time in 8 or 16-bit fixed point. |
#define XFERMODE(Name) \ |
struct Name { \ |
static Sk4px Xfer(const Sk4px&, const Sk4px&); \ |
@@ -97,7 +99,48 @@ XFERMODE(Lighten) { |
colors = (sda < dsa).thenElse(dstover, srcover); |
return alphas.zeroColors() + colors.zeroAlphas(); |
} |
+#undef XFERMODE |
+ |
+// Some xfermodes use math like divide or sqrt that's best done in floats 1 pixel at a time. |
+#define XFERMODE(Name) \ |
+ struct Name { \ |
+ static SkPMFloat Xfer(const SkPMFloat&, const SkPMFloat&); \ |
+ static const SkXfermode::Mode kMode = SkXfermode::k##Name##_Mode; \ |
+ }; \ |
+ inline SkPMFloat Name::Xfer(const SkPMFloat& s, const SkPMFloat& d) |
+ |
+XFERMODE(ColorDodge) { |
+ auto sa = s.alphas(), |
+ da = d.alphas(), |
+ isa = Sk4f(1)-sa, |
+ ida = Sk4f(1)-da; |
+ auto srcover = s + d*isa, |
+ dstover = d + s*ida, |
+ otherwise = sa * Sk4f::Min(da, (d*sa)*(sa-s).approxInvert()) + s*ida + d*isa; |
+ |
+ // Order matters here, preferring d==0 over s==sa. |
+ auto colors = (d == Sk4f(0)).thenElse(dstover, |
+ (s == sa).thenElse(srcover, |
+ otherwise)); |
+ return srcover * SkPMFloat(1,0,0,0) + colors * SkPMFloat(0,1,1,1); |
+} |
+XFERMODE(ColorBurn) { |
+ auto sa = s.alphas(), |
+ da = d.alphas(), |
+ isa = Sk4f(1)-sa, |
+ ida = Sk4f(1)-da; |
+ |
+ auto srcover = s + d*isa, |
+ dstover = d + s*ida, |
+ otherwise = sa*(da-Sk4f::Min(da, (da-d)*sa*s.approxInvert())) + s*ida + d*isa; |
+ |
+ // Order matters here, preferring d==da over s==0. |
+ auto colors = (d == da).thenElse(dstover, |
+ (s == Sk4f(0)).thenElse(srcover, |
+ otherwise)); |
+ return srcover * SkPMFloat(1,0,0,0) + colors * SkPMFloat(0,1,1,1); |
+} |
#undef XFERMODE |
// A reasonable fallback mode for doing AA is to simply apply the transfermode first, |
@@ -140,7 +183,34 @@ public: |
} |
private: |
- SkT4pxXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, ProcType::kMode) {} |
+ SkT4pxXfermode(const ProcCoeff& rec) : INHERITED(rec, ProcType::kMode) {} |
+ |
+ typedef SkProcCoeffXfermode INHERITED; |
+}; |
+ |
+template <typename ProcType> |
+class SkTPMFloatXfermode : public SkProcCoeffXfermode { |
+public: |
+ static SkProcCoeffXfermode* Create(const ProcCoeff& rec) { |
+ return SkNEW_ARGS(SkTPMFloatXfermode, (rec)); |
+ } |
+ |
+ void xfer32(SkPMColor dst[], const SkPMColor src[], int n, const SkAlpha aa[]) const override { |
+ for (int i = 0; i < n; i++) { |
+ SkPMFloat s(src[i]), |
+ d(dst[i]), |
+ b(ProcType::Xfer(s,d)); |
+ if (aa) { |
+ // We do aa in full float precision before going back down to bytes, because we can! |
+ SkPMFloat a = Sk4f(aa[i]) * Sk4f(1.0f/255); |
+ b = b*a + d*(Sk4f(1)-a); |
+ } |
+ dst[i] = b.round(); |
+ } |
+ } |
+ |
+private: |
+ SkTPMFloatXfermode(const ProcCoeff& rec) : INHERITED(rec, ProcType::kMode) {} |
typedef SkProcCoeffXfermode INHERITED; |
}; |
@@ -171,6 +241,9 @@ static SkProcCoeffXfermode* SkCreate4pxXfermode(const ProcCoeff& rec, SkXfermode |
case SkXfermode::kOverlay_Mode: return SkT4pxXfermode<Overlay>::Create(rec); |
case SkXfermode::kDarken_Mode: return SkT4pxXfermode<Darken>::Create(rec); |
case SkXfermode::kLighten_Mode: return SkT4pxXfermode<Lighten>::Create(rec); |
+ |
+ case SkXfermode::kColorDodge_Mode: return SkTPMFloatXfermode<ColorDodge>::Create(rec); |
+ case SkXfermode::kColorBurn_Mode: return SkTPMFloatXfermode<ColorBurn>::Create(rec); |
#endif |
default: break; |
} |