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