forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			3778 lines
		
	
	
	
		
			112 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			3778 lines
		
	
	
	
		
			112 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include <cmath>
 | |
| #include "DataSurfaceHelpers.h"
 | |
| #include "FilterNodeSoftware.h"
 | |
| #include "2D.h"
 | |
| #include "Tools.h"
 | |
| #include "Blur.h"
 | |
| #include <map>
 | |
| #include "FilterProcessing.h"
 | |
| #include "Logging.h"
 | |
| #include "mozilla/PodOperations.h"
 | |
| #include "mozilla/DebugOnly.h"
 | |
| 
 | |
| // #define DEBUG_DUMP_SURFACES
 | |
| 
 | |
| #ifdef DEBUG_DUMP_SURFACES
 | |
| #include "gfxUtils.h" // not part of Moz2D
 | |
| #endif
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace gfx {
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| /**
 | |
|  * This class provides a way to get a pow() results in constant-time. It works
 | |
|  * by caching 129 ((1 << sCacheIndexPrecisionBits) + 1) values for bases between
 | |
|  * 0 and 1 and a fixed exponent.
 | |
|  **/
 | |
| class PowCache
 | |
| {
 | |
| public:
 | |
|   PowCache()
 | |
|     : mNumPowTablePreSquares(-1)
 | |
|   {
 | |
|   }
 | |
| 
 | |
|   void CacheForExponent(Float aExponent)
 | |
|   {
 | |
|     // Since we are in the world where we only care about
 | |
|     // input and results in [0,1], there is no point in
 | |
|     // dealing with non-positive exponents.
 | |
|     if (aExponent <= 0) {
 | |
|       mNumPowTablePreSquares = -1;
 | |
|       return;
 | |
|     }
 | |
|     int numPreSquares = 0;
 | |
|     while (numPreSquares < 5 && aExponent > (1 << (numPreSquares + 2))) {
 | |
|       numPreSquares++;
 | |
|     }
 | |
|     mNumPowTablePreSquares = numPreSquares;
 | |
|     for (size_t i = 0; i < sCacheSize; i++) {
 | |
|       // sCacheSize is chosen in such a way that a takes values
 | |
|       // from 0.0 to 1.0 inclusive.
 | |
|       Float a = i / Float(1 << sCacheIndexPrecisionBits);
 | |
|       MOZ_ASSERT(0.0f <= a && a <= 1.0f, "We only want to cache for bases between 0 and 1.");
 | |
| 
 | |
|       for (int j = 0; j < mNumPowTablePreSquares; j++) {
 | |
|         a = sqrt(a);
 | |
|       }
 | |
|       uint32_t cachedInt = pow(a, aExponent) * (1 << sOutputIntPrecisionBits);
 | |
|       MOZ_ASSERT(cachedInt < (1 << (sizeof(mPowTable[i]) * 8)), "mPowCache integer type too small");
 | |
| 
 | |
|       mPowTable[i] = cachedInt;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Only call Pow() if HasPowerTable() would return true, to avoid complicating
 | |
|   // this code and having it just return (1 << sOutputIntPrecisionBits))
 | |
|   uint16_t Pow(uint16_t aBase)
 | |
|   {
 | |
|     MOZ_ASSERT(HasPowerTable());
 | |
|     // Results should be similar to what the following code would produce:
 | |
|     // Float x = Float(aBase) / (1 << sInputIntPrecisionBits);
 | |
|     // return uint16_t(pow(x, aExponent) * (1 << sOutputIntPrecisionBits));
 | |
| 
 | |
|     MOZ_ASSERT(aBase <= (1 << sInputIntPrecisionBits), "aBase needs to be between 0 and 1!");
 | |
| 
 | |
|     uint32_t a = aBase;
 | |
|     for (int j = 0; j < mNumPowTablePreSquares; j++) {
 | |
|       a = a * a >> sInputIntPrecisionBits;
 | |
|     }
 | |
|     uint32_t i = a >> (sInputIntPrecisionBits - sCacheIndexPrecisionBits);
 | |
|     MOZ_ASSERT(i < sCacheSize, "out-of-bounds mPowTable access");
 | |
|     return mPowTable[i];
 | |
|   }
 | |
| 
 | |
|   static const int sInputIntPrecisionBits = 15;
 | |
|   static const int sOutputIntPrecisionBits = 15;
 | |
|   static const int sCacheIndexPrecisionBits = 7;
 | |
| 
 | |
|   inline bool HasPowerTable() const
 | |
|   {
 | |
|     return mNumPowTablePreSquares >= 0;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   static const size_t sCacheSize = (1 << sCacheIndexPrecisionBits) + 1;
 | |
| 
 | |
|   int mNumPowTablePreSquares;
 | |
|   uint16_t mPowTable[sCacheSize];
 | |
| };
 | |
| 
 | |
| class PointLightSoftware
 | |
| {
 | |
| public:
 | |
|   bool SetAttribute(uint32_t aIndex, Float) { return false; }
 | |
|   bool SetAttribute(uint32_t aIndex, const Point3D &);
 | |
|   void Prepare() {}
 | |
|   Point3D GetVectorToLight(const Point3D &aTargetPoint);
 | |
|   uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
 | |
| 
 | |
| private:
 | |
|   Point3D mPosition;
 | |
| };
 | |
| 
 | |
| class SpotLightSoftware
 | |
| {
 | |
| public:
 | |
|   SpotLightSoftware();
 | |
|   bool SetAttribute(uint32_t aIndex, Float);
 | |
|   bool SetAttribute(uint32_t aIndex, const Point3D &);
 | |
|   void Prepare();
 | |
|   Point3D GetVectorToLight(const Point3D &aTargetPoint);
 | |
|   uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
 | |
| 
 | |
| private:
 | |
|   Point3D mPosition;
 | |
|   Point3D mPointsAt;
 | |
|   Point3D mVectorFromFocusPointToLight;
 | |
|   Float mSpecularFocus;
 | |
|   Float mLimitingConeAngle;
 | |
|   Float mLimitingConeCos;
 | |
|   PowCache mPowCache;
 | |
| };
 | |
| 
 | |
| class DistantLightSoftware
 | |
| {
 | |
| public:
 | |
|   DistantLightSoftware();
 | |
|   bool SetAttribute(uint32_t aIndex, Float);
 | |
|   bool SetAttribute(uint32_t aIndex, const Point3D &) { return false; }
 | |
|   void Prepare();
 | |
|   Point3D GetVectorToLight(const Point3D &aTargetPoint);
 | |
|   uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
 | |
| 
 | |
| private:
 | |
|   Float mAzimuth;
 | |
|   Float mElevation;
 | |
|   Point3D mVectorToLight;
 | |
| };
 | |
| 
 | |
| class DiffuseLightingSoftware
 | |
| {
 | |
| public:
 | |
|   DiffuseLightingSoftware();
 | |
|   bool SetAttribute(uint32_t aIndex, Float);
 | |
|   void Prepare() {}
 | |
|   uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight,
 | |
|                       uint32_t aColor);
 | |
| 
 | |
| private:
 | |
|   Float mDiffuseConstant;
 | |
| };
 | |
| 
 | |
| class SpecularLightingSoftware
 | |
| {
 | |
| public:
 | |
|   SpecularLightingSoftware();
 | |
|   bool SetAttribute(uint32_t aIndex, Float);
 | |
|   void Prepare();
 | |
|   uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight,
 | |
|                       uint32_t aColor);
 | |
| 
 | |
| private:
 | |
|   Float mSpecularConstant;
 | |
|   Float mSpecularExponent;
 | |
|   uint32_t mSpecularConstantInt;
 | |
|   PowCache mPowCache;
 | |
| };
 | |
| 
 | |
| } // unnamed namespace
 | |
| 
 | |
| // from xpcom/ds/nsMathUtils.h
 | |
| static int32_t
 | |
| NS_lround(double x)
 | |
| {
 | |
|   return x >= 0.0 ? int32_t(x + 0.5) : int32_t(x - 0.5);
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| CloneAligned(DataSourceSurface* aSource)
 | |
| {
 | |
|   return CreateDataSourceSurfaceByCloning(aSource);
 | |
| }
 | |
| 
 | |
| static void
 | |
| FillRectWithPixel(DataSourceSurface *aSurface, const IntRect &aFillRect, IntPoint aPixelPos)
 | |
| {
 | |
|   MOZ_ASSERT(!aFillRect.Overflows());
 | |
|   MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
 | |
|              "aFillRect needs to be completely inside the surface");
 | |
|   MOZ_ASSERT(SurfaceContainsPoint(aSurface, aPixelPos),
 | |
|              "aPixelPos needs to be inside the surface");
 | |
| 
 | |
|   DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
 | |
|   if(MOZ2D_WARN_IF(!surfMap.IsMapped())) {
 | |
|     return;
 | |
|   }
 | |
|   uint8_t* sourcePixelData = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aPixelPos);
 | |
|   uint8_t* data = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
 | |
|   int bpp = BytesPerPixel(aSurface->GetFormat());
 | |
| 
 | |
|   // Fill the first row by hand.
 | |
|   if (bpp == 4) {
 | |
|     uint32_t sourcePixel = *(uint32_t*)sourcePixelData;
 | |
|     for (int32_t x = 0; x < aFillRect.Width(); x++) {
 | |
|       *((uint32_t*)data + x) = sourcePixel;
 | |
|     }
 | |
|   } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
 | |
|     uint8_t sourcePixel = *sourcePixelData;
 | |
|     memset(data, sourcePixel, aFillRect.Width());
 | |
|   }
 | |
| 
 | |
|   // Copy the first row into the other rows.
 | |
|   for (int32_t y = 1; y < aFillRect.Height(); y++) {
 | |
|     PodCopy(data + y * surfMap.GetStride(), data, aFillRect.Width() * bpp);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void
 | |
| FillRectWithVerticallyRepeatingHorizontalStrip(DataSourceSurface *aSurface,
 | |
|                                                const IntRect &aFillRect,
 | |
|                                                const IntRect &aSampleRect)
 | |
| {
 | |
|   MOZ_ASSERT(!aFillRect.Overflows());
 | |
|   MOZ_ASSERT(!aSampleRect.Overflows());
 | |
|   MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
 | |
|              "aFillRect needs to be completely inside the surface");
 | |
|   MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
 | |
|              "aSampleRect needs to be completely inside the surface");
 | |
| 
 | |
|   DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
 | |
|   if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   uint8_t* sampleData = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft());
 | |
|   uint8_t* data = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
 | |
|   if (BytesPerPixel(aSurface->GetFormat()) == 4) {
 | |
|     for (int32_t y = 0; y < aFillRect.Height(); y++) {
 | |
|       PodCopy((uint32_t*)data, (uint32_t*)sampleData, aFillRect.Width());
 | |
|       data += surfMap.GetStride();
 | |
|     }
 | |
|   } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
 | |
|     for (int32_t y = 0; y < aFillRect.Height(); y++) {
 | |
|       PodCopy(data, sampleData, aFillRect.Width());
 | |
|       data += surfMap.GetStride();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void
 | |
| FillRectWithHorizontallyRepeatingVerticalStrip(DataSourceSurface *aSurface,
 | |
|                                                const IntRect &aFillRect,
 | |
|                                                const IntRect &aSampleRect)
 | |
| {
 | |
|   MOZ_ASSERT(!aFillRect.Overflows());
 | |
|   MOZ_ASSERT(!aSampleRect.Overflows());
 | |
|   MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
 | |
|              "aFillRect needs to be completely inside the surface");
 | |
|   MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
 | |
|              "aSampleRect needs to be completely inside the surface");
 | |
| 
 | |
|   DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
 | |
|   if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   uint8_t* sampleData = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft());
 | |
|   uint8_t* data = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
 | |
|   if (BytesPerPixel(aSurface->GetFormat()) == 4) {
 | |
|     for (int32_t y = 0; y < aFillRect.Height(); y++) {
 | |
|       int32_t sampleColor = *((uint32_t*)sampleData);
 | |
|       for (int32_t x = 0; x < aFillRect.Width(); x++) {
 | |
|         *((uint32_t*)data + x) = sampleColor;
 | |
|       }
 | |
|       data += surfMap.GetStride();
 | |
|       sampleData += surfMap.GetStride();
 | |
|     }
 | |
|   } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
 | |
|     for (int32_t y = 0; y < aFillRect.Height(); y++) {
 | |
|       uint8_t sampleColor = *sampleData;
 | |
|       memset(data, sampleColor, aFillRect.Width());
 | |
|       data += surfMap.GetStride();
 | |
|       sampleData += surfMap.GetStride();
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void
 | |
| DuplicateEdges(DataSourceSurface* aSurface, const IntRect &aFromRect)
 | |
| {
 | |
|   MOZ_ASSERT(!aFromRect.Overflows());
 | |
|   MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFromRect),
 | |
|              "aFromRect needs to be completely inside the surface");
 | |
| 
 | |
|   IntSize size = aSurface->GetSize();
 | |
|   IntRect fill;
 | |
|   IntRect sampleRect;
 | |
