1 /*
2 * Copyright 2019 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/private/SkMacros.h"
9 #include "src/core/SkArenaAlloc.h"
10 #include "src/core/SkColorSpacePriv.h"
11 #include "src/core/SkColorSpaceXformSteps.h"
12 #include "src/core/SkCoreBlitters.h"
13 #include "src/core/SkLRUCache.h"
14 #include "src/core/SkVM.h"
15
16 namespace {
17
18 enum class Coverage { Full, UniformA8, MaskA8, MaskLCD16, Mask3D };
19
20 SK_BEGIN_REQUIRE_DENSE;
21 struct Key {
22 SkColorType colorType;
23 SkAlphaType alphaType;
24 Coverage coverage;
25 SkBlendMode blendMode;
26 SkShader* shader;
27 SkColorFilter* colorFilter;
28
withCoverage__anonfe2eb6e50111::Key29 Key withCoverage(Coverage c) const {
30 Key k = *this;
31 k.coverage = c;
32 return k;
33 }
34 };
35 SK_END_REQUIRE_DENSE;
36
operator ==(const Key & x,const Key & y)37 static bool operator==(const Key& x, const Key& y) {
38 return x.colorType == y.colorType
39 && x.alphaType == y.alphaType
40 && x.coverage == y.coverage
41 && x.blendMode == y.blendMode
42 && x.shader == y.shader
43 && x.colorFilter == y.colorFilter;
44 }
45
try_acquire_program_cache()46 static SkLRUCache<Key, skvm::Program>* try_acquire_program_cache() {
47 #if defined(SK_BUILD_FOR_IOS)
48 // iOS doesn't support thread_local on versions less than 9.0. pthread
49 // based fallbacks must be used there. We could also use an SkSpinlock
50 // and tryAcquire()/release(), or...
51 return nullptr; // ... we could just not cache programs on those platforms.
52 #else
53 thread_local static auto* cache = new SkLRUCache<Key, skvm::Program>{8};
54 return cache;
55 #endif
56 }
57
release_program_cache()58 static void release_program_cache() { }
59
60
61 struct Uniforms {
62 uint32_t paint_color;
63 uint8_t coverage; // Used when Coverage::UniformA8.
64 };
65
66 struct Builder : public skvm::Builder {
67 //using namespace skvm;
68
69 struct Color { skvm::I32 r,g,b,a; };
70
71
inv__anonfe2eb6e50111::Builder72 skvm::I32 inv(skvm::I32 x) {
73 return sub(splat(255), x);
74 }
75
76 // TODO: provide this in skvm::Builder, with a custom NEON impl.
div255__anonfe2eb6e50111::Builder77 skvm::I32 div255(skvm::I32 v) {
78 // This should be a bit-perfect version of (v+127)/255,
79 // implemented as (v + ((v+128)>>8) + 128)>>8.
80 skvm::I32 v128 = add(v, splat(128));
81 return shr(add(v128, shr(v128, 8)), 8);
82 }
83
mix__anonfe2eb6e50111::Builder84 skvm::I32 mix(skvm::I32 x, skvm::I32 y, skvm::I32 t) {
85 return div255(add(mul(x, inv(t)),
86 mul(y, t )));
87 }
88
unpack_8888__anonfe2eb6e50111::Builder89 Color unpack_8888(skvm::I32 rgba) {
90 return {
91 extract(rgba, 0, splat(0xff)),
92 extract(rgba, 8, splat(0xff)),
93 extract(rgba, 16, splat(0xff)),
94 extract(rgba, 24, splat(0xff)),
95 };
96 }
97
pack_8888__anonfe2eb6e50111::Builder98 skvm::I32 pack_8888(Color c) {
99 return pack(pack(c.r, c.g, 8),
100 pack(c.b, c.a, 8), 16);
101 }
102
unpack_565__anonfe2eb6e50111::Builder103 Color unpack_565(skvm::I32 bgr) {
104 // N.B. kRGB_565_SkColorType is named confusingly;
105 // blue is in the low bits and red the high.
106 skvm::I32 r = extract(bgr, 11, splat(0b011'111)),
107 g = extract(bgr, 5, splat(0b111'111)),
108 b = extract(bgr, 0, splat(0b011'111));
109 return {
110 // Scale 565 up to 888.
111 bit_or(shl(r, 3), shr(r, 2)),
112 bit_or(shl(g, 2), shr(g, 4)),
113 bit_or(shl(b, 3), shr(b, 2)),
114 splat(0xff),
115 };
116 }
117
pack_565__anonfe2eb6e50111::Builder118 skvm::I32 pack_565(Color c) {
119 skvm::I32 r = div255(mul(c.r, splat(31))),
120 g = div255(mul(c.g, splat(63))),
121 b = div255(mul(c.b, splat(31)));
122 return pack(pack(b, g,5), r,11);
123 }
124
125 // TODO: add native min/max ops to skvm::Builder
min__anonfe2eb6e50111::Builder126 skvm::I32 min(skvm::I32 x, skvm::I32 y) { return select(lt(x,y), x,y); }
max__anonfe2eb6e50111::Builder127 skvm::I32 max(skvm::I32 x, skvm::I32 y) { return select(gt(x,y), x,y); }
128
CanBuild__anonfe2eb6e50111::Builder129 static bool CanBuild(const Key& key) {
130 // These checks parallel the TODOs in Builder::Builder().
131 if (key.shader) { return false; }
132 if (key.colorFilter) { return false; }
133
134 switch (key.colorType) {
135 default: return false;
136 case kRGB_565_SkColorType: break;
137 case kRGBA_8888_SkColorType: break;
138 case kBGRA_8888_SkColorType: break;
139 }
140
141 if (key.alphaType == kUnpremul_SkAlphaType) { return false; }
142
143 switch (key.blendMode) {
144 default: return false;
145 case SkBlendMode::kSrc: break;
146 case SkBlendMode::kSrcOver: break;
147 }
148
149 return true;
150 }
151
Builder__anonfe2eb6e50111::Builder152 explicit Builder(const Key& key) {
153 #define TODO SkUNREACHABLE
154 SkASSERT(CanBuild(key));
155 skvm::Arg uniforms = uniform(),
156 dst_ptr = arg(SkColorTypeBytesPerPixel(key.colorType));
157 // When coverage is MaskA8 or MaskLCD16 there will be one more mask varying,
158 // and when coverage is Mask3D there will be three more mask varyings.
159
160
161 // When there's no shader and no color filter, the source color is the paint color.
162 if (key.shader) { TODO; }
163 if (key.colorFilter) { TODO; }
164 Color src = unpack_8888(uniform32(uniforms, offsetof(Uniforms, paint_color)));
165
166 // Load up the destination color.
167 Color dst;
168 switch (key.colorType) {
169 default: TODO;
170
171 case kRGB_565_SkColorType: dst = unpack_565 (load16(dst_ptr)); break;
172
173 case kRGBA_8888_SkColorType: dst = unpack_8888(load32(dst_ptr)); break;
174 case kBGRA_8888_SkColorType: dst = unpack_8888(load32(dst_ptr));
175 std::swap(dst.r, dst.b);
176 break;
177 }
178
179 // When a destination is tagged opaque, we may assume it both starts and stays fully
180 // opaque, ignoring any math that disagrees. So anything involving force_opaque is
181 // optional, and sometimes helps cut a small amount of work in these programs.
182 const bool force_opaque = true && key.alphaType == kOpaque_SkAlphaType;
183 if (force_opaque) { dst.a = splat(0xff); }
184
185 // We'd need to premul dst after loading and unpremul before storing.
186 if (key.alphaType == kUnpremul_SkAlphaType) { TODO; }
187
188 // Blend src and dst.
189 switch (key.blendMode) {
190 default: TODO;
191
192 case SkBlendMode::kSrc: break;
193
194 case SkBlendMode::kSrcOver: {
195 src.r = add(src.r, div255(mul(dst.r, inv(src.a))));
196 src.g = add(src.g, div255(mul(dst.g, inv(src.a))));
197 src.b = add(src.b, div255(mul(dst.b, inv(src.a))));
198 src.a = add(src.a, div255(mul(dst.a, inv(src.a))));
199 } break;
200 }
201
202 // Lerp with coverage if needed.
203 bool apply_coverage = true;
204 Color cov;
205 switch (key.coverage) {
206 case Coverage::Full: apply_coverage = false;
207 break;
208
209 case Coverage::UniformA8: cov.r = cov.g = cov.b = cov.a =
210 uniform8(uniforms, offsetof(Uniforms, coverage));
211 break;
212
213 case Coverage::MaskA8: cov.r = cov.g = cov.b = cov.a =
214 load8(varying<uint8_t>());
215 break;
216
217 case Coverage::MaskLCD16:
218 cov = unpack_565(load16(varying<uint16_t>()));
219 cov.a = select(lt(src.a, dst.a), min(cov.r, min(cov.g,cov.b))
220 , max(cov.r, max(cov.g,cov.b)));
221 break;
222
223 case Coverage::Mask3D: TODO;
224 }
225 if (apply_coverage) {
226 src.r = mix(dst.r, src.r, cov.r);
227 src.g = mix(dst.g, src.g, cov.g);
228 src.b = mix(dst.b, src.b, cov.b);
229 src.a = mix(dst.a, src.a, cov.a);
230 }
231
232 if (force_opaque) { src.a = splat(0xff); }
233
234 // Store back to the destination.
235 switch (key.colorType) {
236 default: SkUNREACHABLE;
237
238 case kRGB_565_SkColorType: store16(dst_ptr, pack_565(src)); break;
239
240 case kBGRA_8888_SkColorType: std::swap(src.r, src.b); // fallthrough
241 case kRGBA_8888_SkColorType: store32(dst_ptr, pack_8888(src)); break;
242 }
243 #undef TODO
244 }
245 };
246
247 class Blitter final : public SkBlitter {
248 public:
249 bool ok = false;
250
Blitter(const SkPixmap & device,const SkPaint & paint)251 Blitter(const SkPixmap& device, const SkPaint& paint)
252 : fDevice(device)
253 , fKey {
254 device.colorType(),
255 device.alphaType(),
256 Coverage::Full,
257 paint.getBlendMode(),
258 paint.getShader(),
259 paint.getColorFilter(),
260 }
261 {
262 SkColor4f color = paint.getColor4f();
263 SkColorSpaceXformSteps{sk_srgb_singleton(), kUnpremul_SkAlphaType,
264 device.colorSpace(), kUnpremul_SkAlphaType}.apply(color.vec());
265
266 if (color.fitsInBytes() && Builder::CanBuild(fKey)) {
267 fUniforms.paint_color = color.premul().toBytes_RGBA();
268 ok = true;
269 }
270 }
271
~Blitter()272 ~Blitter() override {
273 if (SkLRUCache<Key, skvm::Program>* cache = try_acquire_program_cache()) {
274 auto cache_program = [&](skvm::Program&& program, Coverage coverage) {
275 if (!program.empty()) {
276 Key key = fKey.withCoverage(coverage);
277 if (skvm::Program* found = cache->find(key)) {
278 *found = std::move(program);
279 } else {
280 cache->insert(key, std::move(program));
281 }
282 }
283 };
284 cache_program(std::move(fBlitH), Coverage::Full);
285 cache_program(std::move(fBlitAntiH), Coverage::UniformA8);
286 cache_program(std::move(fBlitMaskA8), Coverage::MaskA8);
287 cache_program(std::move(fBlitMaskLCD16), Coverage::MaskLCD16);
288
289 release_program_cache();
290 }
291 }
292
293 private:
294 SkPixmap fDevice; // TODO: can this be const&?
295 const Key fKey;
296 Uniforms fUniforms;
297 skvm::Program fBlitH,
298 fBlitAntiH,
299 fBlitMaskA8,
300 fBlitMaskLCD16;
301
buildProgram(Coverage coverage)302 skvm::Program buildProgram(Coverage coverage) {
303 Key key = fKey.withCoverage(coverage);
304 {
305 skvm::Program p;
306 if (SkLRUCache<Key, skvm::Program>* cache = try_acquire_program_cache()) {
307 if (skvm::Program* found = cache->find(key)) {
308 p = std::move(*found);
309 }
310 release_program_cache();
311 }
312 if (!p.empty()) {
313 return p;
314 }
315 }
316 #if 0
317 static std::atomic<int> done{0};
318 if (0 == done++) {
319 atexit([]{ SkDebugf("%d calls to done\n", done.load()); });
320 }
321 #endif
322 return Builder{key}.done();
323 }
324
blitH(int x,int y,int w)325 void blitH(int x, int y, int w) override {
326 if (fBlitH.empty()) {
327 fBlitH = this->buildProgram(Coverage::Full);
328 }
329 fBlitH.eval(w, &fUniforms, fDevice.addr(x,y));
330 }
331
blitAntiH(int x,int y,const SkAlpha cov[],const int16_t runs[])332 void blitAntiH(int x, int y, const SkAlpha cov[], const int16_t runs[]) override {
333 if (fBlitAntiH.empty()) {
334 fBlitAntiH = this->buildProgram(Coverage::UniformA8);
335 }
336 for (int16_t run = *runs; run > 0; run = *runs) {
337 fUniforms.coverage = *cov;
338 fBlitAntiH.eval(run, &fUniforms, fDevice.addr(x,y));
339
340 x += run;
341 runs += run;
342 cov += run;
343 }
344 }
345
blitMask(const SkMask & mask,const SkIRect & clip)346 void blitMask(const SkMask& mask, const SkIRect& clip) override {
347 if (mask.fFormat == SkMask::kBW_Format) {
348 // TODO: native BW masks?
349 return SkBlitter::blitMask(mask, clip);
350 }
351
352 const skvm::Program* program = nullptr;
353 switch (mask.fFormat) {
354 default: SkUNREACHABLE; // ARGB and SDF masks shouldn't make it here.
355
356 case SkMask::k3D_Format: // TODO: the mul and add 3D mask planes too
357 case SkMask::kA8_Format:
358 if (fBlitMaskA8.empty()) {
359 fBlitMaskA8 = this->buildProgram(Coverage::MaskA8);
360 }
361 program = &fBlitMaskA8;
362 break;
363
364 case SkMask::kLCD16_Format:
365 if (fBlitMaskLCD16.empty()) {
366 fBlitMaskLCD16 = this->buildProgram(Coverage::MaskLCD16);
367 }
368 program = &fBlitMaskLCD16;
369 break;
370 }
371
372 SkASSERT(program);
373 if (program) {
374 for (int y = clip.top(); y < clip.bottom(); y++) {
375 program->eval(clip.width(),
376 &fUniforms,
377 fDevice.addr(clip.left(), y),
378 mask.getAddr(clip.left(), y));
379 }
380 }
381 }
382 };
383
384 } // namespace
385
386
SkCreateSkVMBlitter(const SkPixmap & device,const SkPaint & paint,const SkMatrix & ctm,SkArenaAlloc * alloc)387 SkBlitter* SkCreateSkVMBlitter(const SkPixmap& device,
388 const SkPaint& paint,
389 const SkMatrix& ctm,
390 SkArenaAlloc* alloc) {
391 auto blitter = alloc->make<Blitter>(device, paint);
392 return blitter->ok ? blitter
393 : nullptr;
394 }
395