gfx/2d/SVGTurbulenceRenderer-inl.h

changeset 0
6474c204b198
     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

mercurial