• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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