|   for (int32_t ix = 0; ix < 3; ix++) {
 | |
|     switch (ix) {
 | |
|       case 0:
 | |
|         fill.SetRectX(0, aFromRect.X());
 | |
|         sampleRect.SetRectX(fill.XMost(), 1);
 | |
|         break;
 | |
|       case 1:
 | |
|         fill.SetRectX(aFromRect.X(), aFromRect.Width());
 | |
|         sampleRect.SetRectX(fill.X(), fill.Width());
 | |
|         break;
 | |
|       case 2:
 | |
|         fill.MoveToX(aFromRect.XMost());
 | |
|         fill.SetRightEdge(size.width);
 | |
|         sampleRect.SetRectX(fill.X() - 1, 1);
 | |
|         break;
 | |
|     }
 | |
|     if (fill.Width() <= 0) {
 | |
|       continue;
 | |
|     }
 | |
|     bool xIsMiddle = (ix == 1);
 | |
|     for (int32_t iy = 0; iy < 3; iy++) {
 | |
|       switch (iy) {
 | |
|         case 0:
 | |
|           fill.SetRectY(0, aFromRect.Y());
 | |
|           sampleRect.SetRectY(fill.YMost(), 1);
 | |
|           break;
 | |
|         case 1:
 | |
|           fill.SetRectY(aFromRect.Y(), aFromRect.Height());
 | |
|           sampleRect.SetRectY(fill.Y(), fill.Height());
 | |
|           break;
 | |
|         case 2:
 | |
|           fill.MoveToY(aFromRect.YMost());
 | |
|           fill.SetBottomEdge(size.height);
 | |
|           sampleRect.SetRectY(fill.Y() - 1, 1);
 | |
|           break;
 | |
|       }
 | |
|       if (fill.Height() <= 0) {
 | |
|         continue;
 | |
|       }
 | |
|       bool yIsMiddle = (iy == 1);
 | |
|       if (!xIsMiddle && !yIsMiddle) {
 | |
|         // Corner
 | |
|         FillRectWithPixel(aSurface, fill, sampleRect.TopLeft());
 | |
|       }
 | |
|       if (xIsMiddle && !yIsMiddle) {
 | |
|         // Top middle or bottom middle
 | |
|         FillRectWithVerticallyRepeatingHorizontalStrip(aSurface, fill, sampleRect);
 | |
|       }
 | |
|       if (!xIsMiddle && yIsMiddle) {
 | |
|         // Left middle or right middle
 | |
|         FillRectWithHorizontallyRepeatingVerticalStrip(aSurface, fill, sampleRect);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static IntPoint
 | |
| TileIndex(const IntRect &aFirstTileRect, const IntPoint &aPoint)
 | |
| {
 | |
|   return IntPoint(int32_t(floor(double(aPoint.x - aFirstTileRect.X()) / aFirstTileRect.Width())),
 | |
|                   int32_t(floor(double(aPoint.y - aFirstTileRect.Y()) / aFirstTileRect.Height())));
 | |
| }
 | |
| 
 | |
| static void
 | |
| TileSurface(DataSourceSurface* aSource, DataSourceSurface* aTarget, const IntPoint &aOffset)
 | |
| {
 | |
|   IntRect sourceRect(aOffset, aSource->GetSize());
 | |
|   IntRect targetRect(IntPoint(0, 0), aTarget->GetSize());
 | |
|   IntPoint startIndex = TileIndex(sourceRect, targetRect.TopLeft());
 | |
|   IntPoint endIndex = TileIndex(sourceRect, targetRect.BottomRight());
 | |
| 
 | |
|   for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
 | |
|     for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
 | |
|       IntPoint destPoint(sourceRect.X() + ix * sourceRect.Width(),
 | |
|                          sourceRect.Y() + iy * sourceRect.Height());
 | |
|       IntRect destRect(destPoint, sourceRect.Size());
 | |
|       destRect = destRect.Intersect(targetRect);
 | |
|       IntRect srcRect = destRect - destPoint;
 | |
|       CopyRect(aSource, aTarget, srcRect, destRect.TopLeft());
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static already_AddRefed<DataSourceSurface>
 | |
| GetDataSurfaceInRect(SourceSurface *aSurface,
 | |
|                      const IntRect &aSurfaceRect,
 | |
|                      const IntRect &aDestRect,
 | |
|                      ConvolveMatrixEdgeMode aEdgeMode)
 | |
| {
 | |
|   MOZ_ASSERT(aSurface ? aSurfaceRect.Size() == aSurface->GetSize() : aSurfaceRect.IsEmpty());
 | |
| 
 | |
|   if (aSurfaceRect.Overflows() || aDestRect.Overflows()) {
 | |
|     // We can't rely on the intersection calculations below to make sense when
 | |
|     // XMost() or YMost() overflow. Bail out.
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   IntRect sourceRect = aSurfaceRect;
 | |
| 
 | |
|   if (sourceRect.IsEqualEdges(aDestRect)) {
 | |
|     return aSurface ? aSurface->GetDataSurface() : nullptr;
 | |
|   }
 | |
| 
 | |
|   IntRect intersect = sourceRect.Intersect(aDestRect);
 | |
|   IntRect intersectInSourceSpace = intersect - sourceRect.TopLeft();
 | |
|   IntRect intersectInDestSpace = intersect - aDestRect.TopLeft();
 | |
|   SurfaceFormat format = aSurface ? aSurface->GetFormat() : SurfaceFormat(SurfaceFormat::B8G8R8A8);
 | |
| 
 | |
|   RefPtr<DataSourceSurface> target =
 | |
|     Factory::CreateDataSourceSurface(aDestRect.Size(), format, true);
 | |
|   if (MOZ2D_WARN_IF(!target)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (!aSurface) {
 | |
|     return target.forget();
 | |
|   }
 | |
| 
 | |
|   RefPtr<DataSourceSurface> dataSource = aSurface->GetDataSurface();
 | |
|   MOZ_ASSERT(dataSource);
 | |
| 
 | |
|   if (aEdgeMode == EDGE_MODE_WRAP) {
 | |
|     TileSurface(dataSource, target, intersectInDestSpace.TopLeft());
 | |
|     return target.forget();
 | |
|   }
 | |
| 
 | |
|   CopyRect(dataSource, target, intersectInSourceSpace,
 | |
|            intersectInDestSpace.TopLeft());
 | |
| 
 | |
|   if (aEdgeMode == EDGE_MODE_DUPLICATE) {
 | |
|     DuplicateEdges(target, intersectInDestSpace);
 | |
|   }
 | |
| 
 | |
|   return target.forget();
 | |
| }
 | |
| 
 | |
| /* static */ already_AddRefed<FilterNode>
 | |
| FilterNodeSoftware::Create(FilterType aType)
 | |
| {
 | |
|   RefPtr<FilterNodeSoftware> filter;
 | |
|   switch (aType) {
 | |
|     case FilterType::BLEND:
 | |
|       filter = new FilterNodeBlendSoftware();
 | |
|       break;
 | |
|     case FilterType::TRANSFORM:
 | |
|       filter = new FilterNodeTransformSoftware();
 | |
|       break;
 | |
|     case FilterType::MORPHOLOGY:
 | |
|       filter = new FilterNodeMorphologySoftware();
 | |
|       break;
 | |
|     case FilterType::COLOR_MATRIX:
 | |
|       filter = new FilterNodeColorMatrixSoftware();
 | |
|       break;
 | |
|     case FilterType::FLOOD:
 | |
|       filter = new FilterNodeFloodSoftware();
 | |
|       break;
 | |
|     case FilterType::TILE:
 | |
|       filter = new FilterNodeTileSoftware();
 | |
|       break;
 | |
|     case FilterType::TABLE_TRANSFER:
 | |
|       filter = new FilterNodeTableTransferSoftware();
 | |
|       break;
 | |
|     case FilterType::DISCRETE_TRANSFER:
 | |
|       filter = new FilterNodeDiscreteTransferSoftware();
 | |
|       break;
 | |
|     case FilterType::LINEAR_TRANSFER:
 | |
|       filter = new FilterNodeLinearTransferSoftware();
 | |
|       break;
 | |
|     case FilterType::GAMMA_TRANSFER:
 | |
|       filter = new FilterNodeGammaTransferSoftware();
 | |
|       break;
 | |
|     case FilterType::CONVOLVE_MATRIX:
 | |
|       filter = new FilterNodeConvolveMatrixSoftware();
 | |
|       break;
 | |
|     case FilterType::DISPLACEMENT_MAP:
 | |
|       filter = new FilterNodeDisplacementMapSoftware();
 | |
|       break;
 | |
|     case FilterType::TURBULENCE:
 | |
|       filter = new FilterNodeTurbulenceSoftware();
 | |
|       break;
 | |
|     case FilterType::ARITHMETIC_COMBINE:
 | |
|       filter = new FilterNodeArithmeticCombineSoftware();
 | |
|       break;
 | |
|     case FilterType::COMPOSITE:
 | |
|       filter = new FilterNodeCompositeSoftware();
 | |
|       break;
 | |
|     case FilterType::GAUSSIAN_BLUR:
 | |
|       filter = new FilterNodeGaussianBlurSoftware();
 | |
|       break;
 | |
|     case FilterType::DIRECTIONAL_BLUR:
 | |
|       filter = new FilterNodeDirectionalBlurSoftware();
 | |
|       break;
 | |
|     case FilterType::CROP:
 | |
|       filter = new FilterNodeCropSoftware();
 | |
|       break;
 | |
|     case FilterType::PREMULTIPLY:
 | |
|       filter = new FilterNodePremultiplySoftware();
 | |
|       break;
 | |
|     case FilterType::UNPREMULTIPLY:
 | |
|       filter = new FilterNodeUnpremultiplySoftware();
 | |
|       break;
 | |
|     case FilterType::OPACITY:
 | |
|       filter = new FilterNodeOpacitySoftware();
 | |
|       break;
 | |
|     case FilterType::POINT_DIFFUSE:
 | |
|       filter = new FilterNodeLightingSoftware<PointLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<PointLight, DiffuseLighting>");
 | |
|       break;
 | |
|     case FilterType::POINT_SPECULAR:
 | |
|       filter = new FilterNodeLightingSoftware<PointLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<PointLight, SpecularLighting>");
 | |
|       break;
 | |
|     case FilterType::SPOT_DIFFUSE:
 | |
|       filter = new FilterNodeLightingSoftware<SpotLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<SpotLight, DiffuseLighting>");
 | |
|       break;
 | |
|     case FilterType::SPOT_SPECULAR:
 | |
|       filter = new FilterNodeLightingSoftware<SpotLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<SpotLight, SpecularLighting>");
 | |
|       break;
 | |
|     case FilterType::DISTANT_DIFFUSE:
 | |
|       filter = new FilterNodeLightingSoftware<DistantLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<DistantLight, DiffuseLighting>");
 | |
|       break;
 | |
|     case FilterType::DISTANT_SPECULAR:
 | |
|       filter = new FilterNodeLightingSoftware<DistantLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<DistantLight, SpecularLighting>");
 | |
|       break;
 | |
|   }
 | |
|   return filter.forget();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeSoftware::Draw(DrawTarget* aDrawTarget,
 | |
|                          const Rect &aSourceRect,
 | |
|                          const Point &aDestPoint,
 | |
|                          const DrawOptions &aOptions)
 | |
| {
 | |
| #ifdef DEBUG_DUMP_SURFACES
 | |
|   printf("<style>section{margin:10px;}</style><pre>\nRendering filter %s...\n", GetName());
 | |
| #endif
 | |
| 
 | |
|   Rect renderRect = aSourceRect;
 | |
|   renderRect.RoundOut();
 | |
|   IntRect renderIntRect;
 | |
|   if (!renderRect.ToIntRect(&renderIntRect)) {
 | |
| #ifdef DEBUG_DUMP_SURFACES
 | |
|     printf("render rect overflowed, not painting anything\n");
 | |
|     printf("</pre>\n");
 | |
| #endif
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   IntRect outputRect = GetOutputRectInRect(renderIntRect);
 | |
|   if (outputRect.Overflows()) {
 | |
| #ifdef DEBUG_DUMP_SURFACES
 | |
|     printf("output rect overflowed, not painting anything\n");
 | |
|     printf("</pre>\n");
 | |
| #endif
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   RefPtr<DataSourceSurface> result;
 | |
|   if (!outputRect.IsEmpty()) {
 | |
|     result = GetOutput(outputRect);
 | |
|   }
 | |
| 
 | |
|   if (!result) {
 | |
|     // Null results are allowed and treated as transparent. Don't draw anything.
 | |
| #ifdef DEBUG_DUMP_SURFACES
 | |
|     printf("output returned null\n");
 | |
|     printf("</pre>\n");
 | |
| #endif
 | |
|     return;
 | |
|   }
 | |
| 
 | |
| #ifdef DEBUG_DUMP_SURFACES
 | |
|   printf("output from %s:\n", GetName());
 | |
|   printf("<img src='"); gfxUtils::DumpAsDataURL(result); printf("'>\n");
 | |
|   printf("</pre>\n");
 | |
| #endif
 | |
| 
 | |
|   Point sourceToDestOffset = aDestPoint - aSourceRect.TopLeft();
 | |
|   Rect renderedSourceRect = Rect(outputRect).Intersect(aSourceRect);
 | |
|   Rect renderedDestRect = renderedSourceRect + sourceToDestOffset;
 | |
|   if (result->GetFormat() == SurfaceFormat::A8) {
 | |
|     // Interpret the result as having implicitly black color channels.
 | |
|     aDrawTarget->PushClipRect(renderedDestRect);
 | |
|     aDrawTarget->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)),
 | |
|                              result,
 | |
|                              Point(outputRect.TopLeft()) + sourceToDestOffset,
 | |
|                              aOptions);
 | |
|     aDrawTarget->PopClip();
 | |
|   } else {
 | |
|     aDrawTarget->DrawSurface(result, renderedDestRect,
 | |
|                              renderedSourceRect - Point(outputRect.TopLeft()),
 | |
|                              DrawSurfaceOptions(), aOptions);
 | |
|   }
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeSoftware::GetOutput(const IntRect &aRect)
 | |
| {
 | |
|   MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect));
 | |
| 
 | |
|   if (aRect.Overflows()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   IntRect cachedRect;
 | |
|   IntRect requestedRect;
 | |
|   RefPtr<DataSourceSurface> cachedOutput;
 | |
| 
 | |
|   // Lock the cache and retrieve a cached surface if we have one and it can
 | |
|   // satisfy this request, or else request a rect we will compute and cache
 | |
|   {
 | |
|     MutexAutoLock lock(mCacheMutex);
 | |
| 
 | |
|     if (!mCachedRect.Contains(aRect)) {
 | |
|       RequestRect(aRect);
 | |
|       requestedRect = mRequestedRect;
 | |
|     } else {
 | |
|       MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?");
 | |
|       cachedRect = mCachedRect;
 | |
|       cachedOutput = mCachedOutput;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!cachedOutput) {
 | |
|     // Compute the output
 | |
|     cachedOutput = Render(requestedRect);
 | |
| 
 | |
|     // Update the cache for future requests
 | |
|     MutexAutoLock lock(mCacheMutex);
 | |
| 
 | |
|     mCachedOutput = cachedOutput;
 | |
|     if (!mCachedOutput) {
 | |
|       mCachedRect = IntRect();
 | |
|       mRequestedRect = IntRect();
 | |
|       return nullptr;
 | |
|     }
 | |
|     mCachedRect = requestedRect;
 | |
|     mRequestedRect = IntRect();
 | |
| 
 | |
|     cachedRect = mCachedRect;
 | |
|   }
 | |
| 
 | |
|   return GetDataSurfaceInRect(cachedOutput, cachedRect, aRect, EDGE_MODE_NONE);
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeSoftware::RequestRect(const IntRect &aRect)
 | |
| {
 | |
|   if (mRequestedRect.Contains(aRect)) {
 | |
|     // Bail out now. Otherwise pathological filters can spend time exponential
 | |
|     // in the number of primitives, e.g. if each primitive takes the
 | |
|     // previous primitive as its two inputs.
 | |
|     return;
 | |
|   }
 | |
|   mRequestedRect = mRequestedRect.Union(aRect);
 | |
|   RequestFromInputsForRect(aRect);
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex, const IntRect &aRect)
 | |
| {
 | |
|   if (aRect.Overflows()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   int32_t inputIndex = InputIndex(aInputEnumIndex);
 | |
|   if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
 | |
|     gfxDevCrash(LogReason::FilterInputError) << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
 | |
|     return;
 | |
|   }
 | |
|   if (mInputSurfaces[inputIndex]) {
 | |
|     return;
 | |
|   }
 | |
|   RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
 | |
|   MOZ_ASSERT(filter, "missing input");
 | |
|   filter->RequestRect(filter->GetOutputRectInRect(aRect));
 | |
| }
 | |
| 
 | |
| SurfaceFormat
 | |
| FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat,
 | |
|                                   FormatHint aFormatHint)
 | |
| {
 | |
|   if (aCurrentFormat == SurfaceFormat::A8 && aFormatHint == CAN_HANDLE_A8) {
 | |
|     return SurfaceFormat::A8;
 | |
|   }
 | |
|   return SurfaceFormat::B8G8R8A8;
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeSoftware::GetInputDataSourceSurface(uint32_t aInputEnumIndex,
 | |
|                                               const IntRect& aRect,
 | |
|                                               FormatHint aFormatHint,
 | |
|                                               ConvolveMatrixEdgeMode aEdgeMode,
 | |
|                                               const IntRect *aTransparencyPaddedSourceRect)
 | |
| {
 | |
|   if (aRect.Overflows()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
| #ifdef DEBUG_DUMP_SURFACES
 | |
|   printf("<section><h1>GetInputDataSourceSurface with aRect: %d, %d, %d, %d</h1>\n",
 | |
|          aRect.x, aRect.y, aRect.Width(), aRect.Height());
 | |
| #endif
 | |
|   int32_t inputIndex = InputIndex(aInputEnumIndex);
 | |
|   if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
 | |
|     gfxDevCrash(LogReason::FilterInputData) << "Invalid data " << inputIndex << " vs. " << NumberOfSetInputs();
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (aRect.IsEmpty()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   RefPtr<SourceSurface> surface;
 | |
|   IntRect surfaceRect;
 | |
| 
 | |
|   if (mInputSurfaces[inputIndex]) {
 | |
|     // Input from input surface
 | |
|     surface = mInputSurfaces[inputIndex];
 | |
| #ifdef DEBUG_DUMP_SURFACES
 | |
|     printf("input from input surface:\n");
 | |
| #endif
 | |
|     surfaceRect = IntRect(IntPoint(0, 0), surface->GetSize());
 | |
|   } else {
 | |
|     // Input from input filter
 | |
| #ifdef DEBUG_DUMP_SURFACES
 | |
|     printf("getting input from input filter %s...\n", mInputFilters[inputIndex]->GetName());
 | |
| #endif
 | |
|     RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
 | |
|     MOZ_ASSERT(filter, "missing input");
 | |
|     IntRect inputFilterOutput = filter->GetOutputRectInRect(aRect);
 | |
|     if (!inputFilterOutput.IsEmpty()) {
 | |
|       surface = filter->GetOutput(inputFilterOutput);
 | |
|     }
 | |
| #ifdef DEBUG_DUMP_SURFACES
 | |
|     printf("input from input filter %s:\n", mInputFilters[inputIndex]->GetName());
 | |
| #endif
 | |
|     surfaceRect = inputFilterOutput;
 | |
|     MOZ_ASSERT(!surface || surfaceRect.Size() == surface->GetSize());
 | |
|   }
 | |
| 
 | |
|   if (surface && surface->GetFormat() == SurfaceFormat::UNKNOWN) {
 | |
| #ifdef DEBUG_DUMP_SURFACES
 | |
|     printf("wrong input format</section>\n\n");
 | |
| #endif
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (!surfaceRect.IsEmpty() && !surface) {
 | |
| #ifdef DEBUG_DUMP_SURFACES
 | |
|     printf(" -- no input --</section>\n\n");
 | |
| #endif
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (aTransparencyPaddedSourceRect && !aTransparencyPaddedSourceRect->IsEmpty()) {
 | |
|     IntRect srcRect = aTransparencyPaddedSourceRect->Intersect(aRect);
 | |
|     surface = GetDataSurfaceInRect(surface, surfaceRect, srcRect, EDGE_MODE_NONE);
 | |
|     surfaceRect = srcRect;
 | |
|   }
 | |
| 
 | |
|   RefPtr<DataSourceSurface> result =
 | |
|     GetDataSurfaceInRect(surface, surfaceRect, aRect, aEdgeMode);
 | |
| 
 | |
|   if (result) {
 | |
|     // TODO: This isn't safe since we don't have a guarantee
 | |
|     // that future Maps will have the same stride
 | |
|     DataSourceSurface::MappedSurface map;
 | |
|     if (result->Map(DataSourceSurface::READ, &map)) {
 | |
|        // Unmap immediately since CloneAligned hasn't been updated
 | |
|        // to use the Map API yet. We can still read the stride/data
 | |
|        // values as long as we don't try to dereference them.
 | |
|       result->Unmap();
 | |
|       if (map.mStride != GetAlignedStride<16>(map.mStride, 1) ||
 | |
|           reinterpret_cast<uintptr_t>(map.mData) % 16 != 0) {
 | |
|         // Align unaligned surface.
 | |
|         result = CloneAligned(result);
 | |
|       }
 | |
|     } else {
 | |
|       result = nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| 
 | |
|   if (!result) {
 | |
| #ifdef DEBUG_DUMP_SURFACES
 | |
|     printf(" -- no input --</section>\n\n");
 | |
| #endif
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   SurfaceFormat currentFormat = result->GetFormat();
 | |
|   if (DesiredFormat(currentFormat, aFormatHint) == SurfaceFormat::B8G8R8A8 &&
 | |
|       currentFormat != SurfaceFormat::B8G8R8A8) {
 | |
|     result = FilterProcessing::ConvertToB8G8R8A8(result);
 | |
|   }
 | |
| 
 | |
| #ifdef DEBUG_DUMP_SURFACES
 | |
|   printf("<img src='"); gfxUtils::DumpAsDataURL(result); printf("'></section>");
 | |
| #endif
 | |
| 
 | |
|   MOZ_ASSERT(!result || result->GetSize() == aRect.Size(), "wrong surface size");
 | |
| 
 | |
|   return result.forget();
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex,
 | |
|                                        const IntRect &aInRect)
 | |
| {
 | |
|   if (aInRect.Overflows()) {
 | |
|     return IntRect();
 | |
|   }
 | |
| 
 | |
|   int32_t inputIndex = InputIndex(aInputEnumIndex);
 | |
|   if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
 | |
|     gfxDevCrash(LogReason::FilterInputRect) << "Invalid rect " << inputIndex << " vs. " << NumberOfSetInputs();
 | |
|     return IntRect();
 | |
|   }
 | |
|   if (mInputSurfaces[inputIndex]) {
 | |
|     return aInRect.Intersect(IntRect(IntPoint(0, 0),
 | |
|                                      mInputSurfaces[inputIndex]->GetSize()));
 | |
|   }
 | |
|   RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
 | |
|   MOZ_ASSERT(filter, "missing input");
 | |
|   return filter->GetOutputRectInRect(aInRect);
 | |
| }
 | |
| 
 | |
| size_t
 | |
| FilterNodeSoftware::NumberOfSetInputs()
 | |
| {
 | |
|   return std::max(mInputSurfaces.size(), mInputFilters.size());
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeSoftware::AddInvalidationListener(FilterInvalidationListener* aListener)
 | |
| {
 | |
|   MOZ_ASSERT(aListener, "null listener");
 | |
|   mInvalidationListeners.push_back(aListener);
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeSoftware::RemoveInvalidationListener(FilterInvalidationListener* aListener)
 | |
| {
 | |
|   MOZ_ASSERT(aListener, "null listener");
 | |
|   std::vector<FilterInvalidationListener*>::iterator it =
 | |
|     std::find(mInvalidationListeners.begin(), mInvalidationListeners.end(), aListener);
 | |
|   mInvalidationListeners.erase(it);
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware* aFilter)
 | |
| {
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeSoftware::Invalidate()
 | |
| {
 | |
|   MutexAutoLock lock(mCacheMutex);
 | |
|   mCachedOutput = nullptr;
 | |
|   mCachedRect = IntRect();
 | |
|   for (std::vector<FilterInvalidationListener*>::iterator it = mInvalidationListeners.begin();
 | |
|        it != mInvalidationListeners.end(); it++) {
 | |
|     (*it)->FilterInvalidated(this);
 | |
|   }
 | |
| }
 | |
| 
 | |
| FilterNodeSoftware::FilterNodeSoftware()
 | |
|   : mCacheMutex("FilterNodeSoftware::mCacheMutex")
 | |
| {
 | |
| }
 | |
| 
 | |
| FilterNodeSoftware::~FilterNodeSoftware()
 | |
| {
 | |
|   MOZ_ASSERT(!mInvalidationListeners.size(),
 | |
|              "All invalidation listeners should have unsubscribed themselves by now!");
 | |
| 
 | |
|   for (std::vector<RefPtr<FilterNodeSoftware> >::iterator it = mInputFilters.begin();
 | |
|        it != mInputFilters.end(); it++) {
 | |
|     if (*it) {
 | |
|       (*it)->RemoveInvalidationListener(this);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeSoftware::SetInput(uint32_t aIndex, FilterNode *aFilter)
 | |
| {
 | |
|   if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
 | |
|     MOZ_ASSERT(false, "can only take software filters as inputs");
 | |
|     return;
 | |
|   }
 | |
|   SetInput(aIndex, nullptr, static_cast<FilterNodeSoftware*>(aFilter));
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeSoftware::SetInput(uint32_t aIndex, SourceSurface *aSurface)
 | |
| {
 | |
|   SetInput(aIndex, aSurface, nullptr);
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex,
 | |
|                              SourceSurface *aSurface,
 | |
|                              FilterNodeSoftware *aFilter)
 | |
| {
 | |
|   int32_t inputIndex = InputIndex(aInputEnumIndex);
 | |
|   if (inputIndex < 0) {
 | |
|     gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex;
 | |
|     return;
 | |
|   }
 | |
|   if ((uint32_t)inputIndex >= NumberOfSetInputs()) {
 | |
|     mInputSurfaces.resize(inputIndex + 1);
 | |
|     mInputFilters.resize(inputIndex + 1);
 | |
|   }
 | |
|   mInputSurfaces[inputIndex] = aSurface;
 | |
|   if (mInputFilters[inputIndex]) {
 | |
|     mInputFilters[inputIndex]->RemoveInvalidationListener(this);
 | |
|   }
 | |
|   if (aFilter) {
 | |
|     aFilter->AddInvalidationListener(this);
 | |
|   }
 | |
|   mInputFilters[inputIndex] = aFilter;
 | |
|   if (!aSurface && !aFilter && (size_t)inputIndex == NumberOfSetInputs()) {
 | |
|     mInputSurfaces.resize(inputIndex);
 | |
|     mInputFilters.resize(inputIndex);
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| FilterNodeBlendSoftware::FilterNodeBlendSoftware()
 | |
|  : mBlendMode(BLEND_MODE_MULTIPLY)
 | |
| {}
 | |
| 
 | |
| int32_t
 | |
| FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   switch (aInputEnumIndex) {
 | |
|     case IN_BLEND_IN: return 0;
 | |
|     case IN_BLEND_IN2: return 1;
 | |
|     default: return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex, uint32_t aBlendMode)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_BLEND_BLENDMODE);
 | |
|   mBlendMode = static_cast<BlendMode>(aBlendMode);
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| static CompositionOp ToBlendOp(BlendMode aOp)
 | |
| {
 | |
|   switch (aOp) {
 | |
|   case BLEND_MODE_MULTIPLY:
 | |
|     return CompositionOp::OP_MULTIPLY;
 | |
|   case BLEND_MODE_SCREEN:
 | |
|     return CompositionOp::OP_SCREEN;
 | |
|   case BLEND_MODE_OVERLAY:
 | |
|     return CompositionOp::OP_OVERLAY;
 | |
|   case BLEND_MODE_DARKEN:
 | |
|     return CompositionOp::OP_DARKEN;
 | |
|   case BLEND_MODE_LIGHTEN:
 | |
|     return CompositionOp::OP_LIGHTEN;
 | |
|   case BLEND_MODE_COLOR_DODGE:
 | |
|     return CompositionOp::OP_COLOR_DODGE;
 | |
|   case BLEND_MODE_COLOR_BURN:
 | |
|     return CompositionOp::OP_COLOR_BURN;
 | |
|   case BLEND_MODE_HARD_LIGHT:
 | |
|     return CompositionOp::OP_HARD_LIGHT;
 | |
|   case BLEND_MODE_SOFT_LIGHT:
 | |
|     return CompositionOp::OP_SOFT_LIGHT;
 | |
|   case BLEND_MODE_DIFFERENCE:
 | |
|     return CompositionOp::OP_DIFFERENCE;
 | |
|   case BLEND_MODE_EXCLUSION:
 | |
|     return CompositionOp::OP_EXCLUSION;
 | |
|   case BLEND_MODE_HUE:
 | |
|     return CompositionOp::OP_HUE;
 | |
|   case BLEND_MODE_SATURATION:
 | |
|     return CompositionOp::OP_SATURATION;
 | |
|   case BLEND_MODE_COLOR:
 | |
|     return CompositionOp::OP_COLOR;
 | |
|   case BLEND_MODE_LUMINOSITY:
 | |
|     return CompositionOp::OP_LUMINOSITY;
 | |
|   default:
 | |
|     return CompositionOp::OP_OVER;
 | |
|   }
 | |
| 
 | |
|   return CompositionOp::OP_OVER;
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeBlendSoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   RefPtr<DataSourceSurface> input1 =
 | |
|     GetInputDataSourceSurface(IN_BLEND_IN, aRect, NEED_COLOR_CHANNELS);
 | |
|   RefPtr<DataSourceSurface> input2 =
 | |
|     GetInputDataSourceSurface(IN_BLEND_IN2, aRect, NEED_COLOR_CHANNELS);
 | |
| 
 | |
|   // Null inputs need to be treated as transparent.
 | |
| 
 | |
|   // First case: both are transparent.
 | |
|   if (!input1 && !input2) {
 | |
|     // Then the result is transparent, too.
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Second case: one of them is transparent. Return the non-transparent one.
 | |
|   if (!input1 || !input2) {
 | |
|     return input1 ? input1.forget() : input2.forget();
 | |
|   }
 | |
| 
 | |
|   // Third case: both are non-transparent.
 | |
|   // Apply normal filtering.
 | |
|   RefPtr<DataSourceSurface> target = FilterProcessing::ApplyBlending(input1, input2, mBlendMode);
 | |
|   if (target != nullptr) {
 | |
|     return target.forget();
 | |
|   }
 | |
| 
 | |
|   IntSize size = input1->GetSize();
 | |
|   target =
 | |
|     Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
 | |
|   if (MOZ2D_WARN_IF(!target)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   CopyRect(input1, target, IntRect(IntPoint(), size), IntPoint());
 | |
| 
 | |
|   // This needs to stay in scope until the draw target has been flushed.
 | |
|   DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::READ_WRITE);
 | |
|   if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   RefPtr<DrawTarget> dt =
 | |
|     Factory::CreateDrawTargetForData(BackendType::CAIRO,
 | |
|                                      targetMap.GetData(),
 | |
|                                      target->GetSize(),
 | |
|                                      targetMap.GetStride(),
 | |
|                                      target->GetFormat());
 | |
| 
 | |
|   if (!dt) {
 | |
|     gfxWarning() << "FilterNodeBlendSoftware::Render failed in CreateDrawTargetForData";
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   Rect r(0, 0, size.width, size.height);
 | |
|   dt->DrawSurface(input2, r, r, DrawSurfaceOptions(), DrawOptions(1.0f, ToBlendOp(mBlendMode)));
 | |
|   dt->Flush();
 | |
|   return target.forget();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   RequestInputRect(IN_BLEND_IN, aRect);
 | |
|   RequestInputRect(IN_BLEND_IN2, aRect);
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   return GetInputRectInRect(IN_BLEND_IN, aRect).Union(
 | |
|     GetInputRectInRect(IN_BLEND_IN2, aRect)).Intersect(aRect);
 | |
| }
 | |
| 
 | |
| FilterNodeTransformSoftware::FilterNodeTransformSoftware()
 | |
|   : mSamplingFilter(SamplingFilter::GOOD)
 | |
| {}
 | |
| 
 | |
| int32_t
 | |
| FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   switch (aInputEnumIndex) {
 | |
|     case IN_TRANSFORM_IN: return 0;
 | |
|     default: return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, uint32_t aFilter)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER);
 | |
|   mSamplingFilter = static_cast<SamplingFilter>(aFilter);
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, const Matrix &aMatrix)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX);
 | |
|   mMatrix = aMatrix;
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeTransformSoftware::SourceRectForOutputRect(const IntRect &aRect)
 | |
| {
 | |
|   if (aRect.IsEmpty()) {
 | |
|     return IntRect();
 | |
|   }
 | |
| 
 | |
|   Matrix inverted(mMatrix);
 | |
|   if (!inverted.Invert()) {
 | |
|     return IntRect();
 | |
|   }
 | |
| 
 | |
|   Rect neededRect = inverted.TransformBounds(Rect(aRect));
 | |
|   neededRect.RoundOut();
 | |
|   IntRect neededIntRect;
 | |
|   if (!neededRect.ToIntRect(&neededIntRect)) {
 | |
|     return IntRect();
 | |
|   }
 | |
|   return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect);
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeTransformSoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   IntRect srcRect = SourceRectForOutputRect(aRect);
 | |
| 
 | |
|   RefPtr<DataSourceSurface> input =
 | |
|     GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect);
 | |
| 
 | |
|   if (!input) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   Matrix transform = Matrix::Translation(srcRect.X(), srcRect.Y()) * mMatrix *
 | |
|                      Matrix::Translation(-aRect.X(), -aRect.Y());
 | |
|   if (transform.IsIdentity() && srcRect.Size() == aRect.Size()) {
 | |
|     return input.forget();
 | |
|   }
 | |
| 
 | |
|   RefPtr<DataSourceSurface> surf =
 | |
|     Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat(), true);
 | |
| 
 | |
|   if (!surf) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   DataSourceSurface::MappedSurface mapping;
 | |
|   if (!surf->Map(DataSourceSurface::MapType::WRITE, &mapping)) {
 | |
|     gfxCriticalError() << "FilterNodeTransformSoftware::Render failed to map surface";
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   RefPtr<DrawTarget> dt =
 | |
|     Factory::CreateDrawTargetForData(BackendType::CAIRO,
 | |
|                                      mapping.mData,
 | |
|                                      surf->GetSize(),
 | |
|                                      mapping.mStride,
 | |
|                                      surf->GetFormat());
 | |
|   if (!dt) {
 | |
|     gfxWarning() << "FilterNodeTransformSoftware::Render failed in CreateDrawTargetForData";
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   Rect r(0, 0, srcRect.Width(), srcRect.Height());
 | |
|   dt->SetTransform(transform);
 | |
|   dt->DrawSurface(input, r, r, DrawSurfaceOptions(mSamplingFilter));
 | |
| 
 | |
|   dt->Flush();
 | |
|   surf->Unmap();
 | |
|   return surf.forget();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeTransformSoftware::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect));
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   IntRect srcRect = SourceRectForOutputRect(aRect);
 | |
|   if (srcRect.IsEmpty()) {
 | |
|     return IntRect();
 | |
|   }
 | |
| 
 | |
|   Rect outRect = mMatrix.TransformBounds(Rect(srcRect));
 | |
|   outRect.RoundOut();
 | |
|   IntRect outIntRect;
 | |
|   if (!outRect.ToIntRect(&outIntRect)) {
 | |
|     return IntRect();
 | |
|   }
 | |
|   return outIntRect.Intersect(aRect);
 | |
| }
 | |
| 
 | |
| FilterNodeMorphologySoftware::FilterNodeMorphologySoftware()
 | |
|  : mOperator(MORPHOLOGY_OPERATOR_ERODE)
 | |
| {}
 | |
| 
 | |
| int32_t
 | |
| FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   switch (aInputEnumIndex) {
 | |
|     case IN_MORPHOLOGY_IN: return 0;
 | |
|     default: return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
 | |
|                                            const IntSize &aRadii)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_RADII);
 | |
|   mRadii.width = std::min(std::max(aRadii.width, 0), 100000);
 | |
|   mRadii.height = std::min(std::max(aRadii.height, 0), 100000);
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
 | |
|                                            uint32_t aOperator)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_OPERATOR);
 | |
|   mOperator = static_cast<MorphologyOperator>(aOperator);
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| static already_AddRefed<DataSourceSurface>
 | |
| ApplyMorphology(const IntRect& aSourceRect, DataSourceSurface* aInput,
 | |
|                 const IntRect& aDestRect, int32_t rx, int32_t ry,
 | |
|                 MorphologyOperator aOperator)
 | |
| {
 | |
|   IntRect srcRect = aSourceRect - aDestRect.TopLeft();
 | |
|   IntRect destRect = aDestRect - aDestRect.TopLeft();
 | |
|   IntRect tmpRect(destRect.X(), srcRect.Y(), destRect.Width(), srcRect.Height());
 | |
| #ifdef DEBUG
 | |
|   IntMargin margin = srcRect - destRect;
 | |
|   MOZ_ASSERT(margin.top >= ry && margin.right >= rx &&
 | |
|              margin.bottom >= ry && margin.left >= rx, "insufficient margin");
 | |
| #endif
 | |
| 
 | |
|   RefPtr<DataSourceSurface> tmp;
 | |
|   if (rx == 0) {
 | |
|     tmp = aInput;
 | |
|   } else {
 | |
|     tmp = Factory::CreateDataSourceSurface(tmpRect.Size(), SurfaceFormat::B8G8R8A8);
 | |
|     if (MOZ2D_WARN_IF(!tmp)) {
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
 | |
|     DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::WRITE);
 | |
|     if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !tmpMap.IsMapped())) {
 | |
|       return nullptr;
 | |
|     }
 | |
|     uint8_t* sourceData = DataAtOffset(aInput, sourceMap.GetMappedSurface(),
 | |
|                                        destRect.TopLeft() - srcRect.TopLeft());
 | |
|     uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(),
 | |
|                                     destRect.TopLeft() - tmpRect.TopLeft());
 | |
| 
 | |
|     FilterProcessing::ApplyMorphologyHorizontal(
 | |
|       sourceData, sourceMap.GetStride(), tmpData, tmpMap.GetStride(), tmpRect, rx, aOperator);
 | |
|   }
 | |
| 
 | |
|   RefPtr<DataSourceSurface> dest;
 | |
|   if (ry == 0) {
 | |
|     dest = tmp;
 | |
|   } else {
 | |
|     dest = Factory::CreateDataSourceSurface(destRect.Size(), SurfaceFormat::B8G8R8A8);
 | |
|     if (MOZ2D_WARN_IF(!dest)) {
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::READ);
 | |
|     DataSourceSurface::ScopedMap destMap(dest, DataSourceSurface::WRITE);
 | |
|     if (MOZ2D_WARN_IF(!tmpMap.IsMapped() || !destMap.IsMapped())) {
 | |
|       return nullptr;
 | |
|     }
 | |
|     int32_t tmpStride = tmpMap.GetStride();
 | |
|     uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(), destRect.TopLeft() - tmpRect.TopLeft());
 | |
| 
 | |
|     int32_t destStride = destMap.GetStride();
 | |
|     uint8_t* destData = destMap.GetData();
 | |
| 
 | |
|     FilterProcessing::ApplyMorphologyVertical(
 | |
|       tmpData, tmpStride, destData, destStride, destRect, ry, aOperator);
 | |
|   }
 | |
| 
 | |
|   return dest.forget();
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeMorphologySoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   IntRect srcRect = aRect;
 | |
|   srcRect.Inflate(mRadii);
 | |
| 
 | |
|   RefPtr<DataSourceSurface> input =
 | |
|     GetInputDataSourceSurface(IN_MORPHOLOGY_IN, srcRect, NEED_COLOR_CHANNELS);
 | |
|   if (!input) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   int32_t rx = mRadii.width;
 | |
|   int32_t ry = mRadii.height;
 | |
| 
 | |
|   if (rx == 0 && ry == 0) {
 | |
|     return input.forget();
 | |
|   }
 | |
| 
 | |
|   return ApplyMorphology(srcRect, input, aRect, rx, ry, mOperator);
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeMorphologySoftware::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   IntRect srcRect = aRect;
 | |
|   srcRect.Inflate(mRadii);
 | |
|   RequestInputRect(IN_MORPHOLOGY_IN, srcRect);
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeMorphologySoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   IntRect inflatedSourceRect = aRect;
 | |
|   inflatedSourceRect.Inflate(mRadii);
 | |
|   IntRect inputRect = GetInputRectInRect(IN_MORPHOLOGY_IN, inflatedSourceRect);
 | |
|   if (mOperator == MORPHOLOGY_OPERATOR_ERODE) {
 | |
|     inputRect.Deflate(mRadii);
 | |
|   } else {
 | |
|     inputRect.Inflate(mRadii);
 | |
|   }
 | |
|   return inputRect.Intersect(aRect);
 | |
| }
 | |
| 
 | |
| int32_t
 | |
| FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   switch (aInputEnumIndex) {
 | |
|     case IN_COLOR_MATRIX_IN: return 0;
 | |
|     default: return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                             const Matrix5x4 &aMatrix)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX);
 | |
|   mMatrix = aMatrix;
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                             uint32_t aAlphaMode)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE);
 | |
|   mAlphaMode = (AlphaMode)aAlphaMode;
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| static already_AddRefed<DataSourceSurface>
 | |
| Premultiply(DataSourceSurface* aSurface)
 | |
| {
 | |
|   if (aSurface->GetFormat() == SurfaceFormat::A8) {
 | |
|     RefPtr<DataSourceSurface> surface(aSurface);
 | |
|     return surface.forget();
 | |
|   }
 | |
| 
 | |
|   IntSize size = aSurface->GetSize();
 | |
|   RefPtr<DataSourceSurface> target =
 | |
|     Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
 | |
|   if (MOZ2D_WARN_IF(!target)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
 | |
|   DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
 | |
|   if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   uint8_t* inputData = inputMap.GetData();
 | |
|   int32_t inputStride = inputMap.GetStride();
 | |
|   uint8_t* targetData = targetMap.GetData();
 | |
|   int32_t targetStride = targetMap.GetStride();
 | |
| 
 | |
|   FilterProcessing::DoPremultiplicationCalculation(
 | |
|     size, targetData, targetStride, inputData, inputStride);
 | |
| 
 | |
|   return target.forget();
 | |
| }
 | |
| 
 | |
| static already_AddRefed<DataSourceSurface>
 | |
| Unpremultiply(DataSourceSurface* aSurface)
 | |
| {
 | |
|   if (aSurface->GetFormat() == SurfaceFormat::A8) {
 | |
|     RefPtr<DataSourceSurface> surface(aSurface);
 | |
|     return surface.forget();
 | |
|   }
 | |
| 
 | |
|   IntSize size = aSurface->GetSize();
 | |
|   RefPtr<DataSourceSurface> target =
 | |
|     Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
 | |
|   if (MOZ2D_WARN_IF(!target)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
 | |
|   DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
 | |
|   if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   uint8_t* inputData = inputMap.GetData();
 | |
|   int32_t inputStride = inputMap.GetStride();
 | |
|   uint8_t* targetData = targetMap.GetData();
 | |
|   int32_t targetStride = targetMap.GetStride();
 | |
| 
 | |
|   FilterProcessing::DoUnpremultiplicationCalculation(
 | |
|     size, targetData, targetStride, inputData, inputStride);
 | |
| 
 | |
|   return target.forget();
 | |
| }
 | |
| 
 | |
| static already_AddRefed<DataSourceSurface>
 | |
| Opacity(DataSourceSurface* aSurface, Float aValue)
 | |
| {
 | |
|   if (aValue == 1.0f) {
 | |
|     RefPtr<DataSourceSurface> surface(aSurface);
 | |
|     return surface.forget();
 | |
|   }
 | |
| 
 | |
|   IntSize size = aSurface->GetSize();
 | |
|   RefPtr<DataSourceSurface> target =
 | |
|     Factory::CreateDataSourceSurface(size, aSurface->GetFormat());
 | |
|   if (MOZ2D_WARN_IF(!target)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
 | |
|   DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
 | |
|   if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   uint8_t* inputData = inputMap.GetData();
 | |
|   int32_t inputStride = inputMap.GetStride();
 | |
|   uint8_t* targetData = targetMap.GetData();
 | |
|   int32_t targetStride = targetMap.GetStride();
 | |
| 
 | |
|   if (aSurface->GetFormat() == SurfaceFormat::A8) {
 | |
|     FilterProcessing::DoOpacityCalculationA8(
 | |
|       size, targetData, targetStride, inputData, inputStride, aValue);
 | |
|   } else {
 | |
|     MOZ_ASSERT(aSurface->GetFormat() == SurfaceFormat::B8G8R8A8);
 | |
|     FilterProcessing::DoOpacityCalculation(
 | |
|       size, targetData, targetStride, inputData, inputStride, aValue);
 | |
|   }
 | |
| 
 | |
|   return target.forget();
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeColorMatrixSoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   RefPtr<DataSourceSurface> input =
 | |
|     GetInputDataSourceSurface(IN_COLOR_MATRIX_IN, aRect, NEED_COLOR_CHANNELS);
 | |
|   if (!input) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
 | |
|     input = Unpremultiply(input);
 | |
|   }
 | |
| 
 | |
|   RefPtr<DataSourceSurface> result =
 | |
|     FilterProcessing::ApplyColorMatrix(input, mMatrix);
 | |
| 
 | |
|   if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
 | |
|     result = Premultiply(result);
 | |
|   }
 | |
| 
 | |
|   return result.forget();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeColorMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   RequestInputRect(IN_COLOR_MATRIX_IN, aRect);
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeColorMatrixSoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   if (mMatrix._54 > 0.0f) {
 | |
|     return aRect;
 | |
|   }
 | |
|   return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect);
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex, const Color &aColor)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_FLOOD_COLOR);
 | |
|   mColor = aColor;
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| static uint32_t
 | |
| ColorToBGRA(const Color& aColor)
 | |
| {
 | |
|   union {
 | |
|     uint32_t color;
 | |
|     uint8_t components[4];
 | |
|   };
 | |
|   components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = NS_lround(aColor.r * aColor.a * 255.0f);
 | |
|   components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = NS_lround(aColor.g * aColor.a * 255.0f);
 | |
|   components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = NS_lround(aColor.b * aColor.a * 255.0f);
 | |
|   components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = NS_lround(aColor.a * 255.0f);
 | |
|   return color;
 | |
| }
 | |
| 
 | |
| static SurfaceFormat
 | |
| FormatForColor(Color aColor)
 | |
| {
 | |
|   if (aColor.r == 0 && aColor.g == 0 && aColor.b == 0) {
 | |
|     return SurfaceFormat::A8;
 | |
|   }
 | |
|   return SurfaceFormat::B8G8R8A8;
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeFloodSoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   SurfaceFormat format = FormatForColor(mColor);
 | |
|   RefPtr<DataSourceSurface> target =
 | |
|     Factory::CreateDataSourceSurface(aRect.Size(), format);
 | |
|   if (MOZ2D_WARN_IF(!target)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
 | |
|   if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   uint8_t* targetData = targetMap.GetData();
 | |
|   int32_t stride = targetMap.GetStride();
 | |
| 
 | |
|   if (format == SurfaceFormat::B8G8R8A8) {
 | |
|     uint32_t color = ColorToBGRA(mColor);
 | |
|     for (int32_t y = 0; y < aRect.Height(); y++) {
 | |
|       for (int32_t x = 0; x < aRect.Width(); x++) {
 | |
|         *((uint32_t*)targetData + x) = color;
 | |
|       }
 | |
|       PodZero(&targetData[aRect.Width() * 4], stride - aRect.Width() * 4);
 | |
|       targetData += stride;
 | |
|     }
 | |
|   } else if (format == SurfaceFormat::A8) {
 | |
|     uint8_t alpha = NS_lround(mColor.a * 255.0f);
 | |
|     for (int32_t y = 0; y < aRect.Height(); y++) {
 | |
|       for (int32_t x = 0; x < aRect.Width(); x++) {
 | |
|         targetData[x] = alpha;
 | |
|       }
 | |
|       PodZero(&targetData[aRect.Width()], stride - aRect.Width());
 | |
|       targetData += stride;
 | |
|     }
 | |
|   } else {
 | |
|     gfxDevCrash(LogReason::FilterInputFormat) << "Bad format in flood render " << (int)format;
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   return target.forget();
 | |
| }
 | |
| 
 | |
| // Override GetOutput to get around caching. Rendering simple floods is
 | |
| // comparatively fast.
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeFloodSoftware::GetOutput(const IntRect& aRect)
 | |
| {
 | |
|   return Render(aRect);
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   if (mColor.a == 0.0f) {
 | |
|     return IntRect();
 | |
|   }
 | |
|   return aRect;
 | |
| }
 | |
| 
 | |
| int32_t
 | |
| FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   switch (aInputEnumIndex) {
 | |
|     case IN_TILE_IN: return 0;
 | |
|     default: return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeTileSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                      const IntRect &aSourceRect)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT);
 | |
|   mSourceRect.SetRect(int32_t(aSourceRect.X()), int32_t(aSourceRect.Y()),
 | |
|                       int32_t(aSourceRect.Width()), int32_t(aSourceRect.Height()));
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| namespace {
 | |
| struct CompareIntRects
 | |
| {
 | |
|   bool operator()(const IntRect& a, const IntRect& b) const
 | |
|   {
 | |
|     if (a.X() != b.X()) {
 | |
|       return a.X() < b.X();
 | |
|     }
 | |
|     if (a.Y() != b.Y()) {
 | |
|       return a.Y() < b.Y();
 | |
|     }
 | |
|     if (a.Width() != b.Width()) {
 | |
|       return a.Width() < b.Width();
 | |
|     }
 | |
|     return a.Height() < b.Height();
 | |
|   }
 | |
| };
 | |
| 
 | |
| } // namespace
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeTileSoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   if (mSourceRect.IsEmpty()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (mSourceRect.Contains(aRect)) {
 | |
|     return GetInputDataSourceSurface(IN_TILE_IN, aRect);
 | |
|   }
 | |
| 
 | |
|   RefPtr<DataSourceSurface> target;
 | |
| 
 | |
|   typedef std::map<IntRect, RefPtr<DataSourceSurface>, CompareIntRects> InputMap;
 | |
|   InputMap inputs;
 | |
| 
 | |
|   IntPoint startIndex = TileIndex(mSourceRect, aRect.TopLeft());
 | |
|   IntPoint endIndex = TileIndex(mSourceRect, aRect.BottomRight());
 | |
|   for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
 | |
|     for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
 | |
|       IntPoint sourceToDestOffset(ix * mSourceRect.Width(),
 | |
|                                   iy * mSourceRect.Height());
 | |
|       IntRect destRect = aRect.Intersect(mSourceRect + sourceToDestOffset);
 | |
|       IntRect srcRect = destRect - sourceToDestOffset;
 | |
|       if (srcRect.IsEmpty()) {
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       RefPtr<DataSourceSurface> input;
 | |
|       InputMap::iterator it = inputs.find(srcRect);
 | |
|       if (it == inputs.end()) {
 | |
|         input = GetInputDataSourceSurface(IN_TILE_IN, srcRect);
 | |
|         inputs[srcRect] = input;
 | |
|       } else {
 | |
|         input = it->second;
 | |
|       }
 | |
|       if (!input) {
 | |
|         return nullptr;
 | |
|       }
 | |
|       if (!target) {
 | |
|         // We delay creating the target until now because we want to use the
 | |
|         // same format as our input filter, and we do not actually know the
 | |
|         // input format before we call GetInputDataSourceSurface.
 | |
|         target = Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat());
 | |
|         if (MOZ2D_WARN_IF(!target)) {
 | |
|           return nullptr;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (input->GetFormat() != target->GetFormat()) {
 | |
|         // Different rectangles of the input can have different formats. If
 | |
|         // that happens, just convert everything to B8G8R8A8.
 | |
|         target = FilterProcessing::ConvertToB8G8R8A8(target);
 | |
|         input = FilterProcessing::ConvertToB8G8R8A8(input);
 | |
|         if (MOZ2D_WARN_IF(!target) || MOZ2D_WARN_IF(!input)) {
 | |
|           return nullptr;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       CopyRect(input, target, srcRect - srcRect.TopLeft(), destRect.TopLeft() - aRect.TopLeft());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return target.forget();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   // Do not request anything.
 | |
|   // Source rects for the tile filter can be discontinuous with large gaps
 | |
|   // between them. Requesting those from our input filter might cause it to
 | |
|   // render the whole bounding box of all of them, which would be wasteful.
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeTileSoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   return aRect;
 | |
| }
 | |
| 
 | |
| FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware()
 | |
|  : mDisableR(true)
 | |
|  , mDisableG(true)
 | |
|  , mDisableB(true)
 | |
|  , mDisableA(true)
 | |
| {}
 | |
| 
 | |
| void
 | |
| FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                                   bool aDisable)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_TRANSFER_DISABLE_R:
 | |
|       mDisableR = aDisable;
 | |
|       break;
 | |
|     case ATT_TRANSFER_DISABLE_G:
 | |
|       mDisableG = aDisable;
 | |
|       break;
 | |
|     case ATT_TRANSFER_DISABLE_B:
 | |
|       mDisableB = aDisable;
 | |
|       break;
 | |
|     case ATT_TRANSFER_DISABLE_A:
 | |
|       mDisableA = aDisable;
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeComponentTransferSoftware::SetAttribute");
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeComponentTransferSoftware::GenerateLookupTable(ptrdiff_t aComponent,
 | |
|                                                          uint8_t aTables[4][256],
 | |
|                                                          bool aDisabled)
 | |
| {
 | |
|   if (aDisabled) {
 | |
|     static uint8_t sIdentityLookupTable[256];
 | |
|     static bool sInitializedIdentityLookupTable = false;
 | |
|     if (!sInitializedIdentityLookupTable) {
 | |
|       for (int32_t i = 0; i < 256; i++) {
 | |
|         sIdentityLookupTable[i] = i;
 | |
|       }
 | |
|       sInitializedIdentityLookupTable = true;
 | |
|     }
 | |
|     memcpy(aTables[aComponent], sIdentityLookupTable, 256);
 | |
|   } else {
 | |
|     FillLookupTable(aComponent, aTables[aComponent]);
 | |
|   }
 | |
| }
 | |
| 
 | |
| template<uint32_t BytesPerPixel>
 | |
| static void TransferComponents(DataSourceSurface* aInput,
 | |
|                                DataSourceSurface* aTarget,
 | |
|                                const uint8_t aLookupTables[BytesPerPixel][256])
 | |
| {
 | |
|   MOZ_ASSERT(aInput->GetFormat() == aTarget->GetFormat(), "different formats");
 | |
|   IntSize size = aInput->GetSize();
 | |
| 
 | |
|   DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
 | |
|   DataSourceSurface::ScopedMap targetMap(aTarget, DataSourceSurface::WRITE);
 | |
|   if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   uint8_t* sourceData = sourceMap.GetData();
 | |
|   int32_t sourceStride = sourceMap.GetStride();
 | |
|   uint8_t* targetData = targetMap.GetData();
 | |
|   int32_t targetStride = targetMap.GetStride();
 | |
| 
 | |
|   MOZ_ASSERT(sourceStride <= targetStride, "target smaller than source");
 | |
| 
 | |
|   for (int32_t y = 0; y < size.height; y++) {
 | |
|     for (int32_t x = 0; x < size.width; x++) {
 | |
|       uint32_t sourceIndex = y * sourceStride + x * BytesPerPixel;
 | |
|       uint32_t targetIndex = y * targetStride + x * BytesPerPixel;
 | |
|       for (uint32_t i = 0; i < BytesPerPixel; i++) {
 | |
|         targetData[targetIndex + i] = aLookupTables[i][sourceData[sourceIndex + i]];
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Zero padding to keep valgrind happy.
 | |
|     PodZero(&targetData[y * targetStride + size.width * BytesPerPixel],
 | |
|             targetStride - size.width * BytesPerPixel);
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| IsAllZero(uint8_t aLookupTable[256])
 | |
| {
 | |
|   for (int32_t i = 0; i < 256; i++) {
 | |
|     if (aLookupTable[i] != 0) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeComponentTransferSoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   if (mDisableR && mDisableG && mDisableB && mDisableA) {
 | |
|     return GetInputDataSourceSurface(IN_TRANSFER_IN, aRect);
 | |
|   }
 | |
| 
 | |
|   uint8_t lookupTables[4][256];
 | |
|   GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R, lookupTables, mDisableR);
 | |
|   GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G, lookupTables, mDisableG);
 | |
|   GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B, lookupTables, mDisableB);
 | |
|   GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A, lookupTables, mDisableA);
 | |
| 
 | |
|   bool needColorChannels =
 | |
|     lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R][0] != 0 ||
 | |
|     lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G][0] != 0 ||
 | |
|     lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B][0] != 0;
 | |
| 
 | |
|   FormatHint pref = needColorChannels ? NEED_COLOR_CHANNELS : CAN_HANDLE_A8;
 | |
| 
 | |
|   RefPtr<DataSourceSurface> input =
 | |
|     GetInputDataSourceSurface(IN_TRANSFER_IN, aRect, pref);
 | |
|   if (!input) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (input->GetFormat() == SurfaceFormat::B8G8R8A8 && !needColorChannels) {
 | |
|     bool colorChannelsBecomeBlack =
 | |
|       IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) &&
 | |
|       IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) &&
 | |
|       IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B]);
 | |
| 
 | |
|     if (colorChannelsBecomeBlack) {
 | |
|       input = FilterProcessing::ExtractAlpha(input);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   SurfaceFormat format = input->GetFormat();
 | |
|   if (format == SurfaceFormat::A8 && mDisableA) {
 | |
|     return input.forget();
 | |
|   }
 | |
| 
 | |
|   RefPtr<DataSourceSurface> target =
 | |
|     Factory::CreateDataSourceSurface(aRect.Size(), format);
 | |
|   if (MOZ2D_WARN_IF(!target)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (format == SurfaceFormat::A8) {
 | |
|     TransferComponents<1>(input, target, &lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_A]);
 | |
|   } else {
 | |
|     TransferComponents<4>(input, target, lookupTables);
 | |
|   }
 | |
| 
 | |
|   return target.forget();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeComponentTransferSoftware::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   RequestInputRect(IN_TRANSFER_IN, aRect);
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeComponentTransferSoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   if (mDisableA) {
 | |
|     return GetInputRectInRect(IN_TRANSFER_IN, aRect);
 | |
|   }
 | |
|   return aRect;
 | |
| }
 | |
| 
 | |
| int32_t
 | |
| FilterNodeComponentTransferSoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   switch (aInputEnumIndex) {
 | |
|     case IN_TRANSFER_IN: return 0;
 | |
|     default: return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                               const Float* aFloat,
 | |
|                                               uint32_t aSize)
 | |
| {
 | |
|   std::vector<Float> table(aFloat, aFloat + aSize);
 | |
|   switch (aIndex) {
 | |
|     case ATT_TABLE_TRANSFER_TABLE_R:
 | |
|       mTableR = table;
 | |
|       break;
 | |
|     case ATT_TABLE_TRANSFER_TABLE_G:
 | |
|       mTableG = table;
 | |
|       break;
 | |
|     case ATT_TABLE_TRANSFER_TABLE_B:
 | |
|       mTableB = table;
 | |
|       break;
 | |
|     case ATT_TABLE_TRANSFER_TABLE_A:
 | |
|       mTableA = table;
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeTableTransferSoftware::SetAttribute");
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
 | |
|                                                  uint8_t aTable[256])
 | |
| {
 | |
|   switch (aComponent) {
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
 | |
|       FillLookupTableImpl(mTableR, aTable);
 | |
|       break;
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
 | |
|       FillLookupTableImpl(mTableG, aTable);
 | |
|       break;
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
 | |
|       FillLookupTableImpl(mTableB, aTable);
 | |
|       break;
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
 | |
|       FillLookupTableImpl(mTableA, aTable);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_ASSERT(false, "unknown component");
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeTableTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues,
 | |
|                                                      uint8_t aTable[256])
 | |
| {
 | |
|   uint32_t tvLength = aTableValues.size();
 | |
|   if (tvLength < 2) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   for (size_t i = 0; i < 256; i++) {
 | |
|     uint32_t k = (i * (tvLength - 1)) / 255;
 | |
|     Float v1 = aTableValues[k];
 | |
|     Float v2 = aTableValues[std::min(k + 1, tvLength - 1)];
 | |
|     int32_t val =
 | |
|       int32_t(255 * (v1 + (i/255.0f - k/float(tvLength-1))*(tvLength - 1)*(v2 - v1)));
 | |
|     val = std::min(255, val);
 | |
|     val = std::max(0, val);
 | |
|     aTable[i] = val;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                               const Float* aFloat,
 | |
|                                               uint32_t aSize)
 | |
| {
 | |
|   std::vector<Float> discrete(aFloat, aFloat + aSize);
 | |
|   switch (aIndex) {
 | |
|     case ATT_DISCRETE_TRANSFER_TABLE_R:
 | |
|       mTableR = discrete;
 | |
|       break;
 | |
|     case ATT_DISCRETE_TRANSFER_TABLE_G:
 | |
|       mTableG = discrete;
 | |
|       break;
 | |
|     case ATT_DISCRETE_TRANSFER_TABLE_B:
 | |
|       mTableB = discrete;
 | |
|       break;
 | |
|     case ATT_DISCRETE_TRANSFER_TABLE_A:
 | |
|       mTableA = discrete;
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeDiscreteTransferSoftware::SetAttribute");
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
 | |
|                                                     uint8_t aTable[256])
 | |
| {
 | |
|   switch (aComponent) {
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
 | |
|       FillLookupTableImpl(mTableR, aTable);
 | |
|       break;
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
 | |
|       FillLookupTableImpl(mTableG, aTable);
 | |
|       break;
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
 | |
|       FillLookupTableImpl(mTableB, aTable);
 | |
|       break;
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
 | |
|       FillLookupTableImpl(mTableA, aTable);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_ASSERT(false, "unknown component");
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues,
 | |
|                                                         uint8_t aTable[256])
 | |
| {
 | |
|   uint32_t tvLength = aTableValues.size();
 | |
|   if (tvLength < 1) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   for (size_t i = 0; i < 256; i++) {
 | |
|     uint32_t k = (i * tvLength) / 255;
 | |
|     k = std::min(k, tvLength - 1);
 | |
|     Float v = aTableValues[k];
 | |
|     int32_t val = NS_lround(255 * v);
 | |
|     val = std::min(255, val);
 | |
|     val = std::max(0, val);
 | |
|     aTable[i] = val;
 | |
|   }
 | |
| }
 | |
| 
 | |
| FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware()
 | |
|  : mSlopeR(0)
 | |
|  , mSlopeG(0)
 | |
|  , mSlopeB(0)
 | |
|  , mSlopeA(0)
 | |
|  , mInterceptR(0)
 | |
|  , mInterceptG(0)
 | |
|  , mInterceptB(0)
 | |
|  , mInterceptA(0)
 | |
| {}
 | |
| 
 | |
| void
 | |
| FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                                Float aValue)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_LINEAR_TRANSFER_SLOPE_R:
 | |
|       mSlopeR = aValue;
 | |
|       break;
 | |
|     case ATT_LINEAR_TRANSFER_INTERCEPT_R:
 | |
|       mInterceptR = aValue;
 | |
|       break;
 | |
|     case ATT_LINEAR_TRANSFER_SLOPE_G:
 | |
|       mSlopeG = aValue;
 | |
|       break;
 | |
|     case ATT_LINEAR_TRANSFER_INTERCEPT_G:
 | |
|       mInterceptG = aValue;
 | |
|       break;
 | |
|     case ATT_LINEAR_TRANSFER_SLOPE_B:
 | |
|       mSlopeB = aValue;
 | |
|       break;
 | |
|     case ATT_LINEAR_TRANSFER_INTERCEPT_B:
 | |
|       mInterceptB = aValue;
 | |
|       break;
 | |
|     case ATT_LINEAR_TRANSFER_SLOPE_A:
 | |
|       mSlopeA = aValue;
 | |
|       break;
 | |
|     case ATT_LINEAR_TRANSFER_INTERCEPT_A:
 | |
|       mInterceptA = aValue;
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeLinearTransferSoftware::SetAttribute");
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
 | |
|                                                   uint8_t aTable[256])
 | |
| {
 | |
|   switch (aComponent) {
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
 | |
|       FillLookupTableImpl(mSlopeR, mInterceptR, aTable);
 | |
|       break;
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
 | |
|       FillLookupTableImpl(mSlopeG, mInterceptG, aTable);
 | |
|       break;
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
 | |
|       FillLookupTableImpl(mSlopeB, mInterceptB, aTable);
 | |
|       break;
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
 | |
|       FillLookupTableImpl(mSlopeA, mInterceptA, aTable);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_ASSERT(false, "unknown component");
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeLinearTransferSoftware::FillLookupTableImpl(Float aSlope,
 | |
|                                                       Float aIntercept,
 | |
|                                                       uint8_t aTable[256])
 | |
| {
 | |
|   for (size_t i = 0; i < 256; i++) {
 | |
|     int32_t val = NS_lround(aSlope * i + 255 * aIntercept);
 | |
|     val = std::min(255, val);
 | |
|     val = std::max(0, val);
 | |
|     aTable[i] = val;
 | |
|   }
 | |
| }
 | |
| 
 | |
| FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware()
 | |
|  : mAmplitudeR(0)
 | |
|  , mAmplitudeG(0)
 | |
|  , mAmplitudeB(0)
 | |
|  , mAmplitudeA(0)
 | |
|  , mExponentR(0)
 | |
|  , mExponentG(0)
 | |
|  , mExponentB(0)
 | |
|  , mExponentA(0)
 | |
|  , mOffsetR(0.0)
 | |
|  , mOffsetG(0.0)
 | |
|  , mOffsetB(0.0)
 | |
|  , mOffsetA(0.0)
 | |
| {}
 | |
| 
 | |
| void
 | |
| FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                               Float aValue)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_GAMMA_TRANSFER_AMPLITUDE_R:
 | |
|       mAmplitudeR = aValue;
 | |
|       break;
 | |
|     case ATT_GAMMA_TRANSFER_EXPONENT_R:
 | |
|       mExponentR = aValue;
 | |
|       break;
 | |
|     case ATT_GAMMA_TRANSFER_OFFSET_R:
 | |
|       mOffsetR = aValue;
 | |
|       break;
 | |
|     case ATT_GAMMA_TRANSFER_AMPLITUDE_G:
 | |
|       mAmplitudeG = aValue;
 | |
|       break;
 | |
|     case ATT_GAMMA_TRANSFER_EXPONENT_G:
 | |
|       mExponentG = aValue;
 | |
|       break;
 | |
|     case ATT_GAMMA_TRANSFER_OFFSET_G:
 | |
|       mOffsetG = aValue;
 | |
|       break;
 | |
|     case ATT_GAMMA_TRANSFER_AMPLITUDE_B:
 | |
|       mAmplitudeB = aValue;
 | |
|       break;
 | |
|     case ATT_GAMMA_TRANSFER_EXPONENT_B:
 | |
|       mExponentB = aValue;
 | |
|       break;
 | |
|     case ATT_GAMMA_TRANSFER_OFFSET_B:
 | |
|       mOffsetB = aValue;
 | |
|       break;
 | |
|     case ATT_GAMMA_TRANSFER_AMPLITUDE_A:
 | |
|       mAmplitudeA = aValue;
 | |
|       break;
 | |
|     case ATT_GAMMA_TRANSFER_EXPONENT_A:
 | |
|       mExponentA = aValue;
 | |
|       break;
 | |
|     case ATT_GAMMA_TRANSFER_OFFSET_A:
 | |
|       mOffsetA = aValue;
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeGammaTransferSoftware::SetAttribute");
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
 | |
|                                                  uint8_t aTable[256])
 | |
| {
 | |
|   switch (aComponent) {
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
 | |
|       FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable);
 | |
|       break;
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
 | |
|       FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable);
 | |
|       break;
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
 | |
|       FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable);
 | |
|       break;
 | |
|     case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
 | |
|       FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_ASSERT(false, "unknown component");
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude,
 | |
|                                                      Float aExponent,
 | |
|                                                      Float aOffset,
 | |
|                                                      uint8_t aTable[256])
 | |
| {
 | |
|   for (size_t i = 0; i < 256; i++) {
 | |
|     int32_t val = NS_lround(255 * (aAmplitude * pow(i / 255.0f, aExponent) + aOffset));
 | |
|     val = std::min(255, val);
 | |
|     val = std::max(0, val);
 | |
|     aTable[i] = val;
 | |
|   }
 | |
| }
 | |
| 
 | |
| FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware()
 | |
|  : mDivisor(0)
 | |
|  , mBias(0)
 | |
|  , mEdgeMode(EDGE_MODE_DUPLICATE)
 | |
|  , mPreserveAlpha(false)
 | |
| {}
 | |
| 
 | |
| int32_t
 | |
| FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   switch (aInputEnumIndex) {
 | |
|     case IN_CONVOLVE_MATRIX_IN: return 0;
 | |
|     default: return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                                const IntSize &aKernelSize)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_SIZE);
 | |
|   mKernelSize = aKernelSize;
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                                const Float *aMatrix,
 | |
|                                                uint32_t aSize)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_MATRIX);
 | |
|   mKernelMatrix = std::vector<Float>(aMatrix, aMatrix + aSize);
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, Float aValue)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_CONVOLVE_MATRIX_DIVISOR:
 | |
|       mDivisor = aValue;
 | |
|       break;
 | |
|     case ATT_CONVOLVE_MATRIX_BIAS:
 | |
|       mBias = aValue;
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH:
 | |
|       mKernelUnitLength = aKernelUnitLength;
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                                const IntPoint &aTarget)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_TARGET);
 | |
|   mTarget = aTarget;
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                                const IntRect &aSourceRect)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_SOURCE_RECT);
 | |
