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