gfx/2d/FilterNodeSoftware.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/2d/FilterNodeSoftware.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3544 @@
     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 +#define _USE_MATH_DEFINES
    1.10 +
    1.11 +#include <cmath>
    1.12 +#include "FilterNodeSoftware.h"
    1.13 +#include "2D.h"
    1.14 +#include "Tools.h"
    1.15 +#include "Blur.h"
    1.16 +#include <map>
    1.17 +#include "FilterProcessing.h"
    1.18 +#include "mozilla/PodOperations.h"
    1.19 +#include "mozilla/DebugOnly.h"
    1.20 +
    1.21 +// #define DEBUG_DUMP_SURFACES
    1.22 +
    1.23 +#ifdef DEBUG_DUMP_SURFACES
    1.24 +#include "gfxImageSurface.h"
    1.25 +namespace mozilla {
    1.26 +namespace gfx {
    1.27 +static void
    1.28 +DumpAsPNG(SourceSurface* aSurface)
    1.29 +{
    1.30 +  RefPtr<DataSourceSurface> dataSource = aSurface->GetDataSurface();
    1.31 +  IntSize size = dataSource->GetSize();
    1.32 +  nsRefPtr<gfxImageSurface> imageSurface =
    1.33 +    new gfxImageSurface(dataSource->GetData(), gfxIntSize(size.width, size.height),
    1.34 +                        dataSource->Stride(),
    1.35 +                        aSurface->GetFormat() == SurfaceFormat::A8 ? gfxImageFormat::A8 : gfxImageFormat::ARGB32);
    1.36 +  imageSurface->PrintAsDataURL();
    1.37 +}
    1.38 +} // namespace gfx
    1.39 +} // namespace mozilla
    1.40 +#endif
    1.41 +
    1.42 +namespace mozilla {
    1.43 +namespace gfx {
    1.44 +
    1.45 +namespace {
    1.46 +
    1.47 +/**
    1.48 + * This class provides a way to get a pow() results in constant-time. It works
    1.49 + * by caching 256 values for bases between 0 and 1 and a fixed exponent.
    1.50 + **/
    1.51 +class PowCache
    1.52 +{
    1.53 +public:
    1.54 +  PowCache()
    1.55 +  {
    1.56 +    CacheForExponent(0.0f);
    1.57 +  }
    1.58 +
    1.59 +  void CacheForExponent(Float aExponent)
    1.60 +  {
    1.61 +    mExponent = aExponent;
    1.62 +    int numPreSquares = 0;
    1.63 +    while (numPreSquares < 5 && mExponent > (1 << (numPreSquares + 2))) {
    1.64 +      numPreSquares++;
    1.65 +    }
    1.66 +    mNumPowTablePreSquares = numPreSquares;
    1.67 +    for (size_t i = 0; i < sCacheSize; i++) {
    1.68 +      // sCacheSize is chosen in such a way that a takes values
    1.69 +      // from 0.0 to 1.0 inclusive.
    1.70 +      Float a = i / Float(1 << sCacheIndexPrecisionBits);
    1.71 +      MOZ_ASSERT(0.0f <= a && a <= 1.0f, "We only want to cache for bases between 0 and 1.");
    1.72 +
    1.73 +      for (int j = 0; j < mNumPowTablePreSquares; j++) {
    1.74 +        a = sqrt(a);
    1.75 +      }
    1.76 +      uint32_t cachedInt = pow(a, mExponent) * (1 << sOutputIntPrecisionBits);
    1.77 +      MOZ_ASSERT(cachedInt < (1 << (sizeof(mPowTable[i]) * 8)), "mPowCache integer type too small");
    1.78 +
    1.79 +      mPowTable[i] = cachedInt;
    1.80 +    }
    1.81 +  }
    1.82 +
    1.83 +  uint16_t Pow(uint16_t aBase)
    1.84 +  {
    1.85 +    // Results should be similar to what the following code would produce:
    1.86 +    // Float x = Float(aBase) / (1 << sInputIntPrecisionBits);
    1.87 +    // return uint16_t(pow(x, mExponent) * (1 << sOutputIntPrecisionBits));
    1.88 +
    1.89 +    MOZ_ASSERT(aBase <= (1 << sInputIntPrecisionBits), "aBase needs to be between 0 and 1!");
    1.90 +
    1.91 +    uint32_t a = aBase;
    1.92 +    for (int j = 0; j < mNumPowTablePreSquares; j++) {
    1.93 +      a = a * a >> sInputIntPrecisionBits;
    1.94 +    }
    1.95 +    uint32_t i = a >> (sInputIntPrecisionBits - sCacheIndexPrecisionBits);
    1.96 +    MOZ_ASSERT(i < sCacheSize, "out-of-bounds mPowTable access");
    1.97 +    return mPowTable[i];
    1.98 +  }
    1.99 +
   1.100 +  static const int sInputIntPrecisionBits = 15;
   1.101 +  static const int sOutputIntPrecisionBits = 15;
   1.102 +  static const int sCacheIndexPrecisionBits = 7;
   1.103 +
   1.104 +private:
   1.105 +  static const size_t sCacheSize = (1 << sCacheIndexPrecisionBits) + 1;
   1.106 +
   1.107 +  Float mExponent;
   1.108 +  int mNumPowTablePreSquares;
   1.109 +  uint16_t mPowTable[sCacheSize];
   1.110 +};
   1.111 +
   1.112 +class PointLightSoftware
   1.113 +{
   1.114 +public:
   1.115 +  bool SetAttribute(uint32_t aIndex, Float) { return false; }
   1.116 +  bool SetAttribute(uint32_t aIndex, const Point3D &);
   1.117 +  void Prepare() {}
   1.118 +  Point3D GetVectorToLight(const Point3D &aTargetPoint);
   1.119 +  uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
   1.120 +
   1.121 +private:
   1.122 +  Point3D mPosition;
   1.123 +};
   1.124 +
   1.125 +class SpotLightSoftware
   1.126 +{
   1.127 +public:
   1.128 +  SpotLightSoftware();
   1.129 +  bool SetAttribute(uint32_t aIndex, Float);
   1.130 +  bool SetAttribute(uint32_t aIndex, const Point3D &);
   1.131 +  void Prepare();
   1.132 +  Point3D GetVectorToLight(const Point3D &aTargetPoint);
   1.133 +  uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
   1.134 +
   1.135 +private:
   1.136 +  Point3D mPosition;
   1.137 +  Point3D mPointsAt;
   1.138 +  Point3D mVectorFromFocusPointToLight;
   1.139 +  Float mSpecularFocus;
   1.140 +  Float mLimitingConeAngle;
   1.141 +  Float mLimitingConeCos;
   1.142 +  PowCache mPowCache;
   1.143 +};
   1.144 +
   1.145 +class DistantLightSoftware
   1.146 +{
   1.147 +public:
   1.148 +  DistantLightSoftware();
   1.149 +  bool SetAttribute(uint32_t aIndex, Float);
   1.150 +  bool SetAttribute(uint32_t aIndex, const Point3D &) { return false; }
   1.151 +  void Prepare();
   1.152 +  Point3D GetVectorToLight(const Point3D &aTargetPoint);
   1.153 +  uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
   1.154 +
   1.155 +private:
   1.156 +  Float mAzimuth;
   1.157 +  Float mElevation;
   1.158 +  Point3D mVectorToLight;
   1.159 +};
   1.160 +
   1.161 +class DiffuseLightingSoftware
   1.162 +{
   1.163 +public:
   1.164 +  DiffuseLightingSoftware();
   1.165 +  bool SetAttribute(uint32_t aIndex, Float);
   1.166 +  void Prepare() {}
   1.167 +  uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight,
   1.168 +                      uint32_t aColor);
   1.169 +
   1.170 +private:
   1.171 +  Float mDiffuseConstant;
   1.172 +};
   1.173 +
   1.174 +class SpecularLightingSoftware
   1.175 +{
   1.176 +public:
   1.177 +  SpecularLightingSoftware();
   1.178 +  bool SetAttribute(uint32_t aIndex, Float);
   1.179 +  void Prepare();
   1.180 +  uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight,
   1.181 +                      uint32_t aColor);
   1.182 +
   1.183 +private:
   1.184 +  Float mSpecularConstant;
   1.185 +  Float mSpecularExponent;
   1.186 +  uint32_t mSpecularConstantInt;
   1.187 +  PowCache mPowCache;
   1.188 +};
   1.189 +
   1.190 +} // unnamed namespace
   1.191 +
   1.192 +// from xpcom/ds/nsMathUtils.h
   1.193 +static int32_t
   1.194 +NS_lround(double x)
   1.195 +{
   1.196 +  return x >= 0.0 ? int32_t(x + 0.5) : int32_t(x - 0.5);
   1.197 +}
   1.198 +
   1.199 +void
   1.200 +ClearDataSourceSurface(DataSourceSurface *aSurface)
   1.201 +{
   1.202 +  size_t numBytes = aSurface->GetSize().height * aSurface->Stride();
   1.203 +  uint8_t* data = aSurface->GetData();
   1.204 +  PodZero(data, numBytes);
   1.205 +}
   1.206 +
   1.207 +// This check is safe against integer overflow.
   1.208 +static bool
   1.209 +SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint)
   1.210 +{
   1.211 +  IntSize size = aSurface->GetSize();
   1.212 +  return aPoint.x >= 0 && aPoint.x < size.width &&
   1.213 +         aPoint.y >= 0 && aPoint.y < size.height;
   1.214 +}
   1.215 +
   1.216 +static uint8_t*
   1.217 +DataAtOffset(DataSourceSurface* aSurface, IntPoint aPoint)
   1.218 +{
   1.219 +  if (!SurfaceContainsPoint(aSurface, aPoint)) {
   1.220 +    MOZ_CRASH("sample position needs to be inside surface!");
   1.221 +  }
   1.222 +
   1.223 +  MOZ_ASSERT(Factory::CheckSurfaceSize(aSurface->GetSize()),
   1.224 +             "surface size overflows - this should have been prevented when the surface was created");
   1.225 +
   1.226 +  uint8_t* data = aSurface->GetData() + aPoint.y * aSurface->Stride() +
   1.227 +    aPoint.x * BytesPerPixel(aSurface->GetFormat());
   1.228 +
   1.229 +  if (data < aSurface->GetData()) {
   1.230 +    MOZ_CRASH("out-of-range data access");
   1.231 +  }
   1.232 +
   1.233 +  return data;
   1.234 +}
   1.235 +
   1.236 +static bool
   1.237 +IntRectOverflows(const IntRect& aRect)
   1.238 +{
   1.239 +  CheckedInt<int32_t> xMost = aRect.x;
   1.240 +  xMost += aRect.width;
   1.241 +  CheckedInt<int32_t> yMost = aRect.y;
   1.242 +  yMost += aRect.height;
   1.243 +  return !xMost.isValid() || !yMost.isValid();
   1.244 +}
   1.245 +
   1.246 +/**
   1.247 + * aSrcRect: Rect relative to the aSrc surface
   1.248 + * aDestPoint: Point inside aDest surface
   1.249 + */
   1.250 +static void
   1.251 +CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest,
   1.252 +         IntRect aSrcRect, IntPoint aDestPoint)
   1.253 +{
   1.254 +  if (IntRectOverflows(aSrcRect) ||
   1.255 +      IntRectOverflows(IntRect(aDestPoint, aSrcRect.Size()))) {
   1.256 +    MOZ_CRASH("we should never be getting invalid rects at this point");
   1.257 +  }
   1.258 +
   1.259 +  MOZ_ASSERT(aSrc->GetFormat() == aDest->GetFormat(), "different surface formats");
   1.260 +  MOZ_ASSERT(IntRect(IntPoint(), aSrc->GetSize()).Contains(aSrcRect), "source rect too big for source surface");
   1.261 +  MOZ_ASSERT(IntRect(IntPoint(), aDest->GetSize()).Contains(aSrcRect - aSrcRect.TopLeft() + aDestPoint), "dest surface too small");
   1.262 +
   1.263 +  if (aSrcRect.IsEmpty()) {
   1.264 +    return;
   1.265 +  }
   1.266 +
   1.267 +  uint8_t* sourceData = DataAtOffset(aSrc, aSrcRect.TopLeft());
   1.268 +  uint32_t sourceStride = aSrc->Stride();
   1.269 +  uint8_t* destData = DataAtOffset(aDest, aDestPoint);
   1.270 +  uint32_t destStride = aDest->Stride();
   1.271 +
   1.272 +  if (BytesPerPixel(aSrc->GetFormat()) == 4) {
   1.273 +    for (int32_t y = 0; y < aSrcRect.height; y++) {
   1.274 +      PodCopy((int32_t*)destData, (int32_t*)sourceData, aSrcRect.width);
   1.275 +      sourceData += sourceStride;
   1.276 +      destData += destStride;
   1.277 +    }
   1.278 +  } else if (BytesPerPixel(aSrc->GetFormat()) == 1) {
   1.279 +    for (int32_t y = 0; y < aSrcRect.height; y++) {
   1.280 +      PodCopy(destData, sourceData, aSrcRect.width);
   1.281 +      sourceData += sourceStride;
   1.282 +      destData += destStride;
   1.283 +    }
   1.284 +  }
   1.285 +}
   1.286 +
   1.287 +TemporaryRef<DataSourceSurface>
   1.288 +CloneAligned(DataSourceSurface* aSource)
   1.289 +{
   1.290 +  RefPtr<DataSourceSurface> copy =
   1.291 +    Factory::CreateDataSourceSurface(aSource->GetSize(), aSource->GetFormat());
   1.292 +  if (copy) {
   1.293 +    CopyRect(aSource, copy, IntRect(IntPoint(), aSource->GetSize()), IntPoint());
   1.294 +  }
   1.295 +  return copy;
   1.296 +}
   1.297 +
   1.298 +static void
   1.299 +FillRectWithPixel(DataSourceSurface *aSurface, const IntRect &aFillRect, IntPoint aPixelPos)
   1.300 +{
   1.301 +  MOZ_ASSERT(!IntRectOverflows(aFillRect));
   1.302 +  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
   1.303 +             "aFillRect needs to be completely inside the surface");
   1.304 +  MOZ_ASSERT(SurfaceContainsPoint(aSurface, aPixelPos),
   1.305 +             "aPixelPos needs to be inside the surface");
   1.306 +
   1.307 +  int32_t stride = aSurface->Stride();
   1.308 +  uint8_t* sourcePixelData = DataAtOffset(aSurface, aPixelPos);
   1.309 +  uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft());
   1.310 +  int bpp = BytesPerPixel(aSurface->GetFormat());
   1.311 +
   1.312 +  // Fill the first row by hand.
   1.313 +  if (bpp == 4) {
   1.314 +    uint32_t sourcePixel = *(uint32_t*)sourcePixelData;
   1.315 +    for (int32_t x = 0; x < aFillRect.width; x++) {
   1.316 +      *((uint32_t*)data + x) = sourcePixel;
   1.317 +    }
   1.318 +  } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
   1.319 +    uint8_t sourcePixel = *sourcePixelData;
   1.320 +    memset(data, sourcePixel, aFillRect.width);
   1.321 +  }
   1.322 +
   1.323 +  // Copy the first row into the other rows.
   1.324 +  for (int32_t y = 1; y < aFillRect.height; y++) {
   1.325 +    PodCopy(data + y * stride, data, aFillRect.width * bpp);
   1.326 +  }
   1.327 +}
   1.328 +
   1.329 +static void
   1.330 +FillRectWithVerticallyRepeatingHorizontalStrip(DataSourceSurface *aSurface,
   1.331 +                                               const IntRect &aFillRect,
   1.332 +                                               const IntRect &aSampleRect)
   1.333 +{
   1.334 +  MOZ_ASSERT(!IntRectOverflows(aFillRect));
   1.335 +  MOZ_ASSERT(!IntRectOverflows(aSampleRect));
   1.336 +  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
   1.337 +             "aFillRect needs to be completely inside the surface");
   1.338 +  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
   1.339 +             "aSampleRect needs to be completely inside the surface");
   1.340 +
   1.341 +  int32_t stride = aSurface->Stride();
   1.342 +  uint8_t* sampleData = DataAtOffset(aSurface, aSampleRect.TopLeft());
   1.343 +  uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft());
   1.344 +  if (BytesPerPixel(aSurface->GetFormat()) == 4) {
   1.345 +    for (int32_t y = 0; y < aFillRect.height; y++) {
   1.346 +      PodCopy((uint32_t*)data, (uint32_t*)sampleData, aFillRect.width);
   1.347 +      data += stride;
   1.348 +    }
   1.349 +  } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
   1.350 +    for (int32_t y = 0; y < aFillRect.height; y++) {
   1.351 +      PodCopy(data, sampleData, aFillRect.width);
   1.352 +      data += stride;
   1.353 +    }
   1.354 +  }
   1.355 +}
   1.356 +
   1.357 +static void
   1.358 +FillRectWithHorizontallyRepeatingVerticalStrip(DataSourceSurface *aSurface,
   1.359 +                                               const IntRect &aFillRect,
   1.360 +                                               const IntRect &aSampleRect)
   1.361 +{
   1.362 +  MOZ_ASSERT(!IntRectOverflows(aFillRect));
   1.363 +  MOZ_ASSERT(!IntRectOverflows(aSampleRect));
   1.364 +  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
   1.365 +             "aFillRect needs to be completely inside the surface");
   1.366 +  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
   1.367 +             "aSampleRect needs to be completely inside the surface");
   1.368 +
   1.369 +  int32_t stride = aSurface->Stride();
   1.370 +  uint8_t* sampleData = DataAtOffset(aSurface, aSampleRect.TopLeft());
   1.371 +  uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft());
   1.372 +  if (BytesPerPixel(aSurface->GetFormat()) == 4) {
   1.373 +    for (int32_t y = 0; y < aFillRect.height; y++) {
   1.374 +      int32_t sampleColor = *((uint32_t*)sampleData);
   1.375 +      for (int32_t x = 0; x < aFillRect.width; x++) {
   1.376 +        *((uint32_t*)data + x) = sampleColor;
   1.377 +      }
   1.378 +      data += stride;
   1.379 +      sampleData += stride;
   1.380 +    }
   1.381 +  } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
   1.382 +    for (int32_t y = 0; y < aFillRect.height; y++) {
   1.383 +      uint8_t sampleColor = *sampleData;
   1.384 +      memset(data, sampleColor, aFillRect.width);
   1.385 +      data += stride;
   1.386 +      sampleData += stride;
   1.387 +    }
   1.388 +  }
   1.389 +}
   1.390 +
   1.391 +static void
   1.392 +DuplicateEdges(DataSourceSurface* aSurface, const IntRect &aFromRect)
   1.393 +{
   1.394 +  MOZ_ASSERT(!IntRectOverflows(aFromRect));
   1.395 +  MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFromRect),
   1.396 +             "aFromRect needs to be completely inside the surface");
   1.397 +
   1.398 +  IntSize size = aSurface->GetSize();
   1.399 +  IntRect fill;
   1.400 +  IntRect sampleRect;
   1.401 +  for (int32_t ix = 0; ix < 3; ix++) {
   1.402 +    switch (ix) {
   1.403 +      case 0:
   1.404 +        fill.x = 0;
   1.405 +        fill.width = aFromRect.x;
   1.406 +        sampleRect.x = fill.XMost();
   1.407 +        sampleRect.width = 1;
   1.408 +        break;
   1.409 +      case 1:
   1.410 +        fill.x = aFromRect.x;
   1.411 +        fill.width = aFromRect.width;
   1.412 +        sampleRect.x = fill.x;
   1.413 +        sampleRect.width = fill.width;
   1.414 +        break;
   1.415 +      case 2:
   1.416 +        fill.x = aFromRect.XMost();
   1.417 +        fill.width = size.width - fill.x;
   1.418 +        sampleRect.x = fill.x - 1;
   1.419 +        sampleRect.width = 1;
   1.420 +        break;
   1.421 +    }
   1.422 +    if (fill.width <= 0) {
   1.423 +      continue;
   1.424 +    }
   1.425 +    bool xIsMiddle = (ix == 1);
   1.426 +    for (int32_t iy = 0; iy < 3; iy++) {
   1.427 +      switch (iy) {
   1.428 +        case 0:
   1.429 +          fill.y = 0;
   1.430 +          fill.height = aFromRect.y;
   1.431 +          sampleRect.y = fill.YMost();
   1.432 +          sampleRect.height = 1;
   1.433 +          break;
   1.434 +        case 1:
   1.435 +          fill.y = aFromRect.y;
   1.436 +          fill.height = aFromRect.height;
   1.437 +          sampleRect.y = fill.y;
   1.438 +          sampleRect.height = fill.height;
   1.439 +          break;
   1.440 +        case 2:
   1.441 +          fill.y = aFromRect.YMost();
   1.442 +          fill.height = size.height - fill.y;
   1.443 +          sampleRect.y = fill.y - 1;
   1.444 +          sampleRect.height = 1;
   1.445 +          break;
   1.446 +      }
   1.447 +      if (fill.height <= 0) {
   1.448 +        continue;
   1.449 +      }
   1.450 +      bool yIsMiddle = (iy == 1);
   1.451 +      if (!xIsMiddle && !yIsMiddle) {
   1.452 +        // Corner
   1.453 +        FillRectWithPixel(aSurface, fill, sampleRect.TopLeft());
   1.454 +      }
   1.455 +      if (xIsMiddle && !yIsMiddle) {
   1.456 +        // Top middle or bottom middle
   1.457 +        FillRectWithVerticallyRepeatingHorizontalStrip(aSurface, fill, sampleRect);
   1.458 +      }
   1.459 +      if (!xIsMiddle && yIsMiddle) {
   1.460 +        // Left middle or right middle
   1.461 +        FillRectWithHorizontallyRepeatingVerticalStrip(aSurface, fill, sampleRect);
   1.462 +      }
   1.463 +    }
   1.464 +  }
   1.465 +}
   1.466 +
   1.467 +static IntPoint
   1.468 +TileIndex(const IntRect &aFirstTileRect, const IntPoint &aPoint)
   1.469 +{
   1.470 +  return IntPoint(int32_t(floor(double(aPoint.x - aFirstTileRect.x) / aFirstTileRect.width)),
   1.471 +                  int32_t(floor(double(aPoint.y - aFirstTileRect.y) / aFirstTileRect.height)));
   1.472 +}
   1.473 +
   1.474 +static void
   1.475 +TileSurface(DataSourceSurface* aSource, DataSourceSurface* aTarget, const IntPoint &aOffset)
   1.476 +{
   1.477 +  IntRect sourceRect(aOffset, aSource->GetSize());
   1.478 +  IntRect targetRect(IntPoint(0, 0), aTarget->GetSize());
   1.479 +  IntPoint startIndex = TileIndex(sourceRect, targetRect.TopLeft());
   1.480 +  IntPoint endIndex = TileIndex(sourceRect, targetRect.BottomRight());
   1.481 +
   1.482 +  for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
   1.483 +    for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
   1.484 +      IntPoint destPoint(sourceRect.x + ix * sourceRect.width,
   1.485 +                         sourceRect.y + iy * sourceRect.height);
   1.486 +      IntRect destRect(destPoint, sourceRect.Size());
   1.487 +      destRect = destRect.Intersect(targetRect);
   1.488 +      IntRect srcRect = destRect - destPoint;
   1.489 +      CopyRect(aSource, aTarget, srcRect, destRect.TopLeft());
   1.490 +    }
   1.491 +  }
   1.492 +}
   1.493 +
   1.494 +static TemporaryRef<DataSourceSurface>
   1.495 +GetDataSurfaceInRect(SourceSurface *aSurface,
   1.496 +                     const IntRect &aSurfaceRect,
   1.497 +                     const IntRect &aDestRect,
   1.498 +                     ConvolveMatrixEdgeMode aEdgeMode)
   1.499 +{
   1.500 +  MOZ_ASSERT(aSurface ? aSurfaceRect.Size() == aSurface->GetSize() : aSurfaceRect.IsEmpty());
   1.501 +
   1.502 +  if (IntRectOverflows(aSurfaceRect) || IntRectOverflows(aDestRect)) {
   1.503 +    // We can't rely on the intersection calculations below to make sense when
   1.504 +    // XMost() or YMost() overflow. Bail out.
   1.505 +    return nullptr;
   1.506 +  }
   1.507 +
   1.508 +  IntRect sourceRect = aSurfaceRect;
   1.509 +
   1.510 +  if (sourceRect.IsEqualEdges(aDestRect)) {
   1.511 +    return aSurface ? aSurface->GetDataSurface() : nullptr;
   1.512 +  }
   1.513 +
   1.514 +  IntRect intersect = sourceRect.Intersect(aDestRect);
   1.515 +  IntRect intersectInSourceSpace = intersect - sourceRect.TopLeft();
   1.516 +  IntRect intersectInDestSpace = intersect - aDestRect.TopLeft();
   1.517 +  SurfaceFormat format = aSurface ? aSurface->GetFormat() : SurfaceFormat(SurfaceFormat::B8G8R8A8);
   1.518 +
   1.519 +  RefPtr<DataSourceSurface> target =
   1.520 +    Factory::CreateDataSourceSurface(aDestRect.Size(), format);
   1.521 +
   1.522 +  if (!target) {
   1.523 +    return nullptr;
   1.524 +  }
   1.525 +
   1.526 +  if (aEdgeMode == EDGE_MODE_NONE && !aSurfaceRect.Contains(aDestRect)) {
   1.527 +    ClearDataSourceSurface(target);
   1.528 +  }
   1.529 +
   1.530 +  if (!aSurface) {
   1.531 +    return target;
   1.532 +  }
   1.533 +
   1.534 +  RefPtr<DataSourceSurface> dataSource = aSurface->GetDataSurface();
   1.535 +  MOZ_ASSERT(dataSource);
   1.536 +
   1.537 +  if (aEdgeMode == EDGE_MODE_WRAP) {
   1.538 +    TileSurface(dataSource, target, intersectInDestSpace.TopLeft());
   1.539 +    return target;
   1.540 +  }
   1.541 +
   1.542 +  CopyRect(dataSource, target, intersectInSourceSpace,
   1.543 +           intersectInDestSpace.TopLeft());
   1.544 +
   1.545 +  if (aEdgeMode == EDGE_MODE_DUPLICATE) {
   1.546 +    DuplicateEdges(target, intersectInDestSpace);
   1.547 +  }
   1.548 +
   1.549 +  return target;
   1.550 +}
   1.551 +
   1.552 +/* static */ TemporaryRef<FilterNode>
   1.553 +FilterNodeSoftware::Create(FilterType aType)
   1.554 +{
   1.555 +  RefPtr<FilterNodeSoftware> filter;
   1.556 +  switch (aType) {
   1.557 +    case FilterType::BLEND:
   1.558 +      filter = new FilterNodeBlendSoftware();
   1.559 +      break;
   1.560 +    case FilterType::TRANSFORM:
   1.561 +      filter = new FilterNodeTransformSoftware();
   1.562 +      break;
   1.563 +    case FilterType::MORPHOLOGY:
   1.564 +      filter = new FilterNodeMorphologySoftware();
   1.565 +      break;
   1.566 +    case FilterType::COLOR_MATRIX:
   1.567 +      filter = new FilterNodeColorMatrixSoftware();
   1.568 +      break;
   1.569 +    case FilterType::FLOOD:
   1.570 +      filter = new FilterNodeFloodSoftware();
   1.571 +      break;
   1.572 +    case FilterType::TILE:
   1.573 +      filter = new FilterNodeTileSoftware();
   1.574 +      break;
   1.575 +    case FilterType::TABLE_TRANSFER:
   1.576 +      filter = new FilterNodeTableTransferSoftware();
   1.577 +      break;
   1.578 +    case FilterType::DISCRETE_TRANSFER:
   1.579 +      filter = new FilterNodeDiscreteTransferSoftware();
   1.580 +      break;
   1.581 +    case FilterType::LINEAR_TRANSFER:
   1.582 +      filter = new FilterNodeLinearTransferSoftware();
   1.583 +      break;
   1.584 +    case FilterType::GAMMA_TRANSFER:
   1.585 +      filter = new FilterNodeGammaTransferSoftware();
   1.586 +      break;
   1.587 +    case FilterType::CONVOLVE_MATRIX:
   1.588 +      filter = new FilterNodeConvolveMatrixSoftware();
   1.589 +      break;
   1.590 +    case FilterType::DISPLACEMENT_MAP:
   1.591 +      filter = new FilterNodeDisplacementMapSoftware();
   1.592 +      break;
   1.593 +    case FilterType::TURBULENCE:
   1.594 +      filter = new FilterNodeTurbulenceSoftware();
   1.595 +      break;
   1.596 +    case FilterType::ARITHMETIC_COMBINE:
   1.597 +      filter = new FilterNodeArithmeticCombineSoftware();
   1.598 +      break;
   1.599 +    case FilterType::COMPOSITE:
   1.600 +      filter = new FilterNodeCompositeSoftware();
   1.601 +      break;
   1.602 +    case FilterType::GAUSSIAN_BLUR:
   1.603 +      filter = new FilterNodeGaussianBlurSoftware();
   1.604 +      break;
   1.605 +    case FilterType::DIRECTIONAL_BLUR:
   1.606 +      filter = new FilterNodeDirectionalBlurSoftware();
   1.607 +      break;
   1.608 +    case FilterType::CROP:
   1.609 +      filter = new FilterNodeCropSoftware();
   1.610 +      break;
   1.611 +    case FilterType::PREMULTIPLY:
   1.612 +      filter = new FilterNodePremultiplySoftware();
   1.613 +      break;
   1.614 +    case FilterType::UNPREMULTIPLY:
   1.615 +      filter = new FilterNodeUnpremultiplySoftware();
   1.616 +      break;
   1.617 +    case FilterType::POINT_DIFFUSE:
   1.618 +      filter = new FilterNodeLightingSoftware<PointLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<PointLight, DiffuseLighting>");
   1.619 +      break;
   1.620 +    case FilterType::POINT_SPECULAR:
   1.621 +      filter = new FilterNodeLightingSoftware<PointLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<PointLight, SpecularLighting>");
   1.622 +      break;
   1.623 +    case FilterType::SPOT_DIFFUSE:
   1.624 +      filter = new FilterNodeLightingSoftware<SpotLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<SpotLight, DiffuseLighting>");
   1.625 +      break;
   1.626 +    case FilterType::SPOT_SPECULAR:
   1.627 +      filter = new FilterNodeLightingSoftware<SpotLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<SpotLight, SpecularLighting>");
   1.628 +      break;
   1.629 +    case FilterType::DISTANT_DIFFUSE:
   1.630 +      filter = new FilterNodeLightingSoftware<DistantLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<DistantLight, DiffuseLighting>");
   1.631 +      break;
   1.632 +    case FilterType::DISTANT_SPECULAR:
   1.633 +      filter = new FilterNodeLightingSoftware<DistantLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<DistantLight, SpecularLighting>");
   1.634 +      break;
   1.635 +  }
   1.636 +  return filter;
   1.637 +}
   1.638 +
   1.639 +void
   1.640 +FilterNodeSoftware::Draw(DrawTarget* aDrawTarget,
   1.641 +                         const Rect &aSourceRect,
   1.642 +                         const Point &aDestPoint,
   1.643 +                         const DrawOptions &aOptions)
   1.644 +{
   1.645 +#ifdef DEBUG_DUMP_SURFACES
   1.646 +  printf("<style>section{margin:10px;}</style><pre>\nRendering filter %s...\n", GetName());
   1.647 +#endif
   1.648 +
   1.649 +  Rect renderRect = aSourceRect;
   1.650 +  renderRect.RoundOut();
   1.651 +  IntRect renderIntRect;
   1.652 +  if (!renderRect.ToIntRect(&renderIntRect)) {
   1.653 +#ifdef DEBUG_DUMP_SURFACES
   1.654 +    printf("render rect overflowed, not painting anything\n");
   1.655 +    printf("</pre>\n");
   1.656 +#endif
   1.657 +    return;
   1.658 +  }
   1.659 +
   1.660 +  IntRect outputRect = GetOutputRectInRect(renderIntRect);
   1.661 +  if (IntRectOverflows(outputRect)) {
   1.662 +#ifdef DEBUG_DUMP_SURFACES
   1.663 +    printf("output rect overflowed, not painting anything\n");
   1.664 +    printf("</pre>\n");
   1.665 +#endif
   1.666 +    return;
   1.667 +  }
   1.668 +
   1.669 +  RefPtr<DataSourceSurface> result;
   1.670 +  if (!outputRect.IsEmpty()) {
   1.671 +    result = GetOutput(outputRect);
   1.672 +  }
   1.673 +
   1.674 +  if (!result) {
   1.675 +    // Null results are allowed and treated as transparent. Don't draw anything.
   1.676 +#ifdef DEBUG_DUMP_SURFACES
   1.677 +    printf("output returned null\n");
   1.678 +    printf("</pre>\n");
   1.679 +#endif
   1.680 +    return;
   1.681 +  }
   1.682 +
   1.683 +#ifdef DEBUG_DUMP_SURFACES
   1.684 +  printf("output from %s:\n", GetName());
   1.685 +  printf("<img src='"); DumpAsPNG(result); printf("'>\n");
   1.686 +  printf("</pre>\n");
   1.687 +#endif
   1.688 +
   1.689 +  Point sourceToDestOffset = aDestPoint - aSourceRect.TopLeft();
   1.690 +  Rect renderedSourceRect = Rect(outputRect).Intersect(aSourceRect);
   1.691 +  Rect renderedDestRect = renderedSourceRect + sourceToDestOffset;
   1.692 +  if (result->GetFormat() == SurfaceFormat::A8) {
   1.693 +    // Interpret the result as having implicitly black color channels.
   1.694 +    aDrawTarget->PushClipRect(renderedDestRect);
   1.695 +    aDrawTarget->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)),
   1.696 +                             result,
   1.697 +                             Point(outputRect.TopLeft()) + sourceToDestOffset,
   1.698 +                             aOptions);
   1.699 +    aDrawTarget->PopClip();
   1.700 +  } else {
   1.701 +    aDrawTarget->DrawSurface(result, renderedDestRect,
   1.702 +                             renderedSourceRect - Point(outputRect.TopLeft()),
   1.703 +                             DrawSurfaceOptions(), aOptions);
   1.704 +  }
   1.705 +}
   1.706 +
   1.707 +TemporaryRef<DataSourceSurface>
   1.708 +FilterNodeSoftware::GetOutput(const IntRect &aRect)
   1.709 +{
   1.710 +  MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect));
   1.711 +
   1.712 +  if (IntRectOverflows(aRect)) {
   1.713 +    return nullptr;
   1.714 +  }
   1.715 +
   1.716 +  if (!mCachedRect.Contains(aRect)) {
   1.717 +    RequestRect(aRect);
   1.718 +    mCachedOutput = Render(mRequestedRect);
   1.719 +    if (!mCachedOutput) {
   1.720 +      mCachedRect = IntRect();
   1.721 +      mRequestedRect = IntRect();
   1.722 +      return nullptr;
   1.723 +    }
   1.724 +    mCachedRect = mRequestedRect;
   1.725 +    mRequestedRect = IntRect();
   1.726 +  } else {
   1.727 +    MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?");
   1.728 +  }
   1.729 +  return GetDataSurfaceInRect(mCachedOutput, mCachedRect, aRect, EDGE_MODE_NONE);
   1.730 +}
   1.731 +
   1.732 +void
   1.733 +FilterNodeSoftware::RequestRect(const IntRect &aRect)
   1.734 +{
   1.735 +  mRequestedRect = mRequestedRect.Union(aRect);
   1.736 +  RequestFromInputsForRect(aRect);
   1.737 +}
   1.738 +
   1.739 +void
   1.740 +FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex, const IntRect &aRect)
   1.741 +{
   1.742 +  if (IntRectOverflows(aRect)) {
   1.743 +    return;
   1.744 +  }
   1.745 +
   1.746 +  int32_t inputIndex = InputIndex(aInputEnumIndex);
   1.747 +  if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
   1.748 +    MOZ_CRASH();
   1.749 +  }
   1.750 +  if (mInputSurfaces[inputIndex]) {
   1.751 +    return;
   1.752 +  }
   1.753 +  RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
   1.754 +  MOZ_ASSERT(filter, "missing input");
   1.755 +  filter->RequestRect(filter->GetOutputRectInRect(aRect));
   1.756 +}
   1.757 +
   1.758 +SurfaceFormat
   1.759 +FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat,
   1.760 +                                  FormatHint aFormatHint)
   1.761 +{
   1.762 +  if (aCurrentFormat == SurfaceFormat::A8 && aFormatHint == CAN_HANDLE_A8) {
   1.763 +    return SurfaceFormat::A8;
   1.764 +  }
   1.765 +  return SurfaceFormat::B8G8R8A8;
   1.766 +}
   1.767 +
   1.768 +TemporaryRef<DataSourceSurface>
   1.769 +FilterNodeSoftware::GetInputDataSourceSurface(uint32_t aInputEnumIndex,
   1.770 +                                              const IntRect& aRect,
   1.771 +                                              FormatHint aFormatHint,
   1.772 +                                              ConvolveMatrixEdgeMode aEdgeMode,
   1.773 +                                              const IntRect *aTransparencyPaddedSourceRect)
   1.774 +{
   1.775 +  if (IntRectOverflows(aRect)) {
   1.776 +    return nullptr;
   1.777 +  }
   1.778 +
   1.779 +#ifdef DEBUG_DUMP_SURFACES
   1.780 +  printf("<section><h1>GetInputDataSourceSurface with aRect: %d, %d, %d, %d</h1>\n",
   1.781 +         aRect.x, aRect.y, aRect.width, aRect.height);
   1.782 +#endif
   1.783 +  int32_t inputIndex = InputIndex(aInputEnumIndex);
   1.784 +  if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
   1.785 +    MOZ_CRASH();
   1.786 +    return nullptr;
   1.787 +  }
   1.788 +
   1.789 +  if (aRect.IsEmpty()) {
   1.790 +    return nullptr;
   1.791 +  }
   1.792 +
   1.793 +  RefPtr<SourceSurface> surface;
   1.794 +  IntRect surfaceRect;
   1.795 +
   1.796 +  if (mInputSurfaces[inputIndex]) {
   1.797 +    // Input from input surface
   1.798 +    surface = mInputSurfaces[inputIndex];
   1.799 +#ifdef DEBUG_DUMP_SURFACES
   1.800 +    printf("input from input surface:\n");
   1.801 +#endif
   1.802 +    surfaceRect = IntRect(IntPoint(0, 0), surface->GetSize());
   1.803 +  } else {
   1.804 +    // Input from input filter
   1.805 +#ifdef DEBUG_DUMP_SURFACES
   1.806 +    printf("getting input from input filter %s...\n", mInputFilters[inputIndex]->GetName());
   1.807 +#endif
   1.808 +    RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
   1.809 +    MOZ_ASSERT(filter, "missing input");
   1.810 +    IntRect inputFilterOutput = filter->GetOutputRectInRect(aRect);
   1.811 +    if (!inputFilterOutput.IsEmpty()) {
   1.812 +      surface = filter->GetOutput(inputFilterOutput);
   1.813 +    }
   1.814 +#ifdef DEBUG_DUMP_SURFACES
   1.815 +    printf("input from input filter %s:\n", mInputFilters[inputIndex]->GetName());
   1.816 +#endif
   1.817 +    surfaceRect = inputFilterOutput;
   1.818 +    MOZ_ASSERT(!surface || surfaceRect.Size() == surface->GetSize());
   1.819 +  }
   1.820 +
   1.821 +  if (surface && surface->GetFormat() == SurfaceFormat::UNKNOWN) {
   1.822 +#ifdef DEBUG_DUMP_SURFACES
   1.823 +    printf("wrong input format</section>\n\n");
   1.824 +#endif
   1.825 +    return nullptr;
   1.826 +  }
   1.827 +
   1.828 +  if (!surfaceRect.IsEmpty() && !surface) {
   1.829 +#ifdef DEBUG_DUMP_SURFACES
   1.830 +    printf(" -- no input --</section>\n\n");
   1.831 +#endif
   1.832 +    return nullptr;
   1.833 +  }
   1.834 +
   1.835 +  if (aTransparencyPaddedSourceRect && !aTransparencyPaddedSourceRect->IsEmpty()) {
   1.836 +    IntRect srcRect = aTransparencyPaddedSourceRect->Intersect(aRect);
   1.837 +    surface = GetDataSurfaceInRect(surface, surfaceRect, srcRect, EDGE_MODE_NONE);
   1.838 +    surfaceRect = srcRect;
   1.839 +  }
   1.840 +
   1.841 +  RefPtr<DataSourceSurface> result =
   1.842 +    GetDataSurfaceInRect(surface, surfaceRect, aRect, aEdgeMode);
   1.843 +
   1.844 +  if (result &&
   1.845 +      (result->Stride() != GetAlignedStride<16>(result->Stride()) ||
   1.846 +       reinterpret_cast<uintptr_t>(result->GetData()) % 16 != 0)) {
   1.847 +    // Align unaligned surface.
   1.848 +    result = CloneAligned(result);
   1.849 +  }
   1.850 +
   1.851 +  if (!result) {
   1.852 +#ifdef DEBUG_DUMP_SURFACES
   1.853 +    printf(" -- no input --</section>\n\n");
   1.854 +#endif
   1.855 +    return nullptr;
   1.856 +  }
   1.857 +
   1.858 +  SurfaceFormat currentFormat = result->GetFormat();
   1.859 +  if (DesiredFormat(currentFormat, aFormatHint) == SurfaceFormat::B8G8R8A8 &&
   1.860 +      currentFormat != SurfaceFormat::B8G8R8A8) {
   1.861 +    result = FilterProcessing::ConvertToB8G8R8A8(result);
   1.862 +  }
   1.863 +
   1.864 +#ifdef DEBUG_DUMP_SURFACES
   1.865 +  printf("<img src='"); DumpAsPNG(result); printf("'></section>");
   1.866 +#endif
   1.867 +
   1.868 +  MOZ_ASSERT(!result || result->GetSize() == aRect.Size(), "wrong surface size");
   1.869 +
   1.870 +  return result;
   1.871 +}
   1.872 +
   1.873 +IntRect
   1.874 +FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex,
   1.875 +                                       const IntRect &aInRect)
   1.876 +{
   1.877 +  if (IntRectOverflows(aInRect)) {
   1.878 +    return IntRect();
   1.879 +  }
   1.880 +
   1.881 +  int32_t inputIndex = InputIndex(aInputEnumIndex);
   1.882 +  if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
   1.883 +    MOZ_CRASH();
   1.884 +    return IntRect();
   1.885 +  }
   1.886 +  if (mInputSurfaces[inputIndex]) {
   1.887 +    return aInRect.Intersect(IntRect(IntPoint(0, 0),
   1.888 +                                     mInputSurfaces[inputIndex]->GetSize()));
   1.889 +  }
   1.890 +  RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
   1.891 +  MOZ_ASSERT(filter, "missing input");
   1.892 +  return filter->GetOutputRectInRect(aInRect);
   1.893 +}
   1.894 +
   1.895 +size_t
   1.896 +FilterNodeSoftware::NumberOfSetInputs()
   1.897 +{
   1.898 +  return std::max(mInputSurfaces.size(), mInputFilters.size());
   1.899 +}
   1.900 +
   1.901 +void
   1.902 +FilterNodeSoftware::AddInvalidationListener(FilterInvalidationListener* aListener)
   1.903 +{
   1.904 +  MOZ_ASSERT(aListener, "null listener");
   1.905 +  mInvalidationListeners.push_back(aListener);
   1.906 +}
   1.907 +
   1.908 +void
   1.909 +FilterNodeSoftware::RemoveInvalidationListener(FilterInvalidationListener* aListener)
   1.910 +{
   1.911 +  MOZ_ASSERT(aListener, "null listener");
   1.912 +  std::vector<FilterInvalidationListener*>::iterator it =
   1.913 +    std::find(mInvalidationListeners.begin(), mInvalidationListeners.end(), aListener);
   1.914 +  mInvalidationListeners.erase(it);
   1.915 +}
   1.916 +
   1.917 +void
   1.918 +FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware* aFilter)
   1.919 +{
   1.920 +  Invalidate();
   1.921 +}
   1.922 +
   1.923 +void
   1.924 +FilterNodeSoftware::Invalidate()
   1.925 +{
   1.926 +  mCachedOutput = nullptr;
   1.927 +  mCachedRect = IntRect();
   1.928 +  for (std::vector<FilterInvalidationListener*>::iterator it = mInvalidationListeners.begin();
   1.929 +       it != mInvalidationListeners.end(); it++) {
   1.930 +    (*it)->FilterInvalidated(this);
   1.931 +  }
   1.932 +}
   1.933 +
   1.934 +FilterNodeSoftware::~FilterNodeSoftware()
   1.935 +{
   1.936 +  MOZ_ASSERT(!mInvalidationListeners.size(),
   1.937 +             "All invalidation listeners should have unsubscribed themselves by now!");
   1.938 +
   1.939 +  for (std::vector<RefPtr<FilterNodeSoftware> >::iterator it = mInputFilters.begin();
   1.940 +       it != mInputFilters.end(); it++) {
   1.941 +    if (*it) {
   1.942 +      (*it)->RemoveInvalidationListener(this);
   1.943 +    }
   1.944 +  }
   1.945 +}
   1.946 +
   1.947 +void
   1.948 +FilterNodeSoftware::SetInput(uint32_t aIndex, FilterNode *aFilter)
   1.949 +{
   1.950 +  if (aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
   1.951 +    MOZ_ASSERT(false, "can only take software filters as inputs");
   1.952 +    return;
   1.953 +  }
   1.954 +  SetInput(aIndex, nullptr, static_cast<FilterNodeSoftware*>(aFilter));
   1.955 +}
   1.956 +
   1.957 +void
   1.958 +FilterNodeSoftware::SetInput(uint32_t aIndex, SourceSurface *aSurface)
   1.959 +{
   1.960 +  SetInput(aIndex, aSurface, nullptr);
   1.961 +}
   1.962 +
   1.963 +void
   1.964 +FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex,
   1.965 +                             SourceSurface *aSurface,
   1.966 +                             FilterNodeSoftware *aFilter)
   1.967 +{
   1.968 +  int32_t inputIndex = InputIndex(aInputEnumIndex);
   1.969 +  if (inputIndex < 0) {
   1.970 +    MOZ_CRASH();
   1.971 +    return;
   1.972 +  }
   1.973 +  if ((uint32_t)inputIndex >= mInputSurfaces.size()) {
   1.974 +    mInputSurfaces.resize(inputIndex + 1);
   1.975 +  }
   1.976 +  if ((uint32_t)inputIndex >= mInputFilters.size()) {
   1.977 +    mInputFilters.resize(inputIndex + 1);
   1.978 +  }
   1.979 +  mInputSurfaces[inputIndex] = aSurface;
   1.980 +  if (mInputFilters[inputIndex]) {
   1.981 +    mInputFilters[inputIndex]->RemoveInvalidationListener(this);
   1.982 +  }
   1.983 +  if (aFilter) {
   1.984 +    aFilter->AddInvalidationListener(this);
   1.985 +  }
   1.986 +  mInputFilters[inputIndex] = aFilter;
   1.987 +  Invalidate();
   1.988 +}
   1.989 +
   1.990 +FilterNodeBlendSoftware::FilterNodeBlendSoftware()
   1.991 + : mBlendMode(BLEND_MODE_MULTIPLY)
   1.992 +{}
   1.993 +
   1.994 +int32_t
   1.995 +FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex)
   1.996 +{
   1.997 +  switch (aInputEnumIndex) {
   1.998 +    case IN_BLEND_IN: return 0;
   1.999 +    case IN_BLEND_IN2: return 1;
  1.1000 +    default: return -1;
  1.1001 +  }
  1.1002 +}
  1.1003 +
  1.1004 +void
  1.1005 +FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex, uint32_t aBlendMode)
  1.1006 +{
  1.1007 +  MOZ_ASSERT(aIndex == ATT_BLEND_BLENDMODE);
  1.1008 +  mBlendMode = static_cast<BlendMode>(aBlendMode);
  1.1009 +  Invalidate();
  1.1010 +}
  1.1011 +
  1.1012 +TemporaryRef<DataSourceSurface>
  1.1013 +FilterNodeBlendSoftware::Render(const IntRect& aRect)
  1.1014 +{
  1.1015 +  RefPtr<DataSourceSurface> input1 =
  1.1016 +    GetInputDataSourceSurface(IN_BLEND_IN, aRect, NEED_COLOR_CHANNELS);
  1.1017 +  RefPtr<DataSourceSurface> input2 =
  1.1018 +    GetInputDataSourceSurface(IN_BLEND_IN2, aRect, NEED_COLOR_CHANNELS);
  1.1019 +
  1.1020 +  // Null inputs need to be treated as transparent.
  1.1021 +
  1.1022 +  // First case: both are transparent.
  1.1023 +  if (!input1 && !input2) {
  1.1024 +    // Then the result is transparent, too.
  1.1025 +    return nullptr;
  1.1026 +  }
  1.1027 +
  1.1028 +  // Second case: both are non-transparent.
  1.1029 +  if (input1 && input2) {
  1.1030 +    // Apply normal filtering.
  1.1031 +    return FilterProcessing::ApplyBlending(input1, input2, mBlendMode);
  1.1032 +  }
  1.1033 +
  1.1034 +  // Third case: one of them is transparent. Return the non-transparent one.
  1.1035 +  return input1 ? input1 : input2;
  1.1036 +}
  1.1037 +
  1.1038 +void
  1.1039 +FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect &aRect)
  1.1040 +{
  1.1041 +  RequestInputRect(IN_BLEND_IN, aRect);
  1.1042 +  RequestInputRect(IN_BLEND_IN2, aRect);
  1.1043 +}
  1.1044 +
  1.1045 +IntRect
  1.1046 +FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect& aRect)
  1.1047 +{
  1.1048 +  return GetInputRectInRect(IN_BLEND_IN, aRect).Union(
  1.1049 +    GetInputRectInRect(IN_BLEND_IN2, aRect)).Intersect(aRect);
  1.1050 +}
  1.1051 +
  1.1052 +FilterNodeTransformSoftware::FilterNodeTransformSoftware()
  1.1053 + : mFilter(Filter::GOOD)
  1.1054 +{}
  1.1055 +
  1.1056 +int32_t
  1.1057 +FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex)
  1.1058 +{
  1.1059 +  switch (aInputEnumIndex) {
  1.1060 +    case IN_TRANSFORM_IN: return 0;
  1.1061 +    default: return -1;
  1.1062 +  }
  1.1063 +}
  1.1064 +
  1.1065 +void
  1.1066 +FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, uint32_t aFilter)
  1.1067 +{
  1.1068 +  MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER);
  1.1069 +  mFilter = static_cast<Filter>(aFilter);
  1.1070 +  Invalidate();
  1.1071 +}
  1.1072 +
  1.1073 +void
  1.1074 +FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, const Matrix &aMatrix)
  1.1075 +{
  1.1076 +  MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX);
  1.1077 +  mMatrix = aMatrix;
  1.1078 +  Invalidate();
  1.1079 +}
  1.1080 +
  1.1081 +IntRect
  1.1082 +FilterNodeTransformSoftware::SourceRectForOutputRect(const IntRect &aRect)
  1.1083 +{
  1.1084 +  if (aRect.IsEmpty()) {
  1.1085 +    return IntRect();
  1.1086 +  }
  1.1087 +
  1.1088 +  Matrix inverted(mMatrix);
  1.1089 +  if (!inverted.Invert()) {
  1.1090 +    return IntRect();
  1.1091 +  }
  1.1092 +
  1.1093 +  Rect neededRect = inverted.TransformBounds(Rect(aRect));
  1.1094 +  neededRect.RoundOut();
  1.1095 +  IntRect neededIntRect;
  1.1096 +  if (!neededRect.ToIntRect(&neededIntRect)) {
  1.1097 +    return IntRect();
  1.1098 +  }
  1.1099 +  return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect);
  1.1100 +}
  1.1101 +
  1.1102 +TemporaryRef<DataSourceSurface>
  1.1103 +FilterNodeTransformSoftware::Render(const IntRect& aRect)
  1.1104 +{
  1.1105 +  IntRect srcRect = SourceRectForOutputRect(aRect);
  1.1106 +
  1.1107 +  RefPtr<DataSourceSurface> input =
  1.1108 +    GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect, NEED_COLOR_CHANNELS);
  1.1109 +
  1.1110 +  if (!input) {
  1.1111 +    return nullptr;
  1.1112 +  }
  1.1113 +
  1.1114 +  Matrix transform = Matrix::Translation(srcRect.x, srcRect.y) * mMatrix *
  1.1115 +                     Matrix::Translation(-aRect.x, -aRect.y);
  1.1116 +  if (transform.IsIdentity() && srcRect.Size() == aRect.Size()) {
  1.1117 +    return input;
  1.1118 +  }
  1.1119 +
  1.1120 +  RefPtr<DrawTarget> dt =
  1.1121 +    Factory::CreateDrawTarget(BackendType::CAIRO, aRect.Size(), input->GetFormat());
  1.1122 +  if (!dt) {
  1.1123 +    return nullptr;
  1.1124 +  }
  1.1125 +
  1.1126 +  Rect r(0, 0, srcRect.width, srcRect.height);
  1.1127 +  dt->SetTransform(transform);
  1.1128 +  dt->DrawSurface(input, r, r, DrawSurfaceOptions(mFilter));
  1.1129 +
  1.1130 +  RefPtr<SourceSurface> result = dt->Snapshot();
  1.1131 +  RefPtr<DataSourceSurface> resultData = result->GetDataSurface();
  1.1132 +  return resultData;
  1.1133 +}
  1.1134 +
  1.1135 +void
  1.1136 +FilterNodeTransformSoftware::RequestFromInputsForRect(const IntRect &aRect)
  1.1137 +{
  1.1138 +  RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect));
  1.1139 +}
  1.1140 +
  1.1141 +IntRect
  1.1142 +FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect)
  1.1143 +{
  1.1144 +  IntRect srcRect = SourceRectForOutputRect(aRect);
  1.1145 +  if (srcRect.IsEmpty()) {
  1.1146 +    return IntRect();
  1.1147 +  }
  1.1148 +
  1.1149 +  Rect outRect = mMatrix.TransformBounds(Rect(srcRect));
  1.1150 +  outRect.RoundOut();
  1.1151 +  IntRect outIntRect;
  1.1152 +  if (!outRect.ToIntRect(&outIntRect)) {
  1.1153 +    return IntRect();
  1.1154 +  }
  1.1155 +  return outIntRect.Intersect(aRect);
  1.1156 +}
  1.1157 +
  1.1158 +FilterNodeMorphologySoftware::FilterNodeMorphologySoftware()
  1.1159 + : mOperator(MORPHOLOGY_OPERATOR_ERODE)
  1.1160 +{}
  1.1161 +
  1.1162 +int32_t
  1.1163 +FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex)
  1.1164 +{
  1.1165 +  switch (aInputEnumIndex) {
  1.1166 +    case IN_MORPHOLOGY_IN: return 0;
  1.1167 +    default: return -1;
  1.1168 +  }
  1.1169 +}
  1.1170 +
  1.1171 +void
  1.1172 +FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
  1.1173 +                                           const IntSize &aRadii)
  1.1174 +{
  1.1175 +  MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_RADII);
  1.1176 +  mRadii.width = std::min(std::max(aRadii.width, 0), 100000);
  1.1177 +  mRadii.height = std::min(std::max(aRadii.height, 0), 100000);
  1.1178 +  Invalidate();
  1.1179 +}
  1.1180 +
  1.1181 +void
  1.1182 +FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
  1.1183 +                                           uint32_t aOperator)
  1.1184 +{
  1.1185 +  MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_OPERATOR);
  1.1186 +  mOperator = static_cast<MorphologyOperator>(aOperator);
  1.1187 +  Invalidate();
  1.1188 +}
  1.1189 +
  1.1190 +static TemporaryRef<DataSourceSurface>
  1.1191 +ApplyMorphology(const IntRect& aSourceRect, DataSourceSurface* aInput,
  1.1192 +                const IntRect& aDestRect, int32_t rx, int32_t ry,
  1.1193 +                MorphologyOperator aOperator)
  1.1194 +{
  1.1195 +  IntRect srcRect = aSourceRect - aDestRect.TopLeft();
  1.1196 +  IntRect destRect = aDestRect - aDestRect.TopLeft();
  1.1197 +  IntRect tmpRect(destRect.x, srcRect.y, destRect.width, srcRect.height);
  1.1198 +#ifdef DEBUG
  1.1199 +  IntMargin margin = srcRect - destRect;
  1.1200 +  MOZ_ASSERT(margin.top >= ry && margin.right >= rx &&
  1.1201 +             margin.bottom >= ry && margin.left >= rx, "insufficient margin");
  1.1202 +#endif
  1.1203 +
  1.1204 +  RefPtr<DataSourceSurface> tmp;
  1.1205 +  if (rx == 0) {
  1.1206 +    tmp = aInput;
  1.1207 +  } else {
  1.1208 +    tmp = Factory::CreateDataSourceSurface(tmpRect.Size(), SurfaceFormat::B8G8R8A8);
  1.1209 +    if (!tmp) {
  1.1210 +      return nullptr;
  1.1211 +    }
  1.1212 +
  1.1213 +    int32_t sourceStride = aInput->Stride();
  1.1214 +    uint8_t* sourceData = DataAtOffset(aInput, destRect.TopLeft() - srcRect.TopLeft());
  1.1215 +
  1.1216 +    int32_t tmpStride = tmp->Stride();
  1.1217 +    uint8_t* tmpData = DataAtOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft());
  1.1218 +
  1.1219 +    FilterProcessing::ApplyMorphologyHorizontal(
  1.1220 +      sourceData, sourceStride, tmpData, tmpStride, tmpRect, rx, aOperator);
  1.1221 +  }
  1.1222 +
  1.1223 +  RefPtr<DataSourceSurface> dest;
  1.1224 +  if (ry == 0) {
  1.1225 +    dest = tmp;
  1.1226 +  } else {
  1.1227 +    dest = Factory::CreateDataSourceSurface(destRect.Size(), SurfaceFormat::B8G8R8A8);
  1.1228 +    if (!dest) {
  1.1229 +      return nullptr;
  1.1230 +    }
  1.1231 +
  1.1232 +    int32_t tmpStride = tmp->Stride();
  1.1233 +    uint8_t* tmpData = DataAtOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft());
  1.1234 +
  1.1235 +    int32_t destStride = dest->Stride();
  1.1236 +    uint8_t* destData = dest->GetData();
  1.1237 +
  1.1238 +    FilterProcessing::ApplyMorphologyVertical(
  1.1239 +      tmpData, tmpStride, destData, destStride, destRect, ry, aOperator);
  1.1240 +  }
  1.1241 +
  1.1242 +  return dest;
  1.1243 +}
  1.1244 +
  1.1245 +TemporaryRef<DataSourceSurface>
  1.1246 +FilterNodeMorphologySoftware::Render(const IntRect& aRect)
  1.1247 +{
  1.1248 +  IntRect srcRect = aRect;
  1.1249 +  srcRect.Inflate(mRadii);
  1.1250 +
  1.1251 +  RefPtr<DataSourceSurface> input =
  1.1252 +    GetInputDataSourceSurface(IN_MORPHOLOGY_IN, srcRect, NEED_COLOR_CHANNELS);
  1.1253 +  if (!input) {
  1.1254 +    return nullptr;
  1.1255 +  }
  1.1256 +
  1.1257 +  int32_t rx = mRadii.width;
  1.1258 +  int32_t ry = mRadii.height;
  1.1259 +
  1.1260 +  if (rx == 0 && ry == 0) {
  1.1261 +    return input;
  1.1262 +  }
  1.1263 +
  1.1264 +  return ApplyMorphology(srcRect, input, aRect, rx, ry, mOperator);
  1.1265 +}
  1.1266 +
  1.1267 +void
  1.1268 +FilterNodeMorphologySoftware::RequestFromInputsForRect(const IntRect &aRect)
  1.1269 +{
  1.1270 +  IntRect srcRect = aRect;
  1.1271 +  srcRect.Inflate(mRadii);
  1.1272 +  RequestInputRect(IN_MORPHOLOGY_IN, srcRect);
  1.1273 +}
  1.1274 +
  1.1275 +IntRect
  1.1276 +FilterNodeMorphologySoftware::GetOutputRectInRect(const IntRect& aRect)
  1.1277 +{
  1.1278 +  IntRect inflatedSourceRect = aRect;
  1.1279 +  inflatedSourceRect.Inflate(mRadii);
  1.1280 +  IntRect inputRect = GetInputRectInRect(IN_MORPHOLOGY_IN, inflatedSourceRect);
  1.1281 +  if (mOperator == MORPHOLOGY_OPERATOR_ERODE) {
  1.1282 +    inputRect.Deflate(mRadii);
  1.1283 +  } else {
  1.1284 +    inputRect.Inflate(mRadii);
  1.1285 +  }
  1.1286 +  return inputRect.Intersect(aRect);
  1.1287 +}
  1.1288 +
  1.1289 +int32_t
  1.1290 +FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex)
  1.1291 +{
  1.1292 +  switch (aInputEnumIndex) {
  1.1293 +    case IN_COLOR_MATRIX_IN: return 0;
  1.1294 +    default: return -1;
  1.1295 +  }
  1.1296 +}
  1.1297 +
  1.1298 +void
  1.1299 +FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
  1.1300 +                                            const Matrix5x4 &aMatrix)
  1.1301 +{
  1.1302 +  MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX);
  1.1303 +  mMatrix = aMatrix;
  1.1304 +  Invalidate();
  1.1305 +}
  1.1306 +
  1.1307 +void
  1.1308 +FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
  1.1309 +                                            uint32_t aAlphaMode)
  1.1310 +{
  1.1311 +  MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE);
  1.1312 +  mAlphaMode = (AlphaMode)aAlphaMode;
  1.1313 +  Invalidate();
  1.1314 +}
  1.1315 +
  1.1316 +static TemporaryRef<DataSourceSurface>
  1.1317 +Premultiply(DataSourceSurface* aSurface)
  1.1318 +{
  1.1319 +  if (aSurface->GetFormat() == SurfaceFormat::A8) {
  1.1320 +    return aSurface;
  1.1321 +  }
  1.1322 +
  1.1323 +  IntSize size = aSurface->GetSize();
  1.1324 +  RefPtr<DataSourceSurface> target =
  1.1325 +    Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
  1.1326 +  if (!target) {
  1.1327 +    return nullptr;
  1.1328 +  }
  1.1329 +
  1.1330 +  uint8_t* inputData = aSurface->GetData();
  1.1331 +  int32_t inputStride = aSurface->Stride();
  1.1332 +  uint8_t* targetData = target->GetData();
  1.1333 +  int32_t targetStride = target->Stride();
  1.1334 +
  1.1335 +  FilterProcessing::DoPremultiplicationCalculation(
  1.1336 +    size, targetData, targetStride, inputData, inputStride);
  1.1337 +
  1.1338 +  return target;
  1.1339 +}
  1.1340 +
  1.1341 +static TemporaryRef<DataSourceSurface>
  1.1342 +Unpremultiply(DataSourceSurface* aSurface)
  1.1343 +{
  1.1344 +  if (aSurface->GetFormat() == SurfaceFormat::A8) {
  1.1345 +    return aSurface;
  1.1346 +  }
  1.1347 +
  1.1348 +  IntSize size = aSurface->GetSize();
  1.1349 +  RefPtr<DataSourceSurface> target =
  1.1350 +    Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
  1.1351 +  if (!target) {
  1.1352 +    return nullptr;
  1.1353 +  }
  1.1354 +
  1.1355 +  uint8_t* inputData = aSurface->GetData();
  1.1356 +  int32_t inputStride = aSurface->Stride();
  1.1357 +  uint8_t* targetData = target->GetData();
  1.1358 +  int32_t targetStride = target->Stride();
  1.1359 +
  1.1360 +  FilterProcessing::DoUnpremultiplicationCalculation(
  1.1361 +    size, targetData, targetStride, inputData, inputStride);
  1.1362 +
  1.1363 +  return target;
  1.1364 +}
  1.1365 +
  1.1366 +TemporaryRef<DataSourceSurface>
  1.1367 +FilterNodeColorMatrixSoftware::Render(const IntRect& aRect)
  1.1368 +{
  1.1369 +  RefPtr<DataSourceSurface> input =
  1.1370 +    GetInputDataSourceSurface(IN_COLOR_MATRIX_IN, aRect, NEED_COLOR_CHANNELS);
  1.1371 +  if (!input) {
  1.1372 +    return nullptr;
  1.1373 +  }
  1.1374 +
  1.1375 +  if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
  1.1376 +    input = Unpremultiply(input);
  1.1377 +  }
  1.1378 +
  1.1379 +  RefPtr<DataSourceSurface> result =
  1.1380 +    FilterProcessing::ApplyColorMatrix(input, mMatrix);
  1.1381 +
  1.1382 +  if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
  1.1383 +    result = Premultiply(result);
  1.1384 +  }
  1.1385 +
  1.1386 +  return result;
  1.1387 +}
  1.1388 +
  1.1389 +void
  1.1390 +FilterNodeColorMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
  1.1391 +{
  1.1392 +  RequestInputRect(IN_COLOR_MATRIX_IN, aRect);
  1.1393 +}
  1.1394 +
  1.1395 +IntRect
  1.1396 +FilterNodeColorMatrixSoftware::GetOutputRectInRect(const IntRect& aRect)
  1.1397 +{
  1.1398 +  return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect);
  1.1399 +}
  1.1400 +
  1.1401 +void
  1.1402 +FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex, const Color &aColor)
  1.1403 +{
  1.1404 +  MOZ_ASSERT(aIndex == ATT_FLOOD_COLOR);
  1.1405 +  mColor = aColor;
  1.1406 +  Invalidate();
  1.1407 +}
  1.1408 +
  1.1409 +static uint32_t
  1.1410 +ColorToBGRA(const Color& aColor)
  1.1411 +{
  1.1412 +  union {
  1.1413 +    uint32_t color;
  1.1414 +    uint8_t components[4];
  1.1415 +  };
  1.1416 +  components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = NS_lround(aColor.r * aColor.a * 255.0f);
  1.1417 +  components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = NS_lround(aColor.g * aColor.a * 255.0f);
  1.1418 +  components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = NS_lround(aColor.b * aColor.a * 255.0f);
  1.1419 +  components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = NS_lround(aColor.a * 255.0f);
  1.1420 +  return color;
  1.1421 +}
  1.1422 +
  1.1423 +static SurfaceFormat
  1.1424 +FormatForColor(Color aColor)
  1.1425 +{
  1.1426 +  if (aColor.r == 0 && aColor.g == 0 && aColor.b == 0) {
  1.1427 +    return SurfaceFormat::A8;
  1.1428 +  }
  1.1429 +  return SurfaceFormat::B8G8R8A8;
  1.1430 +}
  1.1431 +
  1.1432 +TemporaryRef<DataSourceSurface>
  1.1433 +FilterNodeFloodSoftware::Render(const IntRect& aRect)
  1.1434 +{
  1.1435 +  SurfaceFormat format = FormatForColor(mColor);
  1.1436 +  RefPtr<DataSourceSurface> target =
  1.1437 +    Factory::CreateDataSourceSurface(aRect.Size(), format);
  1.1438 +  if (!target) {
  1.1439 +    return nullptr;
  1.1440 +  }
  1.1441 +
  1.1442 +  uint8_t* targetData = target->GetData();
  1.1443 +  uint32_t stride = target->Stride();
  1.1444 +
  1.1445 +  if (format == SurfaceFormat::B8G8R8A8) {
  1.1446 +    uint32_t color = ColorToBGRA(mColor);
  1.1447 +    for (int32_t y = 0; y < aRect.height; y++) {
  1.1448 +      for (int32_t x = 0; x < aRect.width; x++) {
  1.1449 +        *((uint32_t*)targetData + x) = color;
  1.1450 +      }
  1.1451 +      targetData += stride;
  1.1452 +    }
  1.1453 +  } else if (format == SurfaceFormat::A8) {
  1.1454 +    uint8_t alpha = NS_lround(mColor.a * 255.0f);
  1.1455 +    for (int32_t y = 0; y < aRect.height; y++) {
  1.1456 +      for (int32_t x = 0; x < aRect.width; x++) {
  1.1457 +        targetData[x] = alpha;
  1.1458 +      }
  1.1459 +      targetData += stride;
  1.1460 +    }
  1.1461 +  } else {
  1.1462 +    MOZ_CRASH();
  1.1463 +  }
  1.1464 +
  1.1465 +  return target;
  1.1466 +}
  1.1467 +
  1.1468 +// Override GetOutput to get around caching. Rendering simple floods is
  1.1469 +// comparatively fast.
  1.1470 +TemporaryRef<DataSourceSurface>
  1.1471 +FilterNodeFloodSoftware::GetOutput(const IntRect& aRect)
  1.1472 +{
  1.1473 +  return Render(aRect);
  1.1474 +}
  1.1475 +
  1.1476 +IntRect
  1.1477 +FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect& aRect)
  1.1478 +{
  1.1479 +  if (mColor.a == 0.0f) {
  1.1480 +    return IntRect();
  1.1481 +  }
  1.1482 +  return aRect;
  1.1483 +}
  1.1484 +
  1.1485 +int32_t
  1.1486 +FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex)
  1.1487 +{
  1.1488 +  switch (aInputEnumIndex) {
  1.1489 +    case IN_TILE_IN: return 0;
  1.1490 +    default: return -1;
  1.1491 +  }
  1.1492 +}
  1.1493 +
  1.1494 +void
  1.1495 +FilterNodeTileSoftware::SetAttribute(uint32_t aIndex,
  1.1496 +                                     const IntRect &aSourceRect)
  1.1497 +{
  1.1498 +  MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT);
  1.1499 +  mSourceRect = IntRect(int32_t(aSourceRect.x), int32_t(aSourceRect.y),
  1.1500 +                        int32_t(aSourceRect.width), int32_t(aSourceRect.height));
  1.1501 +  Invalidate();
  1.1502 +}
  1.1503 +
  1.1504 +namespace {
  1.1505 +struct CompareIntRects
  1.1506 +{
  1.1507 +  bool operator()(const IntRect& a, const IntRect& b) const
  1.1508 +  {
  1.1509 +    if (a.x != b.x) {
  1.1510 +      return a.x < b.x;
  1.1511 +    }
  1.1512 +    if (a.y != b.y) {
  1.1513 +      return a.y < b.y;
  1.1514 +    }
  1.1515 +    if (a.width != b.width) {
  1.1516 +      return a.width < b.width;
  1.1517 +    }
  1.1518 +    return a.height < b.height;
  1.1519 +  }
  1.1520 +};
  1.1521 +}
  1.1522 +
  1.1523 +TemporaryRef<DataSourceSurface>
  1.1524 +FilterNodeTileSoftware::Render(const IntRect& aRect)
  1.1525 +{
  1.1526 +  if (mSourceRect.IsEmpty()) {
  1.1527 +    return nullptr;
  1.1528 +  }
  1.1529 +
  1.1530 +  if (mSourceRect.Contains(aRect)) {
  1.1531 +    return GetInputDataSourceSurface(IN_TILE_IN, aRect);
  1.1532 +  }
  1.1533 +
  1.1534 +  RefPtr<DataSourceSurface> target;
  1.1535 +
  1.1536 +  typedef std::map<IntRect, RefPtr<DataSourceSurface>, CompareIntRects> InputMap;
  1.1537 +  InputMap inputs;
  1.1538 +
  1.1539 +  IntPoint startIndex = TileIndex(mSourceRect, aRect.TopLeft());
  1.1540 +  IntPoint endIndex = TileIndex(mSourceRect, aRect.BottomRight());
  1.1541 +  for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
  1.1542 +    for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
  1.1543 +      IntPoint sourceToDestOffset(ix * mSourceRect.width,
  1.1544 +                                  iy * mSourceRect.height);
  1.1545 +      IntRect destRect = aRect.Intersect(mSourceRect + sourceToDestOffset);
  1.1546 +      IntRect srcRect = destRect - sourceToDestOffset;
  1.1547 +      if (srcRect.IsEmpty()) {
  1.1548 +        continue;
  1.1549 +      }
  1.1550 +
  1.1551 +      RefPtr<DataSourceSurface> input;
  1.1552 +      InputMap::iterator it = inputs.find(srcRect);
  1.1553 +      if (it == inputs.end()) {
  1.1554 +        input = GetInputDataSourceSurface(IN_TILE_IN, srcRect);
  1.1555 +        inputs[srcRect] = input;
  1.1556 +      } else {
  1.1557 +        input = it->second;
  1.1558 +      }
  1.1559 +      if (!input) {
  1.1560 +        return nullptr;
  1.1561 +      }
  1.1562 +      if (!target) {
  1.1563 +        // We delay creating the target until now because we want to use the
  1.1564 +        // same format as our input filter, and we do not actually know the
  1.1565 +        // input format before we call GetInputDataSourceSurface.
  1.1566 +        target = Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat());
  1.1567 +        if (!target) {
  1.1568 +          return nullptr;
  1.1569 +        }
  1.1570 +      }
  1.1571 +      MOZ_ASSERT(input->GetFormat() == target->GetFormat(), "different surface formats from the same input?");
  1.1572 +
  1.1573 +      CopyRect(input, target, srcRect - srcRect.TopLeft(), destRect.TopLeft() - aRect.TopLeft());
  1.1574 +    }
  1.1575 +  }
  1.1576 +
  1.1577 +  return target;
  1.1578 +}
  1.1579 +
  1.1580 +void
  1.1581 +FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect &aRect)
  1.1582 +{
  1.1583 +  // Do not request anything.
  1.1584 +  // Source rects for the tile filter can be discontinuous with large gaps
  1.1585 +  // between them. Requesting those from our input filter might cause it to
  1.1586 +  // render the whole bounding box of all of them, which would be wasteful.
  1.1587 +}
  1.1588 +
  1.1589 +IntRect
  1.1590 +FilterNodeTileSoftware::GetOutputRectInRect(const IntRect& aRect)
  1.1591 +{
  1.1592 +  return aRect;
  1.1593 +}
  1.1594 +
  1.1595 +FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware()
  1.1596 + : mDisableR(true)
  1.1597 + , mDisableG(true)
  1.1598 + , mDisableB(true)
  1.1599 + , mDisableA(true)
  1.1600 +{}
  1.1601 +
  1.1602 +void
  1.1603 +FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex,
  1.1604 +                                                  bool aDisable)
  1.1605 +{
  1.1606 +  switch (aIndex) {
  1.1607 +    case ATT_TRANSFER_DISABLE_R:
  1.1608 +      mDisableR = aDisable;
  1.1609 +      break;
  1.1610 +    case ATT_TRANSFER_DISABLE_G:
  1.1611 +      mDisableG = aDisable;
  1.1612 +      break;
  1.1613 +    case ATT_TRANSFER_DISABLE_B:
  1.1614 +      mDisableB = aDisable;
  1.1615 +      break;
  1.1616 +    case ATT_TRANSFER_DISABLE_A:
  1.1617 +      mDisableA = aDisable;
  1.1618 +      break;
  1.1619 +    default:
  1.1620 +      MOZ_CRASH();
  1.1621 +  }
  1.1622 +  Invalidate();
  1.1623 +}
  1.1624 +
  1.1625 +void
  1.1626 +FilterNodeComponentTransferSoftware::GenerateLookupTable(ptrdiff_t aComponent,
  1.1627 +                                                         uint8_t aTables[4][256],
  1.1628 +                                                         bool aDisabled)
  1.1629 +{
  1.1630 +  if (aDisabled) {
  1.1631 +    static uint8_t sIdentityLookupTable[256];
  1.1632 +    static bool sInitializedIdentityLookupTable = false;
  1.1633 +    if (!sInitializedIdentityLookupTable) {
  1.1634 +      for (int32_t i = 0; i < 256; i++) {
  1.1635 +        sIdentityLookupTable[i] = i;
  1.1636 +      }
  1.1637 +      sInitializedIdentityLookupTable = true;
  1.1638 +    }
  1.1639 +    memcpy(aTables[aComponent], sIdentityLookupTable, 256);
  1.1640 +  } else {
  1.1641 +    FillLookupTable(aComponent, aTables[aComponent]);
  1.1642 +  }
  1.1643 +}
  1.1644 +
  1.1645 +template<uint32_t BytesPerPixel>
  1.1646 +static void TransferComponents(DataSourceSurface* aInput,
  1.1647 +                               DataSourceSurface* aTarget,
  1.1648 +                               const uint8_t aLookupTables[BytesPerPixel][256])
  1.1649 +{
  1.1650 +  MOZ_ASSERT(aInput->GetFormat() == aTarget->GetFormat(), "different formats");
  1.1651 +  IntSize size = aInput->GetSize();
  1.1652 +
  1.1653 +  uint8_t* sourceData = aInput->GetData();
  1.1654 +  uint8_t* targetData = aTarget->GetData();
  1.1655 +  uint32_t sourceStride = aInput->Stride();
  1.1656 +  uint32_t targetStride = aTarget->Stride();
  1.1657 +
  1.1658 +  for (int32_t y = 0; y < size.height; y++) {
  1.1659 +    for (int32_t x = 0; x < size.width; x++) {
  1.1660 +      uint32_t sourceIndex = y * sourceStride + x * BytesPerPixel;
  1.1661 +      uint32_t targetIndex = y * targetStride + x * BytesPerPixel;
  1.1662 +      for (uint32_t i = 0; i < BytesPerPixel; i++) {
  1.1663 +        targetData[targetIndex + i] = aLookupTables[i][sourceData[sourceIndex + i]];
  1.1664 +      }
  1.1665 +    }
  1.1666 +  }
  1.1667 +}
  1.1668 +
  1.1669 +bool
  1.1670 +IsAllZero(uint8_t aLookupTable[256])
  1.1671 +{
  1.1672 +  for (int32_t i = 0; i < 256; i++) {
  1.1673 +    if (aLookupTable[i] != 0) {
  1.1674 +      return false;
  1.1675 +    }
  1.1676 +  }
  1.1677 +  return true;
  1.1678 +}
  1.1679 +
  1.1680 +TemporaryRef<DataSourceSurface>
  1.1681 +FilterNodeComponentTransferSoftware::Render(const IntRect& aRect)
  1.1682 +{
  1.1683 +  if (mDisableR && mDisableG && mDisableB && mDisableA) {
  1.1684 +    return GetInputDataSourceSurface(IN_TRANSFER_IN, aRect);
  1.1685 +  }
  1.1686 +
  1.1687 +  uint8_t lookupTables[4][256];
  1.1688 +  GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R, lookupTables, mDisableR);
  1.1689 +  GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G, lookupTables, mDisableG);
  1.1690 +  GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B, lookupTables, mDisableB);
  1.1691 +  GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A, lookupTables, mDisableA);
  1.1692 +
  1.1693 +  bool needColorChannels =
  1.1694 +    lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R][0] != 0 ||
  1.1695 +    lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G][0] != 0 ||
  1.1696 +    lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B][0] != 0;
  1.1697 +
  1.1698 +  FormatHint pref = needColorChannels ? NEED_COLOR_CHANNELS : CAN_HANDLE_A8;
  1.1699 +
  1.1700 +  RefPtr<DataSourceSurface> input =
  1.1701 +    GetInputDataSourceSurface(IN_TRANSFER_IN, aRect, pref);
  1.1702 +  if (!input) {
  1.1703 +    return nullptr;
  1.1704 +  }
  1.1705 +
  1.1706 +  if (input->GetFormat() == SurfaceFormat::B8G8R8A8 && !needColorChannels) {
  1.1707 +    bool colorChannelsBecomeBlack =
  1.1708 +      IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) &&
  1.1709 +      IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) &&
  1.1710 +      IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B]);
  1.1711 +
  1.1712 +    if (colorChannelsBecomeBlack) {
  1.1713 +      input = FilterProcessing::ExtractAlpha(input);
  1.1714 +    }
  1.1715 +  }
  1.1716 +
  1.1717 +  SurfaceFormat format = input->GetFormat();
  1.1718 +  if (format == SurfaceFormat::A8 && mDisableA) {
  1.1719 +    return input;
  1.1720 +  }
  1.1721 +
  1.1722 +  RefPtr<DataSourceSurface> target =
  1.1723 +    Factory::CreateDataSourceSurface(aRect.Size(), format);
  1.1724 +  if (!target) {
  1.1725 +    return nullptr;
  1.1726 +  }
  1.1727 +
  1.1728 +  if (format == SurfaceFormat::A8) {
  1.1729 +    TransferComponents<1>(input, target, &lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_A]);
  1.1730 +  } else {
  1.1731 +    TransferComponents<4>(input, target, lookupTables);
  1.1732 +  }
  1.1733 +
  1.1734 +  return target;
  1.1735 +}
  1.1736 +
  1.1737 +void
  1.1738 +FilterNodeComponentTransferSoftware::RequestFromInputsForRect(const IntRect &aRect)
  1.1739 +{
  1.1740 +  RequestInputRect(IN_TRANSFER_IN, aRect);
  1.1741 +}
  1.1742 +
  1.1743 +IntRect
  1.1744 +FilterNodeComponentTransferSoftware::GetOutputRectInRect(const IntRect& aRect)
  1.1745 +{
  1.1746 +  return GetInputRectInRect(IN_TRANSFER_IN, aRect);
  1.1747 +}
  1.1748 +
  1.1749 +int32_t
  1.1750 +FilterNodeComponentTransferSoftware::InputIndex(uint32_t aInputEnumIndex)
  1.1751 +{
  1.1752 +  switch (aInputEnumIndex) {
  1.1753 +    case IN_TRANSFER_IN: return 0;
  1.1754 +    default: return -1;
  1.1755 +  }
  1.1756 +}
  1.1757 +
  1.1758 +void
  1.1759 +FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex,
  1.1760 +                                              const Float* aFloat,
  1.1761 +                                              uint32_t aSize)
  1.1762 +{
  1.1763 +  std::vector<Float> table(aFloat, aFloat + aSize);
  1.1764 +  switch (aIndex) {
  1.1765 +    case ATT_TABLE_TRANSFER_TABLE_R:
  1.1766 +      mTableR = table;
  1.1767 +      break;
  1.1768 +    case ATT_TABLE_TRANSFER_TABLE_G:
  1.1769 +      mTableG = table;
  1.1770 +      break;
  1.1771 +    case ATT_TABLE_TRANSFER_TABLE_B:
  1.1772 +      mTableB = table;
  1.1773 +      break;
  1.1774 +    case ATT_TABLE_TRANSFER_TABLE_A:
  1.1775 +      mTableA = table;
  1.1776 +      break;
  1.1777 +    default:
  1.1778 +      MOZ_CRASH();
  1.1779 +  }
  1.1780 +  Invalidate();
  1.1781 +}
  1.1782 +
  1.1783 +void
  1.1784 +FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
  1.1785 +                                                 uint8_t aTable[256])
  1.1786 +{
  1.1787 +  switch (aComponent) {
  1.1788 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
  1.1789 +      FillLookupTableImpl(mTableR, aTable);
  1.1790 +      break;
  1.1791 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
  1.1792 +      FillLookupTableImpl(mTableG, aTable);
  1.1793 +      break;
  1.1794 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
  1.1795 +      FillLookupTableImpl(mTableB, aTable);
  1.1796 +      break;
  1.1797 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
  1.1798 +      FillLookupTableImpl(mTableA, aTable);
  1.1799 +      break;
  1.1800 +    default:
  1.1801 +      MOZ_ASSERT(false, "unknown component");
  1.1802 +      break;
  1.1803 +  }
  1.1804 +}
  1.1805 +
  1.1806 +void
  1.1807 +FilterNodeTableTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues,
  1.1808 +                                                     uint8_t aTable[256])
  1.1809 +{
  1.1810 +  uint32_t tvLength = aTableValues.size();
  1.1811 +  if (tvLength < 2) {
  1.1812 +    return;
  1.1813 +  }
  1.1814 +
  1.1815 +  for (size_t i = 0; i < 256; i++) {
  1.1816 +    uint32_t k = (i * (tvLength - 1)) / 255;
  1.1817 +    Float v1 = aTableValues[k];
  1.1818 +    Float v2 = aTableValues[std::min(k + 1, tvLength - 1)];
  1.1819 +    int32_t val =
  1.1820 +      int32_t(255 * (v1 + (i/255.0f - k/float(tvLength-1))*(tvLength - 1)*(v2 - v1)));
  1.1821 +    val = std::min(255, val);
  1.1822 +    val = std::max(0, val);
  1.1823 +    aTable[i] = val;
  1.1824 +  }
  1.1825 +}
  1.1826 +
  1.1827 +void
  1.1828 +FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex,
  1.1829 +                                              const Float* aFloat,
  1.1830 +                                              uint32_t aSize)
  1.1831 +{
  1.1832 +  std::vector<Float> discrete(aFloat, aFloat + aSize);
  1.1833 +  switch (aIndex) {
  1.1834 +    case ATT_DISCRETE_TRANSFER_TABLE_R:
  1.1835 +      mTableR = discrete;
  1.1836 +      break;
  1.1837 +    case ATT_DISCRETE_TRANSFER_TABLE_G:
  1.1838 +      mTableG = discrete;
  1.1839 +      break;
  1.1840 +    case ATT_DISCRETE_TRANSFER_TABLE_B:
  1.1841 +      mTableB = discrete;
  1.1842 +      break;
  1.1843 +    case ATT_DISCRETE_TRANSFER_TABLE_A:
  1.1844 +      mTableA = discrete;
  1.1845 +      break;
  1.1846 +    default:
  1.1847 +      MOZ_CRASH();
  1.1848 +  }
  1.1849 +  Invalidate();
  1.1850 +}
  1.1851 +
  1.1852 +void
  1.1853 +FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
  1.1854 +                                                    uint8_t aTable[256])
  1.1855 +{
  1.1856 +  switch (aComponent) {
  1.1857 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
  1.1858 +      FillLookupTableImpl(mTableR, aTable);
  1.1859 +      break;
  1.1860 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
  1.1861 +      FillLookupTableImpl(mTableG, aTable);
  1.1862 +      break;
  1.1863 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
  1.1864 +      FillLookupTableImpl(mTableB, aTable);
  1.1865 +      break;
  1.1866 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
  1.1867 +      FillLookupTableImpl(mTableA, aTable);
  1.1868 +      break;
  1.1869 +    default:
  1.1870 +      MOZ_ASSERT(false, "unknown component");
  1.1871 +      break;
  1.1872 +  }
  1.1873 +}
  1.1874 +
  1.1875 +void
  1.1876 +FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues,
  1.1877 +                                                        uint8_t aTable[256])
  1.1878 +{
  1.1879 +  uint32_t tvLength = aTableValues.size();
  1.1880 +  if (tvLength < 1) {
  1.1881 +    return;
  1.1882 +  }
  1.1883 +
  1.1884 +  for (size_t i = 0; i < 256; i++) {
  1.1885 +    uint32_t k = (i * tvLength) / 255;
  1.1886 +    k = std::min(k, tvLength - 1);
  1.1887 +    Float v = aTableValues[k];
  1.1888 +    int32_t val = NS_lround(255 * v);
  1.1889 +    val = std::min(255, val);
  1.1890 +    val = std::max(0, val);
  1.1891 +    aTable[i] = val;
  1.1892 +  }
  1.1893 +}
  1.1894 +
  1.1895 +FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware()
  1.1896 + : mSlopeR(0)
  1.1897 + , mSlopeG(0)
  1.1898 + , mSlopeB(0)
  1.1899 + , mSlopeA(0)
  1.1900 + , mInterceptR(0)
  1.1901 + , mInterceptG(0)
  1.1902 + , mInterceptB(0)
  1.1903 + , mInterceptA(0)
  1.1904 +{}
  1.1905 +
  1.1906 +void
  1.1907 +FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex,
  1.1908 +                                               Float aValue)
  1.1909 +{
  1.1910 +  switch (aIndex) {
  1.1911 +    case ATT_LINEAR_TRANSFER_SLOPE_R:
  1.1912 +      mSlopeR = aValue;
  1.1913 +      break;
  1.1914 +    case ATT_LINEAR_TRANSFER_INTERCEPT_R:
  1.1915 +      mInterceptR = aValue;
  1.1916 +      break;
  1.1917 +    case ATT_LINEAR_TRANSFER_SLOPE_G:
  1.1918 +      mSlopeG = aValue;
  1.1919 +      break;
  1.1920 +    case ATT_LINEAR_TRANSFER_INTERCEPT_G:
  1.1921 +      mInterceptG = aValue;
  1.1922 +      break;
  1.1923 +    case ATT_LINEAR_TRANSFER_SLOPE_B:
  1.1924 +      mSlopeB = aValue;
  1.1925 +      break;
  1.1926 +    case ATT_LINEAR_TRANSFER_INTERCEPT_B:
  1.1927 +      mInterceptB = aValue;
  1.1928 +      break;
  1.1929 +    case ATT_LINEAR_TRANSFER_SLOPE_A:
  1.1930 +      mSlopeA = aValue;
  1.1931 +      break;
  1.1932 +    case ATT_LINEAR_TRANSFER_INTERCEPT_A:
  1.1933 +      mInterceptA = aValue;
  1.1934 +      break;
  1.1935 +    default:
  1.1936 +      MOZ_CRASH();
  1.1937 +  }
  1.1938 +  Invalidate();
  1.1939 +}
  1.1940 +
  1.1941 +void
  1.1942 +FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
  1.1943 +                                                  uint8_t aTable[256])
  1.1944 +{
  1.1945 +  switch (aComponent) {
  1.1946 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
  1.1947 +      FillLookupTableImpl(mSlopeR, mInterceptR, aTable);
  1.1948 +      break;
  1.1949 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
  1.1950 +      FillLookupTableImpl(mSlopeG, mInterceptG, aTable);
  1.1951 +      break;
  1.1952 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
  1.1953 +      FillLookupTableImpl(mSlopeB, mInterceptB, aTable);
  1.1954 +      break;
  1.1955 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
  1.1956 +      FillLookupTableImpl(mSlopeA, mInterceptA, aTable);
  1.1957 +      break;
  1.1958 +    default:
  1.1959 +      MOZ_ASSERT(false, "unknown component");
  1.1960 +      break;
  1.1961 +  }
  1.1962 +}
  1.1963 +
  1.1964 +void
  1.1965 +FilterNodeLinearTransferSoftware::FillLookupTableImpl(Float aSlope,
  1.1966 +                                                      Float aIntercept,
  1.1967 +                                                      uint8_t aTable[256])
  1.1968 +{
  1.1969 +  for (size_t i = 0; i < 256; i++) {
  1.1970 +    int32_t val = NS_lround(aSlope * i + 255 * aIntercept);
  1.1971 +    val = std::min(255, val);
  1.1972 +    val = std::max(0, val);
  1.1973 +    aTable[i] = val;
  1.1974 +  }
  1.1975 +}
  1.1976 +
  1.1977 +FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware()
  1.1978 + : mAmplitudeR(0)
  1.1979 + , mAmplitudeG(0)
  1.1980 + , mAmplitudeB(0)
  1.1981 + , mAmplitudeA(0)
  1.1982 + , mExponentR(0)
  1.1983 + , mExponentG(0)
  1.1984 + , mExponentB(0)
  1.1985 + , mExponentA(0)
  1.1986 +{}
  1.1987 +
  1.1988 +void
  1.1989 +FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex,
  1.1990 +                                              Float aValue)
  1.1991 +{
  1.1992 +  switch (aIndex) {
  1.1993 +    case ATT_GAMMA_TRANSFER_AMPLITUDE_R:
  1.1994 +      mAmplitudeR = aValue;
  1.1995 +      break;
  1.1996 +    case ATT_GAMMA_TRANSFER_EXPONENT_R:
  1.1997 +      mExponentR = aValue;
  1.1998 +      break;
  1.1999 +    case ATT_GAMMA_TRANSFER_OFFSET_R:
  1.2000 +      mOffsetR = aValue;
  1.2001 +      break;
  1.2002 +    case ATT_GAMMA_TRANSFER_AMPLITUDE_G:
  1.2003 +      mAmplitudeG = aValue;
  1.2004 +      break;
  1.2005 +    case ATT_GAMMA_TRANSFER_EXPONENT_G:
  1.2006 +      mExponentG = aValue;
  1.2007 +      break;
  1.2008 +    case ATT_GAMMA_TRANSFER_OFFSET_G:
  1.2009 +      mOffsetG = aValue;
  1.2010 +      break;
  1.2011 +    case ATT_GAMMA_TRANSFER_AMPLITUDE_B:
  1.2012 +      mAmplitudeB = aValue;
  1.2013 +      break;
  1.2014 +    case ATT_GAMMA_TRANSFER_EXPONENT_B:
  1.2015 +      mExponentB = aValue;
  1.2016 +      break;
  1.2017 +    case ATT_GAMMA_TRANSFER_OFFSET_B:
  1.2018 +      mOffsetB = aValue;
  1.2019 +      break;
  1.2020 +    case ATT_GAMMA_TRANSFER_AMPLITUDE_A:
  1.2021 +      mAmplitudeA = aValue;
  1.2022 +      break;
  1.2023 +    case ATT_GAMMA_TRANSFER_EXPONENT_A:
  1.2024 +      mExponentA = aValue;
  1.2025 +      break;
  1.2026 +    case ATT_GAMMA_TRANSFER_OFFSET_A:
  1.2027 +      mOffsetA = aValue;
  1.2028 +      break;
  1.2029 +    default:
  1.2030 +      MOZ_CRASH();
  1.2031 +  }
  1.2032 +  Invalidate();
  1.2033 +}
  1.2034 +
  1.2035 +void
  1.2036 +FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
  1.2037 +                                                 uint8_t aTable[256])
  1.2038 +{
  1.2039 +  switch (aComponent) {
  1.2040 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
  1.2041 +      FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable);
  1.2042 +      break;
  1.2043 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
  1.2044 +      FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable);
  1.2045 +      break;
  1.2046 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
  1.2047 +      FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable);
  1.2048 +      break;
  1.2049 +    case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
  1.2050 +      FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable);
  1.2051 +      break;
  1.2052 +    default:
  1.2053 +      MOZ_ASSERT(false, "unknown component");
  1.2054 +      break;
  1.2055 +  }
  1.2056 +}
  1.2057 +
  1.2058 +void
  1.2059 +FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude,
  1.2060 +                                                     Float aExponent,
  1.2061 +                                                     Float aOffset,
  1.2062 +                                                     uint8_t aTable[256])
  1.2063 +{
  1.2064 +  for (size_t i = 0; i < 256; i++) {
  1.2065 +    int32_t val = NS_lround(255 * (aAmplitude * pow(i / 255.0f, aExponent) + aOffset));
  1.2066 +    val = std::min(255, val);
  1.2067 +    val = std::max(0, val);
  1.2068 +    aTable[i] = val;
  1.2069 +  }
  1.2070 +}
  1.2071 +
  1.2072 +FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware()
  1.2073 + : mDivisor(0)
  1.2074 + , mBias(0)
  1.2075 + , mEdgeMode(EDGE_MODE_DUPLICATE)
  1.2076 + , mPreserveAlpha(false)
  1.2077 +{}
  1.2078 +
  1.2079 +int32_t
  1.2080 +FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex)
  1.2081 +{
  1.2082 +  switch (aInputEnumIndex) {
  1.2083 +    case IN_CONVOLVE_MATRIX_IN: return 0;
  1.2084 +    default: return -1;
  1.2085 +  }
  1.2086 +}
  1.2087 +
  1.2088 +void
  1.2089 +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
  1.2090 +                                               const IntSize &aKernelSize)
  1.2091 +{
  1.2092 +  MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_SIZE);
  1.2093 +  mKernelSize = aKernelSize;
  1.2094 +  Invalidate();
  1.2095 +}
  1.2096 +
  1.2097 +void
  1.2098 +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
  1.2099 +                                               const Float *aMatrix,
  1.2100 +                                               uint32_t aSize)
  1.2101 +{
  1.2102 +  MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_MATRIX);
  1.2103 +  mKernelMatrix = std::vector<Float>(aMatrix, aMatrix + aSize);
  1.2104 +  Invalidate();
  1.2105 +}
  1.2106 +
  1.2107 +void
  1.2108 +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, Float aValue)
  1.2109 +{
  1.2110 +  switch (aIndex) {
  1.2111 +    case ATT_CONVOLVE_MATRIX_DIVISOR:
  1.2112 +      mDivisor = aValue;
  1.2113 +      break;
  1.2114 +    case ATT_CONVOLVE_MATRIX_BIAS:
  1.2115 +      mBias = aValue;
  1.2116 +      break;
  1.2117 +    default:
  1.2118 +      MOZ_CRASH();
  1.2119 +  }
  1.2120 +  Invalidate();
  1.2121 +}
  1.2122 +
  1.2123 +void
  1.2124 +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
  1.2125 +{
  1.2126 +  switch (aIndex) {
  1.2127 +    case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH:
  1.2128 +      mKernelUnitLength = aKernelUnitLength;
  1.2129 +      break;
  1.2130 +    default:
  1.2131 +      MOZ_CRASH();
  1.2132 +  }
  1.2133 +  Invalidate();
  1.2134 +}
  1.2135 +
  1.2136 +void
  1.2137 +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
  1.2138 +                                               const IntPoint &aTarget)
  1.2139 +{
  1.2140 +  MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_TARGET);
  1.2141 +  mTarget = aTarget;
  1.2142 +  Invalidate();
  1.2143 +}
  1.2144 +
  1.2145 +void
  1.2146 +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
  1.2147 +                                               const IntRect &aSourceRect)
  1.2148 +{
  1.2149 +  MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_SOURCE_RECT);
  1.2150 +  mSourceRect = aSourceRect;
  1.2151 +  Invalidate();
  1.2152 +}
  1.2153 +
  1.2154 +void
  1.2155 +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
  1.2156 +                                               uint32_t aEdgeMode)
  1.2157 +{
  1.2158 +  MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_EDGE_MODE);
  1.2159 +  mEdgeMode = static_cast<ConvolveMatrixEdgeMode>(aEdgeMode);
  1.2160 +  Invalidate();
  1.2161 +}
  1.2162 +
  1.2163 +void
  1.2164 +FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
  1.2165 +                                               bool aPreserveAlpha)
  1.2166 +{
  1.2167 +  MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA);
  1.2168 +  mPreserveAlpha = aPreserveAlpha;
  1.2169 +  Invalidate();
  1.2170 +}
  1.2171 +
  1.2172 +#ifdef DEBUG
  1.2173 +static bool sColorSamplingAccessControlEnabled = false;
  1.2174 +static uint8_t* sColorSamplingAccessControlStart = nullptr;
  1.2175 +static uint8_t* sColorSamplingAccessControlEnd = nullptr;
  1.2176 +
  1.2177 +struct DebugOnlyAutoColorSamplingAccessControl
  1.2178 +{
  1.2179 +  DebugOnlyAutoColorSamplingAccessControl(DataSourceSurface* aSurface)
  1.2180 +  {
  1.2181 +    sColorSamplingAccessControlStart = aSurface->GetData();
  1.2182 +    sColorSamplingAccessControlEnd = sColorSamplingAccessControlStart +
  1.2183 +      aSurface->Stride() * aSurface->GetSize().height;
  1.2184 +    sColorSamplingAccessControlEnabled = true;
  1.2185 +  }
  1.2186 +
  1.2187 +  ~DebugOnlyAutoColorSamplingAccessControl()
  1.2188 +  {
  1.2189 +    sColorSamplingAccessControlEnabled = false;
  1.2190 +  }
  1.2191 +};
  1.2192 +
  1.2193 +static inline void
  1.2194 +DebugOnlyCheckColorSamplingAccess(const uint8_t* aSampleAddress)
  1.2195 +{
  1.2196 +  if (sColorSamplingAccessControlEnabled) {
  1.2197 +    MOZ_ASSERT(aSampleAddress >= sColorSamplingAccessControlStart, "accessing before start");
  1.2198 +    MOZ_ASSERT(aSampleAddress < sColorSamplingAccessControlEnd, "accessing after end");
  1.2199 +  }
  1.2200 +}
  1.2201 +#else
  1.2202 +typedef DebugOnly<DataSourceSurface*> DebugOnlyAutoColorSamplingAccessControl;
  1.2203 +#define DebugOnlyCheckColorSamplingAccess(address)
  1.2204 +#endif
  1.2205 +
  1.2206 +static inline uint8_t
  1.2207 +ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y, size_t bpp, ptrdiff_t c)
  1.2208 +{
  1.2209 +  DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c]);
  1.2210 +  return aData[y * aStride + bpp * x + c];
  1.2211 +}
  1.2212 +
  1.2213 +static inline int32_t
  1.2214 +ColorAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y)
  1.2215 +{
  1.2216 +  DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x);
  1.2217 +  return *(uint32_t*)(aData + y * aStride + 4 * x);
  1.2218 +}
  1.2219 +
  1.2220 +// Accepts fractional x & y and does bilinear interpolation.
  1.2221 +// Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible.
  1.2222 +static inline uint8_t
  1.2223 +ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, Float x, Float y, size_t bpp, ptrdiff_t c)
  1.2224 +{
  1.2225 +  const uint32_t f = 256;
  1.2226 +  const int32_t lx = floor(x);
  1.2227 +  const int32_t ly = floor(y);
  1.2228 +  const int32_t tux = uint32_t((x - lx) * f);
  1.2229 +  const int32_t tlx = f - tux;
  1.2230 +  const int32_t tuy = uint32_t((y - ly) * f);
  1.2231 +  const int32_t tly = f - tuy;
  1.2232 +  const uint8_t &cll = ColorComponentAtPoint(aData, aStride, lx,     ly,     bpp, c);
  1.2233 +  const uint8_t &cul = ColorComponentAtPoint(aData, aStride, lx + 1, ly,     bpp, c);
  1.2234 +  const uint8_t &clu = ColorComponentAtPoint(aData, aStride, lx,     ly + 1, bpp, c);
  1.2235 +  const uint8_t &cuu = ColorComponentAtPoint(aData, aStride, lx + 1, ly + 1, bpp, c);
  1.2236 +  return ((cll * tlx + cul * tux) * tly +
  1.2237 +          (clu * tlx + cuu * tux) * tuy + f * f / 2) / (f * f);
  1.2238 +}
  1.2239 +
  1.2240 +static inline uint32_t
  1.2241 +ColorAtPoint(const uint8_t *aData, int32_t aStride, Float x, Float y)
  1.2242 +{
  1.2243 +  return ColorComponentAtPoint(aData, aStride, x, y, 4, 0) |
  1.2244 +         (ColorComponentAtPoint(aData, aStride, x, y, 4, 1) << 8) |
  1.2245 +         (ColorComponentAtPoint(aData, aStride, x, y, 4, 2) << 16) |
  1.2246 +         (ColorComponentAtPoint(aData, aStride, x, y, 4, 3) << 24);
  1.2247 +}
  1.2248 +
  1.2249 +static int32_t
  1.2250 +ClampToNonZero(int32_t a)
  1.2251 +{
  1.2252 +  return a * (a >= 0);
  1.2253 +}
  1.2254 +
  1.2255 +template<typename CoordType>
  1.2256 +static void
  1.2257 +ConvolvePixel(const uint8_t *aSourceData,
  1.2258 +              uint8_t *aTargetData,
  1.2259 +              int32_t aWidth, int32_t aHeight,
  1.2260 +              int32_t aSourceStride, int32_t aTargetStride,
  1.2261 +              int32_t aX, int32_t aY,
  1.2262 +              const int32_t *aKernel,
  1.2263 +              int32_t aBias, int32_t shiftL, int32_t shiftR,
  1.2264 +              bool aPreserveAlpha,
  1.2265 +              int32_t aOrderX, int32_t aOrderY,
  1.2266 +              int32_t aTargetX, int32_t aTargetY,
  1.2267 +              CoordType aKernelUnitLengthX,
  1.2268 +              CoordType aKernelUnitLengthY)
  1.2269 +{
  1.2270 +  int32_t sum[4] = {0, 0, 0, 0};
  1.2271 +  int32_t offsets[4] = { B8G8R8A8_COMPONENT_BYTEOFFSET_R,
  1.2272 +                         B8G8R8A8_COMPONENT_BYTEOFFSET_G,
  1.2273 +                         B8G8R8A8_COMPONENT_BYTEOFFSET_B,
  1.2274 +                         B8G8R8A8_COMPONENT_BYTEOFFSET_A };
  1.2275 +  int32_t channels = aPreserveAlpha ? 3 : 4;
  1.2276 +  int32_t roundingAddition = shiftL == 0 ? 0 : 1 << (shiftL - 1);
  1.2277 +
  1.2278 +  for (int32_t y = 0; y < aOrderY; y++) {
  1.2279 +    CoordType sampleY = aY + (y - aTargetY) * aKernelUnitLengthY;
  1.2280 +    for (int32_t x = 0; x < aOrderX; x++) {
  1.2281 +      CoordType sampleX = aX + (x - aTargetX) * aKernelUnitLengthX;
  1.2282 +      for (int32_t i = 0; i < channels; i++) {
  1.2283 +        sum[i] += aKernel[aOrderX * y + x] *
  1.2284 +          ColorComponentAtPoint(aSourceData, aSourceStride,
  1.2285 +                                sampleX, sampleY, 4, offsets[i]);
  1.2286 +      }
  1.2287 +    }
  1.2288 +  }
  1.2289 +  for (int32_t i = 0; i < channels; i++) {
  1.2290 +    int32_t clamped = umin(ClampToNonZero(sum[i] + aBias), 255 << shiftL >> shiftR);
  1.2291 +    aTargetData[aY * aTargetStride + 4 * aX + offsets[i]] =
  1.2292 +      (clamped + roundingAddition) << shiftR >> shiftL;
  1.2293 +  }
  1.2294 +  if (aPreserveAlpha) {
  1.2295 +    aTargetData[aY * aTargetStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
  1.2296 +      aSourceData[aY * aSourceStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
  1.2297 +  }
  1.2298 +}
  1.2299 +
  1.2300 +TemporaryRef<DataSourceSurface>
  1.2301 +FilterNodeConvolveMatrixSoftware::Render(const IntRect& aRect)
  1.2302 +{
  1.2303 +  if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
  1.2304 +      mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
  1.2305 +    return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height);
  1.2306 +  }
  1.2307 +  return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
  1.2308 +}
  1.2309 +
  1.2310 +static std::vector<Float>
  1.2311 +ReversedVector(const std::vector<Float> &aVector)
  1.2312 +{
  1.2313 +  size_t length = aVector.size();
  1.2314 +  std::vector<Float> result(length, 0);
  1.2315 +  for (size_t i = 0; i < length; i++) {
  1.2316 +    result[length - 1 - i] = aVector[i];
  1.2317 +  }
  1.2318 +  return result;
  1.2319 +}
  1.2320 +
  1.2321 +static std::vector<Float>
  1.2322 +ScaledVector(const std::vector<Float> &aVector, Float aDivisor)
  1.2323 +{
  1.2324 +  size_t length = aVector.size();
  1.2325 +  std::vector<Float> result(length, 0);
  1.2326 +  for (size_t i = 0; i < length; i++) {
  1.2327 +    result[i] = aVector[i] / aDivisor;
  1.2328 +  }
  1.2329 +  return result;
  1.2330 +}
  1.2331 +
  1.2332 +static Float
  1.2333 +MaxVectorSum(const std::vector<Float> &aVector)
  1.2334 +{
  1.2335 +  Float sum = 0;
  1.2336 +  size_t length = aVector.size();
  1.2337 +  for (size_t i = 0; i < length; i++) {
  1.2338 +    if (aVector[i] > 0) {
  1.2339 +      sum += aVector[i];
  1.2340 +    }
  1.2341 +  }
  1.2342 +  return sum;
  1.2343 +}
  1.2344 +
  1.2345 +// Returns shiftL and shiftR in such a way that
  1.2346 +// a << shiftL >> shiftR is roughly a * aFloat.
  1.2347 +static void
  1.2348 +TranslateDoubleToShifts(double aDouble, int32_t &aShiftL, int32_t &aShiftR)
  1.2349 +{
  1.2350 +  aShiftL = 0;
  1.2351 +  aShiftR = 0;
  1.2352 +  if (aDouble <= 0) {
  1.2353 +    MOZ_CRASH();
  1.2354 +  }
  1.2355 +  if (aDouble < 1) {
  1.2356 +    while (1 << (aShiftR + 1) < 1 / aDouble) {
  1.2357 +      aShiftR++;
  1.2358 +    }
  1.2359 +  } else {
  1.2360 +    while (1 << (aShiftL + 1) < aDouble) {
  1.2361 +      aShiftL++;
  1.2362 +    }
  1.2363 +  }
  1.2364 +}
  1.2365 +
  1.2366 +template<typename CoordType>
  1.2367 +TemporaryRef<DataSourceSurface>
  1.2368 +FilterNodeConvolveMatrixSoftware::DoRender(const IntRect& aRect,
  1.2369 +                                           CoordType aKernelUnitLengthX,
  1.2370 +                                           CoordType aKernelUnitLengthY)
  1.2371 +{
  1.2372 +  if (mKernelSize.width <= 0 || mKernelSize.height <= 0 ||
  1.2373 +      mKernelMatrix.size() != uint32_t(mKernelSize.width * mKernelSize.height) ||
  1.2374 +      !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) ||
  1.2375 +      mDivisor == 0) {
  1.2376 +    return Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
  1.2377 +  }
  1.2378 +
  1.2379 +  IntRect srcRect = InflatedSourceRect(aRect);
  1.2380 +
  1.2381 +  // Inflate the source rect by another pixel because the bilinear filtering in
  1.2382 +  // ColorComponentAtPoint may want to access the margins.
  1.2383 +  srcRect.Inflate(1);
  1.2384 +
  1.2385 +  RefPtr<DataSourceSurface> input =
  1.2386 +    GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect, NEED_COLOR_CHANNELS, mEdgeMode, &mSourceRect);
  1.2387 +
  1.2388 +  if (!input) {
  1.2389 +    return nullptr;
  1.2390 +  }
  1.2391 +
  1.2392 +  DebugOnlyAutoColorSamplingAccessControl accessControl(input);
  1.2393 +
  1.2394 +  RefPtr<DataSourceSurface> target =
  1.2395 +    Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
  1.2396 +  if (!target) {
  1.2397 +    return nullptr;
  1.2398 +  }
  1.2399 +  ClearDataSourceSurface(target);
  1.2400 +
  1.2401 +  IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
  1.2402 +
  1.2403 +  uint8_t* sourceData = DataAtOffset(input, offset);
  1.2404 +  int32_t sourceStride = input->Stride();
  1.2405 +  uint8_t* targetData = target->GetData();
  1.2406 +  int32_t targetStride = target->Stride();
  1.2407 +
  1.2408 +  // Why exactly are we reversing the kernel?
  1.2409 +  std::vector<Float> kernel = ReversedVector(mKernelMatrix);
  1.2410 +  kernel = ScaledVector(kernel, mDivisor);
  1.2411 +  Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias,
  1.2412 +                                MaxVectorSum(ScaledVector(kernel, -1)) - mBias);
  1.2413 +  maxResultAbs = std::max(maxResultAbs, 1.0f);
  1.2414 +
  1.2415 +  double idealFactor = INT32_MAX / 2.0 / maxResultAbs / 255.0 * 0.999;
  1.2416 +  MOZ_ASSERT(255.0 * maxResultAbs * idealFactor <= INT32_MAX / 2.0, "badly chosen float-to-int scale");
  1.2417 +  int32_t shiftL, shiftR;
  1.2418 +  TranslateDoubleToShifts(idealFactor, shiftL, shiftR);
  1.2419 +  double factorFromShifts = Float(1 << shiftL) / Float(1 << shiftR);
  1.2420 +  MOZ_ASSERT(255.0 * maxResultAbs * factorFromShifts <= INT32_MAX / 2.0, "badly chosen float-to-int scale");
  1.2421 +
  1.2422 +  int32_t* intKernel = new int32_t[kernel.size()];
  1.2423 +  for (size_t i = 0; i < kernel.size(); i++) {
  1.2424 +    intKernel[i] = NS_lround(kernel[i] * factorFromShifts);
  1.2425 +  }
  1.2426 +  int32_t bias = NS_lround(mBias * 255 * factorFromShifts);
  1.2427 +
  1.2428 +  for (int32_t y = 0; y < aRect.height; y++) {
  1.2429 +    for (int32_t x = 0; x < aRect.width; x++) {
  1.2430 +      ConvolvePixel(sourceData, targetData,
  1.2431 +                    aRect.width, aRect.height, sourceStride, targetStride,
  1.2432 +                    x, y, intKernel, bias, shiftL, shiftR, mPreserveAlpha,
  1.2433 +                    mKernelSize.width, mKernelSize.height, mTarget.x, mTarget.y,
  1.2434 +                    aKernelUnitLengthX, aKernelUnitLengthY);
  1.2435 +    }
  1.2436 +  }
  1.2437 +  delete[] intKernel;
  1.2438 +
  1.2439 +  return target;
  1.2440 +}
  1.2441 +
  1.2442 +void
  1.2443 +FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
  1.2444 +{
  1.2445 +  RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect));
  1.2446 +}
  1.2447 +
  1.2448 +IntRect
  1.2449 +FilterNodeConvolveMatrixSoftware::InflatedSourceRect(const IntRect &aDestRect)
  1.2450 +{
  1.2451 +  if (aDestRect.IsEmpty()) {
  1.2452 +    return IntRect();
  1.2453 +  }
  1.2454 +
  1.2455 +  IntMargin margin;
  1.2456 +  margin.left = ceil(mTarget.x * mKernelUnitLength.width);
  1.2457 +  margin.top = ceil(mTarget.y * mKernelUnitLength.height);
  1.2458 +  margin.right = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
  1.2459 +  margin.bottom = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
  1.2460 +
  1.2461 +  IntRect srcRect = aDestRect;
  1.2462 +  srcRect.Inflate(margin);
  1.2463 +  return srcRect;
  1.2464 +}
  1.2465 +
  1.2466 +IntRect
  1.2467 +FilterNodeConvolveMatrixSoftware::InflatedDestRect(const IntRect &aSourceRect)
  1.2468 +{
  1.2469 +  if (aSourceRect.IsEmpty()) {
  1.2470 +    return IntRect();
  1.2471 +  }
  1.2472 +
  1.2473 +  IntMargin margin;
  1.2474 +  margin.left = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
  1.2475 +  margin.top = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
  1.2476 +  margin.right = ceil(mTarget.x * mKernelUnitLength.width);
  1.2477 +  margin.bottom = ceil(mTarget.y * mKernelUnitLength.height);
  1.2478 +
  1.2479 +  IntRect destRect = aSourceRect;
  1.2480 +  destRect.Inflate(margin);
  1.2481 +  return destRect;
  1.2482 +}
  1.2483 +
  1.2484 +IntRect
  1.2485 +FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(const IntRect& aRect)
  1.2486 +{
  1.2487 +  IntRect srcRequest = InflatedSourceRect(aRect);
  1.2488 +  IntRect srcOutput = GetInputRectInRect(IN_COLOR_MATRIX_IN, srcRequest);
  1.2489 +  return InflatedDestRect(srcOutput).Intersect(aRect);
  1.2490 +}
  1.2491 +
  1.2492 +FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware()
  1.2493 + : mScale(0.0f)
  1.2494 + , mChannelX(COLOR_CHANNEL_R)
  1.2495 + , mChannelY(COLOR_CHANNEL_G)
  1.2496 +{}
  1.2497 +
  1.2498 +int32_t
  1.2499 +FilterNodeDisplacementMapSoftware::InputIndex(uint32_t aInputEnumIndex)
  1.2500 +{
  1.2501 +  switch (aInputEnumIndex) {
  1.2502 +    case IN_DISPLACEMENT_MAP_IN: return 0;
  1.2503 +    case IN_DISPLACEMENT_MAP_IN2: return 1;
  1.2504 +    default: return -1;
  1.2505 +  }
  1.2506 +}
  1.2507 +
  1.2508 +void
  1.2509 +FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
  1.2510 +                                                Float aScale)
  1.2511 +{
  1.2512 +  MOZ_ASSERT(aIndex == ATT_DISPLACEMENT_MAP_SCALE);
  1.2513 +  mScale = aScale;
  1.2514 +  Invalidate();
  1.2515 +}
  1.2516 +
  1.2517 +void
  1.2518 +FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue)
  1.2519 +{
  1.2520 +  switch (aIndex) {
  1.2521 +    case ATT_DISPLACEMENT_MAP_X_CHANNEL:
  1.2522 +      mChannelX = static_cast<ColorChannel>(aValue);
  1.2523 +      break;
  1.2524 +    case ATT_DISPLACEMENT_MAP_Y_CHANNEL:
  1.2525 +      mChannelY = static_cast<ColorChannel>(aValue);
  1.2526 +      break;
  1.2527 +    default:
  1.2528 +      MOZ_CRASH();
  1.2529 +  }
  1.2530 +  Invalidate();
  1.2531 +}
  1.2532 +
  1.2533 +TemporaryRef<DataSourceSurface>
  1.2534 +FilterNodeDisplacementMapSoftware::Render(const IntRect& aRect)
  1.2535 +{
  1.2536 +  IntRect srcRect = InflatedSourceOrDestRect(aRect);
  1.2537 +  RefPtr<DataSourceSurface> input =
  1.2538 +    GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN, srcRect, NEED_COLOR_CHANNELS);
  1.2539 +  RefPtr<DataSourceSurface> map =
  1.2540 +    GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN2, aRect, NEED_COLOR_CHANNELS);
  1.2541 +  RefPtr<DataSourceSurface> target =
  1.2542 +    Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
  1.2543 +  if (!input || !map || !target) {
  1.2544 +    return nullptr;
  1.2545 +  }
  1.2546 +
  1.2547 +  IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
  1.2548 +
  1.2549 +  uint8_t* sourceData = DataAtOffset(input, offset);
  1.2550 +  int32_t sourceStride = input->Stride();
  1.2551 +  uint8_t* mapData = map->GetData();
  1.2552 +  int32_t mapStride = map->Stride();
  1.2553 +  uint8_t* targetData = target->GetData();
  1.2554 +  int32_t targetStride = target->Stride();
  1.2555 +
  1.2556 +  static const ptrdiff_t channelMap[4] = {
  1.2557 +                             B8G8R8A8_COMPONENT_BYTEOFFSET_R,
  1.2558 +                             B8G8R8A8_COMPONENT_BYTEOFFSET_G,
  1.2559 +                             B8G8R8A8_COMPONENT_BYTEOFFSET_B,
  1.2560 +                             B8G8R8A8_COMPONENT_BYTEOFFSET_A };
  1.2561 +  uint16_t xChannel = channelMap[mChannelX];
  1.2562 +  uint16_t yChannel = channelMap[mChannelY];
  1.2563 +
  1.2564 +  float scaleOver255 = mScale / 255.0f;
  1.2565 +  float scaleAdjustment = -0.5f * mScale;
  1.2566 +
  1.2567 +  for (int32_t y = 0; y < aRect.height; y++) {
  1.2568 +    for (int32_t x = 0; x < aRect.width; x++) {
  1.2569 +      uint32_t mapIndex = y * mapStride + 4 * x;
  1.2570 +      uint32_t targIndex = y * targetStride + 4 * x;
  1.2571 +      int32_t sourceX = x +
  1.2572 +        scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment;
  1.2573 +      int32_t sourceY = y +
  1.2574 +        scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment;
  1.2575 +      *(uint32_t*)(targetData + targIndex) =
  1.2576 +        ColorAtPoint(sourceData, sourceStride, sourceX, sourceY);
  1.2577 +    }
  1.2578 +  }
  1.2579 +
  1.2580 +  return target;
  1.2581 +}
  1.2582 +
  1.2583 +void
  1.2584 +FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(const IntRect &aRect)
  1.2585 +{
  1.2586 +  RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect));
  1.2587 +  RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect);
  1.2588 +}
  1.2589 +
  1.2590 +IntRect
  1.2591 +FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect)
  1.2592 +{
  1.2593 +  IntRect sourceOrDestRect = aDestOrSourceRect;
  1.2594 +  sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2));
  1.2595 +  return sourceOrDestRect;
  1.2596 +}
  1.2597 +
  1.2598 +IntRect
  1.2599 +FilterNodeDisplacementMapSoftware::GetOutputRectInRect(const IntRect& aRect)
  1.2600 +{
  1.2601 +  IntRect srcRequest = InflatedSourceOrDestRect(aRect);
  1.2602 +  IntRect srcOutput = GetInputRectInRect(IN_DISPLACEMENT_MAP_IN, srcRequest);
  1.2603 +  return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
  1.2604 +}
  1.2605 +
  1.2606 +FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware()
  1.2607 + : mNumOctaves(0)
  1.2608 + , mSeed(0)
  1.2609 + , mStitchable(false)
  1.2610 + , mType(TURBULENCE_TYPE_TURBULENCE)
  1.2611 +{}
  1.2612 +
  1.2613 +int32_t
  1.2614 +FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex)
  1.2615 +{
  1.2616 +  return -1;
  1.2617 +}
  1.2618 +
  1.2619 +void
  1.2620 +FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const Size &aBaseFrequency)
  1.2621 +{
  1.2622 +  switch (aIndex) {
  1.2623 +    case ATT_TURBULENCE_BASE_FREQUENCY:
  1.2624 +      mBaseFrequency = aBaseFrequency;
  1.2625 +      break;
  1.2626 +    default:
  1.2627 +      MOZ_CRASH();
  1.2628 +      break;
  1.2629 +  }
  1.2630 +  Invalidate();
  1.2631 +}
  1.2632 +
  1.2633 +void
  1.2634 +FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const IntRect &aRect)
  1.2635 +{
  1.2636 +  switch (aIndex) {
  1.2637 +    case ATT_TURBULENCE_RECT:
  1.2638 +      mRenderRect = aRect;
  1.2639 +      break;
  1.2640 +    default:
  1.2641 +      MOZ_CRASH();
  1.2642 +      break;
  1.2643 +  }
  1.2644 +  Invalidate();
  1.2645 +}
  1.2646 +
  1.2647 +void
  1.2648 +FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, bool aStitchable)
  1.2649 +{
  1.2650 +  MOZ_ASSERT(aIndex == ATT_TURBULENCE_STITCHABLE);
  1.2651 +  mStitchable = aStitchable;
  1.2652 +  Invalidate();
  1.2653 +}
  1.2654 +
  1.2655 +void
  1.2656 +FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue)
  1.2657 +{
  1.2658 +  switch (aIndex) {
  1.2659 +    case ATT_TURBULENCE_NUM_OCTAVES:
  1.2660 +      mNumOctaves = aValue;
  1.2661 +      break;
  1.2662 +    case ATT_TURBULENCE_SEED:
  1.2663 +      mSeed = aValue;
  1.2664 +      break;
  1.2665 +    case ATT_TURBULENCE_TYPE:
  1.2666 +      mType = static_cast<TurbulenceType>(aValue);
  1.2667 +      break;
  1.2668 +    default:
  1.2669 +      MOZ_CRASH();
  1.2670 +      break;
  1.2671 +  }
  1.2672 +  Invalidate();
  1.2673 +}
  1.2674 +
  1.2675 +TemporaryRef<DataSourceSurface>
  1.2676 +FilterNodeTurbulenceSoftware::Render(const IntRect& aRect)
  1.2677 +{
  1.2678 +  return FilterProcessing::RenderTurbulence(
  1.2679 +    aRect.Size(), aRect.TopLeft(), mBaseFrequency,
  1.2680 +    mSeed, mNumOctaves, mType, mStitchable, Rect(mRenderRect));
  1.2681 +}
  1.2682 +
  1.2683 +IntRect
  1.2684 +FilterNodeTurbulenceSoftware::GetOutputRectInRect(const IntRect& aRect)
  1.2685 +{
  1.2686 +  return aRect.Intersect(mRenderRect);
  1.2687 +}
  1.2688 +
  1.2689 +FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware()
  1.2690 + : mK1(0), mK2(0), mK3(0), mK4(0)
  1.2691 +{
  1.2692 +}
  1.2693 +
  1.2694 +int32_t
  1.2695 +FilterNodeArithmeticCombineSoftware::InputIndex(uint32_t aInputEnumIndex)
  1.2696 +{
  1.2697 +  switch (aInputEnumIndex) {
  1.2698 +    case IN_ARITHMETIC_COMBINE_IN: return 0;
  1.2699 +    case IN_ARITHMETIC_COMBINE_IN2: return 1;
  1.2700 +    default: return -1;
  1.2701 +  }
  1.2702 +}
  1.2703 +
  1.2704 +void
  1.2705 +FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex,
  1.2706 +                                                  const Float* aFloat,
  1.2707 +                                                  uint32_t aSize)
  1.2708 +{
  1.2709 +  MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS);
  1.2710 +  MOZ_ASSERT(aSize == 4);
  1.2711 +
  1.2712 +  mK1 = aFloat[0];
  1.2713 +  mK2 = aFloat[1];
  1.2714 +  mK3 = aFloat[2];
  1.2715 +  mK4 = aFloat[3];
  1.2716 +
  1.2717 +  Invalidate();
  1.2718 +}
  1.2719 +
  1.2720 +TemporaryRef<DataSourceSurface>
  1.2721 +FilterNodeArithmeticCombineSoftware::Render(const IntRect& aRect)
  1.2722 +{
  1.2723 +  RefPtr<DataSourceSurface> input1 =
  1.2724 +    GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS);
  1.2725 +  RefPtr<DataSourceSurface> input2 =
  1.2726 +    GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN2, aRect, NEED_COLOR_CHANNELS);
  1.2727 +  if (!input1 && !input2) {
  1.2728 +    return nullptr;
  1.2729 +  }
  1.2730 +
  1.2731 +  // If one input is null, treat it as transparent by adjusting the factors.
  1.2732 +  Float k1 = mK1, k2 = mK2, k3 = mK3, k4 = mK4;
  1.2733 +  if (!input1) {
  1.2734 +    k1 = 0.0f;
  1.2735 +    k2 = 0.0f;
  1.2736 +    input1 = input2;
  1.2737 +  }
  1.2738 +
  1.2739 +  if (!input2) {
  1.2740 +    k1 = 0.0f;
  1.2741 +    k3 = 0.0f;
  1.2742 +    input2 = input1;
  1.2743 +  }
  1.2744 +
  1.2745 +  return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3, k4);
  1.2746 +}
  1.2747 +
  1.2748 +void
  1.2749 +FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(const IntRect &aRect)
  1.2750 +{
  1.2751 +  RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect);
  1.2752 +  RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect);
  1.2753 +}
  1.2754 +
  1.2755 +IntRect
  1.2756 +FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(const IntRect& aRect)
  1.2757 +{
  1.2758 +  if (mK4 > 0.0f) {
  1.2759 +    return aRect;
  1.2760 +  }
  1.2761 +  IntRect rectFrom1 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect);
  1.2762 +  IntRect rectFrom2 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2, aRect).Intersect(aRect);
  1.2763 +  IntRect result;
  1.2764 +  if (mK1 > 0.0f) {
  1.2765 +    result = rectFrom1.Intersect(rectFrom2);
  1.2766 +  }
  1.2767 +  if (mK2 > 0.0f) {
  1.2768 +    result = result.Union(rectFrom1);
  1.2769 +  }
  1.2770 +  if (mK3 > 0.0f) {
  1.2771 +    result = result.Union(rectFrom2);
  1.2772 +  }
  1.2773 +  return result;
  1.2774 +}
  1.2775 +
  1.2776 +FilterNodeCompositeSoftware::FilterNodeCompositeSoftware()
  1.2777 + : mOperator(COMPOSITE_OPERATOR_OVER)
  1.2778 +{}
  1.2779 +
  1.2780 +int32_t
  1.2781 +FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex)
  1.2782 +{
  1.2783 +  return aInputEnumIndex - IN_COMPOSITE_IN_START;
  1.2784 +}
  1.2785 +
  1.2786 +void
  1.2787 +FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex, uint32_t aCompositeOperator)
  1.2788 +{
  1.2789 +  MOZ_ASSERT(aIndex == ATT_COMPOSITE_OPERATOR);
  1.2790 +  mOperator = static_cast<CompositeOperator>(aCompositeOperator);
  1.2791 +  Invalidate();
  1.2792 +}
  1.2793 +
  1.2794 +TemporaryRef<DataSourceSurface>
  1.2795 +FilterNodeCompositeSoftware::Render(const IntRect& aRect)
  1.2796 +{
  1.2797 +  RefPtr<DataSourceSurface> start =
  1.2798 +    GetInputDataSourceSurface(IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS);
  1.2799 +  RefPtr<DataSourceSurface> dest =
  1.2800 +    Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
  1.2801 +  if (!dest) {
  1.2802 +    return nullptr;
  1.2803 +  }
  1.2804 +
  1.2805 +  if (start) {
  1.2806 +    CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint());
  1.2807 +  } else {
  1.2808 +    ClearDataSourceSurface(dest);
  1.2809 +  }
  1.2810 +
  1.2811 +  for (size_t inputIndex = 1; inputIndex < NumberOfSetInputs(); inputIndex++) {
  1.2812 +    RefPtr<DataSourceSurface> input =
  1.2813 +      GetInputDataSourceSurface(IN_COMPOSITE_IN_START + inputIndex, aRect, NEED_COLOR_CHANNELS);
  1.2814 +    if (input) {
  1.2815 +      FilterProcessing::ApplyComposition(input, dest, mOperator);
  1.2816 +    } else {
  1.2817 +      // We need to treat input as transparent. Depending on the composite
  1.2818 +      // operator, different things happen to dest.
  1.2819 +      switch (mOperator) {
  1.2820 +        case COMPOSITE_OPERATOR_OVER:
  1.2821 +        case COMPOSITE_OPERATOR_ATOP:
  1.2822 +        case COMPOSITE_OPERATOR_XOR:
  1.2823 +          // dest is unchanged.
  1.2824 +          break;
  1.2825 +        case COMPOSITE_OPERATOR_OUT:
  1.2826 +          // dest is now transparent, but it can become non-transparent again
  1.2827 +          // when compositing additional inputs.
  1.2828 +          ClearDataSourceSurface(dest);
  1.2829 +          break;
  1.2830 +        case COMPOSITE_OPERATOR_IN:
  1.2831 +          // Transparency always wins. We're completely transparent now and
  1.2832 +          // no additional input can get rid of that transparency.
  1.2833 +          return nullptr;
  1.2834 +      }
  1.2835 +    }
  1.2836 +  }
  1.2837 +  return dest;
  1.2838 +}
  1.2839 +
  1.2840 +void
  1.2841 +FilterNodeCompositeSoftware::RequestFromInputsForRect(const IntRect &aRect)
  1.2842 +{
  1.2843 +  for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
  1.2844 +    RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
  1.2845 +  }
  1.2846 +}
  1.2847 +
  1.2848 +IntRect
  1.2849 +FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect& aRect)
  1.2850 +{
  1.2851 +  IntRect rect;
  1.2852 +  for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
  1.2853 +    IntRect inputRect = GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
  1.2854 +    if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) {
  1.2855 +      rect = rect.Intersect(inputRect);
  1.2856 +    } else {
  1.2857 +      rect = rect.Union(inputRect);
  1.2858 +    }
  1.2859 +  }
  1.2860 +  return rect;
  1.2861 +}
  1.2862 +
  1.2863 +int32_t
  1.2864 +FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex)
  1.2865 +{
  1.2866 +  switch (aInputEnumIndex) {
  1.2867 +    case IN_GAUSSIAN_BLUR_IN: return 0;
  1.2868 +    default: return -1;
  1.2869 +  }
  1.2870 +}
  1.2871 +
  1.2872 +TemporaryRef<DataSourceSurface>
  1.2873 +FilterNodeBlurXYSoftware::Render(const IntRect& aRect)
  1.2874 +{
  1.2875 +  Size sigmaXY = StdDeviationXY();
  1.2876 +  IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
  1.2877 +
  1.2878 +  if (d.width == 0 && d.height == 0) {
  1.2879 +    return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, aRect);
  1.2880 +  }
  1.2881 +
  1.2882 +  IntRect srcRect = InflatedSourceOrDestRect(aRect);
  1.2883 +  RefPtr<DataSourceSurface> input =
  1.2884 +    GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, srcRect);
  1.2885 +  if (!input) {
  1.2886 +    return nullptr;
  1.2887 +  }
  1.2888 +
  1.2889 +  RefPtr<DataSourceSurface> target;
  1.2890 +  Rect r(0, 0, srcRect.width, srcRect.height);
  1.2891 +
  1.2892 +  if (input->GetFormat() == SurfaceFormat::A8) {
  1.2893 +    target = Factory::CreateDataSourceSurface(srcRect.Size(), SurfaceFormat::A8);
  1.2894 +    CopyRect(input, target, IntRect(IntPoint(), input->GetSize()), IntPoint());
  1.2895 +    AlphaBoxBlur blur(r, target->Stride(), sigmaXY.width, sigmaXY.height);
  1.2896 +    blur.Blur(target->GetData());
  1.2897 +  } else {
  1.2898 +    RefPtr<DataSourceSurface> channel0, channel1, channel2, channel3;
  1.2899 +    FilterProcessing::SeparateColorChannels(input, channel0, channel1, channel2, channel3);
  1.2900 +    AlphaBoxBlur blur(r, channel0->Stride(), sigmaXY.width, sigmaXY.height);
  1.2901 +    blur.Blur(channel0->GetData());
  1.2902 +    blur.Blur(channel1->GetData());
  1.2903 +    blur.Blur(channel2->GetData());
  1.2904 +    blur.Blur(channel3->GetData());
  1.2905 +    target = FilterProcessing::CombineColorChannels(channel0, channel1, channel2, channel3);
  1.2906 +  }
  1.2907 +
  1.2908 +  return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE);
  1.2909 +}
  1.2910 +
  1.2911 +void
  1.2912 +FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect &aRect)
  1.2913 +{
  1.2914 +  RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect));
  1.2915 +}
  1.2916 +
  1.2917 +IntRect
  1.2918 +FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(const IntRect &aDestRect)
  1.2919 +{
  1.2920 +  Size sigmaXY = StdDeviationXY();
  1.2921 +  IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
  1.2922 +  IntRect srcRect = aDestRect;
  1.2923 +  srcRect.Inflate(d);
  1.2924 +  return srcRect;
  1.2925 +}
  1.2926 +
  1.2927 +IntRect
  1.2928 +FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect& aRect)
  1.2929 +{
  1.2930 +  IntRect srcRequest = InflatedSourceOrDestRect(aRect);
  1.2931 +  IntRect srcOutput = GetInputRectInRect(IN_GAUSSIAN_BLUR_IN, srcRequest);
  1.2932 +  return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
  1.2933 +}
  1.2934 +
  1.2935 +FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware()
  1.2936 + : mStdDeviation(0)
  1.2937 +{}
  1.2938 +
  1.2939 +void
  1.2940 +FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex,
  1.2941 +                                             float aStdDeviation)
  1.2942 +{
  1.2943 +  switch (aIndex) {
  1.2944 +    case ATT_GAUSSIAN_BLUR_STD_DEVIATION:
  1.2945 +      mStdDeviation = std::max(0.0f, aStdDeviation);
  1.2946 +      break;
  1.2947 +    default:
  1.2948 +      MOZ_CRASH();
  1.2949 +  }
  1.2950 +  Invalidate();
  1.2951 +}
  1.2952 +
  1.2953 +Size
  1.2954 +FilterNodeGaussianBlurSoftware::StdDeviationXY()
  1.2955 +{
  1.2956 +  return Size(mStdDeviation, mStdDeviation);
  1.2957 +}
  1.2958 +
  1.2959 +FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware()
  1.2960 + : mBlurDirection(BLUR_DIRECTION_X)
  1.2961 +{}
  1.2962 +
  1.2963 +void
  1.2964 +FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
  1.2965 +                                                Float aStdDeviation)
  1.2966 +{
  1.2967 +  switch (aIndex) {
  1.2968 +    case ATT_DIRECTIONAL_BLUR_STD_DEVIATION:
  1.2969 +      mStdDeviation = std::max(0.0f, aStdDeviation);
  1.2970 +      break;
  1.2971 +    default:
  1.2972 +      MOZ_CRASH();
  1.2973 +  }
  1.2974 +  Invalidate();
  1.2975 +}
  1.2976 +
  1.2977 +void
  1.2978 +FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
  1.2979 +                                                uint32_t aBlurDirection)
  1.2980 +{
  1.2981 +  switch (aIndex) {
  1.2982 +    case ATT_DIRECTIONAL_BLUR_DIRECTION:
  1.2983 +      mBlurDirection = (BlurDirection)aBlurDirection;
  1.2984 +      break;
  1.2985 +    default:
  1.2986 +      MOZ_CRASH();
  1.2987 +  }
  1.2988 +  Invalidate();
  1.2989 +}
  1.2990 +
  1.2991 +Size
  1.2992 +FilterNodeDirectionalBlurSoftware::StdDeviationXY()
  1.2993 +{
  1.2994 +  float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0;
  1.2995 +  float sigmaY = mBlurDirection == BLUR_DIRECTION_Y ? mStdDeviation : 0;
  1.2996 +  return Size(sigmaX, sigmaY);
  1.2997 +}
  1.2998 +
  1.2999 +int32_t
  1.3000 +FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex)
  1.3001 +{
  1.3002 +  switch (aInputEnumIndex) {
  1.3003 +    case IN_CROP_IN: return 0;
  1.3004 +    default: return -1;
  1.3005 +  }
  1.3006 +}
  1.3007 +
  1.3008 +void
  1.3009 +FilterNodeCropSoftware::SetAttribute(uint32_t aIndex,
  1.3010 +                                     const Rect &aSourceRect)
  1.3011 +{
  1.3012 +  MOZ_ASSERT(aIndex == ATT_CROP_RECT);
  1.3013 +  Rect srcRect = aSourceRect;
  1.3014 +  srcRect.Round();
  1.3015 +  if (!srcRect.ToIntRect(&mCropRect)) {
  1.3016 +    mCropRect = IntRect();
  1.3017 +  }
  1.3018 +  Invalidate();
  1.3019 +}
  1.3020 +
  1.3021 +TemporaryRef<DataSourceSurface>
  1.3022 +FilterNodeCropSoftware::Render(const IntRect& aRect)
  1.3023 +{
  1.3024 +  return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect));
  1.3025 +}
  1.3026 +
  1.3027 +void
  1.3028 +FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect &aRect)
  1.3029 +{
  1.3030 +  RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect));
  1.3031 +}
  1.3032 +
  1.3033 +IntRect
  1.3034 +FilterNodeCropSoftware::GetOutputRectInRect(const IntRect& aRect)
  1.3035 +{
  1.3036 +  return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect);
  1.3037 +}
  1.3038 +
  1.3039 +int32_t
  1.3040 +FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex)
  1.3041 +{
  1.3042 +  switch (aInputEnumIndex) {
  1.3043 +    case IN_PREMULTIPLY_IN: return 0;
  1.3044 +    default: return -1;
  1.3045 +  }
  1.3046 +}
  1.3047 +
  1.3048 +TemporaryRef<DataSourceSurface>
  1.3049 +FilterNodePremultiplySoftware::Render(const IntRect& aRect)
  1.3050 +{
  1.3051 +  RefPtr<DataSourceSurface> input =
  1.3052 +    GetInputDataSourceSurface(IN_PREMULTIPLY_IN, aRect);
  1.3053 +  return input ? Premultiply(input) : nullptr;
  1.3054 +}
  1.3055 +
  1.3056 +void
  1.3057 +FilterNodePremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect)
  1.3058 +{
  1.3059 +  RequestInputRect(IN_PREMULTIPLY_IN, aRect);
  1.3060 +}
  1.3061 +
  1.3062 +IntRect
  1.3063 +FilterNodePremultiplySoftware::GetOutputRectInRect(const IntRect& aRect)
  1.3064 +{
  1.3065 +  return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect);
  1.3066 +}
  1.3067 +
  1.3068 +int32_t
  1.3069 +FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex)
  1.3070 +{
  1.3071 +  switch (aInputEnumIndex) {
  1.3072 +    case IN_UNPREMULTIPLY_IN: return 0;
  1.3073 +    default: return -1;
  1.3074 +  }
  1.3075 +}
  1.3076 +
  1.3077 +TemporaryRef<DataSourceSurface>
  1.3078 +FilterNodeUnpremultiplySoftware::Render(const IntRect& aRect)
  1.3079 +{
  1.3080 +  RefPtr<DataSourceSurface> input =
  1.3081 +    GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN, aRect);
  1.3082 +  return input ? Unpremultiply(input) : nullptr;
  1.3083 +}
  1.3084 +
  1.3085 +void
  1.3086 +FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect)
  1.3087 +{
  1.3088 +  RequestInputRect(IN_UNPREMULTIPLY_IN, aRect);
  1.3089 +}
  1.3090 +
  1.3091 +IntRect
  1.3092 +FilterNodeUnpremultiplySoftware::GetOutputRectInRect(const IntRect& aRect)
  1.3093 +{
  1.3094 +  return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect);
  1.3095 +}
  1.3096 +
  1.3097 +bool
  1.3098 +PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
  1.3099 +{
  1.3100 +  switch (aIndex) {
  1.3101 +    case ATT_POINT_LIGHT_POSITION:
  1.3102 +      mPosition = aPoint;
  1.3103 +      break;
  1.3104 +    default:
  1.3105 +      return false;
  1.3106 +  }
  1.3107 +  return true;
  1.3108 +}
  1.3109 +
  1.3110 +SpotLightSoftware::SpotLightSoftware()
  1.3111 + : mSpecularFocus(0)
  1.3112 + , mLimitingConeAngle(0)
  1.3113 + , mLimitingConeCos(1)
  1.3114 +{
  1.3115 +}
  1.3116 +
  1.3117 +bool
  1.3118 +SpotLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
  1.3119 +{
  1.3120 +  switch (aIndex) {
  1.3121 +    case ATT_SPOT_LIGHT_POSITION:
  1.3122 +      mPosition = aPoint;
  1.3123 +      break;
  1.3124 +    case ATT_SPOT_LIGHT_POINTS_AT:
  1.3125 +      mPointsAt = aPoint;
  1.3126 +      break;
  1.3127 +    default:
  1.3128 +      return false;
  1.3129 +  }
  1.3130 +  return true;
  1.3131 +}
  1.3132 +
  1.3133 +bool
  1.3134 +SpotLightSoftware::SetAttribute(uint32_t aIndex, Float aValue)
  1.3135 +{
  1.3136 +  switch (aIndex) {
  1.3137 +    case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE:
  1.3138 +      mLimitingConeAngle = aValue;
  1.3139 +      break;
  1.3140 +    case ATT_SPOT_LIGHT_FOCUS:
  1.3141 +      mSpecularFocus = aValue;
  1.3142 +      break;
  1.3143 +    default:
  1.3144 +      return false;
  1.3145 +  }
  1.3146 +  return true;
  1.3147 +}
  1.3148 +
  1.3149 +DistantLightSoftware::DistantLightSoftware()
  1.3150 + : mAzimuth(0)
  1.3151 + , mElevation(0)
  1.3152 +{
  1.3153 +}
  1.3154 +
  1.3155 +bool
  1.3156 +DistantLightSoftware::SetAttribute(uint32_t aIndex, Float aValue)
  1.3157 +{
  1.3158 +  switch (aIndex) {
  1.3159 +    case ATT_DISTANT_LIGHT_AZIMUTH:
  1.3160 +      mAzimuth = aValue;
  1.3161 +      break;
  1.3162 +    case ATT_DISTANT_LIGHT_ELEVATION:
  1.3163 +      mElevation = aValue;
  1.3164 +      break;
  1.3165 +    default:
  1.3166 +      return false;
  1.3167 +  }
  1.3168 +  return true;
  1.3169 +}
  1.3170 +
  1.3171 +static inline Point3D Normalized(const Point3D &vec) {
  1.3172 +  Point3D copy(vec);
  1.3173 +  copy.Normalize();
  1.3174 +  return copy;
  1.3175 +}
  1.3176 +
  1.3177 +template<typename LightType, typename LightingType>
  1.3178 +FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware(const char* aTypeName)
  1.3179 + : mSurfaceScale(0)
  1.3180 +#if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
  1.3181 + , mTypeName(aTypeName)
  1.3182 +#endif
  1.3183 +{}
  1.3184 +
  1.3185 +template<typename LightType, typename LightingType>
  1.3186 +int32_t
  1.3187 +FilterNodeLightingSoftware<LightType, LightingType>::InputIndex(uint32_t aInputEnumIndex)
  1.3188 +{
  1.3189 +  switch (aInputEnumIndex) {
  1.3190 +    case IN_LIGHTING_IN: return 0;
  1.3191 +    default: return -1;
  1.3192 +  }
  1.3193 +}
  1.3194 +
  1.3195 +template<typename LightType, typename LightingType>
  1.3196 +void
  1.3197 +FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
  1.3198 +{
  1.3199 +  if (mLight.SetAttribute(aIndex, aPoint)) {
  1.3200 +    Invalidate();
  1.3201 +    return;
  1.3202 +  }
  1.3203 +  MOZ_CRASH();
  1.3204 +}
  1.3205 +
  1.3206 +template<typename LightType, typename LightingType>
  1.3207 +void
  1.3208 +FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, Float aValue)
  1.3209 +{
  1.3210 +  if (mLight.SetAttribute(aIndex, aValue) ||
  1.3211 +      mLighting.SetAttribute(aIndex, aValue)) {
  1.3212 +    Invalidate();
  1.3213 +    return;
  1.3214 +  }
  1.3215 +  switch (aIndex) {
  1.3216 +    case ATT_LIGHTING_SURFACE_SCALE:
  1.3217 +      mSurfaceScale = aValue;
  1.3218 +      break;
  1.3219 +    default:
  1.3220 +      MOZ_CRASH();
  1.3221 +  }
  1.3222 +  Invalidate();
  1.3223 +}
  1.3224 +
  1.3225 +template<typename LightType, typename LightingType>
  1.3226 +void
  1.3227 +FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
  1.3228 +{
  1.3229 +  switch (aIndex) {
  1.3230 +    case ATT_LIGHTING_KERNEL_UNIT_LENGTH:
  1.3231 +      mKernelUnitLength = aKernelUnitLength;
  1.3232 +      break;
  1.3233 +    default:
  1.3234 +      MOZ_CRASH();
  1.3235 +  }
  1.3236 +  Invalidate();
  1.3237 +}
  1.3238 +
  1.3239 +template<typename LightType, typename LightingType>
  1.3240 +void
  1.3241 +FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Color &aColor)
  1.3242 +{
  1.3243 +  MOZ_ASSERT(aIndex == ATT_LIGHTING_COLOR);
  1.3244 +  mColor = aColor;
  1.3245 +  Invalidate();
  1.3246 +}
  1.3247 +
  1.3248 +template<typename LightType, typename LightingType>
  1.3249 +IntRect
  1.3250 +FilterNodeLightingSoftware<LightType, LightingType>::GetOutputRectInRect(const IntRect& aRect)
  1.3251 +{
  1.3252 +  return GetInputRectInRect(IN_LIGHTING_IN, aRect);
  1.3253 +}
  1.3254 +
  1.3255 +Point3D
  1.3256 +PointLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
  1.3257 +{
  1.3258 +  return Normalized(mPosition - aTargetPoint);
  1.3259 +}
  1.3260 +
  1.3261 +uint32_t
  1.3262 +PointLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
  1.3263 +{
  1.3264 +  return aLightColor;
  1.3265 +}
  1.3266 +
  1.3267 +void
  1.3268 +SpotLightSoftware::Prepare()
  1.3269 +{
  1.3270 +  mVectorFromFocusPointToLight = Normalized(mPointsAt - mPosition);
  1.3271 +  mLimitingConeCos = std::max<double>(cos(mLimitingConeAngle * M_PI/180.0), 0.0);
  1.3272 +  mPowCache.CacheForExponent(mSpecularFocus);
  1.3273 +}
  1.3274 +
  1.3275 +Point3D
  1.3276 +SpotLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
  1.3277 +{
  1.3278 +  return Normalized(mPosition - aTargetPoint);
  1.3279 +}
  1.3280 +
  1.3281 +uint32_t
  1.3282 +SpotLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
  1.3283 +{
  1.3284 +  union {
  1.3285 +    uint32_t color;
  1.3286 +    uint8_t colorC[4];
  1.3287 +  };
  1.3288 +  color = aLightColor;
  1.3289 +  Float dot = -aVectorToLight.DotProduct(mVectorFromFocusPointToLight);
  1.3290 +  uint16_t doti = dot * (dot >= 0) * (1 << PowCache::sInputIntPrecisionBits);
  1.3291 +  uint32_t tmp = mPowCache.Pow(doti) * (dot >= mLimitingConeCos);
  1.3292 +  MOZ_ASSERT(tmp <= (1 << PowCache::sOutputIntPrecisionBits), "pow() result must not exceed 1.0");
  1.3293 +  colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] * tmp) >> PowCache::sOutputIntPrecisionBits);
  1.3294 +  colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] * tmp) >> PowCache::sOutputIntPrecisionBits);
  1.3295 +  colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] * tmp) >> PowCache::sOutputIntPrecisionBits);
  1.3296 +  colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
  1.3297 +  return color;
  1.3298 +}
  1.3299 +
  1.3300 +void
  1.3301 +DistantLightSoftware::Prepare()
  1.3302 +{
  1.3303 +  const double radPerDeg = M_PI / 180.0;
  1.3304 +  mVectorToLight.x = cos(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
  1.3305 +  mVectorToLight.y = sin(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
  1.3306 +  mVectorToLight.z = sin(mElevation * radPerDeg);
  1.3307 +}
  1.3308 +
  1.3309 +Point3D
  1.3310 +DistantLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
  1.3311 +{
  1.3312 +  return mVectorToLight;
  1.3313 +}
  1.3314 +
  1.3315 +uint32_t
  1.3316 +DistantLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
  1.3317 +{
  1.3318 +  return aLightColor;
  1.3319 +}
  1.3320 +
  1.3321 +template<typename CoordType>
  1.3322 +static Point3D
  1.3323 +GenerateNormal(const uint8_t *data, int32_t stride,
  1.3324 +               int32_t x, int32_t y, float surfaceScale,
  1.3325 +               CoordType dx, CoordType dy)
  1.3326 +{
  1.3327 +  const uint8_t *index = data + y * stride + x;
  1.3328 +
  1.3329 +  CoordType zero = 0;
  1.3330 +
  1.3331 +  // See this for source of constants:
  1.3332 +  //   http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
  1.3333 +  int16_t normalX =
  1.3334 +    -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) +
  1.3335 +     1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) +
  1.3336 +    -2 * ColorComponentAtPoint(index, stride, -dx, zero, 1, 0) +
  1.3337 +     2 * ColorComponentAtPoint(index, stride, dx, zero, 1, 0) +
  1.3338 +    -1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) +
  1.3339 +     1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0);
  1.3340 +
  1.3341 +  int16_t normalY =
  1.3342 +    -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) +
  1.3343 +    -2 * ColorComponentAtPoint(index, stride, zero, -dy, 1, 0) +
  1.3344 +    -1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) +
  1.3345 +     1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) +
  1.3346 +     2 * ColorComponentAtPoint(index, stride, zero, dy, 1, 0) +
  1.3347 +     1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0);
  1.3348 +
  1.3349 +  Point3D normal;
  1.3350 +  normal.x = -surfaceScale * normalX / 4.0f;
  1.3351 +  normal.y = -surfaceScale * normalY / 4.0f;
  1.3352 +  normal.z = 255;
  1.3353 +  return Normalized(normal);
  1.3354 +}
  1.3355 +
  1.3356 +template<typename LightType, typename LightingType>
  1.3357 +TemporaryRef<DataSourceSurface>
  1.3358 +FilterNodeLightingSoftware<LightType, LightingType>::Render(const IntRect& aRect)
  1.3359 +{
  1.3360 +  if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
  1.3361 +      mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
  1.3362 +    return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height);
  1.3363 +  }
  1.3364 +  return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
  1.3365 +}
  1.3366 +
  1.3367 +template<typename LightType, typename LightingType>
  1.3368 +void
  1.3369 +FilterNodeLightingSoftware<LightType, LightingType>::RequestFromInputsForRect(const IntRect &aRect)
  1.3370 +{
  1.3371 +  IntRect srcRect = aRect;
  1.3372 +  srcRect.Inflate(ceil(mKernelUnitLength.width),
  1.3373 +                  ceil(mKernelUnitLength.height));
  1.3374 +  RequestInputRect(IN_LIGHTING_IN, srcRect);
  1.3375 +}
  1.3376 +
  1.3377 +template<typename LightType, typename LightingType> template<typename CoordType>
  1.3378 +TemporaryRef<DataSourceSurface>
  1.3379 +FilterNodeLightingSoftware<LightType, LightingType>::DoRender(const IntRect& aRect,
  1.3380 +                                                              CoordType aKernelUnitLengthX,
  1.3381 +                                                              CoordType aKernelUnitLengthY)
  1.3382 +{
  1.3383 +  IntRect srcRect = aRect;
  1.3384 +  IntSize size = aRect.Size();
  1.3385 +  srcRect.Inflate(ceil(float(aKernelUnitLengthX)),
  1.3386 +                  ceil(float(aKernelUnitLengthY)));
  1.3387 +
  1.3388 +  // Inflate the source rect by another pixel because the bilinear filtering in
  1.3389 +  // ColorComponentAtPoint may want to access the margins.
  1.3390 +  srcRect.Inflate(1);
  1.3391 +
  1.3392 +  RefPtr<DataSourceSurface> input =
  1.3393 +    GetInputDataSourceSurface(IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8,
  1.3394 +                              EDGE_MODE_DUPLICATE);
  1.3395 +
  1.3396 +  if (!input) {
  1.3397 +    return nullptr;
  1.3398 +  }
  1.3399 +
  1.3400 +  if (input->GetFormat() != SurfaceFormat::A8) {
  1.3401 +    input = FilterProcessing::ExtractAlpha(input);
  1.3402 +  }
  1.3403 +
  1.3404 +  DebugOnlyAutoColorSamplingAccessControl accessControl(input);
  1.3405 +
  1.3406 +  RefPtr<DataSourceSurface> target =
  1.3407 +    Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
  1.3408 +  if (!target) {
  1.3409 +    return nullptr;
  1.3410 +  }
  1.3411 +
  1.3412 +  IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
  1.3413 +
  1.3414 +  uint8_t* sourceData = DataAtOffset(input, offset);
  1.3415 +  int32_t sourceStride = input->Stride();
  1.3416 +  uint8_t* targetData = target->GetData();
  1.3417 +  int32_t targetStride = target->Stride();
  1.3418 +
  1.3419 +  uint32_t lightColor = ColorToBGRA(mColor);
  1.3420 +  mLight.Prepare();
  1.3421 +  mLighting.Prepare();
  1.3422 +
  1.3423 +  for (int32_t y = 0; y < size.height; y++) {
  1.3424 +    for (int32_t x = 0; x < size.width; x++) {
  1.3425 +      int32_t sourceIndex = y * sourceStride + x;
  1.3426 +      int32_t targetIndex = y * targetStride + 4 * x;
  1.3427 +
  1.3428 +      Point3D normal = GenerateNormal(sourceData, sourceStride,
  1.3429 +                                      x, y, mSurfaceScale,
  1.3430 +                                      aKernelUnitLengthX, aKernelUnitLengthY);
  1.3431 +
  1.3432 +      IntPoint pointInFilterSpace(aRect.x + x, aRect.y + y);
  1.3433 +      Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f;
  1.3434 +      Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z);
  1.3435 +      Point3D rayDir = mLight.GetVectorToLight(pt);
  1.3436 +      uint32_t color = mLight.GetColor(lightColor, rayDir);
  1.3437 +
  1.3438 +      *(uint32_t*)(targetData + targetIndex) = mLighting.LightPixel(normal, rayDir, color);
  1.3439 +    }
  1.3440 +  }
  1.3441 +
  1.3442 +  return target;
  1.3443 +}
  1.3444 +
  1.3445 +DiffuseLightingSoftware::DiffuseLightingSoftware()
  1.3446 + : mDiffuseConstant(0)
  1.3447 +{
  1.3448 +}
  1.3449 +
  1.3450 +bool
  1.3451 +DiffuseLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue)
  1.3452 +{
  1.3453 +  switch (aIndex) {
  1.3454 +    case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT:
  1.3455 +      mDiffuseConstant = aValue;
  1.3456 +      break;
  1.3457 +    default:
  1.3458 +      return false;
  1.3459 +  }
  1.3460 +  return true;
  1.3461 +}
  1.3462 +
  1.3463 +uint32_t
  1.3464 +DiffuseLightingSoftware::LightPixel(const Point3D &aNormal,
  1.3465 +                                    const Point3D &aVectorToLight,
  1.3466 +                                    uint32_t aColor)
  1.3467 +{
  1.3468 +  Float dotNL = std::max(0.0f, aNormal.DotProduct(aVectorToLight));
  1.3469 +  Float diffuseNL = mDiffuseConstant * dotNL;
  1.3470 +
  1.3471 +  union {
  1.3472 +    uint32_t bgra;
  1.3473 +    uint8_t components[4];
  1.3474 +  } color = { aColor };
  1.3475 +  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
  1.3476 +    umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]), 255U);
  1.3477 +  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
  1.3478 +    umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]), 255U);
  1.3479 +  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
  1.3480 +    umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]), 255U);
  1.3481 +  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
  1.3482 +  return color.bgra;
  1.3483 +}
  1.3484 +
  1.3485 +SpecularLightingSoftware::SpecularLightingSoftware()
  1.3486 + : mSpecularConstant(0)
  1.3487 + , mSpecularExponent(0)
  1.3488 +{
  1.3489 +}
  1.3490 +
  1.3491 +bool
  1.3492 +SpecularLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue)
  1.3493 +{
  1.3494 +  switch (aIndex) {
  1.3495 +    case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT:
  1.3496 +      mSpecularConstant = std::min(std::max(aValue, 0.0f), 255.0f);
  1.3497 +      break;
  1.3498 +    case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT:
  1.3499 +      mSpecularExponent = std::min(std::max(aValue, 1.0f), 128.0f);
  1.3500 +      break;
  1.3501 +    default:
  1.3502 +      return false;
  1.3503 +  }
  1.3504 +  return true;
  1.3505 +}
  1.3506 +
  1.3507 +void
  1.3508 +SpecularLightingSoftware::Prepare()
  1.3509 +{
  1.3510 +  mPowCache.CacheForExponent(mSpecularExponent);
  1.3511 +  mSpecularConstantInt = uint32_t(mSpecularConstant * (1 << 8));
  1.3512 +}
  1.3513 +
  1.3514 +uint32_t
  1.3515 +SpecularLightingSoftware::LightPixel(const Point3D &aNormal,
  1.3516 +                                     const Point3D &aVectorToLight,
  1.3517 +                                     uint32_t aColor)
  1.3518 +{
  1.3519 +  Point3D vectorToEye(0, 0, 1);
  1.3520 +  Point3D halfwayVector = Normalized(aVectorToLight + vectorToEye);
  1.3521 +  Float dotNH = aNormal.DotProduct(halfwayVector);
  1.3522 +  uint16_t dotNHi = uint16_t(dotNH * (dotNH >= 0) * (1 << PowCache::sInputIntPrecisionBits));
  1.3523 +  uint32_t specularNHi = uint32_t(mSpecularConstantInt) * mPowCache.Pow(dotNHi) >> 8;
  1.3524 +
  1.3525 +  union {
  1.3526 +    uint32_t bgra;
  1.3527 +    uint8_t components[4];
  1.3528 +  } color = { aColor };
  1.3529 +  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
  1.3530 +    umin(
  1.3531 +      (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]) >> PowCache::sOutputIntPrecisionBits, 255U);
  1.3532 +  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
  1.3533 +    umin(
  1.3534 +      (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) >> PowCache::sOutputIntPrecisionBits, 255U);
  1.3535 +  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
  1.3536 +    umin(
  1.3537 +      (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) >> PowCache::sOutputIntPrecisionBits, 255U);
  1.3538 +
  1.3539 +  color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
  1.3540 +    umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B],
  1.3541 +      umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G],
  1.3542 +               color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]));
  1.3543 +  return color.bgra;
  1.3544 +}
  1.3545 +
  1.3546 +} // namespace gfx
  1.3547 +} // namespace mozilla

mercurial