michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #define _USE_MATH_DEFINES michael@0: michael@0: #include michael@0: #include "FilterNodeSoftware.h" michael@0: #include "2D.h" michael@0: #include "Tools.h" michael@0: #include "Blur.h" michael@0: #include michael@0: #include "FilterProcessing.h" michael@0: #include "mozilla/PodOperations.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: // #define DEBUG_DUMP_SURFACES michael@0: michael@0: #ifdef DEBUG_DUMP_SURFACES michael@0: #include "gfxImageSurface.h" michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: static void michael@0: DumpAsPNG(SourceSurface* aSurface) michael@0: { michael@0: RefPtr dataSource = aSurface->GetDataSurface(); michael@0: IntSize size = dataSource->GetSize(); michael@0: nsRefPtr imageSurface = michael@0: new gfxImageSurface(dataSource->GetData(), gfxIntSize(size.width, size.height), michael@0: dataSource->Stride(), michael@0: aSurface->GetFormat() == SurfaceFormat::A8 ? gfxImageFormat::A8 : gfxImageFormat::ARGB32); michael@0: imageSurface->PrintAsDataURL(); michael@0: } michael@0: } // namespace gfx michael@0: } // namespace mozilla michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: michael@0: namespace { michael@0: michael@0: /** michael@0: * This class provides a way to get a pow() results in constant-time. It works michael@0: * by caching 256 values for bases between 0 and 1 and a fixed exponent. michael@0: **/ michael@0: class PowCache michael@0: { michael@0: public: michael@0: PowCache() michael@0: { michael@0: CacheForExponent(0.0f); michael@0: } michael@0: michael@0: void CacheForExponent(Float aExponent) michael@0: { michael@0: mExponent = aExponent; michael@0: int numPreSquares = 0; michael@0: while (numPreSquares < 5 && mExponent > (1 << (numPreSquares + 2))) { michael@0: numPreSquares++; michael@0: } michael@0: mNumPowTablePreSquares = numPreSquares; michael@0: for (size_t i = 0; i < sCacheSize; i++) { michael@0: // sCacheSize is chosen in such a way that a takes values michael@0: // from 0.0 to 1.0 inclusive. michael@0: Float a = i / Float(1 << sCacheIndexPrecisionBits); michael@0: MOZ_ASSERT(0.0f <= a && a <= 1.0f, "We only want to cache for bases between 0 and 1."); michael@0: michael@0: for (int j = 0; j < mNumPowTablePreSquares; j++) { michael@0: a = sqrt(a); michael@0: } michael@0: uint32_t cachedInt = pow(a, mExponent) * (1 << sOutputIntPrecisionBits); michael@0: MOZ_ASSERT(cachedInt < (1 << (sizeof(mPowTable[i]) * 8)), "mPowCache integer type too small"); michael@0: michael@0: mPowTable[i] = cachedInt; michael@0: } michael@0: } michael@0: michael@0: uint16_t Pow(uint16_t aBase) michael@0: { michael@0: // Results should be similar to what the following code would produce: michael@0: // Float x = Float(aBase) / (1 << sInputIntPrecisionBits); michael@0: // return uint16_t(pow(x, mExponent) * (1 << sOutputIntPrecisionBits)); michael@0: michael@0: MOZ_ASSERT(aBase <= (1 << sInputIntPrecisionBits), "aBase needs to be between 0 and 1!"); michael@0: michael@0: uint32_t a = aBase; michael@0: for (int j = 0; j < mNumPowTablePreSquares; j++) { michael@0: a = a * a >> sInputIntPrecisionBits; michael@0: } michael@0: uint32_t i = a >> (sInputIntPrecisionBits - sCacheIndexPrecisionBits); michael@0: MOZ_ASSERT(i < sCacheSize, "out-of-bounds mPowTable access"); michael@0: return mPowTable[i]; michael@0: } michael@0: michael@0: static const int sInputIntPrecisionBits = 15; michael@0: static const int sOutputIntPrecisionBits = 15; michael@0: static const int sCacheIndexPrecisionBits = 7; michael@0: michael@0: private: michael@0: static const size_t sCacheSize = (1 << sCacheIndexPrecisionBits) + 1; michael@0: michael@0: Float mExponent; michael@0: int mNumPowTablePreSquares; michael@0: uint16_t mPowTable[sCacheSize]; michael@0: }; michael@0: michael@0: class PointLightSoftware michael@0: { michael@0: public: michael@0: bool SetAttribute(uint32_t aIndex, Float) { return false; } michael@0: bool SetAttribute(uint32_t aIndex, const Point3D &); michael@0: void Prepare() {} michael@0: Point3D GetVectorToLight(const Point3D &aTargetPoint); michael@0: uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight); michael@0: michael@0: private: michael@0: Point3D mPosition; michael@0: }; michael@0: michael@0: class SpotLightSoftware michael@0: { michael@0: public: michael@0: SpotLightSoftware(); michael@0: bool SetAttribute(uint32_t aIndex, Float); michael@0: bool SetAttribute(uint32_t aIndex, const Point3D &); michael@0: void Prepare(); michael@0: Point3D GetVectorToLight(const Point3D &aTargetPoint); michael@0: uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight); michael@0: michael@0: private: michael@0: Point3D mPosition; michael@0: Point3D mPointsAt; michael@0: Point3D mVectorFromFocusPointToLight; michael@0: Float mSpecularFocus; michael@0: Float mLimitingConeAngle; michael@0: Float mLimitingConeCos; michael@0: PowCache mPowCache; michael@0: }; michael@0: michael@0: class DistantLightSoftware michael@0: { michael@0: public: michael@0: DistantLightSoftware(); michael@0: bool SetAttribute(uint32_t aIndex, Float); michael@0: bool SetAttribute(uint32_t aIndex, const Point3D &) { return false; } michael@0: void Prepare(); michael@0: Point3D GetVectorToLight(const Point3D &aTargetPoint); michael@0: uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight); michael@0: michael@0: private: michael@0: Float mAzimuth; michael@0: Float mElevation; michael@0: Point3D mVectorToLight; michael@0: }; michael@0: michael@0: class DiffuseLightingSoftware michael@0: { michael@0: public: michael@0: DiffuseLightingSoftware(); michael@0: bool SetAttribute(uint32_t aIndex, Float); michael@0: void Prepare() {} michael@0: uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight, michael@0: uint32_t aColor); michael@0: michael@0: private: michael@0: Float mDiffuseConstant; michael@0: }; michael@0: michael@0: class SpecularLightingSoftware michael@0: { michael@0: public: michael@0: SpecularLightingSoftware(); michael@0: bool SetAttribute(uint32_t aIndex, Float); michael@0: void Prepare(); michael@0: uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight, michael@0: uint32_t aColor); michael@0: michael@0: private: michael@0: Float mSpecularConstant; michael@0: Float mSpecularExponent; michael@0: uint32_t mSpecularConstantInt; michael@0: PowCache mPowCache; michael@0: }; michael@0: michael@0: } // unnamed namespace michael@0: michael@0: // from xpcom/ds/nsMathUtils.h michael@0: static int32_t michael@0: NS_lround(double x) michael@0: { michael@0: return x >= 0.0 ? int32_t(x + 0.5) : int32_t(x - 0.5); michael@0: } michael@0: michael@0: void michael@0: ClearDataSourceSurface(DataSourceSurface *aSurface) michael@0: { michael@0: size_t numBytes = aSurface->GetSize().height * aSurface->Stride(); michael@0: uint8_t* data = aSurface->GetData(); michael@0: PodZero(data, numBytes); michael@0: } michael@0: michael@0: // This check is safe against integer overflow. michael@0: static bool michael@0: SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint) michael@0: { michael@0: IntSize size = aSurface->GetSize(); michael@0: return aPoint.x >= 0 && aPoint.x < size.width && michael@0: aPoint.y >= 0 && aPoint.y < size.height; michael@0: } michael@0: michael@0: static uint8_t* michael@0: DataAtOffset(DataSourceSurface* aSurface, IntPoint aPoint) michael@0: { michael@0: if (!SurfaceContainsPoint(aSurface, aPoint)) { michael@0: MOZ_CRASH("sample position needs to be inside surface!"); michael@0: } michael@0: michael@0: MOZ_ASSERT(Factory::CheckSurfaceSize(aSurface->GetSize()), michael@0: "surface size overflows - this should have been prevented when the surface was created"); michael@0: michael@0: uint8_t* data = aSurface->GetData() + aPoint.y * aSurface->Stride() + michael@0: aPoint.x * BytesPerPixel(aSurface->GetFormat()); michael@0: michael@0: if (data < aSurface->GetData()) { michael@0: MOZ_CRASH("out-of-range data access"); michael@0: } michael@0: michael@0: return data; michael@0: } michael@0: michael@0: static bool michael@0: IntRectOverflows(const IntRect& aRect) michael@0: { michael@0: CheckedInt xMost = aRect.x; michael@0: xMost += aRect.width; michael@0: CheckedInt yMost = aRect.y; michael@0: yMost += aRect.height; michael@0: return !xMost.isValid() || !yMost.isValid(); michael@0: } michael@0: michael@0: /** michael@0: * aSrcRect: Rect relative to the aSrc surface michael@0: * aDestPoint: Point inside aDest surface michael@0: */ michael@0: static void michael@0: CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest, michael@0: IntRect aSrcRect, IntPoint aDestPoint) michael@0: { michael@0: if (IntRectOverflows(aSrcRect) || michael@0: IntRectOverflows(IntRect(aDestPoint, aSrcRect.Size()))) { michael@0: MOZ_CRASH("we should never be getting invalid rects at this point"); michael@0: } michael@0: michael@0: MOZ_ASSERT(aSrc->GetFormat() == aDest->GetFormat(), "different surface formats"); michael@0: MOZ_ASSERT(IntRect(IntPoint(), aSrc->GetSize()).Contains(aSrcRect), "source rect too big for source surface"); michael@0: MOZ_ASSERT(IntRect(IntPoint(), aDest->GetSize()).Contains(aSrcRect - aSrcRect.TopLeft() + aDestPoint), "dest surface too small"); michael@0: michael@0: if (aSrcRect.IsEmpty()) { michael@0: return; michael@0: } michael@0: michael@0: uint8_t* sourceData = DataAtOffset(aSrc, aSrcRect.TopLeft()); michael@0: uint32_t sourceStride = aSrc->Stride(); michael@0: uint8_t* destData = DataAtOffset(aDest, aDestPoint); michael@0: uint32_t destStride = aDest->Stride(); michael@0: michael@0: if (BytesPerPixel(aSrc->GetFormat()) == 4) { michael@0: for (int32_t y = 0; y < aSrcRect.height; y++) { michael@0: PodCopy((int32_t*)destData, (int32_t*)sourceData, aSrcRect.width); michael@0: sourceData += sourceStride; michael@0: destData += destStride; michael@0: } michael@0: } else if (BytesPerPixel(aSrc->GetFormat()) == 1) { michael@0: for (int32_t y = 0; y < aSrcRect.height; y++) { michael@0: PodCopy(destData, sourceData, aSrcRect.width); michael@0: sourceData += sourceStride; michael@0: destData += destStride; michael@0: } michael@0: } michael@0: } michael@0: michael@0: TemporaryRef michael@0: CloneAligned(DataSourceSurface* aSource) michael@0: { michael@0: RefPtr copy = michael@0: Factory::CreateDataSourceSurface(aSource->GetSize(), aSource->GetFormat()); michael@0: if (copy) { michael@0: CopyRect(aSource, copy, IntRect(IntPoint(), aSource->GetSize()), IntPoint()); michael@0: } michael@0: return copy; michael@0: } michael@0: michael@0: static void michael@0: FillRectWithPixel(DataSourceSurface *aSurface, const IntRect &aFillRect, IntPoint aPixelPos) michael@0: { michael@0: MOZ_ASSERT(!IntRectOverflows(aFillRect)); michael@0: MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect), michael@0: "aFillRect needs to be completely inside the surface"); michael@0: MOZ_ASSERT(SurfaceContainsPoint(aSurface, aPixelPos), michael@0: "aPixelPos needs to be inside the surface"); michael@0: michael@0: int32_t stride = aSurface->Stride(); michael@0: uint8_t* sourcePixelData = DataAtOffset(aSurface, aPixelPos); michael@0: uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft()); michael@0: int bpp = BytesPerPixel(aSurface->GetFormat()); michael@0: michael@0: // Fill the first row by hand. michael@0: if (bpp == 4) { michael@0: uint32_t sourcePixel = *(uint32_t*)sourcePixelData; michael@0: for (int32_t x = 0; x < aFillRect.width; x++) { michael@0: *((uint32_t*)data + x) = sourcePixel; michael@0: } michael@0: } else if (BytesPerPixel(aSurface->GetFormat()) == 1) { michael@0: uint8_t sourcePixel = *sourcePixelData; michael@0: memset(data, sourcePixel, aFillRect.width); michael@0: } michael@0: michael@0: // Copy the first row into the other rows. michael@0: for (int32_t y = 1; y < aFillRect.height; y++) { michael@0: PodCopy(data + y * stride, data, aFillRect.width * bpp); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: FillRectWithVerticallyRepeatingHorizontalStrip(DataSourceSurface *aSurface, michael@0: const IntRect &aFillRect, michael@0: const IntRect &aSampleRect) michael@0: { michael@0: MOZ_ASSERT(!IntRectOverflows(aFillRect)); michael@0: MOZ_ASSERT(!IntRectOverflows(aSampleRect)); michael@0: MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect), michael@0: "aFillRect needs to be completely inside the surface"); michael@0: MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect), michael@0: "aSampleRect needs to be completely inside the surface"); michael@0: michael@0: int32_t stride = aSurface->Stride(); michael@0: uint8_t* sampleData = DataAtOffset(aSurface, aSampleRect.TopLeft()); michael@0: uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft()); michael@0: if (BytesPerPixel(aSurface->GetFormat()) == 4) { michael@0: for (int32_t y = 0; y < aFillRect.height; y++) { michael@0: PodCopy((uint32_t*)data, (uint32_t*)sampleData, aFillRect.width); michael@0: data += stride; michael@0: } michael@0: } else if (BytesPerPixel(aSurface->GetFormat()) == 1) { michael@0: for (int32_t y = 0; y < aFillRect.height; y++) { michael@0: PodCopy(data, sampleData, aFillRect.width); michael@0: data += stride; michael@0: } michael@0: } michael@0: } michael@0: michael@0: static void michael@0: FillRectWithHorizontallyRepeatingVerticalStrip(DataSourceSurface *aSurface, michael@0: const IntRect &aFillRect, michael@0: const IntRect &aSampleRect) michael@0: { michael@0: MOZ_ASSERT(!IntRectOverflows(aFillRect)); michael@0: MOZ_ASSERT(!IntRectOverflows(aSampleRect)); michael@0: MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect), michael@0: "aFillRect needs to be completely inside the surface"); michael@0: MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect), michael@0: "aSampleRect needs to be completely inside the surface"); michael@0: michael@0: int32_t stride = aSurface->Stride(); michael@0: uint8_t* sampleData = DataAtOffset(aSurface, aSampleRect.TopLeft()); michael@0: uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft()); michael@0: if (BytesPerPixel(aSurface->GetFormat()) == 4) { michael@0: for (int32_t y = 0; y < aFillRect.height; y++) { michael@0: int32_t sampleColor = *((uint32_t*)sampleData); michael@0: for (int32_t x = 0; x < aFillRect.width; x++) { michael@0: *((uint32_t*)data + x) = sampleColor; michael@0: } michael@0: data += stride; michael@0: sampleData += stride; michael@0: } michael@0: } else if (BytesPerPixel(aSurface->GetFormat()) == 1) { michael@0: for (int32_t y = 0; y < aFillRect.height; y++) { michael@0: uint8_t sampleColor = *sampleData; michael@0: memset(data, sampleColor, aFillRect.width); michael@0: data += stride; michael@0: sampleData += stride; michael@0: } michael@0: } michael@0: } michael@0: michael@0: static void michael@0: DuplicateEdges(DataSourceSurface* aSurface, const IntRect &aFromRect) michael@0: { michael@0: MOZ_ASSERT(!IntRectOverflows(aFromRect)); michael@0: MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFromRect), michael@0: "aFromRect needs to be completely inside the surface"); michael@0: michael@0: IntSize size = aSurface->GetSize(); michael@0: IntRect fill; michael@0: IntRect sampleRect; michael@0: for (int32_t ix = 0; ix < 3; ix++) { michael@0: switch (ix) { michael@0: case 0: michael@0: fill.x = 0; michael@0: fill.width = aFromRect.x; michael@0: sampleRect.x = fill.XMost(); michael@0: sampleRect.width = 1; michael@0: break; michael@0: case 1: michael@0: fill.x = aFromRect.x; michael@0: fill.width = aFromRect.width; michael@0: sampleRect.x = fill.x; michael@0: sampleRect.width = fill.width; michael@0: break; michael@0: case 2: michael@0: fill.x = aFromRect.XMost(); michael@0: fill.width = size.width - fill.x; michael@0: sampleRect.x = fill.x - 1; michael@0: sampleRect.width = 1; michael@0: break; michael@0: } michael@0: if (fill.width <= 0) { michael@0: continue; michael@0: } michael@0: bool xIsMiddle = (ix == 1); michael@0: for (int32_t iy = 0; iy < 3; iy++) { michael@0: switch (iy) { michael@0: case 0: michael@0: fill.y = 0; michael@0: fill.height = aFromRect.y; michael@0: sampleRect.y = fill.YMost(); michael@0: sampleRect.height = 1; michael@0: break; michael@0: case 1: michael@0: fill.y = aFromRect.y; michael@0: fill.height = aFromRect.height; michael@0: sampleRect.y = fill.y; michael@0: sampleRect.height = fill.height; michael@0: break; michael@0: case 2: michael@0: fill.y = aFromRect.YMost(); michael@0: fill.height = size.height - fill.y; michael@0: sampleRect.y = fill.y - 1; michael@0: sampleRect.height = 1; michael@0: break; michael@0: } michael@0: if (fill.height <= 0) { michael@0: continue; michael@0: } michael@0: bool yIsMiddle = (iy == 1); michael@0: if (!xIsMiddle && !yIsMiddle) { michael@0: // Corner michael@0: FillRectWithPixel(aSurface, fill, sampleRect.TopLeft()); michael@0: } michael@0: if (xIsMiddle && !yIsMiddle) { michael@0: // Top middle or bottom middle michael@0: FillRectWithVerticallyRepeatingHorizontalStrip(aSurface, fill, sampleRect); michael@0: } michael@0: if (!xIsMiddle && yIsMiddle) { michael@0: // Left middle or right middle michael@0: FillRectWithHorizontallyRepeatingVerticalStrip(aSurface, fill, sampleRect); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: static IntPoint michael@0: TileIndex(const IntRect &aFirstTileRect, const IntPoint &aPoint) michael@0: { michael@0: return IntPoint(int32_t(floor(double(aPoint.x - aFirstTileRect.x) / aFirstTileRect.width)), michael@0: int32_t(floor(double(aPoint.y - aFirstTileRect.y) / aFirstTileRect.height))); michael@0: } michael@0: michael@0: static void michael@0: TileSurface(DataSourceSurface* aSource, DataSourceSurface* aTarget, const IntPoint &aOffset) michael@0: { michael@0: IntRect sourceRect(aOffset, aSource->GetSize()); michael@0: IntRect targetRect(IntPoint(0, 0), aTarget->GetSize()); michael@0: IntPoint startIndex = TileIndex(sourceRect, targetRect.TopLeft()); michael@0: IntPoint endIndex = TileIndex(sourceRect, targetRect.BottomRight()); michael@0: michael@0: for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) { michael@0: for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) { michael@0: IntPoint destPoint(sourceRect.x + ix * sourceRect.width, michael@0: sourceRect.y + iy * sourceRect.height); michael@0: IntRect destRect(destPoint, sourceRect.Size()); michael@0: destRect = destRect.Intersect(targetRect); michael@0: IntRect srcRect = destRect - destPoint; michael@0: CopyRect(aSource, aTarget, srcRect, destRect.TopLeft()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: static TemporaryRef michael@0: GetDataSurfaceInRect(SourceSurface *aSurface, michael@0: const IntRect &aSurfaceRect, michael@0: const IntRect &aDestRect, michael@0: ConvolveMatrixEdgeMode aEdgeMode) michael@0: { michael@0: MOZ_ASSERT(aSurface ? aSurfaceRect.Size() == aSurface->GetSize() : aSurfaceRect.IsEmpty()); michael@0: michael@0: if (IntRectOverflows(aSurfaceRect) || IntRectOverflows(aDestRect)) { michael@0: // We can't rely on the intersection calculations below to make sense when michael@0: // XMost() or YMost() overflow. Bail out. michael@0: return nullptr; michael@0: } michael@0: michael@0: IntRect sourceRect = aSurfaceRect; michael@0: michael@0: if (sourceRect.IsEqualEdges(aDestRect)) { michael@0: return aSurface ? aSurface->GetDataSurface() : nullptr; michael@0: } michael@0: michael@0: IntRect intersect = sourceRect.Intersect(aDestRect); michael@0: IntRect intersectInSourceSpace = intersect - sourceRect.TopLeft(); michael@0: IntRect intersectInDestSpace = intersect - aDestRect.TopLeft(); michael@0: SurfaceFormat format = aSurface ? aSurface->GetFormat() : SurfaceFormat(SurfaceFormat::B8G8R8A8); michael@0: michael@0: RefPtr target = michael@0: Factory::CreateDataSourceSurface(aDestRect.Size(), format); michael@0: michael@0: if (!target) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aEdgeMode == EDGE_MODE_NONE && !aSurfaceRect.Contains(aDestRect)) { michael@0: ClearDataSourceSurface(target); michael@0: } michael@0: michael@0: if (!aSurface) { michael@0: return target; michael@0: } michael@0: michael@0: RefPtr dataSource = aSurface->GetDataSurface(); michael@0: MOZ_ASSERT(dataSource); michael@0: michael@0: if (aEdgeMode == EDGE_MODE_WRAP) { michael@0: TileSurface(dataSource, target, intersectInDestSpace.TopLeft()); michael@0: return target; michael@0: } michael@0: michael@0: CopyRect(dataSource, target, intersectInSourceSpace, michael@0: intersectInDestSpace.TopLeft()); michael@0: michael@0: if (aEdgeMode == EDGE_MODE_DUPLICATE) { michael@0: DuplicateEdges(target, intersectInDestSpace); michael@0: } michael@0: michael@0: return target; michael@0: } michael@0: michael@0: /* static */ TemporaryRef michael@0: FilterNodeSoftware::Create(FilterType aType) michael@0: { michael@0: RefPtr filter; michael@0: switch (aType) { michael@0: case FilterType::BLEND: michael@0: filter = new FilterNodeBlendSoftware(); michael@0: break; michael@0: case FilterType::TRANSFORM: michael@0: filter = new FilterNodeTransformSoftware(); michael@0: break; michael@0: case FilterType::MORPHOLOGY: michael@0: filter = new FilterNodeMorphologySoftware(); michael@0: break; michael@0: case FilterType::COLOR_MATRIX: michael@0: filter = new FilterNodeColorMatrixSoftware(); michael@0: break; michael@0: case FilterType::FLOOD: michael@0: filter = new FilterNodeFloodSoftware(); michael@0: break; michael@0: case FilterType::TILE: michael@0: filter = new FilterNodeTileSoftware(); michael@0: break; michael@0: case FilterType::TABLE_TRANSFER: michael@0: filter = new FilterNodeTableTransferSoftware(); michael@0: break; michael@0: case FilterType::DISCRETE_TRANSFER: michael@0: filter = new FilterNodeDiscreteTransferSoftware(); michael@0: break; michael@0: case FilterType::LINEAR_TRANSFER: michael@0: filter = new FilterNodeLinearTransferSoftware(); michael@0: break; michael@0: case FilterType::GAMMA_TRANSFER: michael@0: filter = new FilterNodeGammaTransferSoftware(); michael@0: break; michael@0: case FilterType::CONVOLVE_MATRIX: michael@0: filter = new FilterNodeConvolveMatrixSoftware(); michael@0: break; michael@0: case FilterType::DISPLACEMENT_MAP: michael@0: filter = new FilterNodeDisplacementMapSoftware(); michael@0: break; michael@0: case FilterType::TURBULENCE: michael@0: filter = new FilterNodeTurbulenceSoftware(); michael@0: break; michael@0: case FilterType::ARITHMETIC_COMBINE: michael@0: filter = new FilterNodeArithmeticCombineSoftware(); michael@0: break; michael@0: case FilterType::COMPOSITE: michael@0: filter = new FilterNodeCompositeSoftware(); michael@0: break; michael@0: case FilterType::GAUSSIAN_BLUR: michael@0: filter = new FilterNodeGaussianBlurSoftware(); michael@0: break; michael@0: case FilterType::DIRECTIONAL_BLUR: michael@0: filter = new FilterNodeDirectionalBlurSoftware(); michael@0: break; michael@0: case FilterType::CROP: michael@0: filter = new FilterNodeCropSoftware(); michael@0: break; michael@0: case FilterType::PREMULTIPLY: michael@0: filter = new FilterNodePremultiplySoftware(); michael@0: break; michael@0: case FilterType::UNPREMULTIPLY: michael@0: filter = new FilterNodeUnpremultiplySoftware(); michael@0: break; michael@0: case FilterType::POINT_DIFFUSE: michael@0: filter = new FilterNodeLightingSoftware("FilterNodeLightingSoftware"); michael@0: break; michael@0: case FilterType::POINT_SPECULAR: michael@0: filter = new FilterNodeLightingSoftware("FilterNodeLightingSoftware"); michael@0: break; michael@0: case FilterType::SPOT_DIFFUSE: michael@0: filter = new FilterNodeLightingSoftware("FilterNodeLightingSoftware"); michael@0: break; michael@0: case FilterType::SPOT_SPECULAR: michael@0: filter = new FilterNodeLightingSoftware("FilterNodeLightingSoftware"); michael@0: break; michael@0: case FilterType::DISTANT_DIFFUSE: michael@0: filter = new FilterNodeLightingSoftware("FilterNodeLightingSoftware"); michael@0: break; michael@0: case FilterType::DISTANT_SPECULAR: michael@0: filter = new FilterNodeLightingSoftware("FilterNodeLightingSoftware"); michael@0: break; michael@0: } michael@0: return filter; michael@0: } michael@0: michael@0: void michael@0: FilterNodeSoftware::Draw(DrawTarget* aDrawTarget, michael@0: const Rect &aSourceRect, michael@0: const Point &aDestPoint, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: #ifdef DEBUG_DUMP_SURFACES michael@0: printf("
\nRendering filter %s...\n", GetName());
michael@0: #endif
michael@0: 
michael@0:   Rect renderRect = aSourceRect;
michael@0:   renderRect.RoundOut();
michael@0:   IntRect renderIntRect;
michael@0:   if (!renderRect.ToIntRect(&renderIntRect)) {
michael@0: #ifdef DEBUG_DUMP_SURFACES
michael@0:     printf("render rect overflowed, not painting anything\n");
michael@0:     printf("
\n"); michael@0: #endif michael@0: return; michael@0: } michael@0: michael@0: IntRect outputRect = GetOutputRectInRect(renderIntRect); michael@0: if (IntRectOverflows(outputRect)) { michael@0: #ifdef DEBUG_DUMP_SURFACES michael@0: printf("output rect overflowed, not painting anything\n"); michael@0: printf("\n"); michael@0: #endif michael@0: return; michael@0: } michael@0: michael@0: RefPtr result; michael@0: if (!outputRect.IsEmpty()) { michael@0: result = GetOutput(outputRect); michael@0: } michael@0: michael@0: if (!result) { michael@0: // Null results are allowed and treated as transparent. Don't draw anything. michael@0: #ifdef DEBUG_DUMP_SURFACES michael@0: printf("output returned null\n"); michael@0: printf("\n"); michael@0: #endif michael@0: return; michael@0: } michael@0: michael@0: #ifdef DEBUG_DUMP_SURFACES michael@0: printf("output from %s:\n", GetName()); michael@0: printf("\n"); michael@0: printf("\n"); michael@0: #endif michael@0: michael@0: Point sourceToDestOffset = aDestPoint - aSourceRect.TopLeft(); michael@0: Rect renderedSourceRect = Rect(outputRect).Intersect(aSourceRect); michael@0: Rect renderedDestRect = renderedSourceRect + sourceToDestOffset; michael@0: if (result->GetFormat() == SurfaceFormat::A8) { michael@0: // Interpret the result as having implicitly black color channels. michael@0: aDrawTarget->PushClipRect(renderedDestRect); michael@0: aDrawTarget->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)), michael@0: result, michael@0: Point(outputRect.TopLeft()) + sourceToDestOffset, michael@0: aOptions); michael@0: aDrawTarget->PopClip(); michael@0: } else { michael@0: aDrawTarget->DrawSurface(result, renderedDestRect, michael@0: renderedSourceRect - Point(outputRect.TopLeft()), michael@0: DrawSurfaceOptions(), aOptions); michael@0: } michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeSoftware::GetOutput(const IntRect &aRect) michael@0: { michael@0: MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect)); michael@0: michael@0: if (IntRectOverflows(aRect)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!mCachedRect.Contains(aRect)) { michael@0: RequestRect(aRect); michael@0: mCachedOutput = Render(mRequestedRect); michael@0: if (!mCachedOutput) { michael@0: mCachedRect = IntRect(); michael@0: mRequestedRect = IntRect(); michael@0: return nullptr; michael@0: } michael@0: mCachedRect = mRequestedRect; michael@0: mRequestedRect = IntRect(); michael@0: } else { michael@0: MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?"); michael@0: } michael@0: return GetDataSurfaceInRect(mCachedOutput, mCachedRect, aRect, EDGE_MODE_NONE); michael@0: } michael@0: michael@0: void michael@0: FilterNodeSoftware::RequestRect(const IntRect &aRect) michael@0: { michael@0: mRequestedRect = mRequestedRect.Union(aRect); michael@0: RequestFromInputsForRect(aRect); michael@0: } michael@0: michael@0: void michael@0: FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex, const IntRect &aRect) michael@0: { michael@0: if (IntRectOverflows(aRect)) { michael@0: return; michael@0: } michael@0: michael@0: int32_t inputIndex = InputIndex(aInputEnumIndex); michael@0: if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) { michael@0: MOZ_CRASH(); michael@0: } michael@0: if (mInputSurfaces[inputIndex]) { michael@0: return; michael@0: } michael@0: RefPtr filter = mInputFilters[inputIndex]; michael@0: MOZ_ASSERT(filter, "missing input"); michael@0: filter->RequestRect(filter->GetOutputRectInRect(aRect)); michael@0: } michael@0: michael@0: SurfaceFormat michael@0: FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat, michael@0: FormatHint aFormatHint) michael@0: { michael@0: if (aCurrentFormat == SurfaceFormat::A8 && aFormatHint == CAN_HANDLE_A8) { michael@0: return SurfaceFormat::A8; michael@0: } michael@0: return SurfaceFormat::B8G8R8A8; michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeSoftware::GetInputDataSourceSurface(uint32_t aInputEnumIndex, michael@0: const IntRect& aRect, michael@0: FormatHint aFormatHint, michael@0: ConvolveMatrixEdgeMode aEdgeMode, michael@0: const IntRect *aTransparencyPaddedSourceRect) michael@0: { michael@0: if (IntRectOverflows(aRect)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: #ifdef DEBUG_DUMP_SURFACES michael@0: printf("

GetInputDataSourceSurface with aRect: %d, %d, %d, %d

\n", michael@0: aRect.x, aRect.y, aRect.width, aRect.height); michael@0: #endif michael@0: int32_t inputIndex = InputIndex(aInputEnumIndex); michael@0: if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) { michael@0: MOZ_CRASH(); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aRect.IsEmpty()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: RefPtr surface; michael@0: IntRect surfaceRect; michael@0: michael@0: if (mInputSurfaces[inputIndex]) { michael@0: // Input from input surface michael@0: surface = mInputSurfaces[inputIndex]; michael@0: #ifdef DEBUG_DUMP_SURFACES michael@0: printf("input from input surface:\n"); michael@0: #endif michael@0: surfaceRect = IntRect(IntPoint(0, 0), surface->GetSize()); michael@0: } else { michael@0: // Input from input filter michael@0: #ifdef DEBUG_DUMP_SURFACES michael@0: printf("getting input from input filter %s...\n", mInputFilters[inputIndex]->GetName()); michael@0: #endif michael@0: RefPtr filter = mInputFilters[inputIndex]; michael@0: MOZ_ASSERT(filter, "missing input"); michael@0: IntRect inputFilterOutput = filter->GetOutputRectInRect(aRect); michael@0: if (!inputFilterOutput.IsEmpty()) { michael@0: surface = filter->GetOutput(inputFilterOutput); michael@0: } michael@0: #ifdef DEBUG_DUMP_SURFACES michael@0: printf("input from input filter %s:\n", mInputFilters[inputIndex]->GetName()); michael@0: #endif michael@0: surfaceRect = inputFilterOutput; michael@0: MOZ_ASSERT(!surface || surfaceRect.Size() == surface->GetSize()); michael@0: } michael@0: michael@0: if (surface && surface->GetFormat() == SurfaceFormat::UNKNOWN) { michael@0: #ifdef DEBUG_DUMP_SURFACES michael@0: printf("wrong input format
\n\n"); michael@0: #endif michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!surfaceRect.IsEmpty() && !surface) { michael@0: #ifdef DEBUG_DUMP_SURFACES michael@0: printf(" -- no input --\n\n"); michael@0: #endif michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aTransparencyPaddedSourceRect && !aTransparencyPaddedSourceRect->IsEmpty()) { michael@0: IntRect srcRect = aTransparencyPaddedSourceRect->Intersect(aRect); michael@0: surface = GetDataSurfaceInRect(surface, surfaceRect, srcRect, EDGE_MODE_NONE); michael@0: surfaceRect = srcRect; michael@0: } michael@0: michael@0: RefPtr result = michael@0: GetDataSurfaceInRect(surface, surfaceRect, aRect, aEdgeMode); michael@0: michael@0: if (result && michael@0: (result->Stride() != GetAlignedStride<16>(result->Stride()) || michael@0: reinterpret_cast(result->GetData()) % 16 != 0)) { michael@0: // Align unaligned surface. michael@0: result = CloneAligned(result); michael@0: } michael@0: michael@0: if (!result) { michael@0: #ifdef DEBUG_DUMP_SURFACES michael@0: printf(" -- no input --\n\n"); michael@0: #endif michael@0: return nullptr; michael@0: } michael@0: michael@0: SurfaceFormat currentFormat = result->GetFormat(); michael@0: if (DesiredFormat(currentFormat, aFormatHint) == SurfaceFormat::B8G8R8A8 && michael@0: currentFormat != SurfaceFormat::B8G8R8A8) { michael@0: result = FilterProcessing::ConvertToB8G8R8A8(result); michael@0: } michael@0: michael@0: #ifdef DEBUG_DUMP_SURFACES michael@0: printf(""); michael@0: #endif michael@0: michael@0: MOZ_ASSERT(!result || result->GetSize() == aRect.Size(), "wrong surface size"); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex, michael@0: const IntRect &aInRect) michael@0: { michael@0: if (IntRectOverflows(aInRect)) { michael@0: return IntRect(); michael@0: } michael@0: michael@0: int32_t inputIndex = InputIndex(aInputEnumIndex); michael@0: if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) { michael@0: MOZ_CRASH(); michael@0: return IntRect(); michael@0: } michael@0: if (mInputSurfaces[inputIndex]) { michael@0: return aInRect.Intersect(IntRect(IntPoint(0, 0), michael@0: mInputSurfaces[inputIndex]->GetSize())); michael@0: } michael@0: RefPtr filter = mInputFilters[inputIndex]; michael@0: MOZ_ASSERT(filter, "missing input"); michael@0: return filter->GetOutputRectInRect(aInRect); michael@0: } michael@0: michael@0: size_t michael@0: FilterNodeSoftware::NumberOfSetInputs() michael@0: { michael@0: return std::max(mInputSurfaces.size(), mInputFilters.size()); michael@0: } michael@0: michael@0: void michael@0: FilterNodeSoftware::AddInvalidationListener(FilterInvalidationListener* aListener) michael@0: { michael@0: MOZ_ASSERT(aListener, "null listener"); michael@0: mInvalidationListeners.push_back(aListener); michael@0: } michael@0: michael@0: void michael@0: FilterNodeSoftware::RemoveInvalidationListener(FilterInvalidationListener* aListener) michael@0: { michael@0: MOZ_ASSERT(aListener, "null listener"); michael@0: std::vector::iterator it = michael@0: std::find(mInvalidationListeners.begin(), mInvalidationListeners.end(), aListener); michael@0: mInvalidationListeners.erase(it); michael@0: } michael@0: michael@0: void michael@0: FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware* aFilter) michael@0: { michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeSoftware::Invalidate() michael@0: { michael@0: mCachedOutput = nullptr; michael@0: mCachedRect = IntRect(); michael@0: for (std::vector::iterator it = mInvalidationListeners.begin(); michael@0: it != mInvalidationListeners.end(); it++) { michael@0: (*it)->FilterInvalidated(this); michael@0: } michael@0: } michael@0: michael@0: FilterNodeSoftware::~FilterNodeSoftware() michael@0: { michael@0: MOZ_ASSERT(!mInvalidationListeners.size(), michael@0: "All invalidation listeners should have unsubscribed themselves by now!"); michael@0: michael@0: for (std::vector >::iterator it = mInputFilters.begin(); michael@0: it != mInputFilters.end(); it++) { michael@0: if (*it) { michael@0: (*it)->RemoveInvalidationListener(this); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeSoftware::SetInput(uint32_t aIndex, FilterNode *aFilter) michael@0: { michael@0: if (aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) { michael@0: MOZ_ASSERT(false, "can only take software filters as inputs"); michael@0: return; michael@0: } michael@0: SetInput(aIndex, nullptr, static_cast(aFilter)); michael@0: } michael@0: michael@0: void michael@0: FilterNodeSoftware::SetInput(uint32_t aIndex, SourceSurface *aSurface) michael@0: { michael@0: SetInput(aIndex, aSurface, nullptr); michael@0: } michael@0: michael@0: void michael@0: FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex, michael@0: SourceSurface *aSurface, michael@0: FilterNodeSoftware *aFilter) michael@0: { michael@0: int32_t inputIndex = InputIndex(aInputEnumIndex); michael@0: if (inputIndex < 0) { michael@0: MOZ_CRASH(); michael@0: return; michael@0: } michael@0: if ((uint32_t)inputIndex >= mInputSurfaces.size()) { michael@0: mInputSurfaces.resize(inputIndex + 1); michael@0: } michael@0: if ((uint32_t)inputIndex >= mInputFilters.size()) { michael@0: mInputFilters.resize(inputIndex + 1); michael@0: } michael@0: mInputSurfaces[inputIndex] = aSurface; michael@0: if (mInputFilters[inputIndex]) { michael@0: mInputFilters[inputIndex]->RemoveInvalidationListener(this); michael@0: } michael@0: if (aFilter) { michael@0: aFilter->AddInvalidationListener(this); michael@0: } michael@0: mInputFilters[inputIndex] = aFilter; michael@0: Invalidate(); michael@0: } michael@0: michael@0: FilterNodeBlendSoftware::FilterNodeBlendSoftware() michael@0: : mBlendMode(BLEND_MODE_MULTIPLY) michael@0: {} michael@0: michael@0: int32_t michael@0: FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: switch (aInputEnumIndex) { michael@0: case IN_BLEND_IN: return 0; michael@0: case IN_BLEND_IN2: return 1; michael@0: default: return -1; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex, uint32_t aBlendMode) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_BLEND_BLENDMODE); michael@0: mBlendMode = static_cast(aBlendMode); michael@0: Invalidate(); michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeBlendSoftware::Render(const IntRect& aRect) michael@0: { michael@0: RefPtr input1 = michael@0: GetInputDataSourceSurface(IN_BLEND_IN, aRect, NEED_COLOR_CHANNELS); michael@0: RefPtr input2 = michael@0: GetInputDataSourceSurface(IN_BLEND_IN2, aRect, NEED_COLOR_CHANNELS); michael@0: michael@0: // Null inputs need to be treated as transparent. michael@0: michael@0: // First case: both are transparent. michael@0: if (!input1 && !input2) { michael@0: // Then the result is transparent, too. michael@0: return nullptr; michael@0: } michael@0: michael@0: // Second case: both are non-transparent. michael@0: if (input1 && input2) { michael@0: // Apply normal filtering. michael@0: return FilterProcessing::ApplyBlending(input1, input2, mBlendMode); michael@0: } michael@0: michael@0: // Third case: one of them is transparent. Return the non-transparent one. michael@0: return input1 ? input1 : input2; michael@0: } michael@0: michael@0: void michael@0: FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect &aRect) michael@0: { michael@0: RequestInputRect(IN_BLEND_IN, aRect); michael@0: RequestInputRect(IN_BLEND_IN2, aRect); michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: return GetInputRectInRect(IN_BLEND_IN, aRect).Union( michael@0: GetInputRectInRect(IN_BLEND_IN2, aRect)).Intersect(aRect); michael@0: } michael@0: michael@0: FilterNodeTransformSoftware::FilterNodeTransformSoftware() michael@0: : mFilter(Filter::GOOD) michael@0: {} michael@0: michael@0: int32_t michael@0: FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: switch (aInputEnumIndex) { michael@0: case IN_TRANSFORM_IN: return 0; michael@0: default: return -1; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, uint32_t aFilter) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER); michael@0: mFilter = static_cast(aFilter); michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, const Matrix &aMatrix) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX); michael@0: mMatrix = aMatrix; michael@0: Invalidate(); michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeTransformSoftware::SourceRectForOutputRect(const IntRect &aRect) michael@0: { michael@0: if (aRect.IsEmpty()) { michael@0: return IntRect(); michael@0: } michael@0: michael@0: Matrix inverted(mMatrix); michael@0: if (!inverted.Invert()) { michael@0: return IntRect(); michael@0: } michael@0: michael@0: Rect neededRect = inverted.TransformBounds(Rect(aRect)); michael@0: neededRect.RoundOut(); michael@0: IntRect neededIntRect; michael@0: if (!neededRect.ToIntRect(&neededIntRect)) { michael@0: return IntRect(); michael@0: } michael@0: return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect); michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeTransformSoftware::Render(const IntRect& aRect) michael@0: { michael@0: IntRect srcRect = SourceRectForOutputRect(aRect); michael@0: michael@0: RefPtr input = michael@0: GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect, NEED_COLOR_CHANNELS); michael@0: michael@0: if (!input) { michael@0: return nullptr; michael@0: } michael@0: michael@0: Matrix transform = Matrix::Translation(srcRect.x, srcRect.y) * mMatrix * michael@0: Matrix::Translation(-aRect.x, -aRect.y); michael@0: if (transform.IsIdentity() && srcRect.Size() == aRect.Size()) { michael@0: return input; michael@0: } michael@0: michael@0: RefPtr dt = michael@0: Factory::CreateDrawTarget(BackendType::CAIRO, aRect.Size(), input->GetFormat()); michael@0: if (!dt) { michael@0: return nullptr; michael@0: } michael@0: michael@0: Rect r(0, 0, srcRect.width, srcRect.height); michael@0: dt->SetTransform(transform); michael@0: dt->DrawSurface(input, r, r, DrawSurfaceOptions(mFilter)); michael@0: michael@0: RefPtr result = dt->Snapshot(); michael@0: RefPtr resultData = result->GetDataSurface(); michael@0: return resultData; michael@0: } michael@0: michael@0: void michael@0: FilterNodeTransformSoftware::RequestFromInputsForRect(const IntRect &aRect) michael@0: { michael@0: RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect)); michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: IntRect srcRect = SourceRectForOutputRect(aRect); michael@0: if (srcRect.IsEmpty()) { michael@0: return IntRect(); michael@0: } michael@0: michael@0: Rect outRect = mMatrix.TransformBounds(Rect(srcRect)); michael@0: outRect.RoundOut(); michael@0: IntRect outIntRect; michael@0: if (!outRect.ToIntRect(&outIntRect)) { michael@0: return IntRect(); michael@0: } michael@0: return outIntRect.Intersect(aRect); michael@0: } michael@0: michael@0: FilterNodeMorphologySoftware::FilterNodeMorphologySoftware() michael@0: : mOperator(MORPHOLOGY_OPERATOR_ERODE) michael@0: {} michael@0: michael@0: int32_t michael@0: FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: switch (aInputEnumIndex) { michael@0: case IN_MORPHOLOGY_IN: return 0; michael@0: default: return -1; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex, michael@0: const IntSize &aRadii) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_RADII); michael@0: mRadii.width = std::min(std::max(aRadii.width, 0), 100000); michael@0: mRadii.height = std::min(std::max(aRadii.height, 0), 100000); michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex, michael@0: uint32_t aOperator) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_OPERATOR); michael@0: mOperator = static_cast(aOperator); michael@0: Invalidate(); michael@0: } michael@0: michael@0: static TemporaryRef michael@0: ApplyMorphology(const IntRect& aSourceRect, DataSourceSurface* aInput, michael@0: const IntRect& aDestRect, int32_t rx, int32_t ry, michael@0: MorphologyOperator aOperator) michael@0: { michael@0: IntRect srcRect = aSourceRect - aDestRect.TopLeft(); michael@0: IntRect destRect = aDestRect - aDestRect.TopLeft(); michael@0: IntRect tmpRect(destRect.x, srcRect.y, destRect.width, srcRect.height); michael@0: #ifdef DEBUG michael@0: IntMargin margin = srcRect - destRect; michael@0: MOZ_ASSERT(margin.top >= ry && margin.right >= rx && michael@0: margin.bottom >= ry && margin.left >= rx, "insufficient margin"); michael@0: #endif michael@0: michael@0: RefPtr tmp; michael@0: if (rx == 0) { michael@0: tmp = aInput; michael@0: } else { michael@0: tmp = Factory::CreateDataSourceSurface(tmpRect.Size(), SurfaceFormat::B8G8R8A8); michael@0: if (!tmp) { michael@0: return nullptr; michael@0: } michael@0: michael@0: int32_t sourceStride = aInput->Stride(); michael@0: uint8_t* sourceData = DataAtOffset(aInput, destRect.TopLeft() - srcRect.TopLeft()); michael@0: michael@0: int32_t tmpStride = tmp->Stride(); michael@0: uint8_t* tmpData = DataAtOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft()); michael@0: michael@0: FilterProcessing::ApplyMorphologyHorizontal( michael@0: sourceData, sourceStride, tmpData, tmpStride, tmpRect, rx, aOperator); michael@0: } michael@0: michael@0: RefPtr dest; michael@0: if (ry == 0) { michael@0: dest = tmp; michael@0: } else { michael@0: dest = Factory::CreateDataSourceSurface(destRect.Size(), SurfaceFormat::B8G8R8A8); michael@0: if (!dest) { michael@0: return nullptr; michael@0: } michael@0: michael@0: int32_t tmpStride = tmp->Stride(); michael@0: uint8_t* tmpData = DataAtOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft()); michael@0: michael@0: int32_t destStride = dest->Stride(); michael@0: uint8_t* destData = dest->GetData(); michael@0: michael@0: FilterProcessing::ApplyMorphologyVertical( michael@0: tmpData, tmpStride, destData, destStride, destRect, ry, aOperator); michael@0: } michael@0: michael@0: return dest; michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeMorphologySoftware::Render(const IntRect& aRect) michael@0: { michael@0: IntRect srcRect = aRect; michael@0: srcRect.Inflate(mRadii); michael@0: michael@0: RefPtr input = michael@0: GetInputDataSourceSurface(IN_MORPHOLOGY_IN, srcRect, NEED_COLOR_CHANNELS); michael@0: if (!input) { michael@0: return nullptr; michael@0: } michael@0: michael@0: int32_t rx = mRadii.width; michael@0: int32_t ry = mRadii.height; michael@0: michael@0: if (rx == 0 && ry == 0) { michael@0: return input; michael@0: } michael@0: michael@0: return ApplyMorphology(srcRect, input, aRect, rx, ry, mOperator); michael@0: } michael@0: michael@0: void michael@0: FilterNodeMorphologySoftware::RequestFromInputsForRect(const IntRect &aRect) michael@0: { michael@0: IntRect srcRect = aRect; michael@0: srcRect.Inflate(mRadii); michael@0: RequestInputRect(IN_MORPHOLOGY_IN, srcRect); michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeMorphologySoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: IntRect inflatedSourceRect = aRect; michael@0: inflatedSourceRect.Inflate(mRadii); michael@0: IntRect inputRect = GetInputRectInRect(IN_MORPHOLOGY_IN, inflatedSourceRect); michael@0: if (mOperator == MORPHOLOGY_OPERATOR_ERODE) { michael@0: inputRect.Deflate(mRadii); michael@0: } else { michael@0: inputRect.Inflate(mRadii); michael@0: } michael@0: return inputRect.Intersect(aRect); michael@0: } michael@0: michael@0: int32_t michael@0: FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: switch (aInputEnumIndex) { michael@0: case IN_COLOR_MATRIX_IN: return 0; michael@0: default: return -1; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex, michael@0: const Matrix5x4 &aMatrix) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX); michael@0: mMatrix = aMatrix; michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex, michael@0: uint32_t aAlphaMode) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE); michael@0: mAlphaMode = (AlphaMode)aAlphaMode; michael@0: Invalidate(); michael@0: } michael@0: michael@0: static TemporaryRef michael@0: Premultiply(DataSourceSurface* aSurface) michael@0: { michael@0: if (aSurface->GetFormat() == SurfaceFormat::A8) { michael@0: return aSurface; michael@0: } michael@0: michael@0: IntSize size = aSurface->GetSize(); michael@0: RefPtr target = michael@0: Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); michael@0: if (!target) { michael@0: return nullptr; michael@0: } michael@0: michael@0: uint8_t* inputData = aSurface->GetData(); michael@0: int32_t inputStride = aSurface->Stride(); michael@0: uint8_t* targetData = target->GetData(); michael@0: int32_t targetStride = target->Stride(); michael@0: michael@0: FilterProcessing::DoPremultiplicationCalculation( michael@0: size, targetData, targetStride, inputData, inputStride); michael@0: michael@0: return target; michael@0: } michael@0: michael@0: static TemporaryRef michael@0: Unpremultiply(DataSourceSurface* aSurface) michael@0: { michael@0: if (aSurface->GetFormat() == SurfaceFormat::A8) { michael@0: return aSurface; michael@0: } michael@0: michael@0: IntSize size = aSurface->GetSize(); michael@0: RefPtr target = michael@0: Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); michael@0: if (!target) { michael@0: return nullptr; michael@0: } michael@0: michael@0: uint8_t* inputData = aSurface->GetData(); michael@0: int32_t inputStride = aSurface->Stride(); michael@0: uint8_t* targetData = target->GetData(); michael@0: int32_t targetStride = target->Stride(); michael@0: michael@0: FilterProcessing::DoUnpremultiplicationCalculation( michael@0: size, targetData, targetStride, inputData, inputStride); michael@0: michael@0: return target; michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeColorMatrixSoftware::Render(const IntRect& aRect) michael@0: { michael@0: RefPtr input = michael@0: GetInputDataSourceSurface(IN_COLOR_MATRIX_IN, aRect, NEED_COLOR_CHANNELS); michael@0: if (!input) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) { michael@0: input = Unpremultiply(input); michael@0: } michael@0: michael@0: RefPtr result = michael@0: FilterProcessing::ApplyColorMatrix(input, mMatrix); michael@0: michael@0: if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) { michael@0: result = Premultiply(result); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: FilterNodeColorMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect) michael@0: { michael@0: RequestInputRect(IN_COLOR_MATRIX_IN, aRect); michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeColorMatrixSoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect); michael@0: } michael@0: michael@0: void michael@0: FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex, const Color &aColor) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_FLOOD_COLOR); michael@0: mColor = aColor; michael@0: Invalidate(); michael@0: } michael@0: michael@0: static uint32_t michael@0: ColorToBGRA(const Color& aColor) michael@0: { michael@0: union { michael@0: uint32_t color; michael@0: uint8_t components[4]; michael@0: }; michael@0: components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = NS_lround(aColor.r * aColor.a * 255.0f); michael@0: components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = NS_lround(aColor.g * aColor.a * 255.0f); michael@0: components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = NS_lround(aColor.b * aColor.a * 255.0f); michael@0: components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = NS_lround(aColor.a * 255.0f); michael@0: return color; michael@0: } michael@0: michael@0: static SurfaceFormat michael@0: FormatForColor(Color aColor) michael@0: { michael@0: if (aColor.r == 0 && aColor.g == 0 && aColor.b == 0) { michael@0: return SurfaceFormat::A8; michael@0: } michael@0: return SurfaceFormat::B8G8R8A8; michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeFloodSoftware::Render(const IntRect& aRect) michael@0: { michael@0: SurfaceFormat format = FormatForColor(mColor); michael@0: RefPtr target = michael@0: Factory::CreateDataSourceSurface(aRect.Size(), format); michael@0: if (!target) { michael@0: return nullptr; michael@0: } michael@0: michael@0: uint8_t* targetData = target->GetData(); michael@0: uint32_t stride = target->Stride(); michael@0: michael@0: if (format == SurfaceFormat::B8G8R8A8) { michael@0: uint32_t color = ColorToBGRA(mColor); michael@0: for (int32_t y = 0; y < aRect.height; y++) { michael@0: for (int32_t x = 0; x < aRect.width; x++) { michael@0: *((uint32_t*)targetData + x) = color; michael@0: } michael@0: targetData += stride; michael@0: } michael@0: } else if (format == SurfaceFormat::A8) { michael@0: uint8_t alpha = NS_lround(mColor.a * 255.0f); michael@0: for (int32_t y = 0; y < aRect.height; y++) { michael@0: for (int32_t x = 0; x < aRect.width; x++) { michael@0: targetData[x] = alpha; michael@0: } michael@0: targetData += stride; michael@0: } michael@0: } else { michael@0: MOZ_CRASH(); michael@0: } michael@0: michael@0: return target; michael@0: } michael@0: michael@0: // Override GetOutput to get around caching. Rendering simple floods is michael@0: // comparatively fast. michael@0: TemporaryRef michael@0: FilterNodeFloodSoftware::GetOutput(const IntRect& aRect) michael@0: { michael@0: return Render(aRect); michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: if (mColor.a == 0.0f) { michael@0: return IntRect(); michael@0: } michael@0: return aRect; michael@0: } michael@0: michael@0: int32_t michael@0: FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: switch (aInputEnumIndex) { michael@0: case IN_TILE_IN: return 0; michael@0: default: return -1; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeTileSoftware::SetAttribute(uint32_t aIndex, michael@0: const IntRect &aSourceRect) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT); michael@0: mSourceRect = IntRect(int32_t(aSourceRect.x), int32_t(aSourceRect.y), michael@0: int32_t(aSourceRect.width), int32_t(aSourceRect.height)); michael@0: Invalidate(); michael@0: } michael@0: michael@0: namespace { michael@0: struct CompareIntRects michael@0: { michael@0: bool operator()(const IntRect& a, const IntRect& b) const michael@0: { michael@0: if (a.x != b.x) { michael@0: return a.x < b.x; michael@0: } michael@0: if (a.y != b.y) { michael@0: return a.y < b.y; michael@0: } michael@0: if (a.width != b.width) { michael@0: return a.width < b.width; michael@0: } michael@0: return a.height < b.height; michael@0: } michael@0: }; michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeTileSoftware::Render(const IntRect& aRect) michael@0: { michael@0: if (mSourceRect.IsEmpty()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (mSourceRect.Contains(aRect)) { michael@0: return GetInputDataSourceSurface(IN_TILE_IN, aRect); michael@0: } michael@0: michael@0: RefPtr target; michael@0: michael@0: typedef std::map, CompareIntRects> InputMap; michael@0: InputMap inputs; michael@0: michael@0: IntPoint startIndex = TileIndex(mSourceRect, aRect.TopLeft()); michael@0: IntPoint endIndex = TileIndex(mSourceRect, aRect.BottomRight()); michael@0: for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) { michael@0: for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) { michael@0: IntPoint sourceToDestOffset(ix * mSourceRect.width, michael@0: iy * mSourceRect.height); michael@0: IntRect destRect = aRect.Intersect(mSourceRect + sourceToDestOffset); michael@0: IntRect srcRect = destRect - sourceToDestOffset; michael@0: if (srcRect.IsEmpty()) { michael@0: continue; michael@0: } michael@0: michael@0: RefPtr input; michael@0: InputMap::iterator it = inputs.find(srcRect); michael@0: if (it == inputs.end()) { michael@0: input = GetInputDataSourceSurface(IN_TILE_IN, srcRect); michael@0: inputs[srcRect] = input; michael@0: } else { michael@0: input = it->second; michael@0: } michael@0: if (!input) { michael@0: return nullptr; michael@0: } michael@0: if (!target) { michael@0: // We delay creating the target until now because we want to use the michael@0: // same format as our input filter, and we do not actually know the michael@0: // input format before we call GetInputDataSourceSurface. michael@0: target = Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat()); michael@0: if (!target) { michael@0: return nullptr; michael@0: } michael@0: } michael@0: MOZ_ASSERT(input->GetFormat() == target->GetFormat(), "different surface formats from the same input?"); michael@0: michael@0: CopyRect(input, target, srcRect - srcRect.TopLeft(), destRect.TopLeft() - aRect.TopLeft()); michael@0: } michael@0: } michael@0: michael@0: return target; michael@0: } michael@0: michael@0: void michael@0: FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect &aRect) michael@0: { michael@0: // Do not request anything. michael@0: // Source rects for the tile filter can be discontinuous with large gaps michael@0: // between them. Requesting those from our input filter might cause it to michael@0: // render the whole bounding box of all of them, which would be wasteful. michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeTileSoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: return aRect; michael@0: } michael@0: michael@0: FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware() michael@0: : mDisableR(true) michael@0: , mDisableG(true) michael@0: , mDisableB(true) michael@0: , mDisableA(true) michael@0: {} michael@0: michael@0: void michael@0: FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex, michael@0: bool aDisable) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_TRANSFER_DISABLE_R: michael@0: mDisableR = aDisable; michael@0: break; michael@0: case ATT_TRANSFER_DISABLE_G: michael@0: mDisableG = aDisable; michael@0: break; michael@0: case ATT_TRANSFER_DISABLE_B: michael@0: mDisableB = aDisable; michael@0: break; michael@0: case ATT_TRANSFER_DISABLE_A: michael@0: mDisableA = aDisable; michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeComponentTransferSoftware::GenerateLookupTable(ptrdiff_t aComponent, michael@0: uint8_t aTables[4][256], michael@0: bool aDisabled) michael@0: { michael@0: if (aDisabled) { michael@0: static uint8_t sIdentityLookupTable[256]; michael@0: static bool sInitializedIdentityLookupTable = false; michael@0: if (!sInitializedIdentityLookupTable) { michael@0: for (int32_t i = 0; i < 256; i++) { michael@0: sIdentityLookupTable[i] = i; michael@0: } michael@0: sInitializedIdentityLookupTable = true; michael@0: } michael@0: memcpy(aTables[aComponent], sIdentityLookupTable, 256); michael@0: } else { michael@0: FillLookupTable(aComponent, aTables[aComponent]); michael@0: } michael@0: } michael@0: michael@0: template michael@0: static void TransferComponents(DataSourceSurface* aInput, michael@0: DataSourceSurface* aTarget, michael@0: const uint8_t aLookupTables[BytesPerPixel][256]) michael@0: { michael@0: MOZ_ASSERT(aInput->GetFormat() == aTarget->GetFormat(), "different formats"); michael@0: IntSize size = aInput->GetSize(); michael@0: michael@0: uint8_t* sourceData = aInput->GetData(); michael@0: uint8_t* targetData = aTarget->GetData(); michael@0: uint32_t sourceStride = aInput->Stride(); michael@0: uint32_t targetStride = aTarget->Stride(); michael@0: michael@0: for (int32_t y = 0; y < size.height; y++) { michael@0: for (int32_t x = 0; x < size.width; x++) { michael@0: uint32_t sourceIndex = y * sourceStride + x * BytesPerPixel; michael@0: uint32_t targetIndex = y * targetStride + x * BytesPerPixel; michael@0: for (uint32_t i = 0; i < BytesPerPixel; i++) { michael@0: targetData[targetIndex + i] = aLookupTables[i][sourceData[sourceIndex + i]]; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: IsAllZero(uint8_t aLookupTable[256]) michael@0: { michael@0: for (int32_t i = 0; i < 256; i++) { michael@0: if (aLookupTable[i] != 0) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeComponentTransferSoftware::Render(const IntRect& aRect) michael@0: { michael@0: if (mDisableR && mDisableG && mDisableB && mDisableA) { michael@0: return GetInputDataSourceSurface(IN_TRANSFER_IN, aRect); michael@0: } michael@0: michael@0: uint8_t lookupTables[4][256]; michael@0: GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R, lookupTables, mDisableR); michael@0: GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G, lookupTables, mDisableG); michael@0: GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B, lookupTables, mDisableB); michael@0: GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A, lookupTables, mDisableA); michael@0: michael@0: bool needColorChannels = michael@0: lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R][0] != 0 || michael@0: lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G][0] != 0 || michael@0: lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B][0] != 0; michael@0: michael@0: FormatHint pref = needColorChannels ? NEED_COLOR_CHANNELS : CAN_HANDLE_A8; michael@0: michael@0: RefPtr input = michael@0: GetInputDataSourceSurface(IN_TRANSFER_IN, aRect, pref); michael@0: if (!input) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (input->GetFormat() == SurfaceFormat::B8G8R8A8 && !needColorChannels) { michael@0: bool colorChannelsBecomeBlack = michael@0: IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) && michael@0: IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) && michael@0: IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B]); michael@0: michael@0: if (colorChannelsBecomeBlack) { michael@0: input = FilterProcessing::ExtractAlpha(input); michael@0: } michael@0: } michael@0: michael@0: SurfaceFormat format = input->GetFormat(); michael@0: if (format == SurfaceFormat::A8 && mDisableA) { michael@0: return input; michael@0: } michael@0: michael@0: RefPtr target = michael@0: Factory::CreateDataSourceSurface(aRect.Size(), format); michael@0: if (!target) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (format == SurfaceFormat::A8) { michael@0: TransferComponents<1>(input, target, &lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_A]); michael@0: } else { michael@0: TransferComponents<4>(input, target, lookupTables); michael@0: } michael@0: michael@0: return target; michael@0: } michael@0: michael@0: void michael@0: FilterNodeComponentTransferSoftware::RequestFromInputsForRect(const IntRect &aRect) michael@0: { michael@0: RequestInputRect(IN_TRANSFER_IN, aRect); michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeComponentTransferSoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: return GetInputRectInRect(IN_TRANSFER_IN, aRect); michael@0: } michael@0: michael@0: int32_t michael@0: FilterNodeComponentTransferSoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: switch (aInputEnumIndex) { michael@0: case IN_TRANSFER_IN: return 0; michael@0: default: return -1; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex, michael@0: const Float* aFloat, michael@0: uint32_t aSize) michael@0: { michael@0: std::vector table(aFloat, aFloat + aSize); michael@0: switch (aIndex) { michael@0: case ATT_TABLE_TRANSFER_TABLE_R: michael@0: mTableR = table; michael@0: break; michael@0: case ATT_TABLE_TRANSFER_TABLE_G: michael@0: mTableG = table; michael@0: break; michael@0: case ATT_TABLE_TRANSFER_TABLE_B: michael@0: mTableB = table; michael@0: break; michael@0: case ATT_TABLE_TRANSFER_TABLE_A: michael@0: mTableA = table; michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent, michael@0: uint8_t aTable[256]) michael@0: { michael@0: switch (aComponent) { michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_R: michael@0: FillLookupTableImpl(mTableR, aTable); michael@0: break; michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_G: michael@0: FillLookupTableImpl(mTableG, aTable); michael@0: break; michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_B: michael@0: FillLookupTableImpl(mTableB, aTable); michael@0: break; michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_A: michael@0: FillLookupTableImpl(mTableA, aTable); michael@0: break; michael@0: default: michael@0: MOZ_ASSERT(false, "unknown component"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeTableTransferSoftware::FillLookupTableImpl(std::vector& aTableValues, michael@0: uint8_t aTable[256]) michael@0: { michael@0: uint32_t tvLength = aTableValues.size(); michael@0: if (tvLength < 2) { michael@0: return; michael@0: } michael@0: michael@0: for (size_t i = 0; i < 256; i++) { michael@0: uint32_t k = (i * (tvLength - 1)) / 255; michael@0: Float v1 = aTableValues[k]; michael@0: Float v2 = aTableValues[std::min(k + 1, tvLength - 1)]; michael@0: int32_t val = michael@0: int32_t(255 * (v1 + (i/255.0f - k/float(tvLength-1))*(tvLength - 1)*(v2 - v1))); michael@0: val = std::min(255, val); michael@0: val = std::max(0, val); michael@0: aTable[i] = val; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex, michael@0: const Float* aFloat, michael@0: uint32_t aSize) michael@0: { michael@0: std::vector discrete(aFloat, aFloat + aSize); michael@0: switch (aIndex) { michael@0: case ATT_DISCRETE_TRANSFER_TABLE_R: michael@0: mTableR = discrete; michael@0: break; michael@0: case ATT_DISCRETE_TRANSFER_TABLE_G: michael@0: mTableG = discrete; michael@0: break; michael@0: case ATT_DISCRETE_TRANSFER_TABLE_B: michael@0: mTableB = discrete; michael@0: break; michael@0: case ATT_DISCRETE_TRANSFER_TABLE_A: michael@0: mTableA = discrete; michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent, michael@0: uint8_t aTable[256]) michael@0: { michael@0: switch (aComponent) { michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_R: michael@0: FillLookupTableImpl(mTableR, aTable); michael@0: break; michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_G: michael@0: FillLookupTableImpl(mTableG, aTable); michael@0: break; michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_B: michael@0: FillLookupTableImpl(mTableB, aTable); michael@0: break; michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_A: michael@0: FillLookupTableImpl(mTableA, aTable); michael@0: break; michael@0: default: michael@0: MOZ_ASSERT(false, "unknown component"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(std::vector& aTableValues, michael@0: uint8_t aTable[256]) michael@0: { michael@0: uint32_t tvLength = aTableValues.size(); michael@0: if (tvLength < 1) { michael@0: return; michael@0: } michael@0: michael@0: for (size_t i = 0; i < 256; i++) { michael@0: uint32_t k = (i * tvLength) / 255; michael@0: k = std::min(k, tvLength - 1); michael@0: Float v = aTableValues[k]; michael@0: int32_t val = NS_lround(255 * v); michael@0: val = std::min(255, val); michael@0: val = std::max(0, val); michael@0: aTable[i] = val; michael@0: } michael@0: } michael@0: michael@0: FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware() michael@0: : mSlopeR(0) michael@0: , mSlopeG(0) michael@0: , mSlopeB(0) michael@0: , mSlopeA(0) michael@0: , mInterceptR(0) michael@0: , mInterceptG(0) michael@0: , mInterceptB(0) michael@0: , mInterceptA(0) michael@0: {} michael@0: michael@0: void michael@0: FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex, michael@0: Float aValue) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_LINEAR_TRANSFER_SLOPE_R: michael@0: mSlopeR = aValue; michael@0: break; michael@0: case ATT_LINEAR_TRANSFER_INTERCEPT_R: michael@0: mInterceptR = aValue; michael@0: break; michael@0: case ATT_LINEAR_TRANSFER_SLOPE_G: michael@0: mSlopeG = aValue; michael@0: break; michael@0: case ATT_LINEAR_TRANSFER_INTERCEPT_G: michael@0: mInterceptG = aValue; michael@0: break; michael@0: case ATT_LINEAR_TRANSFER_SLOPE_B: michael@0: mSlopeB = aValue; michael@0: break; michael@0: case ATT_LINEAR_TRANSFER_INTERCEPT_B: michael@0: mInterceptB = aValue; michael@0: break; michael@0: case ATT_LINEAR_TRANSFER_SLOPE_A: michael@0: mSlopeA = aValue; michael@0: break; michael@0: case ATT_LINEAR_TRANSFER_INTERCEPT_A: michael@0: mInterceptA = aValue; michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent, michael@0: uint8_t aTable[256]) michael@0: { michael@0: switch (aComponent) { michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_R: michael@0: FillLookupTableImpl(mSlopeR, mInterceptR, aTable); michael@0: break; michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_G: michael@0: FillLookupTableImpl(mSlopeG, mInterceptG, aTable); michael@0: break; michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_B: michael@0: FillLookupTableImpl(mSlopeB, mInterceptB, aTable); michael@0: break; michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_A: michael@0: FillLookupTableImpl(mSlopeA, mInterceptA, aTable); michael@0: break; michael@0: default: michael@0: MOZ_ASSERT(false, "unknown component"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeLinearTransferSoftware::FillLookupTableImpl(Float aSlope, michael@0: Float aIntercept, michael@0: uint8_t aTable[256]) michael@0: { michael@0: for (size_t i = 0; i < 256; i++) { michael@0: int32_t val = NS_lround(aSlope * i + 255 * aIntercept); michael@0: val = std::min(255, val); michael@0: val = std::max(0, val); michael@0: aTable[i] = val; michael@0: } michael@0: } michael@0: michael@0: FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware() michael@0: : mAmplitudeR(0) michael@0: , mAmplitudeG(0) michael@0: , mAmplitudeB(0) michael@0: , mAmplitudeA(0) michael@0: , mExponentR(0) michael@0: , mExponentG(0) michael@0: , mExponentB(0) michael@0: , mExponentA(0) michael@0: {} michael@0: michael@0: void michael@0: FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex, michael@0: Float aValue) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_GAMMA_TRANSFER_AMPLITUDE_R: michael@0: mAmplitudeR = aValue; michael@0: break; michael@0: case ATT_GAMMA_TRANSFER_EXPONENT_R: michael@0: mExponentR = aValue; michael@0: break; michael@0: case ATT_GAMMA_TRANSFER_OFFSET_R: michael@0: mOffsetR = aValue; michael@0: break; michael@0: case ATT_GAMMA_TRANSFER_AMPLITUDE_G: michael@0: mAmplitudeG = aValue; michael@0: break; michael@0: case ATT_GAMMA_TRANSFER_EXPONENT_G: michael@0: mExponentG = aValue; michael@0: break; michael@0: case ATT_GAMMA_TRANSFER_OFFSET_G: michael@0: mOffsetG = aValue; michael@0: break; michael@0: case ATT_GAMMA_TRANSFER_AMPLITUDE_B: michael@0: mAmplitudeB = aValue; michael@0: break; michael@0: case ATT_GAMMA_TRANSFER_EXPONENT_B: michael@0: mExponentB = aValue; michael@0: break; michael@0: case ATT_GAMMA_TRANSFER_OFFSET_B: michael@0: mOffsetB = aValue; michael@0: break; michael@0: case ATT_GAMMA_TRANSFER_AMPLITUDE_A: michael@0: mAmplitudeA = aValue; michael@0: break; michael@0: case ATT_GAMMA_TRANSFER_EXPONENT_A: michael@0: mExponentA = aValue; michael@0: break; michael@0: case ATT_GAMMA_TRANSFER_OFFSET_A: michael@0: mOffsetA = aValue; michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent, michael@0: uint8_t aTable[256]) michael@0: { michael@0: switch (aComponent) { michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_R: michael@0: FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable); michael@0: break; michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_G: michael@0: FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable); michael@0: break; michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_B: michael@0: FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable); michael@0: break; michael@0: case B8G8R8A8_COMPONENT_BYTEOFFSET_A: michael@0: FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable); michael@0: break; michael@0: default: michael@0: MOZ_ASSERT(false, "unknown component"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude, michael@0: Float aExponent, michael@0: Float aOffset, michael@0: uint8_t aTable[256]) michael@0: { michael@0: for (size_t i = 0; i < 256; i++) { michael@0: int32_t val = NS_lround(255 * (aAmplitude * pow(i / 255.0f, aExponent) + aOffset)); michael@0: val = std::min(255, val); michael@0: val = std::max(0, val); michael@0: aTable[i] = val; michael@0: } michael@0: } michael@0: michael@0: FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware() michael@0: : mDivisor(0) michael@0: , mBias(0) michael@0: , mEdgeMode(EDGE_MODE_DUPLICATE) michael@0: , mPreserveAlpha(false) michael@0: {} michael@0: michael@0: int32_t michael@0: FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: switch (aInputEnumIndex) { michael@0: case IN_CONVOLVE_MATRIX_IN: return 0; michael@0: default: return -1; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, michael@0: const IntSize &aKernelSize) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_SIZE); michael@0: mKernelSize = aKernelSize; michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, michael@0: const Float *aMatrix, michael@0: uint32_t aSize) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_MATRIX); michael@0: mKernelMatrix = std::vector(aMatrix, aMatrix + aSize); michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, Float aValue) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_CONVOLVE_MATRIX_DIVISOR: michael@0: mDivisor = aValue; michael@0: break; michael@0: case ATT_CONVOLVE_MATRIX_BIAS: michael@0: mBias = aValue; michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH: michael@0: mKernelUnitLength = aKernelUnitLength; michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, michael@0: const IntPoint &aTarget) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_TARGET); michael@0: mTarget = aTarget; michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, michael@0: const IntRect &aSourceRect) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_SOURCE_RECT); michael@0: mSourceRect = aSourceRect; michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, michael@0: uint32_t aEdgeMode) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_EDGE_MODE); michael@0: mEdgeMode = static_cast(aEdgeMode); michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, michael@0: bool aPreserveAlpha) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA); michael@0: mPreserveAlpha = aPreserveAlpha; michael@0: Invalidate(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: static bool sColorSamplingAccessControlEnabled = false; michael@0: static uint8_t* sColorSamplingAccessControlStart = nullptr; michael@0: static uint8_t* sColorSamplingAccessControlEnd = nullptr; michael@0: michael@0: struct DebugOnlyAutoColorSamplingAccessControl michael@0: { michael@0: DebugOnlyAutoColorSamplingAccessControl(DataSourceSurface* aSurface) michael@0: { michael@0: sColorSamplingAccessControlStart = aSurface->GetData(); michael@0: sColorSamplingAccessControlEnd = sColorSamplingAccessControlStart + michael@0: aSurface->Stride() * aSurface->GetSize().height; michael@0: sColorSamplingAccessControlEnabled = true; michael@0: } michael@0: michael@0: ~DebugOnlyAutoColorSamplingAccessControl() michael@0: { michael@0: sColorSamplingAccessControlEnabled = false; michael@0: } michael@0: }; michael@0: michael@0: static inline void michael@0: DebugOnlyCheckColorSamplingAccess(const uint8_t* aSampleAddress) michael@0: { michael@0: if (sColorSamplingAccessControlEnabled) { michael@0: MOZ_ASSERT(aSampleAddress >= sColorSamplingAccessControlStart, "accessing before start"); michael@0: MOZ_ASSERT(aSampleAddress < sColorSamplingAccessControlEnd, "accessing after end"); michael@0: } michael@0: } michael@0: #else michael@0: typedef DebugOnly DebugOnlyAutoColorSamplingAccessControl; michael@0: #define DebugOnlyCheckColorSamplingAccess(address) michael@0: #endif michael@0: michael@0: static inline uint8_t michael@0: ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y, size_t bpp, ptrdiff_t c) michael@0: { michael@0: DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c]); michael@0: return aData[y * aStride + bpp * x + c]; michael@0: } michael@0: michael@0: static inline int32_t michael@0: ColorAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y) michael@0: { michael@0: DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x); michael@0: return *(uint32_t*)(aData + y * aStride + 4 * x); michael@0: } michael@0: michael@0: // Accepts fractional x & y and does bilinear interpolation. michael@0: // Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible. michael@0: static inline uint8_t michael@0: ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, Float x, Float y, size_t bpp, ptrdiff_t c) michael@0: { michael@0: const uint32_t f = 256; michael@0: const int32_t lx = floor(x); michael@0: const int32_t ly = floor(y); michael@0: const int32_t tux = uint32_t((x - lx) * f); michael@0: const int32_t tlx = f - tux; michael@0: const int32_t tuy = uint32_t((y - ly) * f); michael@0: const int32_t tly = f - tuy; michael@0: const uint8_t &cll = ColorComponentAtPoint(aData, aStride, lx, ly, bpp, c); michael@0: const uint8_t &cul = ColorComponentAtPoint(aData, aStride, lx + 1, ly, bpp, c); michael@0: const uint8_t &clu = ColorComponentAtPoint(aData, aStride, lx, ly + 1, bpp, c); michael@0: const uint8_t &cuu = ColorComponentAtPoint(aData, aStride, lx + 1, ly + 1, bpp, c); michael@0: return ((cll * tlx + cul * tux) * tly + michael@0: (clu * tlx + cuu * tux) * tuy + f * f / 2) / (f * f); michael@0: } michael@0: michael@0: static inline uint32_t michael@0: ColorAtPoint(const uint8_t *aData, int32_t aStride, Float x, Float y) michael@0: { michael@0: return ColorComponentAtPoint(aData, aStride, x, y, 4, 0) | michael@0: (ColorComponentAtPoint(aData, aStride, x, y, 4, 1) << 8) | michael@0: (ColorComponentAtPoint(aData, aStride, x, y, 4, 2) << 16) | michael@0: (ColorComponentAtPoint(aData, aStride, x, y, 4, 3) << 24); michael@0: } michael@0: michael@0: static int32_t michael@0: ClampToNonZero(int32_t a) michael@0: { michael@0: return a * (a >= 0); michael@0: } michael@0: michael@0: template michael@0: static void michael@0: ConvolvePixel(const uint8_t *aSourceData, michael@0: uint8_t *aTargetData, michael@0: int32_t aWidth, int32_t aHeight, michael@0: int32_t aSourceStride, int32_t aTargetStride, michael@0: int32_t aX, int32_t aY, michael@0: const int32_t *aKernel, michael@0: int32_t aBias, int32_t shiftL, int32_t shiftR, michael@0: bool aPreserveAlpha, michael@0: int32_t aOrderX, int32_t aOrderY, michael@0: int32_t aTargetX, int32_t aTargetY, michael@0: CoordType aKernelUnitLengthX, michael@0: CoordType aKernelUnitLengthY) michael@0: { michael@0: int32_t sum[4] = {0, 0, 0, 0}; michael@0: int32_t offsets[4] = { B8G8R8A8_COMPONENT_BYTEOFFSET_R, michael@0: B8G8R8A8_COMPONENT_BYTEOFFSET_G, michael@0: B8G8R8A8_COMPONENT_BYTEOFFSET_B, michael@0: B8G8R8A8_COMPONENT_BYTEOFFSET_A }; michael@0: int32_t channels = aPreserveAlpha ? 3 : 4; michael@0: int32_t roundingAddition = shiftL == 0 ? 0 : 1 << (shiftL - 1); michael@0: michael@0: for (int32_t y = 0; y < aOrderY; y++) { michael@0: CoordType sampleY = aY + (y - aTargetY) * aKernelUnitLengthY; michael@0: for (int32_t x = 0; x < aOrderX; x++) { michael@0: CoordType sampleX = aX + (x - aTargetX) * aKernelUnitLengthX; michael@0: for (int32_t i = 0; i < channels; i++) { michael@0: sum[i] += aKernel[aOrderX * y + x] * michael@0: ColorComponentAtPoint(aSourceData, aSourceStride, michael@0: sampleX, sampleY, 4, offsets[i]); michael@0: } michael@0: } michael@0: } michael@0: for (int32_t i = 0; i < channels; i++) { michael@0: int32_t clamped = umin(ClampToNonZero(sum[i] + aBias), 255 << shiftL >> shiftR); michael@0: aTargetData[aY * aTargetStride + 4 * aX + offsets[i]] = michael@0: (clamped + roundingAddition) << shiftR >> shiftL; michael@0: } michael@0: if (aPreserveAlpha) { michael@0: aTargetData[aY * aTargetStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A] = michael@0: aSourceData[aY * aSourceStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A]; michael@0: } michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeConvolveMatrixSoftware::Render(const IntRect& aRect) michael@0: { michael@0: if (mKernelUnitLength.width == floor(mKernelUnitLength.width) && michael@0: mKernelUnitLength.height == floor(mKernelUnitLength.height)) { michael@0: return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height); michael@0: } michael@0: return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height); michael@0: } michael@0: michael@0: static std::vector michael@0: ReversedVector(const std::vector &aVector) michael@0: { michael@0: size_t length = aVector.size(); michael@0: std::vector result(length, 0); michael@0: for (size_t i = 0; i < length; i++) { michael@0: result[length - 1 - i] = aVector[i]; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: static std::vector michael@0: ScaledVector(const std::vector &aVector, Float aDivisor) michael@0: { michael@0: size_t length = aVector.size(); michael@0: std::vector result(length, 0); michael@0: for (size_t i = 0; i < length; i++) { michael@0: result[i] = aVector[i] / aDivisor; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: static Float michael@0: MaxVectorSum(const std::vector &aVector) michael@0: { michael@0: Float sum = 0; michael@0: size_t length = aVector.size(); michael@0: for (size_t i = 0; i < length; i++) { michael@0: if (aVector[i] > 0) { michael@0: sum += aVector[i]; michael@0: } michael@0: } michael@0: return sum; michael@0: } michael@0: michael@0: // Returns shiftL and shiftR in such a way that michael@0: // a << shiftL >> shiftR is roughly a * aFloat. michael@0: static void michael@0: TranslateDoubleToShifts(double aDouble, int32_t &aShiftL, int32_t &aShiftR) michael@0: { michael@0: aShiftL = 0; michael@0: aShiftR = 0; michael@0: if (aDouble <= 0) { michael@0: MOZ_CRASH(); michael@0: } michael@0: if (aDouble < 1) { michael@0: while (1 << (aShiftR + 1) < 1 / aDouble) { michael@0: aShiftR++; michael@0: } michael@0: } else { michael@0: while (1 << (aShiftL + 1) < aDouble) { michael@0: aShiftL++; michael@0: } michael@0: } michael@0: } michael@0: michael@0: template michael@0: TemporaryRef michael@0: FilterNodeConvolveMatrixSoftware::DoRender(const IntRect& aRect, michael@0: CoordType aKernelUnitLengthX, michael@0: CoordType aKernelUnitLengthY) michael@0: { michael@0: if (mKernelSize.width <= 0 || mKernelSize.height <= 0 || michael@0: mKernelMatrix.size() != uint32_t(mKernelSize.width * mKernelSize.height) || michael@0: !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) || michael@0: mDivisor == 0) { michael@0: return Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8); michael@0: } michael@0: michael@0: IntRect srcRect = InflatedSourceRect(aRect); michael@0: michael@0: // Inflate the source rect by another pixel because the bilinear filtering in michael@0: // ColorComponentAtPoint may want to access the margins. michael@0: srcRect.Inflate(1); michael@0: michael@0: RefPtr input = michael@0: GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect, NEED_COLOR_CHANNELS, mEdgeMode, &mSourceRect); michael@0: michael@0: if (!input) { michael@0: return nullptr; michael@0: } michael@0: michael@0: DebugOnlyAutoColorSamplingAccessControl accessControl(input); michael@0: michael@0: RefPtr target = michael@0: Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8); michael@0: if (!target) { michael@0: return nullptr; michael@0: } michael@0: ClearDataSourceSurface(target); michael@0: michael@0: IntPoint offset = aRect.TopLeft() - srcRect.TopLeft(); michael@0: michael@0: uint8_t* sourceData = DataAtOffset(input, offset); michael@0: int32_t sourceStride = input->Stride(); michael@0: uint8_t* targetData = target->GetData(); michael@0: int32_t targetStride = target->Stride(); michael@0: michael@0: // Why exactly are we reversing the kernel? michael@0: std::vector kernel = ReversedVector(mKernelMatrix); michael@0: kernel = ScaledVector(kernel, mDivisor); michael@0: Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias, michael@0: MaxVectorSum(ScaledVector(kernel, -1)) - mBias); michael@0: maxResultAbs = std::max(maxResultAbs, 1.0f); michael@0: michael@0: double idealFactor = INT32_MAX / 2.0 / maxResultAbs / 255.0 * 0.999; michael@0: MOZ_ASSERT(255.0 * maxResultAbs * idealFactor <= INT32_MAX / 2.0, "badly chosen float-to-int scale"); michael@0: int32_t shiftL, shiftR; michael@0: TranslateDoubleToShifts(idealFactor, shiftL, shiftR); michael@0: double factorFromShifts = Float(1 << shiftL) / Float(1 << shiftR); michael@0: MOZ_ASSERT(255.0 * maxResultAbs * factorFromShifts <= INT32_MAX / 2.0, "badly chosen float-to-int scale"); michael@0: michael@0: int32_t* intKernel = new int32_t[kernel.size()]; michael@0: for (size_t i = 0; i < kernel.size(); i++) { michael@0: intKernel[i] = NS_lround(kernel[i] * factorFromShifts); michael@0: } michael@0: int32_t bias = NS_lround(mBias * 255 * factorFromShifts); michael@0: michael@0: for (int32_t y = 0; y < aRect.height; y++) { michael@0: for (int32_t x = 0; x < aRect.width; x++) { michael@0: ConvolvePixel(sourceData, targetData, michael@0: aRect.width, aRect.height, sourceStride, targetStride, michael@0: x, y, intKernel, bias, shiftL, shiftR, mPreserveAlpha, michael@0: mKernelSize.width, mKernelSize.height, mTarget.x, mTarget.y, michael@0: aKernelUnitLengthX, aKernelUnitLengthY); michael@0: } michael@0: } michael@0: delete[] intKernel; michael@0: michael@0: return target; michael@0: } michael@0: michael@0: void michael@0: FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect) michael@0: { michael@0: RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect)); michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeConvolveMatrixSoftware::InflatedSourceRect(const IntRect &aDestRect) michael@0: { michael@0: if (aDestRect.IsEmpty()) { michael@0: return IntRect(); michael@0: } michael@0: michael@0: IntMargin margin; michael@0: margin.left = ceil(mTarget.x * mKernelUnitLength.width); michael@0: margin.top = ceil(mTarget.y * mKernelUnitLength.height); michael@0: margin.right = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width); michael@0: margin.bottom = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height); michael@0: michael@0: IntRect srcRect = aDestRect; michael@0: srcRect.Inflate(margin); michael@0: return srcRect; michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeConvolveMatrixSoftware::InflatedDestRect(const IntRect &aSourceRect) michael@0: { michael@0: if (aSourceRect.IsEmpty()) { michael@0: return IntRect(); michael@0: } michael@0: michael@0: IntMargin margin; michael@0: margin.left = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width); michael@0: margin.top = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height); michael@0: margin.right = ceil(mTarget.x * mKernelUnitLength.width); michael@0: margin.bottom = ceil(mTarget.y * mKernelUnitLength.height); michael@0: michael@0: IntRect destRect = aSourceRect; michael@0: destRect.Inflate(margin); michael@0: return destRect; michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: IntRect srcRequest = InflatedSourceRect(aRect); michael@0: IntRect srcOutput = GetInputRectInRect(IN_COLOR_MATRIX_IN, srcRequest); michael@0: return InflatedDestRect(srcOutput).Intersect(aRect); michael@0: } michael@0: michael@0: FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware() michael@0: : mScale(0.0f) michael@0: , mChannelX(COLOR_CHANNEL_R) michael@0: , mChannelY(COLOR_CHANNEL_G) michael@0: {} michael@0: michael@0: int32_t michael@0: FilterNodeDisplacementMapSoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: switch (aInputEnumIndex) { michael@0: case IN_DISPLACEMENT_MAP_IN: return 0; michael@0: case IN_DISPLACEMENT_MAP_IN2: return 1; michael@0: default: return -1; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, michael@0: Float aScale) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_DISPLACEMENT_MAP_SCALE); michael@0: mScale = aScale; michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_DISPLACEMENT_MAP_X_CHANNEL: michael@0: mChannelX = static_cast(aValue); michael@0: break; michael@0: case ATT_DISPLACEMENT_MAP_Y_CHANNEL: michael@0: mChannelY = static_cast(aValue); michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeDisplacementMapSoftware::Render(const IntRect& aRect) michael@0: { michael@0: IntRect srcRect = InflatedSourceOrDestRect(aRect); michael@0: RefPtr input = michael@0: GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN, srcRect, NEED_COLOR_CHANNELS); michael@0: RefPtr map = michael@0: GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN2, aRect, NEED_COLOR_CHANNELS); michael@0: RefPtr target = michael@0: Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8); michael@0: if (!input || !map || !target) { michael@0: return nullptr; michael@0: } michael@0: michael@0: IntPoint offset = aRect.TopLeft() - srcRect.TopLeft(); michael@0: michael@0: uint8_t* sourceData = DataAtOffset(input, offset); michael@0: int32_t sourceStride = input->Stride(); michael@0: uint8_t* mapData = map->GetData(); michael@0: int32_t mapStride = map->Stride(); michael@0: uint8_t* targetData = target->GetData(); michael@0: int32_t targetStride = target->Stride(); michael@0: michael@0: static const ptrdiff_t channelMap[4] = { michael@0: B8G8R8A8_COMPONENT_BYTEOFFSET_R, michael@0: B8G8R8A8_COMPONENT_BYTEOFFSET_G, michael@0: B8G8R8A8_COMPONENT_BYTEOFFSET_B, michael@0: B8G8R8A8_COMPONENT_BYTEOFFSET_A }; michael@0: uint16_t xChannel = channelMap[mChannelX]; michael@0: uint16_t yChannel = channelMap[mChannelY]; michael@0: michael@0: float scaleOver255 = mScale / 255.0f; michael@0: float scaleAdjustment = -0.5f * mScale; michael@0: michael@0: for (int32_t y = 0; y < aRect.height; y++) { michael@0: for (int32_t x = 0; x < aRect.width; x++) { michael@0: uint32_t mapIndex = y * mapStride + 4 * x; michael@0: uint32_t targIndex = y * targetStride + 4 * x; michael@0: int32_t sourceX = x + michael@0: scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment; michael@0: int32_t sourceY = y + michael@0: scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment; michael@0: *(uint32_t*)(targetData + targIndex) = michael@0: ColorAtPoint(sourceData, sourceStride, sourceX, sourceY); michael@0: } michael@0: } michael@0: michael@0: return target; michael@0: } michael@0: michael@0: void michael@0: FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(const IntRect &aRect) michael@0: { michael@0: RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect)); michael@0: RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect); michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect) michael@0: { michael@0: IntRect sourceOrDestRect = aDestOrSourceRect; michael@0: sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2)); michael@0: return sourceOrDestRect; michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeDisplacementMapSoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: IntRect srcRequest = InflatedSourceOrDestRect(aRect); michael@0: IntRect srcOutput = GetInputRectInRect(IN_DISPLACEMENT_MAP_IN, srcRequest); michael@0: return InflatedSourceOrDestRect(srcOutput).Intersect(aRect); michael@0: } michael@0: michael@0: FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware() michael@0: : mNumOctaves(0) michael@0: , mSeed(0) michael@0: , mStitchable(false) michael@0: , mType(TURBULENCE_TYPE_TURBULENCE) michael@0: {} michael@0: michael@0: int32_t michael@0: FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: return -1; michael@0: } michael@0: michael@0: void michael@0: FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const Size &aBaseFrequency) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_TURBULENCE_BASE_FREQUENCY: michael@0: mBaseFrequency = aBaseFrequency; michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: break; michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const IntRect &aRect) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_TURBULENCE_RECT: michael@0: mRenderRect = aRect; michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: break; michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, bool aStitchable) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_TURBULENCE_STITCHABLE); michael@0: mStitchable = aStitchable; michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_TURBULENCE_NUM_OCTAVES: michael@0: mNumOctaves = aValue; michael@0: break; michael@0: case ATT_TURBULENCE_SEED: michael@0: mSeed = aValue; michael@0: break; michael@0: case ATT_TURBULENCE_TYPE: michael@0: mType = static_cast(aValue); michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: break; michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeTurbulenceSoftware::Render(const IntRect& aRect) michael@0: { michael@0: return FilterProcessing::RenderTurbulence( michael@0: aRect.Size(), aRect.TopLeft(), mBaseFrequency, michael@0: mSeed, mNumOctaves, mType, mStitchable, Rect(mRenderRect)); michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeTurbulenceSoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: return aRect.Intersect(mRenderRect); michael@0: } michael@0: michael@0: FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware() michael@0: : mK1(0), mK2(0), mK3(0), mK4(0) michael@0: { michael@0: } michael@0: michael@0: int32_t michael@0: FilterNodeArithmeticCombineSoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: switch (aInputEnumIndex) { michael@0: case IN_ARITHMETIC_COMBINE_IN: return 0; michael@0: case IN_ARITHMETIC_COMBINE_IN2: return 1; michael@0: default: return -1; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex, michael@0: const Float* aFloat, michael@0: uint32_t aSize) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS); michael@0: MOZ_ASSERT(aSize == 4); michael@0: michael@0: mK1 = aFloat[0]; michael@0: mK2 = aFloat[1]; michael@0: mK3 = aFloat[2]; michael@0: mK4 = aFloat[3]; michael@0: michael@0: Invalidate(); michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeArithmeticCombineSoftware::Render(const IntRect& aRect) michael@0: { michael@0: RefPtr input1 = michael@0: GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS); michael@0: RefPtr input2 = michael@0: GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN2, aRect, NEED_COLOR_CHANNELS); michael@0: if (!input1 && !input2) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // If one input is null, treat it as transparent by adjusting the factors. michael@0: Float k1 = mK1, k2 = mK2, k3 = mK3, k4 = mK4; michael@0: if (!input1) { michael@0: k1 = 0.0f; michael@0: k2 = 0.0f; michael@0: input1 = input2; michael@0: } michael@0: michael@0: if (!input2) { michael@0: k1 = 0.0f; michael@0: k3 = 0.0f; michael@0: input2 = input1; michael@0: } michael@0: michael@0: return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3, k4); michael@0: } michael@0: michael@0: void michael@0: FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(const IntRect &aRect) michael@0: { michael@0: RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect); michael@0: RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect); michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: if (mK4 > 0.0f) { michael@0: return aRect; michael@0: } michael@0: IntRect rectFrom1 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect); michael@0: IntRect rectFrom2 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2, aRect).Intersect(aRect); michael@0: IntRect result; michael@0: if (mK1 > 0.0f) { michael@0: result = rectFrom1.Intersect(rectFrom2); michael@0: } michael@0: if (mK2 > 0.0f) { michael@0: result = result.Union(rectFrom1); michael@0: } michael@0: if (mK3 > 0.0f) { michael@0: result = result.Union(rectFrom2); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: FilterNodeCompositeSoftware::FilterNodeCompositeSoftware() michael@0: : mOperator(COMPOSITE_OPERATOR_OVER) michael@0: {} michael@0: michael@0: int32_t michael@0: FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: return aInputEnumIndex - IN_COMPOSITE_IN_START; michael@0: } michael@0: michael@0: void michael@0: FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex, uint32_t aCompositeOperator) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_COMPOSITE_OPERATOR); michael@0: mOperator = static_cast(aCompositeOperator); michael@0: Invalidate(); michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeCompositeSoftware::Render(const IntRect& aRect) michael@0: { michael@0: RefPtr start = michael@0: GetInputDataSourceSurface(IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS); michael@0: RefPtr dest = michael@0: Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8); michael@0: if (!dest) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (start) { michael@0: CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint()); michael@0: } else { michael@0: ClearDataSourceSurface(dest); michael@0: } michael@0: michael@0: for (size_t inputIndex = 1; inputIndex < NumberOfSetInputs(); inputIndex++) { michael@0: RefPtr input = michael@0: GetInputDataSourceSurface(IN_COMPOSITE_IN_START + inputIndex, aRect, NEED_COLOR_CHANNELS); michael@0: if (input) { michael@0: FilterProcessing::ApplyComposition(input, dest, mOperator); michael@0: } else { michael@0: // We need to treat input as transparent. Depending on the composite michael@0: // operator, different things happen to dest. michael@0: switch (mOperator) { michael@0: case COMPOSITE_OPERATOR_OVER: michael@0: case COMPOSITE_OPERATOR_ATOP: michael@0: case COMPOSITE_OPERATOR_XOR: michael@0: // dest is unchanged. michael@0: break; michael@0: case COMPOSITE_OPERATOR_OUT: michael@0: // dest is now transparent, but it can become non-transparent again michael@0: // when compositing additional inputs. michael@0: ClearDataSourceSurface(dest); michael@0: break; michael@0: case COMPOSITE_OPERATOR_IN: michael@0: // Transparency always wins. We're completely transparent now and michael@0: // no additional input can get rid of that transparency. michael@0: return nullptr; michael@0: } michael@0: } michael@0: } michael@0: return dest; michael@0: } michael@0: michael@0: void michael@0: FilterNodeCompositeSoftware::RequestFromInputsForRect(const IntRect &aRect) michael@0: { michael@0: for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) { michael@0: RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect); michael@0: } michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: IntRect rect; michael@0: for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) { michael@0: IntRect inputRect = GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect); michael@0: if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) { michael@0: rect = rect.Intersect(inputRect); michael@0: } else { michael@0: rect = rect.Union(inputRect); michael@0: } michael@0: } michael@0: return rect; michael@0: } michael@0: michael@0: int32_t michael@0: FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: switch (aInputEnumIndex) { michael@0: case IN_GAUSSIAN_BLUR_IN: return 0; michael@0: default: return -1; michael@0: } michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeBlurXYSoftware::Render(const IntRect& aRect) michael@0: { michael@0: Size sigmaXY = StdDeviationXY(); michael@0: IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height)); michael@0: michael@0: if (d.width == 0 && d.height == 0) { michael@0: return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, aRect); michael@0: } michael@0: michael@0: IntRect srcRect = InflatedSourceOrDestRect(aRect); michael@0: RefPtr input = michael@0: GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, srcRect); michael@0: if (!input) { michael@0: return nullptr; michael@0: } michael@0: michael@0: RefPtr target; michael@0: Rect r(0, 0, srcRect.width, srcRect.height); michael@0: michael@0: if (input->GetFormat() == SurfaceFormat::A8) { michael@0: target = Factory::CreateDataSourceSurface(srcRect.Size(), SurfaceFormat::A8); michael@0: CopyRect(input, target, IntRect(IntPoint(), input->GetSize()), IntPoint()); michael@0: AlphaBoxBlur blur(r, target->Stride(), sigmaXY.width, sigmaXY.height); michael@0: blur.Blur(target->GetData()); michael@0: } else { michael@0: RefPtr channel0, channel1, channel2, channel3; michael@0: FilterProcessing::SeparateColorChannels(input, channel0, channel1, channel2, channel3); michael@0: AlphaBoxBlur blur(r, channel0->Stride(), sigmaXY.width, sigmaXY.height); michael@0: blur.Blur(channel0->GetData()); michael@0: blur.Blur(channel1->GetData()); michael@0: blur.Blur(channel2->GetData()); michael@0: blur.Blur(channel3->GetData()); michael@0: target = FilterProcessing::CombineColorChannels(channel0, channel1, channel2, channel3); michael@0: } michael@0: michael@0: return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE); michael@0: } michael@0: michael@0: void michael@0: FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect &aRect) michael@0: { michael@0: RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect)); michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(const IntRect &aDestRect) michael@0: { michael@0: Size sigmaXY = StdDeviationXY(); michael@0: IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height)); michael@0: IntRect srcRect = aDestRect; michael@0: srcRect.Inflate(d); michael@0: return srcRect; michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: IntRect srcRequest = InflatedSourceOrDestRect(aRect); michael@0: IntRect srcOutput = GetInputRectInRect(IN_GAUSSIAN_BLUR_IN, srcRequest); michael@0: return InflatedSourceOrDestRect(srcOutput).Intersect(aRect); michael@0: } michael@0: michael@0: FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware() michael@0: : mStdDeviation(0) michael@0: {} michael@0: michael@0: void michael@0: FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex, michael@0: float aStdDeviation) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_GAUSSIAN_BLUR_STD_DEVIATION: michael@0: mStdDeviation = std::max(0.0f, aStdDeviation); michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: Size michael@0: FilterNodeGaussianBlurSoftware::StdDeviationXY() michael@0: { michael@0: return Size(mStdDeviation, mStdDeviation); michael@0: } michael@0: michael@0: FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware() michael@0: : mBlurDirection(BLUR_DIRECTION_X) michael@0: {} michael@0: michael@0: void michael@0: FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex, michael@0: Float aStdDeviation) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_DIRECTIONAL_BLUR_STD_DEVIATION: michael@0: mStdDeviation = std::max(0.0f, aStdDeviation); michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: void michael@0: FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex, michael@0: uint32_t aBlurDirection) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_DIRECTIONAL_BLUR_DIRECTION: michael@0: mBlurDirection = (BlurDirection)aBlurDirection; michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: Size michael@0: FilterNodeDirectionalBlurSoftware::StdDeviationXY() michael@0: { michael@0: float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0; michael@0: float sigmaY = mBlurDirection == BLUR_DIRECTION_Y ? mStdDeviation : 0; michael@0: return Size(sigmaX, sigmaY); michael@0: } michael@0: michael@0: int32_t michael@0: FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: switch (aInputEnumIndex) { michael@0: case IN_CROP_IN: return 0; michael@0: default: return -1; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FilterNodeCropSoftware::SetAttribute(uint32_t aIndex, michael@0: const Rect &aSourceRect) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_CROP_RECT); michael@0: Rect srcRect = aSourceRect; michael@0: srcRect.Round(); michael@0: if (!srcRect.ToIntRect(&mCropRect)) { michael@0: mCropRect = IntRect(); michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeCropSoftware::Render(const IntRect& aRect) michael@0: { michael@0: return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect)); michael@0: } michael@0: michael@0: void michael@0: FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect &aRect) michael@0: { michael@0: RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect)); michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeCropSoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect); michael@0: } michael@0: michael@0: int32_t michael@0: FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: switch (aInputEnumIndex) { michael@0: case IN_PREMULTIPLY_IN: return 0; michael@0: default: return -1; michael@0: } michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodePremultiplySoftware::Render(const IntRect& aRect) michael@0: { michael@0: RefPtr input = michael@0: GetInputDataSourceSurface(IN_PREMULTIPLY_IN, aRect); michael@0: return input ? Premultiply(input) : nullptr; michael@0: } michael@0: michael@0: void michael@0: FilterNodePremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect) michael@0: { michael@0: RequestInputRect(IN_PREMULTIPLY_IN, aRect); michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodePremultiplySoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect); michael@0: } michael@0: michael@0: int32_t michael@0: FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: switch (aInputEnumIndex) { michael@0: case IN_UNPREMULTIPLY_IN: return 0; michael@0: default: return -1; michael@0: } michael@0: } michael@0: michael@0: TemporaryRef michael@0: FilterNodeUnpremultiplySoftware::Render(const IntRect& aRect) michael@0: { michael@0: RefPtr input = michael@0: GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN, aRect); michael@0: return input ? Unpremultiply(input) : nullptr; michael@0: } michael@0: michael@0: void michael@0: FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect) michael@0: { michael@0: RequestInputRect(IN_UNPREMULTIPLY_IN, aRect); michael@0: } michael@0: michael@0: IntRect michael@0: FilterNodeUnpremultiplySoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect); michael@0: } michael@0: michael@0: bool michael@0: PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_POINT_LIGHT_POSITION: michael@0: mPosition = aPoint; michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: SpotLightSoftware::SpotLightSoftware() michael@0: : mSpecularFocus(0) michael@0: , mLimitingConeAngle(0) michael@0: , mLimitingConeCos(1) michael@0: { michael@0: } michael@0: michael@0: bool michael@0: SpotLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_SPOT_LIGHT_POSITION: michael@0: mPosition = aPoint; michael@0: break; michael@0: case ATT_SPOT_LIGHT_POINTS_AT: michael@0: mPointsAt = aPoint; michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: SpotLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE: michael@0: mLimitingConeAngle = aValue; michael@0: break; michael@0: case ATT_SPOT_LIGHT_FOCUS: michael@0: mSpecularFocus = aValue; michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: DistantLightSoftware::DistantLightSoftware() michael@0: : mAzimuth(0) michael@0: , mElevation(0) michael@0: { michael@0: } michael@0: michael@0: bool michael@0: DistantLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_DISTANT_LIGHT_AZIMUTH: michael@0: mAzimuth = aValue; michael@0: break; michael@0: case ATT_DISTANT_LIGHT_ELEVATION: michael@0: mElevation = aValue; michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static inline Point3D Normalized(const Point3D &vec) { michael@0: Point3D copy(vec); michael@0: copy.Normalize(); michael@0: return copy; michael@0: } michael@0: michael@0: template michael@0: FilterNodeLightingSoftware::FilterNodeLightingSoftware(const char* aTypeName) michael@0: : mSurfaceScale(0) michael@0: #if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING)) michael@0: , mTypeName(aTypeName) michael@0: #endif michael@0: {} michael@0: michael@0: template michael@0: int32_t michael@0: FilterNodeLightingSoftware::InputIndex(uint32_t aInputEnumIndex) michael@0: { michael@0: switch (aInputEnumIndex) { michael@0: case IN_LIGHTING_IN: return 0; michael@0: default: return -1; michael@0: } michael@0: } michael@0: michael@0: template michael@0: void michael@0: FilterNodeLightingSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint) michael@0: { michael@0: if (mLight.SetAttribute(aIndex, aPoint)) { michael@0: Invalidate(); michael@0: return; michael@0: } michael@0: MOZ_CRASH(); michael@0: } michael@0: michael@0: template michael@0: void michael@0: FilterNodeLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) michael@0: { michael@0: if (mLight.SetAttribute(aIndex, aValue) || michael@0: mLighting.SetAttribute(aIndex, aValue)) { michael@0: Invalidate(); michael@0: return; michael@0: } michael@0: switch (aIndex) { michael@0: case ATT_LIGHTING_SURFACE_SCALE: michael@0: mSurfaceScale = aValue; michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: template michael@0: void michael@0: FilterNodeLightingSoftware::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_LIGHTING_KERNEL_UNIT_LENGTH: michael@0: mKernelUnitLength = aKernelUnitLength; michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: Invalidate(); michael@0: } michael@0: michael@0: template michael@0: void michael@0: FilterNodeLightingSoftware::SetAttribute(uint32_t aIndex, const Color &aColor) michael@0: { michael@0: MOZ_ASSERT(aIndex == ATT_LIGHTING_COLOR); michael@0: mColor = aColor; michael@0: Invalidate(); michael@0: } michael@0: michael@0: template michael@0: IntRect michael@0: FilterNodeLightingSoftware::GetOutputRectInRect(const IntRect& aRect) michael@0: { michael@0: return GetInputRectInRect(IN_LIGHTING_IN, aRect); michael@0: } michael@0: michael@0: Point3D michael@0: PointLightSoftware::GetVectorToLight(const Point3D &aTargetPoint) michael@0: { michael@0: return Normalized(mPosition - aTargetPoint); michael@0: } michael@0: michael@0: uint32_t michael@0: PointLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight) michael@0: { michael@0: return aLightColor; michael@0: } michael@0: michael@0: void michael@0: SpotLightSoftware::Prepare() michael@0: { michael@0: mVectorFromFocusPointToLight = Normalized(mPointsAt - mPosition); michael@0: mLimitingConeCos = std::max(cos(mLimitingConeAngle * M_PI/180.0), 0.0); michael@0: mPowCache.CacheForExponent(mSpecularFocus); michael@0: } michael@0: michael@0: Point3D michael@0: SpotLightSoftware::GetVectorToLight(const Point3D &aTargetPoint) michael@0: { michael@0: return Normalized(mPosition - aTargetPoint); michael@0: } michael@0: michael@0: uint32_t michael@0: SpotLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight) michael@0: { michael@0: union { michael@0: uint32_t color; michael@0: uint8_t colorC[4]; michael@0: }; michael@0: color = aLightColor; michael@0: Float dot = -aVectorToLight.DotProduct(mVectorFromFocusPointToLight); michael@0: uint16_t doti = dot * (dot >= 0) * (1 << PowCache::sInputIntPrecisionBits); michael@0: uint32_t tmp = mPowCache.Pow(doti) * (dot >= mLimitingConeCos); michael@0: MOZ_ASSERT(tmp <= (1 << PowCache::sOutputIntPrecisionBits), "pow() result must not exceed 1.0"); michael@0: colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] * tmp) >> PowCache::sOutputIntPrecisionBits); michael@0: colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] * tmp) >> PowCache::sOutputIntPrecisionBits); michael@0: colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] * tmp) >> PowCache::sOutputIntPrecisionBits); michael@0: colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255; michael@0: return color; michael@0: } michael@0: michael@0: void michael@0: DistantLightSoftware::Prepare() michael@0: { michael@0: const double radPerDeg = M_PI / 180.0; michael@0: mVectorToLight.x = cos(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg); michael@0: mVectorToLight.y = sin(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg); michael@0: mVectorToLight.z = sin(mElevation * radPerDeg); michael@0: } michael@0: michael@0: Point3D michael@0: DistantLightSoftware::GetVectorToLight(const Point3D &aTargetPoint) michael@0: { michael@0: return mVectorToLight; michael@0: } michael@0: michael@0: uint32_t michael@0: DistantLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight) michael@0: { michael@0: return aLightColor; michael@0: } michael@0: michael@0: template michael@0: static Point3D michael@0: GenerateNormal(const uint8_t *data, int32_t stride, michael@0: int32_t x, int32_t y, float surfaceScale, michael@0: CoordType dx, CoordType dy) michael@0: { michael@0: const uint8_t *index = data + y * stride + x; michael@0: michael@0: CoordType zero = 0; michael@0: michael@0: // See this for source of constants: michael@0: // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement michael@0: int16_t normalX = michael@0: -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) + michael@0: 1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) + michael@0: -2 * ColorComponentAtPoint(index, stride, -dx, zero, 1, 0) + michael@0: 2 * ColorComponentAtPoint(index, stride, dx, zero, 1, 0) + michael@0: -1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) + michael@0: 1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0); michael@0: michael@0: int16_t normalY = michael@0: -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) + michael@0: -2 * ColorComponentAtPoint(index, stride, zero, -dy, 1, 0) + michael@0: -1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) + michael@0: 1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) + michael@0: 2 * ColorComponentAtPoint(index, stride, zero, dy, 1, 0) + michael@0: 1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0); michael@0: michael@0: Point3D normal; michael@0: normal.x = -surfaceScale * normalX / 4.0f; michael@0: normal.y = -surfaceScale * normalY / 4.0f; michael@0: normal.z = 255; michael@0: return Normalized(normal); michael@0: } michael@0: michael@0: template michael@0: TemporaryRef michael@0: FilterNodeLightingSoftware::Render(const IntRect& aRect) michael@0: { michael@0: if (mKernelUnitLength.width == floor(mKernelUnitLength.width) && michael@0: mKernelUnitLength.height == floor(mKernelUnitLength.height)) { michael@0: return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height); michael@0: } michael@0: return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height); michael@0: } michael@0: michael@0: template michael@0: void michael@0: FilterNodeLightingSoftware::RequestFromInputsForRect(const IntRect &aRect) michael@0: { michael@0: IntRect srcRect = aRect; michael@0: srcRect.Inflate(ceil(mKernelUnitLength.width), michael@0: ceil(mKernelUnitLength.height)); michael@0: RequestInputRect(IN_LIGHTING_IN, srcRect); michael@0: } michael@0: michael@0: template template michael@0: TemporaryRef michael@0: FilterNodeLightingSoftware::DoRender(const IntRect& aRect, michael@0: CoordType aKernelUnitLengthX, michael@0: CoordType aKernelUnitLengthY) michael@0: { michael@0: IntRect srcRect = aRect; michael@0: IntSize size = aRect.Size(); michael@0: srcRect.Inflate(ceil(float(aKernelUnitLengthX)), michael@0: ceil(float(aKernelUnitLengthY))); michael@0: michael@0: // Inflate the source rect by another pixel because the bilinear filtering in michael@0: // ColorComponentAtPoint may want to access the margins. michael@0: srcRect.Inflate(1); michael@0: michael@0: RefPtr input = michael@0: GetInputDataSourceSurface(IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8, michael@0: EDGE_MODE_DUPLICATE); michael@0: michael@0: if (!input) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (input->GetFormat() != SurfaceFormat::A8) { michael@0: input = FilterProcessing::ExtractAlpha(input); michael@0: } michael@0: michael@0: DebugOnlyAutoColorSamplingAccessControl accessControl(input); michael@0: michael@0: RefPtr target = michael@0: Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); michael@0: if (!target) { michael@0: return nullptr; michael@0: } michael@0: michael@0: IntPoint offset = aRect.TopLeft() - srcRect.TopLeft(); michael@0: michael@0: uint8_t* sourceData = DataAtOffset(input, offset); michael@0: int32_t sourceStride = input->Stride(); michael@0: uint8_t* targetData = target->GetData(); michael@0: int32_t targetStride = target->Stride(); michael@0: michael@0: uint32_t lightColor = ColorToBGRA(mColor); michael@0: mLight.Prepare(); michael@0: mLighting.Prepare(); michael@0: michael@0: for (int32_t y = 0; y < size.height; y++) { michael@0: for (int32_t x = 0; x < size.width; x++) { michael@0: int32_t sourceIndex = y * sourceStride + x; michael@0: int32_t targetIndex = y * targetStride + 4 * x; michael@0: michael@0: Point3D normal = GenerateNormal(sourceData, sourceStride, michael@0: x, y, mSurfaceScale, michael@0: aKernelUnitLengthX, aKernelUnitLengthY); michael@0: michael@0: IntPoint pointInFilterSpace(aRect.x + x, aRect.y + y); michael@0: Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f; michael@0: Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z); michael@0: Point3D rayDir = mLight.GetVectorToLight(pt); michael@0: uint32_t color = mLight.GetColor(lightColor, rayDir); michael@0: michael@0: *(uint32_t*)(targetData + targetIndex) = mLighting.LightPixel(normal, rayDir, color); michael@0: } michael@0: } michael@0: michael@0: return target; michael@0: } michael@0: michael@0: DiffuseLightingSoftware::DiffuseLightingSoftware() michael@0: : mDiffuseConstant(0) michael@0: { michael@0: } michael@0: michael@0: bool michael@0: DiffuseLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT: michael@0: mDiffuseConstant = aValue; michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: uint32_t michael@0: DiffuseLightingSoftware::LightPixel(const Point3D &aNormal, michael@0: const Point3D &aVectorToLight, michael@0: uint32_t aColor) michael@0: { michael@0: Float dotNL = std::max(0.0f, aNormal.DotProduct(aVectorToLight)); michael@0: Float diffuseNL = mDiffuseConstant * dotNL; michael@0: michael@0: union { michael@0: uint32_t bgra; michael@0: uint8_t components[4]; michael@0: } color = { aColor }; michael@0: color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = michael@0: umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]), 255U); michael@0: color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = michael@0: umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]), 255U); michael@0: color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = michael@0: umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]), 255U); michael@0: color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255; michael@0: return color.bgra; michael@0: } michael@0: michael@0: SpecularLightingSoftware::SpecularLightingSoftware() michael@0: : mSpecularConstant(0) michael@0: , mSpecularExponent(0) michael@0: { michael@0: } michael@0: michael@0: bool michael@0: SpecularLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) michael@0: { michael@0: switch (aIndex) { michael@0: case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT: michael@0: mSpecularConstant = std::min(std::max(aValue, 0.0f), 255.0f); michael@0: break; michael@0: case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT: michael@0: mSpecularExponent = std::min(std::max(aValue, 1.0f), 128.0f); michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: SpecularLightingSoftware::Prepare() michael@0: { michael@0: mPowCache.CacheForExponent(mSpecularExponent); michael@0: mSpecularConstantInt = uint32_t(mSpecularConstant * (1 << 8)); michael@0: } michael@0: michael@0: uint32_t michael@0: SpecularLightingSoftware::LightPixel(const Point3D &aNormal, michael@0: const Point3D &aVectorToLight, michael@0: uint32_t aColor) michael@0: { michael@0: Point3D vectorToEye(0, 0, 1); michael@0: Point3D halfwayVector = Normalized(aVectorToLight + vectorToEye); michael@0: Float dotNH = aNormal.DotProduct(halfwayVector); michael@0: uint16_t dotNHi = uint16_t(dotNH * (dotNH >= 0) * (1 << PowCache::sInputIntPrecisionBits)); michael@0: uint32_t specularNHi = uint32_t(mSpecularConstantInt) * mPowCache.Pow(dotNHi) >> 8; michael@0: michael@0: union { michael@0: uint32_t bgra; michael@0: uint8_t components[4]; michael@0: } color = { aColor }; michael@0: color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = michael@0: umin( michael@0: (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]) >> PowCache::sOutputIntPrecisionBits, 255U); michael@0: color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = michael@0: umin( michael@0: (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) >> PowCache::sOutputIntPrecisionBits, 255U); michael@0: color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = michael@0: umin( michael@0: (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) >> PowCache::sOutputIntPrecisionBits, 255U); michael@0: michael@0: color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = michael@0: umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B], michael@0: umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G], michael@0: color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R])); michael@0: return color.bgra; michael@0: } michael@0: michael@0: } // namespace gfx michael@0: } // namespace mozilla