xref: /aosp_15_r20/external/skia/src/core/SkRasterPipelineBlitter.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 kRGB_F16F16F16x_SkColorType:
262             case kRGBA_F16Norm_SkColorType:
263             case kRGBA_F32_SkColorType:
264             case kR8G8_unorm_SkColorType:
265             case kA16_float_SkColorType:
266             case kA16_unorm_SkColorType:
267             case kR16G16_float_SkColorType:
268             case kR16G16_unorm_SkColorType:
269             case kR16G16B16A16_unorm_SkColorType:
270                 blitter->fDitherRate = 0.0f;
271                 break;
272         }
273         if (blitter->fDitherRate > 0.0f) {
274             colorPipeline->append(SkRasterPipelineOp::dither, &blitter->fDitherRate);
275         }
276     }
277 
278     // Optimization: A pipeline that's still constant here can collapse back into a constant color.
279     if (is_constant) {
280         SkColor4f constantColor;
281         SkRasterPipeline_MemoryCtx constantColorPtr = { &constantColor, 0 };
282         // We could remove this clamp entirely, but if the destination is 8888, doing the clamp
283         // here allows the color pipeline to still run in lowp (we'll use uniform_color, rather than
284         // unbounded_uniform_color).
285         colorPipeline->appendClampIfNormalized(dst.info());
286         colorPipeline->append(SkRasterPipelineOp::store_f32, &constantColorPtr);
287         colorPipeline->run(0,0,1,1);
288         colorPipeline->reset();
289         colorPipeline->appendConstantColor(alloc, constantColor);
290 
291         is_opaque = constantColor.fA == 1.0f;
292     }
293 
294     // Now we'll build the blend pipeline
295     auto blendPipeline = &blitter->fBlendPipeline;
296 
297     sk_sp<SkBlender> blender = paint.refBlender();
298     if (!blender) {
299         blender = SkBlender::Mode(SkBlendMode::kSrcOver);
300     }
301 
302     // We can strength-reduce SrcOver into Src when opaque.
303     if (is_opaque && as_BB(blender)->asBlendMode() == SkBlendMode::kSrcOver) {
304         blender = SkBlender::Mode(SkBlendMode::kSrc);
305     }
306 
307     // When we're drawing a constant color in Src mode, we can sometimes just memset.
308     // (The previous two optimizations help find more opportunities for this one.)
309     if (is_constant && as_BB(blender)->asBlendMode() == SkBlendMode::kSrc &&
310         dst.info().bytesPerPixel() <= static_cast<int>(sizeof(blitter->fMemsetColor))) {
311         // Run our color pipeline all the way through to produce what we'd memset when we can.
312         // Not all blits can memset, so we need to keep colorPipeline too.
313         SkRasterPipeline_<256> p;
314         p.extend(*colorPipeline);
315         blitter->fDstPtr = SkRasterPipeline_MemoryCtx{&blitter->fMemsetColor, 0};
316         blitter->appendStore(&p);
317         p.run(0,0,1,1);
318 
319         switch (blitter->fDst.shiftPerPixel()) {
320             case 0: blitter->fMemset2D = [](SkPixmap* dst, int x,int y, int w,int h, uint64_t c) {
321                 void* p = dst->writable_addr(x,y);
322                 while (h --> 0) {
323                     memset(p, c, w);
324                     p = SkTAddOffset<void>(p, dst->rowBytes());
325                 }
326             }; break;
327 
328             case 1: blitter->fMemset2D = [](SkPixmap* dst, int x,int y, int w,int h, uint64_t c) {
329                 SkOpts::rect_memset16(dst->writable_addr16(x,y), c, w, dst->rowBytes(), h);
330             }; break;
331 
332             case 2: blitter->fMemset2D = [](SkPixmap* dst, int x,int y, int w,int h, uint64_t c) {
333                 SkOpts::rect_memset32(dst->writable_addr32(x,y), c, w, dst->rowBytes(), h);
334             }; break;
335 
336             case 3: blitter->fMemset2D = [](SkPixmap* dst, int x,int y, int w,int h, uint64_t c) {
337                 SkOpts::rect_memset64(dst->writable_addr64(x,y), c, w, dst->rowBytes(), h);
338             }; break;
339 
340             // TODO(F32)?
341         }
342     }
343 
344     {
345         SkSurfaceProps props{};  // default OK; blender doesn't render text
346         SkStageRec rec = {
347                 blendPipeline, alloc, dst.colorType(), dst.colorSpace(), dstPaintColor, props};
348         if (!as_BB(blender)->appendStages(rec)) {
349             return nullptr;
350         }
351         blitter->fBlendMode = as_BB(blender)->asBlendMode();
352     }
353 
354     blitter->fDstPtr = SkRasterPipeline_MemoryCtx{
355         blitter->fDst.writable_addr(),
356         blitter->fDst.rowBytesAsPixels(),
357     };
358 
359     return blitter;
360 }
361 
appendLoadDst(SkRasterPipeline * p) const362 void SkRasterPipelineBlitter::appendLoadDst(SkRasterPipeline* p) const {
363     p->appendLoadDst(fDst.info().colorType(), &fDstPtr);
364     if (fDst.info().alphaType() == kUnpremul_SkAlphaType) {
365         p->append(SkRasterPipelineOp::premul_dst);
366     }
367 }
368 
appendStore(SkRasterPipeline * p) const369 void SkRasterPipelineBlitter::appendStore(SkRasterPipeline* p) const {
370     if (fDst.info().alphaType() == kUnpremul_SkAlphaType) {
371         p->append(SkRasterPipelineOp::unpremul);
372     }
373     p->appendStore(fDst.info().colorType(), &fDstPtr);
374 }
375 
appendClipScale(SkRasterPipeline * p) const376 void SkRasterPipelineBlitter::appendClipScale(SkRasterPipeline* p) const {
377     if (fClipShaderBuffer) {
378         p->append(SkRasterPipelineOp::scale_native, fClipShaderBuffer);
379     }
380 }
381 
appendClipLerp(SkRasterPipeline * p) const382 void SkRasterPipelineBlitter::appendClipLerp(SkRasterPipeline* p) const {
383     if (fClipShaderBuffer) {
384         p->append(SkRasterPipelineOp::lerp_native, fClipShaderBuffer);
385     }
386 }
387 
blitH(int x,int y,int w)388 void SkRasterPipelineBlitter::blitH(int x, int y, int w) {
389     this->blitRect(x,y,w,1);
390 }
391 
blitRect(int x,int y,int w,int h)392 void SkRasterPipelineBlitter::blitRect(int x, int y, int w, int h) {
393     this->blitRectWithTrace(x, y, w, h, true);
394 }
395 
blitRectWithTrace(int x,int y,int w,int h,bool trace)396 void SkRasterPipelineBlitter::blitRectWithTrace(int x, int y, int w, int h, bool trace) {
397     if (fMemset2D) {
398         fMemset2D(&fDst, x,y, w,h, fMemsetColor);
399         return;
400     }
401 
402     if (!fBlitRect) {
403         SkRasterPipeline p(fAlloc);
404         p.extend(fColorPipeline);
405         p.appendClampIfNormalized(fDst.info());
406         if (fBlendMode == SkBlendMode::kSrcOver
407                 && (fDst.info().colorType() == kRGBA_8888_SkColorType ||
408                     fDst.info().colorType() == kBGRA_8888_SkColorType)
409                 && !fDst.colorSpace()
410                 && fDst.info().alphaType() != kUnpremul_SkAlphaType
411                 && fDitherRate == 0.0f) {
412             if (fDst.info().colorType() == kBGRA_8888_SkColorType) {
413                 p.append(SkRasterPipelineOp::swap_rb);
414             }
415             this->appendClipScale(&p);
416             p.append(SkRasterPipelineOp::srcover_rgba_8888, &fDstPtr);
417         } else {
418             if (fBlendMode != SkBlendMode::kSrc) {
419                 this->appendLoadDst(&p);
420                 p.extend(fBlendPipeline);
421                 this->appendClipLerp(&p);
422             } else if (fClipShaderBuffer) {
423                 this->appendLoadDst(&p);
424                 this->appendClipLerp(&p);
425             }
426             this->appendStore(&p);
427         }
428         fBlitRect = p.compile();
429     }
430 
431     fBlitRect(x,y,w,h);
432 }
433 
blitAntiH(int x,int y,const SkAlpha aa[],const int16_t runs[])434 void SkRasterPipelineBlitter::blitAntiH(int x, int y, const SkAlpha aa[], const int16_t runs[]) {
435     if (!fBlitAntiH) {
436         SkRasterPipeline p(fAlloc);
437         p.extend(fColorPipeline);
438         p.appendClampIfNormalized(fDst.info());
439         if (fBlendMode.has_value() &&
440             SkBlendMode_ShouldPreScaleCoverage(*fBlendMode, /*rgb_coverage=*/false)) {
441             p.append(SkRasterPipelineOp::scale_1_float, &fCurrentCoverage);
442             this->appendClipScale(&p);
443             this->appendLoadDst(&p);
444             p.extend(fBlendPipeline);
445         } else {
446             this->appendLoadDst(&p);
447             p.extend(fBlendPipeline);
448             p.append(SkRasterPipelineOp::lerp_1_float, &fCurrentCoverage);
449             this->appendClipLerp(&p);
450         }
451 
452         this->appendStore(&p);
453         fBlitAntiH = p.compile();
454     }
455 
456     for (int16_t run = *runs; run > 0; run = *runs) {
457         switch (*aa) {
458             case 0x00:                                break;
459             case 0xff:this->blitRectWithTrace(x,y,run, 1, false); break;
460             default:
461                 fCurrentCoverage = *aa * (1/255.0f);
462                 fBlitAntiH(x,y,run,1);
463         }
464         x    += run;
465         runs += run;
466         aa   += run;
467     }
468 }
469 
blitAntiH2(int x,int y,U8CPU a0,U8CPU a1)470 void SkRasterPipelineBlitter::blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) {
471     SkIRect clip = {x,y, x+2,y+1};
472     uint8_t coverage[] = { (uint8_t)a0, (uint8_t)a1 };
473     SkMask mask(coverage, clip, 2, SkMask::kA8_Format);
474     this->blitMask(mask, clip);
475 }
476 
blitAntiV2(int x,int y,U8CPU a0,U8CPU a1)477 void SkRasterPipelineBlitter::blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) {
478     SkIRect clip = {x,y, x+1,y+2};
479     uint8_t coverage[] = { (uint8_t)a0, (uint8_t)a1 };
480     SkMask mask(coverage, clip, 1, SkMask::kA8_Format);
481     this->blitMask(mask, clip);
482 }
483 
blitV(int x,int y,int height,SkAlpha alpha)484 void SkRasterPipelineBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
485     SkIRect clip = {x,y, x+1,y+height};
486     SkMask mask(&alpha, clip,
487                 0,     // so we reuse the 1 "row" for all of height
488                 SkMask::kA8_Format);
489     this->blitMask(mask, clip);
490 }
491 
blitMask(const SkMask & mask,const SkIRect & clip)492 void SkRasterPipelineBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
493     if (mask.fFormat == SkMask::kBW_Format) {
494         // TODO: native BW masks?
495         return INHERITED::blitMask(mask, clip);
496     }
497 
498     // ARGB and SDF masks shouldn't make it here.
499     SkASSERT(mask.fFormat == SkMask::kA8_Format
500           || mask.fFormat == SkMask::kLCD16_Format
501           || mask.fFormat == SkMask::k3D_Format);
502 
503     auto extract_mask_plane = [&mask](int plane, SkRasterPipeline_MemoryCtx* ctx) {
504         // LCD is 16-bit per pixel; A8 and 3D are 8-bit per pixel.
505         size_t bpp = mask.fFormat == SkMask::kLCD16_Format ? 2 : 1;
506 
507         // Select the right mask plane.  Usually plane == 0 and this is just mask.fImage.
508         auto ptr = (uintptr_t)mask.fImage
509                  + plane * mask.computeImageSize();
510 
511         // Update ctx to point "into" this current mask, but lined up with fDstPtr at (0,0).
512         // This sort of trickery upsets UBSAN (pointer-overflow) so our ptr must be a uintptr_t.
513         // mask.fRowBytes is a uint32_t, which would break our addressing math on 64-bit builds.
514         size_t rowBytes = mask.fRowBytes;
515         ctx->stride = rowBytes / bpp;
516         ctx->pixels = (void*)(ptr - mask.fBounds.left() * bpp
517                                   - mask.fBounds.top()  * rowBytes);
518     };
519 
520     extract_mask_plane(0, &fMaskPtr);
521     if (mask.fFormat == SkMask::k3D_Format) {
522         extract_mask_plane(1, &fEmbossCtx.mul);
523         extract_mask_plane(2, &fEmbossCtx.add);
524     }
525 
526     // Lazily build whichever pipeline we need, specialized for each mask format.
527     if (mask.fFormat == SkMask::kA8_Format && !fBlitMaskA8) {
528         SkRasterPipeline p(fAlloc);
529         p.extend(fColorPipeline);
530         p.appendClampIfNormalized(fDst.info());
531         if (fBlendMode.has_value() &&
532             SkBlendMode_ShouldPreScaleCoverage(*fBlendMode, /*rgb_coverage=*/false)) {
533             p.append(SkRasterPipelineOp::scale_u8, &fMaskPtr);
534             this->appendClipScale(&p);
535             this->appendLoadDst(&p);
536             p.extend(fBlendPipeline);
537         } else {
538             this->appendLoadDst(&p);
539             p.extend(fBlendPipeline);
540             p.append(SkRasterPipelineOp::lerp_u8, &fMaskPtr);
541             this->appendClipLerp(&p);
542         }
543         this->appendStore(&p);
544         fBlitMaskA8 = p.compile();
545     }
546     if (mask.fFormat == SkMask::kLCD16_Format && !fBlitMaskLCD16) {
547         SkRasterPipeline p(fAlloc);
548         p.extend(fColorPipeline);
549         p.appendClampIfNormalized(fDst.info());
550         if (fBlendMode.has_value() &&
551             SkBlendMode_ShouldPreScaleCoverage(*fBlendMode, /*rgb_coverage=*/true)) {
552             // Somewhat unusually, scale_565 needs dst loaded first.
553             this->appendLoadDst(&p);
554             p.append(SkRasterPipelineOp::scale_565, &fMaskPtr);
555             this->appendClipScale(&p);
556             p.extend(fBlendPipeline);
557         } else {
558             this->appendLoadDst(&p);
559             p.extend(fBlendPipeline);
560             p.append(SkRasterPipelineOp::lerp_565, &fMaskPtr);
561             this->appendClipLerp(&p);
562         }
563         this->appendStore(&p);
564         fBlitMaskLCD16 = p.compile();
565     }
566     if (mask.fFormat == SkMask::k3D_Format && !fBlitMask3D) {
567         SkRasterPipeline p(fAlloc);
568         p.extend(fColorPipeline);
569         // This bit is where we differ from kA8_Format:
570         p.append(SkRasterPipelineOp::emboss, &fEmbossCtx);
571         // Now onward just as kA8.
572         p.appendClampIfNormalized(fDst.info());
573         if (fBlendMode.has_value() &&
574             SkBlendMode_ShouldPreScaleCoverage(*fBlendMode, /*rgb_coverage=*/false)) {
575             p.append(SkRasterPipelineOp::scale_u8, &fMaskPtr);
576             this->appendClipScale(&p);
577             this->appendLoadDst(&p);
578             p.extend(fBlendPipeline);
579         } else {
580             this->appendLoadDst(&p);
581             p.extend(fBlendPipeline);
582             p.append(SkRasterPipelineOp::lerp_u8, &fMaskPtr);
583             this->appendClipLerp(&p);
584         }
585         this->appendStore(&p);
586         fBlitMask3D = p.compile();
587     }
588 
589     std::function<void(size_t,size_t,size_t,size_t)>* blitter = nullptr;
590     switch (mask.fFormat) {
591         case SkMask::kA8_Format:    blitter = &fBlitMaskA8;    break;
592         case SkMask::kLCD16_Format: blitter = &fBlitMaskLCD16; break;
593         case SkMask::k3D_Format:    blitter = &fBlitMask3D;    break;
594         default:
595             SkASSERT(false);
596             return;
597     }
598 
599     SkASSERT(blitter);
600     (*blitter)(clip.left(),clip.top(), clip.width(),clip.height());
601 }
602