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