|   mSourceRect = aSourceRect;
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                                uint32_t aEdgeMode)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_EDGE_MODE);
 | |
|   mEdgeMode = static_cast<ConvolveMatrixEdgeMode>(aEdgeMode);
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                                bool aPreserveAlpha)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA);
 | |
|   mPreserveAlpha = aPreserveAlpha;
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| #ifdef DEBUG
 | |
| static inline void
 | |
| DebugOnlyCheckColorSamplingAccess(const uint8_t* aSampleAddress, const uint8_t* aBoundsBegin, const uint8_t* aBoundsEnd)
 | |
| {
 | |
|   MOZ_ASSERT(aSampleAddress >= aBoundsBegin, "accessing before start");
 | |
|   MOZ_ASSERT(aSampleAddress < aBoundsEnd, "accessing after end");
 | |
| }
 | |
| #else
 | |
| #define DebugOnlyCheckColorSamplingAccess(address, boundsBegin, boundsEnd)
 | |
| #endif
 | |
| 
 | |
| static inline uint8_t
 | |
| ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, const uint8_t *aBoundsBegin, const uint8_t *aBoundsEnd, int32_t x, int32_t y, size_t bpp, ptrdiff_t c)
 | |
| {
 | |
|   DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c], aBoundsBegin, aBoundsEnd);
 | |
