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