Line data Source code
1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "2D.h"
7 : #include "Filters.h"
8 : #include "SIMD.h"
9 :
10 : namespace mozilla {
11 : namespace gfx {
12 :
13 : template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
14 : class SVGTurbulenceRenderer
15 : {
16 : public:
17 : SVGTurbulenceRenderer(const Size &aBaseFrequency, int32_t aSeed,
18 : int aNumOctaves, const Rect &aTileRect);
19 :
20 : already_AddRefed<DataSourceSurface> Render(const IntSize &aSize, const Point &aOffset) const;
21 :
22 : private:
23 : /* The turbulence calculation code is an adapted version of what
24 : appears in the SVG 1.1 specification:
25 : http://www.w3.org/TR/SVG11/filters.html#feTurbulence
26 : */
27 :
28 : struct StitchInfo {
29 : int32_t width; // How much to subtract to wrap for stitching.
30 : int32_t height;
31 : int32_t wrapX; // Minimum value to wrap.
32 : int32_t wrapY;
33 : };
34 :
35 : const static int sBSize = 0x100;
36 : const static int sBM = 0xff;
37 : void InitFromSeed(int32_t aSeed);
38 : void AdjustBaseFrequencyForStitch(const Rect &aTileRect);
39 : IntPoint AdjustForStitch(IntPoint aLatticePoint, const StitchInfo& aStitchInfo) const;
40 : StitchInfo CreateStitchInfo(const Rect &aTileRect) const;
41 : f32x4_t Noise2(Point aVec, const StitchInfo& aStitchInfo) const;
42 : i32x4_t Turbulence(const Point &aPoint) const;
43 : Point EquivalentNonNegativeOffset(const Point &aOffset) const;
44 :
45 : Size mBaseFrequency;
46 : int32_t mNumOctaves;
47 : StitchInfo mStitchInfo;
48 : bool mStitchable;
49 : TurbulenceType mType;
50 : uint8_t mLatticeSelector[sBSize];
51 : f32x4_t mGradient[sBSize][2];
52 : };
53 :
54 : namespace {
55 :
56 : struct RandomNumberSource
57 : {
58 0 : explicit RandomNumberSource(int32_t aSeed) : mLast(SetupSeed(aSeed)) {}
59 0 : int32_t Next() { mLast = Random(mLast); return mLast; }
60 :
61 : private:
62 : static const int32_t RAND_M = 2147483647; /* 2**31 - 1 */
63 : static const int32_t RAND_A = 16807; /* 7**5; primitive root of m */
64 : static const int32_t RAND_Q = 127773; /* m / a */
65 : static const int32_t RAND_R = 2836; /* m % a */
66 :
67 : /* Produces results in the range [1, 2**31 - 2].
68 : Algorithm is: r = (a * r) mod m
69 : where a = 16807 and m = 2**31 - 1 = 2147483647
70 : See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988
71 : To test: the algorithm should produce the result 1043618065
72 : as the 10,000th generated number if the original seed is 1.
73 : */
74 :
75 : static int32_t
76 0 : SetupSeed(int32_t aSeed) {
77 0 : if (aSeed <= 0)
78 0 : aSeed = -(aSeed % (RAND_M - 1)) + 1;
79 0 : if (aSeed > RAND_M - 1)
80 0 : aSeed = RAND_M - 1;
81 0 : return aSeed;
82 : }
83 :
84 : static int32_t
85 0 : Random(int32_t aSeed)
86 : {
87 0 : int32_t result = RAND_A * (aSeed % RAND_Q) - RAND_R * (aSeed / RAND_Q);
88 0 : if (result <= 0)
89 0 : result += RAND_M;
90 0 : return result;
91 : }
92 :
93 : int32_t mLast;
94 : };
95 :
96 : } // unnamed namespace
97 :
98 : template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
99 0 : SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::SVGTurbulenceRenderer(const Size &aBaseFrequency, int32_t aSeed,
100 : int aNumOctaves, const Rect &aTileRect)
101 : : mBaseFrequency(aBaseFrequency)
102 0 : , mNumOctaves(aNumOctaves)
103 : {
104 0 : InitFromSeed(aSeed);
105 : if (Stitch) {
106 0 : AdjustBaseFrequencyForStitch(aTileRect);
107 0 : mStitchInfo = CreateStitchInfo(aTileRect);
108 : }
109 0 : }
110 :
111 : template<typename T>
112 : static void
113 0 : Swap(T& a, T& b) {
114 0 : T c = a;
115 0 : a = b;
116 0 : b = c;
117 0 : }
118 :
119 : template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
120 : void
121 0 : SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::InitFromSeed(int32_t aSeed)
122 : {
123 0 : RandomNumberSource rand(aSeed);
124 :
125 : float gradient[4][sBSize][2];
126 0 : for (int32_t k = 0; k < 4; k++) {
127 0 : for (int32_t i = 0; i < sBSize; i++) {
128 : float a, b;
129 0 : do {
130 0 : a = float((rand.Next() % (sBSize + sBSize)) - sBSize) / sBSize;
131 0 : b = float((rand.Next() % (sBSize + sBSize)) - sBSize) / sBSize;
132 0 : } while (a == 0 && b == 0);
133 0 : float s = sqrt(a * a + b * b);
134 0 : gradient[k][i][0] = a / s;
135 0 : gradient[k][i][1] = b / s;
136 : }
137 : }
138 :
139 0 : for (int32_t i = 0; i < sBSize; i++) {
140 0 : mLatticeSelector[i] = i;
141 : }
142 0 : for (int32_t i1 = sBSize - 1; i1 > 0; i1--) {
143 0 : int32_t i2 = rand.Next() % sBSize;
144 0 : Swap(mLatticeSelector[i1], mLatticeSelector[i2]);
145 : }
146 :
147 0 : for (int32_t i = 0; i < sBSize; i++) {
148 : // Contrary to the code in the spec, we build the first lattice selector
149 : // lookup into mGradient so that we don't need to do it again for every
150 : // pixel.
151 : // We also change the order of the gradient indexing so that we can process
152 : // all four color channels at the same time.
153 0 : uint8_t j = mLatticeSelector[i];
154 0 : mGradient[i][0] = simd::FromF32<f32x4_t>(gradient[2][j][0], gradient[1][j][0],
155 : gradient[0][j][0], gradient[3][j][0]);
156 0 : mGradient[i][1] = simd::FromF32<f32x4_t>(gradient[2][j][1], gradient[1][j][1],
157 : gradient[0][j][1], gradient[3][j][1]);
158 : }
159 0 : }
160 :
161 : // Adjust aFreq such that aLength * AdjustForLength(aFreq, aLength) is integer
162 : // and as close to aLength * aFreq as possible.
163 : static inline float
164 0 : AdjustForLength(float aFreq, float aLength)
165 : {
166 0 : float lowFreq = floor(aLength * aFreq) / aLength;
167 0 : float hiFreq = ceil(aLength * aFreq) / aLength;
168 0 : if (aFreq / lowFreq < hiFreq / aFreq) {
169 0 : return lowFreq;
170 : }
171 0 : return hiFreq;
172 : }
173 :
174 : template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
175 : void
176 0 : SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::AdjustBaseFrequencyForStitch(const Rect &aTileRect)
177 : {
178 0 : mBaseFrequency = Size(AdjustForLength(mBaseFrequency.width, aTileRect.width),
179 0 : AdjustForLength(mBaseFrequency.height, aTileRect.height));
180 0 : }
181 :
182 : template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
183 : typename SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::StitchInfo
184 0 : SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::CreateStitchInfo(const Rect &aTileRect) const
185 : {
186 : StitchInfo stitch;
187 0 : stitch.width = int32_t(floorf(aTileRect.width * mBaseFrequency.width + 0.5f));
188 0 : stitch.height = int32_t(floorf(aTileRect.height * mBaseFrequency.height + 0.5f));
189 0 : stitch.wrapX = int32_t(aTileRect.x * mBaseFrequency.width) + stitch.width;
190 0 : stitch.wrapY = int32_t(aTileRect.y * mBaseFrequency.height) + stitch.height;
191 0 : return stitch;
192 : }
193 :
194 : static MOZ_ALWAYS_INLINE Float
195 0 : SCurve(Float t)
196 : {
197 0 : return t * t * (3 - 2 * t);
198 : }
199 :
200 : static MOZ_ALWAYS_INLINE Point
201 0 : SCurve(Point t)
202 : {
203 0 : return Point(SCurve(t.x), SCurve(t.y));
204 : }
205 :
206 : template<typename f32x4_t>
207 : static MOZ_ALWAYS_INLINE f32x4_t
208 0 : BiMix(const f32x4_t& aa, const f32x4_t& ab,
209 : const f32x4_t& ba, const f32x4_t& bb, Point s)
210 : {
211 0 : return simd::MixF32(simd::MixF32(aa, ab, s.x),
212 0 : simd::MixF32(ba, bb, s.x), s.y);
213 : }
214 :
215 : template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
216 : IntPoint
217 0 : SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::AdjustForStitch(IntPoint aLatticePoint,
218 : const StitchInfo& aStitchInfo) const
219 : {
220 : if (Stitch) {
221 0 : if (aLatticePoint.x >= aStitchInfo.wrapX) {
222 0 : aLatticePoint.x -= aStitchInfo.width;
223 : }
224 0 : if (aLatticePoint.y >= aStitchInfo.wrapY) {
225 0 : aLatticePoint.y -= aStitchInfo.height;
226 : }
227 : }
228 0 : return aLatticePoint;
229 : }
230 :
231 : template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
232 : f32x4_t
233 0 : SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::Noise2(Point aVec, const StitchInfo& aStitchInfo) const
234 : {
235 : // aVec is guaranteed to be non-negative, so casting to int32_t always
236 : // rounds towards negative infinity.
237 0 : IntPoint topLeftLatticePoint(int32_t(aVec.x), int32_t(aVec.y));
238 0 : Point r = aVec - topLeftLatticePoint; // fractional offset
239 :
240 0 : IntPoint b0 = AdjustForStitch(topLeftLatticePoint, aStitchInfo);
241 0 : IntPoint b1 = AdjustForStitch(b0 + IntPoint(1, 1), aStitchInfo);
242 :
243 0 : uint8_t i = mLatticeSelector[b0.x & sBM];
244 0 : uint8_t j = mLatticeSelector[b1.x & sBM];
245 :
246 0 : const f32x4_t* qua = mGradient[(i + b0.y) & sBM];
247 0 : const f32x4_t* qub = mGradient[(i + b1.y) & sBM];
248 0 : const f32x4_t* qva = mGradient[(j + b0.y) & sBM];
249 0 : const f32x4_t* qvb = mGradient[(j + b1.y) & sBM];
250 :
251 0 : return BiMix(simd::WSumF32(qua[0], qua[1], r.x, r.y),
252 0 : simd::WSumF32(qva[0], qva[1], r.x - 1.f, r.y),
253 0 : simd::WSumF32(qub[0], qub[1], r.x, r.y - 1.f),
254 0 : simd::WSumF32(qvb[0], qvb[1], r.x - 1.f, r.y - 1.f),
255 0 : SCurve(r));
256 : }
257 :
258 : template<typename f32x4_t, typename i32x4_t, typename u8x16_t>
259 : static inline i32x4_t
260 0 : ColorToBGRA(f32x4_t aUnscaledUnpreFloat)
261 : {
262 : // Color is an unpremultiplied float vector where 1.0f means white. We will
263 : // convert it into an integer vector where 255 means white.
264 0 : f32x4_t alpha = simd::SplatF32<3>(aUnscaledUnpreFloat);
265 0 : f32x4_t scaledUnpreFloat = simd::MulF32(aUnscaledUnpreFloat, simd::FromF32<f32x4_t>(255));
266 0 : i32x4_t scaledUnpreInt = simd::F32ToI32(scaledUnpreFloat);
267 :
268 : // Multiply all channels with alpha.
269 0 : i32x4_t scaledPreInt = simd::F32ToI32(simd::MulF32(scaledUnpreFloat, alpha));
270 :
271 : // Use the premultiplied color channels and the unpremultiplied alpha channel.
272 0 : i32x4_t alphaMask = simd::From32<i32x4_t>(0, 0, 0, -1);
273 0 : return simd::Pick(alphaMask, scaledPreInt, scaledUnpreInt);
274 : }
275 :
276 : template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
277 : i32x4_t
278 0 : SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::Turbulence(const Point &aPoint) const
279 : {
280 0 : StitchInfo stitchInfo = mStitchInfo;
281 0 : f32x4_t sum = simd::FromF32<f32x4_t>(0);
282 0 : Point vec(aPoint.x * mBaseFrequency.width, aPoint.y * mBaseFrequency.height);
283 0 : f32x4_t ratio = simd::FromF32<f32x4_t>(1);
284 :
285 0 : for (int octave = 0; octave < mNumOctaves; octave++) {
286 0 : f32x4_t thisOctave = Noise2(vec, stitchInfo);
287 : if (Type == TURBULENCE_TYPE_TURBULENCE) {
288 0 : thisOctave = simd::AbsF32(thisOctave);
289 : }
290 0 : sum = simd::AddF32(sum, simd::DivF32(thisOctave, ratio));
291 0 : vec = vec * 2;
292 0 : ratio = simd::MulF32(ratio, simd::FromF32<f32x4_t>(2));
293 :
294 : if (Stitch) {
295 0 : stitchInfo.width *= 2;
296 0 : stitchInfo.wrapX *= 2;
297 0 : stitchInfo.height *= 2;
298 0 : stitchInfo.wrapY *= 2;
299 : }
300 : }
301 :
302 : if (Type == TURBULENCE_TYPE_FRACTAL_NOISE) {
303 0 : sum = simd::DivF32(simd::AddF32(sum, simd::FromF32<f32x4_t>(1)), simd::FromF32<f32x4_t>(2));
304 : }
305 0 : return ColorToBGRA<f32x4_t,i32x4_t,u8x16_t>(sum);
306 : }
307 :
308 : static inline Float
309 0 : MakeNonNegative(Float aValue, Float aIncrementSize)
310 : {
311 0 : if (aValue >= 0) {
312 0 : return aValue;
313 : }
314 0 : return aValue + ceilf(-aValue / aIncrementSize) * aIncrementSize;
315 : }
316 :
317 : template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
318 : Point
319 0 : SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::EquivalentNonNegativeOffset(const Point &aOffset) const
320 : {
321 0 : Size basePeriod = Stitch ? Size(mStitchInfo.width, mStitchInfo.height) :
322 0 : Size(sBSize, sBSize);
323 0 : Size repeatingSize(basePeriod.width / mBaseFrequency.width,
324 0 : basePeriod.height / mBaseFrequency.height);
325 0 : return Point(MakeNonNegative(aOffset.x, repeatingSize.width),
326 0 : MakeNonNegative(aOffset.y, repeatingSize.height));
327 : }
328 :
329 : template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t>
330 : already_AddRefed<DataSourceSurface>
331 0 : SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::Render(const IntSize &aSize, const Point &aOffset) const
332 : {
333 : RefPtr<DataSourceSurface> target =
334 0 : Factory::CreateDataSourceSurface(aSize, SurfaceFormat::B8G8R8A8);
335 0 : if (!target) {
336 0 : return nullptr;
337 : }
338 :
339 0 : uint8_t* targetData = target->GetData();
340 0 : uint32_t stride = target->Stride();
341 :
342 0 : Point startOffset = EquivalentNonNegativeOffset(aOffset);
343 :
344 0 : for (int32_t y = 0; y < aSize.height; y++) {
345 0 : for (int32_t x = 0; x < aSize.width; x += 4) {
346 0 : int32_t targIndex = y * stride + x * 4;
347 0 : i32x4_t a = Turbulence(startOffset + Point(x, y));
348 0 : i32x4_t b = Turbulence(startOffset + Point(x + 1, y));
349 0 : i32x4_t c = Turbulence(startOffset + Point(x + 2, y));
350 0 : i32x4_t d = Turbulence(startOffset + Point(x + 3, y));
351 0 : u8x16_t result1234 = simd::PackAndSaturate32To8(a, b, c, d);
352 0 : simd::Store8(&targetData[targIndex], result1234);
353 : }
354 : }
355 :
356 0 : return target.forget();
357 : }
358 :
359 : } // namespace gfx
360 : } // namespace mozilla
|