@@ -47,6 +47,66 @@ bool gSkVMJITViaDylib{false};
4747 #include < sys/mman.h> // mmap, mprotect
4848#endif
4949
50+ // We're basing our implementation of non-separable blend modes on
51+ // https://www.w3.org/TR/compositing-1/#blendingnonseparable.
52+ // and
53+ // https://www.khronos.org/registry/OpenGL/specs/es/3.2/es_spec_3.2.pdf
54+ // They're equivalent, but ES' math has been better simplified.
55+ //
56+ // Anything extra we add beyond that is to make the math work with premul inputs.
57+
58+ static skvm::F32 saturation (skvm::Builder* p, skvm::F32 r, skvm::F32 g, skvm::F32 b) {
59+ return p->sub (p->max (r, p->max (g, b)), p->min (r, p->min (g, b)));
60+ }
61+
62+ static skvm::F32 luminance (skvm::Builder* p, skvm::F32 r, skvm::F32 g, skvm::F32 b) {
63+ return p->mad (r, p->splat (0 .30f ),
64+ p->mad (g, p->splat (0 .59f ),
65+ p->mul (b, p->splat (0 .11f ))));
66+ }
67+
68+ static void set_sat (skvm::Builder* p, skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32 s) {
69+ auto mn = p->min (*r, p->min (*g, *b)),
70+ mx = p->max (*r, p->max (*g, *b)),
71+ sat = p->sub (mx, mn);
72+
73+ // Map min channel to 0, max channel to s, and scale the middle proportionally.
74+ auto scale = [&](auto c) {
75+ auto zero = p->splat (0 .0f );
76+ return p->select (p->eq (sat, zero), zero, p->div (p->mul (p->sub (c, mn), s), sat));
77+ };
78+ *r = scale (*r);
79+ *g = scale (*g);
80+ *b = scale (*b);
81+ }
82+
83+ static void set_lum (skvm::Builder* p, skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32 lu) {
84+ auto diff = p->sub (lu, luminance (p, *r, *g, *b));
85+ *r = p->add (*r, diff);
86+ *g = p->add (*g, diff);
87+ *b = p->add (*b, diff);
88+ }
89+
90+ static void clip_color (skvm::Builder* p, skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32 a) {
91+ auto mn = p->min (*r, p->min (*g, *b)),
92+ mx = p->max (*r, p->max (*g, *b)),
93+ lu = luminance (p, *r, *g, *b);
94+
95+ auto clip = [&](auto c) {
96+ c = p->select (p->gte (mn, p->splat (0 .0f )),
97+ c,
98+ p->add (lu, p->div (p->mul (p->sub (c, lu), lu), p->sub (lu, mn))));
99+ c = p->select (p->gt (mx, a),
100+ p->add (lu, p->div (p->mul (p->sub (c, lu), p->sub (a, lu)), p->sub (mx, lu))),
101+ c);
102+ // Sometimes without this we may dip just a little negative.
103+ return p->max (c, p->splat (0 .0f ));
104+ };
105+ *r = clip (*r);
106+ *g = clip (*g);
107+ *b = clip (*b);
108+ }
109+
50110namespace skvm {
51111
52112 struct Program ::Impl {
@@ -1090,6 +1150,239 @@ namespace skvm {
10901150 };
10911151 }
10921152
1153+ Color Builder::blend (SkBlendMode mode, Color src, Color dst) {
1154+ auto mma = [this ](skvm::F32 x, skvm::F32 y, skvm::F32 z, skvm::F32 w) {
1155+ return mad (x,y, mul (z,w));
1156+ };
1157+
1158+ auto inv = [this ](skvm::F32 x) { return sub (splat (1 .0f ), x); };
1159+
1160+ auto two = [this ](skvm::F32 x) { return add (x, x); };
1161+
1162+ auto apply_rgba = [&](auto fn) {
1163+ return Color {
1164+ fn (src.r , dst.r ),
1165+ fn (src.g , dst.g ),
1166+ fn (src.b , dst.b ),
1167+ fn (src.a , dst.a ),
1168+ };
1169+ };
1170+
1171+ auto apply_rgb_srcover_a = [&](auto fn) {
1172+ return Color {
1173+ fn (src.r , dst.r ),
1174+ fn (src.g , dst.g ),
1175+ fn (src.b , dst.b ),
1176+ mad (dst.a , inv (src.a ), src.a ), // srcover for alpha
1177+ };
1178+ };
1179+
1180+ auto non_sep = [&](auto R, auto G, auto B) {
1181+ return Color{
1182+ add (mma (src.r , inv (dst.a ), dst.r , inv (src.a )), R),
1183+ add (mma (src.g , inv (dst.a ), dst.g , inv (src.a )), G),
1184+ add (mma (src.b , inv (dst.a ), dst.b , inv (src.a )), B),
1185+ mad (dst.a , inv (src.a ), src.a ), // srcover
1186+ };
1187+ };
1188+
1189+ switch (mode) {
1190+ default : SkASSERT (false ); /* but also, for safety, fallthrough*/
1191+
1192+ case SkBlendMode::kClear : return {
1193+ splat (0 .0f ),
1194+ splat (0 .0f ),
1195+ splat (0 .0f ),
1196+ splat (0 .0f ),
1197+ };
1198+
1199+ case SkBlendMode::kSrc : return src;
1200+ case SkBlendMode::kDst : return dst;
1201+
1202+ case SkBlendMode::kDstOver : std::swap (src, dst); // fall-through
1203+ case SkBlendMode::kSrcOver :
1204+ return apply_rgba ([&](auto s, auto d) {
1205+ return mad (d, inv (src.a ), s);
1206+ });
1207+
1208+ case SkBlendMode::kDstIn : std::swap (src, dst); // fall-through
1209+ case SkBlendMode::kSrcIn :
1210+ return apply_rgba ([&](auto s, auto d) {
1211+ return mul (s, dst.a );
1212+ });
1213+
1214+ case SkBlendMode::kDstOut : std::swap (src, dst); // fall-through
1215+ case SkBlendMode::kSrcOut :
1216+ return apply_rgba ([&](auto s, auto d) {
1217+ return mul (s, inv (dst.a ));
1218+ });
1219+
1220+ case SkBlendMode::kDstATop : std::swap (src, dst); // fall-through
1221+ case SkBlendMode::kSrcATop :
1222+ return apply_rgba ([&](auto s, auto d) {
1223+ return mma (s, dst.a , d, inv (src.a ));
1224+ });
1225+
1226+ case SkBlendMode::kXor :
1227+ return apply_rgba ([&](auto s, auto d) {
1228+ return mma (s, inv (dst.a ), d, inv (src.a ));
1229+ });
1230+
1231+ case SkBlendMode::kPlus :
1232+ return apply_rgba ([&](auto s, auto d) {
1233+ return min (add (s, d), splat (1 .0f ));
1234+ });
1235+
1236+ case SkBlendMode::kModulate :
1237+ return apply_rgba ([&](auto s, auto d) {
1238+ return mul (s, d);
1239+ });
1240+
1241+ case SkBlendMode::kScreen :
1242+ // (s+d)-(s*d) gave us trouble with our "r,g,b <= after blending" asserts.
1243+ // It's kind of plausible that s + (d - sd) keeps more precision?
1244+ return apply_rgba ([&](auto s, auto d) {
1245+ return add (s, sub (d, mul (s, d)));
1246+ });
1247+
1248+ case SkBlendMode::kDarken :
1249+ return apply_rgb_srcover_a ([&](auto s, auto d) {
1250+ return add (s, sub (d, max (mul (s, dst.a ),
1251+ mul (d, src.a ))));
1252+ });
1253+
1254+ case SkBlendMode::kLighten :
1255+ return apply_rgb_srcover_a ([&](auto s, auto d) {
1256+ return add (s, sub (d, min (mul (s, dst.a ),
1257+ mul (d, src.a ))));
1258+ });
1259+
1260+ case SkBlendMode::kDifference :
1261+ return apply_rgb_srcover_a ([&](auto s, auto d) {
1262+ return add (s, sub (d, two (min (mul (s, dst.a ),
1263+ mul (d, src.a )))));
1264+ });
1265+
1266+ case SkBlendMode::kExclusion :
1267+ return apply_rgb_srcover_a ([&](auto s, auto d) {
1268+ return add (s, sub (d, two (mul (s, d))));
1269+ });
1270+
1271+ case SkBlendMode::kColorBurn :
1272+ return apply_rgb_srcover_a ([&](auto s, auto d) {
1273+ auto mn = min (dst.a ,
1274+ div (mul (sub (dst.a , d), src.a ), s)),
1275+ burn = mad (src.a , sub (dst.a , mn), mma (s, inv (dst.a ), d, inv (src.a )));
1276+ return select (eq (d, dst.a ), mad (s, inv (dst.a ), d),
1277+ select (eq (s, splat (0 .0f )), mul (d, inv (src.a )), burn));
1278+ });
1279+
1280+ case SkBlendMode::kColorDodge :
1281+ return apply_rgb_srcover_a ([&](auto s, auto d) {
1282+ auto tmp = mad (src.a , min (dst.a ,
1283+ div (mul (d, src.a ), sub (src.a , s))),
1284+ mma (s, inv (dst.a ), d, inv (src.a )));
1285+ return select (eq (d, splat (0 .0f )), mul (s, inv (dst.a )),
1286+ select (eq (s, src.a ), mad (d, inv (src.a ), s), tmp));
1287+ });
1288+
1289+ case SkBlendMode::kHardLight :
1290+ return apply_rgb_srcover_a ([&](auto s, auto d) {
1291+ return add (mma (s, inv (dst.a ), d, inv (src.a )),
1292+ select (lte (two (s), src.a ),
1293+ two (mul (s, d)),
1294+ sub (mul (src.a , dst.a ), two (mul (sub (dst.a , d), sub (src.a , s))))));
1295+ });
1296+
1297+ case SkBlendMode::kOverlay :
1298+ return apply_rgb_srcover_a ([&](auto s, auto d) {
1299+ return add (mma (s, inv (dst.a ), d, inv (src.a )),
1300+ select (lte (two (d), dst.a ),
1301+ two (mul (s, d)),
1302+ sub (mul (src.a , dst.a ), two (mul (sub (dst.a , d), sub (src.a , s))))));
1303+ });
1304+
1305+ case SkBlendMode::kMultiply :
1306+ return apply_rgba ([&](auto s, auto d) {
1307+ return add (mma (s, inv (dst.a ), d, inv (src.a )), mul (s, d));
1308+ });
1309+
1310+ case SkBlendMode::kSoftLight :
1311+ return apply_rgb_srcover_a ([&](auto s, auto d) {
1312+ auto m = select (gt (dst.a , splat (0 .0f )), div (d, dst.a ), splat (0 .0f )),
1313+ s2 = two (s),
1314+ m4 = two (two (m));
1315+
1316+ // The logic forks three ways:
1317+ // 1. dark src?
1318+ // 2. light src, dark dst?
1319+ // 3. light src, light dst?
1320+
1321+ // Used in case 1
1322+ auto darkSrc = mul (d, mad (sub (s2, src.a ), inv (m), src.a )),
1323+ // Used in case 2
1324+ darkDst = mad (mad (m4, m4, m4), sub (m, splat (1 .0f )), mul (splat (7 .0f ), m)),
1325+ // Used in case 3.
1326+ liteDst = sub (sqrt (m), m),
1327+ // Used in 2 or 3?
1328+ liteSrc = mad (mul (dst.a , sub (s2, src.a )),
1329+ select (lte (two (two (d)), dst.a ), darkDst, liteDst),
1330+ mul (d, src.a ));
1331+ return mad (s, inv (dst.a ), mad (d,
1332+ inv (src.a ),
1333+ select (lte (s2, src.a ), darkSrc, liteSrc)));
1334+
1335+
1336+ });
1337+
1338+ case SkBlendMode::kHue : {
1339+ skvm::F32 R = mul (src.r , src.a ),
1340+ G = mul (src.g , src.a ),
1341+ B = mul (src.b , src.a );
1342+
1343+ set_sat (this , &R, &G, &B, mul (saturation (this , dst.r , dst.g , dst.b ), src.a ));
1344+ set_lum (this , &R, &G, &B, mul ( luminance (this , dst.r , dst.g , dst.b ), src.a ));
1345+ clip_color (this , &R, &G, &B, mul (src.a , dst.a ));
1346+
1347+ return non_sep (R, G, B);
1348+ }
1349+
1350+ case SkBlendMode::kSaturation : {
1351+ skvm::F32 R = mul (dst.r , src.a ),
1352+ G = mul (dst.g , src.a ),
1353+ B = mul (dst.b , src.a );
1354+
1355+ set_sat (this , &R, &G, &B, mul (saturation (this , src.r , src.g , src.b ), dst.a ));
1356+ set_lum (this , &R, &G, &B, mul ( luminance (this , dst.r , dst.g , dst.b ), src.a ));
1357+ clip_color (this , &R, &G, &B, mul (src.a , dst.a ));
1358+
1359+ return non_sep (R, G, B);
1360+ }
1361+
1362+ case SkBlendMode::kColor : {
1363+ skvm::F32 R = mul (src.r , dst.a ),
1364+ G = mul (src.g , dst.a ),
1365+ B = mul (src.b , dst.a );
1366+
1367+ set_lum (this , &R, &G, &B, mul (luminance (this , dst.r , dst.g , dst.b ), src.a ));
1368+ clip_color (this , &R, &G, &B, mul (src.a , dst.a ));
1369+
1370+ return non_sep (R, G, B);
1371+ }
1372+
1373+ case SkBlendMode::kLuminosity : {
1374+ skvm::F32 R = mul (dst.r , src.a ),
1375+ G = mul (dst.g , src.a ),
1376+ B = mul (dst.b , src.a );
1377+
1378+ set_lum (this , &R, &G, &B, mul (luminance (this , src.r , src.g , src.b ), dst.a ));
1379+ clip_color (this , &R, &G, &B, mul (src.a , dst.a ));
1380+
1381+ return non_sep (R, G, B);
1382+ }
1383+ }
1384+ }
1385+
10931386 // ~~~~ Program::eval() and co. ~~~~ //
10941387
10951388 // Handy references for x86-64 instruction encoding:
0 commit comments