diff options
author | Mike Klein <mtklein@chromium.org> | 2016-10-04 14:03:27 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2016-10-04 18:33:52 +0000 |
commit | 9161ef012fb22d1b76122cb95dbfddb72f02ef02 (patch) | |
tree | ef8bc78b52f1f17431003ac1f7300b15d96ebb2d | |
parent | 38911a7cb53474575e1cd1cb545902b50ee00889 (diff) |
Make all SkRasterPipeline stages stock stages in SkOpts.
If we want to support VEX-encoded instructions (AVX, F16C, etc.) without a ridiculous slowdown, we need to make sure we're running either all VEX-encoded instructions or all non-VEX-encoded instructions. That means we cannot mix arbitrary user-defined SkRasterPipeline::Fn (never VEX) with those living in SkOpts (maybe VEX)... it's SkOpts or bust.
This ports the existing user-defined SkRasterPipeline::Fn use cases over to use stock stages from SkOpts. I rewrote the unit test to use stock stages, and moved the SkXfermode implementations to SkOpts. The code deleted for SkArithmeticMode_scalar should already be dead.
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2940
CQ_INCLUDE_TRYBOTS=master.client.skia:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD-Trybot
Change-Id: I94dbe766b2d65bfec6e544d260f71d721f0f5cb0
Reviewed-on: https://skia-review.googlesource.com/2940
Commit-Queue: Mike Klein <mtklein@google.com>
Reviewed-by: Mike Reed <reed@google.com>
-rw-r--r-- | src/core/SkOpts.cpp | 50 | ||||
-rw-r--r-- | src/core/SkRasterPipeline.h | 30 | ||||
-rw-r--r-- | src/core/SkXfermode.cpp | 135 | ||||
-rw-r--r-- | src/effects/SkArithmeticMode.cpp | 30 | ||||
-rw-r--r-- | src/opts/SkOpts_sse41.cpp | 31 | ||||
-rw-r--r-- | src/opts/SkRasterPipeline_opts.h | 99 | ||||
-rw-r--r-- | tests/SkRasterPipelineTest.cpp | 80 |
7 files changed, 242 insertions, 213 deletions
diff --git a/src/core/SkOpts.cpp b/src/core/SkOpts.cpp index 4d600797f7..7784e7fcf1 100644 --- a/src/core/SkOpts.cpp +++ b/src/core/SkOpts.cpp @@ -113,7 +113,30 @@ namespace SkOpts { stage_4<SK_OPTS_NS::constant_color, true>, - stage_4<SK_OPTS_NS::srcover, true>, + SK_OPTS_NS::dst, + SK_OPTS_NS::dstatop, + SK_OPTS_NS::dstin, + SK_OPTS_NS::dstout, + SK_OPTS_NS::dstover, + SK_OPTS_NS::srcatop, + SK_OPTS_NS::srcin, + SK_OPTS_NS::srcout, + SK_OPTS_NS::srcover, + SK_OPTS_NS::clear, + SK_OPTS_NS::modulate, + SK_OPTS_NS::multiply, + SK_OPTS_NS::plus_, + SK_OPTS_NS::screen, + SK_OPTS_NS::xor_, + SK_OPTS_NS::colorburn, + SK_OPTS_NS::colordodge, + SK_OPTS_NS::darken, + SK_OPTS_NS::difference, + SK_OPTS_NS::exclusion, + SK_OPTS_NS::hardlight, + SK_OPTS_NS::lighten, + SK_OPTS_NS::overlay, + SK_OPTS_NS::softlight, }; static_assert(SK_ARRAY_COUNT(stages_4) == SkRasterPipeline::kNumStockStages, ""); @@ -138,7 +161,30 @@ namespace SkOpts { stage_1_3<SK_OPTS_NS::constant_color, true>, - stage_1_3<SK_OPTS_NS::srcover, true>, + SK_OPTS_NS::dst, + SK_OPTS_NS::dstatop, + SK_OPTS_NS::dstin, + SK_OPTS_NS::dstout, + SK_OPTS_NS::dstover, + SK_OPTS_NS::srcatop, + SK_OPTS_NS::srcin, + SK_OPTS_NS::srcout, + SK_OPTS_NS::srcover, + SK_OPTS_NS::clear, + SK_OPTS_NS::modulate, + SK_OPTS_NS::multiply, + SK_OPTS_NS::plus_, + SK_OPTS_NS::screen, + SK_OPTS_NS::xor_, + SK_OPTS_NS::colorburn, + SK_OPTS_NS::colordodge, + SK_OPTS_NS::darken, + SK_OPTS_NS::difference, + SK_OPTS_NS::exclusion, + SK_OPTS_NS::hardlight, + SK_OPTS_NS::lighten, + SK_OPTS_NS::overlay, + SK_OPTS_NS::softlight, }; static_assert(SK_ARRAY_COUNT(stages_1_3) == SkRasterPipeline::kNumStockStages, ""); diff --git a/src/core/SkRasterPipeline.h b/src/core/SkRasterPipeline.h index 789257e0e2..785a118ad8 100644 --- a/src/core/SkRasterPipeline.h +++ b/src/core/SkRasterPipeline.h @@ -83,11 +83,6 @@ public: void run(size_t x, size_t n); void run(size_t n) { this->run(0, n); } - // body() will only be called with tail=0, indicating it always works on a full 4 pixels. - // tail() will only be called with tail=1..3 to handle the jagged end of n%4 pixels. - void append(Fn body, Fn tail, const void* ctx = nullptr); - void append(Fn fn, const void* ctx = nullptr) { this->append(fn, fn, ctx); } - enum StockStage { store_565, store_srgb, @@ -109,7 +104,30 @@ public: constant_color, + dst, + dstatop, + dstin, + dstout, + dstover, + srcatop, + srcin, + srcout, srcover, + clear, + modulate, + multiply, + plus_, + screen, + xor_, + colorburn, + colordodge, + darken, + difference, + exclusion, + hardlight, + lighten, + overlay, + softlight, kNumStockStages, }; @@ -122,6 +140,8 @@ public: private: using Stages = SkSTArray<10, Stage, /*MEM_COPY=*/true>; + void append(Fn body, Fn tail, const void*); + // This no-op default makes fBodyStart and fTailStart unconditionally safe to call, // and is always the last stage's fNext as a sort of safety net to make sure even a // buggy pipeline can't walk off its own end. diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp index 2717fab7e9..58c557805b 100644 --- a/src/core/SkXfermode.cpp +++ b/src/core/SkXfermode.cpp @@ -1433,117 +1433,36 @@ SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkXfermode) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkProcCoeffXfermode) SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END -static Sk4f inv(const Sk4f& x) { return 1.0f - x; } - -// Most of these modes apply the same logic kernel to each channel. -template <Sk4f kernel(const Sk4f& s, const Sk4f& sa, const Sk4f& d, const Sk4f& da)> -static void SK_VECTORCALL rgba(SkRasterPipeline::Stage* st, size_t x, size_t tail, - Sk4f r, Sk4f g, Sk4f b, Sk4f a, - Sk4f dr, Sk4f dg, Sk4f db, Sk4f da) { - r = kernel(r,a,dr,da); - g = kernel(g,a,dg,da); - b = kernel(b,a,db,da); - a = kernel(a,a,da,da); - st->next(x,tail, r,g,b,a, dr,dg,db,da); -} - -#define KERNEL(name) static Sk4f name(const Sk4f& s, const Sk4f& sa, const Sk4f& d, const Sk4f& da) -KERNEL(clear) { return 0.0f; } -KERNEL(dst) { return d; } -KERNEL(dstover) { return d + inv(da)*s; } - -KERNEL(srcin) { return s * da; } -KERNEL(srcout) { return s * inv(da); } -KERNEL(srcatop) { return s*da + d*inv(sa); } -KERNEL(dstin) { return srcin (d,da,s,sa); } -KERNEL(dstout) { return srcout (d,da,s,sa); } -KERNEL(dstatop) { return srcatop(d,da,s,sa); } - -KERNEL(modulate) { return s*d; } -KERNEL(multiply) { return s*inv(da) + d*inv(sa) + s*d; } -KERNEL(plus_) { return s + d; } -KERNEL(screen) { return s + d - s*d; } -KERNEL(xor_) { return s*inv(da) + d*inv(sa); } - -// Most of the rest apply the same logic to each color channel, and srcover's logic to alpha. -// (darken and lighten can actually go either way, but they're a little faster this way.) -template <Sk4f kernel(const Sk4f& s, const Sk4f& sa, const Sk4f& d, const Sk4f& da)> -static void SK_VECTORCALL rgb_srcover(SkRasterPipeline::Stage* st, size_t x, size_t tail, - Sk4f r, Sk4f g, Sk4f b, Sk4f a, - Sk4f dr, Sk4f dg, Sk4f db, Sk4f da) { - r = kernel(r,a,dr,da); - g = kernel(g,a,dg,da); - b = kernel(b,a,db,da); - a = a + da*inv(a); - st->next(x,tail, r,g,b,a, dr,dg,db,da); -} - -KERNEL(colorburn) { - return (d == da ).thenElse(d + s*inv(da), - (s == 0.0f).thenElse(s + d*inv(sa), - sa*(da - Sk4f::Min(da, (da-d)*sa/s)) + s*inv(da) + d*inv(sa))); -} -KERNEL(colordodge) { - return (d == 0.0f).thenElse(d + s*inv(da), - (s == sa ).thenElse(s + d*inv(sa), - sa*Sk4f::Min(da, (d*sa)/(sa - s)) + s*inv(da) + d*inv(sa))); -} -KERNEL(darken) { return s + d - Sk4f::Max(s*da, d*sa); } -KERNEL(difference) { return s + d - 2.0f*Sk4f::Min(s*da,d*sa); } -KERNEL(exclusion) { return s + d - 2.0f*s*d; } -KERNEL(hardlight) { - return s*inv(da) + d*inv(sa) - + (2.0f*s <= sa).thenElse(2.0f*s*d, sa*da - 2.0f*(da-d)*(sa-s)); -} -KERNEL(lighten) { return s + d - Sk4f::Min(s*da, d*sa); } -KERNEL(overlay) { return hardlight(d,da,s,sa); } -KERNEL(softlight) { - Sk4f m = (da > 0.0f).thenElse(d / da, 0.0f), - s2 = 2.0f*s, - m4 = 4.0f*m; - - // The logic forks three ways: - // 1. dark src? - // 2. light src, dark dst? - // 3. light src, light dst? - Sk4f darkSrc = d*(sa + (s2 - sa)*(1.0f - m)), // Used in case 1. - darkDst = (m4*m4 + m4)*(m - 1.0f) + 7.0f*m, // Used in case 2. - liteDst = m.rsqrt().invert() - m, // Used in case 3. - liteSrc = d*sa + da*(s2 - sa) * (4.0f*d <= da).thenElse(darkDst, liteDst); // 2 or 3? - return s*inv(da) + d*inv(sa) + (s2 <= sa).thenElse(darkSrc, liteSrc); // 1 or (2 or 3)? -} -#undef KERNEL bool SkProcCoeffXfermode::onAppendStages(SkRasterPipeline* p) const { switch (fMode) { - case kSrcOver_Mode: SkASSERT(false); return false; // Well how did we get here? - - case kSrc_Mode: /*This stage is a no-op.*/ return true; - case kDst_Mode: p->append(rgba<dst>); return true; - case kSrcATop_Mode: p->append(rgba<srcatop>); return true; - case kDstATop_Mode: p->append(rgba<dstatop>); return true; - case kSrcIn_Mode: p->append(rgba<srcin>); return true; - case kDstIn_Mode: p->append(rgba<dstin>); return true; - case kSrcOut_Mode: p->append(rgba<srcout>); return true; - case kDstOut_Mode: p->append(rgba<dstout>); return true; - case kDstOver_Mode: p->append(rgba<dstover>); return true; - - case kClear_Mode: p->append(rgba<clear>); return true; - case kModulate_Mode: p->append(rgba<modulate>); return true; - case kMultiply_Mode: p->append(rgba<multiply>); return true; - case kPlus_Mode: p->append(rgba<plus_>); return true; - case kScreen_Mode: p->append(rgba<screen>); return true; - case kXor_Mode: p->append(rgba<xor_>); return true; - - case kColorBurn_Mode: p->append(rgb_srcover<colorburn>); return true; - case kColorDodge_Mode: p->append(rgb_srcover<colordodge>); return true; - case kDarken_Mode: p->append(rgb_srcover<darken>); return true; - case kDifference_Mode: p->append(rgb_srcover<difference>); return true; - case kExclusion_Mode: p->append(rgb_srcover<exclusion>); return true; - case kHardLight_Mode: p->append(rgb_srcover<hardlight>); return true; - case kLighten_Mode: p->append(rgb_srcover<lighten>); return true; - case kOverlay_Mode: p->append(rgb_srcover<overlay>); return true; - case kSoftLight_Mode: p->append(rgb_srcover<softlight>); return true; + case kSrc_Mode: /*This stage is a no-op.*/ return true; + case kDst_Mode: p->append(SkRasterPipeline::dst); return true; + case kSrcATop_Mode: p->append(SkRasterPipeline::srcatop); return true; + case kDstATop_Mode: p->append(SkRasterPipeline::dstatop); return true; + case kSrcIn_Mode: p->append(SkRasterPipeline::srcin); return true; + case kDstIn_Mode: p->append(SkRasterPipeline::dstin); return true; + case kSrcOut_Mode: p->append(SkRasterPipeline::srcout); return true; + case kDstOut_Mode: p->append(SkRasterPipeline::dstout); return true; + case kSrcOver_Mode: p->append(SkRasterPipeline::srcover); return true; + case kDstOver_Mode: p->append(SkRasterPipeline::dstover); return true; + + case kClear_Mode: p->append(SkRasterPipeline::clear); return true; + case kModulate_Mode: p->append(SkRasterPipeline::modulate); return true; + case kMultiply_Mode: p->append(SkRasterPipeline::multiply); return true; + case kPlus_Mode: p->append(SkRasterPipeline::plus_); return true; + case kScreen_Mode: p->append(SkRasterPipeline::screen); return true; + case kXor_Mode: p->append(SkRasterPipeline::xor_); return true; + + case kColorBurn_Mode: p->append(SkRasterPipeline::colorburn); return true; + case kColorDodge_Mode: p->append(SkRasterPipeline::colordodge); return true; + case kDarken_Mode: p->append(SkRasterPipeline::darken); return true; + case kDifference_Mode: p->append(SkRasterPipeline::difference); return true; + case kExclusion_Mode: p->append(SkRasterPipeline::exclusion); return true; + case kHardLight_Mode: p->append(SkRasterPipeline::hardlight); return true; + case kLighten_Mode: p->append(SkRasterPipeline::lighten); return true; + case kOverlay_Mode: p->append(SkRasterPipeline::overlay); return true; + case kSoftLight_Mode: p->append(SkRasterPipeline::softlight); return true; // TODO case kColor_Mode: return false; diff --git a/src/effects/SkArithmeticMode.cpp b/src/effects/SkArithmeticMode.cpp index e142533b0d..81f29bb9d8 100644 --- a/src/effects/SkArithmeticMode.cpp +++ b/src/effects/SkArithmeticMode.cpp @@ -30,11 +30,6 @@ public: void xfer32(SkPMColor[], const SkPMColor[], int count, const SkAlpha[]) const override; - bool onAppendStages(SkRasterPipeline* p) const override { - p->append(&Stage, this); - return true; - } - SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkArithmeticMode_scalar) @@ -45,10 +40,6 @@ public: #endif private: - static void SK_VECTORCALL Stage(SkRasterPipeline::Stage* st, size_t x, size_t tail, - Sk4f r, Sk4f g, Sk4f b, Sk4f a, - Sk4f dr, Sk4f dg, Sk4f db, Sk4f da); - void flatten(SkWriteBuffer& buffer) const override { buffer.writeScalar(fK[0]); buffer.writeScalar(fK[1]); @@ -74,27 +65,6 @@ sk_sp<SkFlattenable> SkArithmeticMode_scalar::CreateProc(SkReadBuffer& buffer) { return SkArithmeticMode::Make(k1, k2, k3, k4, enforcePMColor); } -void SK_VECTORCALL SkArithmeticMode_scalar::Stage(SkRasterPipeline::Stage* st, - size_t x, size_t tail, - Sk4f r, Sk4f g, Sk4f b, Sk4f a, - Sk4f dr, Sk4f dg, Sk4f db, Sk4f da) { - auto self = st->ctx<const SkArithmeticMode_scalar*>(); - - const Sk4f k1 = self->fK[0], - k2 = self->fK[1], - k3 = self->fK[2], - k4 = self->fK[3]; - - r = k1*r*dr + k2*r + k3*dr + k4; - g = k1*g*dg + k2*g + k3*dg + k4; - b = k1*b*db + k2*b + k3*db + k4; - a = k1*a*da + k2*a + k3*da + k4; - - // A later stage (clamp_01_premul) will pin and fEnforcePMColor for us. - - st->next(x,tail, r,g,b,a, dr,dg,db,da); -} - void SkArithmeticMode_scalar::xfer32(SkPMColor dst[], const SkPMColor src[], int count, const SkAlpha aaCoverage[]) const { const Sk4f k1 = fK[0] * (1/255.0f), diff --git a/src/opts/SkOpts_sse41.cpp b/src/opts/SkOpts_sse41.cpp index 0509b91fdf..3a37834c72 100644 --- a/src/opts/SkOpts_sse41.cpp +++ b/src/opts/SkOpts_sse41.cpp @@ -45,7 +45,36 @@ namespace SkOpts { STAGE(constant_color, true); - STAGE(srcover, true); + #undef STAGE + + #define STAGE(stage) \ + stages_4 [SkRasterPipeline::stage] = SK_OPTS_NS::stage; \ + stages_1_3[SkRasterPipeline::stage] = SK_OPTS_NS::stage + + STAGE(dst); + STAGE(dstatop); + STAGE(dstin); + STAGE(dstout); + STAGE(dstover); + STAGE(srcatop); + STAGE(srcin); + STAGE(srcout); + STAGE(srcover); + STAGE(clear); + STAGE(modulate); + STAGE(multiply); + STAGE(plus_); + STAGE(screen); + STAGE(xor_); + STAGE(colorburn); + STAGE(colordodge); + STAGE(darken); + STAGE(difference); + STAGE(exclusion); + STAGE(hardlight); + STAGE(lighten); + STAGE(overlay); + STAGE(softlight); #undef STAGE } diff --git a/src/opts/SkRasterPipeline_opts.h b/src/opts/SkRasterPipeline_opts.h index a23940ee5d..fdb15b421e 100644 --- a/src/opts/SkRasterPipeline_opts.h +++ b/src/opts/SkRasterPipeline_opts.h @@ -48,6 +48,38 @@ static inline void SK_VECTORCALL stage_1_3(SkRasterPipeline::Stage* st, size_t x } } +// Many xfermodes apply the same logic to each channel. +#define RGBA_XFERMODE_Sk4f(name) \ + static SK_ALWAYS_INLINE Sk4f name##_kernel(const Sk4f& s, const Sk4f& sa, \ + const Sk4f& d, const Sk4f& da); \ + static void SK_VECTORCALL name(SkRasterPipeline::Stage* st, size_t x, size_t tail, \ + Sk4f r, Sk4f g, Sk4f b, Sk4f a, \ + Sk4f dr, Sk4f dg, Sk4f db, Sk4f da) { \ + r = name##_kernel(r,a,dr,da); \ + g = name##_kernel(g,a,dg,da); \ + b = name##_kernel(b,a,db,da); \ + a = name##_kernel(a,a,da,da); \ + st->next(x,tail, r,g,b,a, dr,dg,db,da); \ + } \ + static SK_ALWAYS_INLINE Sk4f name##_kernel(const Sk4f& s, const Sk4f& sa, \ + const Sk4f& d, const Sk4f& da) + +// Most of the rest apply the same logic to color channels and use srcover's alpha logic. +#define RGB_XFERMODE_Sk4f(name) \ + static SK_ALWAYS_INLINE Sk4f name##_kernel(const Sk4f& s, const Sk4f& sa, \ + const Sk4f& d, const Sk4f& da); \ + static void SK_VECTORCALL name(SkRasterPipeline::Stage* st, size_t x, size_t tail, \ + Sk4f r, Sk4f g, Sk4f b, Sk4f a, \ + Sk4f dr, Sk4f dg, Sk4f db, Sk4f da) { \ + r = name##_kernel(r,a,dr,da); \ + g = name##_kernel(g,a,dg,da); \ + b = name##_kernel(b,a,db,da); \ + a = a + (da * (1.0f-a)); \ + st->next(x,tail, r,g,b,a, dr,dg,db,da); \ + } \ + static SK_ALWAYS_INLINE Sk4f name##_kernel(const Sk4f& s, const Sk4f& sa, \ + const Sk4f& d, const Sk4f& da) + namespace SK_OPTS_NS { // Clamp colors into [0,1] premul (e.g. just before storing back to memory). @@ -63,6 +95,8 @@ namespace SK_OPTS_NS { b = Sk4f::Min(b, a); } + static Sk4f inv(const Sk4f& x) { return 1.0f - x; } + static Sk4f lerp(const Sk4f& from, const Sk4f& to, const Sk4f& cov) { return from + (to-from)*cov; } @@ -109,14 +143,6 @@ namespace SK_OPTS_NS { a = color->a(); } - // The default transfer mode is srcover, s' = s + d*(1-sa). - KERNEL_Sk4f(srcover) { - r += dr*(1.0f - a); - g += dg*(1.0f - a); - b += db*(1.0f - a); - a += da*(1.0f - a); - } - // s' = d(1-c) + sc, for a constant c. KERNEL_Sk4f(lerp_constant_float) { Sk4f c = *(const float*)ctx; @@ -270,6 +296,63 @@ namespace SK_OPTS_NS { | Sk4f_round(255.0f * a) << SK_A32_SHIFT), (int*)ptr); } + RGBA_XFERMODE_Sk4f(clear) { return 0.0f; } + //RGBA_XFERMODE_Sk4f(src) { return s; } // This would be a no-op stage, so we just omit it. + RGBA_XFERMODE_Sk4f(dst) { return d; } + + RGBA_XFERMODE_Sk4f(srcatop) { return s*da + d*inv(sa); } + RGBA_XFERMODE_Sk4f(srcin) { return s * da; } + RGBA_XFERMODE_Sk4f(srcout) { return s * inv(da); } + RGBA_XFERMODE_Sk4f(srcover) { return s + inv(sa)*d; } + RGBA_XFERMODE_Sk4f(dstatop) { return srcatop_kernel(d,da,s,sa); } + RGBA_XFERMODE_Sk4f(dstin) { return srcin_kernel (d,da,s,sa); } + RGBA_XFERMODE_Sk4f(dstout) { return srcout_kernel (d,da,s,sa); } + RGBA_XFERMODE_Sk4f(dstover) { return srcover_kernel(d,da,s,sa); } + + RGBA_XFERMODE_Sk4f(modulate) { return s*d; } + RGBA_XFERMODE_Sk4f(multiply) { return s*inv(da) + d*inv(sa) + s*d; } + RGBA_XFERMODE_Sk4f(plus_) { return s + d; } + RGBA_XFERMODE_Sk4f(screen) { return s + d - s*d; } + RGBA_XFERMODE_Sk4f(xor_) { return s*inv(da) + d*inv(sa); } + + RGB_XFERMODE_Sk4f(colorburn) { + return (d == da ).thenElse(d + s*inv(da), + (s == 0.0f).thenElse(s + d*inv(sa), + sa*(da - Sk4f::Min(da, (da-d)*sa/s)) + s*inv(da) + d*inv(sa))); + } + RGB_XFERMODE_Sk4f(colordodge) { + return (d == 0.0f).thenElse(d + s*inv(da), + (s == sa ).thenElse(s + d*inv(sa), + sa*Sk4f::Min(da, (d*sa)/(sa - s)) + s*inv(da) + d*inv(sa))); + } + RGB_XFERMODE_Sk4f(darken) { return s + d - Sk4f::Max(s*da, d*sa); } + RGB_XFERMODE_Sk4f(difference) { return s + d - 2.0f*Sk4f::Min(s*da,d*sa); } + RGB_XFERMODE_Sk4f(exclusion) { return s + d - 2.0f*s*d; } + RGB_XFERMODE_Sk4f(hardlight) { + return s*inv(da) + d*inv(sa) + + (2.0f*s <= sa).thenElse(2.0f*s*d, sa*da - 2.0f*(da-d)*(sa-s)); + } + RGB_XFERMODE_Sk4f(lighten) { return s + d - Sk4f::Min(s*da, d*sa); } + RGB_XFERMODE_Sk4f(overlay) { return hardlight_kernel(d,da,s,sa); } + RGB_XFERMODE_Sk4f(softlight) { + Sk4f m = (da > 0.0f).thenElse(d / da, 0.0f), + s2 = 2.0f*s, + m4 = 4.0f*m; + + // The logic forks three ways: + // 1. dark src? + // 2. light src, dark dst? + // 3. light src, light dst? + Sk4f darkSrc = d*(sa + (s2 - sa)*(1.0f - m)), // Used in case 1. + darkDst = (m4*m4 + m4)*(m - 1.0f) + 7.0f*m, // Used in case 2. + liteDst = m.rsqrt().invert() - m, // Used in case 3. + liteSrc = d*sa + da*(s2 - sa) * (4.0f*d <= da).thenElse(darkDst, liteDst); // 2 or 3? + return s*inv(da) + d*inv(sa) + (s2 <= sa).thenElse(darkSrc, liteSrc); // 1 or (2 or 3)? + } } +#undef KERNEL_Sk4f +#undef RGB_XFERMODE_Sk4f +#undef RGB_XFERMODE_Sk4f + #endif//SkRasterPipeline_opts_DEFINED diff --git a/tests/SkRasterPipelineTest.cpp b/tests/SkRasterPipelineTest.cpp index ccc728e64a..282b2347ef 100644 --- a/tests/SkRasterPipelineTest.cpp +++ b/tests/SkRasterPipelineTest.cpp @@ -6,69 +6,31 @@ */ #include "Test.h" +#include "SkHalf.h" #include "SkRasterPipeline.h" -static void SK_VECTORCALL load(SkRasterPipeline::Stage* st, size_t x, size_t tail, - Sk4f r, Sk4f g, Sk4f b, Sk4f a, - Sk4f dr, Sk4f dg, Sk4f db, Sk4f da) { - auto ptr = st->ctx<const float*>() + x; - switch(tail&3) { - case 0: a = Sk4f{ptr[3]}; - case 3: b = Sk4f{ptr[2]}; - case 2: g = Sk4f{ptr[1]}; - case 1: r = Sk4f{ptr[0]}; - } - st->next(x,tail, r,g,b,a, dr,dg,db,da); -} - -static void SK_VECTORCALL square(SkRasterPipeline::Stage* st, size_t x, size_t tail, - Sk4f r, Sk4f g, Sk4f b, Sk4f a, - Sk4f dr, Sk4f dg, Sk4f db, Sk4f da) { - r *= r; - g *= g; - b *= b; - a *= a; - st->next(x,tail, r,g,b,a, dr,dg,db,da); -} - -static void SK_VECTORCALL store(SkRasterPipeline::Stage* st, size_t x, size_t tail, - Sk4f r, Sk4f g, Sk4f b, Sk4f a, - Sk4f dr, Sk4f dg, Sk4f db, Sk4f da) { - auto ptr = st->ctx<float*>() + x; - switch (tail&3) { - case 0: ptr[3] = a[0]; - case 3: ptr[2] = b[0]; - case 2: ptr[1] = g[0]; - case 1: ptr[0] = r[0]; - } -} - DEF_TEST(SkRasterPipeline, r) { - // We'll build up and run a simple pipeline that exercises the salient - // mechanics of SkRasterPipeline: - // - context pointers (load,store) - // - stages sensitive to the number of pixels (load,store) - // - stages insensitive to the number of pixels (square) - // - stages that chain to the next stage (load,square) - // - stages that terminate the pipeline (store) - // - // This pipeline loads up some values, squares them, then writes them back to memory. + // Build and run a simple pipeline to exercise SkRasterPipeline, + // drawing 50% transparent blue over opaque red in half-floats. - const float src_vals[] = { 1,2,3,4,5 }; - float dst_vals[] = { 0,0,0,0,0 }; + Sk4h red = SkFloatToHalf_finite_ftz({ 1.0f, 0.0f, 0.0f, 1.0f }), + blue = SkFloatToHalf_finite_ftz({ 0.0f, 0.0f, 0.5f, 0.5f }), + result; SkRasterPipeline p; - p.append(load, src_vals); - p.append(square); - p.append(store, dst_vals); - - p.run(5); - - REPORTER_ASSERT(r, dst_vals[0] == 1); - REPORTER_ASSERT(r, dst_vals[1] == 4); - REPORTER_ASSERT(r, dst_vals[2] == 9); - REPORTER_ASSERT(r, dst_vals[3] == 16); - REPORTER_ASSERT(r, dst_vals[4] == 25); + p.append(SkRasterPipeline::load_s_f16, &blue); + p.append(SkRasterPipeline::load_d_f16, &red); + p.append(SkRasterPipeline::srcover); + p.append(SkRasterPipeline::store_f16, &result); + p.run(1); + + Sk4f f = SkHalfToFloat_finite_ftz(result); + + // We should see half-intensity magenta. + REPORTER_ASSERT(r, f[0] == 0.5f); + REPORTER_ASSERT(r, f[1] == 0.0f); + REPORTER_ASSERT(r, f[2] == 0.5f); + REPORTER_ASSERT(r, f[3] == 1.0f); } DEF_TEST(SkRasterPipeline_empty, r) { @@ -79,8 +41,8 @@ DEF_TEST(SkRasterPipeline_empty, r) { DEF_TEST(SkRasterPipeline_nonsense, r) { // No asserts... just a test that this is safe to run and terminates. - // square() always calls st->next(); this makes sure we've always got something there to call. + // srcover() calls st->next(); this makes sure we've always got something there to call. SkRasterPipeline p; - p.append(square); + p.append(SkRasterPipeline::srcover); p.run(20); } |