• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 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/SkColor.h"
9 #include "include/core/SkPaint.h"
10 #include "include/core/SkPixmap.h"
11 #include "include/core/SkShader.h"
12 #include "include/private/base/SkTo.h"
13 #include "src/base/SkArenaAlloc.h"
14 #include "src/base/SkUtils.h"
15 #include "src/core/SkBlendModePriv.h"
16 #include "src/core/SkBlitter.h"
17 #include "src/core/SkColorFilterBase.h"
18 #include "src/core/SkColorSpacePriv.h"
19 #include "src/core/SkColorSpaceXformSteps.h"
20 #include "src/core/SkMatrixProvider.h"
21 #include "src/core/SkOpts.h"
22 #include "src/core/SkRasterPipeline.h"
23 #include "src/shaders/SkShaderBase.h"
24 
25 #define SK_BLITTER_TRACE_IS_RASTER_PIPELINE
26 #include "src/utils/SkBlitterTrace.h"
27 
28 class SkRasterPipelineBlitter final : public SkBlitter {
29 public:
30     // This is our common entrypoint for creating the blitter once we've sorted out shaders.
31     static SkBlitter* Create(const SkPixmap&, const SkPaint&, SkArenaAlloc*,
32                              const SkRasterPipeline& shaderPipeline,
33                              bool is_opaque, bool is_constant,
34                              sk_sp<SkShader> clipShader);
35 
SkRasterPipelineBlitter(SkPixmap dst,SkBlendMode blend,SkArenaAlloc * alloc)36     SkRasterPipelineBlitter(SkPixmap dst,
37                             SkBlendMode blend,
38                             SkArenaAlloc* alloc)
39         : fDst(dst)
40         , fBlend(blend)
41         , fAlloc(alloc)
42         , fColorPipeline(alloc)
43     {}
44 
45     void blitH     (int x, int y, int w)                            override;
46     void blitAntiH (int x, int y, const SkAlpha[], const int16_t[]) override;
47     void blitAntiH2(int x, int y, U8CPU a0, U8CPU a1)               override;
48     void blitAntiV2(int x, int y, U8CPU a0, U8CPU a1)               override;
49     void blitMask  (const SkMask&, const SkIRect& clip)             override;
50     void blitRect  (int x, int y, int width, int height)            override;
51     void blitV     (int x, int y, int height, SkAlpha alpha)        override;
52 
53 private:
54     void blitRectWithTrace(int x, int y, int w, int h, bool trace);
55     void append_load_dst      (SkRasterPipeline*) const;
56     void append_store         (SkRasterPipeline*) const;
57 
58     // these check internally, and only append if there was a native clipShader
59     void append_clip_scale    (SkRasterPipeline*) const;
60     void append_clip_lerp     (SkRasterPipeline*) const;
61 
62     SkPixmap               fDst;
63     SkBlendMode            fBlend;
64     SkArenaAlloc*          fAlloc;
65     SkRasterPipeline       fColorPipeline;
66     // set to pipeline storage (for alpha) if we have a clipShader
67     void*                  fClipShaderBuffer = nullptr; // "native" : float or U16
68 
69     SkRasterPipeline_MemoryCtx
70         fDstPtr       = {nullptr,0},  // Always points to the top-left of fDst.
71         fMaskPtr      = {nullptr,0};  // Updated each call to blitMask().
72     SkRasterPipeline_EmbossCtx fEmbossCtx;  // Used only for k3D_Format masks.
73 
74     // We may be able to specialize blitH() or blitRect() into a memset.
75     void   (*fMemset2D)(SkPixmap*, int x,int y, int w,int h, uint64_t color) = nullptr;
76     uint64_t fMemsetColor = 0;   // Big enough for largest memsettable dst format, F16.
77 
78     // Built lazily on first use.
79     std::function<void(size_t, size_t, size_t, size_t)> fBlitRect,
80                                                         fBlitAntiH,
81                                                         fBlitMaskA8,
82                                                         fBlitMaskLCD16,
83                                                         fBlitMask3D;
84 
85     // These values are pointed to by the blit pipelines above,
86     // which allows us to adjust them from call to call.
87     float fCurrentCoverage = 0.0f;
88     float fDitherRate      = 0.0f;
89 
90     using INHERITED = SkBlitter;
91 };
92 
SkCreateRasterPipelineBlitter(const SkPixmap & dst,const SkPaint & paint,const SkMatrix & ctm,SkArenaAlloc * alloc,sk_sp<SkShader> clipShader,const SkSurfaceProps & props)93 SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
94                                          const SkPaint& paint,
95                                          const SkMatrix& ctm,
96                                          SkArenaAlloc* alloc,
97                                          sk_sp<SkShader> clipShader,
98                                          const SkSurfaceProps& props) {
99     if (!paint.asBlendMode()) {
100         // The raster pipeline doesn't support SkBlender.
101         return nullptr;
102     }
103 
104     SkColorSpace* dstCS = dst.colorSpace();
105     SkColorType dstCT = dst.colorType();
106     SkColor4f paintColor = paint.getColor4f();
107     SkColorSpaceXformSteps(sk_srgb_singleton(), kUnpremul_SkAlphaType,
108                            dstCS,               kUnpremul_SkAlphaType).apply(paintColor.vec());
109 
110     auto shader = as_SB(paint.getShader());
111 
112     SkRasterPipeline_<256> shaderPipeline;
113     if (!shader) {
114         // Having no shader makes things nice and easy... just use the paint color.
115         shaderPipeline.append_constant_color(alloc, paintColor.premul().vec());
116         bool is_opaque    = paintColor.fA == 1.0f,
117              is_constant  = true;
118         return SkRasterPipelineBlitter::Create(dst, paint, alloc,
119                                                shaderPipeline, is_opaque, is_constant,
120                                                std::move(clipShader));
121     }
122 
123     bool is_opaque    = shader->isOpaque() && paintColor.fA == 1.0f;
124     bool is_constant  = shader->isConstant();
125 
126     if (shader->appendRootStages({&shaderPipeline, alloc, dstCT, dstCS, paint, props}, ctm)) {
127         if (paintColor.fA != 1.0f) {
128             shaderPipeline.append(SkRasterPipelineOp::scale_1_float,
129                                   alloc->make<float>(paintColor.fA));
130         }
131         return SkRasterPipelineBlitter::Create(dst, paint, alloc,
132                                                shaderPipeline, is_opaque, is_constant,
133                                                std::move(clipShader));
134     }
135 
136     // The shader can't draw with SkRasterPipeline.
137     return nullptr;
138 }
139 
SkCreateRasterPipelineBlitter(const SkPixmap & dst,const SkPaint & paint,const SkRasterPipeline & shaderPipeline,bool is_opaque,SkArenaAlloc * alloc,sk_sp<SkShader> clipShader)140 SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
141                                          const SkPaint& paint,
142                                          const SkRasterPipeline& shaderPipeline,
143                                          bool is_opaque,
144                                          SkArenaAlloc* alloc,
145                                          sk_sp<SkShader> clipShader) {
146     bool is_constant = false;  // If this were the case, it'd be better to just set a paint color.
147     return SkRasterPipelineBlitter::Create(dst, paint, alloc,
148                                            shaderPipeline, is_opaque, is_constant,
149                                            clipShader);
150 }
151 
Create(const SkPixmap & dst,const SkPaint & paint,SkArenaAlloc * alloc,const SkRasterPipeline & shaderPipeline,bool is_opaque,bool is_constant,sk_sp<SkShader> clipShader)152 SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
153                                            const SkPaint& paint,
154                                            SkArenaAlloc* alloc,
155                                            const SkRasterPipeline& shaderPipeline,
156                                            bool is_opaque,
157                                            bool is_constant,
158                                            sk_sp<SkShader> clipShader) {
159     const auto bm = paint.asBlendMode();
160     if (!bm) {
161         return nullptr;
162     }
163 
164     auto blitter = alloc->make<SkRasterPipelineBlitter>(dst, bm.value(), alloc);
165 
166     // Our job in this factory is to fill out the blitter's color pipeline.
167     // This is the common front of the full blit pipelines, each constructed lazily on first use.
168     // The full blit pipelines handle reading and writing the dst, blending, coverage, dithering.
169     auto colorPipeline = &blitter->fColorPipeline;
170 
171     if (clipShader) {
172         auto clipP = colorPipeline;
173         SkPaint clipPaint;  // just need default values
174         SkColorType clipCT = kRGBA_8888_SkColorType;
175         SkColorSpace* clipCS = nullptr;
176         SkSurfaceProps props{}; // default OK; clipShader doesn't render text
177         SkStageRec rec = {clipP, alloc, clipCT, clipCS, clipPaint, props};
178         if (as_SB(clipShader)->appendRootStages(rec, SkMatrix::I())) {
179             struct Storage {
180                 // large enough for highp (float) or lowp(U16)
181                 float   fA[SkRasterPipeline_kMaxStride];
182             };
183             auto storage = alloc->make<Storage>();
184             clipP->append(SkRasterPipelineOp::store_src_a, storage->fA);
185             blitter->fClipShaderBuffer = storage->fA;
186             is_constant = false;
187         } else {
188             return nullptr;
189         }
190     }
191 
192     // Let's get the shader in first.
193     colorPipeline->extend(shaderPipeline);
194 
195     // If there's a color filter it comes next.
196     if (auto colorFilter = paint.getColorFilter()) {
197         SkSurfaceProps props{}; // default OK; colorFilter doesn't render text
198         SkStageRec rec = {colorPipeline, alloc, dst.colorType(), dst.colorSpace(), paint, props};
199         if (!as_CFB(colorFilter)->appendStages(rec, is_opaque)) {
200             return nullptr;
201         }
202         is_opaque = is_opaque && as_CFB(colorFilter)->isAlphaUnchanged();
203     }
204 
205     // Not all formats make sense to dither (think, F16).  We set their dither rate
206     // to zero.  We only dither non-constant shaders, so is_constant won't change here.
207     if (paint.isDither() && !is_constant) {
208         switch (dst.info().colorType()) {
209             case kARGB_4444_SkColorType:
210                 blitter->fDitherRate = 1 / 15.0f;
211                 break;
212             case kRGB_565_SkColorType:
213                 blitter->fDitherRate = 1 / 63.0f;
214                 break;
215             case kGray_8_SkColorType:
216             case kRGB_888x_SkColorType:
217             case kRGBA_8888_SkColorType:
218             case kBGRA_8888_SkColorType:
219             case kSRGBA_8888_SkColorType:
220             case kR8_unorm_SkColorType:
221                 blitter->fDitherRate = 1 / 255.0f;
222                 break;
223             case kRGB_101010x_SkColorType:
224             case kRGBA_1010102_SkColorType:
225             case kBGR_101010x_SkColorType:
226             case kBGRA_1010102_SkColorType:
227                 blitter->fDitherRate = 1 / 1023.0f;
228                 break;
229 
230             case kUnknown_SkColorType:
231             case kAlpha_8_SkColorType:
232             case kBGR_101010x_XR_SkColorType:
233             case kRGBA_F16_SkColorType:
234             case kRGBA_F16Norm_SkColorType:
235             case kRGBA_F32_SkColorType:
236             case kR8G8_unorm_SkColorType:
237             case kA16_float_SkColorType:
238             case kA16_unorm_SkColorType:
239             case kR16G16_float_SkColorType:
240             case kR16G16_unorm_SkColorType:
241             case kR16G16B16A16_unorm_SkColorType:
242                 blitter->fDitherRate = 0.0f;
243                 break;
244         }
245         if (blitter->fDitherRate > 0.0f) {
246             colorPipeline->append(SkRasterPipelineOp::dither, &blitter->fDitherRate);
247         }
248     }
249 
250     // We're logically done here.  The code between here and return blitter is all optimization.
251 
252     // A pipeline that's still constant here can collapse back into a constant color.
253     if (is_constant) {
254         SkColor4f constantColor;
255         SkRasterPipeline_MemoryCtx constantColorPtr = { &constantColor, 0 };
256         // We could remove this clamp entirely, but if the destination is 8888, doing the clamp
257         // here allows the color pipeline to still run in lowp (we'll use uniform_color, rather than
258         // unbounded_uniform_color).
259         colorPipeline->append_clamp_if_normalized(dst.info());
260         colorPipeline->append(SkRasterPipelineOp::store_f32, &constantColorPtr);
261         colorPipeline->run(0,0,1,1);
262         colorPipeline->reset();
263         colorPipeline->append_constant_color(alloc, constantColor);
264 
265         is_opaque = constantColor.fA == 1.0f;
266     }
267 
268     // We can strength-reduce SrcOver into Src when opaque.
269     if (is_opaque && blitter->fBlend == SkBlendMode::kSrcOver) {
270         blitter->fBlend = SkBlendMode::kSrc;
271     }
272 
273     // When we're drawing a constant color in Src mode, we can sometimes just memset.
274     // (The previous two optimizations help find more opportunities for this one.)
275     if (is_constant && blitter->fBlend == SkBlendMode::kSrc) {
276         // Run our color pipeline all the way through to produce what we'd memset when we can.
277         // Not all blits can memset, so we need to keep colorPipeline too.
278         SkRasterPipeline_<256> p;
279         p.extend(*colorPipeline);
280         blitter->fDstPtr = SkRasterPipeline_MemoryCtx{&blitter->fMemsetColor, 0};
281         blitter->append_store(&p);
282         p.run(0,0,1,1);
283 
284         switch (blitter->fDst.shiftPerPixel()) {
285             case 0: blitter->fMemset2D = [](SkPixmap* dst, int x,int y, int w,int h, uint64_t c) {
286                 void* p = dst->writable_addr(x,y);
287                 while (h --> 0) {
288                     memset(p, c, w);
289                     p = SkTAddOffset<void>(p, dst->rowBytes());
290                 }
291             }; break;
292 
293             case 1: blitter->fMemset2D = [](SkPixmap* dst, int x,int y, int w,int h, uint64_t c) {
294                 SkOpts::rect_memset16(dst->writable_addr16(x,y), c, w, dst->rowBytes(), h);
295             }; break;
296 
297             case 2: blitter->fMemset2D = [](SkPixmap* dst, int x,int y, int w,int h, uint64_t c) {
298                 SkOpts::rect_memset32(dst->writable_addr32(x,y), c, w, dst->rowBytes(), h);
299             }; break;
300 
301             case 3: blitter->fMemset2D = [](SkPixmap* dst, int x,int y, int w,int h, uint64_t c) {
302                 SkOpts::rect_memset64(dst->writable_addr64(x,y), c, w, dst->rowBytes(), h);
303             }; break;
304 
305             // TODO(F32)?
306         }
307     }
308 
309     blitter->fDstPtr = SkRasterPipeline_MemoryCtx{
310         blitter->fDst.writable_addr(),
311         blitter->fDst.rowBytesAsPixels(),
312     };
313 
314     return blitter;
315 }
316 
append_load_dst(SkRasterPipeline * p) const317 void SkRasterPipelineBlitter::append_load_dst(SkRasterPipeline* p) const {
318     p->append_load_dst(fDst.info().colorType(), &fDstPtr);
319     if (fDst.info().alphaType() == kUnpremul_SkAlphaType) {
320         p->append(SkRasterPipelineOp::premul_dst);
321     }
322 }
323 
append_store(SkRasterPipeline * p) const324 void SkRasterPipelineBlitter::append_store(SkRasterPipeline* p) const {
325     if (fDst.info().alphaType() == kUnpremul_SkAlphaType) {
326         p->append(SkRasterPipelineOp::unpremul);
327     }
328     p->append_store(fDst.info().colorType(), &fDstPtr);
329 }
330 
append_clip_scale(SkRasterPipeline * p) const331 void SkRasterPipelineBlitter::append_clip_scale(SkRasterPipeline* p) const {
332     if (fClipShaderBuffer) {
333         p->append(SkRasterPipelineOp::scale_native, fClipShaderBuffer);
334     }
335 }
336 
append_clip_lerp(SkRasterPipeline * p) const337 void SkRasterPipelineBlitter::append_clip_lerp(SkRasterPipeline* p) const {
338     if (fClipShaderBuffer) {
339         p->append(SkRasterPipelineOp::lerp_native, fClipShaderBuffer);
340     }
341 }
342 
blitH(int x,int y,int w)343 void SkRasterPipelineBlitter::blitH(int x, int y, int w) {
344     this->blitRect(x,y,w,1);
345 }
346 
blitRect(int x,int y,int w,int h)347 void SkRasterPipelineBlitter::blitRect(int x, int y, int w, int h) {
348     this->blitRectWithTrace(x, y, w, h, true);
349 }
350 
blitRectWithTrace(int x,int y,int w,int h,bool trace)351 void SkRasterPipelineBlitter::blitRectWithTrace(int x, int y, int w, int h, bool trace) {
352     if (fMemset2D) {
353         SK_BLITTER_TRACE_STEP(blitRectByMemset,
354                            trace,
355                            /*scanlines=*/h,
356                            /*pixels=*/w * h);
357         fMemset2D(&fDst, x,y, w,h, fMemsetColor);
358         return;
359     }
360 
361     if (!fBlitRect) {
362         SkRasterPipeline p(fAlloc);
363         p.extend(fColorPipeline);
364         p.append_clamp_if_normalized(fDst.info());
365         if (fBlend == SkBlendMode::kSrcOver
366                 && (fDst.info().colorType() == kRGBA_8888_SkColorType ||
367                     fDst.info().colorType() == kBGRA_8888_SkColorType)
368                 && !fDst.colorSpace()
369                 && fDst.info().alphaType() != kUnpremul_SkAlphaType
370                 && fDitherRate == 0.0f) {
371             if (fDst.info().colorType() == kBGRA_8888_SkColorType) {
372                 p.append(SkRasterPipelineOp::swap_rb);
373             }
374             this->append_clip_scale(&p);
375             p.append(SkRasterPipelineOp::srcover_rgba_8888, &fDstPtr);
376         } else {
377             if (fBlend != SkBlendMode::kSrc) {
378                 this->append_load_dst(&p);
379                 SkBlendMode_AppendStages(fBlend, &p);
380                 this->append_clip_lerp(&p);
381             } else if (fClipShaderBuffer) {
382                 this->append_load_dst(&p);
383                 this->append_clip_lerp(&p);
384             }
385             this->append_store(&p);
386         }
387         fBlitRect = p.compile();
388     }
389 
390     SK_BLITTER_TRACE_STEP(blitRect, trace, /*scanlines=*/h, /*pixels=*/w * h);
391     fBlitRect(x,y,w,h);
392 }
393 
blitAntiH(int x,int y,const SkAlpha aa[],const int16_t runs[])394 void SkRasterPipelineBlitter::blitAntiH(int x, int y, const SkAlpha aa[], const int16_t runs[]) {
395     if (!fBlitAntiH) {
396         SkRasterPipeline p(fAlloc);
397         p.extend(fColorPipeline);
398         p.append_clamp_if_normalized(fDst.info());
399         if (SkBlendMode_ShouldPreScaleCoverage(fBlend, /*rgb_coverage=*/false)) {
400             p.append(SkRasterPipelineOp::scale_1_float, &fCurrentCoverage);
401             this->append_clip_scale(&p);
402             this->append_load_dst(&p);
403             SkBlendMode_AppendStages(fBlend, &p);
404         } else {
405             this->append_load_dst(&p);
406             SkBlendMode_AppendStages(fBlend, &p);
407             p.append(SkRasterPipelineOp::lerp_1_float, &fCurrentCoverage);
408             this->append_clip_lerp(&p);
409         }
410 
411         this->append_store(&p);
412         fBlitAntiH = p.compile();
413     }
414 
415     SK_BLITTER_TRACE_STEP(blitAntiH, true, /*scanlines=*/1ul, /*pixels=*/0ul);
416     for (int16_t run = *runs; run > 0; run = *runs) {
417         SK_BLITTER_TRACE_STEP_ACCUMULATE(blitAntiH, /*pixels=*/run);
418         switch (*aa) {
419             case 0x00:                                break;
420             case 0xff:this->blitRectWithTrace(x,y,run, 1, false); break;
421             default:
422                 fCurrentCoverage = *aa * (1/255.0f);
423                 fBlitAntiH(x,y,run,1);
424         }
425         x    += run;
426         runs += run;
427         aa   += run;
428     }
429 }
430 
blitAntiH2(int x,int y,U8CPU a0,U8CPU a1)431 void SkRasterPipelineBlitter::blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) {
432     SkIRect clip = {x,y, x+2,y+1};
433     uint8_t coverage[] = { (uint8_t)a0, (uint8_t)a1 };
434 
435     SkMask mask;
436     mask.fImage    = coverage;
437     mask.fBounds   = clip;
438     mask.fRowBytes = 2;
439     mask.fFormat   = SkMask::kA8_Format;
440 
441     this->blitMask(mask, clip);
442 }
443 
blitAntiV2(int x,int y,U8CPU a0,U8CPU a1)444 void SkRasterPipelineBlitter::blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) {
445     SkIRect clip = {x,y, x+1,y+2};
446     uint8_t coverage[] = { (uint8_t)a0, (uint8_t)a1 };
447 
448     SkMask mask;
449     mask.fImage    = coverage;
450     mask.fBounds   = clip;
451     mask.fRowBytes = 1;
452     mask.fFormat   = SkMask::kA8_Format;
453 
454     this->blitMask(mask, clip);
455 }
456 
blitV(int x,int y,int height,SkAlpha alpha)457 void SkRasterPipelineBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
458     SkIRect clip = {x,y, x+1,y+height};
459 
460     SkMask mask;
461     mask.fImage    = &alpha;
462     mask.fBounds   = clip;
463     mask.fRowBytes = 0;     // so we reuse the 1 "row" for all of height
464     mask.fFormat   = SkMask::kA8_Format;
465 
466     this->blitMask(mask, clip);
467 }
468 
blitMask(const SkMask & mask,const SkIRect & clip)469 void SkRasterPipelineBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
470     if (mask.fFormat == SkMask::kBW_Format) {
471         // TODO: native BW masks?
472         return INHERITED::blitMask(mask, clip);
473     }
474 
475     // ARGB and SDF masks shouldn't make it here.
476     SkASSERT(mask.fFormat == SkMask::kA8_Format
477           || mask.fFormat == SkMask::kLCD16_Format
478           || mask.fFormat == SkMask::k3D_Format);
479 
480     auto extract_mask_plane = [&mask](int plane, SkRasterPipeline_MemoryCtx* ctx) {
481         // LCD is 16-bit per pixel; A8 and 3D are 8-bit per pixel.
482         size_t bpp = mask.fFormat == SkMask::kLCD16_Format ? 2 : 1;
483 
484         // Select the right mask plane.  Usually plane == 0 and this is just mask.fImage.
485         auto ptr = (uintptr_t)mask.fImage
486                  + plane * mask.computeImageSize();
487 
488         // Update ctx to point "into" this current mask, but lined up with fDstPtr at (0,0).
489         // This sort of trickery upsets UBSAN (pointer-overflow) so our ptr must be a uintptr_t.
490         // mask.fRowBytes is a uint32_t, which would break our addressing math on 64-bit builds.
491         size_t rowBytes = mask.fRowBytes;
492         ctx->stride = rowBytes / bpp;
493         ctx->pixels = (void*)(ptr - mask.fBounds.left() * bpp
494                                   - mask.fBounds.top()  * rowBytes);
495     };
496 
497     extract_mask_plane(0, &fMaskPtr);
498     if (mask.fFormat == SkMask::k3D_Format) {
499         extract_mask_plane(1, &fEmbossCtx.mul);
500         extract_mask_plane(2, &fEmbossCtx.add);
501     }
502 
503     // Lazily build whichever pipeline we need, specialized for each mask format.
504     if (mask.fFormat == SkMask::kA8_Format && !fBlitMaskA8) {
505         SkRasterPipeline p(fAlloc);
506         p.extend(fColorPipeline);
507         p.append_clamp_if_normalized(fDst.info());
508         if (SkBlendMode_ShouldPreScaleCoverage(fBlend, /*rgb_coverage=*/false)) {
509             p.append(SkRasterPipelineOp::scale_u8, &fMaskPtr);
510             this->append_clip_scale(&p);
511             this->append_load_dst(&p);
512             SkBlendMode_AppendStages(fBlend, &p);
513         } else {
514             this->append_load_dst(&p);
515             SkBlendMode_AppendStages(fBlend, &p);
516             p.append(SkRasterPipelineOp::lerp_u8, &fMaskPtr);
517             this->append_clip_lerp(&p);
518         }
519         this->append_store(&p);
520         fBlitMaskA8 = p.compile();
521     }
522     if (mask.fFormat == SkMask::kLCD16_Format && !fBlitMaskLCD16) {
523         SkRasterPipeline p(fAlloc);
524         p.extend(fColorPipeline);
525         p.append_clamp_if_normalized(fDst.info());
526         if (SkBlendMode_ShouldPreScaleCoverage(fBlend, /*rgb_coverage=*/true)) {
527             // Somewhat unusually, scale_565 needs dst loaded first.
528             this->append_load_dst(&p);
529             p.append(SkRasterPipelineOp::scale_565, &fMaskPtr);
530             this->append_clip_scale(&p);
531             SkBlendMode_AppendStages(fBlend, &p);
532         } else {
533             this->append_load_dst(&p);
534             SkBlendMode_AppendStages(fBlend, &p);
535             p.append(SkRasterPipelineOp::lerp_565, &fMaskPtr);
536             this->append_clip_lerp(&p);
537         }
538         this->append_store(&p);
539         fBlitMaskLCD16 = p.compile();
540     }
541     if (mask.fFormat == SkMask::k3D_Format && !fBlitMask3D) {
542         SkRasterPipeline p(fAlloc);
543         p.extend(fColorPipeline);
544         // This bit is where we differ from kA8_Format:
545         p.append(SkRasterPipelineOp::emboss, &fEmbossCtx);
546         // Now onward just as kA8.
547         p.append_clamp_if_normalized(fDst.info());
548         if (SkBlendMode_ShouldPreScaleCoverage(fBlend, /*rgb_coverage=*/false)) {
549             p.append(SkRasterPipelineOp::scale_u8, &fMaskPtr);
550             this->append_clip_scale(&p);
551             this->append_load_dst(&p);
552             SkBlendMode_AppendStages(fBlend, &p);
553         } else {
554             this->append_load_dst(&p);
555             SkBlendMode_AppendStages(fBlend, &p);
556             p.append(SkRasterPipelineOp::lerp_u8, &fMaskPtr);
557             this->append_clip_lerp(&p);
558         }
559         this->append_store(&p);
560         fBlitMask3D = p.compile();
561     }
562 
563     std::function<void(size_t,size_t,size_t,size_t)>* blitter = nullptr;
564     switch (mask.fFormat) {
565         case SkMask::kA8_Format:    blitter = &fBlitMaskA8;    break;
566         case SkMask::kLCD16_Format: blitter = &fBlitMaskLCD16; break;
567         case SkMask::k3D_Format:    blitter = &fBlitMask3D;    break;
568         default:
569             SkASSERT(false);
570             return;
571     }
572 
573     SkASSERT(blitter);
574     SK_BLITTER_TRACE_STEP(blitMask,
575                        true,
576                        /*scanlines=*/clip.height(),
577                        /*pixels=*/clip.width() * clip.height());
578     (*blitter)(clip.left(),clip.top(), clip.width(),clip.height());
579 }
580