summaryrefslogtreecommitdiffstats
path: root/gfx/2d/SVGTurbulenceRenderer-inl.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /gfx/2d/SVGTurbulenceRenderer-inl.h
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/2d/SVGTurbulenceRenderer-inl.h')
-rw-r--r--gfx/2d/SVGTurbulenceRenderer-inl.h362
1 files changed, 362 insertions, 0 deletions
diff --git a/gfx/2d/SVGTurbulenceRenderer-inl.h b/gfx/2d/SVGTurbulenceRenderer-inl.h
new file mode 100644
index 0000000000..27448befe1
--- /dev/null
+++ b/gfx/2d/SVGTurbulenceRenderer-inl.h
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "2D.h"
+#include "Filters.h"
+#include "SIMD.h"
+
+namespace mozilla {
+namespace gfx {
+
+template <TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t,
+ typename u8x16_t>
+class SVGTurbulenceRenderer {
+ public:
+ SVGTurbulenceRenderer(const Size& aBaseFrequency, int32_t aSeed,
+ int aNumOctaves, const Rect& aTileRect);
+
+ already_AddRefed<DataSourceSurface> Render(const IntSize& aSize,
+ const Point& aOffset) const;
+
+ private:
+ /* The turbulence calculation code is an adapted version of what
+ appears in the SVG 1.1 specification:
+ http://www.w3.org/TR/SVG11/filters.html#feTurbulence
+ */
+
+ struct StitchInfo {
+ int32_t width; // How much to subtract to wrap for stitching.
+ int32_t height;
+ int32_t wrapX; // Minimum value to wrap.
+ int32_t wrapY;
+ };
+
+ const static int sBSize = 0x100;
+ const static int sBM = 0xff;
+ void InitFromSeed(int32_t aSeed);
+ void AdjustBaseFrequencyForStitch(const Rect& aTileRect);
+ IntPoint AdjustForStitch(IntPoint aLatticePoint,
+ const StitchInfo& aStitchInfo) const;
+ StitchInfo CreateStitchInfo(const Rect& aTileRect) const;
+ f32x4_t Noise2(Point aVec, const StitchInfo& aStitchInfo) const;
+ i32x4_t Turbulence(const Point& aPoint) const;
+ Point EquivalentNonNegativeOffset(const Point& aOffset) const;
+
+ Size mBaseFrequency;
+ int32_t mNumOctaves;
+ StitchInfo mStitchInfo;
+ bool mStitchable;
+ TurbulenceType mType;
+ uint8_t mLatticeSelector[sBSize];
+ f32x4_t mGradient[sBSize][2];
+};
+
+namespace {
+
+struct RandomNumberSource {
+ explicit RandomNumberSource(int32_t aSeed) : mLast(SetupSeed(aSeed)) {}
+ int32_t Next() {
+ mLast = Random(mLast);
+ return mLast;
+ }
+
+ private:
+ static const int32_t RAND_M = 2147483647; /* 2**31 - 1 */
+ static const int32_t RAND_A = 16807; /* 7**5; primitive root of m */
+ static const int32_t RAND_Q = 127773; /* m / a */
+ static const int32_t RAND_R = 2836; /* m % a */
+
+ /* Produces results in the range [1, 2**31 - 2].
+ Algorithm is: r = (a * r) mod m
+ where a = 16807 and m = 2**31 - 1 = 2147483647
+ See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988
+ To test: the algorithm should produce the result 1043618065
+ as the 10,000th generated number if the original seed is 1.
+ */
+
+ static int32_t SetupSeed(int32_t aSeed) {
+ if (aSeed <= 0) aSeed = -(aSeed % (RAND_M - 1)) + 1;
+ if (aSeed > RAND_M - 1) aSeed = RAND_M - 1;
+ return aSeed;
+ }
+
+ static int32_t Random(int32_t aSeed) {
+ int32_t result = RAND_A * (aSeed % RAND_Q) - RAND_R * (aSeed / RAND_Q);
+ if (result <= 0) result += RAND_M;
+ return result;
+ }
+
+ int32_t mLast;
+};
+
+} // unnamed namespace
+
+template <TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t,
+ typename u8x16_t>
+SVGTurbulenceRenderer<Type, Stitch, f32x4_t, i32x4_t, u8x16_t>::
+ SVGTurbulenceRenderer(const Size& aBaseFrequency, int32_t aSeed,
+ int aNumOctaves, const Rect& aTileRect)
+ : mBaseFrequency(aBaseFrequency),
+ mNumOctaves(aNumOctaves),
+ mStitchInfo(),
+ mStitchable(false),
+ mType(TURBULENCE_TYPE_TURBULENCE) {
+ InitFromSeed(aSeed);
+ if (Stitch) {
+ AdjustBaseFrequencyForStitch(aTileRect);
+ mStitchInfo = CreateStitchInfo(aTileRect);
+ }
+}
+
+template <TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t,
+ typename u8x16_t>
+void SVGTurbulenceRenderer<Type, Stitch, f32x4_t, i32x4_t,
+ u8x16_t>::InitFromSeed(int32_t aSeed) {
+ RandomNumberSource rand(aSeed);
+
+ float gradient[4][sBSize][2];
+ for (int32_t k = 0; k < 4; k++) {
+ for (int32_t i = 0; i < sBSize; i++) {
+ float a, b;
+ do {
+ a = float((rand.Next() % (sBSize + sBSize)) - sBSize) / sBSize;
+ b = float((rand.Next() % (sBSize + sBSize)) - sBSize) / sBSize;
+ } while (a == 0 && b == 0);
+ float s = sqrt(a * a + b * b);
+ gradient[k][i][0] = a / s;
+ gradient[k][i][1] = b / s;
+ }
+ }
+
+ for (int32_t i = 0; i < sBSize; i++) {
+ mLatticeSelector[i] = i;
+ }
+ for (int32_t i1 = sBSize - 1; i1 > 0; i1--) {
+ int32_t i2 = rand.Next() % sBSize;
+ std::swap(mLatticeSelector[i1], mLatticeSelector[i2]);
+ }
+
+ for (int32_t i = 0; i < sBSize; i++) {
+ // Contrary to the code in the spec, we build the first lattice selector
+ // lookup into mGradient so that we don't need to do it again for every
+ // pixel.
+ // We also change the order of the gradient indexing so that we can process
+ // all four color channels at the same time.
+ uint8_t j = mLatticeSelector[i];
+ mGradient[i][0] =
+ simd::FromF32<f32x4_t>(gradient[2][j][0], gradient[1][j][0],
+ gradient[0][j][0], gradient[3][j][0]);
+ mGradient[i][1] =
+ simd::FromF32<f32x4_t>(gradient[2][j][1], gradient[1][j][1],
+ gradient[0][j][1], gradient[3][j][1]);
+ }
+}
+
+// Adjust aFreq such that aLength * AdjustForLength(aFreq, aLength) is integer
+// and as close to aLength * aFreq as possible.
+static inline float AdjustForLength(float aFreq, float aLength) {
+ float lowFreq = floor(aLength * aFreq) / aLength;
+ float hiFreq = ceil(aLength * aFreq) / aLength;
+ if (aFreq / lowFreq < hiFreq / aFreq) {
+ return lowFreq;
+ }
+ return hiFreq;
+}
+
+template <TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t,
+ typename u8x16_t>
+void SVGTurbulenceRenderer<Type, Stitch, f32x4_t, i32x4_t, u8x16_t>::
+ AdjustBaseFrequencyForStitch(const Rect& aTileRect) {
+ mBaseFrequency =
+ Size(AdjustForLength(mBaseFrequency.width, aTileRect.Width()),
+ AdjustForLength(mBaseFrequency.height, aTileRect.Height()));
+}
+
+template <TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t,
+ typename u8x16_t>
+typename SVGTurbulenceRenderer<Type, Stitch, f32x4_t, i32x4_t,
+ u8x16_t>::StitchInfo
+SVGTurbulenceRenderer<Type, Stitch, f32x4_t, i32x4_t,
+ u8x16_t>::CreateStitchInfo(const Rect& aTileRect) const {
+ StitchInfo stitch;
+ stitch.width =
+ int32_t(floorf(aTileRect.Width() * mBaseFrequency.width + 0.5f));
+ stitch.height =
+ int32_t(floorf(aTileRect.Height() * mBaseFrequency.height + 0.5f));
+ stitch.wrapX = int32_t(aTileRect.X() * mBaseFrequency.width) + stitch.width;
+ stitch.wrapY = int32_t(aTileRect.Y() * mBaseFrequency.height) + stitch.height;
+ return stitch;
+}
+
+static MOZ_ALWAYS_INLINE Float SCurve(Float t) { return t * t * (3 - 2 * t); }
+
+static MOZ_ALWAYS_INLINE Point SCurve(Point t) {
+ return Point(SCurve(t.x), SCurve(t.y));
+}
+
+template <typename f32x4_t>
+static MOZ_ALWAYS_INLINE f32x4_t BiMix(const f32x4_t& aa, const f32x4_t& ab,
+ const f32x4_t& ba, const f32x4_t& bb,
+ Point s) {
+ return simd::MixF32(simd::MixF32(aa, ab, s.x), simd::MixF32(ba, bb, s.x),
+ s.y);
+}
+
+template <TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t,
+ typename u8x16_t>
+IntPoint
+SVGTurbulenceRenderer<Type, Stitch, f32x4_t, i32x4_t, u8x16_t>::AdjustForStitch(
+ IntPoint aLatticePoint, const StitchInfo& aStitchInfo) const {
+ if (Stitch) {
+ if (aLatticePoint.x >= aStitchInfo.wrapX) {
+ aLatticePoint.x -= aStitchInfo.width;
+ }
+ if (aLatticePoint.y >= aStitchInfo.wrapY) {
+ aLatticePoint.y -= aStitchInfo.height;
+ }
+ }
+ return aLatticePoint;
+}
+
+template <TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t,
+ typename u8x16_t>
+f32x4_t SVGTurbulenceRenderer<Type, Stitch, f32x4_t, i32x4_t, u8x16_t>::Noise2(
+ Point aVec, const StitchInfo& aStitchInfo) const {
+ // aVec is guaranteed to be non-negative, so casting to int32_t always
+ // rounds towards negative infinity.
+ IntPoint topLeftLatticePoint(int32_t(aVec.x), int32_t(aVec.y));
+ Point r = aVec - topLeftLatticePoint; // fractional offset
+
+ IntPoint b0 = AdjustForStitch(topLeftLatticePoint, aStitchInfo);
+ IntPoint b1 = AdjustForStitch(b0 + IntPoint(1, 1), aStitchInfo);
+
+ uint8_t i = mLatticeSelector[b0.x & sBM];
+ uint8_t j = mLatticeSelector[b1.x & sBM];
+
+ const f32x4_t* qua = mGradient[(i + b0.y) & sBM];
+ const f32x4_t* qub = mGradient[(i + b1.y) & sBM];
+ const f32x4_t* qva = mGradient[(j + b0.y) & sBM];
+ const f32x4_t* qvb = mGradient[(j + b1.y) & sBM];
+
+ return BiMix(simd::WSumF32(qua[0], qua[1], r.x, r.y),
+ simd::WSumF32(qva[0], qva[1], r.x - 1.f, r.y),
+ simd::WSumF32(qub[0], qub[1], r.x, r.y - 1.f),
+ simd::WSumF32(qvb[0], qvb[1], r.x - 1.f, r.y - 1.f), SCurve(r));
+}
+
+template <typename f32x4_t, typename i32x4_t, typename u8x16_t>
+static inline i32x4_t ColorToBGRA(f32x4_t aUnscaledUnpreFloat) {
+ // Color is an unpremultiplied float vector where 1.0f means white. We will
+ // convert it into an integer vector where 255 means white.
+ f32x4_t alpha = simd::SplatF32<3>(aUnscaledUnpreFloat);
+ f32x4_t scaledUnpreFloat =
+ simd::MulF32(aUnscaledUnpreFloat, simd::FromF32<f32x4_t>(255));
+ i32x4_t scaledUnpreInt = simd::F32ToI32(scaledUnpreFloat);
+
+ // Multiply all channels with alpha.
+ i32x4_t scaledPreInt = simd::F32ToI32(simd::MulF32(scaledUnpreFloat, alpha));
+
+ // Use the premultiplied color channels and the unpremultiplied alpha channel.
+ i32x4_t alphaMask = simd::From32<i32x4_t>(0, 0, 0, -1);
+ return simd::Pick(alphaMask, scaledPreInt, scaledUnpreInt);
+}
+
+template <TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t,
+ typename u8x16_t>
+i32x4_t SVGTurbulenceRenderer<Type, Stitch, f32x4_t, i32x4_t,
+ u8x16_t>::Turbulence(const Point& aPoint) const {
+ StitchInfo stitchInfo = mStitchInfo;
+ f32x4_t sum = simd::FromF32<f32x4_t>(0);
+ Point vec(aPoint.x * mBaseFrequency.width, aPoint.y * mBaseFrequency.height);
+ f32x4_t ratio = simd::FromF32<f32x4_t>(1);
+
+ for (int octave = 0; octave < mNumOctaves; octave++) {
+ f32x4_t thisOctave = Noise2(vec, stitchInfo);
+ if (Type == TURBULENCE_TYPE_TURBULENCE) {
+ thisOctave = simd::AbsF32(thisOctave);
+ }
+ sum = simd::AddF32(sum, simd::DivF32(thisOctave, ratio));
+ vec = vec * 2;
+ ratio = simd::MulF32(ratio, simd::FromF32<f32x4_t>(2));
+
+ if (Stitch) {
+ stitchInfo.width *= 2;
+ stitchInfo.wrapX *= 2;
+ stitchInfo.height *= 2;
+ stitchInfo.wrapY *= 2;
+ }
+ }
+
+ if (Type == TURBULENCE_TYPE_FRACTAL_NOISE) {
+ sum = simd::DivF32(simd::AddF32(sum, simd::FromF32<f32x4_t>(1)),
+ simd::FromF32<f32x4_t>(2));
+ }
+ return ColorToBGRA<f32x4_t, i32x4_t, u8x16_t>(sum);
+}
+
+static inline Float MakeNonNegative(Float aValue, Float aIncrementSize) {
+ if (aIncrementSize == 0) {
+ return 0;
+ }
+ if (aValue >= 0) {
+ return aValue;
+ }
+ return aValue + ceilf(-aValue / aIncrementSize) * aIncrementSize;
+}
+
+static inline Float FiniteDivide(Float aValue, Float aDivisor) {
+ if (aDivisor == 0) {
+ return 0;
+ }
+ return aValue / aDivisor;
+}
+
+template <TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t,
+ typename u8x16_t>
+Point SVGTurbulenceRenderer<Type, Stitch, f32x4_t, i32x4_t, u8x16_t>::
+ EquivalentNonNegativeOffset(const Point& aOffset) const {
+ Size basePeriod = Stitch ? Size(mStitchInfo.width, mStitchInfo.height)
+ : Size(sBSize, sBSize);
+ Size repeatingSize(FiniteDivide(basePeriod.width, mBaseFrequency.width),
+ FiniteDivide(basePeriod.height, mBaseFrequency.height));
+ return Point(MakeNonNegative(aOffset.x, repeatingSize.width),
+ MakeNonNegative(aOffset.y, repeatingSize.height));
+}
+
+template <TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t,
+ typename u8x16_t>
+already_AddRefed<DataSourceSurface>
+SVGTurbulenceRenderer<Type, Stitch, f32x4_t, i32x4_t, u8x16_t>::Render(
+ const IntSize& aSize, const Point& aOffset) const {
+ RefPtr<DataSourceSurface> target =
+ Factory::CreateDataSourceSurface(aSize, SurfaceFormat::B8G8R8A8);
+ if (!target) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap map(target, DataSourceSurface::READ_WRITE);
+ uint8_t* targetData = map.GetData();
+ uint32_t stride = map.GetStride();
+
+ Point startOffset = EquivalentNonNegativeOffset(aOffset);
+
+ for (int32_t y = 0; y < aSize.height; y++) {
+ for (int32_t x = 0; x < aSize.width; x += 4) {
+ int32_t targIndex = y * stride + x * 4;
+ i32x4_t a = Turbulence(startOffset + Point(x, y));
+ i32x4_t b = Turbulence(startOffset + Point(x + 1, y));
+ i32x4_t c = Turbulence(startOffset + Point(x + 2, y));
+ i32x4_t d = Turbulence(startOffset + Point(x + 3, y));
+ u8x16_t result1234 = simd::PackAndSaturate32To8(a, b, c, d);
+ simd::Store8(&targetData[targIndex], result1234);
+ }
+ }
+
+ return target.forget();
+}
+
+} // namespace gfx
+} // namespace mozilla