skia2/src/effects/SkArithmeticMode.cpp

183 lines
6.1 KiB
C++
Raw Normal View History

/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkArithmeticModePriv.h"
#include "SkColorPriv.h"
arithmetic mode with Sk4f After reading the SSE version, I figured I'd show off the new hotness a little. This'll get us SSE, NEON and portable implementations all in one easy to read package. Since we've been talking about it, it's worth noting the several ways this implementation is still not constant time: - short circuits on 0x00 and 0xff coverage; - floating point multiplication with untrusted k1-k4; if someone figures out a clever way to sometimes create denorm floats and sometimes not, there's a gigantic performance difference. I would hazard the pin is constant time now though. I've also fixed the lerp to lerp between dst and r instead of src and r. That can't have been right. curr/maxrss loops min median mean max stddev samples config bench 9/9 MB 1 25.5ms 25.5ms 25.5ms 25.5ms 0% ▃▁▁▃▂▇▅▆▇█ 8888 Xfermode_arithmetic_enforce_pm_aa 9/9 MB 1 24.1ms 24.2ms 24.2ms 24.3ms 0% ▄▃▁▄█▆▆█▃█ 8888 Xfermode_arithmetic_aa 9/9 MB 1 102ms 102ms 102ms 103ms 0% ▁▅▂▆▂█▂█▁▂ 8888 Xfermode_arithmetic_enforce_pm 9/9 MB 1 94.8ms 95.4ms 95.2ms 95.8ms 0% ▅▅▁▁▁▁▄▇█▇ 8888 Xfermode_arithmetic ~~~~> curr/maxrss loops min median mean max stddev samples config bench 9/9 MB 1 9.71ms 9.74ms 9.73ms 9.78ms 0% █▅▄▄▁▂▂▂▄▄ 8888 Xfermode_arithmetic_enforce_pm_aa 9/9 MB 1 9.5ms 9.57ms 9.58ms 9.7ms 1% ▂▁█▅▂▂▆▃▄▄ 8888 Xfermode_arithmetic_aa 9/9 MB 1 21.8ms 21.8ms 21.8ms 21.9ms 0% █▂▂▂▂▂▂▁▄▂ 8888 Xfermode_arithmetic_enforce_pm 9/9 MB 1 16.5ms 16.6ms 16.6ms 16.6ms 0% ▃█▁▁▄▄▁▁▆▅ 8888 Xfermode_arithmetic BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1873963003 CQ_EXTRA_TRYBOTS=client.skia:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD-Trybot Review URL: https://codereview.chromium.org/1873963003
2016-04-10 13:23:28 +00:00
#include "SkNx.h"
#include "SkRasterPipeline.h"
#include "SkReadBuffer.h"
#include "SkString.h"
#include "SkUnPreMultiply.h"
arithmetic mode with Sk4f After reading the SSE version, I figured I'd show off the new hotness a little. This'll get us SSE, NEON and portable implementations all in one easy to read package. Since we've been talking about it, it's worth noting the several ways this implementation is still not constant time: - short circuits on 0x00 and 0xff coverage; - floating point multiplication with untrusted k1-k4; if someone figures out a clever way to sometimes create denorm floats and sometimes not, there's a gigantic performance difference. I would hazard the pin is constant time now though. I've also fixed the lerp to lerp between dst and r instead of src and r. That can't have been right. curr/maxrss loops min median mean max stddev samples config bench 9/9 MB 1 25.5ms 25.5ms 25.5ms 25.5ms 0% ▃▁▁▃▂▇▅▆▇█ 8888 Xfermode_arithmetic_enforce_pm_aa 9/9 MB 1 24.1ms 24.2ms 24.2ms 24.3ms 0% ▄▃▁▄█▆▆█▃█ 8888 Xfermode_arithmetic_aa 9/9 MB 1 102ms 102ms 102ms 103ms 0% ▁▅▂▆▂█▂█▁▂ 8888 Xfermode_arithmetic_enforce_pm 9/9 MB 1 94.8ms 95.4ms 95.2ms 95.8ms 0% ▅▅▁▁▁▁▄▇█▇ 8888 Xfermode_arithmetic ~~~~> curr/maxrss loops min median mean max stddev samples config bench 9/9 MB 1 9.71ms 9.74ms 9.73ms 9.78ms 0% █▅▄▄▁▂▂▂▄▄ 8888 Xfermode_arithmetic_enforce_pm_aa 9/9 MB 1 9.5ms 9.57ms 9.58ms 9.7ms 1% ▂▁█▅▂▂▆▃▄▄ 8888 Xfermode_arithmetic_aa 9/9 MB 1 21.8ms 21.8ms 21.8ms 21.9ms 0% █▂▂▂▂▂▂▁▄▂ 8888 Xfermode_arithmetic_enforce_pm 9/9 MB 1 16.5ms 16.6ms 16.6ms 16.6ms 0% ▃█▁▁▄▄▁▁▆▅ 8888 Xfermode_arithmetic BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1873963003 CQ_EXTRA_TRYBOTS=client.skia:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD-Trybot Review URL: https://codereview.chromium.org/1873963003
2016-04-10 13:23:28 +00:00
#include "SkWriteBuffer.h"
#if SK_SUPPORT_GPU
#include "SkArithmeticMode_gpu.h"
#endif
class SkArithmeticMode_scalar : public SkXfermode {
public:
SkArithmeticMode_scalar(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4,
bool enforcePMColor) {
fK[0] = k1;
fK[1] = k2;
fK[2] = k3;
fK[3] = k4;
fEnforcePMColor = enforcePMColor;
}
void xfer32(SkPMColor[], const SkPMColor[], int count, const SkAlpha[]) const override;
bool onAppendStages(SkRasterPipeline* p) const override {
p->append(&Stage, this);
return true;
}
SK_TO_STRING_OVERRIDE()
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkArithmeticMode_scalar)
#if SK_SUPPORT_GPU
sk_sp<GrFragmentProcessor> makeFragmentProcessorForImageFilter(
sk_sp<GrFragmentProcessor> dst) const override;
sk_sp<GrXPFactory> asXPFactory() const override;
#endif
private:
static void SK_VECTORCALL Stage(SkRasterPipeline::Stage* st, size_t x,
Sk4f r, Sk4f g, Sk4f b, Sk4f a,
Sk4f dr, Sk4f dg, Sk4f db, Sk4f da);
void flatten(SkWriteBuffer& buffer) const override {
buffer.writeScalar(fK[0]);
buffer.writeScalar(fK[1]);
buffer.writeScalar(fK[2]);
buffer.writeScalar(fK[3]);
buffer.writeBool(fEnforcePMColor);
}
SkScalar fK[4];
bool fEnforcePMColor;
friend class SkArithmeticMode;
typedef SkXfermode INHERITED;
};
sk_sp<SkFlattenable> SkArithmeticMode_scalar::CreateProc(SkReadBuffer& buffer) {
const SkScalar k1 = buffer.readScalar();
const SkScalar k2 = buffer.readScalar();
const SkScalar k3 = buffer.readScalar();
const SkScalar k4 = buffer.readScalar();
const bool enforcePMColor = buffer.readBool();
return SkArithmeticMode::Make(k1, k2, k3, k4, enforcePMColor);
}
void SK_VECTORCALL SkArithmeticMode_scalar::Stage(SkRasterPipeline::Stage* st, size_t x,
Sk4f r, Sk4f g, Sk4f b, Sk4f a,
Sk4f dr, Sk4f dg, Sk4f db, Sk4f da) {
auto self = st->ctx<const SkArithmeticMode_scalar*>();
const Sk4f k1 = self->fK[0],
k2 = self->fK[1],
k3 = self->fK[2],
k4 = self->fK[3];
r = k1*r*dr + k2*r + k3*dr + k4;
g = k1*g*dg + k2*g + k3*dg + k4;
b = k1*b*db + k2*b + k3*db + k4;
a = k1*a*da + k2*a + k3*da + k4;
// A later stage (clamp_01_premul) will pin and fEnforcePMColor for us.
st->next(x, r,g,b,a, dr,dg,db,da);
}
arithmetic mode with Sk4f After reading the SSE version, I figured I'd show off the new hotness a little. This'll get us SSE, NEON and portable implementations all in one easy to read package. Since we've been talking about it, it's worth noting the several ways this implementation is still not constant time: - short circuits on 0x00 and 0xff coverage; - floating point multiplication with untrusted k1-k4; if someone figures out a clever way to sometimes create denorm floats and sometimes not, there's a gigantic performance difference. I would hazard the pin is constant time now though. I've also fixed the lerp to lerp between dst and r instead of src and r. That can't have been right. curr/maxrss loops min median mean max stddev samples config bench 9/9 MB 1 25.5ms 25.5ms 25.5ms 25.5ms 0% ▃▁▁▃▂▇▅▆▇█ 8888 Xfermode_arithmetic_enforce_pm_aa 9/9 MB 1 24.1ms 24.2ms 24.2ms 24.3ms 0% ▄▃▁▄█▆▆█▃█ 8888 Xfermode_arithmetic_aa 9/9 MB 1 102ms 102ms 102ms 103ms 0% ▁▅▂▆▂█▂█▁▂ 8888 Xfermode_arithmetic_enforce_pm 9/9 MB 1 94.8ms 95.4ms 95.2ms 95.8ms 0% ▅▅▁▁▁▁▄▇█▇ 8888 Xfermode_arithmetic ~~~~> curr/maxrss loops min median mean max stddev samples config bench 9/9 MB 1 9.71ms 9.74ms 9.73ms 9.78ms 0% █▅▄▄▁▂▂▂▄▄ 8888 Xfermode_arithmetic_enforce_pm_aa 9/9 MB 1 9.5ms 9.57ms 9.58ms 9.7ms 1% ▂▁█▅▂▂▆▃▄▄ 8888 Xfermode_arithmetic_aa 9/9 MB 1 21.8ms 21.8ms 21.8ms 21.9ms 0% █▂▂▂▂▂▂▁▄▂ 8888 Xfermode_arithmetic_enforce_pm 9/9 MB 1 16.5ms 16.6ms 16.6ms 16.6ms 0% ▃█▁▁▄▄▁▁▆▅ 8888 Xfermode_arithmetic BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1873963003 CQ_EXTRA_TRYBOTS=client.skia:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD-Trybot Review URL: https://codereview.chromium.org/1873963003
2016-04-10 13:23:28 +00:00
void SkArithmeticMode_scalar::xfer32(SkPMColor dst[], const SkPMColor src[],
int count, const SkAlpha aaCoverage[]) const {
const Sk4f k1 = fK[0] * (1/255.0f),
k2 = fK[1],
k3 = fK[2],
k4 = fK[3] * 255.0f + 0.5f;
auto pin = [](float min, const Sk4f& val, float max) {
return Sk4f::Max(min, Sk4f::Min(val, max));
};
for (int i = 0; i < count; i++) {
if (aaCoverage && aaCoverage[i] == 0) {
continue;
}
arithmetic mode with Sk4f After reading the SSE version, I figured I'd show off the new hotness a little. This'll get us SSE, NEON and portable implementations all in one easy to read package. Since we've been talking about it, it's worth noting the several ways this implementation is still not constant time: - short circuits on 0x00 and 0xff coverage; - floating point multiplication with untrusted k1-k4; if someone figures out a clever way to sometimes create denorm floats and sometimes not, there's a gigantic performance difference. I would hazard the pin is constant time now though. I've also fixed the lerp to lerp between dst and r instead of src and r. That can't have been right. curr/maxrss loops min median mean max stddev samples config bench 9/9 MB 1 25.5ms 25.5ms 25.5ms 25.5ms 0% ▃▁▁▃▂▇▅▆▇█ 8888 Xfermode_arithmetic_enforce_pm_aa 9/9 MB 1 24.1ms 24.2ms 24.2ms 24.3ms 0% ▄▃▁▄█▆▆█▃█ 8888 Xfermode_arithmetic_aa 9/9 MB 1 102ms 102ms 102ms 103ms 0% ▁▅▂▆▂█▂█▁▂ 8888 Xfermode_arithmetic_enforce_pm 9/9 MB 1 94.8ms 95.4ms 95.2ms 95.8ms 0% ▅▅▁▁▁▁▄▇█▇ 8888 Xfermode_arithmetic ~~~~> curr/maxrss loops min median mean max stddev samples config bench 9/9 MB 1 9.71ms 9.74ms 9.73ms 9.78ms 0% █▅▄▄▁▂▂▂▄▄ 8888 Xfermode_arithmetic_enforce_pm_aa 9/9 MB 1 9.5ms 9.57ms 9.58ms 9.7ms 1% ▂▁█▅▂▂▆▃▄▄ 8888 Xfermode_arithmetic_aa 9/9 MB 1 21.8ms 21.8ms 21.8ms 21.9ms 0% █▂▂▂▂▂▂▁▄▂ 8888 Xfermode_arithmetic_enforce_pm 9/9 MB 1 16.5ms 16.6ms 16.6ms 16.6ms 0% ▃█▁▁▄▄▁▁▆▅ 8888 Xfermode_arithmetic BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1873963003 CQ_EXTRA_TRYBOTS=client.skia:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD-Trybot Review URL: https://codereview.chromium.org/1873963003
2016-04-10 13:23:28 +00:00
Sk4f s = SkNx_cast<float>(Sk4b::Load(src+i)),
d = SkNx_cast<float>(Sk4b::Load(dst+i)),
r = pin(0, k1*s*d + k2*s + k3*d + k4, 255);
arithmetic mode with Sk4f After reading the SSE version, I figured I'd show off the new hotness a little. This'll get us SSE, NEON and portable implementations all in one easy to read package. Since we've been talking about it, it's worth noting the several ways this implementation is still not constant time: - short circuits on 0x00 and 0xff coverage; - floating point multiplication with untrusted k1-k4; if someone figures out a clever way to sometimes create denorm floats and sometimes not, there's a gigantic performance difference. I would hazard the pin is constant time now though. I've also fixed the lerp to lerp between dst and r instead of src and r. That can't have been right. curr/maxrss loops min median mean max stddev samples config bench 9/9 MB 1 25.5ms 25.5ms 25.5ms 25.5ms 0% ▃▁▁▃▂▇▅▆▇█ 8888 Xfermode_arithmetic_enforce_pm_aa 9/9 MB 1 24.1ms 24.2ms 24.2ms 24.3ms 0% ▄▃▁▄█▆▆█▃█ 8888 Xfermode_arithmetic_aa 9/9 MB 1 102ms 102ms 102ms 103ms 0% ▁▅▂▆▂█▂█▁▂ 8888 Xfermode_arithmetic_enforce_pm 9/9 MB 1 94.8ms 95.4ms 95.2ms 95.8ms 0% ▅▅▁▁▁▁▄▇█▇ 8888 Xfermode_arithmetic ~~~~> curr/maxrss loops min median mean max stddev samples config bench 9/9 MB 1 9.71ms 9.74ms 9.73ms 9.78ms 0% █▅▄▄▁▂▂▂▄▄ 8888 Xfermode_arithmetic_enforce_pm_aa 9/9 MB 1 9.5ms 9.57ms 9.58ms 9.7ms 1% ▂▁█▅▂▂▆▃▄▄ 8888 Xfermode_arithmetic_aa 9/9 MB 1 21.8ms 21.8ms 21.8ms 21.9ms 0% █▂▂▂▂▂▂▁▄▂ 8888 Xfermode_arithmetic_enforce_pm 9/9 MB 1 16.5ms 16.6ms 16.6ms 16.6ms 0% ▃█▁▁▄▄▁▁▆▅ 8888 Xfermode_arithmetic BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1873963003 CQ_EXTRA_TRYBOTS=client.skia:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD-Trybot Review URL: https://codereview.chromium.org/1873963003
2016-04-10 13:23:28 +00:00
if (fEnforcePMColor) {
Sk4f a = SkNx_shuffle<3,3,3,3>(r);
r = Sk4f::Min(a, r);
}
arithmetic mode with Sk4f After reading the SSE version, I figured I'd show off the new hotness a little. This'll get us SSE, NEON and portable implementations all in one easy to read package. Since we've been talking about it, it's worth noting the several ways this implementation is still not constant time: - short circuits on 0x00 and 0xff coverage; - floating point multiplication with untrusted k1-k4; if someone figures out a clever way to sometimes create denorm floats and sometimes not, there's a gigantic performance difference. I would hazard the pin is constant time now though. I've also fixed the lerp to lerp between dst and r instead of src and r. That can't have been right. curr/maxrss loops min median mean max stddev samples config bench 9/9 MB 1 25.5ms 25.5ms 25.5ms 25.5ms 0% ▃▁▁▃▂▇▅▆▇█ 8888 Xfermode_arithmetic_enforce_pm_aa 9/9 MB 1 24.1ms 24.2ms 24.2ms 24.3ms 0% ▄▃▁▄█▆▆█▃█ 8888 Xfermode_arithmetic_aa 9/9 MB 1 102ms 102ms 102ms 103ms 0% ▁▅▂▆▂█▂█▁▂ 8888 Xfermode_arithmetic_enforce_pm 9/9 MB 1 94.8ms 95.4ms 95.2ms 95.8ms 0% ▅▅▁▁▁▁▄▇█▇ 8888 Xfermode_arithmetic ~~~~> curr/maxrss loops min median mean max stddev samples config bench 9/9 MB 1 9.71ms 9.74ms 9.73ms 9.78ms 0% █▅▄▄▁▂▂▂▄▄ 8888 Xfermode_arithmetic_enforce_pm_aa 9/9 MB 1 9.5ms 9.57ms 9.58ms 9.7ms 1% ▂▁█▅▂▂▆▃▄▄ 8888 Xfermode_arithmetic_aa 9/9 MB 1 21.8ms 21.8ms 21.8ms 21.9ms 0% █▂▂▂▂▂▂▁▄▂ 8888 Xfermode_arithmetic_enforce_pm 9/9 MB 1 16.5ms 16.6ms 16.6ms 16.6ms 0% ▃█▁▁▄▄▁▁▆▅ 8888 Xfermode_arithmetic BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1873963003 CQ_EXTRA_TRYBOTS=client.skia:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD-Trybot Review URL: https://codereview.chromium.org/1873963003
2016-04-10 13:23:28 +00:00
if (aaCoverage && aaCoverage[i] != 255) {
Sk4f c = aaCoverage[i] * (1/255.0f);
r = d + (r-d)*c;
}
arithmetic mode with Sk4f After reading the SSE version, I figured I'd show off the new hotness a little. This'll get us SSE, NEON and portable implementations all in one easy to read package. Since we've been talking about it, it's worth noting the several ways this implementation is still not constant time: - short circuits on 0x00 and 0xff coverage; - floating point multiplication with untrusted k1-k4; if someone figures out a clever way to sometimes create denorm floats and sometimes not, there's a gigantic performance difference. I would hazard the pin is constant time now though. I've also fixed the lerp to lerp between dst and r instead of src and r. That can't have been right. curr/maxrss loops min median mean max stddev samples config bench 9/9 MB 1 25.5ms 25.5ms 25.5ms 25.5ms 0% ▃▁▁▃▂▇▅▆▇█ 8888 Xfermode_arithmetic_enforce_pm_aa 9/9 MB 1 24.1ms 24.2ms 24.2ms 24.3ms 0% ▄▃▁▄█▆▆█▃█ 8888 Xfermode_arithmetic_aa 9/9 MB 1 102ms 102ms 102ms 103ms 0% ▁▅▂▆▂█▂█▁▂ 8888 Xfermode_arithmetic_enforce_pm 9/9 MB 1 94.8ms 95.4ms 95.2ms 95.8ms 0% ▅▅▁▁▁▁▄▇█▇ 8888 Xfermode_arithmetic ~~~~> curr/maxrss loops min median mean max stddev samples config bench 9/9 MB 1 9.71ms 9.74ms 9.73ms 9.78ms 0% █▅▄▄▁▂▂▂▄▄ 8888 Xfermode_arithmetic_enforce_pm_aa 9/9 MB 1 9.5ms 9.57ms 9.58ms 9.7ms 1% ▂▁█▅▂▂▆▃▄▄ 8888 Xfermode_arithmetic_aa 9/9 MB 1 21.8ms 21.8ms 21.8ms 21.9ms 0% █▂▂▂▂▂▂▁▄▂ 8888 Xfermode_arithmetic_enforce_pm 9/9 MB 1 16.5ms 16.6ms 16.6ms 16.6ms 0% ▃█▁▁▄▄▁▁▆▅ 8888 Xfermode_arithmetic BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1873963003 CQ_EXTRA_TRYBOTS=client.skia:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD-Trybot Review URL: https://codereview.chromium.org/1873963003
2016-04-10 13:23:28 +00:00
SkNx_cast<uint8_t>(r).store(dst+i);
}
}
#ifndef SK_IGNORE_TO_STRING
void SkArithmeticMode_scalar::toString(SkString* str) const {
str->append("SkArithmeticMode_scalar: ");
for (int i = 0; i < 4; ++i) {
str->appendScalar(fK[i]);
str->append(" ");
}
str->appendS32(fEnforcePMColor ? 1 : 0);
}
#endif
///////////////////////////////////////////////////////////////////////////////
sk_sp<SkXfermode> SkArithmeticMode::Make(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4,
bool enforcePMColor) {
if (SkScalarNearlyZero(k1) && SkScalarNearlyEqual(k2, SK_Scalar1) &&
SkScalarNearlyZero(k3) && SkScalarNearlyZero(k4)) {
return SkXfermode::Make(SkXfermode::kSrc_Mode);
} else if (SkScalarNearlyZero(k1) && SkScalarNearlyZero(k2) &&
SkScalarNearlyEqual(k3, SK_Scalar1) && SkScalarNearlyZero(k4)) {
return SkXfermode::Make(SkXfermode::kDst_Mode);
}
return sk_make_sp<SkArithmeticMode_scalar>(k1, k2, k3, k4, enforcePMColor);
}
//////////////////////////////////////////////////////////////////////////////
#if SK_SUPPORT_GPU
sk_sp<GrFragmentProcessor> SkArithmeticMode_scalar::makeFragmentProcessorForImageFilter(
sk_sp<GrFragmentProcessor> dst) const {
return GrArithmeticFP::Make(SkScalarToFloat(fK[0]),
SkScalarToFloat(fK[1]),
SkScalarToFloat(fK[2]),
SkScalarToFloat(fK[3]),
fEnforcePMColor,
std::move(dst));
}
sk_sp<GrXPFactory> SkArithmeticMode_scalar::asXPFactory() const {
return GrArithmeticXPFactory::Make(SkScalarToFloat(fK[0]),
SkScalarToFloat(fK[1]),
SkScalarToFloat(fK[2]),
SkScalarToFloat(fK[3]),
fEnforcePMColor);
}
#endif
SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkArithmeticMode)
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkArithmeticMode_scalar)
SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END