|   return aData[y * aStride + bpp * x + c];
 | |
| }
 | |
| 
 | |
| static inline int32_t
 | |
| ColorAtPoint(const uint8_t *aData, int32_t aStride, const uint8_t *aBoundsBegin, const uint8_t *aBoundsEnd, int32_t x, int32_t y)
 | |
| {
 | |
|   DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x, aBoundsBegin, aBoundsEnd);
 | |
|   return *(uint32_t*)(aData + y * aStride + 4 * x);
 | |
| }
 | |
| 
 | |
| // Accepts fractional x & y and does bilinear interpolation.
 | |
| // Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible.
 | |
| static inline uint8_t
 | |
| ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, const uint8_t *aBoundsBegin, const uint8_t *aBoundsEnd, Float x, Float y, size_t bpp, ptrdiff_t c)
 | |
| {
 | |
|   const uint32_t f = 256;
 | |
|   const int32_t lx = floor(x);
 | |
|   const int32_t ly = floor(y);
 | |
|   const int32_t tux = uint32_t((x - lx) * f);
 | |
|   const int32_t tlx = f - tux;
 | |
|   const int32_t tuy = uint32_t((y - ly) * f);
 | |
|   const int32_t tly = f - tuy;
 | |
|   const uint8_t &cll = ColorComponentAtPoint(aData, aStride, aBoundsBegin, aBoundsEnd, lx,     ly,     bpp, c);
 | |
|   const uint8_t &cul = ColorComponentAtPoint(aData, aStride, aBoundsBegin, aBoundsEnd, lx + 1, ly,     bpp, c);
 | |
|   const uint8_t &clu = ColorComponentAtPoint(aData, aStride, aBoundsBegin, aBoundsEnd, lx,     ly + 1, bpp, c);
 | |
|   const uint8_t &cuu = ColorComponentAtPoint(aData, aStride, aBoundsBegin, aBoundsEnd, lx + 1, ly + 1, bpp, c);
 | |
|   return ((cll * tlx + cul * tux) * tly +
 | |
|           (clu * tlx + cuu * tux) * tuy + f * f / 2) / (f * f);
 | |
| }
 | |
