| 1 | /* |
| 2 | * Copyright 2017 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "src/core/SkBlendModePriv.h" |
| 9 | #include "src/core/SkRasterPipeline.h" |
| 10 | |
| 11 | bool SkBlendMode_ShouldPreScaleCoverage(SkBlendMode mode, bool rgb_coverage) { |
| 12 | // The most important things we do here are: |
| 13 | // 1) never pre-scale with rgb coverage if the blend mode involves a source-alpha term; |
| 14 | // 2) always pre-scale Plus. |
| 15 | // |
| 16 | // When we pre-scale with rgb coverage, we scale each of source r,g,b, with a distinct value, |
| 17 | // and source alpha with one of those three values. This process destructively updates the |
| 18 | // source-alpha term, so we can't evaluate blend modes that need its original value. |
| 19 | // |
| 20 | // Plus always requires pre-scaling as a specific quirk of its implementation in |
| 21 | // SkRasterPipeline. This lets us put the clamp inside the blend mode itself rather |
| 22 | // than as a separate stage that'd come after the lerp. |
| 23 | // |
| 24 | // This function is a finer-grained breakdown of SkBlendMode_SupportsCoverageAsAlpha(). |
| 25 | switch (mode) { |
| 26 | case SkBlendMode::kDst: // d --> no sa term, ok! |
| 27 | case SkBlendMode::kDstOver: // d + s*inv(da) --> no sa term, ok! |
| 28 | case SkBlendMode::kPlus: // clamp(s+d) --> no sa term, ok! |
| 29 | return true; |
| 30 | |
| 31 | case SkBlendMode::kDstOut: // d * inv(sa) |
| 32 | case SkBlendMode::kSrcATop: // s*da + d*inv(sa) |
| 33 | case SkBlendMode::kSrcOver: // s + d*inv(sa) |
| 34 | case SkBlendMode::kXor: // s*inv(da) + d*inv(sa) |
| 35 | return !rgb_coverage; |
| 36 | |
| 37 | default: break; |
| 38 | } |
| 39 | return false; |
| 40 | } |
| 41 | |
| 42 | // Users of this function may want to switch to the rgb-coverage aware version above. |
| 43 | bool SkBlendMode_SupportsCoverageAsAlpha(SkBlendMode mode) { |
| 44 | return SkBlendMode_ShouldPreScaleCoverage(mode, false); |
| 45 | } |
| 46 | |
| 47 | struct CoeffRec { |
| 48 | SkBlendModeCoeff fSrc; |
| 49 | SkBlendModeCoeff fDst; |
| 50 | }; |
| 51 | |
| 52 | const CoeffRec gCoeffs[] = { |
| 53 | // For Porter-Duff blend functions, color = src * src coeff + dst * dst coeff |
| 54 | // src coeff dst coeff blend func |
| 55 | // ---------------------- ----------------------- ---------- |
| 56 | { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kZero }, // clear |
| 57 | { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kZero }, // src |
| 58 | { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kOne }, // dst |
| 59 | { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kISA }, // src-over |
| 60 | { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kOne }, // dst-over |
| 61 | { SkBlendModeCoeff::kDA, SkBlendModeCoeff::kZero }, // src-in |
| 62 | { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kSA }, // dst-in |
| 63 | { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kZero }, // src-out |
| 64 | { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kISA }, // dst-out |
| 65 | { SkBlendModeCoeff::kDA, SkBlendModeCoeff::kISA }, // src-atop |
| 66 | { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kSA }, // dst-atop |
| 67 | { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kISA }, // xor |
| 68 | |
| 69 | { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kOne }, // plus |
| 70 | { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kSC }, // modulate |
| 71 | { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kISC }, // screen |
| 72 | }; |
| 73 | |
| 74 | bool SkBlendMode_AsCoeff(SkBlendMode mode, SkBlendModeCoeff* src, SkBlendModeCoeff* dst) { |
| 75 | if (mode > SkBlendMode::kScreen) { |
| 76 | return false; |
| 77 | } |
| 78 | if (src) { |
| 79 | *src = gCoeffs[static_cast<int>(mode)].fSrc; |
| 80 | } |
| 81 | if (dst) { |
| 82 | *dst = gCoeffs[static_cast<int>(mode)].fDst; |
| 83 | } |
| 84 | return true; |
| 85 | } |
| 86 | |
| 87 | void SkBlendMode_AppendStages(SkBlendMode mode, SkRasterPipeline* p) { |
| 88 | auto stage = SkRasterPipeline::srcover; |
| 89 | switch (mode) { |
| 90 | case SkBlendMode::kClear: stage = SkRasterPipeline::clear; break; |
| 91 | case SkBlendMode::kSrc: return; // This stage is a no-op. |
| 92 | case SkBlendMode::kDst: stage = SkRasterPipeline::move_dst_src; break; |
| 93 | case SkBlendMode::kSrcOver: stage = SkRasterPipeline::srcover; break; |
| 94 | case SkBlendMode::kDstOver: stage = SkRasterPipeline::dstover; break; |
| 95 | case SkBlendMode::kSrcIn: stage = SkRasterPipeline::srcin; break; |
| 96 | case SkBlendMode::kDstIn: stage = SkRasterPipeline::dstin; break; |
| 97 | case SkBlendMode::kSrcOut: stage = SkRasterPipeline::srcout; break; |
| 98 | case SkBlendMode::kDstOut: stage = SkRasterPipeline::dstout; break; |
| 99 | case SkBlendMode::kSrcATop: stage = SkRasterPipeline::srcatop; break; |
| 100 | case SkBlendMode::kDstATop: stage = SkRasterPipeline::dstatop; break; |
| 101 | case SkBlendMode::kXor: stage = SkRasterPipeline::xor_; break; |
| 102 | case SkBlendMode::kPlus: stage = SkRasterPipeline::plus_; break; |
| 103 | case SkBlendMode::kModulate: stage = SkRasterPipeline::modulate; break; |
| 104 | |
| 105 | case SkBlendMode::kScreen: stage = SkRasterPipeline::screen; break; |
| 106 | case SkBlendMode::kOverlay: stage = SkRasterPipeline::overlay; break; |
| 107 | case SkBlendMode::kDarken: stage = SkRasterPipeline::darken; break; |
| 108 | case SkBlendMode::kLighten: stage = SkRasterPipeline::lighten; break; |
| 109 | case SkBlendMode::kColorDodge: stage = SkRasterPipeline::colordodge; break; |
| 110 | case SkBlendMode::kColorBurn: stage = SkRasterPipeline::colorburn; break; |
| 111 | case SkBlendMode::kHardLight: stage = SkRasterPipeline::hardlight; break; |
| 112 | case SkBlendMode::kSoftLight: stage = SkRasterPipeline::softlight; break; |
| 113 | case SkBlendMode::kDifference: stage = SkRasterPipeline::difference; break; |
| 114 | case SkBlendMode::kExclusion: stage = SkRasterPipeline::exclusion; break; |
| 115 | case SkBlendMode::kMultiply: stage = SkRasterPipeline::multiply; break; |
| 116 | |
| 117 | case SkBlendMode::kHue: stage = SkRasterPipeline::hue; break; |
| 118 | case SkBlendMode::kSaturation: stage = SkRasterPipeline::saturation; break; |
| 119 | case SkBlendMode::kColor: stage = SkRasterPipeline::color; break; |
| 120 | case SkBlendMode::kLuminosity: stage = SkRasterPipeline::luminosity; break; |
| 121 | } |
| 122 | p->append(stage); |
| 123 | } |
| 124 | |
| 125 | SkPMColor4f SkBlendMode_Apply(SkBlendMode mode, const SkPMColor4f& src, const SkPMColor4f& dst) { |
| 126 | // special-case simple/common modes... |
| 127 | switch (mode) { |
| 128 | case SkBlendMode::kClear: return SK_PMColor4fTRANSPARENT; |
| 129 | case SkBlendMode::kSrc: return src; |
| 130 | case SkBlendMode::kDst: return dst; |
| 131 | case SkBlendMode::kSrcOver: { |
| 132 | Sk4f r = Sk4f::Load(src.vec()) + Sk4f::Load(dst.vec()) * Sk4f(1 - src.fA); |
| 133 | return { r[0], r[1], r[2], r[3] }; |
| 134 | } |
| 135 | default: |
| 136 | break; |
| 137 | } |
| 138 | |
| 139 | SkRasterPipeline_<256> p; |
| 140 | SkPMColor4f src_storage = src, |
| 141 | dst_storage = dst, |
| 142 | res_storage; |
| 143 | SkRasterPipeline_MemoryCtx src_ctx = { &src_storage, 0 }, |
| 144 | dst_ctx = { &dst_storage, 0 }, |
| 145 | res_ctx = { &res_storage, 0 }; |
| 146 | |
| 147 | p.append(SkRasterPipeline::load_f32, &dst_ctx); |
| 148 | p.append(SkRasterPipeline::move_src_dst); |
| 149 | p.append(SkRasterPipeline::load_f32, &src_ctx); |
| 150 | SkBlendMode_AppendStages(mode, &p); |
| 151 | p.append(SkRasterPipeline::store_f32, &res_ctx); |
| 152 | p.run(0,0, 1,1); |
| 153 | return res_storage; |
| 154 | } |
| 155 | |