| /* | |
| * 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 "SkBlendModePriv.h" | |
| #include "SkCoverageModePriv.h" | |
| #include "SkRasterPipeline.h" | |
| #include "../jumper/SkJumper.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); | |
| } | |
| struct CoeffRec { | |
| SkBlendModeCoeff fSrc; | |
| SkBlendModeCoeff fDst; | |
| }; | |
| const CoeffRec gCoeffs[] = { | |
| { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kZero }, | |
| { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kZero }, | |
| { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kOne }, | |
| { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kISA }, | |
| { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kOne }, | |
| { SkBlendModeCoeff::kDA, SkBlendModeCoeff::kZero }, | |
| { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kSA }, | |
| { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kZero }, | |
| { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kISA }, | |
| { SkBlendModeCoeff::kDA, SkBlendModeCoeff::kISA }, | |
| { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kSA }, | |
| { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kISA }, | |
| { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kOne }, | |
| { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kSC }, | |
| { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kISC }, // screen | |
| }; | |
| bool SkBlendMode_AsCoeff(SkBlendMode mode, SkBlendModeCoeff* src, SkBlendModeCoeff* dst) { | |
| if (mode > SkBlendMode::kScreen) { | |
| return false; | |
| } | |
| if (src) { | |
| *src = gCoeffs[static_cast<int>(mode)].fSrc; | |
| } | |
| if (dst) { | |
| *dst = gCoeffs[static_cast<int>(mode)].fDst; | |
| } | |
| return true; | |
| } | |
| void SkBlendMode_AppendStages(SkBlendMode mode, SkRasterPipeline* p) { | |
| auto stage = SkRasterPipeline::srcover; | |
| switch (mode) { | |
| case SkBlendMode::kClear: stage = SkRasterPipeline::clear; break; | |
| case SkBlendMode::kSrc: return; // This stage is a no-op. | |
| case SkBlendMode::kDst: stage = SkRasterPipeline::move_dst_src; break; | |
| case SkBlendMode::kSrcOver: stage = SkRasterPipeline::srcover; break; | |
| case SkBlendMode::kDstOver: stage = SkRasterPipeline::dstover; break; | |
| case SkBlendMode::kSrcIn: stage = SkRasterPipeline::srcin; break; | |
| case SkBlendMode::kDstIn: stage = SkRasterPipeline::dstin; break; | |
| case SkBlendMode::kSrcOut: stage = SkRasterPipeline::srcout; break; | |
| case SkBlendMode::kDstOut: stage = SkRasterPipeline::dstout; break; | |
| case SkBlendMode::kSrcATop: stage = SkRasterPipeline::srcatop; break; | |
| case SkBlendMode::kDstATop: stage = SkRasterPipeline::dstatop; break; | |
| case SkBlendMode::kXor: stage = SkRasterPipeline::xor_; break; | |
| case SkBlendMode::kPlus: stage = SkRasterPipeline::plus_; break; | |
| case SkBlendMode::kModulate: stage = SkRasterPipeline::modulate; break; | |
| case SkBlendMode::kScreen: stage = SkRasterPipeline::screen; break; | |
| case SkBlendMode::kOverlay: stage = SkRasterPipeline::overlay; break; | |
| case SkBlendMode::kDarken: stage = SkRasterPipeline::darken; break; | |
| case SkBlendMode::kLighten: stage = SkRasterPipeline::lighten; break; | |
| case SkBlendMode::kColorDodge: stage = SkRasterPipeline::colordodge; break; | |
| case SkBlendMode::kColorBurn: stage = SkRasterPipeline::colorburn; break; | |
| case SkBlendMode::kHardLight: stage = SkRasterPipeline::hardlight; break; | |
| case SkBlendMode::kSoftLight: stage = SkRasterPipeline::softlight; break; | |
| case SkBlendMode::kDifference: stage = SkRasterPipeline::difference; break; | |
| case SkBlendMode::kExclusion: stage = SkRasterPipeline::exclusion; break; | |
| case SkBlendMode::kMultiply: stage = SkRasterPipeline::multiply; break; | |
| case SkBlendMode::kHue: stage = SkRasterPipeline::hue; break; | |
| case SkBlendMode::kSaturation: stage = SkRasterPipeline::saturation; break; | |
| case SkBlendMode::kColor: stage = SkRasterPipeline::color; break; | |
| case SkBlendMode::kLuminosity: stage = SkRasterPipeline::luminosity; break; | |
| } | |
| p->append(stage); | |
| } | |
| SkPM4f SkBlendMode_Apply(SkBlendMode mode, const SkPM4f& src, const SkPM4f& dst) { | |
| // special-case simple/common modes... | |
| switch (mode) { | |
| case SkBlendMode::kClear: return {{ 0, 0, 0, 0 }}; | |
| case SkBlendMode::kSrc: return src; | |
| case SkBlendMode::kDst: return dst; | |
| case SkBlendMode::kSrcOver: | |
| return SkPM4f::From4f(src.to4f() + dst.to4f() * Sk4f(1 - src.a())); | |
| default: | |
| break; | |
| } | |
| SkRasterPipeline_<256> p; | |
| SkPM4f src_storage = src, | |
| dst_storage = dst, | |
| res_storage; | |
| SkJumper_MemoryCtx src_ctx = { &src_storage, 0 }, | |
| dst_ctx = { &dst_storage, 0 }, | |
| res_ctx = { &res_storage, 0 }; | |
| p.append(SkRasterPipeline::load_f32, &dst_ctx); | |
| p.append(SkRasterPipeline::move_src_dst); | |
| p.append(SkRasterPipeline::load_f32, &src_ctx); | |
| SkBlendMode_AppendStages(mode, &p); | |
| p.append(SkRasterPipeline::store_f32, &res_ctx); | |
| p.run(0,0, 1,1); | |
| return res_storage; | |
| } | |
| /////////////////////////////////////////////////////////////////////////////////////////////////// | |
| const SkBlendMode gUncorrelatedCoverageToBlend[] = { | |
| SkBlendMode::kSrcOver, // or DstOver | |
| SkBlendMode::kSrcIn, // or kDstIn | |
| SkBlendMode::kSrcOut, | |
| SkBlendMode::kDstOut, | |
| SkBlendMode::kXor, | |
| }; | |
| SkBlendMode SkUncorrelatedCoverageModeToBlendMode(SkCoverageMode cm) { | |
| unsigned index = static_cast<unsigned>(cm); | |
| SkASSERT(index < SK_ARRAY_COUNT(gUncorrelatedCoverageToBlend)); | |
| return gUncorrelatedCoverageToBlend[index]; | |
| } |