| 
 | |
| static int32_t
 | |
| ClampToNonZero(int32_t a)
 | |
| {
 | |
|   return a * (a >= 0);
 | |
| }
 | |
| 
 | |
| template<typename CoordType>
 | |
| static void
 | |
| ConvolvePixel(const uint8_t *aSourceData,
 | |
|               uint8_t *aTargetData,
 | |
|               int32_t aWidth, int32_t aHeight,
 | |
|               int32_t aSourceStride, int32_t aTargetStride,
 | |
|               const uint8_t* aSourceBegin, const uint8_t* aSourceEnd,
 | |
|               int32_t aX, int32_t aY,
 | |
|               const int32_t *aKernel,
 | |
|               int32_t aBias, int32_t shiftL, int32_t shiftR,
 | |
|               bool aPreserveAlpha,
 | |
|               int32_t aOrderX, int32_t aOrderY,
 | |
|               int32_t aTargetX, int32_t aTargetY,
 | |
|               CoordType aKernelUnitLengthX,
 | |
|               CoordType aKernelUnitLengthY)
 | |
| {
 | |
|   int32_t sum[4] = {0, 0, 0, 0};
 | |
|   int32_t offsets[4] = { B8G8R8A8_COMPONENT_BYTEOFFSET_R,
 | |
|                          B8G8R8A8_COMPONENT_BYTEOFFSET_G,
 | |
|                          B8G8R8A8_COMPONENT_BYTEOFFSET_B,
 | |
|                          B8G8R8A8_COMPONENT_BYTEOFFSET_A };
 | |
|   int32_t channels = aPreserveAlpha ? 3 : 4;
 | |
|   int32_t roundingAddition = shiftL == 0 ? 0 : 1 << (shiftL - 1);
 | |
| 
 | |
|   for (int32_t y = 0; y < aOrderY; y++) {
 | |
|     CoordType sampleY = aY + (y - aTargetY) * aKernelUnitLengthY;
 | |
|     for (int32_t x = 0; x < aOrderX; x++) {
 | |
|       CoordType sampleX = aX + (x - aTargetX) * aKernelUnitLengthX;
 | |
|       for (int32_t i = 0; i < channels; i++) {
 | |
|         sum[i] += aKernel[aOrderX * y + x] *
 | |
|           ColorComponentAtPoint(aSourceData, aSourceStride, aSourceBegin, aSourceEnd,
 | |
|                                 sampleX, sampleY, 4, offsets[i]);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   for (int32_t i = 0; i < channels; i++) {
 | |
|     int32_t clamped = umin(ClampToNonZero(sum[i] + aBias), 255 << shiftL >> shiftR);
 | |
|     aTargetData[aY * aTargetStride + 4 * aX + offsets[i]] =
 | |
|       (clamped + roundingAddition) << shiftR >> shiftL;
 | |
|   }
 | |
|   if (aPreserveAlpha) {
 | |
|     aTargetData[aY * aTargetStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
 | |
|       aSourceData[aY * aSourceStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
 | |
|   }
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeConvolveMatrixSoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
 | |
|       mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
 | |
|     return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height);
 | |
|   }
 | |
|   return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
 | |
| }
 | |
| 
 | |
| static std::vector<Float>
 | |
| ReversedVector(const std::vector<Float> &aVector)
 | |
| {
 | |
|   size_t length = aVector.size();
 | |
|   std::vector<Float> result(length, 0);
 | |
|   for (size_t i = 0; i < length; i++) {
 | |
|     result[length - 1 - i] = aVector[i];
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| static std::vector<Float>
 | |
| ScaledVector(const std::vector<Float> &aVector, Float aDivisor)
 | |
| {
 | |
|   size_t length = aVector.size();
 | |
|   std::vector<Float> result(length, 0);
 | |
|   for (size_t i = 0; i < length; i++) {
 | |
|     result[i] = aVector[i] / aDivisor;
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| static Float
 | |
| MaxVectorSum(const std::vector<Float> &aVector)
 | |
| {
 | |
|   Float sum = 0;
 | |
|   size_t length = aVector.size();
 | |
|   for (size_t i = 0; i < length; i++) {
 | |
|     if (aVector[i] > 0) {
 | |
|       sum += aVector[i];
 | |
|     }
 | |
|   }
 | |
|   return sum;
 | |
| }
 | |
| 
 | |
| // Returns shiftL and shiftR in such a way that
 | |
| // a << shiftL >> shiftR is roughly a * aFloat.
 | |
| static void
 | |
| TranslateDoubleToShifts(double aDouble, int32_t &aShiftL, int32_t &aShiftR)
 | |
| {
 | |
|   aShiftL = 0;
 | |
|   aShiftR = 0;
 | |
|   if (aDouble <= 0) {
 | |
|     MOZ_CRASH("GFX: TranslateDoubleToShifts");
 | |
|   }
 | |
|   if (aDouble < 1) {
 | |
|     while (1 << (aShiftR + 1) < 1 / aDouble) {
 | |
|       aShiftR++;
 | |
|     }
 | |
|   } else {
 | |
|     while (1 << (aShiftL + 1) < aDouble) {
 | |
|       aShiftL++;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| template<typename CoordType>
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeConvolveMatrixSoftware::DoRender(const IntRect& aRect,
 | |
|                                            CoordType aKernelUnitLengthX,
 | |
|                                            CoordType aKernelUnitLengthY)
 | |
| {
 | |
|   if (mKernelSize.width <= 0 || mKernelSize.height <= 0 ||
 | |
|       mKernelMatrix.size() != uint32_t(mKernelSize.width * mKernelSize.height) ||
 | |
|       !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) ||
 | |
|       mDivisor == 0) {
 | |
|     return Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true);
 | |
|   }
 | |
| 
 | |
|   IntRect srcRect = InflatedSourceRect(aRect);
 | |
| 
 | |
|   // Inflate the source rect by another pixel because the bilinear filtering in
 | |
|   // ColorComponentAtPoint may want to access the margins.
 | |
|   srcRect.Inflate(1);
 | |
| 
 | |
|   RefPtr<DataSourceSurface> input =
 | |
|     GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect, NEED_COLOR_CHANNELS, mEdgeMode, &mSourceRect);
 | |
| 
 | |
|   if (!input) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   RefPtr<DataSourceSurface> target =
 | |
|     Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true);
 | |
|   if (MOZ2D_WARN_IF(!target)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
 | |
| 
 | |
|   DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
 | |
|   DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
 | |
|   if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   uint8_t* sourceData = DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
 | |
|   int32_t sourceStride = sourceMap.GetStride();
 | |
|   uint8_t* sourceBegin = sourceMap.GetData();
 | |
|   uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
 | |
|   uint8_t* targetData = targetMap.GetData();
 | |
|   int32_t targetStride = targetMap.GetStride();
 | |
| 
 | |
|   // Why exactly are we reversing the kernel?
 | |
|   std::vector<Float> kernel = ReversedVector(mKernelMatrix);
 | |
|   kernel = ScaledVector(kernel, mDivisor);
 | |
|   Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias,
 | |
|                                 MaxVectorSum(ScaledVector(kernel, -1)) - mBias);
 | |
|   maxResultAbs = std::max(maxResultAbs, 1.0f);
 | |
| 
 | |
|   double idealFactor = INT32_MAX / 2.0 / maxResultAbs / 255.0 * 0.999;
 | |
|   MOZ_ASSERT(255.0 * maxResultAbs * idealFactor <= INT32_MAX / 2.0, "badly chosen float-to-int scale");
 | |
|   int32_t shiftL, shiftR;
 | |
|   TranslateDoubleToShifts(idealFactor, shiftL, shiftR);
 | |
|   double factorFromShifts = Float(1 << shiftL) / Float(1 << shiftR);
 | |
|   MOZ_ASSERT(255.0 * maxResultAbs * factorFromShifts <= INT32_MAX / 2.0, "badly chosen float-to-int scale");
 | |
| 
 | |
|   int32_t* intKernel = new int32_t[kernel.size()];
 | |
|   for (size_t i = 0; i < kernel.size(); i++) {
 | |
|     intKernel[i] = NS_lround(kernel[i] * factorFromShifts);
 | |
|   }
 | |
|   int32_t bias = NS_lround(mBias * 255 * factorFromShifts);
 | |
| 
 | |
|   for (int32_t y = 0; y < aRect.Height(); y++) {
 | |
|     for (int32_t x = 0; x < aRect.Width(); x++) {
 | |
|       ConvolvePixel(sourceData, targetData,
 | |
|                     aRect.Width(), aRect.Height(), sourceStride, targetStride, sourceBegin, sourceEnd,
 | |
|                     x, y, intKernel, bias, shiftL, shiftR, mPreserveAlpha,
 | |
|                     mKernelSize.width, mKernelSize.height, mTarget.x, mTarget.y,
 | |
|                     aKernelUnitLengthX, aKernelUnitLengthY);
 | |
|     }
 | |
|   }
 | |
|   delete[] intKernel;
 | |
| 
 | |
|   return target.forget();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect));
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeConvolveMatrixSoftware::InflatedSourceRect(const IntRect &aDestRect)
 | |
| {
 | |
|   if (aDestRect.IsEmpty()) {
 | |
|     return IntRect();
 | |
|   }
 | |
| 
 | |
|   IntMargin margin;
 | |
|   margin.left = ceil(mTarget.x * mKernelUnitLength.width);
 | |
|   margin.top = ceil(mTarget.y * mKernelUnitLength.height);
 | |
|   margin.right = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
 | |
|   margin.bottom = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
 | |
| 
 | |
|   IntRect srcRect = aDestRect;
 | |
|   srcRect.Inflate(margin);
 | |
|   return srcRect;
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeConvolveMatrixSoftware::InflatedDestRect(const IntRect &aSourceRect)
 | |
| {
 | |
|   if (aSourceRect.IsEmpty()) {
 | |
|     return IntRect();
 | |
|   }
 | |
| 
 | |
|   IntMargin margin;
 | |
|   margin.left = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
 | |
|   margin.top = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
 | |
|   margin.right = ceil(mTarget.x * mKernelUnitLength.width);
 | |
|   margin.bottom = ceil(mTarget.y * mKernelUnitLength.height);
 | |
| 
 | |
|   IntRect destRect = aSourceRect;
 | |
|   destRect.Inflate(margin);
 | |
|   return destRect;
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   IntRect srcRequest = InflatedSourceRect(aRect);
 | |
|   IntRect srcOutput = GetInputRectInRect(IN_COLOR_MATRIX_IN, srcRequest);
 | |
|   return InflatedDestRect(srcOutput).Intersect(aRect);
 | |
| }
 | |
| 
 | |
| FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware()
 | |
|  : mScale(0.0f)
 | |
|  , mChannelX(COLOR_CHANNEL_R)
 | |
|  , mChannelY(COLOR_CHANNEL_G)
 | |
| {}
 | |
| 
 | |
| int32_t
 | |
| FilterNodeDisplacementMapSoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   switch (aInputEnumIndex) {
 | |
|     case IN_DISPLACEMENT_MAP_IN: return 0;
 | |
|     case IN_DISPLACEMENT_MAP_IN2: return 1;
 | |
|     default: return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                                 Float aScale)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_DISPLACEMENT_MAP_SCALE);
 | |
|   mScale = aScale;
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_DISPLACEMENT_MAP_X_CHANNEL:
 | |
|       mChannelX = static_cast<ColorChannel>(aValue);
 | |
|       break;
 | |
|     case ATT_DISPLACEMENT_MAP_Y_CHANNEL:
 | |
|       mChannelY = static_cast<ColorChannel>(aValue);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeDisplacementMapSoftware::SetAttribute");
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeDisplacementMapSoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   IntRect srcRect = InflatedSourceOrDestRect(aRect);
 | |
|   RefPtr<DataSourceSurface> input =
 | |
|     GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN, srcRect, NEED_COLOR_CHANNELS);
 | |
|   RefPtr<DataSourceSurface> map =
 | |
|     GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN2, aRect, NEED_COLOR_CHANNELS);
 | |
|   RefPtr<DataSourceSurface> target =
 | |
|     Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
 | |
|   if (MOZ2D_WARN_IF(!(input && map && target))) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
 | |
| 
 | |
|   DataSourceSurface::ScopedMap inputMap(input, DataSourceSurface::READ);
 | |
|   DataSourceSurface::ScopedMap mapMap(map, DataSourceSurface::READ);
 | |
|   DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
 | |
|   if (MOZ2D_WARN_IF(!(inputMap.IsMapped() && mapMap.IsMapped() && targetMap.IsMapped()))) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   uint8_t* sourceData = DataAtOffset(input, inputMap.GetMappedSurface(), offset);
 | |
|   int32_t sourceStride = inputMap.GetStride();
 | |
|   uint8_t* sourceBegin = inputMap.GetData();
 | |
|   uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
 | |
|   uint8_t* mapData = mapMap.GetData();
 | |
|   int32_t mapStride = mapMap.GetStride();
 | |
|   uint8_t* targetData = targetMap.GetData();
 | |
|   int32_t targetStride = targetMap.GetStride();
 | |
| 
 | |
|   static const ptrdiff_t channelMap[4] = {
 | |
|                              B8G8R8A8_COMPONENT_BYTEOFFSET_R,
 | |
|                              B8G8R8A8_COMPONENT_BYTEOFFSET_G,
 | |
|                              B8G8R8A8_COMPONENT_BYTEOFFSET_B,
 | |
|                              B8G8R8A8_COMPONENT_BYTEOFFSET_A };
 | |
|   uint16_t xChannel = channelMap[mChannelX];
 | |
|   uint16_t yChannel = channelMap[mChannelY];
 | |
| 
 | |
|   float scaleOver255 = mScale / 255.0f;
 | |
|   float scaleAdjustment = -0.5f * mScale;
 | |
| 
 | |
|   for (int32_t y = 0; y < aRect.Height(); y++) {
 | |
|     for (int32_t x = 0; x < aRect.Width(); x++) {
 | |
|       uint32_t mapIndex = y * mapStride + 4 * x;
 | |
|       uint32_t targIndex = y * targetStride + 4 * x;
 | |
|       int32_t sourceX = x +
 | |
|         scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment;
 | |
|       int32_t sourceY = y +
 | |
|         scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment;
 | |
|       *(uint32_t*)(targetData + targIndex) =
 | |
|         ColorAtPoint(sourceData, sourceStride, sourceBegin, sourceEnd, sourceX, sourceY);
 | |
|     }
 | |
| 
 | |
|     // Keep valgrind happy.
 | |
|     PodZero(&targetData[y * targetStride + 4 * aRect.Width()], targetStride - 4 * aRect.Width());
 | |
|   }
 | |
| 
 | |
|   return target.forget();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect));
 | |
|   RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect);
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect)
 | |
