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 "include/core/SkBlendMode.h"
9
10 #include "include/core/SkColor.h"
11 #include "include/core/SkPaint.h"
12 #include "src/base/SkVx.h"
13 #include "src/core/SkBlendModePriv.h"
14 #include "src/core/SkColorData.h"
15 #include "src/core/SkRasterPipeline.h"
16 #include "src/core/SkRasterPipelineOpContexts.h"
17 #include "src/core/SkRasterPipelineOpList.h"
18
19 #include <optional>
20
SkBlendMode_ShouldPreScaleCoverage(SkBlendMode mode,bool rgb_coverage)21 bool SkBlendMode_ShouldPreScaleCoverage(SkBlendMode mode, bool rgb_coverage) {
22 // The most important things we do here are:
23 // 1) never pre-scale with rgb coverage if the blend mode involves a source-alpha term;
24 // 2) always pre-scale Plus.
25 //
26 // When we pre-scale with rgb coverage, we scale each of source r,g,b, with a distinct value,
27 // and source alpha with one of those three values. This process destructively updates the
28 // source-alpha term, so we can't evaluate blend modes that need its original value.
29 //
30 // Plus always requires pre-scaling as a specific quirk of its implementation in
31 // SkRasterPipeline. This lets us put the clamp inside the blend mode itself rather
32 // than as a separate stage that'd come after the lerp.
33 //
34 // This function is a finer-grained breakdown of SkBlendMode_SupportsCoverageAsAlpha().
35 switch (mode) {
36 case SkBlendMode::kDst: // d --> no sa term, ok!
37 case SkBlendMode::kDstOver: // d + s*inv(da) --> no sa term, ok!
38 case SkBlendMode::kPlus: // clamp(s+d) --> no sa term, ok!
39 return true;
40
41 case SkBlendMode::kDstOut: // d * inv(sa)
42 case SkBlendMode::kSrcATop: // s*da + d*inv(sa)
43 case SkBlendMode::kSrcOver: // s + d*inv(sa)
44 case SkBlendMode::kXor: // s*inv(da) + d*inv(sa)
45 return !rgb_coverage;
46
47 default: break;
48 }
49 return false;
50 }
51
52 // Users of this function may want to switch to the rgb-coverage aware version above.
SkBlendMode_SupportsCoverageAsAlpha(SkBlendMode mode)53 bool SkBlendMode_SupportsCoverageAsAlpha(SkBlendMode mode) {
54 return SkBlendMode_ShouldPreScaleCoverage(mode, false);
55 }
56
SkBlendMode_AsCoeff(SkBlendMode mode,SkBlendModeCoeff * src,SkBlendModeCoeff * dst)57 bool SkBlendMode_AsCoeff(SkBlendMode mode, SkBlendModeCoeff* src, SkBlendModeCoeff* dst) {
58 struct CoeffRec {
59 SkBlendModeCoeff fSrc;
60 SkBlendModeCoeff fDst;
61 };
62
63 static constexpr CoeffRec kCoeffs[] = {
64 // For Porter-Duff blend functions, color = src * src coeff + dst * dst coeff
65 // src coeff dst coeff blend func
66 // ---------------------- ----------------------- ----------
67 { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kZero }, // clear
68 { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kZero }, // src
69 { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kOne }, // dst
70 { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kISA }, // src-over
71 { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kOne }, // dst-over
72 { SkBlendModeCoeff::kDA, SkBlendModeCoeff::kZero }, // src-in
73 { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kSA }, // dst-in
74 { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kZero }, // src-out
75 { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kISA }, // dst-out
76 { SkBlendModeCoeff::kDA, SkBlendModeCoeff::kISA }, // src-atop
77 { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kSA }, // dst-atop
78 { SkBlendModeCoeff::kIDA, SkBlendModeCoeff::kISA }, // xor
79
80 { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kOne }, // plus
81 { SkBlendModeCoeff::kZero, SkBlendModeCoeff::kSC }, // modulate
82 { SkBlendModeCoeff::kOne, SkBlendModeCoeff::kISC }, // screen
83 };
84
85 if (mode > SkBlendMode::kScreen) {
86 return false;
87 }
88 if (src) {
89 *src = kCoeffs[static_cast<int>(mode)].fSrc;
90 }
91 if (dst) {
92 *dst = kCoeffs[static_cast<int>(mode)].fDst;
93 }
94 return true;
95 }
96
SkBlendMode_AppendStages(SkBlendMode mode,SkRasterPipeline * p)97 void SkBlendMode_AppendStages(SkBlendMode mode, SkRasterPipeline* p) {
98 switch (mode) {
99 case SkBlendMode::kClear: p->append(SkRasterPipelineOp::clear); return;
100 case SkBlendMode::kSrc: return; // This stage is a no-op.
101 case SkBlendMode::kDst: p->append(SkRasterPipelineOp::move_dst_src); return;
102 case SkBlendMode::kSrcOver: p->append(SkRasterPipelineOp::srcover); return;
103 case SkBlendMode::kDstOver: p->append(SkRasterPipelineOp::dstover); return;
104 case SkBlendMode::kSrcIn: p->append(SkRasterPipelineOp::srcin); return;
105 case SkBlendMode::kDstIn: p->append(SkRasterPipelineOp::dstin); return;
106 case SkBlendMode::kSrcOut: p->append(SkRasterPipelineOp::srcout); return;
107 case SkBlendMode::kDstOut: p->append(SkRasterPipelineOp::dstout); return;
108 case SkBlendMode::kSrcATop: p->append(SkRasterPipelineOp::srcatop); return;
109 case SkBlendMode::kDstATop: p->append(SkRasterPipelineOp::dstatop); return;
110 case SkBlendMode::kXor: p->append(SkRasterPipelineOp::xor_); return;
111 case SkBlendMode::kPlus: p->append(SkRasterPipelineOp::plus_); return;
112 case SkBlendMode::kModulate: p->append(SkRasterPipelineOp::modulate); return;
113
114 case SkBlendMode::kScreen: p->append(SkRasterPipelineOp::screen); return;
115 case SkBlendMode::kOverlay: p->append(SkRasterPipelineOp::overlay); return;
116 case SkBlendMode::kDarken: p->append(SkRasterPipelineOp::darken); return;
117 case SkBlendMode::kLighten: p->append(SkRasterPipelineOp::lighten); return;
118 case SkBlendMode::kColorDodge: p->append(SkRasterPipelineOp::colordodge); return;
119 case SkBlendMode::kColorBurn: p->append(SkRasterPipelineOp::colorburn); return;
120 case SkBlendMode::kHardLight: p->append(SkRasterPipelineOp::hardlight); return;
121 case SkBlendMode::kSoftLight: p->append(SkRasterPipelineOp::softlight); return;
122 case SkBlendMode::kDifference: p->append(SkRasterPipelineOp::difference); return;
123 case SkBlendMode::kExclusion: p->append(SkRasterPipelineOp::exclusion); return;
124 case SkBlendMode::kMultiply: p->append(SkRasterPipelineOp::multiply); return;
125
126 case SkBlendMode::kHue: p->append(SkRasterPipelineOp::hue); return;
127 case SkBlendMode::kSaturation: p->append(SkRasterPipelineOp::saturation); return;
128 case SkBlendMode::kColor: p->append(SkRasterPipelineOp::color); return;
129 case SkBlendMode::kLuminosity: p->append(SkRasterPipelineOp::luminosity); return;
130 default: p->append(SkRasterPipelineOp::srcover); return;
131 }
132 SkUNREACHABLE;
133 }
134
SkBlendMode_Apply(SkBlendMode mode,const SkPMColor4f & src,const SkPMColor4f & dst)135 SkPMColor4f SkBlendMode_Apply(SkBlendMode mode, const SkPMColor4f& src, const SkPMColor4f& dst) {
136 // special-case simple/common modes...
137 switch (mode) {
138 case SkBlendMode::kClear: return SK_PMColor4fTRANSPARENT;
139 case SkBlendMode::kSrc: return src;
140 case SkBlendMode::kDst: return dst;
141 case SkBlendMode::kSrcOver: {
142 SkPMColor4f r;
143 (skvx::float4::Load(src.vec()) + skvx::float4::Load(dst.vec()) * (1-src.fA)).store(&r);
144 return r;
145 }
146 default:
147 break;
148 }
149
150 SkRasterPipeline_<256> p;
151 SkPMColor4f src_storage = src,
152 dst_storage = dst,
153 res_storage;
154 SkRasterPipeline_MemoryCtx src_ctx = { &src_storage, 0 },
155 dst_ctx = { &dst_storage, 0 },
156 res_ctx = { &res_storage, 0 };
157
158 p.append(SkRasterPipelineOp::load_f32, &dst_ctx);
159 p.append(SkRasterPipelineOp::move_src_dst);
160 p.append(SkRasterPipelineOp::load_f32, &src_ctx);
161 SkBlendMode_AppendStages(mode, &p);
162 p.append(SkRasterPipelineOp::store_f32, &res_ctx);
163 p.run(0,0, 1,1);
164 return res_storage;
165 }
166
SkBlendMode_Name(SkBlendMode bm)167 const char* SkBlendMode_Name(SkBlendMode bm) {
168 switch (bm) {
169 case SkBlendMode::kClear: return "Clear";
170 case SkBlendMode::kSrc: return "Src";
171 case SkBlendMode::kDst: return "Dst";
172 case SkBlendMode::kSrcOver: return "SrcOver";
173 case SkBlendMode::kDstOver: return "DstOver";
174 case SkBlendMode::kSrcIn: return "SrcIn";
175 case SkBlendMode::kDstIn: return "DstIn";
176 case SkBlendMode::kSrcOut: return "SrcOut";
177 case SkBlendMode::kDstOut: return "DstOut";
178 case SkBlendMode::kSrcATop: return "SrcATop";
179 case SkBlendMode::kDstATop: return "DstATop";
180 case SkBlendMode::kXor: return "Xor";
181 case SkBlendMode::kPlus: return "Plus";
182 case SkBlendMode::kModulate: return "Modulate";
183 case SkBlendMode::kScreen: return "Screen";
184
185 case SkBlendMode::kOverlay: return "Overlay";
186 case SkBlendMode::kDarken: return "Darken";
187 case SkBlendMode::kLighten: return "Lighten";
188 case SkBlendMode::kColorDodge: return "ColorDodge";
189 case SkBlendMode::kColorBurn: return "ColorBurn";
190 case SkBlendMode::kHardLight: return "HardLight";
191 case SkBlendMode::kSoftLight: return "SoftLight";
192 case SkBlendMode::kDifference: return "Difference";
193 case SkBlendMode::kExclusion: return "Exclusion";
194 case SkBlendMode::kMultiply: return "Multiply";
195
196 case SkBlendMode::kHue: return "Hue";
197 case SkBlendMode::kSaturation: return "Saturation";
198 case SkBlendMode::kColor: return "Color";
199 case SkBlendMode::kLuminosity: return "Luminosity";
200 }
201 SkUNREACHABLE;
202 }
203
just_solid_color(const SkPaint & p)204 static bool just_solid_color(const SkPaint& p) {
205 return SK_AlphaOPAQUE == p.getAlpha() && !p.getColorFilter() && !p.getShader();
206 }
207
CheckFastPath(const SkPaint & paint,bool dstIsOpaque)208 SkBlendFastPath CheckFastPath(const SkPaint& paint, bool dstIsOpaque) {
209 const auto bm = paint.asBlendMode();
210 if (!bm) {
211 return SkBlendFastPath::kNormal;
212 }
213 switch (bm.value()) {
214 case SkBlendMode::kSrcOver:
215 return SkBlendFastPath::kSrcOver;
216 case SkBlendMode::kSrc:
217 if (just_solid_color(paint)) {
218 return SkBlendFastPath::kSrcOver;
219 }
220 return SkBlendFastPath::kNormal;
221 case SkBlendMode::kDst:
222 return SkBlendFastPath::kSkipDrawing;
223 case SkBlendMode::kDstOver:
224 if (dstIsOpaque) {
225 return SkBlendFastPath::kSkipDrawing;
226 }
227 return SkBlendFastPath::kNormal;
228 case SkBlendMode::kSrcIn:
229 if (dstIsOpaque && just_solid_color(paint)) {
230 return SkBlendFastPath::kSrcOver;
231 }
232 return SkBlendFastPath::kNormal;
233 case SkBlendMode::kDstIn:
234 if (just_solid_color(paint)) {
235 return SkBlendFastPath::kSkipDrawing;
236 }
237 return SkBlendFastPath::kNormal;
238 default:
239 return SkBlendFastPath::kNormal;
240 }
241 }
242