1 /*
2 * Copyright 2006 The Android Open Source Project
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 "src/core/SkBlitter_A8.h"
9
10 #include "include/core/SkBlendMode.h"
11 #include "include/core/SkColorType.h"
12 #include "include/core/SkPaint.h"
13 #include "include/core/SkRect.h"
14 #include "include/core/SkShader.h" // IWYU pragma: keep
15 #include "include/core/SkTypes.h"
16 #include "include/private/base/SkDebug.h"
17 #include "src/base/SkArenaAlloc.h"
18 #include "src/core/SkDrawTypes.h"
19 #include "src/core/SkMask.h"
20
21 #include <cstring>
22 #include <optional>
23
SkA8_Coverage_Blitter(const SkPixmap & device,const SkPaint & paint)24 SkA8_Coverage_Blitter::SkA8_Coverage_Blitter(const SkPixmap& device, const SkPaint& paint)
25 : fDevice(device)
26 {
27 SkASSERT(nullptr == paint.getShader());
28 SkASSERT(nullptr == paint.getColorFilter());
29 }
30
blitAntiH(int x,int y,const SkAlpha antialias[],const int16_t runs[])31 void SkA8_Coverage_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
32 const int16_t runs[]) {
33 uint8_t* device = fDevice.writable_addr8(x, y);
34 SkDEBUGCODE(int totalCount = 0;)
35
36 for (;;) {
37 int count = runs[0];
38 SkASSERT(count >= 0);
39 if (count == 0) {
40 return;
41 }
42 if (antialias[0]) {
43 memset(device, antialias[0], count);
44 }
45 runs += count;
46 antialias += count;
47 device += count;
48
49 SkDEBUGCODE(totalCount += count;)
50 }
51 SkASSERT(fDevice.width() == totalCount);
52 }
53
blitH(int x,int y,int width)54 void SkA8_Coverage_Blitter::blitH(int x, int y, int width) {
55 memset(fDevice.writable_addr8(x, y), 0xFF, width);
56 }
57
blitV(int x,int y,int height,SkAlpha alpha)58 void SkA8_Coverage_Blitter::blitV(int x, int y, int height, SkAlpha alpha) {
59 if (0 == alpha) {
60 return;
61 }
62
63 uint8_t* dst = fDevice.writable_addr8(x, y);
64 const size_t dstRB = fDevice.rowBytes();
65 while (--height >= 0) {
66 *dst = alpha;
67 dst += dstRB;
68 }
69 }
70
blitRect(int x,int y,int width,int height)71 void SkA8_Coverage_Blitter::blitRect(int x, int y, int width, int height) {
72 uint8_t* dst = fDevice.writable_addr8(x, y);
73 const size_t dstRB = fDevice.rowBytes();
74 while (--height >= 0) {
75 memset(dst, 0xFF, width);
76 dst += dstRB;
77 }
78 }
79
blitMask(const SkMask & mask,const SkIRect & clip)80 void SkA8_Coverage_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) {
81 if (SkMask::kA8_Format != mask.fFormat) {
82 this->SkBlitter::blitMask(mask, clip);
83 return;
84 }
85
86 int x = clip.fLeft;
87 int y = clip.fTop;
88 int width = clip.width();
89 int height = clip.height();
90
91 uint8_t* dst = fDevice.writable_addr8(x, y);
92 const uint8_t* src = mask.getAddr8(x, y);
93 const size_t srcRB = mask.fRowBytes;
94 const size_t dstRB = fDevice.rowBytes();
95
96 while (--height >= 0) {
97 memcpy(dst, src, width);
98 dst += dstRB;
99 src += srcRB;
100 }
101 }
102
103 //////////////
104
div255(unsigned prod)105 static inline uint8_t div255(unsigned prod) {
106 SkASSERT(prod <= 255*255);
107 return (prod + 128) * 257 >> 16;
108 }
109
u8_lerp(uint8_t a,uint8_t b,uint8_t t)110 static inline unsigned u8_lerp(uint8_t a, uint8_t b, uint8_t t) {
111 return div255((255 - t) * a + t * b);
112 }
113
114 using AlphaProc = uint8_t(*)(uint8_t src, uint8_t dst);
115
srcover_p(uint8_t src,uint8_t dst)116 static uint8_t srcover_p (uint8_t src, uint8_t dst) { return src + div255((255 - src) * dst); }
src_p(uint8_t src,uint8_t dst)117 static uint8_t src_p (uint8_t src, uint8_t dst) { return src; }
118
A8_row_bw(uint8_t dst[],uint8_t src,int N,Mode proc)119 template <typename Mode> void A8_row_bw(uint8_t dst[], uint8_t src, int N, Mode proc) {
120 for (int i = 0; i < N; ++i) {
121 dst[i] = proc(src, dst[i]);
122 }
123 }
124 using A8_RowBlitBW = void(*)(uint8_t[], uint8_t, int);
125
126 template <typename Mode>
A8_row_aa(uint8_t dst[],uint8_t src,int N,uint8_t aa,Mode proc,const bool canFoldAA)127 void A8_row_aa(uint8_t dst[], uint8_t src, int N, uint8_t aa, Mode proc, const bool canFoldAA) {
128 if (canFoldAA) {
129 src = div255(src * aa);
130 for (int i = 0; i < N; ++i) {
131 dst[i] = proc(src, dst[i]);
132 }
133 } else {
134 for (int i = 0; i < N; ++i) {
135 dst[i] = u8_lerp(dst[i], proc(src, dst[i]), aa);
136 }
137 }
138 }
139 using A8_RowBlitAA = void(*)(uint8_t[], uint8_t, int, uint8_t aa);
140
141 #define WRAP_BLIT(proc, canFoldAA) \
142 proc, \
143 [](uint8_t dst[], uint8_t src, int N) \
144 { A8_row_bw(dst, src, N, proc); }, \
145 [](uint8_t dst[], uint8_t src, int N, uint8_t aa) \
146 { A8_row_aa(dst, src, N, aa, proc, canFoldAA); }
147
148 struct A8_RowBlitBWPair {
149 SkBlendMode mode;
150 AlphaProc oneProc;
151 A8_RowBlitBW bwProc;
152 A8_RowBlitAA aaProc;
153 };
154 constexpr A8_RowBlitBWPair gA8_RowBlitPairs[] = {
155 {SkBlendMode::kSrcOver, WRAP_BLIT(srcover_p, true)},
156 {SkBlendMode::kSrc, WRAP_BLIT(src_p, false)},
157 };
158 #undef WRAP_BLIT
159
find_a8_rowproc_pair(SkBlendMode bm)160 static const A8_RowBlitBWPair* find_a8_rowproc_pair(SkBlendMode bm) {
161 for (auto& pair : gA8_RowBlitPairs) {
162 if (pair.mode == bm) {
163 return &pair;
164 }
165 }
166 return nullptr;
167 }
168
169 class SkA8_Blitter : public SkBlitter {
170 public:
171 SkA8_Blitter(const SkPixmap& device, const SkPaint& paint);
172 void blitH(int x, int y, int width) override;
173 void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override;
174 void blitV(int x, int y, int height, SkAlpha alpha) override;
175 void blitRect(int x, int y, int width, int height) override;
176 void blitMask(const SkMask&, const SkIRect&) override;
177
178 private:
179 const SkPixmap fDevice;
180 AlphaProc fOneProc;
181 A8_RowBlitBW fBWProc;
182 A8_RowBlitAA fAAProc;
183 SkAlpha fSrc;
184
185 using INHERITED = SkBlitter;
186 };
187
SkA8_Blitter(const SkPixmap & device,const SkPaint & paint)188 SkA8_Blitter::SkA8_Blitter(const SkPixmap& device,
189 const SkPaint& paint) : fDevice(device) {
190 SkASSERT(nullptr == paint.getShader());
191 SkASSERT(nullptr == paint.getColorFilter());
192 auto mode = paint.asBlendMode();
193 SkASSERT(mode);
194 auto pair = find_a8_rowproc_pair(*mode);
195 SkASSERT(pair);
196
197 fOneProc = pair->oneProc;
198 fBWProc = pair->bwProc;
199 fAAProc = pair->aaProc;
200 fSrc = paint.getAlpha();
201 }
202
blitAntiH(int x,int y,const SkAlpha antialias[],const int16_t runs[])203 void SkA8_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) {
204 uint8_t* device = fDevice.writable_addr8(x, y);
205 SkDEBUGCODE(int totalCount = 0;)
206
207 for (;;) {
208 int count = runs[0];
209 SkASSERT(count >= 0);
210 if (count == 0) {
211 return;
212 }
213
214 if (antialias[0] == 0xFF) {
215 fBWProc(device, fSrc, count);
216 } else if (antialias[0] != 0) {
217 fAAProc(device, fSrc, count, antialias[0]);
218 }
219
220 runs += count;
221 antialias += count;
222 device += count;
223
224 SkDEBUGCODE(totalCount += count;)
225 }
226 SkASSERT(fDevice.width() == totalCount);
227 }
228
blitH(int x,int y,int width)229 void SkA8_Blitter::blitH(int x, int y, int width) {
230 fBWProc(fDevice.writable_addr8(x, y), fSrc, width);
231 }
232
blitV(int x,int y,int height,SkAlpha aa)233 void SkA8_Blitter::blitV(int x, int y, int height, SkAlpha aa) {
234 uint8_t* device = fDevice.writable_addr8(x, y);
235 const size_t dstRB = fDevice.rowBytes();
236
237 if (aa == 0xFF) {
238 while (--height >= 0) {
239 *device = fOneProc(fSrc, *device);
240 device += dstRB;
241 }
242 } else if (aa != 0) {
243 while (--height >= 0) {
244 fAAProc(device, fSrc, 1, aa);
245 device += dstRB;
246 }
247 }
248 }
249
blitRect(int x,int y,int width,int height)250 void SkA8_Blitter::blitRect(int x, int y, int width, int height) {
251 uint8_t* device = fDevice.writable_addr8(x, y);
252 const size_t dstRB = fDevice.rowBytes();
253
254 while (--height >= 0) {
255 fBWProc(device, fSrc, width);
256 device += dstRB;
257 }
258 }
259
blitMask(const SkMask & mask,const SkIRect & clip)260 void SkA8_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) {
261 if (SkMask::kA8_Format != mask.fFormat) {
262 this->INHERITED::blitMask(mask, clip);
263 return;
264 }
265
266 int x = clip.fLeft;
267 int y = clip.fTop;
268 int width = clip.width();
269 int height = clip.height();
270
271 uint8_t* dst = fDevice.writable_addr8(x, y);
272 const uint8_t* src = mask.getAddr8(x, y);
273 const size_t srcRB = mask.fRowBytes;
274 const size_t dstRB = fDevice.rowBytes();
275
276 while (--height >= 0) {
277 for (int i = 0; i < width; ++i) {
278 dst[i] = u8_lerp(dst[i], fOneProc(fSrc, dst[i]), src[i]);
279 }
280 dst += dstRB;
281 src += srcRB;
282 }
283 }
284
285 //////////////////
286
SkA8Blitter_Choose(const SkPixmap & dst,const SkMatrix & ctm,const SkPaint & paint,SkArenaAlloc * alloc,SkDrawCoverage drawCoverage,sk_sp<SkShader> clipShader,const SkSurfaceProps &)287 SkBlitter* SkA8Blitter_Choose(const SkPixmap& dst,
288 const SkMatrix& ctm,
289 const SkPaint& paint,
290 SkArenaAlloc* alloc,
291 SkDrawCoverage drawCoverage,
292 sk_sp<SkShader> clipShader,
293 const SkSurfaceProps&) {
294 if (dst.colorType() != SkColorType::kAlpha_8_SkColorType) {
295 return nullptr;
296 }
297 if (paint.getShader() || paint.getColorFilter()) {
298 return nullptr;
299 }
300 if (clipShader) {
301 return nullptr; // would not be hard to support ...?
302 }
303
304 if (drawCoverage == SkDrawCoverage::kYes) {
305 return alloc->make<SkA8_Coverage_Blitter>(dst, paint);
306 } else {
307 // we only support certain blendmodes...
308 auto mode = paint.asBlendMode();
309 if (mode && find_a8_rowproc_pair(*mode)) {
310 return alloc->make<SkA8_Blitter>(dst, paint);
311 }
312 }
313 return nullptr;
314 }
315