| {
 | |
|   IntRect sourceOrDestRect = aDestOrSourceRect;
 | |
|   sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2));
 | |
|   return sourceOrDestRect;
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeDisplacementMapSoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   IntRect srcRequest = InflatedSourceOrDestRect(aRect);
 | |
|   IntRect srcOutput = GetInputRectInRect(IN_DISPLACEMENT_MAP_IN, srcRequest);
 | |
|   return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
 | |
| }
 | |
| 
 | |
| FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware()
 | |
|  : mNumOctaves(0)
 | |
|  , mSeed(0)
 | |
|  , mStitchable(false)
 | |
|  , mType(TURBULENCE_TYPE_TURBULENCE)
 | |
| {}
 | |
| 
 | |
| int32_t
 | |
| FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const Size &aBaseFrequency)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_TURBULENCE_BASE_FREQUENCY:
 | |
|       mBaseFrequency = aBaseFrequency;
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
 | |
|       break;
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const IntRect &aRect)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_TURBULENCE_RECT:
 | |
|       mRenderRect = aRect;
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
 | |
|       break;
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, bool aStitchable)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_TURBULENCE_STITCHABLE);
 | |
|   mStitchable = aStitchable;
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_TURBULENCE_NUM_OCTAVES:
 | |
|       mNumOctaves = aValue;
 | |
