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