• 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 blitRectWithTrace(int x, int y, int w, int h, bool trace);
77     void appendLoadDst      (SkRasterPipeline*) const;
78     void appendStore        (SkRasterPipeline*) const;
79 
80     // these check internally, and only append if there was a native clipShader
81     void appendClipScale    (SkRasterPipeline*) const;
82     void appendClipLerp     (SkRasterPipeline*) const;
83 
84     SkPixmap               fDst;
85     SkArenaAlloc*          fAlloc;
86     SkRasterPipeline       fColorPipeline;
87     SkRasterPipeline       fBlendPipeline;
88     // If the blender is a blend-mode, we retain that information for late-stage optimizations
89     std::optional<SkBlendMode> fBlendMode;
90     // set to pipeline storage (for alpha) if we have a clipShader
91     void*                  fClipShaderBuffer = nullptr; // "native" : float or U16
92 
93     SkRasterPipeline_MemoryCtx
94         fDstPtr       = {nullptr,0},  // Always points to the top-left of fDst.
95         fMaskPtr      = {nullptr,0};  // Updated each call to blitMask().
96     SkRasterPipeline_EmbossCtx fEmbossCtx;  // Used only for k3D_Format masks.
97 
98     // We may be able to specialize blitH() or blitRect() into a memset.
99     void   (*fMemset2D)(SkPixmap*, int x,int y, int w,int h, uint64_t color) = nullptr;
100     uint64_t fMemsetColor = 0;   // Big enough for largest memsettable dst format, F16.
101 
102     // Built lazily on first use.
103     std::function<void(size_t, size_t, size_t, size_t)> fBlitRect,
104                                                         fBlitAntiH,
105                                                         fBlitMaskA8,
106                                                         fBlitMaskLCD16,
107                                                         fBlitMask3D;
108 
109     // These values are pointed to by the blit pipelines above,
110     // which allows us to adjust them from call to call.
111     float fCurrentCoverage = 0.0f;
112     float fDitherRate      = 0.0f;
113 
114     using INHERITED = SkBlitter;
115 };
116 
paint_color_to_dst(const SkPaint & paint,const SkPixmap & dst)117 static SkColor4f paint_color_to_dst(const SkPaint& paint, const SkPixmap& dst) {
118     SkColor4f paintColor = paint.getColor4f();
119     SkColorSpaceXformSteps(sk_srgb_singleton(), kUnpremul_SkAlphaType,
120                            dst.colorSpace(),    kUnpremul_SkAlphaType).apply(paintColor.vec());
121     return paintColor;
122 }
123 
SkCreateRasterPipelineBlitter(const SkPixmap & dst,const SkPaint & paint,const SkMatrix & ctm,SkArenaAlloc * alloc,sk_sp<SkShader> clipShader,const SkSurfaceProps & props)124 SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
125                                          const SkPaint& paint,
126                                          const SkMatrix& ctm,
127                                          SkArenaAlloc* alloc,
128                                          sk_sp<SkShader> clipShader,
129                                          const SkSurfaceProps& props) {
130     SkColorSpace* dstCS = dst.colorSpace();
131     SkColorType dstCT = dst.colorType();
132     SkColor4f dstPaintColor = paint_color_to_dst(paint, dst);
133 
134     auto shader = as_SB(paint.getShader());
135 
136     SkRasterPipeline_<256> shaderPipeline;
137     if (!shader) {
138         // Having no shader makes things nice and easy... just use the paint color
139         shaderPipeline.appendConstantColor(alloc, dstPaintColor.premul().vec());
140         bool is_opaque    = dstPaintColor.fA == 1.0f,
141              is_constant  = true;
142         return SkRasterPipelineBlitter::Create(dst, paint, dstPaintColor, alloc, shaderPipeline,
143                                                is_opaque, is_constant, clipShader.get());
144     }
145 
146     bool is_opaque    = shader->isOpaque() && dstPaintColor.fA == 1.0f;
147     bool is_constant  = shader->isConstant();
148 
149     if (shader->appendRootStages({&shaderPipeline, alloc, dstCT, dstCS, dstPaintColor, props},
150                                  ctm)) {
151         if (dstPaintColor.fA != 1.0f) {
152             shaderPipeline.append(SkRasterPipelineOp::scale_1_float,
153                                   alloc->make<float>(dstPaintColor.fA));
154         }
155         return SkRasterPipelineBlitter::Create(dst, paint, dstPaintColor, alloc, shaderPipeline,
156                                                is_opaque, is_constant, clipShader.get());
157     }
158 
159     // The shader can't draw with SkRasterPipeline.
160     return nullptr;
161 }
162 
SkCreateRasterPipelineBlitter(const SkPixmap & dst,const SkPaint & paint,const SkRasterPipeline & shaderPipeline,bool is_opaque,SkArenaAlloc * alloc,sk_sp<SkShader> clipShader)163 SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
164                                          const SkPaint& paint,
165                                          const SkRasterPipeline& shaderPipeline,
166                                          bool is_opaque,
167                                          SkArenaAlloc* alloc,
168                                          sk_sp<SkShader> clipShader) {
169     bool is_constant = false;  // If this were the case, it'd be better to just set a paint color.
170     return SkRasterPipelineBlitter::Create(dst, paint, paint_color_to_dst(paint, dst), alloc,
171                                            shaderPipeline, is_opaque, is_constant,
172                                            clipShader.get());
173 }
174 
Create(const SkPixmap & dst,const SkPaint & paint,const SkColor4f & dstPaintColor,SkArenaAlloc * alloc,const SkRasterPipeline & shaderPipeline,bool is_opaque,bool is_constant,const SkShader * clipShader)175 SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
176                                            const SkPaint& paint,
177                                            const SkColor4f& dstPaintColor,
178                                            SkArenaAlloc* alloc,
179                                            const SkRasterPipeline& shaderPipeline,
180                                            bool is_opaque,
181                                            bool is_constant,
182                                            const SkShader* clipShader) {
183     auto blitter = alloc->make<SkRasterPipelineBlitter>(dst, alloc);
184 
185     // Our job in this factory is to fill out the blitter's color and blend pipelines.
186     // The color pipeline is the common front of the full blit pipeline. The blend pipeline is just
187     // the portion that does the actual blending math (and assumes that src and dst are already
188     // loaded).
189     //
190     // The full blit pipelines are each constructed lazily on first use, and include the color
191     // pipeline, reading the dst, the blend pipeline, coverage, dithering, and writing the dst.
192 
193     // Start with the color pipeline
194     auto colorPipeline = &blitter->fColorPipeline;
195 
196     if (clipShader) {
197         auto clipP = colorPipeline;
198         SkColorType clipCT = kRGBA_8888_SkColorType;
199         SkColorSpace* clipCS = nullptr;
200         SkSurfaceProps props{}; // default OK; clipShader doesn't render text
201         SkStageRec rec = {clipP, alloc, clipCT, clipCS, SkColors::kBlack, props};
202         if (as_SB(clipShader)->appendRootStages(rec, SkMatrix::I())) {
203             struct Storage {
204                 // large enough for highp (float) or lowp(U16)
205                 float   fA[SkRasterPipeline_kMaxStride];
206             };
207             auto storage = alloc->make<Storage>();
208             clipP->append(SkRasterPipelineOp::store_src_a, storage->fA);
209             blitter->fClipShaderBuffer = storage->fA;
210             is_constant = false;
211         } else {
212             return nullptr;
213         }
214     }
215 
216     // Let's get the shader in first.
217     colorPipeline->extend(shaderPipeline);
218 
219     // If there's a color filter it comes next.
220     if (auto colorFilter = paint.getColorFilter()) {
221         SkSurfaceProps props{}; // default OK; colorFilter doesn't render text
222         SkStageRec rec = {
223                 colorPipeline, alloc, dst.colorType(), dst.colorSpace(), dstPaintColor, props};
224         if (!as_CFB(colorFilter)->appendStages(rec, is_opaque)) {
225             return nullptr;
226         }
227         is_opaque = is_opaque && as_CFB(colorFilter)->isAlphaUnchanged();
228     }
229 
230     // Not all formats make sense to dither (think, F16).  We set their dither rate
231     // to zero.  We only dither non-constant shaders, so is_constant won't change here.
232     if (paint.isDither() && !is_constant) {
233         switch (dst.info().colorType()) {
234             case kARGB_4444_SkColorType:
235                 blitter->fDitherRate = 1 / 15.0f;
236                 break;
237             case kRGB_565_SkColorType:
238                 blitter->fDitherRate = 1 / 63.0f;
239                 break;
240             case kGray_8_SkColorType:
241             case kRGB_888x_SkColorType:
242             case kRGBA_8888_SkColorType:
243             case kBGRA_8888_SkColorType:
244             case kSRGBA_8888_SkColorType:
245             case kR8_unorm_SkColorType:
246                 blitter->fDitherRate = 1 / 255.0f;
247                 break;
248             case kRGB_101010x_SkColorType:
249             case kRGBA_1010102_SkColorType:
250             case kBGR_101010x_SkColorType:
251             case kBGRA_1010102_SkColorType:
252             case kBGRA_10101010_XR_SkColorType:
253             case kRGBA_10x6_SkColorType:
254                 blitter->fDitherRate = 1 / 1023.0f;
255                 break;
256 
257             case kUnknown_SkColorType:
258             case kAlpha_8_SkColorType:
259             case kBGR_101010x_XR_SkColorType:
260             case kRGBA_F16_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     this->blitRectWithTrace(x, y, w, h, true);
393 }
394 
blitRectWithTrace(int x,int y,int w,int h,bool trace)395 void SkRasterPipelineBlitter::blitRectWithTrace(int x, int y, int w, int h, bool trace) {
396     if (fMemset2D) {
397         fMemset2D(&fDst, x,y, w,h, fMemsetColor);
398         return;
399     }
400 
401     if (!fBlitRect) {
402         SkRasterPipeline p(fAlloc);
403         p.extend(fColorPipeline);
404         p.appendClampIfNormalized(fDst.info());
405         if (fBlendMode == SkBlendMode::kSrcOver
406                 && (fDst.info().colorType() == kRGBA_8888_SkColorType ||
407                     fDst.info().colorType() == kBGRA_8888_SkColorType)
408                 && !fDst.colorSpace()
409                 && fDst.info().alphaType() != kUnpremul_SkAlphaType
410                 && fDitherRate == 0.0f) {
411             if (fDst.info().colorType() == kBGRA_8888_SkColorType) {
412                 p.append(SkRasterPipelineOp::swap_rb);
413             }
414             this->appendClipScale(&p);
415             p.append(SkRasterPipelineOp::srcover_rgba_8888, &fDstPtr);
416         } else {
417             if (fBlendMode != SkBlendMode::kSrc) {
418                 this->appendLoadDst(&p);
419                 p.extend(fBlendPipeline);
420                 this->appendClipLerp(&p);
421             } else if (fClipShaderBuffer) {
422                 this->appendLoadDst(&p);
423                 this->appendClipLerp(&p);
424             }
425             this->appendStore(&p);
426         }
427         fBlitRect = p.compile();
428     }
429 
430     fBlitRect(x,y,w,h);
431 }
432 
blitAntiH(int x,int y,const SkAlpha aa[],const int16_t runs[])433 void SkRasterPipelineBlitter::blitAntiH(int x, int y, const SkAlpha aa[], const int16_t runs[]) {
434     if (!fBlitAntiH) {
435         SkRasterPipeline p(fAlloc);
436         p.extend(fColorPipeline);
437         p.appendClampIfNormalized(fDst.info());
438         if (fBlendMode.has_value() &&
439             SkBlendMode_ShouldPreScaleCoverage(*fBlendMode, /*rgb_coverage=*/false)) {
440             p.append(SkRasterPipelineOp::scale_1_float, &fCurrentCoverage);
441             this->appendClipScale(&p);
442             this->appendLoadDst(&p);
443             p.extend(fBlendPipeline);
444         } else {
445             this->appendLoadDst(&p);
446             p.extend(fBlendPipeline);
447             p.append(SkRasterPipelineOp::lerp_1_float, &fCurrentCoverage);
448             this->appendClipLerp(&p);
449         }
450 
451         this->appendStore(&p);
452         fBlitAntiH = p.compile();
453     }
454 
455     for (int16_t run = *runs; run > 0; run = *runs) {
456         switch (*aa) {
457             case 0x00:                                break;
458             case 0xff:this->blitRectWithTrace(x,y,run, 1, false); break;
459             default:
460                 fCurrentCoverage = *aa * (1/255.0f);
461                 fBlitAntiH(x,y,run,1);
462         }
463         x    += run;
464         runs += run;
465         aa   += run;
466     }
467 }
468 
blitAntiH2(int x,int y,U8CPU a0,U8CPU a1)469 void SkRasterPipelineBlitter::blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) {
470     SkIRect clip = {x,y, x+2,y+1};
471     uint8_t coverage[] = { (uint8_t)a0, (uint8_t)a1 };
472     SkMask mask(coverage, clip, 2, SkMask::kA8_Format);
473     this->blitMask(mask, clip);
474 }
475 
blitAntiV2(int x,int y,U8CPU a0,U8CPU a1)476 void SkRasterPipelineBlitter::blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) {
477     SkIRect clip = {x,y, x+1,y+2};
478     uint8_t coverage[] = { (uint8_t)a0, (uint8_t)a1 };
479     SkMask mask(coverage, clip, 1, SkMask::kA8_Format);
480     this->blitMask(mask, clip);
481 }
482 
blitV(int x,int y,int height,SkAlpha alpha)483 void SkRasterPipelineBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
484     SkIRect clip = {x,y, x+1,y+height};
485     SkMask mask(&alpha, clip,
486                 0,     // so we reuse the 1 "row" for all of height
487                 SkMask::kA8_Format);
488     this->blitMask(mask, clip);
489 }
490 
blitMask(const SkMask & mask,const SkIRect & clip)491 void SkRasterPipelineBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
492     if (mask.fFormat == SkMask::kBW_Format) {
493         // TODO: native BW masks?
494         return INHERITED::blitMask(mask, clip);
495     }
496 
497     // ARGB and SDF masks shouldn't make it here.
498     SkASSERT(mask.fFormat == SkMask::kA8_Format
499           || mask.fFormat == SkMask::kLCD16_Format
500           || mask.fFormat == SkMask::k3D_Format);
501 
502     auto extract_mask_plane = [&mask](int plane, SkRasterPipeline_MemoryCtx* ctx) {
503         // LCD is 16-bit per pixel; A8 and 3D are 8-bit per pixel.
504         size_t bpp = mask.fFormat == SkMask::kLCD16_Format ? 2 : 1;
505 
506         // Select the right mask plane.  Usually plane == 0 and this is just mask.fImage.
507         auto ptr = (uintptr_t)mask.fImage
508                  + plane * mask.computeImageSize();
509 
510         // Update ctx to point "into" this current mask, but lined up with fDstPtr at (0,0).
511         // This sort of trickery upsets UBSAN (pointer-overflow) so our ptr must be a uintptr_t.
512         // mask.fRowBytes is a uint32_t, which would break our addressing math on 64-bit builds.
513         size_t rowBytes = mask.fRowBytes;
514         ctx->stride = rowBytes / bpp;
515         ctx->pixels = (void*)(ptr - mask.fBounds.left() * bpp
516                                   - mask.fBounds.top()  * rowBytes);
517     };
518 
519     extract_mask_plane(0, &fMaskPtr);
520     if (mask.fFormat == SkMask::k3D_Format) {
521         extract_mask_plane(1, &fEmbossCtx.mul);
522         extract_mask_plane(2, &fEmbossCtx.add);
523     }
524 
525     // Lazily build whichever pipeline we need, specialized for each mask format.
526     if (mask.fFormat == SkMask::kA8_Format && !fBlitMaskA8) {
527         SkRasterPipeline p(fAlloc);
528         p.extend(fColorPipeline);
529         p.appendClampIfNormalized(fDst.info());
530         if (fBlendMode.has_value() &&
531             SkBlendMode_ShouldPreScaleCoverage(*fBlendMode, /*rgb_coverage=*/false)) {
532             p.append(SkRasterPipelineOp::scale_u8, &fMaskPtr);
533             this->appendClipScale(&p);
534             this->appendLoadDst(&p);
535             p.extend(fBlendPipeline);
536         } else {
537             this->appendLoadDst(&p);
538             p.extend(fBlendPipeline);
539             p.append(SkRasterPipelineOp::lerp_u8, &fMaskPtr);
540             this->appendClipLerp(&p);
541         }
542         this->appendStore(&p);
543         fBlitMaskA8 = p.compile();
544     }
545     if (mask.fFormat == SkMask::kLCD16_Format && !fBlitMaskLCD16) {
546         SkRasterPipeline p(fAlloc);
547         p.extend(fColorPipeline);
548         p.appendClampIfNormalized(fDst.info());
549         if (fBlendMode.has_value() &&
550             SkBlendMode_ShouldPreScaleCoverage(*fBlendMode, /*rgb_coverage=*/true)) {
551             // Somewhat unusually, scale_565 needs dst loaded first.
552             this->appendLoadDst(&p);
553             p.append(SkRasterPipelineOp::scale_565, &fMaskPtr);
554             this->appendClipScale(&p);
555             p.extend(fBlendPipeline);
556         } else {
557             this->appendLoadDst(&p);
558             p.extend(fBlendPipeline);
559             p.append(SkRasterPipelineOp::lerp_565, &fMaskPtr);
560             this->appendClipLerp(&p);
561         }
562         this->appendStore(&p);
563         fBlitMaskLCD16 = p.compile();
564     }
565     if (mask.fFormat == SkMask::k3D_Format && !fBlitMask3D) {
566         SkRasterPipeline p(fAlloc);
567         p.extend(fColorPipeline);
568         // This bit is where we differ from kA8_Format:
569         p.append(SkRasterPipelineOp::emboss, &fEmbossCtx);
570         // Now onward just as kA8.
571         p.appendClampIfNormalized(fDst.info());
572         if (fBlendMode.has_value() &&
573             SkBlendMode_ShouldPreScaleCoverage(*fBlendMode, /*rgb_coverage=*/false)) {
574             p.append(SkRasterPipelineOp::scale_u8, &fMaskPtr);
575             this->appendClipScale(&p);
576             this->appendLoadDst(&p);
577             p.extend(fBlendPipeline);
578         } else {
579             this->appendLoadDst(&p);
580             p.extend(fBlendPipeline);
581             p.append(SkRasterPipelineOp::lerp_u8, &fMaskPtr);
582             this->appendClipLerp(&p);
583         }
584         this->appendStore(&p);
585         fBlitMask3D = p.compile();
586     }
587 
588     std::function<void(size_t,size_t,size_t,size_t)>* blitter = nullptr;
589     switch (mask.fFormat) {
590         case SkMask::kA8_Format:    blitter = &fBlitMaskA8;    break;
591         case SkMask::kLCD16_Format: blitter = &fBlitMaskLCD16; break;
592         case SkMask::k3D_Format:    blitter = &fBlitMask3D;    break;
593         default:
594             SkASSERT(false);
595             return;
596     }
597 
598     SkASSERT(blitter);
599     (*blitter)(clip.left(),clip.top(), clip.width(),clip.height());
600 }
601