|       break;
 | |
|     case ATT_TURBULENCE_SEED:
 | |
|       mSeed = aValue;
 | |
|       break;
 | |
|     case ATT_TURBULENCE_TYPE:
 | |
|       mType = static_cast<TurbulenceType>(aValue);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
 | |
|       break;
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeTurbulenceSoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   return FilterProcessing::RenderTurbulence(
 | |
|     aRect.Size(), aRect.TopLeft(), mBaseFrequency,
 | |
|     mSeed, mNumOctaves, mType, mStitchable, Rect(mRenderRect));
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeTurbulenceSoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   return aRect.Intersect(mRenderRect);
 | |
| }
 | |
| 
 | |
| FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware()
 | |
|  : mK1(0), mK2(0), mK3(0), mK4(0)
 | |
| {
 | |
| }
 | |
| 
 | |
| int32_t
 | |
| FilterNodeArithmeticCombineSoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   switch (aInputEnumIndex) {
 | |
|     case IN_ARITHMETIC_COMBINE_IN: return 0;
 | |
|     case IN_ARITHMETIC_COMBINE_IN2: return 1;
 | |
|     default: return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                                   const Float* aFloat,
 | |
|                                                   uint32_t aSize)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS);
 | |
|   MOZ_RELEASE_ASSERT(aSize == 4);
 | |
| 
 | |
|   mK1 = aFloat[0];
 | |
|   mK2 = aFloat[1];
 | |
|   mK3 = aFloat[2];
 | |
|   mK4 = aFloat[3];
 | |
| 
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeArithmeticCombineSoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   RefPtr<DataSourceSurface> input1 =
 | |
|     GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS);
 | |
|   RefPtr<DataSourceSurface> input2 =
 | |
|     GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN2, aRect, NEED_COLOR_CHANNELS);
 | |
|   if (!input1 && !input2) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // If one input is null, treat it as transparent by adjusting the factors.
 | |
|   Float k1 = mK1, k2 = mK2, k3 = mK3, k4 = mK4;
 | |
|   if (!input1) {
 | |
|     k1 = 0.0f;
 | |
|     k2 = 0.0f;
 | |
|     input1 = input2;
 | |
|   }
 | |
| 
 | |
|   if (!input2) {
 | |
|     k1 = 0.0f;
 | |
|     k3 = 0.0f;
 | |
|     input2 = input1;
 | |
|   }
 | |
| 
 | |
|   return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3, k4);
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect);
 | |
|   RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect);
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   if (mK4 > 0.0f) {
 | |
|     return aRect;
 | |
|   }
 | |
|   IntRect rectFrom1 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect);
 | |
|   IntRect rectFrom2 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2, aRect).Intersect(aRect);
 | |
|   IntRect result;
 | |
|   if (mK1 > 0.0f) {
 | |
|     result = rectFrom1.Intersect(rectFrom2);
 | |
|   }
 | |
|   if (mK2 > 0.0f) {
 | |
|     result = result.Union(rectFrom1);
 | |
|   }
 | |
|   if (mK3 > 0.0f) {
 | |
|     result = result.Union(rectFrom2);
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| FilterNodeCompositeSoftware::FilterNodeCompositeSoftware()
 | |
|  : mOperator(COMPOSITE_OPERATOR_OVER)
 | |
| {}
 | |
| 
 | |
| int32_t
 | |
| FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   return aInputEnumIndex - IN_COMPOSITE_IN_START;
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex, uint32_t aCompositeOperator)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_COMPOSITE_OPERATOR);
 | |
|   mOperator = static_cast<CompositeOperator>(aCompositeOperator);
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeCompositeSoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   RefPtr<DataSourceSurface> start =
 | |
|     GetInputDataSourceSurface(IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS);
 | |
|   RefPtr<DataSourceSurface> dest =
 | |
|     Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true);
 | |
|   if (MOZ2D_WARN_IF(!dest)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (start) {
 | |
|     CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint());
 | |
|   }
 | |
| 
 | |
|   for (size_t inputIndex = 1; inputIndex < NumberOfSetInputs(); inputIndex++) {
 | |
|     RefPtr<DataSourceSurface> input =
 | |
|       GetInputDataSourceSurface(IN_COMPOSITE_IN_START + inputIndex, aRect, NEED_COLOR_CHANNELS);
 | |
|     if (input) {
 | |
|       FilterProcessing::ApplyComposition(input, dest, mOperator);
 | |
|     } else {
 | |
|       // We need to treat input as transparent. Depending on the composite
 | |
|       // operator, different things happen to dest.
 | |
|       switch (mOperator) {
 | |
|         case COMPOSITE_OPERATOR_OVER:
 | |
|         case COMPOSITE_OPERATOR_ATOP:
 | |
|         case COMPOSITE_OPERATOR_XOR:
 | |
|           // dest is unchanged.
 | |
|           break;
 | |
|         case COMPOSITE_OPERATOR_OUT:
 | |
|           // dest is now transparent, but it can become non-transparent again
 | |
|           // when compositing additional inputs.
 | |
|           ClearDataSourceSurface(dest);
 | |
|           break;
 | |
|         case COMPOSITE_OPERATOR_IN:
 | |
|           // Transparency always wins. We're completely transparent now and
 | |
|           // no additional input can get rid of that transparency.
 | |
|           return nullptr;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return dest.forget();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeCompositeSoftware::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
 | |
|     RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
 | |
|   }
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   IntRect rect;
 | |
|   for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
 | |
|     IntRect inputRect = GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
 | |
|     if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) {
 | |
|       rect = rect.Intersect(inputRect);
 | |
|     } else {
 | |
|       rect = rect.Union(inputRect);
 | |
|     }
 | |
|   }
 | |
|   return rect;
 | |
| }
 | |
| 
 | |
| int32_t
 | |
| FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   switch (aInputEnumIndex) {
 | |
|     case IN_GAUSSIAN_BLUR_IN: return 0;
 | |
|     default: return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeBlurXYSoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   Size sigmaXY = StdDeviationXY();
 | |
|   IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
 | |
| 
 | |
|   if (d.width == 0 && d.height == 0) {
 | |
|     return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, aRect);
 | |
|   }
 | |
| 
 | |
|   IntRect srcRect = InflatedSourceOrDestRect(aRect);
 | |
|   RefPtr<DataSourceSurface> input =
 | |
|     GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, srcRect);
 | |
|   if (!input) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   RefPtr<DataSourceSurface> target;
 | |
|   Rect r(0, 0, srcRect.Width(), srcRect.Height());
 | |
| 
 | |
|   if (input->GetFormat() == SurfaceFormat::A8) {
 | |
|     target = Factory::CreateDataSourceSurface(srcRect.Size(), SurfaceFormat::A8);
 | |
|     if (MOZ2D_WARN_IF(!target)) {
 | |
|       return nullptr;
 | |
|     }
 | |
|     CopyRect(input, target, IntRect(IntPoint(), input->GetSize()), IntPoint());
 | |
| 
 | |
|     DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::READ_WRITE);
 | |
|     if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
 | |
|       return nullptr;
 | |
|     }
 | |
|     AlphaBoxBlur blur(r, targetMap.GetStride(), sigmaXY.width, sigmaXY.height);
 | |
|     blur.Blur(targetMap.GetData());
 | |
|   } else {
 | |
|     RefPtr<DataSourceSurface> channel0, channel1, channel2, channel3;
 | |
|     FilterProcessing::SeparateColorChannels(input, channel0, channel1, channel2, channel3);
 | |
|     if (MOZ2D_WARN_IF(!(channel0 && channel1 && channel2 && channel3))) {
 | |
|       return nullptr;
 | |
|     }
 | |
|     {
 | |
|       DataSourceSurface::ScopedMap channel0Map(channel0, DataSourceSurface::READ_WRITE);
 | |
|       DataSourceSurface::ScopedMap channel1Map(channel1, DataSourceSurface::READ_WRITE);
 | |
|       DataSourceSurface::ScopedMap channel2Map(channel2, DataSourceSurface::READ_WRITE);
 | |
|       DataSourceSurface::ScopedMap channel3Map(channel3, DataSourceSurface::READ_WRITE);
 | |
|       if (MOZ2D_WARN_IF(!(channel0Map.IsMapped() && channel1Map.IsMapped() &&
 | |
|                           channel2Map.IsMapped() && channel3Map.IsMapped()))) {
 | |
|         return nullptr;
 | |
|       }
 | |
| 
 | |
|       AlphaBoxBlur blur(r, channel0Map.GetStride(), sigmaXY.width, sigmaXY.height);
 | |
|       blur.Blur(channel0Map.GetData());
 | |
|       blur.Blur(channel1Map.GetData());
 | |
|       blur.Blur(channel2Map.GetData());
 | |
|       blur.Blur(channel3Map.GetData());
 | |
|     }
 | |
|     target = FilterProcessing::CombineColorChannels(channel0, channel1, channel2, channel3);
 | |
|   }
 | |
| 
 | |
|   return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE);
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect));
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(const IntRect &aDestRect)
 | |
| {
 | |
|   Size sigmaXY = StdDeviationXY();
 | |
|   IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
 | |
|   IntRect srcRect = aDestRect;
 | |
|   srcRect.Inflate(d);
 | |
|   return srcRect;
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   IntRect srcRequest = InflatedSourceOrDestRect(aRect);
 | |
|   IntRect srcOutput = GetInputRectInRect(IN_GAUSSIAN_BLUR_IN, srcRequest);
 | |
|   return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
 | |
| }
 | |
| 
 | |
| FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware()
 | |
|  : mStdDeviation(0)
 | |
| {}
 | |
| 
 | |
| static float
 | |
| ClampStdDeviation(float aStdDeviation)
 | |
| {
 | |
|   // Cap software blur radius for performance reasons.
 | |
|   return std::min(std::max(0.0f, aStdDeviation), 100.0f);
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                              float aStdDeviation)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_GAUSSIAN_BLUR_STD_DEVIATION:
 | |
|       mStdDeviation = ClampStdDeviation(aStdDeviation);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeGaussianBlurSoftware::SetAttribute");
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| Size
 | |
| FilterNodeGaussianBlurSoftware::StdDeviationXY()
 | |
| {
 | |
|   return Size(mStdDeviation, mStdDeviation);
 | |
| }
 | |
| 
 | |
| FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware()
 | |
|   : mStdDeviation(0.0)
 | |
|   , mBlurDirection(BLUR_DIRECTION_X)
 | |
| {}
 | |
| 
 | |
| void
 | |
| FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                                 Float aStdDeviation)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_DIRECTIONAL_BLUR_STD_DEVIATION:
 | |
|       mStdDeviation = ClampStdDeviation(aStdDeviation);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                                 uint32_t aBlurDirection)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_DIRECTIONAL_BLUR_DIRECTION:
 | |
|       mBlurDirection = (BlurDirection)aBlurDirection;
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| Size
 | |
| FilterNodeDirectionalBlurSoftware::StdDeviationXY()
 | |
| {
 | |
|   float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0;
 | |
|   float sigmaY = mBlurDirection == BLUR_DIRECTION_Y ? mStdDeviation : 0;
 | |
|   return Size(sigmaX, sigmaY);
 | |
| }
 | |
| 
 | |
| int32_t
 | |
| FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   switch (aInputEnumIndex) {
 | |
|     case IN_CROP_IN: return 0;
 | |
|     default: return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeCropSoftware::SetAttribute(uint32_t aIndex,
 | |
|                                      const Rect &aSourceRect)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_CROP_RECT);
 | |
|   Rect srcRect = aSourceRect;
 | |
|   srcRect.Round();
 | |
|   if (!srcRect.ToIntRect(&mCropRect)) {
 | |
|     mCropRect = IntRect();
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeCropSoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect));
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect));
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeCropSoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect);
 | |
| }
 | |
| 
 | |
| int32_t
 | |
| FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   switch (aInputEnumIndex) {
 | |
|     case IN_PREMULTIPLY_IN: return 0;
 | |
|     default: return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodePremultiplySoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   RefPtr<DataSourceSurface> input =
 | |
|     GetInputDataSourceSurface(IN_PREMULTIPLY_IN, aRect);
 | |
|   return input ? Premultiply(input) : nullptr;
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodePremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   RequestInputRect(IN_PREMULTIPLY_IN, aRect);
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodePremultiplySoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect);
 | |
| }
 | |
| 
 | |
| int32_t
 | |
| FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   switch (aInputEnumIndex) {
 | |
|     case IN_UNPREMULTIPLY_IN: return 0;
 | |
|     default: return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeUnpremultiplySoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   RefPtr<DataSourceSurface> input =
 | |
|     GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN, aRect);
 | |
|   return input ? Unpremultiply(input) : nullptr;
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   RequestInputRect(IN_UNPREMULTIPLY_IN, aRect);
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeUnpremultiplySoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect);
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeOpacitySoftware::SetAttribute(uint32_t aIndex,
 | |
|                                         Float aValue)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_OPACITY_VALUE);
 | |
|   mValue = aValue;
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| int32_t
 | |
| FilterNodeOpacitySoftware::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   switch (aInputEnumIndex) {
 | |
|   case IN_OPACITY_IN: return 0;
 | |
|   default: return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeOpacitySoftware::Render(const IntRect& aRect)
 | |
| {
 | |
|   RefPtr<DataSourceSurface> input =
 | |
|     GetInputDataSourceSurface(IN_OPACITY_IN, aRect);
 | |
|   return input ? Opacity(input, mValue) : nullptr;
 | |
| }
 | |
| 
 | |
| void
 | |
| FilterNodeOpacitySoftware::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   RequestInputRect(IN_OPACITY_IN, aRect);
 | |
| }
 | |
| 
 | |
| IntRect
 | |
| FilterNodeOpacitySoftware::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   return GetInputRectInRect(IN_OPACITY_IN, aRect);
 | |
| }
 | |
| 
 | |
| bool
 | |
| PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_POINT_LIGHT_POSITION:
 | |
|       mPosition = aPoint;
 | |
