xref: /aosp_15_r20/external/skia/src/core/SkBlendMode.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 "include/private/SkColorData.h"
13 #include "src/base/SkVx.h"
14 #include "src/core/SkBlendModePriv.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     auto stage = SkRasterPipelineOp::srcover;
99     switch (mode) {
100         case SkBlendMode::kClear:    stage = SkRasterPipelineOp::clear; break;
101         case SkBlendMode::kSrc:      return;  // This stage is a no-op.
102         case SkBlendMode::kDst:      stage = SkRasterPipelineOp::move_dst_src; break;
103         case SkBlendMode::kSrcOver:  stage = SkRasterPipelineOp::srcover; break;
104         case SkBlendMode::kDstOver:  stage = SkRasterPipelineOp::dstover; break;
105         case SkBlendMode::kSrcIn:    stage = SkRasterPipelineOp::srcin; break;
106         case SkBlendMode::kDstIn:    stage = SkRasterPipelineOp::dstin; break;
107         case SkBlendMode::kSrcOut:   stage = SkRasterPipelineOp::srcout; break;
108         case SkBlendMode::kDstOut:   stage = SkRasterPipelineOp::dstout; break;
109         case SkBlendMode::kSrcATop:  stage = SkRasterPipelineOp::srcatop; break;
110         case SkBlendMode::kDstATop:  stage = SkRasterPipelineOp::dstatop; break;
111         case SkBlendMode::kXor:      stage = SkRasterPipelineOp::xor_; break;
112         case SkBlendMode::kPlus:     stage = SkRasterPipelineOp::plus_; break;
113         case SkBlendMode::kModulate: stage = SkRasterPipelineOp::modulate; break;
114 
115         case SkBlendMode::kScreen:     stage = SkRasterPipelineOp::screen; break;
116         case SkBlendMode::kOverlay:    stage = SkRasterPipelineOp::overlay; break;
117         case SkBlendMode::kDarken:     stage = SkRasterPipelineOp::darken; break;
118         case SkBlendMode::kLighten:    stage = SkRasterPipelineOp::lighten; break;
119         case SkBlendMode::kColorDodge: stage = SkRasterPipelineOp::colordodge; break;
120         case SkBlendMode::kColorBurn:  stage = SkRasterPipelineOp::colorburn; break;
121         case SkBlendMode::kHardLight:  stage = SkRasterPipelineOp::hardlight; break;
122         case SkBlendMode::kSoftLight:  stage = SkRasterPipelineOp::softlight; break;
123         case SkBlendMode::kDifference: stage = SkRasterPipelineOp::difference; break;
124         case SkBlendMode::kExclusion:  stage = SkRasterPipelineOp::exclusion; break;
125         case SkBlendMode::kMultiply:   stage = SkRasterPipelineOp::multiply; break;
126 
127         case SkBlendMode::kHue:        stage = SkRasterPipelineOp::hue; break;
128         case SkBlendMode::kSaturation: stage = SkRasterPipelineOp::saturation; break;
129         case SkBlendMode::kColor:      stage = SkRasterPipelineOp::color; break;
130         case SkBlendMode::kLuminosity: stage = SkRasterPipelineOp::luminosity; break;
131     }
132     p->append(stage);
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