1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/2d/SVGTurbulenceRenderer-inl.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,357 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "2D.h" 1.10 +#include "Filters.h" 1.11 +#include "SIMD.h" 1.12 + 1.13 +namespace mozilla { 1.14 +namespace gfx { 1.15 + 1.16 +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> 1.17 +class SVGTurbulenceRenderer 1.18 +{ 1.19 +public: 1.20 + SVGTurbulenceRenderer(const Size &aBaseFrequency, int32_t aSeed, 1.21 + int aNumOctaves, const Rect &aTileRect); 1.22 + 1.23 + TemporaryRef<DataSourceSurface> Render(const IntSize &aSize, const Point &aOffset) const; 1.24 + 1.25 +private: 1.26 + /* The turbulence calculation code is an adapted version of what 1.27 + appears in the SVG 1.1 specification: 1.28 + http://www.w3.org/TR/SVG11/filters.html#feTurbulence 1.29 + */ 1.30 + 1.31 + struct StitchInfo { 1.32 + int32_t width; // How much to subtract to wrap for stitching. 1.33 + int32_t height; 1.34 + int32_t wrapX; // Minimum value to wrap. 1.35 + int32_t wrapY; 1.36 + }; 1.37 + 1.38 + const static int sBSize = 0x100; 1.39 + const static int sBM = 0xff; 1.40 + void InitFromSeed(int32_t aSeed); 1.41 + void AdjustBaseFrequencyForStitch(const Rect &aTileRect); 1.42 + IntPoint AdjustForStitch(IntPoint aLatticePoint, const StitchInfo& aStitchInfo) const; 1.43 + StitchInfo CreateStitchInfo(const Rect &aTileRect) const; 1.44 + f32x4_t Noise2(Point aVec, const StitchInfo& aStitchInfo) const; 1.45 + i32x4_t Turbulence(const Point &aPoint) const; 1.46 + Point EquivalentNonNegativeOffset(const Point &aOffset) const; 1.47 + 1.48 + Size mBaseFrequency; 1.49 + int32_t mNumOctaves; 1.50 + StitchInfo mStitchInfo; 1.51 + bool mStitchable; 1.52 + TurbulenceType mType; 1.53 + uint8_t mLatticeSelector[sBSize]; 1.54 + f32x4_t mGradient[sBSize][2]; 1.55 +}; 1.56 + 1.57 +namespace { 1.58 + 1.59 +struct RandomNumberSource 1.60 +{ 1.61 + RandomNumberSource(int32_t aSeed) : mLast(SetupSeed(aSeed)) {} 1.62 + int32_t Next() { mLast = Random(mLast); return mLast; } 1.63 + 1.64 +private: 1.65 + static const int32_t RAND_M = 2147483647; /* 2**31 - 1 */ 1.66 + static const int32_t RAND_A = 16807; /* 7**5; primitive root of m */ 1.67 + static const int32_t RAND_Q = 127773; /* m / a */ 1.68 + static const int32_t RAND_R = 2836; /* m % a */ 1.69 + 1.70 + /* Produces results in the range [1, 2**31 - 2]. 1.71 + Algorithm is: r = (a * r) mod m 1.72 + where a = 16807 and m = 2**31 - 1 = 2147483647 1.73 + See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988 1.74 + To test: the algorithm should produce the result 1043618065 1.75 + as the 10,000th generated number if the original seed is 1. 1.76 + */ 1.77 + 1.78 + static int32_t 1.79 + SetupSeed(int32_t aSeed) { 1.80 + if (aSeed <= 0) 1.81 + aSeed = -(aSeed % (RAND_M - 1)) + 1; 1.82 + if (aSeed > RAND_M - 1) 1.83 + aSeed = RAND_M - 1; 1.84 + return aSeed; 1.85 + } 1.86 + 1.87 + static int32_t 1.88 + Random(int32_t aSeed) 1.89 + { 1.90 + int32_t result = RAND_A * (aSeed % RAND_Q) - RAND_R * (aSeed / RAND_Q); 1.91 + if (result <= 0) 1.92 + result += RAND_M; 1.93 + return result; 1.94 + } 1.95 + 1.96 + int32_t mLast; 1.97 +}; 1.98 + 1.99 +} // unnamed namespace 1.100 + 1.101 +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> 1.102 +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::SVGTurbulenceRenderer(const Size &aBaseFrequency, int32_t aSeed, 1.103 + int aNumOctaves, const Rect &aTileRect) 1.104 + : mBaseFrequency(aBaseFrequency) 1.105 + , mNumOctaves(aNumOctaves) 1.106 +{ 1.107 + InitFromSeed(aSeed); 1.108 + if (Stitch) { 1.109 + AdjustBaseFrequencyForStitch(aTileRect); 1.110 + mStitchInfo = CreateStitchInfo(aTileRect); 1.111 + } 1.112 +} 1.113 + 1.114 +template<typename T> 1.115 +static void 1.116 +Swap(T& a, T& b) { 1.117 + T c = a; 1.118 + a = b; 1.119 + b = c; 1.120 +} 1.121 + 1.122 +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> 1.123 +void 1.124 +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::InitFromSeed(int32_t aSeed) 1.125 +{ 1.126 + RandomNumberSource rand(aSeed); 1.127 + 1.128 + float gradient[4][sBSize][2]; 1.129 + for (int32_t k = 0; k < 4; k++) { 1.130 + for (int32_t i = 0; i < sBSize; i++) { 1.131 + float a = float((rand.Next() % (sBSize + sBSize)) - sBSize) / sBSize; 1.132 + float b = float((rand.Next() % (sBSize + sBSize)) - sBSize) / sBSize; 1.133 + float s = sqrt(a * a + b * b); 1.134 + gradient[k][i][0] = a / s; 1.135 + gradient[k][i][1] = b / s; 1.136 + } 1.137 + } 1.138 + 1.139 + for (int32_t i = 0; i < sBSize; i++) { 1.140 + mLatticeSelector[i] = i; 1.141 + } 1.142 + for (int32_t i1 = sBSize - 1; i1 > 0; i1--) { 1.143 + int32_t i2 = rand.Next() % sBSize; 1.144 + Swap(mLatticeSelector[i1], mLatticeSelector[i2]); 1.145 + } 1.146 + 1.147 + for (int32_t i = 0; i < sBSize; i++) { 1.148 + // Contrary to the code in the spec, we build the first lattice selector 1.149 + // lookup into mGradient so that we don't need to do it again for every 1.150 + // pixel. 1.151 + // We also change the order of the gradient indexing so that we can process 1.152 + // all four color channels at the same time. 1.153 + uint8_t j = mLatticeSelector[i]; 1.154 + mGradient[i][0] = simd::FromF32<f32x4_t>(gradient[2][j][0], gradient[1][j][0], 1.155 + gradient[0][j][0], gradient[3][j][0]); 1.156 + mGradient[i][1] = simd::FromF32<f32x4_t>(gradient[2][j][1], gradient[1][j][1], 1.157 + gradient[0][j][1], gradient[3][j][1]); 1.158 + } 1.159 +} 1.160 + 1.161 +// Adjust aFreq such that aLength * AdjustForLength(aFreq, aLength) is integer 1.162 +// and as close to aLength * aFreq as possible. 1.163 +static inline float 1.164 +AdjustForLength(float aFreq, float aLength) 1.165 +{ 1.166 + float lowFreq = floor(aLength * aFreq) / aLength; 1.167 + float hiFreq = ceil(aLength * aFreq) / aLength; 1.168 + if (aFreq / lowFreq < hiFreq / aFreq) { 1.169 + return lowFreq; 1.170 + } 1.171 + return hiFreq; 1.172 +} 1.173 + 1.174 +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> 1.175 +void 1.176 +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::AdjustBaseFrequencyForStitch(const Rect &aTileRect) 1.177 +{ 1.178 + mBaseFrequency = Size(AdjustForLength(mBaseFrequency.width, aTileRect.width), 1.179 + AdjustForLength(mBaseFrequency.height, aTileRect.height)); 1.180 +} 1.181 + 1.182 +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> 1.183 +typename SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::StitchInfo 1.184 +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::CreateStitchInfo(const Rect &aTileRect) const 1.185 +{ 1.186 + StitchInfo stitch; 1.187 + stitch.width = int32_t(floorf(aTileRect.width * mBaseFrequency.width + 0.5f)); 1.188 + stitch.height = int32_t(floorf(aTileRect.height * mBaseFrequency.height + 0.5f)); 1.189 + stitch.wrapX = int32_t(aTileRect.x * mBaseFrequency.width) + stitch.width; 1.190 + stitch.wrapY = int32_t(aTileRect.y * mBaseFrequency.height) + stitch.height; 1.191 + return stitch; 1.192 +} 1.193 + 1.194 +static MOZ_ALWAYS_INLINE Float 1.195 +SCurve(Float t) 1.196 +{ 1.197 + return t * t * (3 - 2 * t); 1.198 +} 1.199 + 1.200 +static MOZ_ALWAYS_INLINE Point 1.201 +SCurve(Point t) 1.202 +{ 1.203 + return Point(SCurve(t.x), SCurve(t.y)); 1.204 +} 1.205 + 1.206 +template<typename f32x4_t> 1.207 +static MOZ_ALWAYS_INLINE f32x4_t 1.208 +BiMix(const f32x4_t& aa, const f32x4_t& ab, 1.209 + const f32x4_t& ba, const f32x4_t& bb, Point s) 1.210 +{ 1.211 + return simd::MixF32(simd::MixF32(aa, ab, s.x), 1.212 + simd::MixF32(ba, bb, s.x), s.y); 1.213 +} 1.214 + 1.215 +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> 1.216 +IntPoint 1.217 +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::AdjustForStitch(IntPoint aLatticePoint, 1.218 + const StitchInfo& aStitchInfo) const 1.219 +{ 1.220 + if (Stitch) { 1.221 + if (aLatticePoint.x >= aStitchInfo.wrapX) { 1.222 + aLatticePoint.x -= aStitchInfo.width; 1.223 + } 1.224 + if (aLatticePoint.y >= aStitchInfo.wrapY) { 1.225 + aLatticePoint.y -= aStitchInfo.height; 1.226 + } 1.227 + } 1.228 + return aLatticePoint; 1.229 +} 1.230 + 1.231 +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> 1.232 +f32x4_t 1.233 +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::Noise2(Point aVec, const StitchInfo& aStitchInfo) const 1.234 +{ 1.235 + // aVec is guaranteed to be non-negative, so casting to int32_t always 1.236 + // rounds towards negative infinity. 1.237 + IntPoint topLeftLatticePoint(int32_t(aVec.x), int32_t(aVec.y)); 1.238 + Point r = aVec - topLeftLatticePoint; // fractional offset 1.239 + 1.240 + IntPoint b0 = AdjustForStitch(topLeftLatticePoint, aStitchInfo); 1.241 + IntPoint b1 = AdjustForStitch(b0 + IntPoint(1, 1), aStitchInfo); 1.242 + 1.243 + uint8_t i = mLatticeSelector[b0.x & sBM]; 1.244 + uint8_t j = mLatticeSelector[b1.x & sBM]; 1.245 + 1.246 + const f32x4_t* qua = mGradient[(i + b0.y) & sBM]; 1.247 + const f32x4_t* qub = mGradient[(i + b1.y) & sBM]; 1.248 + const f32x4_t* qva = mGradient[(j + b0.y) & sBM]; 1.249 + const f32x4_t* qvb = mGradient[(j + b1.y) & sBM]; 1.250 + 1.251 + return BiMix(simd::WSumF32(qua[0], qua[1], r.x, r.y), 1.252 + simd::WSumF32(qva[0], qva[1], r.x - 1, r.y), 1.253 + simd::WSumF32(qub[0], qub[1], r.x, r.y - 1), 1.254 + simd::WSumF32(qvb[0], qvb[1], r.x - 1, r.y - 1), 1.255 + SCurve(r)); 1.256 +} 1.257 + 1.258 +template<typename f32x4_t, typename i32x4_t, typename u8x16_t> 1.259 +static inline i32x4_t 1.260 +ColorToBGRA(f32x4_t aUnscaledUnpreFloat) 1.261 +{ 1.262 + // Color is an unpremultiplied float vector where 1.0f means white. We will 1.263 + // convert it into an integer vector where 255 means white. 1.264 + f32x4_t alpha = simd::SplatF32<3>(aUnscaledUnpreFloat); 1.265 + f32x4_t scaledUnpreFloat = simd::MulF32(aUnscaledUnpreFloat, simd::FromF32<f32x4_t>(255)); 1.266 + i32x4_t scaledUnpreInt = simd::F32ToI32(scaledUnpreFloat); 1.267 + 1.268 + // Multiply all channels with alpha. 1.269 + i32x4_t scaledPreInt = simd::F32ToI32(simd::MulF32(scaledUnpreFloat, alpha)); 1.270 + 1.271 + // Use the premultiplied color channels and the unpremultiplied alpha channel. 1.272 + i32x4_t alphaMask = simd::From32<i32x4_t>(0, 0, 0, -1); 1.273 + return simd::Pick(alphaMask, scaledPreInt, scaledUnpreInt); 1.274 +} 1.275 + 1.276 +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> 1.277 +i32x4_t 1.278 +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::Turbulence(const Point &aPoint) const 1.279 +{ 1.280 + StitchInfo stitchInfo = mStitchInfo; 1.281 + f32x4_t sum = simd::FromF32<f32x4_t>(0); 1.282 + Point vec(aPoint.x * mBaseFrequency.width, aPoint.y * mBaseFrequency.height); 1.283 + f32x4_t ratio = simd::FromF32<f32x4_t>(1); 1.284 + 1.285 + for (int octave = 0; octave < mNumOctaves; octave++) { 1.286 + f32x4_t thisOctave = Noise2(vec, stitchInfo); 1.287 + if (Type == TURBULENCE_TYPE_TURBULENCE) { 1.288 + thisOctave = simd::AbsF32(thisOctave); 1.289 + } 1.290 + sum = simd::AddF32(sum, simd::DivF32(thisOctave, ratio)); 1.291 + vec = vec * 2; 1.292 + ratio = simd::MulF32(ratio, simd::FromF32<f32x4_t>(2)); 1.293 + 1.294 + if (Stitch) { 1.295 + stitchInfo.width *= 2; 1.296 + stitchInfo.wrapX *= 2; 1.297 + stitchInfo.height *= 2; 1.298 + stitchInfo.wrapY *= 2; 1.299 + } 1.300 + } 1.301 + 1.302 + if (Type == TURBULENCE_TYPE_FRACTAL_NOISE) { 1.303 + sum = simd::DivF32(simd::AddF32(sum, simd::FromF32<f32x4_t>(1)), simd::FromF32<f32x4_t>(2)); 1.304 + } 1.305 + return ColorToBGRA<f32x4_t,i32x4_t,u8x16_t>(sum); 1.306 +} 1.307 + 1.308 +static inline Float 1.309 +MakeNonNegative(Float aValue, Float aIncrementSize) 1.310 +{ 1.311 + if (aValue >= 0) { 1.312 + return aValue; 1.313 + } 1.314 + return aValue + ceilf(-aValue / aIncrementSize) * aIncrementSize; 1.315 +} 1.316 + 1.317 +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> 1.318 +Point 1.319 +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::EquivalentNonNegativeOffset(const Point &aOffset) const 1.320 +{ 1.321 + Size basePeriod = Stitch ? Size(mStitchInfo.width, mStitchInfo.height) : 1.322 + Size(sBSize, sBSize); 1.323 + Size repeatingSize(basePeriod.width / mBaseFrequency.width, 1.324 + basePeriod.height / mBaseFrequency.height); 1.325 + return Point(MakeNonNegative(aOffset.x, repeatingSize.width), 1.326 + MakeNonNegative(aOffset.y, repeatingSize.height)); 1.327 +} 1.328 + 1.329 +template<TurbulenceType Type, bool Stitch, typename f32x4_t, typename i32x4_t, typename u8x16_t> 1.330 +TemporaryRef<DataSourceSurface> 1.331 +SVGTurbulenceRenderer<Type,Stitch,f32x4_t,i32x4_t,u8x16_t>::Render(const IntSize &aSize, const Point &aOffset) const 1.332 +{ 1.333 + RefPtr<DataSourceSurface> target = 1.334 + Factory::CreateDataSourceSurface(aSize, SurfaceFormat::B8G8R8A8); 1.335 + if (!target) { 1.336 + return nullptr; 1.337 + } 1.338 + 1.339 + uint8_t* targetData = target->GetData(); 1.340 + uint32_t stride = target->Stride(); 1.341 + 1.342 + Point startOffset = EquivalentNonNegativeOffset(aOffset); 1.343 + 1.344 + for (int32_t y = 0; y < aSize.height; y++) { 1.345 + for (int32_t x = 0; x < aSize.width; x += 4) { 1.346 + int32_t targIndex = y * stride + x * 4; 1.347 + i32x4_t a = Turbulence(startOffset + Point(x, y)); 1.348 + i32x4_t b = Turbulence(startOffset + Point(x + 1, y)); 1.349 + i32x4_t c = Turbulence(startOffset + Point(x + 2, y)); 1.350 + i32x4_t d = Turbulence(startOffset + Point(x + 3, y)); 1.351 + u8x16_t result1234 = simd::PackAndSaturate32To8(a, b, c, d); 1.352 + simd::Store8(&targetData[targIndex], result1234); 1.353 + } 1.354 + } 1.355 + 1.356 + return target; 1.357 +} 1.358 + 1.359 +} // namespace gfx 1.360 +} // namespace mozilla