summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/src/opts/SkXfermode_opts.h
blob: 15714791a3f2dd3f054185cf77985a54e67c8cb1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef Sk4pxXfermode_DEFINED
#define Sk4pxXfermode_DEFINED

#include "src/base/SkMSAN.h"
#include "src/core/Sk4px.h"
#include "src/core/SkXfermodePriv.h"

#ifdef SK_FORCE_RASTER_PIPELINE_BLITTER

namespace SK_OPTS_NS {
    /*not static*/ inline SkXfermode* create_xfermode(SkBlendMode) { return nullptr; }
}

#else

namespace {  // NOLINT(google-build-namespaces)

// Most xfermodes can be done most efficiently 4 pixels at a time in 8 or 16-bit fixed point.
#define XFERMODE(Xfermode) \
    struct Xfermode { Sk4px operator()(const Sk4px&, const Sk4px&) const; }; \
    inline Sk4px Xfermode::operator()(const Sk4px& d, const Sk4px& s) const

XFERMODE(Clear) { return Sk4px::DupPMColor(0); }
XFERMODE(Src)   { return s; }
XFERMODE(Dst)   { return d; }
XFERMODE(SrcIn)   { return     s.approxMulDiv255(d.alphas()      ); }
XFERMODE(SrcOut)  { return     s.approxMulDiv255(d.alphas().inv()); }
XFERMODE(SrcOver) { return s + d.approxMulDiv255(s.alphas().inv()); }
XFERMODE(DstIn)   { return SrcIn  ()(s,d); }
XFERMODE(DstOut)  { return SrcOut ()(s,d); }
XFERMODE(DstOver) { return SrcOver()(s,d); }

// [ S * Da + (1 - Sa) * D]
XFERMODE(SrcATop) { return (s * d.alphas() + d * s.alphas().inv()).div255(); }
XFERMODE(DstATop) { return SrcATop()(s,d); }
//[ S * (1 - Da) + (1 - Sa) * D ]
XFERMODE(Xor) { return (s * d.alphas().inv() + d * s.alphas().inv()).div255(); }
// [S + D ]
XFERMODE(Plus) { return s.saturatedAdd(d); }
// [S * D ]
XFERMODE(Modulate) { return s.approxMulDiv255(d); }
// [S + D - S * D]
XFERMODE(Screen) {
    // Doing the math as S + (1-S)*D or S + (D - S*D) means the add and subtract can be done
    // in 8-bit space without overflow.  S + (1-S)*D is a touch faster because inv() is cheap.
    return s + d.approxMulDiv255(s.inv());
}

#undef XFERMODE

// A reasonable fallback mode for doing AA is to simply apply the transfermode first,
// then linearly interpolate the AA.
template <typename Xfermode>
static Sk4px xfer_aa(const Sk4px& d, const Sk4px& s, const Sk4px& aa) {
    Sk4px bw = Xfermode()(d, s);
    return (bw * aa + d * aa.inv()).div255();
}

// For some transfermodes we specialize AA, either for correctness or performance.
#define XFERMODE_AA(Xfermode) \
    template <> inline Sk4px xfer_aa<Xfermode>(const Sk4px& d, const Sk4px& s, const Sk4px& aa)

// Plus' clamp needs to happen after AA.  skia:3852
XFERMODE_AA(Plus) {  // [ clamp( (1-AA)D + (AA)(S+D) ) == clamp(D + AA*S) ]
    return d.saturatedAdd(s.approxMulDiv255(aa));
}

#undef XFERMODE_AA

// Src and Clear modes are safe to use with uninitialized dst buffers,
// even if the implementation branches based on bytes from dst (e.g. asserts in Debug mode).
// For those modes, just lie to MSAN that dst is always intialized.
template <typename Xfermode> static void mark_dst_initialized_if_safe(void*, void*) {}
template <> inline void mark_dst_initialized_if_safe<Src>(void* dst, void* end) {
    sk_msan_mark_initialized(dst, end, "Src doesn't read dst.");
}
template <> inline void mark_dst_initialized_if_safe<Clear>(void* dst, void* end) {
    sk_msan_mark_initialized(dst, end, "Clear doesn't read dst.");
}

template <typename Xfermode>
class Sk4pxXfermode : public SkXfermode {
public:
    Sk4pxXfermode() {}

    void xfer32(SkPMColor dst[], const SkPMColor src[], int n, const SkAlpha aa[]) const override {
        mark_dst_initialized_if_safe<Xfermode>(dst, dst+n);
        if (nullptr == aa) {
            Sk4px::MapDstSrc(n, dst, src, Xfermode());
        } else {
            Sk4px::MapDstSrcAlpha(n, dst, src, aa, xfer_aa<Xfermode>);
        }
    }
};

} // namespace

namespace SK_OPTS_NS {

/*not static*/ inline SkXfermode* create_xfermode(SkBlendMode mode) {
    switch (mode) {
#define CASE(Xfermode) \
    case SkBlendMode::k##Xfermode: return new Sk4pxXfermode<Xfermode>()
        CASE(Clear);
        CASE(Src);
        CASE(Dst);
        CASE(SrcOver);
        CASE(DstOver);
        CASE(SrcIn);
        CASE(DstIn);
        CASE(SrcOut);
        CASE(DstOut);
        CASE(SrcATop);
        CASE(DstATop);
        CASE(Xor);
        CASE(Plus);
        CASE(Modulate);
        CASE(Screen);
    #undef CASE

        default: break;
    }
    return nullptr;
}

} // namespace SK_OPTS_NS

#endif // #ifdef SK_FORCE_RASTER_PIPELINE_BLITTER

#endif//Sk4pxXfermode_DEFINED