1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
14 template <TurbulenceType Type
, bool Stitch
, typename f32x4_t
, typename i32x4_t
,
16 class SVGTurbulenceRenderer
{
18 SVGTurbulenceRenderer(const Size
& aBaseFrequency
, int32_t aSeed
,
19 int aNumOctaves
, const Rect
& aTileRect
);
21 already_AddRefed
<DataSourceSurface
> Render(const IntSize
& aSize
,
22 const Point
& aOffset
) const;
25 /* The turbulence calculation code is an adapted version of what
26 appears in the SVG 1.1 specification:
27 http://www.w3.org/TR/SVG11/filters.html#feTurbulence
31 int32_t width
; // How much to subtract to wrap for stitching.
33 int32_t wrapX
; // Minimum value to wrap.
37 const static int sBSize
= 0x100;
38 const static int sBM
= 0xff;
39 void InitFromSeed(int32_t aSeed
);
40 void AdjustBaseFrequencyForStitch(const Rect
& aTileRect
);
41 IntPoint
AdjustForStitch(IntPoint aLatticePoint
,
42 const StitchInfo
& aStitchInfo
) const;
43 StitchInfo
CreateStitchInfo(const Rect
& aTileRect
) const;
44 f32x4_t
Noise2(Point aVec
, const StitchInfo
& aStitchInfo
) const;
45 i32x4_t
Turbulence(const Point
& aPoint
) const;
46 Point
EquivalentNonNegativeOffset(const Point
& aOffset
) const;
50 StitchInfo mStitchInfo
;
53 uint8_t mLatticeSelector
[sBSize
];
54 f32x4_t mGradient
[sBSize
][2];
59 struct RandomNumberSource
{
60 explicit RandomNumberSource(int32_t aSeed
) : mLast(SetupSeed(aSeed
)) {}
62 mLast
= Random(mLast
);
67 static const int32_t RAND_M
= 2147483647; /* 2**31 - 1 */
68 static const int32_t RAND_A
= 16807; /* 7**5; primitive root of m */
69 static const int32_t RAND_Q
= 127773; /* m / a */
70 static const int32_t RAND_R
= 2836; /* m % a */
72 /* Produces results in the range [1, 2**31 - 2].
73 Algorithm is: r = (a * r) mod m
74 where a = 16807 and m = 2**31 - 1 = 2147483647
75 See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988
76 To test: the algorithm should produce the result 1043618065
77 as the 10,000th generated number if the original seed is 1.
80 static int32_t SetupSeed(int32_t aSeed
) {
81 if (aSeed
<= 0) aSeed
= -(aSeed
% (RAND_M
- 1)) + 1;
82 if (aSeed
> RAND_M
- 1) aSeed
= RAND_M
- 1;
86 static int32_t Random(int32_t aSeed
) {
87 int32_t result
= RAND_A
* (aSeed
% RAND_Q
) - RAND_R
* (aSeed
/ RAND_Q
);
88 if (result
<= 0) result
+= RAND_M
;
95 } // unnamed namespace
97 template <TurbulenceType Type
, bool Stitch
, typename f32x4_t
, typename i32x4_t
,
99 SVGTurbulenceRenderer
<Type
, Stitch
, f32x4_t
, i32x4_t
, u8x16_t
>::
100 SVGTurbulenceRenderer(const Size
& aBaseFrequency
, int32_t aSeed
,
101 int aNumOctaves
, const Rect
& aTileRect
)
102 : mBaseFrequency(aBaseFrequency
),
103 mNumOctaves(aNumOctaves
),
106 mType(TURBULENCE_TYPE_TURBULENCE
) {
109 AdjustBaseFrequencyForStitch(aTileRect
);
110 mStitchInfo
= CreateStitchInfo(aTileRect
);
114 template <TurbulenceType Type
, bool Stitch
, typename f32x4_t
, typename i32x4_t
,
116 void SVGTurbulenceRenderer
<Type
, Stitch
, f32x4_t
, i32x4_t
,
117 u8x16_t
>::InitFromSeed(int32_t aSeed
) {
118 RandomNumberSource
rand(aSeed
);
120 float gradient
[4][sBSize
][2];
121 for (int32_t k
= 0; k
< 4; k
++) {
122 for (int32_t i
= 0; i
< sBSize
; i
++) {
125 a
= float((rand
.Next() % (sBSize
+ sBSize
)) - sBSize
) / sBSize
;
126 b
= float((rand
.Next() % (sBSize
+ sBSize
)) - sBSize
) / sBSize
;
127 } while (a
== 0 && b
== 0);
128 float s
= sqrt(a
* a
+ b
* b
);
129 gradient
[k
][i
][0] = a
/ s
;
130 gradient
[k
][i
][1] = b
/ s
;
134 for (int32_t i
= 0; i
< sBSize
; i
++) {
135 mLatticeSelector
[i
] = i
;
137 for (int32_t i1
= sBSize
- 1; i1
> 0; i1
--) {
138 int32_t i2
= rand
.Next() % sBSize
;
139 std::swap(mLatticeSelector
[i1
], mLatticeSelector
[i2
]);
142 for (int32_t i
= 0; i
< sBSize
; i
++) {
143 // Contrary to the code in the spec, we build the first lattice selector
144 // lookup into mGradient so that we don't need to do it again for every
146 // We also change the order of the gradient indexing so that we can process
147 // all four color channels at the same time.
148 uint8_t j
= mLatticeSelector
[i
];
150 simd::FromF32
<f32x4_t
>(gradient
[2][j
][0], gradient
[1][j
][0],
151 gradient
[0][j
][0], gradient
[3][j
][0]);
153 simd::FromF32
<f32x4_t
>(gradient
[2][j
][1], gradient
[1][j
][1],
154 gradient
[0][j
][1], gradient
[3][j
][1]);
158 // Adjust aFreq such that aLength * AdjustForLength(aFreq, aLength) is integer
159 // and as close to aLength * aFreq as possible.
160 static inline float AdjustForLength(float aFreq
, float aLength
) {
161 float lowFreq
= floor(aLength
* aFreq
) / aLength
;
162 float hiFreq
= ceil(aLength
* aFreq
) / aLength
;
163 if (aFreq
/ lowFreq
< hiFreq
/ aFreq
) {
169 template <TurbulenceType Type
, bool Stitch
, typename f32x4_t
, typename i32x4_t
,
171 void SVGTurbulenceRenderer
<Type
, Stitch
, f32x4_t
, i32x4_t
, u8x16_t
>::
172 AdjustBaseFrequencyForStitch(const Rect
& aTileRect
) {
174 Size(AdjustForLength(mBaseFrequency
.width
, aTileRect
.Width()),
175 AdjustForLength(mBaseFrequency
.height
, aTileRect
.Height()));
178 template <TurbulenceType Type
, bool Stitch
, typename f32x4_t
, typename i32x4_t
,
180 typename SVGTurbulenceRenderer
<Type
, Stitch
, f32x4_t
, i32x4_t
,
182 SVGTurbulenceRenderer
<Type
, Stitch
, f32x4_t
, i32x4_t
,
183 u8x16_t
>::CreateStitchInfo(const Rect
& aTileRect
) const {
186 int32_t(floorf(aTileRect
.Width() * mBaseFrequency
.width
+ 0.5f
));
188 int32_t(floorf(aTileRect
.Height() * mBaseFrequency
.height
+ 0.5f
));
189 stitch
.wrapX
= int32_t(aTileRect
.X() * mBaseFrequency
.width
) + stitch
.width
;
190 stitch
.wrapY
= int32_t(aTileRect
.Y() * mBaseFrequency
.height
) + stitch
.height
;
194 static MOZ_ALWAYS_INLINE Float
SCurve(Float t
) { return t
* t
* (3 - 2 * t
); }
196 static MOZ_ALWAYS_INLINE Point
SCurve(Point t
) {
197 return Point(SCurve(t
.x
), SCurve(t
.y
));
200 template <typename f32x4_t
>
201 static MOZ_ALWAYS_INLINE f32x4_t
BiMix(const f32x4_t
& aa
, const f32x4_t
& ab
,
202 const f32x4_t
& ba
, const f32x4_t
& bb
,
204 return simd::MixF32(simd::MixF32(aa
, ab
, s
.x
), simd::MixF32(ba
, bb
, s
.x
),
208 template <TurbulenceType Type
, bool Stitch
, typename f32x4_t
, typename i32x4_t
,
211 SVGTurbulenceRenderer
<Type
, Stitch
, f32x4_t
, i32x4_t
, u8x16_t
>::AdjustForStitch(
212 IntPoint aLatticePoint
, const StitchInfo
& aStitchInfo
) const {
214 if (aLatticePoint
.x
>= aStitchInfo
.wrapX
) {
215 aLatticePoint
.x
-= aStitchInfo
.width
;
217 if (aLatticePoint
.y
>= aStitchInfo
.wrapY
) {
218 aLatticePoint
.y
-= aStitchInfo
.height
;
221 return aLatticePoint
;
224 template <TurbulenceType Type
, bool Stitch
, typename f32x4_t
, typename i32x4_t
,
226 f32x4_t SVGTurbulenceRenderer
<Type
, Stitch
, f32x4_t
, i32x4_t
, u8x16_t
>::Noise2(
227 Point aVec
, const StitchInfo
& aStitchInfo
) const {
228 // aVec is guaranteed to be non-negative, so casting to int32_t always
229 // rounds towards negative infinity.
230 IntPoint
topLeftLatticePoint(int32_t(aVec
.x
), int32_t(aVec
.y
));
231 Point r
= aVec
- topLeftLatticePoint
; // fractional offset
233 IntPoint b0
= AdjustForStitch(topLeftLatticePoint
, aStitchInfo
);
234 IntPoint b1
= AdjustForStitch(b0
+ IntPoint(1, 1), aStitchInfo
);
236 uint8_t i
= mLatticeSelector
[b0
.x
& sBM
];
237 uint8_t j
= mLatticeSelector
[b1
.x
& sBM
];
239 const f32x4_t
* qua
= mGradient
[(i
+ b0
.y
) & sBM
];
240 const f32x4_t
* qub
= mGradient
[(i
+ b1
.y
) & sBM
];
241 const f32x4_t
* qva
= mGradient
[(j
+ b0
.y
) & sBM
];
242 const f32x4_t
* qvb
= mGradient
[(j
+ b1
.y
) & sBM
];
244 return BiMix(simd::WSumF32(qua
[0], qua
[1], r
.x
, r
.y
),
245 simd::WSumF32(qva
[0], qva
[1], r
.x
- 1.f
, r
.y
),
246 simd::WSumF32(qub
[0], qub
[1], r
.x
, r
.y
- 1.f
),
247 simd::WSumF32(qvb
[0], qvb
[1], r
.x
- 1.f
, r
.y
- 1.f
), SCurve(r
));
250 template <typename f32x4_t
, typename i32x4_t
, typename u8x16_t
>
251 static inline i32x4_t
ColorToBGRA(f32x4_t aUnscaledUnpreFloat
) {
252 // Color is an unpremultiplied float vector where 1.0f means white. We will
253 // convert it into an integer vector where 255 means white.
254 f32x4_t alpha
= simd::SplatF32
<3>(aUnscaledUnpreFloat
);
255 f32x4_t scaledUnpreFloat
=
256 simd::MulF32(aUnscaledUnpreFloat
, simd::FromF32
<f32x4_t
>(255));
257 i32x4_t scaledUnpreInt
= simd::F32ToI32(scaledUnpreFloat
);
259 // Multiply all channels with alpha.
260 i32x4_t scaledPreInt
= simd::F32ToI32(simd::MulF32(scaledUnpreFloat
, alpha
));
262 // Use the premultiplied color channels and the unpremultiplied alpha channel.
263 i32x4_t alphaMask
= simd::From32
<i32x4_t
>(0, 0, 0, -1);
264 return simd::Pick(alphaMask
, scaledPreInt
, scaledUnpreInt
);
267 template <TurbulenceType Type
, bool Stitch
, typename f32x4_t
, typename i32x4_t
,
269 i32x4_t SVGTurbulenceRenderer
<Type
, Stitch
, f32x4_t
, i32x4_t
,
270 u8x16_t
>::Turbulence(const Point
& aPoint
) const {
271 StitchInfo stitchInfo
= mStitchInfo
;
272 f32x4_t sum
= simd::FromF32
<f32x4_t
>(0);
273 Point
vec(aPoint
.x
* mBaseFrequency
.width
, aPoint
.y
* mBaseFrequency
.height
);
274 f32x4_t ratio
= simd::FromF32
<f32x4_t
>(1);
276 for (int octave
= 0; octave
< mNumOctaves
; octave
++) {
277 f32x4_t thisOctave
= Noise2(vec
, stitchInfo
);
278 if (Type
== TURBULENCE_TYPE_TURBULENCE
) {
279 thisOctave
= simd::AbsF32(thisOctave
);
281 sum
= simd::AddF32(sum
, simd::DivF32(thisOctave
, ratio
));
283 ratio
= simd::MulF32(ratio
, simd::FromF32
<f32x4_t
>(2));
286 stitchInfo
.width
*= 2;
287 stitchInfo
.wrapX
*= 2;
288 stitchInfo
.height
*= 2;
289 stitchInfo
.wrapY
*= 2;
293 if (Type
== TURBULENCE_TYPE_FRACTAL_NOISE
) {
294 sum
= simd::DivF32(simd::AddF32(sum
, simd::FromF32
<f32x4_t
>(1)),
295 simd::FromF32
<f32x4_t
>(2));
297 return ColorToBGRA
<f32x4_t
, i32x4_t
, u8x16_t
>(sum
);
300 static inline Float
MakeNonNegative(Float aValue
, Float aIncrementSize
) {
301 if (aIncrementSize
== 0) {
307 return aValue
+ ceilf(-aValue
/ aIncrementSize
) * aIncrementSize
;
310 static inline Float
FiniteDivide(Float aValue
, Float aDivisor
) {
314 return aValue
/ aDivisor
;
317 template <TurbulenceType Type
, bool Stitch
, typename f32x4_t
, typename i32x4_t
,
319 Point SVGTurbulenceRenderer
<Type
, Stitch
, f32x4_t
, i32x4_t
, u8x16_t
>::
320 EquivalentNonNegativeOffset(const Point
& aOffset
) const {
321 Size basePeriod
= Stitch
? Size(mStitchInfo
.width
, mStitchInfo
.height
)
322 : Size(sBSize
, sBSize
);
323 Size
repeatingSize(FiniteDivide(basePeriod
.width
, mBaseFrequency
.width
),
324 FiniteDivide(basePeriod
.height
, mBaseFrequency
.height
));
325 return Point(MakeNonNegative(aOffset
.x
, repeatingSize
.width
),
326 MakeNonNegative(aOffset
.y
, repeatingSize
.height
));
329 template <TurbulenceType Type
, bool Stitch
, typename f32x4_t
, typename i32x4_t
,
331 already_AddRefed
<DataSourceSurface
>
332 SVGTurbulenceRenderer
<Type
, Stitch
, f32x4_t
, i32x4_t
, u8x16_t
>::Render(
333 const IntSize
& aSize
, const Point
& aOffset
) const {
334 RefPtr
<DataSourceSurface
> target
=
335 Factory::CreateDataSourceSurface(aSize
, SurfaceFormat::B8G8R8A8
);
340 DataSourceSurface::ScopedMap
map(target
, DataSourceSurface::READ_WRITE
);
341 uint8_t* targetData
= map
.GetData();
342 uint32_t stride
= map
.GetStride();
344 Point startOffset
= EquivalentNonNegativeOffset(aOffset
);
346 for (int32_t y
= 0; y
< aSize
.height
; y
++) {
347 for (int32_t x
= 0; x
< aSize
.width
; x
+= 4) {
348 int32_t targIndex
= y
* stride
+ x
* 4;
349 i32x4_t a
= Turbulence(startOffset
+ Point(x
, y
));
350 i32x4_t b
= Turbulence(startOffset
+ Point(x
+ 1, y
));
351 i32x4_t c
= Turbulence(startOffset
+ Point(x
+ 2, y
));
352 i32x4_t d
= Turbulence(startOffset
+ Point(x
+ 3, y
));
353 u8x16_t result1234
= simd::PackAndSaturate32To8(a
, b
, c
, d
);
354 simd::Store8(&targetData
[targIndex
], result1234
);
358 return target
.forget();
362 } // namespace mozilla