| /* |
| * Copyright 2017 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/core/SkBlendModePriv.h" |
| |
| #include "include/private/base/SkVx.h" |
| #include "src/core/SkRasterPipeline.h" |
| |
| bool SkBlendMode_ShouldPreScaleCoverage(SkBlendMode mode, bool rgb_coverage) { |
| // The most important things we do here are: |
| // 1) never pre-scale with rgb coverage if the blend mode involves a source-alpha term; |
| // 2) always pre-scale Plus. |
| // |
| // When we pre-scale with rgb coverage, we scale each of source r,g,b, with a distinct value, |
| // and source alpha with one of those three values. This process destructively updates the |
| // source-alpha term, so we can't evaluate blend modes that need its original value. |
| // |
| // Plus always requires pre-scaling as a specific quirk of its implementation in |
| // SkRasterPipeline. This lets us put the clamp inside the blend mode itself rather |
| // than as a separate stage that'd come after the lerp. |
| // |
| // This function is a finer-grained breakdown of SkBlendMode_SupportsCoverageAsAlpha(). |
| switch (mode) { |
| case SkBlendMode::kDst: // d --> no sa term, ok! |
| case SkBlendMode::kDstOver: // d + s*inv(da) --> no sa term, ok! |
| case SkBlendMode::kPlus: // clamp(s+d) --> no sa term, ok! |
| return true; |
| |
| case SkBlendMode::kDstOut: // d * inv(sa) |
| case SkBlendMode::kSrcATop: // s*da + d*inv(sa) |
| case SkBlendMode::kSrcOver: // s + d*inv(sa) |
| case SkBlendMode::kXor: // s*inv(da) + d*inv(sa) |
| return !rgb_coverage; |
| |
| default: break; |
| } |
| return false; |
| } |
| |
| // Users of this function may want to switch to the rgb-coverage aware version above. |
| bool SkBlendMode_SupportsCoverageAsAlpha(SkBlendMode mode) { |
| return SkBlendMode_ShouldPreScaleCoverage(mode, false); |
| } |
| |
| bool SkBlendMode_AsCoeff(SkBlendMode mode, SkBlendModeCoeff* src, SkBlendModeCoeff* dst) { |
| struct CoeffRec { |
| SkBlendModeCoeff fSrc; |
| SkBlendModeCoeff fDst; |
| }; |
| |
| static constexpr CoeffRec kCoeffs[] = { |
| // For Porter-Duff blend functions, color = src * src coeff + dst * dst coeff |
| // src coeff dst coeff blend func |
| // ---------------------- ----------------------- ---------- |
| { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kZero }, // clear |
| { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kZero }, // src |
| { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kOne }, // dst |
| { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kISA }, // src-over |
| { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kOne }, // dst-over |
| { SkBlendModeCoeff::kDA, SkBlendModeCoeff::kZero }, // src-in |
| { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kSA }, // dst-in |
| { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kZero }, // src-out |
| { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kISA }, // dst-out |
| { SkBlendModeCoeff::kDA, SkBlendModeCoeff::kISA }, // src-atop |
| { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kSA }, // dst-atop |
| { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kISA }, // xor |
| |
| { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kOne }, // plus |
| { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kSC }, // modulate |
| { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kISC }, // screen |
| }; |
| |
| if (mode > SkBlendMode::kScreen) { |
| return false; |
| } |
| if (src) { |
| *src = kCoeffs[static_cast<int>(mode)].fSrc; |
| } |
| if (dst) { |
| *dst = kCoeffs[static_cast<int>(mode)].fDst; |
| } |
| return true; |
| } |
| |
| void SkBlendMode_AppendStages(SkBlendMode mode, SkRasterPipeline* p) { |
| auto stage = SkRasterPipelineOp::srcover; |
| switch (mode) { |
| case SkBlendMode::kClear: stage = SkRasterPipelineOp::clear; break; |
| case SkBlendMode::kSrc: return; // This stage is a no-op. |
| case SkBlendMode::kDst: stage = SkRasterPipelineOp::move_dst_src; break; |
| case SkBlendMode::kSrcOver: stage = SkRasterPipelineOp::srcover; break; |
| case SkBlendMode::kDstOver: stage = SkRasterPipelineOp::dstover; break; |
| case SkBlendMode::kSrcIn: stage = SkRasterPipelineOp::srcin; break; |
| case SkBlendMode::kDstIn: stage = SkRasterPipelineOp::dstin; break; |
| case SkBlendMode::kSrcOut: stage = SkRasterPipelineOp::srcout; break; |
| case SkBlendMode::kDstOut: stage = SkRasterPipelineOp::dstout; break; |
| case SkBlendMode::kSrcATop: stage = SkRasterPipelineOp::srcatop; break; |
| case SkBlendMode::kDstATop: stage = SkRasterPipelineOp::dstatop; break; |
| case SkBlendMode::kXor: stage = SkRasterPipelineOp::xor_; break; |
| case SkBlendMode::kPlus: stage = SkRasterPipelineOp::plus_; break; |
| case SkBlendMode::kModulate: stage = SkRasterPipelineOp::modulate; break; |
| |
| case SkBlendMode::kScreen: stage = SkRasterPipelineOp::screen; break; |
| case SkBlendMode::kOverlay: stage = SkRasterPipelineOp::overlay; break; |
| case SkBlendMode::kDarken: stage = SkRasterPipelineOp::darken; break; |
| case SkBlendMode::kLighten: stage = SkRasterPipelineOp::lighten; break; |
| case SkBlendMode::kColorDodge: stage = SkRasterPipelineOp::colordodge; break; |
| case SkBlendMode::kColorBurn: stage = SkRasterPipelineOp::colorburn; break; |
| case SkBlendMode::kHardLight: stage = SkRasterPipelineOp::hardlight; break; |
| case SkBlendMode::kSoftLight: stage = SkRasterPipelineOp::softlight; break; |
| case SkBlendMode::kDifference: stage = SkRasterPipelineOp::difference; break; |
| case SkBlendMode::kExclusion: stage = SkRasterPipelineOp::exclusion; break; |
| case SkBlendMode::kMultiply: stage = SkRasterPipelineOp::multiply; break; |
| |
| case SkBlendMode::kHue: stage = SkRasterPipelineOp::hue; break; |
| case SkBlendMode::kSaturation: stage = SkRasterPipelineOp::saturation; break; |
| case SkBlendMode::kColor: stage = SkRasterPipelineOp::color; break; |
| case SkBlendMode::kLuminosity: stage = SkRasterPipelineOp::luminosity; break; |
| } |
| p->append(stage); |
| } |
| |
| SkPMColor4f SkBlendMode_Apply(SkBlendMode mode, const SkPMColor4f& src, const SkPMColor4f& dst) { |
| // special-case simple/common modes... |
| switch (mode) { |
| case SkBlendMode::kClear: return SK_PMColor4fTRANSPARENT; |
| case SkBlendMode::kSrc: return src; |
| case SkBlendMode::kDst: return dst; |
| case SkBlendMode::kSrcOver: { |
| SkPMColor4f r; |
| (skvx::float4::Load(src.vec()) + skvx::float4::Load(dst.vec()) * (1-src.fA)).store(&r); |
| return r; |
| } |
| default: |
| break; |
| } |
| |
| SkRasterPipeline_<256> p; |
| SkPMColor4f src_storage = src, |
| dst_storage = dst, |
| res_storage; |
| SkRasterPipeline_MemoryCtx src_ctx = { &src_storage, 0 }, |
| dst_ctx = { &dst_storage, 0 }, |
| res_ctx = { &res_storage, 0 }; |
| |
| p.append(SkRasterPipelineOp::load_f32, &dst_ctx); |
| p.append(SkRasterPipelineOp::move_src_dst); |
| p.append(SkRasterPipelineOp::load_f32, &src_ctx); |
| SkBlendMode_AppendStages(mode, &p); |
| p.append(SkRasterPipelineOp::store_f32, &res_ctx); |
| p.run(0,0, 1,1); |
| return res_storage; |
| } |