|       break;
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| SpotLightSoftware::SpotLightSoftware()
 | |
|  : mSpecularFocus(0)
 | |
|  , mLimitingConeAngle(0)
 | |
|  , mLimitingConeCos(1)
 | |
| {
 | |
| }
 | |
| 
 | |
| bool
 | |
| SpotLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_SPOT_LIGHT_POSITION:
 | |
|       mPosition = aPoint;
 | |
|       break;
 | |
|     case ATT_SPOT_LIGHT_POINTS_AT:
 | |
|       mPointsAt = aPoint;
 | |
|       break;
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool
 | |
| SpotLightSoftware::SetAttribute(uint32_t aIndex, Float aValue)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE:
 | |
|       mLimitingConeAngle = aValue;
 | |
|       break;
 | |
|     case ATT_SPOT_LIGHT_FOCUS:
 | |
|       mSpecularFocus = aValue;
 | |
|       break;
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| DistantLightSoftware::DistantLightSoftware()
 | |
|  : mAzimuth(0)
 | |
|  , mElevation(0)
 | |
| {
 | |
| }
 | |
| 
 | |
| bool
 | |
| DistantLightSoftware::SetAttribute(uint32_t aIndex, Float aValue)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_DISTANT_LIGHT_AZIMUTH:
 | |
|       mAzimuth = aValue;
 | |
|       break;
 | |
|     case ATT_DISTANT_LIGHT_ELEVATION:
 | |
|       mElevation = aValue;
 | |
|       break;
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| static inline Point3D Normalized(const Point3D &vec) {
 | |
|   Point3D copy(vec);
 | |
|   copy.Normalize();
 | |
|   return copy;
 | |
| }
 | |
| 
 | |
| template<typename LightType, typename LightingType>
 | |
| FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware(const char* aTypeName)
 | |
|  : mLock("FilterNodeLightingSoftware")
 | |
|  , mSurfaceScale(0)
 | |
| #if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
 | |
|  , mTypeName(aTypeName)
 | |
| #endif
 | |
| {}
 | |
| 
 | |
| template<typename LightType, typename LightingType>
 | |
| int32_t
 | |
| FilterNodeLightingSoftware<LightType, LightingType>::InputIndex(uint32_t aInputEnumIndex)
 | |
| {
 | |
|   switch (aInputEnumIndex) {
 | |
|     case IN_LIGHTING_IN: return 0;
 | |
|     default: return -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| template<typename LightType, typename LightingType>
 | |
| void
 | |
| FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
 | |
| {
 | |
|   if (mLight.SetAttribute(aIndex, aPoint)) {
 | |
|     Invalidate();
 | |
|     return;
 | |
|   }
 | |
|   MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute point");
 | |
| }
 | |
| 
 | |
| template<typename LightType, typename LightingType>
 | |
| void
 | |
| FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, Float aValue)
 | |
| {
 | |
|   if (mLight.SetAttribute(aIndex, aValue) ||
 | |
|       mLighting.SetAttribute(aIndex, aValue)) {
 | |
|     Invalidate();
 | |
|     return;
 | |
|   }
 | |
|   switch (aIndex) {
 | |
|     case ATT_LIGHTING_SURFACE_SCALE:
 | |
|       mSurfaceScale = std::fpclassify(aValue) == FP_SUBNORMAL ? 0.0 : aValue;
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float");
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| template<typename LightType, typename LightingType>
 | |
| void
 | |
| FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_LIGHTING_KERNEL_UNIT_LENGTH:
 | |
|       mKernelUnitLength = aKernelUnitLength;
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size");
 | |
|   }
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| template<typename LightType, typename LightingType>
 | |
| void
 | |
| FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Color &aColor)
 | |
| {
 | |
|   MOZ_ASSERT(aIndex == ATT_LIGHTING_COLOR);
 | |
|   mColor = aColor;
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| template<typename LightType, typename LightingType>
 | |
| IntRect
 | |
| FilterNodeLightingSoftware<LightType, LightingType>::GetOutputRectInRect(const IntRect& aRect)
 | |
| {
 | |
|   return aRect;
 | |
| }
 | |
| 
 | |
| Point3D
 | |
| PointLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
 | |
| {
 | |
|   return Normalized(mPosition - aTargetPoint);
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| PointLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
 | |
| {
 | |
|   return aLightColor;
 | |
| }
 | |
| 
 | |
| void
 | |
| SpotLightSoftware::Prepare()
 | |
| {
 | |
|   mVectorFromFocusPointToLight = Normalized(mPointsAt - mPosition);
 | |
|   mLimitingConeCos = std::max<double>(cos(mLimitingConeAngle * M_PI/180.0), 0.0);
 | |
|   mPowCache.CacheForExponent(mSpecularFocus);
 | |
| }
 | |
| 
 | |
| Point3D
 | |
| SpotLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
 | |
| {
 | |
|   return Normalized(mPosition - aTargetPoint);
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| SpotLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
 | |
| {
 | |
|   union {
 | |
|     uint32_t color;
 | |
|     uint8_t colorC[4];
 | |
|   };
 | |
| 
 | |
|   Float dot = -aVectorToLight.DotProduct(mVectorFromFocusPointToLight);
 | |
|   if (!mPowCache.HasPowerTable()) {
 | |
|     dot *= (dot >= mLimitingConeCos);
 | |
|     color = aLightColor;
 | |
|     colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] *= dot;
 | |
|     colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] *= dot;
 | |
|     colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] *= dot;
 | |
|   } else {
 | |
|     color = aLightColor;
 | |
|     uint16_t doti = dot * (dot >= 0) * (1 << PowCache::sInputIntPrecisionBits);
 | |
|     uint32_t tmp = mPowCache.Pow(doti) * (dot >= mLimitingConeCos);
 | |
|     MOZ_ASSERT(tmp <= (1 << PowCache::sOutputIntPrecisionBits), "pow() result must not exceed 1.0");
 | |
|     colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] * tmp) >> PowCache::sOutputIntPrecisionBits);
 | |
|     colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] * tmp) >> PowCache::sOutputIntPrecisionBits);
 | |
|     colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] * tmp) >> PowCache::sOutputIntPrecisionBits);
 | |
|   }
 | |
|   colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
 | |
|   return color;
 | |
| }
 | |
| 
 | |
| void
 | |
| DistantLightSoftware::Prepare()
 | |
| {
 | |
|   const double radPerDeg = M_PI / 180.0;
 | |
|   mVectorToLight.x = cos(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
 | |
|   mVectorToLight.y = sin(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
 | |
|   mVectorToLight.z = sin(mElevation * radPerDeg);
 | |
| }
 | |
| 
 | |
| Point3D
 | |
| DistantLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
 | |
| {
 | |
|   return mVectorToLight;
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| DistantLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
 | |
| {
 | |
|   return aLightColor;
 | |
| }
 | |
| 
 | |
| template<typename CoordType>
 | |
| static Point3D
 | |
| GenerateNormal(const uint8_t *data, int32_t stride, uint8_t* boundsBegin, uint8_t* boundsEnd,
 | |
|                int32_t x, int32_t y, float surfaceScale,
 | |
|                CoordType dx, CoordType dy)
 | |
| {
 | |
|   const uint8_t *index = data + y * stride + x;
 | |
| 
 | |
|   CoordType zero = 0;
 | |
| 
 | |
|   // See this for source of constants:
 | |
|   //   http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
 | |
|   int16_t normalX =
 | |
|     -1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, -dx, -dy, 1, 0) +
 | |
|      1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, dx, -dy, 1, 0) +
 | |
|     -2 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, -dx, zero, 1, 0) +
 | |
|      2 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, dx, zero, 1, 0) +
 | |
|     -1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, -dx, dy, 1, 0) +
 | |
|      1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, dx, dy, 1, 0);
 | |
| 
 | |
|   int16_t normalY =
 | |
|     -1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, -dx, -dy, 1, 0) +
 | |
|     -2 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, zero, -dy, 1, 0) +
 | |
|     -1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, dx, -dy, 1, 0) +
 | |
|      1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, -dx, dy, 1, 0) +
 | |
|      2 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, zero, dy, 1, 0) +
 | |
|      1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, dx, dy, 1, 0);
 | |
| 
 | |
|   Point3D normal;
 | |
|   normal.x = -surfaceScale * normalX / 4.0f;
 | |
|   normal.y = -surfaceScale * normalY / 4.0f;
 | |
|   normal.z = 255;
 | |
|   return Normalized(normal);
 | |
| }
 | |
| 
 | |
| template<typename LightType, typename LightingType>
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeLightingSoftware<LightType, LightingType>::Render(const IntRect& aRect)
 | |
| {
 | |
|   if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
 | |
|       mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
 | |
|     return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height);
 | |
|   }
 | |
|   return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
 | |
| }
 | |
| 
 | |
| template<typename LightType, typename LightingType>
 | |
| void
 | |
| FilterNodeLightingSoftware<LightType, LightingType>::RequestFromInputsForRect(const IntRect &aRect)
 | |
| {
 | |
|   IntRect srcRect = aRect;
 | |
|   srcRect.Inflate(ceil(mKernelUnitLength.width),
 | |
|                   ceil(mKernelUnitLength.height));
 | |
|   RequestInputRect(IN_LIGHTING_IN, srcRect);
 | |
| }
 | |
| 
 | |
| template<typename LightType, typename LightingType> template<typename CoordType>
 | |
| already_AddRefed<DataSourceSurface>
 | |
| FilterNodeLightingSoftware<LightType, LightingType>::DoRender(const IntRect& aRect,
 | |
|                                                               CoordType aKernelUnitLengthX,
 | |
|                                                               CoordType aKernelUnitLengthY)
 | |
| {
 | |
|   MOZ_ASSERT(aKernelUnitLengthX > 0, "aKernelUnitLengthX can be a negative or zero value");
 | |
|   MOZ_ASSERT(aKernelUnitLengthY > 0, "aKernelUnitLengthY can be a negative or zero value");
 | |
| 
 | |
|   IntRect srcRect = aRect;
 | |
|   IntSize size = aRect.Size();
 | |
|   srcRect.Inflate(ceil(float(aKernelUnitLengthX)),
 | |
|                   ceil(float(aKernelUnitLengthY)));
 | |
| 
 | |
|   // Inflate the source rect by another pixel because the bilinear filtering in
 | |
|   // ColorComponentAtPoint may want to access the margins.
 | |
|   srcRect.Inflate(1);
 | |
| 
 | |
|   RefPtr<DataSourceSurface> input =
 | |
|     GetInputDataSourceSurface(IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8,
 | |
|                               EDGE_MODE_NONE);
 | |
| 
 | |
|   if (!input) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (input->GetFormat() != SurfaceFormat::A8) {
 | |
|     input = FilterProcessing::ExtractAlpha(input);
 | |
|   }
 | |
| 
 | |
|   RefPtr<DataSourceSurface> target =
 | |
|     Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
 | |
|   if (MOZ2D_WARN_IF(!target)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
 | |
| 
 | |
| 
 | |
|   DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
 | |
|   DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
 | |
|   if (MOZ2D_WARN_IF(!(sourceMap.IsMapped() && targetMap.IsMapped()))) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   uint8_t* sourceData = DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
 | |
|   int32_t sourceStride = sourceMap.GetStride();
 | |
|   uint8_t* sourceBegin = sourceMap.GetData();
 | |
|   uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
 | |
|   uint8_t* targetData = targetMap.GetData();
 | |
|   int32_t targetStride = targetMap.GetStride();
 | |
| 
 | |
|   MutexAutoLock lock(mLock);
 | |
| 
 | |
|   uint32_t lightColor = ColorToBGRA(mColor);
 | |
|   mLight.Prepare();
 | |
|   mLighting.Prepare();
 | |
| 
 | |
|   for (int32_t y = 0; y < size.height; y++) {
 | |
|     for (int32_t x = 0; x < size.width; x++) {
 | |
|       int32_t sourceIndex = y * sourceStride + x;
 | |
|       int32_t targetIndex = y * targetStride + 4 * x;
 | |
| 
 | |
|       Point3D normal = GenerateNormal(sourceData, sourceStride, sourceBegin, sourceEnd,
 | |
|                                       x, y, mSurfaceScale,
 | |
|                                       aKernelUnitLengthX, aKernelUnitLengthY);
 | |
| 
 | |
|       IntPoint pointInFilterSpace(aRect.X() + x, aRect.Y() + y);
 | |
|       Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f;
 | |
|       Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z);
 | |
|       Point3D rayDir = mLight.GetVectorToLight(pt);
 | |
|       uint32_t color = mLight.GetColor(lightColor, rayDir);
 | |
| 
 | |
|       *(uint32_t*)(targetData + targetIndex) = mLighting.LightPixel(normal, rayDir, color);
 | |
|     }
 | |
| 
 | |
|     // Zero padding to keep valgrind happy.
 | |
|     PodZero(&targetData[y * targetStride + 4 * size.width], targetStride - 4 * size.width);
 | |
|   }
 | |
| 
 | |
|   return target.forget();
 | |
| }
 | |
| 
 | |
| DiffuseLightingSoftware::DiffuseLightingSoftware()
 | |
|  : mDiffuseConstant(0)
 | |
| {
 | |
| }
 | |
| 
 | |
| bool
 | |
| DiffuseLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT:
 | |
|       mDiffuseConstant = aValue;
 | |
|       break;
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| DiffuseLightingSoftware::LightPixel(const Point3D &aNormal,
 | |
|                                     const Point3D &aVectorToLight,
 | |
|                                     uint32_t aColor)
 | |
| {
 | |
|   Float dotNL = std::max(0.0f, aNormal.DotProduct(aVectorToLight));
 | |
|   Float diffuseNL = mDiffuseConstant * dotNL;
 | |
| 
 | |
|   union {
 | |
|     uint32_t bgra;
 | |
|     uint8_t components[4];
 | |
|   } color = { aColor };
 | |
|   color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
 | |
|     umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]), 255U);
 | |
|   color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
 | |
|     umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]), 255U);
 | |
|   color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
 | |
|     umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]), 255U);
 | |
|   color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
 | |
|   return color.bgra;
 | |
| }
 | |
| 
 | |
| SpecularLightingSoftware::SpecularLightingSoftware()
 | |
|  : mSpecularConstant(0)
 | |
|  , mSpecularExponent(0)
 | |
|  , mSpecularConstantInt(0)
 | |
| {
 | |
| }
 | |
| 
 | |
| bool
 | |
| SpecularLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue)
 | |
| {
 | |
|   switch (aIndex) {
 | |
|     case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT:
 | |
|       mSpecularConstant = std::min(std::max(aValue, 0.0f), 255.0f);
 | |
|       break;
 | |
|     case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT:
 | |
|       mSpecularExponent = std::min(std::max(aValue, 1.0f), 128.0f);
 | |
|       break;
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void
 | |
| SpecularLightingSoftware::Prepare()
 | |
| {
 | |
|   mPowCache.CacheForExponent(mSpecularExponent);
 | |
|   mSpecularConstantInt = uint32_t(mSpecularConstant * (1 << 8));
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| SpecularLightingSoftware::LightPixel(const Point3D &aNormal,
 | |
|                                      const Point3D &aVectorToLight,
 | |
|                                      uint32_t aColor)
 | |
| {
 | |
|   Point3D vectorToEye(0, 0, 1);
 | |
|   Point3D halfwayVector = Normalized(aVectorToLight + vectorToEye);
 | |
|   Float dotNH = aNormal.DotProduct(halfwayVector);
 | |
|   uint16_t dotNHi = uint16_t(dotNH * (dotNH >= 0) * (1 << PowCache::sInputIntPrecisionBits));
 | |
|   // The exponent for specular is in [1,128] range, so we don't need to check and
 | |
|   // optimize for the "default power table" scenario here.
 | |
|   MOZ_ASSERT(mPowCache.HasPowerTable());
 | |
|   uint32_t specularNHi = uint32_t(mSpecularConstantInt) * mPowCache.Pow(dotNHi) >> 8;
 | |
| 
 | |
|   union {
 | |
|     uint32_t bgra;
 | |
|     uint8_t components[4];
 | |
|   } color = { aColor };
 | |
|   color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
 | |
|     umin(
 | |
|       (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]) >> PowCache::sOutputIntPrecisionBits, 255U);
 | |
|   color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
 | |
|     umin(
 | |
|       (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) >> PowCache::sOutputIntPrecisionBits, 255U);
 | |
|   color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
 | |
|     umin(
 | |
|       (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) >> PowCache::sOutputIntPrecisionBits, 255U);
 | |
| 
 | |
|   color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
 | |
|     umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B],
 | |
|       umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G],
 | |
|                color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]));
 | |
|   return color.bgra;
 | |
| }
 | |
| 
 | |
| } // namespace gfx
 | |
| } // namespace mozilla
 | 
