Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
michael@0 | 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #define _USE_MATH_DEFINES |
michael@0 | 7 | |
michael@0 | 8 | #include <cmath> |
michael@0 | 9 | #include "FilterNodeSoftware.h" |
michael@0 | 10 | #include "2D.h" |
michael@0 | 11 | #include "Tools.h" |
michael@0 | 12 | #include "Blur.h" |
michael@0 | 13 | #include <map> |
michael@0 | 14 | #include "FilterProcessing.h" |
michael@0 | 15 | #include "mozilla/PodOperations.h" |
michael@0 | 16 | #include "mozilla/DebugOnly.h" |
michael@0 | 17 | |
michael@0 | 18 | // #define DEBUG_DUMP_SURFACES |
michael@0 | 19 | |
michael@0 | 20 | #ifdef DEBUG_DUMP_SURFACES |
michael@0 | 21 | #include "gfxImageSurface.h" |
michael@0 | 22 | namespace mozilla { |
michael@0 | 23 | namespace gfx { |
michael@0 | 24 | static void |
michael@0 | 25 | DumpAsPNG(SourceSurface* aSurface) |
michael@0 | 26 | { |
michael@0 | 27 | RefPtr<DataSourceSurface> dataSource = aSurface->GetDataSurface(); |
michael@0 | 28 | IntSize size = dataSource->GetSize(); |
michael@0 | 29 | nsRefPtr<gfxImageSurface> imageSurface = |
michael@0 | 30 | new gfxImageSurface(dataSource->GetData(), gfxIntSize(size.width, size.height), |
michael@0 | 31 | dataSource->Stride(), |
michael@0 | 32 | aSurface->GetFormat() == SurfaceFormat::A8 ? gfxImageFormat::A8 : gfxImageFormat::ARGB32); |
michael@0 | 33 | imageSurface->PrintAsDataURL(); |
michael@0 | 34 | } |
michael@0 | 35 | } // namespace gfx |
michael@0 | 36 | } // namespace mozilla |
michael@0 | 37 | #endif |
michael@0 | 38 | |
michael@0 | 39 | namespace mozilla { |
michael@0 | 40 | namespace gfx { |
michael@0 | 41 | |
michael@0 | 42 | namespace { |
michael@0 | 43 | |
michael@0 | 44 | /** |
michael@0 | 45 | * This class provides a way to get a pow() results in constant-time. It works |
michael@0 | 46 | * by caching 256 values for bases between 0 and 1 and a fixed exponent. |
michael@0 | 47 | **/ |
michael@0 | 48 | class PowCache |
michael@0 | 49 | { |
michael@0 | 50 | public: |
michael@0 | 51 | PowCache() |
michael@0 | 52 | { |
michael@0 | 53 | CacheForExponent(0.0f); |
michael@0 | 54 | } |
michael@0 | 55 | |
michael@0 | 56 | void CacheForExponent(Float aExponent) |
michael@0 | 57 | { |
michael@0 | 58 | mExponent = aExponent; |
michael@0 | 59 | int numPreSquares = 0; |
michael@0 | 60 | while (numPreSquares < 5 && mExponent > (1 << (numPreSquares + 2))) { |
michael@0 | 61 | numPreSquares++; |
michael@0 | 62 | } |
michael@0 | 63 | mNumPowTablePreSquares = numPreSquares; |
michael@0 | 64 | for (size_t i = 0; i < sCacheSize; i++) { |
michael@0 | 65 | // sCacheSize is chosen in such a way that a takes values |
michael@0 | 66 | // from 0.0 to 1.0 inclusive. |
michael@0 | 67 | Float a = i / Float(1 << sCacheIndexPrecisionBits); |
michael@0 | 68 | MOZ_ASSERT(0.0f <= a && a <= 1.0f, "We only want to cache for bases between 0 and 1."); |
michael@0 | 69 | |
michael@0 | 70 | for (int j = 0; j < mNumPowTablePreSquares; j++) { |
michael@0 | 71 | a = sqrt(a); |
michael@0 | 72 | } |
michael@0 | 73 | uint32_t cachedInt = pow(a, mExponent) * (1 << sOutputIntPrecisionBits); |
michael@0 | 74 | MOZ_ASSERT(cachedInt < (1 << (sizeof(mPowTable[i]) * 8)), "mPowCache integer type too small"); |
michael@0 | 75 | |
michael@0 | 76 | mPowTable[i] = cachedInt; |
michael@0 | 77 | } |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | uint16_t Pow(uint16_t aBase) |
michael@0 | 81 | { |
michael@0 | 82 | // Results should be similar to what the following code would produce: |
michael@0 | 83 | // Float x = Float(aBase) / (1 << sInputIntPrecisionBits); |
michael@0 | 84 | // return uint16_t(pow(x, mExponent) * (1 << sOutputIntPrecisionBits)); |
michael@0 | 85 | |
michael@0 | 86 | MOZ_ASSERT(aBase <= (1 << sInputIntPrecisionBits), "aBase needs to be between 0 and 1!"); |
michael@0 | 87 | |
michael@0 | 88 | uint32_t a = aBase; |
michael@0 | 89 | for (int j = 0; j < mNumPowTablePreSquares; j++) { |
michael@0 | 90 | a = a * a >> sInputIntPrecisionBits; |
michael@0 | 91 | } |
michael@0 | 92 | uint32_t i = a >> (sInputIntPrecisionBits - sCacheIndexPrecisionBits); |
michael@0 | 93 | MOZ_ASSERT(i < sCacheSize, "out-of-bounds mPowTable access"); |
michael@0 | 94 | return mPowTable[i]; |
michael@0 | 95 | } |
michael@0 | 96 | |
michael@0 | 97 | static const int sInputIntPrecisionBits = 15; |
michael@0 | 98 | static const int sOutputIntPrecisionBits = 15; |
michael@0 | 99 | static const int sCacheIndexPrecisionBits = 7; |
michael@0 | 100 | |
michael@0 | 101 | private: |
michael@0 | 102 | static const size_t sCacheSize = (1 << sCacheIndexPrecisionBits) + 1; |
michael@0 | 103 | |
michael@0 | 104 | Float mExponent; |
michael@0 | 105 | int mNumPowTablePreSquares; |
michael@0 | 106 | uint16_t mPowTable[sCacheSize]; |
michael@0 | 107 | }; |
michael@0 | 108 | |
michael@0 | 109 | class PointLightSoftware |
michael@0 | 110 | { |
michael@0 | 111 | public: |
michael@0 | 112 | bool SetAttribute(uint32_t aIndex, Float) { return false; } |
michael@0 | 113 | bool SetAttribute(uint32_t aIndex, const Point3D &); |
michael@0 | 114 | void Prepare() {} |
michael@0 | 115 | Point3D GetVectorToLight(const Point3D &aTargetPoint); |
michael@0 | 116 | uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight); |
michael@0 | 117 | |
michael@0 | 118 | private: |
michael@0 | 119 | Point3D mPosition; |
michael@0 | 120 | }; |
michael@0 | 121 | |
michael@0 | 122 | class SpotLightSoftware |
michael@0 | 123 | { |
michael@0 | 124 | public: |
michael@0 | 125 | SpotLightSoftware(); |
michael@0 | 126 | bool SetAttribute(uint32_t aIndex, Float); |
michael@0 | 127 | bool SetAttribute(uint32_t aIndex, const Point3D &); |
michael@0 | 128 | void Prepare(); |
michael@0 | 129 | Point3D GetVectorToLight(const Point3D &aTargetPoint); |
michael@0 | 130 | uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight); |
michael@0 | 131 | |
michael@0 | 132 | private: |
michael@0 | 133 | Point3D mPosition; |
michael@0 | 134 | Point3D mPointsAt; |
michael@0 | 135 | Point3D mVectorFromFocusPointToLight; |
michael@0 | 136 | Float mSpecularFocus; |
michael@0 | 137 | Float mLimitingConeAngle; |
michael@0 | 138 | Float mLimitingConeCos; |
michael@0 | 139 | PowCache mPowCache; |
michael@0 | 140 | }; |
michael@0 | 141 | |
michael@0 | 142 | class DistantLightSoftware |
michael@0 | 143 | { |
michael@0 | 144 | public: |
michael@0 | 145 | DistantLightSoftware(); |
michael@0 | 146 | bool SetAttribute(uint32_t aIndex, Float); |
michael@0 | 147 | bool SetAttribute(uint32_t aIndex, const Point3D &) { return false; } |
michael@0 | 148 | void Prepare(); |
michael@0 | 149 | Point3D GetVectorToLight(const Point3D &aTargetPoint); |
michael@0 | 150 | uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight); |
michael@0 | 151 | |
michael@0 | 152 | private: |
michael@0 | 153 | Float mAzimuth; |
michael@0 | 154 | Float mElevation; |
michael@0 | 155 | Point3D mVectorToLight; |
michael@0 | 156 | }; |
michael@0 | 157 | |
michael@0 | 158 | class DiffuseLightingSoftware |
michael@0 | 159 | { |
michael@0 | 160 | public: |
michael@0 | 161 | DiffuseLightingSoftware(); |
michael@0 | 162 | bool SetAttribute(uint32_t aIndex, Float); |
michael@0 | 163 | void Prepare() {} |
michael@0 | 164 | uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight, |
michael@0 | 165 | uint32_t aColor); |
michael@0 | 166 | |
michael@0 | 167 | private: |
michael@0 | 168 | Float mDiffuseConstant; |
michael@0 | 169 | }; |
michael@0 | 170 | |
michael@0 | 171 | class SpecularLightingSoftware |
michael@0 | 172 | { |
michael@0 | 173 | public: |
michael@0 | 174 | SpecularLightingSoftware(); |
michael@0 | 175 | bool SetAttribute(uint32_t aIndex, Float); |
michael@0 | 176 | void Prepare(); |
michael@0 | 177 | uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight, |
michael@0 | 178 | uint32_t aColor); |
michael@0 | 179 | |
michael@0 | 180 | private: |
michael@0 | 181 | Float mSpecularConstant; |
michael@0 | 182 | Float mSpecularExponent; |
michael@0 | 183 | uint32_t mSpecularConstantInt; |
michael@0 | 184 | PowCache mPowCache; |
michael@0 | 185 | }; |
michael@0 | 186 | |
michael@0 | 187 | } // unnamed namespace |
michael@0 | 188 | |
michael@0 | 189 | // from xpcom/ds/nsMathUtils.h |
michael@0 | 190 | static int32_t |
michael@0 | 191 | NS_lround(double x) |
michael@0 | 192 | { |
michael@0 | 193 | return x >= 0.0 ? int32_t(x + 0.5) : int32_t(x - 0.5); |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | void |
michael@0 | 197 | ClearDataSourceSurface(DataSourceSurface *aSurface) |
michael@0 | 198 | { |
michael@0 | 199 | size_t numBytes = aSurface->GetSize().height * aSurface->Stride(); |
michael@0 | 200 | uint8_t* data = aSurface->GetData(); |
michael@0 | 201 | PodZero(data, numBytes); |
michael@0 | 202 | } |
michael@0 | 203 | |
michael@0 | 204 | // This check is safe against integer overflow. |
michael@0 | 205 | static bool |
michael@0 | 206 | SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint) |
michael@0 | 207 | { |
michael@0 | 208 | IntSize size = aSurface->GetSize(); |
michael@0 | 209 | return aPoint.x >= 0 && aPoint.x < size.width && |
michael@0 | 210 | aPoint.y >= 0 && aPoint.y < size.height; |
michael@0 | 211 | } |
michael@0 | 212 | |
michael@0 | 213 | static uint8_t* |
michael@0 | 214 | DataAtOffset(DataSourceSurface* aSurface, IntPoint aPoint) |
michael@0 | 215 | { |
michael@0 | 216 | if (!SurfaceContainsPoint(aSurface, aPoint)) { |
michael@0 | 217 | MOZ_CRASH("sample position needs to be inside surface!"); |
michael@0 | 218 | } |
michael@0 | 219 | |
michael@0 | 220 | MOZ_ASSERT(Factory::CheckSurfaceSize(aSurface->GetSize()), |
michael@0 | 221 | "surface size overflows - this should have been prevented when the surface was created"); |
michael@0 | 222 | |
michael@0 | 223 | uint8_t* data = aSurface->GetData() + aPoint.y * aSurface->Stride() + |
michael@0 | 224 | aPoint.x * BytesPerPixel(aSurface->GetFormat()); |
michael@0 | 225 | |
michael@0 | 226 | if (data < aSurface->GetData()) { |
michael@0 | 227 | MOZ_CRASH("out-of-range data access"); |
michael@0 | 228 | } |
michael@0 | 229 | |
michael@0 | 230 | return data; |
michael@0 | 231 | } |
michael@0 | 232 | |
michael@0 | 233 | static bool |
michael@0 | 234 | IntRectOverflows(const IntRect& aRect) |
michael@0 | 235 | { |
michael@0 | 236 | CheckedInt<int32_t> xMost = aRect.x; |
michael@0 | 237 | xMost += aRect.width; |
michael@0 | 238 | CheckedInt<int32_t> yMost = aRect.y; |
michael@0 | 239 | yMost += aRect.height; |
michael@0 | 240 | return !xMost.isValid() || !yMost.isValid(); |
michael@0 | 241 | } |
michael@0 | 242 | |
michael@0 | 243 | /** |
michael@0 | 244 | * aSrcRect: Rect relative to the aSrc surface |
michael@0 | 245 | * aDestPoint: Point inside aDest surface |
michael@0 | 246 | */ |
michael@0 | 247 | static void |
michael@0 | 248 | CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest, |
michael@0 | 249 | IntRect aSrcRect, IntPoint aDestPoint) |
michael@0 | 250 | { |
michael@0 | 251 | if (IntRectOverflows(aSrcRect) || |
michael@0 | 252 | IntRectOverflows(IntRect(aDestPoint, aSrcRect.Size()))) { |
michael@0 | 253 | MOZ_CRASH("we should never be getting invalid rects at this point"); |
michael@0 | 254 | } |
michael@0 | 255 | |
michael@0 | 256 | MOZ_ASSERT(aSrc->GetFormat() == aDest->GetFormat(), "different surface formats"); |
michael@0 | 257 | MOZ_ASSERT(IntRect(IntPoint(), aSrc->GetSize()).Contains(aSrcRect), "source rect too big for source surface"); |
michael@0 | 258 | MOZ_ASSERT(IntRect(IntPoint(), aDest->GetSize()).Contains(aSrcRect - aSrcRect.TopLeft() + aDestPoint), "dest surface too small"); |
michael@0 | 259 | |
michael@0 | 260 | if (aSrcRect.IsEmpty()) { |
michael@0 | 261 | return; |
michael@0 | 262 | } |
michael@0 | 263 | |
michael@0 | 264 | uint8_t* sourceData = DataAtOffset(aSrc, aSrcRect.TopLeft()); |
michael@0 | 265 | uint32_t sourceStride = aSrc->Stride(); |
michael@0 | 266 | uint8_t* destData = DataAtOffset(aDest, aDestPoint); |
michael@0 | 267 | uint32_t destStride = aDest->Stride(); |
michael@0 | 268 | |
michael@0 | 269 | if (BytesPerPixel(aSrc->GetFormat()) == 4) { |
michael@0 | 270 | for (int32_t y = 0; y < aSrcRect.height; y++) { |
michael@0 | 271 | PodCopy((int32_t*)destData, (int32_t*)sourceData, aSrcRect.width); |
michael@0 | 272 | sourceData += sourceStride; |
michael@0 | 273 | destData += destStride; |
michael@0 | 274 | } |
michael@0 | 275 | } else if (BytesPerPixel(aSrc->GetFormat()) == 1) { |
michael@0 | 276 | for (int32_t y = 0; y < aSrcRect.height; y++) { |
michael@0 | 277 | PodCopy(destData, sourceData, aSrcRect.width); |
michael@0 | 278 | sourceData += sourceStride; |
michael@0 | 279 | destData += destStride; |
michael@0 | 280 | } |
michael@0 | 281 | } |
michael@0 | 282 | } |
michael@0 | 283 | |
michael@0 | 284 | TemporaryRef<DataSourceSurface> |
michael@0 | 285 | CloneAligned(DataSourceSurface* aSource) |
michael@0 | 286 | { |
michael@0 | 287 | RefPtr<DataSourceSurface> copy = |
michael@0 | 288 | Factory::CreateDataSourceSurface(aSource->GetSize(), aSource->GetFormat()); |
michael@0 | 289 | if (copy) { |
michael@0 | 290 | CopyRect(aSource, copy, IntRect(IntPoint(), aSource->GetSize()), IntPoint()); |
michael@0 | 291 | } |
michael@0 | 292 | return copy; |
michael@0 | 293 | } |
michael@0 | 294 | |
michael@0 | 295 | static void |
michael@0 | 296 | FillRectWithPixel(DataSourceSurface *aSurface, const IntRect &aFillRect, IntPoint aPixelPos) |
michael@0 | 297 | { |
michael@0 | 298 | MOZ_ASSERT(!IntRectOverflows(aFillRect)); |
michael@0 | 299 | MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect), |
michael@0 | 300 | "aFillRect needs to be completely inside the surface"); |
michael@0 | 301 | MOZ_ASSERT(SurfaceContainsPoint(aSurface, aPixelPos), |
michael@0 | 302 | "aPixelPos needs to be inside the surface"); |
michael@0 | 303 | |
michael@0 | 304 | int32_t stride = aSurface->Stride(); |
michael@0 | 305 | uint8_t* sourcePixelData = DataAtOffset(aSurface, aPixelPos); |
michael@0 | 306 | uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft()); |
michael@0 | 307 | int bpp = BytesPerPixel(aSurface->GetFormat()); |
michael@0 | 308 | |
michael@0 | 309 | // Fill the first row by hand. |
michael@0 | 310 | if (bpp == 4) { |
michael@0 | 311 | uint32_t sourcePixel = *(uint32_t*)sourcePixelData; |
michael@0 | 312 | for (int32_t x = 0; x < aFillRect.width; x++) { |
michael@0 | 313 | *((uint32_t*)data + x) = sourcePixel; |
michael@0 | 314 | } |
michael@0 | 315 | } else if (BytesPerPixel(aSurface->GetFormat()) == 1) { |
michael@0 | 316 | uint8_t sourcePixel = *sourcePixelData; |
michael@0 | 317 | memset(data, sourcePixel, aFillRect.width); |
michael@0 | 318 | } |
michael@0 | 319 | |
michael@0 | 320 | // Copy the first row into the other rows. |
michael@0 | 321 | for (int32_t y = 1; y < aFillRect.height; y++) { |
michael@0 | 322 | PodCopy(data + y * stride, data, aFillRect.width * bpp); |
michael@0 | 323 | } |
michael@0 | 324 | } |
michael@0 | 325 | |
michael@0 | 326 | static void |
michael@0 | 327 | FillRectWithVerticallyRepeatingHorizontalStrip(DataSourceSurface *aSurface, |
michael@0 | 328 | const IntRect &aFillRect, |
michael@0 | 329 | const IntRect &aSampleRect) |
michael@0 | 330 | { |
michael@0 | 331 | MOZ_ASSERT(!IntRectOverflows(aFillRect)); |
michael@0 | 332 | MOZ_ASSERT(!IntRectOverflows(aSampleRect)); |
michael@0 | 333 | MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect), |
michael@0 | 334 | "aFillRect needs to be completely inside the surface"); |
michael@0 | 335 | MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect), |
michael@0 | 336 | "aSampleRect needs to be completely inside the surface"); |
michael@0 | 337 | |
michael@0 | 338 | int32_t stride = aSurface->Stride(); |
michael@0 | 339 | uint8_t* sampleData = DataAtOffset(aSurface, aSampleRect.TopLeft()); |
michael@0 | 340 | uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft()); |
michael@0 | 341 | if (BytesPerPixel(aSurface->GetFormat()) == 4) { |
michael@0 | 342 | for (int32_t y = 0; y < aFillRect.height; y++) { |
michael@0 | 343 | PodCopy((uint32_t*)data, (uint32_t*)sampleData, aFillRect.width); |
michael@0 | 344 | data += stride; |
michael@0 | 345 | } |
michael@0 | 346 | } else if (BytesPerPixel(aSurface->GetFormat()) == 1) { |
michael@0 | 347 | for (int32_t y = 0; y < aFillRect.height; y++) { |
michael@0 | 348 | PodCopy(data, sampleData, aFillRect.width); |
michael@0 | 349 | data += stride; |
michael@0 | 350 | } |
michael@0 | 351 | } |
michael@0 | 352 | } |
michael@0 | 353 | |
michael@0 | 354 | static void |
michael@0 | 355 | FillRectWithHorizontallyRepeatingVerticalStrip(DataSourceSurface *aSurface, |
michael@0 | 356 | const IntRect &aFillRect, |
michael@0 | 357 | const IntRect &aSampleRect) |
michael@0 | 358 | { |
michael@0 | 359 | MOZ_ASSERT(!IntRectOverflows(aFillRect)); |
michael@0 | 360 | MOZ_ASSERT(!IntRectOverflows(aSampleRect)); |
michael@0 | 361 | MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect), |
michael@0 | 362 | "aFillRect needs to be completely inside the surface"); |
michael@0 | 363 | MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect), |
michael@0 | 364 | "aSampleRect needs to be completely inside the surface"); |
michael@0 | 365 | |
michael@0 | 366 | int32_t stride = aSurface->Stride(); |
michael@0 | 367 | uint8_t* sampleData = DataAtOffset(aSurface, aSampleRect.TopLeft()); |
michael@0 | 368 | uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft()); |
michael@0 | 369 | if (BytesPerPixel(aSurface->GetFormat()) == 4) { |
michael@0 | 370 | for (int32_t y = 0; y < aFillRect.height; y++) { |
michael@0 | 371 | int32_t sampleColor = *((uint32_t*)sampleData); |
michael@0 | 372 | for (int32_t x = 0; x < aFillRect.width; x++) { |
michael@0 | 373 | *((uint32_t*)data + x) = sampleColor; |
michael@0 | 374 | } |
michael@0 | 375 | data += stride; |
michael@0 | 376 | sampleData += stride; |
michael@0 | 377 | } |
michael@0 | 378 | } else if (BytesPerPixel(aSurface->GetFormat()) == 1) { |
michael@0 | 379 | for (int32_t y = 0; y < aFillRect.height; y++) { |
michael@0 | 380 | uint8_t sampleColor = *sampleData; |
michael@0 | 381 | memset(data, sampleColor, aFillRect.width); |
michael@0 | 382 | data += stride; |
michael@0 | 383 | sampleData += stride; |
michael@0 | 384 | } |
michael@0 | 385 | } |
michael@0 | 386 | } |
michael@0 | 387 | |
michael@0 | 388 | static void |
michael@0 | 389 | DuplicateEdges(DataSourceSurface* aSurface, const IntRect &aFromRect) |
michael@0 | 390 | { |
michael@0 | 391 | MOZ_ASSERT(!IntRectOverflows(aFromRect)); |
michael@0 | 392 | MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFromRect), |
michael@0 | 393 | "aFromRect needs to be completely inside the surface"); |
michael@0 | 394 | |
michael@0 | 395 | IntSize size = aSurface->GetSize(); |
michael@0 | 396 | IntRect fill; |
michael@0 | 397 | IntRect sampleRect; |
michael@0 | 398 | for (int32_t ix = 0; ix < 3; ix++) { |
michael@0 | 399 | switch (ix) { |
michael@0 | 400 | case 0: |
michael@0 | 401 | fill.x = 0; |
michael@0 | 402 | fill.width = aFromRect.x; |
michael@0 | 403 | sampleRect.x = fill.XMost(); |
michael@0 | 404 | sampleRect.width = 1; |
michael@0 | 405 | break; |
michael@0 | 406 | case 1: |
michael@0 | 407 | fill.x = aFromRect.x; |
michael@0 | 408 | fill.width = aFromRect.width; |
michael@0 | 409 | sampleRect.x = fill.x; |
michael@0 | 410 | sampleRect.width = fill.width; |
michael@0 | 411 | break; |
michael@0 | 412 | case 2: |
michael@0 | 413 | fill.x = aFromRect.XMost(); |
michael@0 | 414 | fill.width = size.width - fill.x; |
michael@0 | 415 | sampleRect.x = fill.x - 1; |
michael@0 | 416 | sampleRect.width = 1; |
michael@0 | 417 | break; |
michael@0 | 418 | } |
michael@0 | 419 | if (fill.width <= 0) { |
michael@0 | 420 | continue; |
michael@0 | 421 | } |
michael@0 | 422 | bool xIsMiddle = (ix == 1); |
michael@0 | 423 | for (int32_t iy = 0; iy < 3; iy++) { |
michael@0 | 424 | switch (iy) { |
michael@0 | 425 | case 0: |
michael@0 | 426 | fill.y = 0; |
michael@0 | 427 | fill.height = aFromRect.y; |
michael@0 | 428 | sampleRect.y = fill.YMost(); |
michael@0 | 429 | sampleRect.height = 1; |
michael@0 | 430 | break; |
michael@0 | 431 | case 1: |
michael@0 | 432 | fill.y = aFromRect.y; |
michael@0 | 433 | fill.height = aFromRect.height; |
michael@0 | 434 | sampleRect.y = fill.y; |
michael@0 | 435 | sampleRect.height = fill.height; |
michael@0 | 436 | break; |
michael@0 | 437 | case 2: |
michael@0 | 438 | fill.y = aFromRect.YMost(); |
michael@0 | 439 | fill.height = size.height - fill.y; |
michael@0 | 440 | sampleRect.y = fill.y - 1; |
michael@0 | 441 | sampleRect.height = 1; |
michael@0 | 442 | break; |
michael@0 | 443 | } |
michael@0 | 444 | if (fill.height <= 0) { |
michael@0 | 445 | continue; |
michael@0 | 446 | } |
michael@0 | 447 | bool yIsMiddle = (iy == 1); |
michael@0 | 448 | if (!xIsMiddle && !yIsMiddle) { |
michael@0 | 449 | // Corner |
michael@0 | 450 | FillRectWithPixel(aSurface, fill, sampleRect.TopLeft()); |
michael@0 | 451 | } |
michael@0 | 452 | if (xIsMiddle && !yIsMiddle) { |
michael@0 | 453 | // Top middle or bottom middle |
michael@0 | 454 | FillRectWithVerticallyRepeatingHorizontalStrip(aSurface, fill, sampleRect); |
michael@0 | 455 | } |
michael@0 | 456 | if (!xIsMiddle && yIsMiddle) { |
michael@0 | 457 | // Left middle or right middle |
michael@0 | 458 | FillRectWithHorizontallyRepeatingVerticalStrip(aSurface, fill, sampleRect); |
michael@0 | 459 | } |
michael@0 | 460 | } |
michael@0 | 461 | } |
michael@0 | 462 | } |
michael@0 | 463 | |
michael@0 | 464 | static IntPoint |
michael@0 | 465 | TileIndex(const IntRect &aFirstTileRect, const IntPoint &aPoint) |
michael@0 | 466 | { |
michael@0 | 467 | return IntPoint(int32_t(floor(double(aPoint.x - aFirstTileRect.x) / aFirstTileRect.width)), |
michael@0 | 468 | int32_t(floor(double(aPoint.y - aFirstTileRect.y) / aFirstTileRect.height))); |
michael@0 | 469 | } |
michael@0 | 470 | |
michael@0 | 471 | static void |
michael@0 | 472 | TileSurface(DataSourceSurface* aSource, DataSourceSurface* aTarget, const IntPoint &aOffset) |
michael@0 | 473 | { |
michael@0 | 474 | IntRect sourceRect(aOffset, aSource->GetSize()); |
michael@0 | 475 | IntRect targetRect(IntPoint(0, 0), aTarget->GetSize()); |
michael@0 | 476 | IntPoint startIndex = TileIndex(sourceRect, targetRect.TopLeft()); |
michael@0 | 477 | IntPoint endIndex = TileIndex(sourceRect, targetRect.BottomRight()); |
michael@0 | 478 | |
michael@0 | 479 | for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) { |
michael@0 | 480 | for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) { |
michael@0 | 481 | IntPoint destPoint(sourceRect.x + ix * sourceRect.width, |
michael@0 | 482 | sourceRect.y + iy * sourceRect.height); |
michael@0 | 483 | IntRect destRect(destPoint, sourceRect.Size()); |
michael@0 | 484 | destRect = destRect.Intersect(targetRect); |
michael@0 | 485 | IntRect srcRect = destRect - destPoint; |
michael@0 | 486 | CopyRect(aSource, aTarget, srcRect, destRect.TopLeft()); |
michael@0 | 487 | } |
michael@0 | 488 | } |
michael@0 | 489 | } |
michael@0 | 490 | |
michael@0 | 491 | static TemporaryRef<DataSourceSurface> |
michael@0 | 492 | GetDataSurfaceInRect(SourceSurface *aSurface, |
michael@0 | 493 | const IntRect &aSurfaceRect, |
michael@0 | 494 | const IntRect &aDestRect, |
michael@0 | 495 | ConvolveMatrixEdgeMode aEdgeMode) |
michael@0 | 496 | { |
michael@0 | 497 | MOZ_ASSERT(aSurface ? aSurfaceRect.Size() == aSurface->GetSize() : aSurfaceRect.IsEmpty()); |
michael@0 | 498 | |
michael@0 | 499 | if (IntRectOverflows(aSurfaceRect) || IntRectOverflows(aDestRect)) { |
michael@0 | 500 | // We can't rely on the intersection calculations below to make sense when |
michael@0 | 501 | // XMost() or YMost() overflow. Bail out. |
michael@0 | 502 | return nullptr; |
michael@0 | 503 | } |
michael@0 | 504 | |
michael@0 | 505 | IntRect sourceRect = aSurfaceRect; |
michael@0 | 506 | |
michael@0 | 507 | if (sourceRect.IsEqualEdges(aDestRect)) { |
michael@0 | 508 | return aSurface ? aSurface->GetDataSurface() : nullptr; |
michael@0 | 509 | } |
michael@0 | 510 | |
michael@0 | 511 | IntRect intersect = sourceRect.Intersect(aDestRect); |
michael@0 | 512 | IntRect intersectInSourceSpace = intersect - sourceRect.TopLeft(); |
michael@0 | 513 | IntRect intersectInDestSpace = intersect - aDestRect.TopLeft(); |
michael@0 | 514 | SurfaceFormat format = aSurface ? aSurface->GetFormat() : SurfaceFormat(SurfaceFormat::B8G8R8A8); |
michael@0 | 515 | |
michael@0 | 516 | RefPtr<DataSourceSurface> target = |
michael@0 | 517 | Factory::CreateDataSourceSurface(aDestRect.Size(), format); |
michael@0 | 518 | |
michael@0 | 519 | if (!target) { |
michael@0 | 520 | return nullptr; |
michael@0 | 521 | } |
michael@0 | 522 | |
michael@0 | 523 | if (aEdgeMode == EDGE_MODE_NONE && !aSurfaceRect.Contains(aDestRect)) { |
michael@0 | 524 | ClearDataSourceSurface(target); |
michael@0 | 525 | } |
michael@0 | 526 | |
michael@0 | 527 | if (!aSurface) { |
michael@0 | 528 | return target; |
michael@0 | 529 | } |
michael@0 | 530 | |
michael@0 | 531 | RefPtr<DataSourceSurface> dataSource = aSurface->GetDataSurface(); |
michael@0 | 532 | MOZ_ASSERT(dataSource); |
michael@0 | 533 | |
michael@0 | 534 | if (aEdgeMode == EDGE_MODE_WRAP) { |
michael@0 | 535 | TileSurface(dataSource, target, intersectInDestSpace.TopLeft()); |
michael@0 | 536 | return target; |
michael@0 | 537 | } |
michael@0 | 538 | |
michael@0 | 539 | CopyRect(dataSource, target, intersectInSourceSpace, |
michael@0 | 540 | intersectInDestSpace.TopLeft()); |
michael@0 | 541 | |
michael@0 | 542 | if (aEdgeMode == EDGE_MODE_DUPLICATE) { |
michael@0 | 543 | DuplicateEdges(target, intersectInDestSpace); |
michael@0 | 544 | } |
michael@0 | 545 | |
michael@0 | 546 | return target; |
michael@0 | 547 | } |
michael@0 | 548 | |
michael@0 | 549 | /* static */ TemporaryRef<FilterNode> |
michael@0 | 550 | FilterNodeSoftware::Create(FilterType aType) |
michael@0 | 551 | { |
michael@0 | 552 | RefPtr<FilterNodeSoftware> filter; |
michael@0 | 553 | switch (aType) { |
michael@0 | 554 | case FilterType::BLEND: |
michael@0 | 555 | filter = new FilterNodeBlendSoftware(); |
michael@0 | 556 | break; |
michael@0 | 557 | case FilterType::TRANSFORM: |
michael@0 | 558 | filter = new FilterNodeTransformSoftware(); |
michael@0 | 559 | break; |
michael@0 | 560 | case FilterType::MORPHOLOGY: |
michael@0 | 561 | filter = new FilterNodeMorphologySoftware(); |
michael@0 | 562 | break; |
michael@0 | 563 | case FilterType::COLOR_MATRIX: |
michael@0 | 564 | filter = new FilterNodeColorMatrixSoftware(); |
michael@0 | 565 | break; |
michael@0 | 566 | case FilterType::FLOOD: |
michael@0 | 567 | filter = new FilterNodeFloodSoftware(); |
michael@0 | 568 | break; |
michael@0 | 569 | case FilterType::TILE: |
michael@0 | 570 | filter = new FilterNodeTileSoftware(); |
michael@0 | 571 | break; |
michael@0 | 572 | case FilterType::TABLE_TRANSFER: |
michael@0 | 573 | filter = new FilterNodeTableTransferSoftware(); |
michael@0 | 574 | break; |
michael@0 | 575 | case FilterType::DISCRETE_TRANSFER: |
michael@0 | 576 | filter = new FilterNodeDiscreteTransferSoftware(); |
michael@0 | 577 | break; |
michael@0 | 578 | case FilterType::LINEAR_TRANSFER: |
michael@0 | 579 | filter = new FilterNodeLinearTransferSoftware(); |
michael@0 | 580 | break; |
michael@0 | 581 | case FilterType::GAMMA_TRANSFER: |
michael@0 | 582 | filter = new FilterNodeGammaTransferSoftware(); |
michael@0 | 583 | break; |
michael@0 | 584 | case FilterType::CONVOLVE_MATRIX: |
michael@0 | 585 | filter = new FilterNodeConvolveMatrixSoftware(); |
michael@0 | 586 | break; |
michael@0 | 587 | case FilterType::DISPLACEMENT_MAP: |
michael@0 | 588 | filter = new FilterNodeDisplacementMapSoftware(); |
michael@0 | 589 | break; |
michael@0 | 590 | case FilterType::TURBULENCE: |
michael@0 | 591 | filter = new FilterNodeTurbulenceSoftware(); |
michael@0 | 592 | break; |
michael@0 | 593 | case FilterType::ARITHMETIC_COMBINE: |
michael@0 | 594 | filter = new FilterNodeArithmeticCombineSoftware(); |
michael@0 | 595 | break; |
michael@0 | 596 | case FilterType::COMPOSITE: |
michael@0 | 597 | filter = new FilterNodeCompositeSoftware(); |
michael@0 | 598 | break; |
michael@0 | 599 | case FilterType::GAUSSIAN_BLUR: |
michael@0 | 600 | filter = new FilterNodeGaussianBlurSoftware(); |
michael@0 | 601 | break; |
michael@0 | 602 | case FilterType::DIRECTIONAL_BLUR: |
michael@0 | 603 | filter = new FilterNodeDirectionalBlurSoftware(); |
michael@0 | 604 | break; |
michael@0 | 605 | case FilterType::CROP: |
michael@0 | 606 | filter = new FilterNodeCropSoftware(); |
michael@0 | 607 | break; |
michael@0 | 608 | case FilterType::PREMULTIPLY: |
michael@0 | 609 | filter = new FilterNodePremultiplySoftware(); |
michael@0 | 610 | break; |
michael@0 | 611 | case FilterType::UNPREMULTIPLY: |
michael@0 | 612 | filter = new FilterNodeUnpremultiplySoftware(); |
michael@0 | 613 | break; |
michael@0 | 614 | case FilterType::POINT_DIFFUSE: |
michael@0 | 615 | filter = new FilterNodeLightingSoftware<PointLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<PointLight, DiffuseLighting>"); |
michael@0 | 616 | break; |
michael@0 | 617 | case FilterType::POINT_SPECULAR: |
michael@0 | 618 | filter = new FilterNodeLightingSoftware<PointLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<PointLight, SpecularLighting>"); |
michael@0 | 619 | break; |
michael@0 | 620 | case FilterType::SPOT_DIFFUSE: |
michael@0 | 621 | filter = new FilterNodeLightingSoftware<SpotLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<SpotLight, DiffuseLighting>"); |
michael@0 | 622 | break; |
michael@0 | 623 | case FilterType::SPOT_SPECULAR: |
michael@0 | 624 | filter = new FilterNodeLightingSoftware<SpotLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<SpotLight, SpecularLighting>"); |
michael@0 | 625 | break; |
michael@0 | 626 | case FilterType::DISTANT_DIFFUSE: |
michael@0 | 627 | filter = new FilterNodeLightingSoftware<DistantLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<DistantLight, DiffuseLighting>"); |
michael@0 | 628 | break; |
michael@0 | 629 | case FilterType::DISTANT_SPECULAR: |
michael@0 | 630 | filter = new FilterNodeLightingSoftware<DistantLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<DistantLight, SpecularLighting>"); |
michael@0 | 631 | break; |
michael@0 | 632 | } |
michael@0 | 633 | return filter; |
michael@0 | 634 | } |
michael@0 | 635 | |
michael@0 | 636 | void |
michael@0 | 637 | FilterNodeSoftware::Draw(DrawTarget* aDrawTarget, |
michael@0 | 638 | const Rect &aSourceRect, |
michael@0 | 639 | const Point &aDestPoint, |
michael@0 | 640 | const DrawOptions &aOptions) |
michael@0 | 641 | { |
michael@0 | 642 | #ifdef DEBUG_DUMP_SURFACES |
michael@0 | 643 | printf("<style>section{margin:10px;}</style><pre>\nRendering filter %s...\n", GetName()); |
michael@0 | 644 | #endif |
michael@0 | 645 | |
michael@0 | 646 | Rect renderRect = aSourceRect; |
michael@0 | 647 | renderRect.RoundOut(); |
michael@0 | 648 | IntRect renderIntRect; |
michael@0 | 649 | if (!renderRect.ToIntRect(&renderIntRect)) { |
michael@0 | 650 | #ifdef DEBUG_DUMP_SURFACES |
michael@0 | 651 | printf("render rect overflowed, not painting anything\n"); |
michael@0 | 652 | printf("</pre>\n"); |
michael@0 | 653 | #endif |
michael@0 | 654 | return; |
michael@0 | 655 | } |
michael@0 | 656 | |
michael@0 | 657 | IntRect outputRect = GetOutputRectInRect(renderIntRect); |
michael@0 | 658 | if (IntRectOverflows(outputRect)) { |
michael@0 | 659 | #ifdef DEBUG_DUMP_SURFACES |
michael@0 | 660 | printf("output rect overflowed, not painting anything\n"); |
michael@0 | 661 | printf("</pre>\n"); |
michael@0 | 662 | #endif |
michael@0 | 663 | return; |
michael@0 | 664 | } |
michael@0 | 665 | |
michael@0 | 666 | RefPtr<DataSourceSurface> result; |
michael@0 | 667 | if (!outputRect.IsEmpty()) { |
michael@0 | 668 | result = GetOutput(outputRect); |
michael@0 | 669 | } |
michael@0 | 670 | |
michael@0 | 671 | if (!result) { |
michael@0 | 672 | // Null results are allowed and treated as transparent. Don't draw anything. |
michael@0 | 673 | #ifdef DEBUG_DUMP_SURFACES |
michael@0 | 674 | printf("output returned null\n"); |
michael@0 | 675 | printf("</pre>\n"); |
michael@0 | 676 | #endif |
michael@0 | 677 | return; |
michael@0 | 678 | } |
michael@0 | 679 | |
michael@0 | 680 | #ifdef DEBUG_DUMP_SURFACES |
michael@0 | 681 | printf("output from %s:\n", GetName()); |
michael@0 | 682 | printf("<img src='"); DumpAsPNG(result); printf("'>\n"); |
michael@0 | 683 | printf("</pre>\n"); |
michael@0 | 684 | #endif |
michael@0 | 685 | |
michael@0 | 686 | Point sourceToDestOffset = aDestPoint - aSourceRect.TopLeft(); |
michael@0 | 687 | Rect renderedSourceRect = Rect(outputRect).Intersect(aSourceRect); |
michael@0 | 688 | Rect renderedDestRect = renderedSourceRect + sourceToDestOffset; |
michael@0 | 689 | if (result->GetFormat() == SurfaceFormat::A8) { |
michael@0 | 690 | // Interpret the result as having implicitly black color channels. |
michael@0 | 691 | aDrawTarget->PushClipRect(renderedDestRect); |
michael@0 | 692 | aDrawTarget->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)), |
michael@0 | 693 | result, |
michael@0 | 694 | Point(outputRect.TopLeft()) + sourceToDestOffset, |
michael@0 | 695 | aOptions); |
michael@0 | 696 | aDrawTarget->PopClip(); |
michael@0 | 697 | } else { |
michael@0 | 698 | aDrawTarget->DrawSurface(result, renderedDestRect, |
michael@0 | 699 | renderedSourceRect - Point(outputRect.TopLeft()), |
michael@0 | 700 | DrawSurfaceOptions(), aOptions); |
michael@0 | 701 | } |
michael@0 | 702 | } |
michael@0 | 703 | |
michael@0 | 704 | TemporaryRef<DataSourceSurface> |
michael@0 | 705 | FilterNodeSoftware::GetOutput(const IntRect &aRect) |
michael@0 | 706 | { |
michael@0 | 707 | MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect)); |
michael@0 | 708 | |
michael@0 | 709 | if (IntRectOverflows(aRect)) { |
michael@0 | 710 | return nullptr; |
michael@0 | 711 | } |
michael@0 | 712 | |
michael@0 | 713 | if (!mCachedRect.Contains(aRect)) { |
michael@0 | 714 | RequestRect(aRect); |
michael@0 | 715 | mCachedOutput = Render(mRequestedRect); |
michael@0 | 716 | if (!mCachedOutput) { |
michael@0 | 717 | mCachedRect = IntRect(); |
michael@0 | 718 | mRequestedRect = IntRect(); |
michael@0 | 719 | return nullptr; |
michael@0 | 720 | } |
michael@0 | 721 | mCachedRect = mRequestedRect; |
michael@0 | 722 | mRequestedRect = IntRect(); |
michael@0 | 723 | } else { |
michael@0 | 724 | MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?"); |
michael@0 | 725 | } |
michael@0 | 726 | return GetDataSurfaceInRect(mCachedOutput, mCachedRect, aRect, EDGE_MODE_NONE); |
michael@0 | 727 | } |
michael@0 | 728 | |
michael@0 | 729 | void |
michael@0 | 730 | FilterNodeSoftware::RequestRect(const IntRect &aRect) |
michael@0 | 731 | { |
michael@0 | 732 | mRequestedRect = mRequestedRect.Union(aRect); |
michael@0 | 733 | RequestFromInputsForRect(aRect); |
michael@0 | 734 | } |
michael@0 | 735 | |
michael@0 | 736 | void |
michael@0 | 737 | FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex, const IntRect &aRect) |
michael@0 | 738 | { |
michael@0 | 739 | if (IntRectOverflows(aRect)) { |
michael@0 | 740 | return; |
michael@0 | 741 | } |
michael@0 | 742 | |
michael@0 | 743 | int32_t inputIndex = InputIndex(aInputEnumIndex); |
michael@0 | 744 | if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) { |
michael@0 | 745 | MOZ_CRASH(); |
michael@0 | 746 | } |
michael@0 | 747 | if (mInputSurfaces[inputIndex]) { |
michael@0 | 748 | return; |
michael@0 | 749 | } |
michael@0 | 750 | RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex]; |
michael@0 | 751 | MOZ_ASSERT(filter, "missing input"); |
michael@0 | 752 | filter->RequestRect(filter->GetOutputRectInRect(aRect)); |
michael@0 | 753 | } |
michael@0 | 754 | |
michael@0 | 755 | SurfaceFormat |
michael@0 | 756 | FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat, |
michael@0 | 757 | FormatHint aFormatHint) |
michael@0 | 758 | { |
michael@0 | 759 | if (aCurrentFormat == SurfaceFormat::A8 && aFormatHint == CAN_HANDLE_A8) { |
michael@0 | 760 | return SurfaceFormat::A8; |
michael@0 | 761 | } |
michael@0 | 762 | return SurfaceFormat::B8G8R8A8; |
michael@0 | 763 | } |
michael@0 | 764 | |
michael@0 | 765 | TemporaryRef<DataSourceSurface> |
michael@0 | 766 | FilterNodeSoftware::GetInputDataSourceSurface(uint32_t aInputEnumIndex, |
michael@0 | 767 | const IntRect& aRect, |
michael@0 | 768 | FormatHint aFormatHint, |
michael@0 | 769 | ConvolveMatrixEdgeMode aEdgeMode, |
michael@0 | 770 | const IntRect *aTransparencyPaddedSourceRect) |
michael@0 | 771 | { |
michael@0 | 772 | if (IntRectOverflows(aRect)) { |
michael@0 | 773 | return nullptr; |
michael@0 | 774 | } |
michael@0 | 775 | |
michael@0 | 776 | #ifdef DEBUG_DUMP_SURFACES |
michael@0 | 777 | printf("<section><h1>GetInputDataSourceSurface with aRect: %d, %d, %d, %d</h1>\n", |
michael@0 | 778 | aRect.x, aRect.y, aRect.width, aRect.height); |
michael@0 | 779 | #endif |
michael@0 | 780 | int32_t inputIndex = InputIndex(aInputEnumIndex); |
michael@0 | 781 | if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) { |
michael@0 | 782 | MOZ_CRASH(); |
michael@0 | 783 | return nullptr; |
michael@0 | 784 | } |
michael@0 | 785 | |
michael@0 | 786 | if (aRect.IsEmpty()) { |
michael@0 | 787 | return nullptr; |
michael@0 | 788 | } |
michael@0 | 789 | |
michael@0 | 790 | RefPtr<SourceSurface> surface; |
michael@0 | 791 | IntRect surfaceRect; |
michael@0 | 792 | |
michael@0 | 793 | if (mInputSurfaces[inputIndex]) { |
michael@0 | 794 | // Input from input surface |
michael@0 | 795 | surface = mInputSurfaces[inputIndex]; |
michael@0 | 796 | #ifdef DEBUG_DUMP_SURFACES |
michael@0 | 797 | printf("input from input surface:\n"); |
michael@0 | 798 | #endif |
michael@0 | 799 | surfaceRect = IntRect(IntPoint(0, 0), surface->GetSize()); |
michael@0 | 800 | } else { |
michael@0 | 801 | // Input from input filter |
michael@0 | 802 | #ifdef DEBUG_DUMP_SURFACES |
michael@0 | 803 | printf("getting input from input filter %s...\n", mInputFilters[inputIndex]->GetName()); |
michael@0 | 804 | #endif |
michael@0 | 805 | RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex]; |
michael@0 | 806 | MOZ_ASSERT(filter, "missing input"); |
michael@0 | 807 | IntRect inputFilterOutput = filter->GetOutputRectInRect(aRect); |
michael@0 | 808 | if (!inputFilterOutput.IsEmpty()) { |
michael@0 | 809 | surface = filter->GetOutput(inputFilterOutput); |
michael@0 | 810 | } |
michael@0 | 811 | #ifdef DEBUG_DUMP_SURFACES |
michael@0 | 812 | printf("input from input filter %s:\n", mInputFilters[inputIndex]->GetName()); |
michael@0 | 813 | #endif |
michael@0 | 814 | surfaceRect = inputFilterOutput; |
michael@0 | 815 | MOZ_ASSERT(!surface || surfaceRect.Size() == surface->GetSize()); |
michael@0 | 816 | } |
michael@0 | 817 | |
michael@0 | 818 | if (surface && surface->GetFormat() == SurfaceFormat::UNKNOWN) { |
michael@0 | 819 | #ifdef DEBUG_DUMP_SURFACES |
michael@0 | 820 | printf("wrong input format</section>\n\n"); |
michael@0 | 821 | #endif |
michael@0 | 822 | return nullptr; |
michael@0 | 823 | } |
michael@0 | 824 | |
michael@0 | 825 | if (!surfaceRect.IsEmpty() && !surface) { |
michael@0 | 826 | #ifdef DEBUG_DUMP_SURFACES |
michael@0 | 827 | printf(" -- no input --</section>\n\n"); |
michael@0 | 828 | #endif |
michael@0 | 829 | return nullptr; |
michael@0 | 830 | } |
michael@0 | 831 | |
michael@0 | 832 | if (aTransparencyPaddedSourceRect && !aTransparencyPaddedSourceRect->IsEmpty()) { |
michael@0 | 833 | IntRect srcRect = aTransparencyPaddedSourceRect->Intersect(aRect); |
michael@0 | 834 | surface = GetDataSurfaceInRect(surface, surfaceRect, srcRect, EDGE_MODE_NONE); |
michael@0 | 835 | surfaceRect = srcRect; |
michael@0 | 836 | } |
michael@0 | 837 | |
michael@0 | 838 | RefPtr<DataSourceSurface> result = |
michael@0 | 839 | GetDataSurfaceInRect(surface, surfaceRect, aRect, aEdgeMode); |
michael@0 | 840 | |
michael@0 | 841 | if (result && |
michael@0 | 842 | (result->Stride() != GetAlignedStride<16>(result->Stride()) || |
michael@0 | 843 | reinterpret_cast<uintptr_t>(result->GetData()) % 16 != 0)) { |
michael@0 | 844 | // Align unaligned surface. |
michael@0 | 845 | result = CloneAligned(result); |
michael@0 | 846 | } |
michael@0 | 847 | |
michael@0 | 848 | if (!result) { |
michael@0 | 849 | #ifdef DEBUG_DUMP_SURFACES |
michael@0 | 850 | printf(" -- no input --</section>\n\n"); |
michael@0 | 851 | #endif |
michael@0 | 852 | return nullptr; |
michael@0 | 853 | } |
michael@0 | 854 | |
michael@0 | 855 | SurfaceFormat currentFormat = result->GetFormat(); |
michael@0 | 856 | if (DesiredFormat(currentFormat, aFormatHint) == SurfaceFormat::B8G8R8A8 && |
michael@0 | 857 | currentFormat != SurfaceFormat::B8G8R8A8) { |
michael@0 | 858 | result = FilterProcessing::ConvertToB8G8R8A8(result); |
michael@0 | 859 | } |
michael@0 | 860 | |
michael@0 | 861 | #ifdef DEBUG_DUMP_SURFACES |
michael@0 | 862 | printf("<img src='"); DumpAsPNG(result); printf("'></section>"); |
michael@0 | 863 | #endif |
michael@0 | 864 | |
michael@0 | 865 | MOZ_ASSERT(!result || result->GetSize() == aRect.Size(), "wrong surface size"); |
michael@0 | 866 | |
michael@0 | 867 | return result; |
michael@0 | 868 | } |
michael@0 | 869 | |
michael@0 | 870 | IntRect |
michael@0 | 871 | FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex, |
michael@0 | 872 | const IntRect &aInRect) |
michael@0 | 873 | { |
michael@0 | 874 | if (IntRectOverflows(aInRect)) { |
michael@0 | 875 | return IntRect(); |
michael@0 | 876 | } |
michael@0 | 877 | |
michael@0 | 878 | int32_t inputIndex = InputIndex(aInputEnumIndex); |
michael@0 | 879 | if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) { |
michael@0 | 880 | MOZ_CRASH(); |
michael@0 | 881 | return IntRect(); |
michael@0 | 882 | } |
michael@0 | 883 | if (mInputSurfaces[inputIndex]) { |
michael@0 | 884 | return aInRect.Intersect(IntRect(IntPoint(0, 0), |
michael@0 | 885 | mInputSurfaces[inputIndex]->GetSize())); |
michael@0 | 886 | } |
michael@0 | 887 | RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex]; |
michael@0 | 888 | MOZ_ASSERT(filter, "missing input"); |
michael@0 | 889 | return filter->GetOutputRectInRect(aInRect); |
michael@0 | 890 | } |
michael@0 | 891 | |
michael@0 | 892 | size_t |
michael@0 | 893 | FilterNodeSoftware::NumberOfSetInputs() |
michael@0 | 894 | { |
michael@0 | 895 | return std::max(mInputSurfaces.size(), mInputFilters.size()); |
michael@0 | 896 | } |
michael@0 | 897 | |
michael@0 | 898 | void |
michael@0 | 899 | FilterNodeSoftware::AddInvalidationListener(FilterInvalidationListener* aListener) |
michael@0 | 900 | { |
michael@0 | 901 | MOZ_ASSERT(aListener, "null listener"); |
michael@0 | 902 | mInvalidationListeners.push_back(aListener); |
michael@0 | 903 | } |
michael@0 | 904 | |
michael@0 | 905 | void |
michael@0 | 906 | FilterNodeSoftware::RemoveInvalidationListener(FilterInvalidationListener* aListener) |
michael@0 | 907 | { |
michael@0 | 908 | MOZ_ASSERT(aListener, "null listener"); |
michael@0 | 909 | std::vector<FilterInvalidationListener*>::iterator it = |
michael@0 | 910 | std::find(mInvalidationListeners.begin(), mInvalidationListeners.end(), aListener); |
michael@0 | 911 | mInvalidationListeners.erase(it); |
michael@0 | 912 | } |
michael@0 | 913 | |
michael@0 | 914 | void |
michael@0 | 915 | FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware* aFilter) |
michael@0 | 916 | { |
michael@0 | 917 | Invalidate(); |
michael@0 | 918 | } |
michael@0 | 919 | |
michael@0 | 920 | void |
michael@0 | 921 | FilterNodeSoftware::Invalidate() |
michael@0 | 922 | { |
michael@0 | 923 | mCachedOutput = nullptr; |
michael@0 | 924 | mCachedRect = IntRect(); |
michael@0 | 925 | for (std::vector<FilterInvalidationListener*>::iterator it = mInvalidationListeners.begin(); |
michael@0 | 926 | it != mInvalidationListeners.end(); it++) { |
michael@0 | 927 | (*it)->FilterInvalidated(this); |
michael@0 | 928 | } |
michael@0 | 929 | } |
michael@0 | 930 | |
michael@0 | 931 | FilterNodeSoftware::~FilterNodeSoftware() |
michael@0 | 932 | { |
michael@0 | 933 | MOZ_ASSERT(!mInvalidationListeners.size(), |
michael@0 | 934 | "All invalidation listeners should have unsubscribed themselves by now!"); |
michael@0 | 935 | |
michael@0 | 936 | for (std::vector<RefPtr<FilterNodeSoftware> >::iterator it = mInputFilters.begin(); |
michael@0 | 937 | it != mInputFilters.end(); it++) { |
michael@0 | 938 | if (*it) { |
michael@0 | 939 | (*it)->RemoveInvalidationListener(this); |
michael@0 | 940 | } |
michael@0 | 941 | } |
michael@0 | 942 | } |
michael@0 | 943 | |
michael@0 | 944 | void |
michael@0 | 945 | FilterNodeSoftware::SetInput(uint32_t aIndex, FilterNode *aFilter) |
michael@0 | 946 | { |
michael@0 | 947 | if (aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) { |
michael@0 | 948 | MOZ_ASSERT(false, "can only take software filters as inputs"); |
michael@0 | 949 | return; |
michael@0 | 950 | } |
michael@0 | 951 | SetInput(aIndex, nullptr, static_cast<FilterNodeSoftware*>(aFilter)); |
michael@0 | 952 | } |
michael@0 | 953 | |
michael@0 | 954 | void |
michael@0 | 955 | FilterNodeSoftware::SetInput(uint32_t aIndex, SourceSurface *aSurface) |
michael@0 | 956 | { |
michael@0 | 957 | SetInput(aIndex, aSurface, nullptr); |
michael@0 | 958 | } |
michael@0 | 959 | |
michael@0 | 960 | void |
michael@0 | 961 | FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex, |
michael@0 | 962 | SourceSurface *aSurface, |
michael@0 | 963 | FilterNodeSoftware *aFilter) |
michael@0 | 964 | { |
michael@0 | 965 | int32_t inputIndex = InputIndex(aInputEnumIndex); |
michael@0 | 966 | if (inputIndex < 0) { |
michael@0 | 967 | MOZ_CRASH(); |
michael@0 | 968 | return; |
michael@0 | 969 | } |
michael@0 | 970 | if ((uint32_t)inputIndex >= mInputSurfaces.size()) { |
michael@0 | 971 | mInputSurfaces.resize(inputIndex + 1); |
michael@0 | 972 | } |
michael@0 | 973 | if ((uint32_t)inputIndex >= mInputFilters.size()) { |
michael@0 | 974 | mInputFilters.resize(inputIndex + 1); |
michael@0 | 975 | } |
michael@0 | 976 | mInputSurfaces[inputIndex] = aSurface; |
michael@0 | 977 | if (mInputFilters[inputIndex]) { |
michael@0 | 978 | mInputFilters[inputIndex]->RemoveInvalidationListener(this); |
michael@0 | 979 | } |
michael@0 | 980 | if (aFilter) { |
michael@0 | 981 | aFilter->AddInvalidationListener(this); |
michael@0 | 982 | } |
michael@0 | 983 | mInputFilters[inputIndex] = aFilter; |
michael@0 | 984 | Invalidate(); |
michael@0 | 985 | } |
michael@0 | 986 | |
michael@0 | 987 | FilterNodeBlendSoftware::FilterNodeBlendSoftware() |
michael@0 | 988 | : mBlendMode(BLEND_MODE_MULTIPLY) |
michael@0 | 989 | {} |
michael@0 | 990 | |
michael@0 | 991 | int32_t |
michael@0 | 992 | FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 993 | { |
michael@0 | 994 | switch (aInputEnumIndex) { |
michael@0 | 995 | case IN_BLEND_IN: return 0; |
michael@0 | 996 | case IN_BLEND_IN2: return 1; |
michael@0 | 997 | default: return -1; |
michael@0 | 998 | } |
michael@0 | 999 | } |
michael@0 | 1000 | |
michael@0 | 1001 | void |
michael@0 | 1002 | FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex, uint32_t aBlendMode) |
michael@0 | 1003 | { |
michael@0 | 1004 | MOZ_ASSERT(aIndex == ATT_BLEND_BLENDMODE); |
michael@0 | 1005 | mBlendMode = static_cast<BlendMode>(aBlendMode); |
michael@0 | 1006 | Invalidate(); |
michael@0 | 1007 | } |
michael@0 | 1008 | |
michael@0 | 1009 | TemporaryRef<DataSourceSurface> |
michael@0 | 1010 | FilterNodeBlendSoftware::Render(const IntRect& aRect) |
michael@0 | 1011 | { |
michael@0 | 1012 | RefPtr<DataSourceSurface> input1 = |
michael@0 | 1013 | GetInputDataSourceSurface(IN_BLEND_IN, aRect, NEED_COLOR_CHANNELS); |
michael@0 | 1014 | RefPtr<DataSourceSurface> input2 = |
michael@0 | 1015 | GetInputDataSourceSurface(IN_BLEND_IN2, aRect, NEED_COLOR_CHANNELS); |
michael@0 | 1016 | |
michael@0 | 1017 | // Null inputs need to be treated as transparent. |
michael@0 | 1018 | |
michael@0 | 1019 | // First case: both are transparent. |
michael@0 | 1020 | if (!input1 && !input2) { |
michael@0 | 1021 | // Then the result is transparent, too. |
michael@0 | 1022 | return nullptr; |
michael@0 | 1023 | } |
michael@0 | 1024 | |
michael@0 | 1025 | // Second case: both are non-transparent. |
michael@0 | 1026 | if (input1 && input2) { |
michael@0 | 1027 | // Apply normal filtering. |
michael@0 | 1028 | return FilterProcessing::ApplyBlending(input1, input2, mBlendMode); |
michael@0 | 1029 | } |
michael@0 | 1030 | |
michael@0 | 1031 | // Third case: one of them is transparent. Return the non-transparent one. |
michael@0 | 1032 | return input1 ? input1 : input2; |
michael@0 | 1033 | } |
michael@0 | 1034 | |
michael@0 | 1035 | void |
michael@0 | 1036 | FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect &aRect) |
michael@0 | 1037 | { |
michael@0 | 1038 | RequestInputRect(IN_BLEND_IN, aRect); |
michael@0 | 1039 | RequestInputRect(IN_BLEND_IN2, aRect); |
michael@0 | 1040 | } |
michael@0 | 1041 | |
michael@0 | 1042 | IntRect |
michael@0 | 1043 | FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 1044 | { |
michael@0 | 1045 | return GetInputRectInRect(IN_BLEND_IN, aRect).Union( |
michael@0 | 1046 | GetInputRectInRect(IN_BLEND_IN2, aRect)).Intersect(aRect); |
michael@0 | 1047 | } |
michael@0 | 1048 | |
michael@0 | 1049 | FilterNodeTransformSoftware::FilterNodeTransformSoftware() |
michael@0 | 1050 | : mFilter(Filter::GOOD) |
michael@0 | 1051 | {} |
michael@0 | 1052 | |
michael@0 | 1053 | int32_t |
michael@0 | 1054 | FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 1055 | { |
michael@0 | 1056 | switch (aInputEnumIndex) { |
michael@0 | 1057 | case IN_TRANSFORM_IN: return 0; |
michael@0 | 1058 | default: return -1; |
michael@0 | 1059 | } |
michael@0 | 1060 | } |
michael@0 | 1061 | |
michael@0 | 1062 | void |
michael@0 | 1063 | FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, uint32_t aFilter) |
michael@0 | 1064 | { |
michael@0 | 1065 | MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER); |
michael@0 | 1066 | mFilter = static_cast<Filter>(aFilter); |
michael@0 | 1067 | Invalidate(); |
michael@0 | 1068 | } |
michael@0 | 1069 | |
michael@0 | 1070 | void |
michael@0 | 1071 | FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, const Matrix &aMatrix) |
michael@0 | 1072 | { |
michael@0 | 1073 | MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX); |
michael@0 | 1074 | mMatrix = aMatrix; |
michael@0 | 1075 | Invalidate(); |
michael@0 | 1076 | } |
michael@0 | 1077 | |
michael@0 | 1078 | IntRect |
michael@0 | 1079 | FilterNodeTransformSoftware::SourceRectForOutputRect(const IntRect &aRect) |
michael@0 | 1080 | { |
michael@0 | 1081 | if (aRect.IsEmpty()) { |
michael@0 | 1082 | return IntRect(); |
michael@0 | 1083 | } |
michael@0 | 1084 | |
michael@0 | 1085 | Matrix inverted(mMatrix); |
michael@0 | 1086 | if (!inverted.Invert()) { |
michael@0 | 1087 | return IntRect(); |
michael@0 | 1088 | } |
michael@0 | 1089 | |
michael@0 | 1090 | Rect neededRect = inverted.TransformBounds(Rect(aRect)); |
michael@0 | 1091 | neededRect.RoundOut(); |
michael@0 | 1092 | IntRect neededIntRect; |
michael@0 | 1093 | if (!neededRect.ToIntRect(&neededIntRect)) { |
michael@0 | 1094 | return IntRect(); |
michael@0 | 1095 | } |
michael@0 | 1096 | return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect); |
michael@0 | 1097 | } |
michael@0 | 1098 | |
michael@0 | 1099 | TemporaryRef<DataSourceSurface> |
michael@0 | 1100 | FilterNodeTransformSoftware::Render(const IntRect& aRect) |
michael@0 | 1101 | { |
michael@0 | 1102 | IntRect srcRect = SourceRectForOutputRect(aRect); |
michael@0 | 1103 | |
michael@0 | 1104 | RefPtr<DataSourceSurface> input = |
michael@0 | 1105 | GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect, NEED_COLOR_CHANNELS); |
michael@0 | 1106 | |
michael@0 | 1107 | if (!input) { |
michael@0 | 1108 | return nullptr; |
michael@0 | 1109 | } |
michael@0 | 1110 | |
michael@0 | 1111 | Matrix transform = Matrix::Translation(srcRect.x, srcRect.y) * mMatrix * |
michael@0 | 1112 | Matrix::Translation(-aRect.x, -aRect.y); |
michael@0 | 1113 | if (transform.IsIdentity() && srcRect.Size() == aRect.Size()) { |
michael@0 | 1114 | return input; |
michael@0 | 1115 | } |
michael@0 | 1116 | |
michael@0 | 1117 | RefPtr<DrawTarget> dt = |
michael@0 | 1118 | Factory::CreateDrawTarget(BackendType::CAIRO, aRect.Size(), input->GetFormat()); |
michael@0 | 1119 | if (!dt) { |
michael@0 | 1120 | return nullptr; |
michael@0 | 1121 | } |
michael@0 | 1122 | |
michael@0 | 1123 | Rect r(0, 0, srcRect.width, srcRect.height); |
michael@0 | 1124 | dt->SetTransform(transform); |
michael@0 | 1125 | dt->DrawSurface(input, r, r, DrawSurfaceOptions(mFilter)); |
michael@0 | 1126 | |
michael@0 | 1127 | RefPtr<SourceSurface> result = dt->Snapshot(); |
michael@0 | 1128 | RefPtr<DataSourceSurface> resultData = result->GetDataSurface(); |
michael@0 | 1129 | return resultData; |
michael@0 | 1130 | } |
michael@0 | 1131 | |
michael@0 | 1132 | void |
michael@0 | 1133 | FilterNodeTransformSoftware::RequestFromInputsForRect(const IntRect &aRect) |
michael@0 | 1134 | { |
michael@0 | 1135 | RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect)); |
michael@0 | 1136 | } |
michael@0 | 1137 | |
michael@0 | 1138 | IntRect |
michael@0 | 1139 | FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 1140 | { |
michael@0 | 1141 | IntRect srcRect = SourceRectForOutputRect(aRect); |
michael@0 | 1142 | if (srcRect.IsEmpty()) { |
michael@0 | 1143 | return IntRect(); |
michael@0 | 1144 | } |
michael@0 | 1145 | |
michael@0 | 1146 | Rect outRect = mMatrix.TransformBounds(Rect(srcRect)); |
michael@0 | 1147 | outRect.RoundOut(); |
michael@0 | 1148 | IntRect outIntRect; |
michael@0 | 1149 | if (!outRect.ToIntRect(&outIntRect)) { |
michael@0 | 1150 | return IntRect(); |
michael@0 | 1151 | } |
michael@0 | 1152 | return outIntRect.Intersect(aRect); |
michael@0 | 1153 | } |
michael@0 | 1154 | |
michael@0 | 1155 | FilterNodeMorphologySoftware::FilterNodeMorphologySoftware() |
michael@0 | 1156 | : mOperator(MORPHOLOGY_OPERATOR_ERODE) |
michael@0 | 1157 | {} |
michael@0 | 1158 | |
michael@0 | 1159 | int32_t |
michael@0 | 1160 | FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 1161 | { |
michael@0 | 1162 | switch (aInputEnumIndex) { |
michael@0 | 1163 | case IN_MORPHOLOGY_IN: return 0; |
michael@0 | 1164 | default: return -1; |
michael@0 | 1165 | } |
michael@0 | 1166 | } |
michael@0 | 1167 | |
michael@0 | 1168 | void |
michael@0 | 1169 | FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 1170 | const IntSize &aRadii) |
michael@0 | 1171 | { |
michael@0 | 1172 | MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_RADII); |
michael@0 | 1173 | mRadii.width = std::min(std::max(aRadii.width, 0), 100000); |
michael@0 | 1174 | mRadii.height = std::min(std::max(aRadii.height, 0), 100000); |
michael@0 | 1175 | Invalidate(); |
michael@0 | 1176 | } |
michael@0 | 1177 | |
michael@0 | 1178 | void |
michael@0 | 1179 | FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 1180 | uint32_t aOperator) |
michael@0 | 1181 | { |
michael@0 | 1182 | MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_OPERATOR); |
michael@0 | 1183 | mOperator = static_cast<MorphologyOperator>(aOperator); |
michael@0 | 1184 | Invalidate(); |
michael@0 | 1185 | } |
michael@0 | 1186 | |
michael@0 | 1187 | static TemporaryRef<DataSourceSurface> |
michael@0 | 1188 | ApplyMorphology(const IntRect& aSourceRect, DataSourceSurface* aInput, |
michael@0 | 1189 | const IntRect& aDestRect, int32_t rx, int32_t ry, |
michael@0 | 1190 | MorphologyOperator aOperator) |
michael@0 | 1191 | { |
michael@0 | 1192 | IntRect srcRect = aSourceRect - aDestRect.TopLeft(); |
michael@0 | 1193 | IntRect destRect = aDestRect - aDestRect.TopLeft(); |
michael@0 | 1194 | IntRect tmpRect(destRect.x, srcRect.y, destRect.width, srcRect.height); |
michael@0 | 1195 | #ifdef DEBUG |
michael@0 | 1196 | IntMargin margin = srcRect - destRect; |
michael@0 | 1197 | MOZ_ASSERT(margin.top >= ry && margin.right >= rx && |
michael@0 | 1198 | margin.bottom >= ry && margin.left >= rx, "insufficient margin"); |
michael@0 | 1199 | #endif |
michael@0 | 1200 | |
michael@0 | 1201 | RefPtr<DataSourceSurface> tmp; |
michael@0 | 1202 | if (rx == 0) { |
michael@0 | 1203 | tmp = aInput; |
michael@0 | 1204 | } else { |
michael@0 | 1205 | tmp = Factory::CreateDataSourceSurface(tmpRect.Size(), SurfaceFormat::B8G8R8A8); |
michael@0 | 1206 | if (!tmp) { |
michael@0 | 1207 | return nullptr; |
michael@0 | 1208 | } |
michael@0 | 1209 | |
michael@0 | 1210 | int32_t sourceStride = aInput->Stride(); |
michael@0 | 1211 | uint8_t* sourceData = DataAtOffset(aInput, destRect.TopLeft() - srcRect.TopLeft()); |
michael@0 | 1212 | |
michael@0 | 1213 | int32_t tmpStride = tmp->Stride(); |
michael@0 | 1214 | uint8_t* tmpData = DataAtOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft()); |
michael@0 | 1215 | |
michael@0 | 1216 | FilterProcessing::ApplyMorphologyHorizontal( |
michael@0 | 1217 | sourceData, sourceStride, tmpData, tmpStride, tmpRect, rx, aOperator); |
michael@0 | 1218 | } |
michael@0 | 1219 | |
michael@0 | 1220 | RefPtr<DataSourceSurface> dest; |
michael@0 | 1221 | if (ry == 0) { |
michael@0 | 1222 | dest = tmp; |
michael@0 | 1223 | } else { |
michael@0 | 1224 | dest = Factory::CreateDataSourceSurface(destRect.Size(), SurfaceFormat::B8G8R8A8); |
michael@0 | 1225 | if (!dest) { |
michael@0 | 1226 | return nullptr; |
michael@0 | 1227 | } |
michael@0 | 1228 | |
michael@0 | 1229 | int32_t tmpStride = tmp->Stride(); |
michael@0 | 1230 | uint8_t* tmpData = DataAtOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft()); |
michael@0 | 1231 | |
michael@0 | 1232 | int32_t destStride = dest->Stride(); |
michael@0 | 1233 | uint8_t* destData = dest->GetData(); |
michael@0 | 1234 | |
michael@0 | 1235 | FilterProcessing::ApplyMorphologyVertical( |
michael@0 | 1236 | tmpData, tmpStride, destData, destStride, destRect, ry, aOperator); |
michael@0 | 1237 | } |
michael@0 | 1238 | |
michael@0 | 1239 | return dest; |
michael@0 | 1240 | } |
michael@0 | 1241 | |
michael@0 | 1242 | TemporaryRef<DataSourceSurface> |
michael@0 | 1243 | FilterNodeMorphologySoftware::Render(const IntRect& aRect) |
michael@0 | 1244 | { |
michael@0 | 1245 | IntRect srcRect = aRect; |
michael@0 | 1246 | srcRect.Inflate(mRadii); |
michael@0 | 1247 | |
michael@0 | 1248 | RefPtr<DataSourceSurface> input = |
michael@0 | 1249 | GetInputDataSourceSurface(IN_MORPHOLOGY_IN, srcRect, NEED_COLOR_CHANNELS); |
michael@0 | 1250 | if (!input) { |
michael@0 | 1251 | return nullptr; |
michael@0 | 1252 | } |
michael@0 | 1253 | |
michael@0 | 1254 | int32_t rx = mRadii.width; |
michael@0 | 1255 | int32_t ry = mRadii.height; |
michael@0 | 1256 | |
michael@0 | 1257 | if (rx == 0 && ry == 0) { |
michael@0 | 1258 | return input; |
michael@0 | 1259 | } |
michael@0 | 1260 | |
michael@0 | 1261 | return ApplyMorphology(srcRect, input, aRect, rx, ry, mOperator); |
michael@0 | 1262 | } |
michael@0 | 1263 | |
michael@0 | 1264 | void |
michael@0 | 1265 | FilterNodeMorphologySoftware::RequestFromInputsForRect(const IntRect &aRect) |
michael@0 | 1266 | { |
michael@0 | 1267 | IntRect srcRect = aRect; |
michael@0 | 1268 | srcRect.Inflate(mRadii); |
michael@0 | 1269 | RequestInputRect(IN_MORPHOLOGY_IN, srcRect); |
michael@0 | 1270 | } |
michael@0 | 1271 | |
michael@0 | 1272 | IntRect |
michael@0 | 1273 | FilterNodeMorphologySoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 1274 | { |
michael@0 | 1275 | IntRect inflatedSourceRect = aRect; |
michael@0 | 1276 | inflatedSourceRect.Inflate(mRadii); |
michael@0 | 1277 | IntRect inputRect = GetInputRectInRect(IN_MORPHOLOGY_IN, inflatedSourceRect); |
michael@0 | 1278 | if (mOperator == MORPHOLOGY_OPERATOR_ERODE) { |
michael@0 | 1279 | inputRect.Deflate(mRadii); |
michael@0 | 1280 | } else { |
michael@0 | 1281 | inputRect.Inflate(mRadii); |
michael@0 | 1282 | } |
michael@0 | 1283 | return inputRect.Intersect(aRect); |
michael@0 | 1284 | } |
michael@0 | 1285 | |
michael@0 | 1286 | int32_t |
michael@0 | 1287 | FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 1288 | { |
michael@0 | 1289 | switch (aInputEnumIndex) { |
michael@0 | 1290 | case IN_COLOR_MATRIX_IN: return 0; |
michael@0 | 1291 | default: return -1; |
michael@0 | 1292 | } |
michael@0 | 1293 | } |
michael@0 | 1294 | |
michael@0 | 1295 | void |
michael@0 | 1296 | FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 1297 | const Matrix5x4 &aMatrix) |
michael@0 | 1298 | { |
michael@0 | 1299 | MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX); |
michael@0 | 1300 | mMatrix = aMatrix; |
michael@0 | 1301 | Invalidate(); |
michael@0 | 1302 | } |
michael@0 | 1303 | |
michael@0 | 1304 | void |
michael@0 | 1305 | FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 1306 | uint32_t aAlphaMode) |
michael@0 | 1307 | { |
michael@0 | 1308 | MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE); |
michael@0 | 1309 | mAlphaMode = (AlphaMode)aAlphaMode; |
michael@0 | 1310 | Invalidate(); |
michael@0 | 1311 | } |
michael@0 | 1312 | |
michael@0 | 1313 | static TemporaryRef<DataSourceSurface> |
michael@0 | 1314 | Premultiply(DataSourceSurface* aSurface) |
michael@0 | 1315 | { |
michael@0 | 1316 | if (aSurface->GetFormat() == SurfaceFormat::A8) { |
michael@0 | 1317 | return aSurface; |
michael@0 | 1318 | } |
michael@0 | 1319 | |
michael@0 | 1320 | IntSize size = aSurface->GetSize(); |
michael@0 | 1321 | RefPtr<DataSourceSurface> target = |
michael@0 | 1322 | Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); |
michael@0 | 1323 | if (!target) { |
michael@0 | 1324 | return nullptr; |
michael@0 | 1325 | } |
michael@0 | 1326 | |
michael@0 | 1327 | uint8_t* inputData = aSurface->GetData(); |
michael@0 | 1328 | int32_t inputStride = aSurface->Stride(); |
michael@0 | 1329 | uint8_t* targetData = target->GetData(); |
michael@0 | 1330 | int32_t targetStride = target->Stride(); |
michael@0 | 1331 | |
michael@0 | 1332 | FilterProcessing::DoPremultiplicationCalculation( |
michael@0 | 1333 | size, targetData, targetStride, inputData, inputStride); |
michael@0 | 1334 | |
michael@0 | 1335 | return target; |
michael@0 | 1336 | } |
michael@0 | 1337 | |
michael@0 | 1338 | static TemporaryRef<DataSourceSurface> |
michael@0 | 1339 | Unpremultiply(DataSourceSurface* aSurface) |
michael@0 | 1340 | { |
michael@0 | 1341 | if (aSurface->GetFormat() == SurfaceFormat::A8) { |
michael@0 | 1342 | return aSurface; |
michael@0 | 1343 | } |
michael@0 | 1344 | |
michael@0 | 1345 | IntSize size = aSurface->GetSize(); |
michael@0 | 1346 | RefPtr<DataSourceSurface> target = |
michael@0 | 1347 | Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); |
michael@0 | 1348 | if (!target) { |
michael@0 | 1349 | return nullptr; |
michael@0 | 1350 | } |
michael@0 | 1351 | |
michael@0 | 1352 | uint8_t* inputData = aSurface->GetData(); |
michael@0 | 1353 | int32_t inputStride = aSurface->Stride(); |
michael@0 | 1354 | uint8_t* targetData = target->GetData(); |
michael@0 | 1355 | int32_t targetStride = target->Stride(); |
michael@0 | 1356 | |
michael@0 | 1357 | FilterProcessing::DoUnpremultiplicationCalculation( |
michael@0 | 1358 | size, targetData, targetStride, inputData, inputStride); |
michael@0 | 1359 | |
michael@0 | 1360 | return target; |
michael@0 | 1361 | } |
michael@0 | 1362 | |
michael@0 | 1363 | TemporaryRef<DataSourceSurface> |
michael@0 | 1364 | FilterNodeColorMatrixSoftware::Render(const IntRect& aRect) |
michael@0 | 1365 | { |
michael@0 | 1366 | RefPtr<DataSourceSurface> input = |
michael@0 | 1367 | GetInputDataSourceSurface(IN_COLOR_MATRIX_IN, aRect, NEED_COLOR_CHANNELS); |
michael@0 | 1368 | if (!input) { |
michael@0 | 1369 | return nullptr; |
michael@0 | 1370 | } |
michael@0 | 1371 | |
michael@0 | 1372 | if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) { |
michael@0 | 1373 | input = Unpremultiply(input); |
michael@0 | 1374 | } |
michael@0 | 1375 | |
michael@0 | 1376 | RefPtr<DataSourceSurface> result = |
michael@0 | 1377 | FilterProcessing::ApplyColorMatrix(input, mMatrix); |
michael@0 | 1378 | |
michael@0 | 1379 | if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) { |
michael@0 | 1380 | result = Premultiply(result); |
michael@0 | 1381 | } |
michael@0 | 1382 | |
michael@0 | 1383 | return result; |
michael@0 | 1384 | } |
michael@0 | 1385 | |
michael@0 | 1386 | void |
michael@0 | 1387 | FilterNodeColorMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect) |
michael@0 | 1388 | { |
michael@0 | 1389 | RequestInputRect(IN_COLOR_MATRIX_IN, aRect); |
michael@0 | 1390 | } |
michael@0 | 1391 | |
michael@0 | 1392 | IntRect |
michael@0 | 1393 | FilterNodeColorMatrixSoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 1394 | { |
michael@0 | 1395 | return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect); |
michael@0 | 1396 | } |
michael@0 | 1397 | |
michael@0 | 1398 | void |
michael@0 | 1399 | FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex, const Color &aColor) |
michael@0 | 1400 | { |
michael@0 | 1401 | MOZ_ASSERT(aIndex == ATT_FLOOD_COLOR); |
michael@0 | 1402 | mColor = aColor; |
michael@0 | 1403 | Invalidate(); |
michael@0 | 1404 | } |
michael@0 | 1405 | |
michael@0 | 1406 | static uint32_t |
michael@0 | 1407 | ColorToBGRA(const Color& aColor) |
michael@0 | 1408 | { |
michael@0 | 1409 | union { |
michael@0 | 1410 | uint32_t color; |
michael@0 | 1411 | uint8_t components[4]; |
michael@0 | 1412 | }; |
michael@0 | 1413 | components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = NS_lround(aColor.r * aColor.a * 255.0f); |
michael@0 | 1414 | components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = NS_lround(aColor.g * aColor.a * 255.0f); |
michael@0 | 1415 | components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = NS_lround(aColor.b * aColor.a * 255.0f); |
michael@0 | 1416 | components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = NS_lround(aColor.a * 255.0f); |
michael@0 | 1417 | return color; |
michael@0 | 1418 | } |
michael@0 | 1419 | |
michael@0 | 1420 | static SurfaceFormat |
michael@0 | 1421 | FormatForColor(Color aColor) |
michael@0 | 1422 | { |
michael@0 | 1423 | if (aColor.r == 0 && aColor.g == 0 && aColor.b == 0) { |
michael@0 | 1424 | return SurfaceFormat::A8; |
michael@0 | 1425 | } |
michael@0 | 1426 | return SurfaceFormat::B8G8R8A8; |
michael@0 | 1427 | } |
michael@0 | 1428 | |
michael@0 | 1429 | TemporaryRef<DataSourceSurface> |
michael@0 | 1430 | FilterNodeFloodSoftware::Render(const IntRect& aRect) |
michael@0 | 1431 | { |
michael@0 | 1432 | SurfaceFormat format = FormatForColor(mColor); |
michael@0 | 1433 | RefPtr<DataSourceSurface> target = |
michael@0 | 1434 | Factory::CreateDataSourceSurface(aRect.Size(), format); |
michael@0 | 1435 | if (!target) { |
michael@0 | 1436 | return nullptr; |
michael@0 | 1437 | } |
michael@0 | 1438 | |
michael@0 | 1439 | uint8_t* targetData = target->GetData(); |
michael@0 | 1440 | uint32_t stride = target->Stride(); |
michael@0 | 1441 | |
michael@0 | 1442 | if (format == SurfaceFormat::B8G8R8A8) { |
michael@0 | 1443 | uint32_t color = ColorToBGRA(mColor); |
michael@0 | 1444 | for (int32_t y = 0; y < aRect.height; y++) { |
michael@0 | 1445 | for (int32_t x = 0; x < aRect.width; x++) { |
michael@0 | 1446 | *((uint32_t*)targetData + x) = color; |
michael@0 | 1447 | } |
michael@0 | 1448 | targetData += stride; |
michael@0 | 1449 | } |
michael@0 | 1450 | } else if (format == SurfaceFormat::A8) { |
michael@0 | 1451 | uint8_t alpha = NS_lround(mColor.a * 255.0f); |
michael@0 | 1452 | for (int32_t y = 0; y < aRect.height; y++) { |
michael@0 | 1453 | for (int32_t x = 0; x < aRect.width; x++) { |
michael@0 | 1454 | targetData[x] = alpha; |
michael@0 | 1455 | } |
michael@0 | 1456 | targetData += stride; |
michael@0 | 1457 | } |
michael@0 | 1458 | } else { |
michael@0 | 1459 | MOZ_CRASH(); |
michael@0 | 1460 | } |
michael@0 | 1461 | |
michael@0 | 1462 | return target; |
michael@0 | 1463 | } |
michael@0 | 1464 | |
michael@0 | 1465 | // Override GetOutput to get around caching. Rendering simple floods is |
michael@0 | 1466 | // comparatively fast. |
michael@0 | 1467 | TemporaryRef<DataSourceSurface> |
michael@0 | 1468 | FilterNodeFloodSoftware::GetOutput(const IntRect& aRect) |
michael@0 | 1469 | { |
michael@0 | 1470 | return Render(aRect); |
michael@0 | 1471 | } |
michael@0 | 1472 | |
michael@0 | 1473 | IntRect |
michael@0 | 1474 | FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 1475 | { |
michael@0 | 1476 | if (mColor.a == 0.0f) { |
michael@0 | 1477 | return IntRect(); |
michael@0 | 1478 | } |
michael@0 | 1479 | return aRect; |
michael@0 | 1480 | } |
michael@0 | 1481 | |
michael@0 | 1482 | int32_t |
michael@0 | 1483 | FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 1484 | { |
michael@0 | 1485 | switch (aInputEnumIndex) { |
michael@0 | 1486 | case IN_TILE_IN: return 0; |
michael@0 | 1487 | default: return -1; |
michael@0 | 1488 | } |
michael@0 | 1489 | } |
michael@0 | 1490 | |
michael@0 | 1491 | void |
michael@0 | 1492 | FilterNodeTileSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 1493 | const IntRect &aSourceRect) |
michael@0 | 1494 | { |
michael@0 | 1495 | MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT); |
michael@0 | 1496 | mSourceRect = IntRect(int32_t(aSourceRect.x), int32_t(aSourceRect.y), |
michael@0 | 1497 | int32_t(aSourceRect.width), int32_t(aSourceRect.height)); |
michael@0 | 1498 | Invalidate(); |
michael@0 | 1499 | } |
michael@0 | 1500 | |
michael@0 | 1501 | namespace { |
michael@0 | 1502 | struct CompareIntRects |
michael@0 | 1503 | { |
michael@0 | 1504 | bool operator()(const IntRect& a, const IntRect& b) const |
michael@0 | 1505 | { |
michael@0 | 1506 | if (a.x != b.x) { |
michael@0 | 1507 | return a.x < b.x; |
michael@0 | 1508 | } |
michael@0 | 1509 | if (a.y != b.y) { |
michael@0 | 1510 | return a.y < b.y; |
michael@0 | 1511 | } |
michael@0 | 1512 | if (a.width != b.width) { |
michael@0 | 1513 | return a.width < b.width; |
michael@0 | 1514 | } |
michael@0 | 1515 | return a.height < b.height; |
michael@0 | 1516 | } |
michael@0 | 1517 | }; |
michael@0 | 1518 | } |
michael@0 | 1519 | |
michael@0 | 1520 | TemporaryRef<DataSourceSurface> |
michael@0 | 1521 | FilterNodeTileSoftware::Render(const IntRect& aRect) |
michael@0 | 1522 | { |
michael@0 | 1523 | if (mSourceRect.IsEmpty()) { |
michael@0 | 1524 | return nullptr; |
michael@0 | 1525 | } |
michael@0 | 1526 | |
michael@0 | 1527 | if (mSourceRect.Contains(aRect)) { |
michael@0 | 1528 | return GetInputDataSourceSurface(IN_TILE_IN, aRect); |
michael@0 | 1529 | } |
michael@0 | 1530 | |
michael@0 | 1531 | RefPtr<DataSourceSurface> target; |
michael@0 | 1532 | |
michael@0 | 1533 | typedef std::map<IntRect, RefPtr<DataSourceSurface>, CompareIntRects> InputMap; |
michael@0 | 1534 | InputMap inputs; |
michael@0 | 1535 | |
michael@0 | 1536 | IntPoint startIndex = TileIndex(mSourceRect, aRect.TopLeft()); |
michael@0 | 1537 | IntPoint endIndex = TileIndex(mSourceRect, aRect.BottomRight()); |
michael@0 | 1538 | for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) { |
michael@0 | 1539 | for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) { |
michael@0 | 1540 | IntPoint sourceToDestOffset(ix * mSourceRect.width, |
michael@0 | 1541 | iy * mSourceRect.height); |
michael@0 | 1542 | IntRect destRect = aRect.Intersect(mSourceRect + sourceToDestOffset); |
michael@0 | 1543 | IntRect srcRect = destRect - sourceToDestOffset; |
michael@0 | 1544 | if (srcRect.IsEmpty()) { |
michael@0 | 1545 | continue; |
michael@0 | 1546 | } |
michael@0 | 1547 | |
michael@0 | 1548 | RefPtr<DataSourceSurface> input; |
michael@0 | 1549 | InputMap::iterator it = inputs.find(srcRect); |
michael@0 | 1550 | if (it == inputs.end()) { |
michael@0 | 1551 | input = GetInputDataSourceSurface(IN_TILE_IN, srcRect); |
michael@0 | 1552 | inputs[srcRect] = input; |
michael@0 | 1553 | } else { |
michael@0 | 1554 | input = it->second; |
michael@0 | 1555 | } |
michael@0 | 1556 | if (!input) { |
michael@0 | 1557 | return nullptr; |
michael@0 | 1558 | } |
michael@0 | 1559 | if (!target) { |
michael@0 | 1560 | // We delay creating the target until now because we want to use the |
michael@0 | 1561 | // same format as our input filter, and we do not actually know the |
michael@0 | 1562 | // input format before we call GetInputDataSourceSurface. |
michael@0 | 1563 | target = Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat()); |
michael@0 | 1564 | if (!target) { |
michael@0 | 1565 | return nullptr; |
michael@0 | 1566 | } |
michael@0 | 1567 | } |
michael@0 | 1568 | MOZ_ASSERT(input->GetFormat() == target->GetFormat(), "different surface formats from the same input?"); |
michael@0 | 1569 | |
michael@0 | 1570 | CopyRect(input, target, srcRect - srcRect.TopLeft(), destRect.TopLeft() - aRect.TopLeft()); |
michael@0 | 1571 | } |
michael@0 | 1572 | } |
michael@0 | 1573 | |
michael@0 | 1574 | return target; |
michael@0 | 1575 | } |
michael@0 | 1576 | |
michael@0 | 1577 | void |
michael@0 | 1578 | FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect &aRect) |
michael@0 | 1579 | { |
michael@0 | 1580 | // Do not request anything. |
michael@0 | 1581 | // Source rects for the tile filter can be discontinuous with large gaps |
michael@0 | 1582 | // between them. Requesting those from our input filter might cause it to |
michael@0 | 1583 | // render the whole bounding box of all of them, which would be wasteful. |
michael@0 | 1584 | } |
michael@0 | 1585 | |
michael@0 | 1586 | IntRect |
michael@0 | 1587 | FilterNodeTileSoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 1588 | { |
michael@0 | 1589 | return aRect; |
michael@0 | 1590 | } |
michael@0 | 1591 | |
michael@0 | 1592 | FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware() |
michael@0 | 1593 | : mDisableR(true) |
michael@0 | 1594 | , mDisableG(true) |
michael@0 | 1595 | , mDisableB(true) |
michael@0 | 1596 | , mDisableA(true) |
michael@0 | 1597 | {} |
michael@0 | 1598 | |
michael@0 | 1599 | void |
michael@0 | 1600 | FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 1601 | bool aDisable) |
michael@0 | 1602 | { |
michael@0 | 1603 | switch (aIndex) { |
michael@0 | 1604 | case ATT_TRANSFER_DISABLE_R: |
michael@0 | 1605 | mDisableR = aDisable; |
michael@0 | 1606 | break; |
michael@0 | 1607 | case ATT_TRANSFER_DISABLE_G: |
michael@0 | 1608 | mDisableG = aDisable; |
michael@0 | 1609 | break; |
michael@0 | 1610 | case ATT_TRANSFER_DISABLE_B: |
michael@0 | 1611 | mDisableB = aDisable; |
michael@0 | 1612 | break; |
michael@0 | 1613 | case ATT_TRANSFER_DISABLE_A: |
michael@0 | 1614 | mDisableA = aDisable; |
michael@0 | 1615 | break; |
michael@0 | 1616 | default: |
michael@0 | 1617 | MOZ_CRASH(); |
michael@0 | 1618 | } |
michael@0 | 1619 | Invalidate(); |
michael@0 | 1620 | } |
michael@0 | 1621 | |
michael@0 | 1622 | void |
michael@0 | 1623 | FilterNodeComponentTransferSoftware::GenerateLookupTable(ptrdiff_t aComponent, |
michael@0 | 1624 | uint8_t aTables[4][256], |
michael@0 | 1625 | bool aDisabled) |
michael@0 | 1626 | { |
michael@0 | 1627 | if (aDisabled) { |
michael@0 | 1628 | static uint8_t sIdentityLookupTable[256]; |
michael@0 | 1629 | static bool sInitializedIdentityLookupTable = false; |
michael@0 | 1630 | if (!sInitializedIdentityLookupTable) { |
michael@0 | 1631 | for (int32_t i = 0; i < 256; i++) { |
michael@0 | 1632 | sIdentityLookupTable[i] = i; |
michael@0 | 1633 | } |
michael@0 | 1634 | sInitializedIdentityLookupTable = true; |
michael@0 | 1635 | } |
michael@0 | 1636 | memcpy(aTables[aComponent], sIdentityLookupTable, 256); |
michael@0 | 1637 | } else { |
michael@0 | 1638 | FillLookupTable(aComponent, aTables[aComponent]); |
michael@0 | 1639 | } |
michael@0 | 1640 | } |
michael@0 | 1641 | |
michael@0 | 1642 | template<uint32_t BytesPerPixel> |
michael@0 | 1643 | static void TransferComponents(DataSourceSurface* aInput, |
michael@0 | 1644 | DataSourceSurface* aTarget, |
michael@0 | 1645 | const uint8_t aLookupTables[BytesPerPixel][256]) |
michael@0 | 1646 | { |
michael@0 | 1647 | MOZ_ASSERT(aInput->GetFormat() == aTarget->GetFormat(), "different formats"); |
michael@0 | 1648 | IntSize size = aInput->GetSize(); |
michael@0 | 1649 | |
michael@0 | 1650 | uint8_t* sourceData = aInput->GetData(); |
michael@0 | 1651 | uint8_t* targetData = aTarget->GetData(); |
michael@0 | 1652 | uint32_t sourceStride = aInput->Stride(); |
michael@0 | 1653 | uint32_t targetStride = aTarget->Stride(); |
michael@0 | 1654 | |
michael@0 | 1655 | for (int32_t y = 0; y < size.height; y++) { |
michael@0 | 1656 | for (int32_t x = 0; x < size.width; x++) { |
michael@0 | 1657 | uint32_t sourceIndex = y * sourceStride + x * BytesPerPixel; |
michael@0 | 1658 | uint32_t targetIndex = y * targetStride + x * BytesPerPixel; |
michael@0 | 1659 | for (uint32_t i = 0; i < BytesPerPixel; i++) { |
michael@0 | 1660 | targetData[targetIndex + i] = aLookupTables[i][sourceData[sourceIndex + i]]; |
michael@0 | 1661 | } |
michael@0 | 1662 | } |
michael@0 | 1663 | } |
michael@0 | 1664 | } |
michael@0 | 1665 | |
michael@0 | 1666 | bool |
michael@0 | 1667 | IsAllZero(uint8_t aLookupTable[256]) |
michael@0 | 1668 | { |
michael@0 | 1669 | for (int32_t i = 0; i < 256; i++) { |
michael@0 | 1670 | if (aLookupTable[i] != 0) { |
michael@0 | 1671 | return false; |
michael@0 | 1672 | } |
michael@0 | 1673 | } |
michael@0 | 1674 | return true; |
michael@0 | 1675 | } |
michael@0 | 1676 | |
michael@0 | 1677 | TemporaryRef<DataSourceSurface> |
michael@0 | 1678 | FilterNodeComponentTransferSoftware::Render(const IntRect& aRect) |
michael@0 | 1679 | { |
michael@0 | 1680 | if (mDisableR && mDisableG && mDisableB && mDisableA) { |
michael@0 | 1681 | return GetInputDataSourceSurface(IN_TRANSFER_IN, aRect); |
michael@0 | 1682 | } |
michael@0 | 1683 | |
michael@0 | 1684 | uint8_t lookupTables[4][256]; |
michael@0 | 1685 | GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R, lookupTables, mDisableR); |
michael@0 | 1686 | GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G, lookupTables, mDisableG); |
michael@0 | 1687 | GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B, lookupTables, mDisableB); |
michael@0 | 1688 | GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A, lookupTables, mDisableA); |
michael@0 | 1689 | |
michael@0 | 1690 | bool needColorChannels = |
michael@0 | 1691 | lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R][0] != 0 || |
michael@0 | 1692 | lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G][0] != 0 || |
michael@0 | 1693 | lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B][0] != 0; |
michael@0 | 1694 | |
michael@0 | 1695 | FormatHint pref = needColorChannels ? NEED_COLOR_CHANNELS : CAN_HANDLE_A8; |
michael@0 | 1696 | |
michael@0 | 1697 | RefPtr<DataSourceSurface> input = |
michael@0 | 1698 | GetInputDataSourceSurface(IN_TRANSFER_IN, aRect, pref); |
michael@0 | 1699 | if (!input) { |
michael@0 | 1700 | return nullptr; |
michael@0 | 1701 | } |
michael@0 | 1702 | |
michael@0 | 1703 | if (input->GetFormat() == SurfaceFormat::B8G8R8A8 && !needColorChannels) { |
michael@0 | 1704 | bool colorChannelsBecomeBlack = |
michael@0 | 1705 | IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) && |
michael@0 | 1706 | IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) && |
michael@0 | 1707 | IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B]); |
michael@0 | 1708 | |
michael@0 | 1709 | if (colorChannelsBecomeBlack) { |
michael@0 | 1710 | input = FilterProcessing::ExtractAlpha(input); |
michael@0 | 1711 | } |
michael@0 | 1712 | } |
michael@0 | 1713 | |
michael@0 | 1714 | SurfaceFormat format = input->GetFormat(); |
michael@0 | 1715 | if (format == SurfaceFormat::A8 && mDisableA) { |
michael@0 | 1716 | return input; |
michael@0 | 1717 | } |
michael@0 | 1718 | |
michael@0 | 1719 | RefPtr<DataSourceSurface> target = |
michael@0 | 1720 | Factory::CreateDataSourceSurface(aRect.Size(), format); |
michael@0 | 1721 | if (!target) { |
michael@0 | 1722 | return nullptr; |
michael@0 | 1723 | } |
michael@0 | 1724 | |
michael@0 | 1725 | if (format == SurfaceFormat::A8) { |
michael@0 | 1726 | TransferComponents<1>(input, target, &lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_A]); |
michael@0 | 1727 | } else { |
michael@0 | 1728 | TransferComponents<4>(input, target, lookupTables); |
michael@0 | 1729 | } |
michael@0 | 1730 | |
michael@0 | 1731 | return target; |
michael@0 | 1732 | } |
michael@0 | 1733 | |
michael@0 | 1734 | void |
michael@0 | 1735 | FilterNodeComponentTransferSoftware::RequestFromInputsForRect(const IntRect &aRect) |
michael@0 | 1736 | { |
michael@0 | 1737 | RequestInputRect(IN_TRANSFER_IN, aRect); |
michael@0 | 1738 | } |
michael@0 | 1739 | |
michael@0 | 1740 | IntRect |
michael@0 | 1741 | FilterNodeComponentTransferSoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 1742 | { |
michael@0 | 1743 | return GetInputRectInRect(IN_TRANSFER_IN, aRect); |
michael@0 | 1744 | } |
michael@0 | 1745 | |
michael@0 | 1746 | int32_t |
michael@0 | 1747 | FilterNodeComponentTransferSoftware::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 1748 | { |
michael@0 | 1749 | switch (aInputEnumIndex) { |
michael@0 | 1750 | case IN_TRANSFER_IN: return 0; |
michael@0 | 1751 | default: return -1; |
michael@0 | 1752 | } |
michael@0 | 1753 | } |
michael@0 | 1754 | |
michael@0 | 1755 | void |
michael@0 | 1756 | FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 1757 | const Float* aFloat, |
michael@0 | 1758 | uint32_t aSize) |
michael@0 | 1759 | { |
michael@0 | 1760 | std::vector<Float> table(aFloat, aFloat + aSize); |
michael@0 | 1761 | switch (aIndex) { |
michael@0 | 1762 | case ATT_TABLE_TRANSFER_TABLE_R: |
michael@0 | 1763 | mTableR = table; |
michael@0 | 1764 | break; |
michael@0 | 1765 | case ATT_TABLE_TRANSFER_TABLE_G: |
michael@0 | 1766 | mTableG = table; |
michael@0 | 1767 | break; |
michael@0 | 1768 | case ATT_TABLE_TRANSFER_TABLE_B: |
michael@0 | 1769 | mTableB = table; |
michael@0 | 1770 | break; |
michael@0 | 1771 | case ATT_TABLE_TRANSFER_TABLE_A: |
michael@0 | 1772 | mTableA = table; |
michael@0 | 1773 | break; |
michael@0 | 1774 | default: |
michael@0 | 1775 | MOZ_CRASH(); |
michael@0 | 1776 | } |
michael@0 | 1777 | Invalidate(); |
michael@0 | 1778 | } |
michael@0 | 1779 | |
michael@0 | 1780 | void |
michael@0 | 1781 | FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent, |
michael@0 | 1782 | uint8_t aTable[256]) |
michael@0 | 1783 | { |
michael@0 | 1784 | switch (aComponent) { |
michael@0 | 1785 | case B8G8R8A8_COMPONENT_BYTEOFFSET_R: |
michael@0 | 1786 | FillLookupTableImpl(mTableR, aTable); |
michael@0 | 1787 | break; |
michael@0 | 1788 | case B8G8R8A8_COMPONENT_BYTEOFFSET_G: |
michael@0 | 1789 | FillLookupTableImpl(mTableG, aTable); |
michael@0 | 1790 | break; |
michael@0 | 1791 | case B8G8R8A8_COMPONENT_BYTEOFFSET_B: |
michael@0 | 1792 | FillLookupTableImpl(mTableB, aTable); |
michael@0 | 1793 | break; |
michael@0 | 1794 | case B8G8R8A8_COMPONENT_BYTEOFFSET_A: |
michael@0 | 1795 | FillLookupTableImpl(mTableA, aTable); |
michael@0 | 1796 | break; |
michael@0 | 1797 | default: |
michael@0 | 1798 | MOZ_ASSERT(false, "unknown component"); |
michael@0 | 1799 | break; |
michael@0 | 1800 | } |
michael@0 | 1801 | } |
michael@0 | 1802 | |
michael@0 | 1803 | void |
michael@0 | 1804 | FilterNodeTableTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues, |
michael@0 | 1805 | uint8_t aTable[256]) |
michael@0 | 1806 | { |
michael@0 | 1807 | uint32_t tvLength = aTableValues.size(); |
michael@0 | 1808 | if (tvLength < 2) { |
michael@0 | 1809 | return; |
michael@0 | 1810 | } |
michael@0 | 1811 | |
michael@0 | 1812 | for (size_t i = 0; i < 256; i++) { |
michael@0 | 1813 | uint32_t k = (i * (tvLength - 1)) / 255; |
michael@0 | 1814 | Float v1 = aTableValues[k]; |
michael@0 | 1815 | Float v2 = aTableValues[std::min(k + 1, tvLength - 1)]; |
michael@0 | 1816 | int32_t val = |
michael@0 | 1817 | int32_t(255 * (v1 + (i/255.0f - k/float(tvLength-1))*(tvLength - 1)*(v2 - v1))); |
michael@0 | 1818 | val = std::min(255, val); |
michael@0 | 1819 | val = std::max(0, val); |
michael@0 | 1820 | aTable[i] = val; |
michael@0 | 1821 | } |
michael@0 | 1822 | } |
michael@0 | 1823 | |
michael@0 | 1824 | void |
michael@0 | 1825 | FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 1826 | const Float* aFloat, |
michael@0 | 1827 | uint32_t aSize) |
michael@0 | 1828 | { |
michael@0 | 1829 | std::vector<Float> discrete(aFloat, aFloat + aSize); |
michael@0 | 1830 | switch (aIndex) { |
michael@0 | 1831 | case ATT_DISCRETE_TRANSFER_TABLE_R: |
michael@0 | 1832 | mTableR = discrete; |
michael@0 | 1833 | break; |
michael@0 | 1834 | case ATT_DISCRETE_TRANSFER_TABLE_G: |
michael@0 | 1835 | mTableG = discrete; |
michael@0 | 1836 | break; |
michael@0 | 1837 | case ATT_DISCRETE_TRANSFER_TABLE_B: |
michael@0 | 1838 | mTableB = discrete; |
michael@0 | 1839 | break; |
michael@0 | 1840 | case ATT_DISCRETE_TRANSFER_TABLE_A: |
michael@0 | 1841 | mTableA = discrete; |
michael@0 | 1842 | break; |
michael@0 | 1843 | default: |
michael@0 | 1844 | MOZ_CRASH(); |
michael@0 | 1845 | } |
michael@0 | 1846 | Invalidate(); |
michael@0 | 1847 | } |
michael@0 | 1848 | |
michael@0 | 1849 | void |
michael@0 | 1850 | FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent, |
michael@0 | 1851 | uint8_t aTable[256]) |
michael@0 | 1852 | { |
michael@0 | 1853 | switch (aComponent) { |
michael@0 | 1854 | case B8G8R8A8_COMPONENT_BYTEOFFSET_R: |
michael@0 | 1855 | FillLookupTableImpl(mTableR, aTable); |
michael@0 | 1856 | break; |
michael@0 | 1857 | case B8G8R8A8_COMPONENT_BYTEOFFSET_G: |
michael@0 | 1858 | FillLookupTableImpl(mTableG, aTable); |
michael@0 | 1859 | break; |
michael@0 | 1860 | case B8G8R8A8_COMPONENT_BYTEOFFSET_B: |
michael@0 | 1861 | FillLookupTableImpl(mTableB, aTable); |
michael@0 | 1862 | break; |
michael@0 | 1863 | case B8G8R8A8_COMPONENT_BYTEOFFSET_A: |
michael@0 | 1864 | FillLookupTableImpl(mTableA, aTable); |
michael@0 | 1865 | break; |
michael@0 | 1866 | default: |
michael@0 | 1867 | MOZ_ASSERT(false, "unknown component"); |
michael@0 | 1868 | break; |
michael@0 | 1869 | } |
michael@0 | 1870 | } |
michael@0 | 1871 | |
michael@0 | 1872 | void |
michael@0 | 1873 | FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues, |
michael@0 | 1874 | uint8_t aTable[256]) |
michael@0 | 1875 | { |
michael@0 | 1876 | uint32_t tvLength = aTableValues.size(); |
michael@0 | 1877 | if (tvLength < 1) { |
michael@0 | 1878 | return; |
michael@0 | 1879 | } |
michael@0 | 1880 | |
michael@0 | 1881 | for (size_t i = 0; i < 256; i++) { |
michael@0 | 1882 | uint32_t k = (i * tvLength) / 255; |
michael@0 | 1883 | k = std::min(k, tvLength - 1); |
michael@0 | 1884 | Float v = aTableValues[k]; |
michael@0 | 1885 | int32_t val = NS_lround(255 * v); |
michael@0 | 1886 | val = std::min(255, val); |
michael@0 | 1887 | val = std::max(0, val); |
michael@0 | 1888 | aTable[i] = val; |
michael@0 | 1889 | } |
michael@0 | 1890 | } |
michael@0 | 1891 | |
michael@0 | 1892 | FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware() |
michael@0 | 1893 | : mSlopeR(0) |
michael@0 | 1894 | , mSlopeG(0) |
michael@0 | 1895 | , mSlopeB(0) |
michael@0 | 1896 | , mSlopeA(0) |
michael@0 | 1897 | , mInterceptR(0) |
michael@0 | 1898 | , mInterceptG(0) |
michael@0 | 1899 | , mInterceptB(0) |
michael@0 | 1900 | , mInterceptA(0) |
michael@0 | 1901 | {} |
michael@0 | 1902 | |
michael@0 | 1903 | void |
michael@0 | 1904 | FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 1905 | Float aValue) |
michael@0 | 1906 | { |
michael@0 | 1907 | switch (aIndex) { |
michael@0 | 1908 | case ATT_LINEAR_TRANSFER_SLOPE_R: |
michael@0 | 1909 | mSlopeR = aValue; |
michael@0 | 1910 | break; |
michael@0 | 1911 | case ATT_LINEAR_TRANSFER_INTERCEPT_R: |
michael@0 | 1912 | mInterceptR = aValue; |
michael@0 | 1913 | break; |
michael@0 | 1914 | case ATT_LINEAR_TRANSFER_SLOPE_G: |
michael@0 | 1915 | mSlopeG = aValue; |
michael@0 | 1916 | break; |
michael@0 | 1917 | case ATT_LINEAR_TRANSFER_INTERCEPT_G: |
michael@0 | 1918 | mInterceptG = aValue; |
michael@0 | 1919 | break; |
michael@0 | 1920 | case ATT_LINEAR_TRANSFER_SLOPE_B: |
michael@0 | 1921 | mSlopeB = aValue; |
michael@0 | 1922 | break; |
michael@0 | 1923 | case ATT_LINEAR_TRANSFER_INTERCEPT_B: |
michael@0 | 1924 | mInterceptB = aValue; |
michael@0 | 1925 | break; |
michael@0 | 1926 | case ATT_LINEAR_TRANSFER_SLOPE_A: |
michael@0 | 1927 | mSlopeA = aValue; |
michael@0 | 1928 | break; |
michael@0 | 1929 | case ATT_LINEAR_TRANSFER_INTERCEPT_A: |
michael@0 | 1930 | mInterceptA = aValue; |
michael@0 | 1931 | break; |
michael@0 | 1932 | default: |
michael@0 | 1933 | MOZ_CRASH(); |
michael@0 | 1934 | } |
michael@0 | 1935 | Invalidate(); |
michael@0 | 1936 | } |
michael@0 | 1937 | |
michael@0 | 1938 | void |
michael@0 | 1939 | FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent, |
michael@0 | 1940 | uint8_t aTable[256]) |
michael@0 | 1941 | { |
michael@0 | 1942 | switch (aComponent) { |
michael@0 | 1943 | case B8G8R8A8_COMPONENT_BYTEOFFSET_R: |
michael@0 | 1944 | FillLookupTableImpl(mSlopeR, mInterceptR, aTable); |
michael@0 | 1945 | break; |
michael@0 | 1946 | case B8G8R8A8_COMPONENT_BYTEOFFSET_G: |
michael@0 | 1947 | FillLookupTableImpl(mSlopeG, mInterceptG, aTable); |
michael@0 | 1948 | break; |
michael@0 | 1949 | case B8G8R8A8_COMPONENT_BYTEOFFSET_B: |
michael@0 | 1950 | FillLookupTableImpl(mSlopeB, mInterceptB, aTable); |
michael@0 | 1951 | break; |
michael@0 | 1952 | case B8G8R8A8_COMPONENT_BYTEOFFSET_A: |
michael@0 | 1953 | FillLookupTableImpl(mSlopeA, mInterceptA, aTable); |
michael@0 | 1954 | break; |
michael@0 | 1955 | default: |
michael@0 | 1956 | MOZ_ASSERT(false, "unknown component"); |
michael@0 | 1957 | break; |
michael@0 | 1958 | } |
michael@0 | 1959 | } |
michael@0 | 1960 | |
michael@0 | 1961 | void |
michael@0 | 1962 | FilterNodeLinearTransferSoftware::FillLookupTableImpl(Float aSlope, |
michael@0 | 1963 | Float aIntercept, |
michael@0 | 1964 | uint8_t aTable[256]) |
michael@0 | 1965 | { |
michael@0 | 1966 | for (size_t i = 0; i < 256; i++) { |
michael@0 | 1967 | int32_t val = NS_lround(aSlope * i + 255 * aIntercept); |
michael@0 | 1968 | val = std::min(255, val); |
michael@0 | 1969 | val = std::max(0, val); |
michael@0 | 1970 | aTable[i] = val; |
michael@0 | 1971 | } |
michael@0 | 1972 | } |
michael@0 | 1973 | |
michael@0 | 1974 | FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware() |
michael@0 | 1975 | : mAmplitudeR(0) |
michael@0 | 1976 | , mAmplitudeG(0) |
michael@0 | 1977 | , mAmplitudeB(0) |
michael@0 | 1978 | , mAmplitudeA(0) |
michael@0 | 1979 | , mExponentR(0) |
michael@0 | 1980 | , mExponentG(0) |
michael@0 | 1981 | , mExponentB(0) |
michael@0 | 1982 | , mExponentA(0) |
michael@0 | 1983 | {} |
michael@0 | 1984 | |
michael@0 | 1985 | void |
michael@0 | 1986 | FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 1987 | Float aValue) |
michael@0 | 1988 | { |
michael@0 | 1989 | switch (aIndex) { |
michael@0 | 1990 | case ATT_GAMMA_TRANSFER_AMPLITUDE_R: |
michael@0 | 1991 | mAmplitudeR = aValue; |
michael@0 | 1992 | break; |
michael@0 | 1993 | case ATT_GAMMA_TRANSFER_EXPONENT_R: |
michael@0 | 1994 | mExponentR = aValue; |
michael@0 | 1995 | break; |
michael@0 | 1996 | case ATT_GAMMA_TRANSFER_OFFSET_R: |
michael@0 | 1997 | mOffsetR = aValue; |
michael@0 | 1998 | break; |
michael@0 | 1999 | case ATT_GAMMA_TRANSFER_AMPLITUDE_G: |
michael@0 | 2000 | mAmplitudeG = aValue; |
michael@0 | 2001 | break; |
michael@0 | 2002 | case ATT_GAMMA_TRANSFER_EXPONENT_G: |
michael@0 | 2003 | mExponentG = aValue; |
michael@0 | 2004 | break; |
michael@0 | 2005 | case ATT_GAMMA_TRANSFER_OFFSET_G: |
michael@0 | 2006 | mOffsetG = aValue; |
michael@0 | 2007 | break; |
michael@0 | 2008 | case ATT_GAMMA_TRANSFER_AMPLITUDE_B: |
michael@0 | 2009 | mAmplitudeB = aValue; |
michael@0 | 2010 | break; |
michael@0 | 2011 | case ATT_GAMMA_TRANSFER_EXPONENT_B: |
michael@0 | 2012 | mExponentB = aValue; |
michael@0 | 2013 | break; |
michael@0 | 2014 | case ATT_GAMMA_TRANSFER_OFFSET_B: |
michael@0 | 2015 | mOffsetB = aValue; |
michael@0 | 2016 | break; |
michael@0 | 2017 | case ATT_GAMMA_TRANSFER_AMPLITUDE_A: |
michael@0 | 2018 | mAmplitudeA = aValue; |
michael@0 | 2019 | break; |
michael@0 | 2020 | case ATT_GAMMA_TRANSFER_EXPONENT_A: |
michael@0 | 2021 | mExponentA = aValue; |
michael@0 | 2022 | break; |
michael@0 | 2023 | case ATT_GAMMA_TRANSFER_OFFSET_A: |
michael@0 | 2024 | mOffsetA = aValue; |
michael@0 | 2025 | break; |
michael@0 | 2026 | default: |
michael@0 | 2027 | MOZ_CRASH(); |
michael@0 | 2028 | } |
michael@0 | 2029 | Invalidate(); |
michael@0 | 2030 | } |
michael@0 | 2031 | |
michael@0 | 2032 | void |
michael@0 | 2033 | FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent, |
michael@0 | 2034 | uint8_t aTable[256]) |
michael@0 | 2035 | { |
michael@0 | 2036 | switch (aComponent) { |
michael@0 | 2037 | case B8G8R8A8_COMPONENT_BYTEOFFSET_R: |
michael@0 | 2038 | FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable); |
michael@0 | 2039 | break; |
michael@0 | 2040 | case B8G8R8A8_COMPONENT_BYTEOFFSET_G: |
michael@0 | 2041 | FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable); |
michael@0 | 2042 | break; |
michael@0 | 2043 | case B8G8R8A8_COMPONENT_BYTEOFFSET_B: |
michael@0 | 2044 | FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable); |
michael@0 | 2045 | break; |
michael@0 | 2046 | case B8G8R8A8_COMPONENT_BYTEOFFSET_A: |
michael@0 | 2047 | FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable); |
michael@0 | 2048 | break; |
michael@0 | 2049 | default: |
michael@0 | 2050 | MOZ_ASSERT(false, "unknown component"); |
michael@0 | 2051 | break; |
michael@0 | 2052 | } |
michael@0 | 2053 | } |
michael@0 | 2054 | |
michael@0 | 2055 | void |
michael@0 | 2056 | FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude, |
michael@0 | 2057 | Float aExponent, |
michael@0 | 2058 | Float aOffset, |
michael@0 | 2059 | uint8_t aTable[256]) |
michael@0 | 2060 | { |
michael@0 | 2061 | for (size_t i = 0; i < 256; i++) { |
michael@0 | 2062 | int32_t val = NS_lround(255 * (aAmplitude * pow(i / 255.0f, aExponent) + aOffset)); |
michael@0 | 2063 | val = std::min(255, val); |
michael@0 | 2064 | val = std::max(0, val); |
michael@0 | 2065 | aTable[i] = val; |
michael@0 | 2066 | } |
michael@0 | 2067 | } |
michael@0 | 2068 | |
michael@0 | 2069 | FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware() |
michael@0 | 2070 | : mDivisor(0) |
michael@0 | 2071 | , mBias(0) |
michael@0 | 2072 | , mEdgeMode(EDGE_MODE_DUPLICATE) |
michael@0 | 2073 | , mPreserveAlpha(false) |
michael@0 | 2074 | {} |
michael@0 | 2075 | |
michael@0 | 2076 | int32_t |
michael@0 | 2077 | FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 2078 | { |
michael@0 | 2079 | switch (aInputEnumIndex) { |
michael@0 | 2080 | case IN_CONVOLVE_MATRIX_IN: return 0; |
michael@0 | 2081 | default: return -1; |
michael@0 | 2082 | } |
michael@0 | 2083 | } |
michael@0 | 2084 | |
michael@0 | 2085 | void |
michael@0 | 2086 | FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 2087 | const IntSize &aKernelSize) |
michael@0 | 2088 | { |
michael@0 | 2089 | MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_SIZE); |
michael@0 | 2090 | mKernelSize = aKernelSize; |
michael@0 | 2091 | Invalidate(); |
michael@0 | 2092 | } |
michael@0 | 2093 | |
michael@0 | 2094 | void |
michael@0 | 2095 | FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 2096 | const Float *aMatrix, |
michael@0 | 2097 | uint32_t aSize) |
michael@0 | 2098 | { |
michael@0 | 2099 | MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_MATRIX); |
michael@0 | 2100 | mKernelMatrix = std::vector<Float>(aMatrix, aMatrix + aSize); |
michael@0 | 2101 | Invalidate(); |
michael@0 | 2102 | } |
michael@0 | 2103 | |
michael@0 | 2104 | void |
michael@0 | 2105 | FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, Float aValue) |
michael@0 | 2106 | { |
michael@0 | 2107 | switch (aIndex) { |
michael@0 | 2108 | case ATT_CONVOLVE_MATRIX_DIVISOR: |
michael@0 | 2109 | mDivisor = aValue; |
michael@0 | 2110 | break; |
michael@0 | 2111 | case ATT_CONVOLVE_MATRIX_BIAS: |
michael@0 | 2112 | mBias = aValue; |
michael@0 | 2113 | break; |
michael@0 | 2114 | default: |
michael@0 | 2115 | MOZ_CRASH(); |
michael@0 | 2116 | } |
michael@0 | 2117 | Invalidate(); |
michael@0 | 2118 | } |
michael@0 | 2119 | |
michael@0 | 2120 | void |
michael@0 | 2121 | FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength) |
michael@0 | 2122 | { |
michael@0 | 2123 | switch (aIndex) { |
michael@0 | 2124 | case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH: |
michael@0 | 2125 | mKernelUnitLength = aKernelUnitLength; |
michael@0 | 2126 | break; |
michael@0 | 2127 | default: |
michael@0 | 2128 | MOZ_CRASH(); |
michael@0 | 2129 | } |
michael@0 | 2130 | Invalidate(); |
michael@0 | 2131 | } |
michael@0 | 2132 | |
michael@0 | 2133 | void |
michael@0 | 2134 | FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 2135 | const IntPoint &aTarget) |
michael@0 | 2136 | { |
michael@0 | 2137 | MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_TARGET); |
michael@0 | 2138 | mTarget = aTarget; |
michael@0 | 2139 | Invalidate(); |
michael@0 | 2140 | } |
michael@0 | 2141 | |
michael@0 | 2142 | void |
michael@0 | 2143 | FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 2144 | const IntRect &aSourceRect) |
michael@0 | 2145 | { |
michael@0 | 2146 | MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_SOURCE_RECT); |
michael@0 | 2147 | mSourceRect = aSourceRect; |
michael@0 | 2148 | Invalidate(); |
michael@0 | 2149 | } |
michael@0 | 2150 | |
michael@0 | 2151 | void |
michael@0 | 2152 | FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 2153 | uint32_t aEdgeMode) |
michael@0 | 2154 | { |
michael@0 | 2155 | MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_EDGE_MODE); |
michael@0 | 2156 | mEdgeMode = static_cast<ConvolveMatrixEdgeMode>(aEdgeMode); |
michael@0 | 2157 | Invalidate(); |
michael@0 | 2158 | } |
michael@0 | 2159 | |
michael@0 | 2160 | void |
michael@0 | 2161 | FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 2162 | bool aPreserveAlpha) |
michael@0 | 2163 | { |
michael@0 | 2164 | MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA); |
michael@0 | 2165 | mPreserveAlpha = aPreserveAlpha; |
michael@0 | 2166 | Invalidate(); |
michael@0 | 2167 | } |
michael@0 | 2168 | |
michael@0 | 2169 | #ifdef DEBUG |
michael@0 | 2170 | static bool sColorSamplingAccessControlEnabled = false; |
michael@0 | 2171 | static uint8_t* sColorSamplingAccessControlStart = nullptr; |
michael@0 | 2172 | static uint8_t* sColorSamplingAccessControlEnd = nullptr; |
michael@0 | 2173 | |
michael@0 | 2174 | struct DebugOnlyAutoColorSamplingAccessControl |
michael@0 | 2175 | { |
michael@0 | 2176 | DebugOnlyAutoColorSamplingAccessControl(DataSourceSurface* aSurface) |
michael@0 | 2177 | { |
michael@0 | 2178 | sColorSamplingAccessControlStart = aSurface->GetData(); |
michael@0 | 2179 | sColorSamplingAccessControlEnd = sColorSamplingAccessControlStart + |
michael@0 | 2180 | aSurface->Stride() * aSurface->GetSize().height; |
michael@0 | 2181 | sColorSamplingAccessControlEnabled = true; |
michael@0 | 2182 | } |
michael@0 | 2183 | |
michael@0 | 2184 | ~DebugOnlyAutoColorSamplingAccessControl() |
michael@0 | 2185 | { |
michael@0 | 2186 | sColorSamplingAccessControlEnabled = false; |
michael@0 | 2187 | } |
michael@0 | 2188 | }; |
michael@0 | 2189 | |
michael@0 | 2190 | static inline void |
michael@0 | 2191 | DebugOnlyCheckColorSamplingAccess(const uint8_t* aSampleAddress) |
michael@0 | 2192 | { |
michael@0 | 2193 | if (sColorSamplingAccessControlEnabled) { |
michael@0 | 2194 | MOZ_ASSERT(aSampleAddress >= sColorSamplingAccessControlStart, "accessing before start"); |
michael@0 | 2195 | MOZ_ASSERT(aSampleAddress < sColorSamplingAccessControlEnd, "accessing after end"); |
michael@0 | 2196 | } |
michael@0 | 2197 | } |
michael@0 | 2198 | #else |
michael@0 | 2199 | typedef DebugOnly<DataSourceSurface*> DebugOnlyAutoColorSamplingAccessControl; |
michael@0 | 2200 | #define DebugOnlyCheckColorSamplingAccess(address) |
michael@0 | 2201 | #endif |
michael@0 | 2202 | |
michael@0 | 2203 | static inline uint8_t |
michael@0 | 2204 | ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y, size_t bpp, ptrdiff_t c) |
michael@0 | 2205 | { |
michael@0 | 2206 | DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c]); |
michael@0 | 2207 | return aData[y * aStride + bpp * x + c]; |
michael@0 | 2208 | } |
michael@0 | 2209 | |
michael@0 | 2210 | static inline int32_t |
michael@0 | 2211 | ColorAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y) |
michael@0 | 2212 | { |
michael@0 | 2213 | DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x); |
michael@0 | 2214 | return *(uint32_t*)(aData + y * aStride + 4 * x); |
michael@0 | 2215 | } |
michael@0 | 2216 | |
michael@0 | 2217 | // Accepts fractional x & y and does bilinear interpolation. |
michael@0 | 2218 | // Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible. |
michael@0 | 2219 | static inline uint8_t |
michael@0 | 2220 | ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, Float x, Float y, size_t bpp, ptrdiff_t c) |
michael@0 | 2221 | { |
michael@0 | 2222 | const uint32_t f = 256; |
michael@0 | 2223 | const int32_t lx = floor(x); |
michael@0 | 2224 | const int32_t ly = floor(y); |
michael@0 | 2225 | const int32_t tux = uint32_t((x - lx) * f); |
michael@0 | 2226 | const int32_t tlx = f - tux; |
michael@0 | 2227 | const int32_t tuy = uint32_t((y - ly) * f); |
michael@0 | 2228 | const int32_t tly = f - tuy; |
michael@0 | 2229 | const uint8_t &cll = ColorComponentAtPoint(aData, aStride, lx, ly, bpp, c); |
michael@0 | 2230 | const uint8_t &cul = ColorComponentAtPoint(aData, aStride, lx + 1, ly, bpp, c); |
michael@0 | 2231 | const uint8_t &clu = ColorComponentAtPoint(aData, aStride, lx, ly + 1, bpp, c); |
michael@0 | 2232 | const uint8_t &cuu = ColorComponentAtPoint(aData, aStride, lx + 1, ly + 1, bpp, c); |
michael@0 | 2233 | return ((cll * tlx + cul * tux) * tly + |
michael@0 | 2234 | (clu * tlx + cuu * tux) * tuy + f * f / 2) / (f * f); |
michael@0 | 2235 | } |
michael@0 | 2236 | |
michael@0 | 2237 | static inline uint32_t |
michael@0 | 2238 | ColorAtPoint(const uint8_t *aData, int32_t aStride, Float x, Float y) |
michael@0 | 2239 | { |
michael@0 | 2240 | return ColorComponentAtPoint(aData, aStride, x, y, 4, 0) | |
michael@0 | 2241 | (ColorComponentAtPoint(aData, aStride, x, y, 4, 1) << 8) | |
michael@0 | 2242 | (ColorComponentAtPoint(aData, aStride, x, y, 4, 2) << 16) | |
michael@0 | 2243 | (ColorComponentAtPoint(aData, aStride, x, y, 4, 3) << 24); |
michael@0 | 2244 | } |
michael@0 | 2245 | |
michael@0 | 2246 | static int32_t |
michael@0 | 2247 | ClampToNonZero(int32_t a) |
michael@0 | 2248 | { |
michael@0 | 2249 | return a * (a >= 0); |
michael@0 | 2250 | } |
michael@0 | 2251 | |
michael@0 | 2252 | template<typename CoordType> |
michael@0 | 2253 | static void |
michael@0 | 2254 | ConvolvePixel(const uint8_t *aSourceData, |
michael@0 | 2255 | uint8_t *aTargetData, |
michael@0 | 2256 | int32_t aWidth, int32_t aHeight, |
michael@0 | 2257 | int32_t aSourceStride, int32_t aTargetStride, |
michael@0 | 2258 | int32_t aX, int32_t aY, |
michael@0 | 2259 | const int32_t *aKernel, |
michael@0 | 2260 | int32_t aBias, int32_t shiftL, int32_t shiftR, |
michael@0 | 2261 | bool aPreserveAlpha, |
michael@0 | 2262 | int32_t aOrderX, int32_t aOrderY, |
michael@0 | 2263 | int32_t aTargetX, int32_t aTargetY, |
michael@0 | 2264 | CoordType aKernelUnitLengthX, |
michael@0 | 2265 | CoordType aKernelUnitLengthY) |
michael@0 | 2266 | { |
michael@0 | 2267 | int32_t sum[4] = {0, 0, 0, 0}; |
michael@0 | 2268 | int32_t offsets[4] = { B8G8R8A8_COMPONENT_BYTEOFFSET_R, |
michael@0 | 2269 | B8G8R8A8_COMPONENT_BYTEOFFSET_G, |
michael@0 | 2270 | B8G8R8A8_COMPONENT_BYTEOFFSET_B, |
michael@0 | 2271 | B8G8R8A8_COMPONENT_BYTEOFFSET_A }; |
michael@0 | 2272 | int32_t channels = aPreserveAlpha ? 3 : 4; |
michael@0 | 2273 | int32_t roundingAddition = shiftL == 0 ? 0 : 1 << (shiftL - 1); |
michael@0 | 2274 | |
michael@0 | 2275 | for (int32_t y = 0; y < aOrderY; y++) { |
michael@0 | 2276 | CoordType sampleY = aY + (y - aTargetY) * aKernelUnitLengthY; |
michael@0 | 2277 | for (int32_t x = 0; x < aOrderX; x++) { |
michael@0 | 2278 | CoordType sampleX = aX + (x - aTargetX) * aKernelUnitLengthX; |
michael@0 | 2279 | for (int32_t i = 0; i < channels; i++) { |
michael@0 | 2280 | sum[i] += aKernel[aOrderX * y + x] * |
michael@0 | 2281 | ColorComponentAtPoint(aSourceData, aSourceStride, |
michael@0 | 2282 | sampleX, sampleY, 4, offsets[i]); |
michael@0 | 2283 | } |
michael@0 | 2284 | } |
michael@0 | 2285 | } |
michael@0 | 2286 | for (int32_t i = 0; i < channels; i++) { |
michael@0 | 2287 | int32_t clamped = umin(ClampToNonZero(sum[i] + aBias), 255 << shiftL >> shiftR); |
michael@0 | 2288 | aTargetData[aY * aTargetStride + 4 * aX + offsets[i]] = |
michael@0 | 2289 | (clamped + roundingAddition) << shiftR >> shiftL; |
michael@0 | 2290 | } |
michael@0 | 2291 | if (aPreserveAlpha) { |
michael@0 | 2292 | aTargetData[aY * aTargetStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A] = |
michael@0 | 2293 | aSourceData[aY * aSourceStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A]; |
michael@0 | 2294 | } |
michael@0 | 2295 | } |
michael@0 | 2296 | |
michael@0 | 2297 | TemporaryRef<DataSourceSurface> |
michael@0 | 2298 | FilterNodeConvolveMatrixSoftware::Render(const IntRect& aRect) |
michael@0 | 2299 | { |
michael@0 | 2300 | if (mKernelUnitLength.width == floor(mKernelUnitLength.width) && |
michael@0 | 2301 | mKernelUnitLength.height == floor(mKernelUnitLength.height)) { |
michael@0 | 2302 | return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height); |
michael@0 | 2303 | } |
michael@0 | 2304 | return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height); |
michael@0 | 2305 | } |
michael@0 | 2306 | |
michael@0 | 2307 | static std::vector<Float> |
michael@0 | 2308 | ReversedVector(const std::vector<Float> &aVector) |
michael@0 | 2309 | { |
michael@0 | 2310 | size_t length = aVector.size(); |
michael@0 | 2311 | std::vector<Float> result(length, 0); |
michael@0 | 2312 | for (size_t i = 0; i < length; i++) { |
michael@0 | 2313 | result[length - 1 - i] = aVector[i]; |
michael@0 | 2314 | } |
michael@0 | 2315 | return result; |
michael@0 | 2316 | } |
michael@0 | 2317 | |
michael@0 | 2318 | static std::vector<Float> |
michael@0 | 2319 | ScaledVector(const std::vector<Float> &aVector, Float aDivisor) |
michael@0 | 2320 | { |
michael@0 | 2321 | size_t length = aVector.size(); |
michael@0 | 2322 | std::vector<Float> result(length, 0); |
michael@0 | 2323 | for (size_t i = 0; i < length; i++) { |
michael@0 | 2324 | result[i] = aVector[i] / aDivisor; |
michael@0 | 2325 | } |
michael@0 | 2326 | return result; |
michael@0 | 2327 | } |
michael@0 | 2328 | |
michael@0 | 2329 | static Float |
michael@0 | 2330 | MaxVectorSum(const std::vector<Float> &aVector) |
michael@0 | 2331 | { |
michael@0 | 2332 | Float sum = 0; |
michael@0 | 2333 | size_t length = aVector.size(); |
michael@0 | 2334 | for (size_t i = 0; i < length; i++) { |
michael@0 | 2335 | if (aVector[i] > 0) { |
michael@0 | 2336 | sum += aVector[i]; |
michael@0 | 2337 | } |
michael@0 | 2338 | } |
michael@0 | 2339 | return sum; |
michael@0 | 2340 | } |
michael@0 | 2341 | |
michael@0 | 2342 | // Returns shiftL and shiftR in such a way that |
michael@0 | 2343 | // a << shiftL >> shiftR is roughly a * aFloat. |
michael@0 | 2344 | static void |
michael@0 | 2345 | TranslateDoubleToShifts(double aDouble, int32_t &aShiftL, int32_t &aShiftR) |
michael@0 | 2346 | { |
michael@0 | 2347 | aShiftL = 0; |
michael@0 | 2348 | aShiftR = 0; |
michael@0 | 2349 | if (aDouble <= 0) { |
michael@0 | 2350 | MOZ_CRASH(); |
michael@0 | 2351 | } |
michael@0 | 2352 | if (aDouble < 1) { |
michael@0 | 2353 | while (1 << (aShiftR + 1) < 1 / aDouble) { |
michael@0 | 2354 | aShiftR++; |
michael@0 | 2355 | } |
michael@0 | 2356 | } else { |
michael@0 | 2357 | while (1 << (aShiftL + 1) < aDouble) { |
michael@0 | 2358 | aShiftL++; |
michael@0 | 2359 | } |
michael@0 | 2360 | } |
michael@0 | 2361 | } |
michael@0 | 2362 | |
michael@0 | 2363 | template<typename CoordType> |
michael@0 | 2364 | TemporaryRef<DataSourceSurface> |
michael@0 | 2365 | FilterNodeConvolveMatrixSoftware::DoRender(const IntRect& aRect, |
michael@0 | 2366 | CoordType aKernelUnitLengthX, |
michael@0 | 2367 | CoordType aKernelUnitLengthY) |
michael@0 | 2368 | { |
michael@0 | 2369 | if (mKernelSize.width <= 0 || mKernelSize.height <= 0 || |
michael@0 | 2370 | mKernelMatrix.size() != uint32_t(mKernelSize.width * mKernelSize.height) || |
michael@0 | 2371 | !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) || |
michael@0 | 2372 | mDivisor == 0) { |
michael@0 | 2373 | return Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8); |
michael@0 | 2374 | } |
michael@0 | 2375 | |
michael@0 | 2376 | IntRect srcRect = InflatedSourceRect(aRect); |
michael@0 | 2377 | |
michael@0 | 2378 | // Inflate the source rect by another pixel because the bilinear filtering in |
michael@0 | 2379 | // ColorComponentAtPoint may want to access the margins. |
michael@0 | 2380 | srcRect.Inflate(1); |
michael@0 | 2381 | |
michael@0 | 2382 | RefPtr<DataSourceSurface> input = |
michael@0 | 2383 | GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect, NEED_COLOR_CHANNELS, mEdgeMode, &mSourceRect); |
michael@0 | 2384 | |
michael@0 | 2385 | if (!input) { |
michael@0 | 2386 | return nullptr; |
michael@0 | 2387 | } |
michael@0 | 2388 | |
michael@0 | 2389 | DebugOnlyAutoColorSamplingAccessControl accessControl(input); |
michael@0 | 2390 | |
michael@0 | 2391 | RefPtr<DataSourceSurface> target = |
michael@0 | 2392 | Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8); |
michael@0 | 2393 | if (!target) { |
michael@0 | 2394 | return nullptr; |
michael@0 | 2395 | } |
michael@0 | 2396 | ClearDataSourceSurface(target); |
michael@0 | 2397 | |
michael@0 | 2398 | IntPoint offset = aRect.TopLeft() - srcRect.TopLeft(); |
michael@0 | 2399 | |
michael@0 | 2400 | uint8_t* sourceData = DataAtOffset(input, offset); |
michael@0 | 2401 | int32_t sourceStride = input->Stride(); |
michael@0 | 2402 | uint8_t* targetData = target->GetData(); |
michael@0 | 2403 | int32_t targetStride = target->Stride(); |
michael@0 | 2404 | |
michael@0 | 2405 | // Why exactly are we reversing the kernel? |
michael@0 | 2406 | std::vector<Float> kernel = ReversedVector(mKernelMatrix); |
michael@0 | 2407 | kernel = ScaledVector(kernel, mDivisor); |
michael@0 | 2408 | Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias, |
michael@0 | 2409 | MaxVectorSum(ScaledVector(kernel, -1)) - mBias); |
michael@0 | 2410 | maxResultAbs = std::max(maxResultAbs, 1.0f); |
michael@0 | 2411 | |
michael@0 | 2412 | double idealFactor = INT32_MAX / 2.0 / maxResultAbs / 255.0 * 0.999; |
michael@0 | 2413 | MOZ_ASSERT(255.0 * maxResultAbs * idealFactor <= INT32_MAX / 2.0, "badly chosen float-to-int scale"); |
michael@0 | 2414 | int32_t shiftL, shiftR; |
michael@0 | 2415 | TranslateDoubleToShifts(idealFactor, shiftL, shiftR); |
michael@0 | 2416 | double factorFromShifts = Float(1 << shiftL) / Float(1 << shiftR); |
michael@0 | 2417 | MOZ_ASSERT(255.0 * maxResultAbs * factorFromShifts <= INT32_MAX / 2.0, "badly chosen float-to-int scale"); |
michael@0 | 2418 | |
michael@0 | 2419 | int32_t* intKernel = new int32_t[kernel.size()]; |
michael@0 | 2420 | for (size_t i = 0; i < kernel.size(); i++) { |
michael@0 | 2421 | intKernel[i] = NS_lround(kernel[i] * factorFromShifts); |
michael@0 | 2422 | } |
michael@0 | 2423 | int32_t bias = NS_lround(mBias * 255 * factorFromShifts); |
michael@0 | 2424 | |
michael@0 | 2425 | for (int32_t y = 0; y < aRect.height; y++) { |
michael@0 | 2426 | for (int32_t x = 0; x < aRect.width; x++) { |
michael@0 | 2427 | ConvolvePixel(sourceData, targetData, |
michael@0 | 2428 | aRect.width, aRect.height, sourceStride, targetStride, |
michael@0 | 2429 | x, y, intKernel, bias, shiftL, shiftR, mPreserveAlpha, |
michael@0 | 2430 | mKernelSize.width, mKernelSize.height, mTarget.x, mTarget.y, |
michael@0 | 2431 | aKernelUnitLengthX, aKernelUnitLengthY); |
michael@0 | 2432 | } |
michael@0 | 2433 | } |
michael@0 | 2434 | delete[] intKernel; |
michael@0 | 2435 | |
michael@0 | 2436 | return target; |
michael@0 | 2437 | } |
michael@0 | 2438 | |
michael@0 | 2439 | void |
michael@0 | 2440 | FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect) |
michael@0 | 2441 | { |
michael@0 | 2442 | RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect)); |
michael@0 | 2443 | } |
michael@0 | 2444 | |
michael@0 | 2445 | IntRect |
michael@0 | 2446 | FilterNodeConvolveMatrixSoftware::InflatedSourceRect(const IntRect &aDestRect) |
michael@0 | 2447 | { |
michael@0 | 2448 | if (aDestRect.IsEmpty()) { |
michael@0 | 2449 | return IntRect(); |
michael@0 | 2450 | } |
michael@0 | 2451 | |
michael@0 | 2452 | IntMargin margin; |
michael@0 | 2453 | margin.left = ceil(mTarget.x * mKernelUnitLength.width); |
michael@0 | 2454 | margin.top = ceil(mTarget.y * mKernelUnitLength.height); |
michael@0 | 2455 | margin.right = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width); |
michael@0 | 2456 | margin.bottom = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height); |
michael@0 | 2457 | |
michael@0 | 2458 | IntRect srcRect = aDestRect; |
michael@0 | 2459 | srcRect.Inflate(margin); |
michael@0 | 2460 | return srcRect; |
michael@0 | 2461 | } |
michael@0 | 2462 | |
michael@0 | 2463 | IntRect |
michael@0 | 2464 | FilterNodeConvolveMatrixSoftware::InflatedDestRect(const IntRect &aSourceRect) |
michael@0 | 2465 | { |
michael@0 | 2466 | if (aSourceRect.IsEmpty()) { |
michael@0 | 2467 | return IntRect(); |
michael@0 | 2468 | } |
michael@0 | 2469 | |
michael@0 | 2470 | IntMargin margin; |
michael@0 | 2471 | margin.left = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width); |
michael@0 | 2472 | margin.top = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height); |
michael@0 | 2473 | margin.right = ceil(mTarget.x * mKernelUnitLength.width); |
michael@0 | 2474 | margin.bottom = ceil(mTarget.y * mKernelUnitLength.height); |
michael@0 | 2475 | |
michael@0 | 2476 | IntRect destRect = aSourceRect; |
michael@0 | 2477 | destRect.Inflate(margin); |
michael@0 | 2478 | return destRect; |
michael@0 | 2479 | } |
michael@0 | 2480 | |
michael@0 | 2481 | IntRect |
michael@0 | 2482 | FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 2483 | { |
michael@0 | 2484 | IntRect srcRequest = InflatedSourceRect(aRect); |
michael@0 | 2485 | IntRect srcOutput = GetInputRectInRect(IN_COLOR_MATRIX_IN, srcRequest); |
michael@0 | 2486 | return InflatedDestRect(srcOutput).Intersect(aRect); |
michael@0 | 2487 | } |
michael@0 | 2488 | |
michael@0 | 2489 | FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware() |
michael@0 | 2490 | : mScale(0.0f) |
michael@0 | 2491 | , mChannelX(COLOR_CHANNEL_R) |
michael@0 | 2492 | , mChannelY(COLOR_CHANNEL_G) |
michael@0 | 2493 | {} |
michael@0 | 2494 | |
michael@0 | 2495 | int32_t |
michael@0 | 2496 | FilterNodeDisplacementMapSoftware::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 2497 | { |
michael@0 | 2498 | switch (aInputEnumIndex) { |
michael@0 | 2499 | case IN_DISPLACEMENT_MAP_IN: return 0; |
michael@0 | 2500 | case IN_DISPLACEMENT_MAP_IN2: return 1; |
michael@0 | 2501 | default: return -1; |
michael@0 | 2502 | } |
michael@0 | 2503 | } |
michael@0 | 2504 | |
michael@0 | 2505 | void |
michael@0 | 2506 | FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 2507 | Float aScale) |
michael@0 | 2508 | { |
michael@0 | 2509 | MOZ_ASSERT(aIndex == ATT_DISPLACEMENT_MAP_SCALE); |
michael@0 | 2510 | mScale = aScale; |
michael@0 | 2511 | Invalidate(); |
michael@0 | 2512 | } |
michael@0 | 2513 | |
michael@0 | 2514 | void |
michael@0 | 2515 | FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue) |
michael@0 | 2516 | { |
michael@0 | 2517 | switch (aIndex) { |
michael@0 | 2518 | case ATT_DISPLACEMENT_MAP_X_CHANNEL: |
michael@0 | 2519 | mChannelX = static_cast<ColorChannel>(aValue); |
michael@0 | 2520 | break; |
michael@0 | 2521 | case ATT_DISPLACEMENT_MAP_Y_CHANNEL: |
michael@0 | 2522 | mChannelY = static_cast<ColorChannel>(aValue); |
michael@0 | 2523 | break; |
michael@0 | 2524 | default: |
michael@0 | 2525 | MOZ_CRASH(); |
michael@0 | 2526 | } |
michael@0 | 2527 | Invalidate(); |
michael@0 | 2528 | } |
michael@0 | 2529 | |
michael@0 | 2530 | TemporaryRef<DataSourceSurface> |
michael@0 | 2531 | FilterNodeDisplacementMapSoftware::Render(const IntRect& aRect) |
michael@0 | 2532 | { |
michael@0 | 2533 | IntRect srcRect = InflatedSourceOrDestRect(aRect); |
michael@0 | 2534 | RefPtr<DataSourceSurface> input = |
michael@0 | 2535 | GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN, srcRect, NEED_COLOR_CHANNELS); |
michael@0 | 2536 | RefPtr<DataSourceSurface> map = |
michael@0 | 2537 | GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN2, aRect, NEED_COLOR_CHANNELS); |
michael@0 | 2538 | RefPtr<DataSourceSurface> target = |
michael@0 | 2539 | Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8); |
michael@0 | 2540 | if (!input || !map || !target) { |
michael@0 | 2541 | return nullptr; |
michael@0 | 2542 | } |
michael@0 | 2543 | |
michael@0 | 2544 | IntPoint offset = aRect.TopLeft() - srcRect.TopLeft(); |
michael@0 | 2545 | |
michael@0 | 2546 | uint8_t* sourceData = DataAtOffset(input, offset); |
michael@0 | 2547 | int32_t sourceStride = input->Stride(); |
michael@0 | 2548 | uint8_t* mapData = map->GetData(); |
michael@0 | 2549 | int32_t mapStride = map->Stride(); |
michael@0 | 2550 | uint8_t* targetData = target->GetData(); |
michael@0 | 2551 | int32_t targetStride = target->Stride(); |
michael@0 | 2552 | |
michael@0 | 2553 | static const ptrdiff_t channelMap[4] = { |
michael@0 | 2554 | B8G8R8A8_COMPONENT_BYTEOFFSET_R, |
michael@0 | 2555 | B8G8R8A8_COMPONENT_BYTEOFFSET_G, |
michael@0 | 2556 | B8G8R8A8_COMPONENT_BYTEOFFSET_B, |
michael@0 | 2557 | B8G8R8A8_COMPONENT_BYTEOFFSET_A }; |
michael@0 | 2558 | uint16_t xChannel = channelMap[mChannelX]; |
michael@0 | 2559 | uint16_t yChannel = channelMap[mChannelY]; |
michael@0 | 2560 | |
michael@0 | 2561 | float scaleOver255 = mScale / 255.0f; |
michael@0 | 2562 | float scaleAdjustment = -0.5f * mScale; |
michael@0 | 2563 | |
michael@0 | 2564 | for (int32_t y = 0; y < aRect.height; y++) { |
michael@0 | 2565 | for (int32_t x = 0; x < aRect.width; x++) { |
michael@0 | 2566 | uint32_t mapIndex = y * mapStride + 4 * x; |
michael@0 | 2567 | uint32_t targIndex = y * targetStride + 4 * x; |
michael@0 | 2568 | int32_t sourceX = x + |
michael@0 | 2569 | scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment; |
michael@0 | 2570 | int32_t sourceY = y + |
michael@0 | 2571 | scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment; |
michael@0 | 2572 | *(uint32_t*)(targetData + targIndex) = |
michael@0 | 2573 | ColorAtPoint(sourceData, sourceStride, sourceX, sourceY); |
michael@0 | 2574 | } |
michael@0 | 2575 | } |
michael@0 | 2576 | |
michael@0 | 2577 | return target; |
michael@0 | 2578 | } |
michael@0 | 2579 | |
michael@0 | 2580 | void |
michael@0 | 2581 | FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(const IntRect &aRect) |
michael@0 | 2582 | { |
michael@0 | 2583 | RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect)); |
michael@0 | 2584 | RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect); |
michael@0 | 2585 | } |
michael@0 | 2586 | |
michael@0 | 2587 | IntRect |
michael@0 | 2588 | FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect) |
michael@0 | 2589 | { |
michael@0 | 2590 | IntRect sourceOrDestRect = aDestOrSourceRect; |
michael@0 | 2591 | sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2)); |
michael@0 | 2592 | return sourceOrDestRect; |
michael@0 | 2593 | } |
michael@0 | 2594 | |
michael@0 | 2595 | IntRect |
michael@0 | 2596 | FilterNodeDisplacementMapSoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 2597 | { |
michael@0 | 2598 | IntRect srcRequest = InflatedSourceOrDestRect(aRect); |
michael@0 | 2599 | IntRect srcOutput = GetInputRectInRect(IN_DISPLACEMENT_MAP_IN, srcRequest); |
michael@0 | 2600 | return InflatedSourceOrDestRect(srcOutput).Intersect(aRect); |
michael@0 | 2601 | } |
michael@0 | 2602 | |
michael@0 | 2603 | FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware() |
michael@0 | 2604 | : mNumOctaves(0) |
michael@0 | 2605 | , mSeed(0) |
michael@0 | 2606 | , mStitchable(false) |
michael@0 | 2607 | , mType(TURBULENCE_TYPE_TURBULENCE) |
michael@0 | 2608 | {} |
michael@0 | 2609 | |
michael@0 | 2610 | int32_t |
michael@0 | 2611 | FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 2612 | { |
michael@0 | 2613 | return -1; |
michael@0 | 2614 | } |
michael@0 | 2615 | |
michael@0 | 2616 | void |
michael@0 | 2617 | FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const Size &aBaseFrequency) |
michael@0 | 2618 | { |
michael@0 | 2619 | switch (aIndex) { |
michael@0 | 2620 | case ATT_TURBULENCE_BASE_FREQUENCY: |
michael@0 | 2621 | mBaseFrequency = aBaseFrequency; |
michael@0 | 2622 | break; |
michael@0 | 2623 | default: |
michael@0 | 2624 | MOZ_CRASH(); |
michael@0 | 2625 | break; |
michael@0 | 2626 | } |
michael@0 | 2627 | Invalidate(); |
michael@0 | 2628 | } |
michael@0 | 2629 | |
michael@0 | 2630 | void |
michael@0 | 2631 | FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const IntRect &aRect) |
michael@0 | 2632 | { |
michael@0 | 2633 | switch (aIndex) { |
michael@0 | 2634 | case ATT_TURBULENCE_RECT: |
michael@0 | 2635 | mRenderRect = aRect; |
michael@0 | 2636 | break; |
michael@0 | 2637 | default: |
michael@0 | 2638 | MOZ_CRASH(); |
michael@0 | 2639 | break; |
michael@0 | 2640 | } |
michael@0 | 2641 | Invalidate(); |
michael@0 | 2642 | } |
michael@0 | 2643 | |
michael@0 | 2644 | void |
michael@0 | 2645 | FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, bool aStitchable) |
michael@0 | 2646 | { |
michael@0 | 2647 | MOZ_ASSERT(aIndex == ATT_TURBULENCE_STITCHABLE); |
michael@0 | 2648 | mStitchable = aStitchable; |
michael@0 | 2649 | Invalidate(); |
michael@0 | 2650 | } |
michael@0 | 2651 | |
michael@0 | 2652 | void |
michael@0 | 2653 | FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue) |
michael@0 | 2654 | { |
michael@0 | 2655 | switch (aIndex) { |
michael@0 | 2656 | case ATT_TURBULENCE_NUM_OCTAVES: |
michael@0 | 2657 | mNumOctaves = aValue; |
michael@0 | 2658 | break; |
michael@0 | 2659 | case ATT_TURBULENCE_SEED: |
michael@0 | 2660 | mSeed = aValue; |
michael@0 | 2661 | break; |
michael@0 | 2662 | case ATT_TURBULENCE_TYPE: |
michael@0 | 2663 | mType = static_cast<TurbulenceType>(aValue); |
michael@0 | 2664 | break; |
michael@0 | 2665 | default: |
michael@0 | 2666 | MOZ_CRASH(); |
michael@0 | 2667 | break; |
michael@0 | 2668 | } |
michael@0 | 2669 | Invalidate(); |
michael@0 | 2670 | } |
michael@0 | 2671 | |
michael@0 | 2672 | TemporaryRef<DataSourceSurface> |
michael@0 | 2673 | FilterNodeTurbulenceSoftware::Render(const IntRect& aRect) |
michael@0 | 2674 | { |
michael@0 | 2675 | return FilterProcessing::RenderTurbulence( |
michael@0 | 2676 | aRect.Size(), aRect.TopLeft(), mBaseFrequency, |
michael@0 | 2677 | mSeed, mNumOctaves, mType, mStitchable, Rect(mRenderRect)); |
michael@0 | 2678 | } |
michael@0 | 2679 | |
michael@0 | 2680 | IntRect |
michael@0 | 2681 | FilterNodeTurbulenceSoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 2682 | { |
michael@0 | 2683 | return aRect.Intersect(mRenderRect); |
michael@0 | 2684 | } |
michael@0 | 2685 | |
michael@0 | 2686 | FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware() |
michael@0 | 2687 | : mK1(0), mK2(0), mK3(0), mK4(0) |
michael@0 | 2688 | { |
michael@0 | 2689 | } |
michael@0 | 2690 | |
michael@0 | 2691 | int32_t |
michael@0 | 2692 | FilterNodeArithmeticCombineSoftware::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 2693 | { |
michael@0 | 2694 | switch (aInputEnumIndex) { |
michael@0 | 2695 | case IN_ARITHMETIC_COMBINE_IN: return 0; |
michael@0 | 2696 | case IN_ARITHMETIC_COMBINE_IN2: return 1; |
michael@0 | 2697 | default: return -1; |
michael@0 | 2698 | } |
michael@0 | 2699 | } |
michael@0 | 2700 | |
michael@0 | 2701 | void |
michael@0 | 2702 | FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 2703 | const Float* aFloat, |
michael@0 | 2704 | uint32_t aSize) |
michael@0 | 2705 | { |
michael@0 | 2706 | MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS); |
michael@0 | 2707 | MOZ_ASSERT(aSize == 4); |
michael@0 | 2708 | |
michael@0 | 2709 | mK1 = aFloat[0]; |
michael@0 | 2710 | mK2 = aFloat[1]; |
michael@0 | 2711 | mK3 = aFloat[2]; |
michael@0 | 2712 | mK4 = aFloat[3]; |
michael@0 | 2713 | |
michael@0 | 2714 | Invalidate(); |
michael@0 | 2715 | } |
michael@0 | 2716 | |
michael@0 | 2717 | TemporaryRef<DataSourceSurface> |
michael@0 | 2718 | FilterNodeArithmeticCombineSoftware::Render(const IntRect& aRect) |
michael@0 | 2719 | { |
michael@0 | 2720 | RefPtr<DataSourceSurface> input1 = |
michael@0 | 2721 | GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS); |
michael@0 | 2722 | RefPtr<DataSourceSurface> input2 = |
michael@0 | 2723 | GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN2, aRect, NEED_COLOR_CHANNELS); |
michael@0 | 2724 | if (!input1 && !input2) { |
michael@0 | 2725 | return nullptr; |
michael@0 | 2726 | } |
michael@0 | 2727 | |
michael@0 | 2728 | // If one input is null, treat it as transparent by adjusting the factors. |
michael@0 | 2729 | Float k1 = mK1, k2 = mK2, k3 = mK3, k4 = mK4; |
michael@0 | 2730 | if (!input1) { |
michael@0 | 2731 | k1 = 0.0f; |
michael@0 | 2732 | k2 = 0.0f; |
michael@0 | 2733 | input1 = input2; |
michael@0 | 2734 | } |
michael@0 | 2735 | |
michael@0 | 2736 | if (!input2) { |
michael@0 | 2737 | k1 = 0.0f; |
michael@0 | 2738 | k3 = 0.0f; |
michael@0 | 2739 | input2 = input1; |
michael@0 | 2740 | } |
michael@0 | 2741 | |
michael@0 | 2742 | return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3, k4); |
michael@0 | 2743 | } |
michael@0 | 2744 | |
michael@0 | 2745 | void |
michael@0 | 2746 | FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(const IntRect &aRect) |
michael@0 | 2747 | { |
michael@0 | 2748 | RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect); |
michael@0 | 2749 | RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect); |
michael@0 | 2750 | } |
michael@0 | 2751 | |
michael@0 | 2752 | IntRect |
michael@0 | 2753 | FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 2754 | { |
michael@0 | 2755 | if (mK4 > 0.0f) { |
michael@0 | 2756 | return aRect; |
michael@0 | 2757 | } |
michael@0 | 2758 | IntRect rectFrom1 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect); |
michael@0 | 2759 | IntRect rectFrom2 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2, aRect).Intersect(aRect); |
michael@0 | 2760 | IntRect result; |
michael@0 | 2761 | if (mK1 > 0.0f) { |
michael@0 | 2762 | result = rectFrom1.Intersect(rectFrom2); |
michael@0 | 2763 | } |
michael@0 | 2764 | if (mK2 > 0.0f) { |
michael@0 | 2765 | result = result.Union(rectFrom1); |
michael@0 | 2766 | } |
michael@0 | 2767 | if (mK3 > 0.0f) { |
michael@0 | 2768 | result = result.Union(rectFrom2); |
michael@0 | 2769 | } |
michael@0 | 2770 | return result; |
michael@0 | 2771 | } |
michael@0 | 2772 | |
michael@0 | 2773 | FilterNodeCompositeSoftware::FilterNodeCompositeSoftware() |
michael@0 | 2774 | : mOperator(COMPOSITE_OPERATOR_OVER) |
michael@0 | 2775 | {} |
michael@0 | 2776 | |
michael@0 | 2777 | int32_t |
michael@0 | 2778 | FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 2779 | { |
michael@0 | 2780 | return aInputEnumIndex - IN_COMPOSITE_IN_START; |
michael@0 | 2781 | } |
michael@0 | 2782 | |
michael@0 | 2783 | void |
michael@0 | 2784 | FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex, uint32_t aCompositeOperator) |
michael@0 | 2785 | { |
michael@0 | 2786 | MOZ_ASSERT(aIndex == ATT_COMPOSITE_OPERATOR); |
michael@0 | 2787 | mOperator = static_cast<CompositeOperator>(aCompositeOperator); |
michael@0 | 2788 | Invalidate(); |
michael@0 | 2789 | } |
michael@0 | 2790 | |
michael@0 | 2791 | TemporaryRef<DataSourceSurface> |
michael@0 | 2792 | FilterNodeCompositeSoftware::Render(const IntRect& aRect) |
michael@0 | 2793 | { |
michael@0 | 2794 | RefPtr<DataSourceSurface> start = |
michael@0 | 2795 | GetInputDataSourceSurface(IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS); |
michael@0 | 2796 | RefPtr<DataSourceSurface> dest = |
michael@0 | 2797 | Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8); |
michael@0 | 2798 | if (!dest) { |
michael@0 | 2799 | return nullptr; |
michael@0 | 2800 | } |
michael@0 | 2801 | |
michael@0 | 2802 | if (start) { |
michael@0 | 2803 | CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint()); |
michael@0 | 2804 | } else { |
michael@0 | 2805 | ClearDataSourceSurface(dest); |
michael@0 | 2806 | } |
michael@0 | 2807 | |
michael@0 | 2808 | for (size_t inputIndex = 1; inputIndex < NumberOfSetInputs(); inputIndex++) { |
michael@0 | 2809 | RefPtr<DataSourceSurface> input = |
michael@0 | 2810 | GetInputDataSourceSurface(IN_COMPOSITE_IN_START + inputIndex, aRect, NEED_COLOR_CHANNELS); |
michael@0 | 2811 | if (input) { |
michael@0 | 2812 | FilterProcessing::ApplyComposition(input, dest, mOperator); |
michael@0 | 2813 | } else { |
michael@0 | 2814 | // We need to treat input as transparent. Depending on the composite |
michael@0 | 2815 | // operator, different things happen to dest. |
michael@0 | 2816 | switch (mOperator) { |
michael@0 | 2817 | case COMPOSITE_OPERATOR_OVER: |
michael@0 | 2818 | case COMPOSITE_OPERATOR_ATOP: |
michael@0 | 2819 | case COMPOSITE_OPERATOR_XOR: |
michael@0 | 2820 | // dest is unchanged. |
michael@0 | 2821 | break; |
michael@0 | 2822 | case COMPOSITE_OPERATOR_OUT: |
michael@0 | 2823 | // dest is now transparent, but it can become non-transparent again |
michael@0 | 2824 | // when compositing additional inputs. |
michael@0 | 2825 | ClearDataSourceSurface(dest); |
michael@0 | 2826 | break; |
michael@0 | 2827 | case COMPOSITE_OPERATOR_IN: |
michael@0 | 2828 | // Transparency always wins. We're completely transparent now and |
michael@0 | 2829 | // no additional input can get rid of that transparency. |
michael@0 | 2830 | return nullptr; |
michael@0 | 2831 | } |
michael@0 | 2832 | } |
michael@0 | 2833 | } |
michael@0 | 2834 | return dest; |
michael@0 | 2835 | } |
michael@0 | 2836 | |
michael@0 | 2837 | void |
michael@0 | 2838 | FilterNodeCompositeSoftware::RequestFromInputsForRect(const IntRect &aRect) |
michael@0 | 2839 | { |
michael@0 | 2840 | for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) { |
michael@0 | 2841 | RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect); |
michael@0 | 2842 | } |
michael@0 | 2843 | } |
michael@0 | 2844 | |
michael@0 | 2845 | IntRect |
michael@0 | 2846 | FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 2847 | { |
michael@0 | 2848 | IntRect rect; |
michael@0 | 2849 | for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) { |
michael@0 | 2850 | IntRect inputRect = GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect); |
michael@0 | 2851 | if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) { |
michael@0 | 2852 | rect = rect.Intersect(inputRect); |
michael@0 | 2853 | } else { |
michael@0 | 2854 | rect = rect.Union(inputRect); |
michael@0 | 2855 | } |
michael@0 | 2856 | } |
michael@0 | 2857 | return rect; |
michael@0 | 2858 | } |
michael@0 | 2859 | |
michael@0 | 2860 | int32_t |
michael@0 | 2861 | FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 2862 | { |
michael@0 | 2863 | switch (aInputEnumIndex) { |
michael@0 | 2864 | case IN_GAUSSIAN_BLUR_IN: return 0; |
michael@0 | 2865 | default: return -1; |
michael@0 | 2866 | } |
michael@0 | 2867 | } |
michael@0 | 2868 | |
michael@0 | 2869 | TemporaryRef<DataSourceSurface> |
michael@0 | 2870 | FilterNodeBlurXYSoftware::Render(const IntRect& aRect) |
michael@0 | 2871 | { |
michael@0 | 2872 | Size sigmaXY = StdDeviationXY(); |
michael@0 | 2873 | IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height)); |
michael@0 | 2874 | |
michael@0 | 2875 | if (d.width == 0 && d.height == 0) { |
michael@0 | 2876 | return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, aRect); |
michael@0 | 2877 | } |
michael@0 | 2878 | |
michael@0 | 2879 | IntRect srcRect = InflatedSourceOrDestRect(aRect); |
michael@0 | 2880 | RefPtr<DataSourceSurface> input = |
michael@0 | 2881 | GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, srcRect); |
michael@0 | 2882 | if (!input) { |
michael@0 | 2883 | return nullptr; |
michael@0 | 2884 | } |
michael@0 | 2885 | |
michael@0 | 2886 | RefPtr<DataSourceSurface> target; |
michael@0 | 2887 | Rect r(0, 0, srcRect.width, srcRect.height); |
michael@0 | 2888 | |
michael@0 | 2889 | if (input->GetFormat() == SurfaceFormat::A8) { |
michael@0 | 2890 | target = Factory::CreateDataSourceSurface(srcRect.Size(), SurfaceFormat::A8); |
michael@0 | 2891 | CopyRect(input, target, IntRect(IntPoint(), input->GetSize()), IntPoint()); |
michael@0 | 2892 | AlphaBoxBlur blur(r, target->Stride(), sigmaXY.width, sigmaXY.height); |
michael@0 | 2893 | blur.Blur(target->GetData()); |
michael@0 | 2894 | } else { |
michael@0 | 2895 | RefPtr<DataSourceSurface> channel0, channel1, channel2, channel3; |
michael@0 | 2896 | FilterProcessing::SeparateColorChannels(input, channel0, channel1, channel2, channel3); |
michael@0 | 2897 | AlphaBoxBlur blur(r, channel0->Stride(), sigmaXY.width, sigmaXY.height); |
michael@0 | 2898 | blur.Blur(channel0->GetData()); |
michael@0 | 2899 | blur.Blur(channel1->GetData()); |
michael@0 | 2900 | blur.Blur(channel2->GetData()); |
michael@0 | 2901 | blur.Blur(channel3->GetData()); |
michael@0 | 2902 | target = FilterProcessing::CombineColorChannels(channel0, channel1, channel2, channel3); |
michael@0 | 2903 | } |
michael@0 | 2904 | |
michael@0 | 2905 | return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE); |
michael@0 | 2906 | } |
michael@0 | 2907 | |
michael@0 | 2908 | void |
michael@0 | 2909 | FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect &aRect) |
michael@0 | 2910 | { |
michael@0 | 2911 | RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect)); |
michael@0 | 2912 | } |
michael@0 | 2913 | |
michael@0 | 2914 | IntRect |
michael@0 | 2915 | FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(const IntRect &aDestRect) |
michael@0 | 2916 | { |
michael@0 | 2917 | Size sigmaXY = StdDeviationXY(); |
michael@0 | 2918 | IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height)); |
michael@0 | 2919 | IntRect srcRect = aDestRect; |
michael@0 | 2920 | srcRect.Inflate(d); |
michael@0 | 2921 | return srcRect; |
michael@0 | 2922 | } |
michael@0 | 2923 | |
michael@0 | 2924 | IntRect |
michael@0 | 2925 | FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 2926 | { |
michael@0 | 2927 | IntRect srcRequest = InflatedSourceOrDestRect(aRect); |
michael@0 | 2928 | IntRect srcOutput = GetInputRectInRect(IN_GAUSSIAN_BLUR_IN, srcRequest); |
michael@0 | 2929 | return InflatedSourceOrDestRect(srcOutput).Intersect(aRect); |
michael@0 | 2930 | } |
michael@0 | 2931 | |
michael@0 | 2932 | FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware() |
michael@0 | 2933 | : mStdDeviation(0) |
michael@0 | 2934 | {} |
michael@0 | 2935 | |
michael@0 | 2936 | void |
michael@0 | 2937 | FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 2938 | float aStdDeviation) |
michael@0 | 2939 | { |
michael@0 | 2940 | switch (aIndex) { |
michael@0 | 2941 | case ATT_GAUSSIAN_BLUR_STD_DEVIATION: |
michael@0 | 2942 | mStdDeviation = std::max(0.0f, aStdDeviation); |
michael@0 | 2943 | break; |
michael@0 | 2944 | default: |
michael@0 | 2945 | MOZ_CRASH(); |
michael@0 | 2946 | } |
michael@0 | 2947 | Invalidate(); |
michael@0 | 2948 | } |
michael@0 | 2949 | |
michael@0 | 2950 | Size |
michael@0 | 2951 | FilterNodeGaussianBlurSoftware::StdDeviationXY() |
michael@0 | 2952 | { |
michael@0 | 2953 | return Size(mStdDeviation, mStdDeviation); |
michael@0 | 2954 | } |
michael@0 | 2955 | |
michael@0 | 2956 | FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware() |
michael@0 | 2957 | : mBlurDirection(BLUR_DIRECTION_X) |
michael@0 | 2958 | {} |
michael@0 | 2959 | |
michael@0 | 2960 | void |
michael@0 | 2961 | FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 2962 | Float aStdDeviation) |
michael@0 | 2963 | { |
michael@0 | 2964 | switch (aIndex) { |
michael@0 | 2965 | case ATT_DIRECTIONAL_BLUR_STD_DEVIATION: |
michael@0 | 2966 | mStdDeviation = std::max(0.0f, aStdDeviation); |
michael@0 | 2967 | break; |
michael@0 | 2968 | default: |
michael@0 | 2969 | MOZ_CRASH(); |
michael@0 | 2970 | } |
michael@0 | 2971 | Invalidate(); |
michael@0 | 2972 | } |
michael@0 | 2973 | |
michael@0 | 2974 | void |
michael@0 | 2975 | FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 2976 | uint32_t aBlurDirection) |
michael@0 | 2977 | { |
michael@0 | 2978 | switch (aIndex) { |
michael@0 | 2979 | case ATT_DIRECTIONAL_BLUR_DIRECTION: |
michael@0 | 2980 | mBlurDirection = (BlurDirection)aBlurDirection; |
michael@0 | 2981 | break; |
michael@0 | 2982 | default: |
michael@0 | 2983 | MOZ_CRASH(); |
michael@0 | 2984 | } |
michael@0 | 2985 | Invalidate(); |
michael@0 | 2986 | } |
michael@0 | 2987 | |
michael@0 | 2988 | Size |
michael@0 | 2989 | FilterNodeDirectionalBlurSoftware::StdDeviationXY() |
michael@0 | 2990 | { |
michael@0 | 2991 | float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0; |
michael@0 | 2992 | float sigmaY = mBlurDirection == BLUR_DIRECTION_Y ? mStdDeviation : 0; |
michael@0 | 2993 | return Size(sigmaX, sigmaY); |
michael@0 | 2994 | } |
michael@0 | 2995 | |
michael@0 | 2996 | int32_t |
michael@0 | 2997 | FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 2998 | { |
michael@0 | 2999 | switch (aInputEnumIndex) { |
michael@0 | 3000 | case IN_CROP_IN: return 0; |
michael@0 | 3001 | default: return -1; |
michael@0 | 3002 | } |
michael@0 | 3003 | } |
michael@0 | 3004 | |
michael@0 | 3005 | void |
michael@0 | 3006 | FilterNodeCropSoftware::SetAttribute(uint32_t aIndex, |
michael@0 | 3007 | const Rect &aSourceRect) |
michael@0 | 3008 | { |
michael@0 | 3009 | MOZ_ASSERT(aIndex == ATT_CROP_RECT); |
michael@0 | 3010 | Rect srcRect = aSourceRect; |
michael@0 | 3011 | srcRect.Round(); |
michael@0 | 3012 | if (!srcRect.ToIntRect(&mCropRect)) { |
michael@0 | 3013 | mCropRect = IntRect(); |
michael@0 | 3014 | } |
michael@0 | 3015 | Invalidate(); |
michael@0 | 3016 | } |
michael@0 | 3017 | |
michael@0 | 3018 | TemporaryRef<DataSourceSurface> |
michael@0 | 3019 | FilterNodeCropSoftware::Render(const IntRect& aRect) |
michael@0 | 3020 | { |
michael@0 | 3021 | return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect)); |
michael@0 | 3022 | } |
michael@0 | 3023 | |
michael@0 | 3024 | void |
michael@0 | 3025 | FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect &aRect) |
michael@0 | 3026 | { |
michael@0 | 3027 | RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect)); |
michael@0 | 3028 | } |
michael@0 | 3029 | |
michael@0 | 3030 | IntRect |
michael@0 | 3031 | FilterNodeCropSoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 3032 | { |
michael@0 | 3033 | return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect); |
michael@0 | 3034 | } |
michael@0 | 3035 | |
michael@0 | 3036 | int32_t |
michael@0 | 3037 | FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 3038 | { |
michael@0 | 3039 | switch (aInputEnumIndex) { |
michael@0 | 3040 | case IN_PREMULTIPLY_IN: return 0; |
michael@0 | 3041 | default: return -1; |
michael@0 | 3042 | } |
michael@0 | 3043 | } |
michael@0 | 3044 | |
michael@0 | 3045 | TemporaryRef<DataSourceSurface> |
michael@0 | 3046 | FilterNodePremultiplySoftware::Render(const IntRect& aRect) |
michael@0 | 3047 | { |
michael@0 | 3048 | RefPtr<DataSourceSurface> input = |
michael@0 | 3049 | GetInputDataSourceSurface(IN_PREMULTIPLY_IN, aRect); |
michael@0 | 3050 | return input ? Premultiply(input) : nullptr; |
michael@0 | 3051 | } |
michael@0 | 3052 | |
michael@0 | 3053 | void |
michael@0 | 3054 | FilterNodePremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect) |
michael@0 | 3055 | { |
michael@0 | 3056 | RequestInputRect(IN_PREMULTIPLY_IN, aRect); |
michael@0 | 3057 | } |
michael@0 | 3058 | |
michael@0 | 3059 | IntRect |
michael@0 | 3060 | FilterNodePremultiplySoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 3061 | { |
michael@0 | 3062 | return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect); |
michael@0 | 3063 | } |
michael@0 | 3064 | |
michael@0 | 3065 | int32_t |
michael@0 | 3066 | FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 3067 | { |
michael@0 | 3068 | switch (aInputEnumIndex) { |
michael@0 | 3069 | case IN_UNPREMULTIPLY_IN: return 0; |
michael@0 | 3070 | default: return -1; |
michael@0 | 3071 | } |
michael@0 | 3072 | } |
michael@0 | 3073 | |
michael@0 | 3074 | TemporaryRef<DataSourceSurface> |
michael@0 | 3075 | FilterNodeUnpremultiplySoftware::Render(const IntRect& aRect) |
michael@0 | 3076 | { |
michael@0 | 3077 | RefPtr<DataSourceSurface> input = |
michael@0 | 3078 | GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN, aRect); |
michael@0 | 3079 | return input ? Unpremultiply(input) : nullptr; |
michael@0 | 3080 | } |
michael@0 | 3081 | |
michael@0 | 3082 | void |
michael@0 | 3083 | FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect) |
michael@0 | 3084 | { |
michael@0 | 3085 | RequestInputRect(IN_UNPREMULTIPLY_IN, aRect); |
michael@0 | 3086 | } |
michael@0 | 3087 | |
michael@0 | 3088 | IntRect |
michael@0 | 3089 | FilterNodeUnpremultiplySoftware::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 3090 | { |
michael@0 | 3091 | return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect); |
michael@0 | 3092 | } |
michael@0 | 3093 | |
michael@0 | 3094 | bool |
michael@0 | 3095 | PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint) |
michael@0 | 3096 | { |
michael@0 | 3097 | switch (aIndex) { |
michael@0 | 3098 | case ATT_POINT_LIGHT_POSITION: |
michael@0 | 3099 | mPosition = aPoint; |
michael@0 | 3100 | break; |
michael@0 | 3101 | default: |
michael@0 | 3102 | return false; |
michael@0 | 3103 | } |
michael@0 | 3104 | return true; |
michael@0 | 3105 | } |
michael@0 | 3106 | |
michael@0 | 3107 | SpotLightSoftware::SpotLightSoftware() |
michael@0 | 3108 | : mSpecularFocus(0) |
michael@0 | 3109 | , mLimitingConeAngle(0) |
michael@0 | 3110 | , mLimitingConeCos(1) |
michael@0 | 3111 | { |
michael@0 | 3112 | } |
michael@0 | 3113 | |
michael@0 | 3114 | bool |
michael@0 | 3115 | SpotLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint) |
michael@0 | 3116 | { |
michael@0 | 3117 | switch (aIndex) { |
michael@0 | 3118 | case ATT_SPOT_LIGHT_POSITION: |
michael@0 | 3119 | mPosition = aPoint; |
michael@0 | 3120 | break; |
michael@0 | 3121 | case ATT_SPOT_LIGHT_POINTS_AT: |
michael@0 | 3122 | mPointsAt = aPoint; |
michael@0 | 3123 | break; |
michael@0 | 3124 | default: |
michael@0 | 3125 | return false; |
michael@0 | 3126 | } |
michael@0 | 3127 | return true; |
michael@0 | 3128 | } |
michael@0 | 3129 | |
michael@0 | 3130 | bool |
michael@0 | 3131 | SpotLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) |
michael@0 | 3132 | { |
michael@0 | 3133 | switch (aIndex) { |
michael@0 | 3134 | case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE: |
michael@0 | 3135 | mLimitingConeAngle = aValue; |
michael@0 | 3136 | break; |
michael@0 | 3137 | case ATT_SPOT_LIGHT_FOCUS: |
michael@0 | 3138 | mSpecularFocus = aValue; |
michael@0 | 3139 | break; |
michael@0 | 3140 | default: |
michael@0 | 3141 | return false; |
michael@0 | 3142 | } |
michael@0 | 3143 | return true; |
michael@0 | 3144 | } |
michael@0 | 3145 | |
michael@0 | 3146 | DistantLightSoftware::DistantLightSoftware() |
michael@0 | 3147 | : mAzimuth(0) |
michael@0 | 3148 | , mElevation(0) |
michael@0 | 3149 | { |
michael@0 | 3150 | } |
michael@0 | 3151 | |
michael@0 | 3152 | bool |
michael@0 | 3153 | DistantLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) |
michael@0 | 3154 | { |
michael@0 | 3155 | switch (aIndex) { |
michael@0 | 3156 | case ATT_DISTANT_LIGHT_AZIMUTH: |
michael@0 | 3157 | mAzimuth = aValue; |
michael@0 | 3158 | break; |
michael@0 | 3159 | case ATT_DISTANT_LIGHT_ELEVATION: |
michael@0 | 3160 | mElevation = aValue; |
michael@0 | 3161 | break; |
michael@0 | 3162 | default: |
michael@0 | 3163 | return false; |
michael@0 | 3164 | } |
michael@0 | 3165 | return true; |
michael@0 | 3166 | } |
michael@0 | 3167 | |
michael@0 | 3168 | static inline Point3D Normalized(const Point3D &vec) { |
michael@0 | 3169 | Point3D copy(vec); |
michael@0 | 3170 | copy.Normalize(); |
michael@0 | 3171 | return copy; |
michael@0 | 3172 | } |
michael@0 | 3173 | |
michael@0 | 3174 | template<typename LightType, typename LightingType> |
michael@0 | 3175 | FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware(const char* aTypeName) |
michael@0 | 3176 | : mSurfaceScale(0) |
michael@0 | 3177 | #if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING)) |
michael@0 | 3178 | , mTypeName(aTypeName) |
michael@0 | 3179 | #endif |
michael@0 | 3180 | {} |
michael@0 | 3181 | |
michael@0 | 3182 | template<typename LightType, typename LightingType> |
michael@0 | 3183 | int32_t |
michael@0 | 3184 | FilterNodeLightingSoftware<LightType, LightingType>::InputIndex(uint32_t aInputEnumIndex) |
michael@0 | 3185 | { |
michael@0 | 3186 | switch (aInputEnumIndex) { |
michael@0 | 3187 | case IN_LIGHTING_IN: return 0; |
michael@0 | 3188 | default: return -1; |
michael@0 | 3189 | } |
michael@0 | 3190 | } |
michael@0 | 3191 | |
michael@0 | 3192 | template<typename LightType, typename LightingType> |
michael@0 | 3193 | void |
michael@0 | 3194 | FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Point3D &aPoint) |
michael@0 | 3195 | { |
michael@0 | 3196 | if (mLight.SetAttribute(aIndex, aPoint)) { |
michael@0 | 3197 | Invalidate(); |
michael@0 | 3198 | return; |
michael@0 | 3199 | } |
michael@0 | 3200 | MOZ_CRASH(); |
michael@0 | 3201 | } |
michael@0 | 3202 | |
michael@0 | 3203 | template<typename LightType, typename LightingType> |
michael@0 | 3204 | void |
michael@0 | 3205 | FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, Float aValue) |
michael@0 | 3206 | { |
michael@0 | 3207 | if (mLight.SetAttribute(aIndex, aValue) || |
michael@0 | 3208 | mLighting.SetAttribute(aIndex, aValue)) { |
michael@0 | 3209 | Invalidate(); |
michael@0 | 3210 | return; |
michael@0 | 3211 | } |
michael@0 | 3212 | switch (aIndex) { |
michael@0 | 3213 | case ATT_LIGHTING_SURFACE_SCALE: |
michael@0 | 3214 | mSurfaceScale = aValue; |
michael@0 | 3215 | break; |
michael@0 | 3216 | default: |
michael@0 | 3217 | MOZ_CRASH(); |
michael@0 | 3218 | } |
michael@0 | 3219 | Invalidate(); |
michael@0 | 3220 | } |
michael@0 | 3221 | |
michael@0 | 3222 | template<typename LightType, typename LightingType> |
michael@0 | 3223 | void |
michael@0 | 3224 | FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength) |
michael@0 | 3225 | { |
michael@0 | 3226 | switch (aIndex) { |
michael@0 | 3227 | case ATT_LIGHTING_KERNEL_UNIT_LENGTH: |
michael@0 | 3228 | mKernelUnitLength = aKernelUnitLength; |
michael@0 | 3229 | break; |
michael@0 | 3230 | default: |
michael@0 | 3231 | MOZ_CRASH(); |
michael@0 | 3232 | } |
michael@0 | 3233 | Invalidate(); |
michael@0 | 3234 | } |
michael@0 | 3235 | |
michael@0 | 3236 | template<typename LightType, typename LightingType> |
michael@0 | 3237 | void |
michael@0 | 3238 | FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Color &aColor) |
michael@0 | 3239 | { |
michael@0 | 3240 | MOZ_ASSERT(aIndex == ATT_LIGHTING_COLOR); |
michael@0 | 3241 | mColor = aColor; |
michael@0 | 3242 | Invalidate(); |
michael@0 | 3243 | } |
michael@0 | 3244 | |
michael@0 | 3245 | template<typename LightType, typename LightingType> |
michael@0 | 3246 | IntRect |
michael@0 | 3247 | FilterNodeLightingSoftware<LightType, LightingType>::GetOutputRectInRect(const IntRect& aRect) |
michael@0 | 3248 | { |
michael@0 | 3249 | return GetInputRectInRect(IN_LIGHTING_IN, aRect); |
michael@0 | 3250 | } |
michael@0 | 3251 | |
michael@0 | 3252 | Point3D |
michael@0 | 3253 | PointLightSoftware::GetVectorToLight(const Point3D &aTargetPoint) |
michael@0 | 3254 | { |
michael@0 | 3255 | return Normalized(mPosition - aTargetPoint); |
michael@0 | 3256 | } |
michael@0 | 3257 | |
michael@0 | 3258 | uint32_t |
michael@0 | 3259 | PointLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight) |
michael@0 | 3260 | { |
michael@0 | 3261 | return aLightColor; |
michael@0 | 3262 | } |
michael@0 | 3263 | |
michael@0 | 3264 | void |
michael@0 | 3265 | SpotLightSoftware::Prepare() |
michael@0 | 3266 | { |
michael@0 | 3267 | mVectorFromFocusPointToLight = Normalized(mPointsAt - mPosition); |
michael@0 | 3268 | mLimitingConeCos = std::max<double>(cos(mLimitingConeAngle * M_PI/180.0), 0.0); |
michael@0 | 3269 | mPowCache.CacheForExponent(mSpecularFocus); |
michael@0 | 3270 | } |
michael@0 | 3271 | |
michael@0 | 3272 | Point3D |
michael@0 | 3273 | SpotLightSoftware::GetVectorToLight(const Point3D &aTargetPoint) |
michael@0 | 3274 | { |
michael@0 | 3275 | return Normalized(mPosition - aTargetPoint); |
michael@0 | 3276 | } |
michael@0 | 3277 | |
michael@0 | 3278 | uint32_t |
michael@0 | 3279 | SpotLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight) |
michael@0 | 3280 | { |
michael@0 | 3281 | union { |
michael@0 | 3282 | uint32_t color; |
michael@0 | 3283 | uint8_t colorC[4]; |
michael@0 | 3284 | }; |
michael@0 | 3285 | color = aLightColor; |
michael@0 | 3286 | Float dot = -aVectorToLight.DotProduct(mVectorFromFocusPointToLight); |
michael@0 | 3287 | uint16_t doti = dot * (dot >= 0) * (1 << PowCache::sInputIntPrecisionBits); |
michael@0 | 3288 | uint32_t tmp = mPowCache.Pow(doti) * (dot >= mLimitingConeCos); |
michael@0 | 3289 | MOZ_ASSERT(tmp <= (1 << PowCache::sOutputIntPrecisionBits), "pow() result must not exceed 1.0"); |
michael@0 | 3290 | colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] * tmp) >> PowCache::sOutputIntPrecisionBits); |
michael@0 | 3291 | colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] * tmp) >> PowCache::sOutputIntPrecisionBits); |
michael@0 | 3292 | colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] * tmp) >> PowCache::sOutputIntPrecisionBits); |
michael@0 | 3293 | colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255; |
michael@0 | 3294 | return color; |
michael@0 | 3295 | } |
michael@0 | 3296 | |
michael@0 | 3297 | void |
michael@0 | 3298 | DistantLightSoftware::Prepare() |
michael@0 | 3299 | { |
michael@0 | 3300 | const double radPerDeg = M_PI / 180.0; |
michael@0 | 3301 | mVectorToLight.x = cos(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg); |
michael@0 | 3302 | mVectorToLight.y = sin(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg); |
michael@0 | 3303 | mVectorToLight.z = sin(mElevation * radPerDeg); |
michael@0 | 3304 | } |
michael@0 | 3305 | |
michael@0 | 3306 | Point3D |
michael@0 | 3307 | DistantLightSoftware::GetVectorToLight(const Point3D &aTargetPoint) |
michael@0 | 3308 | { |
michael@0 | 3309 | return mVectorToLight; |
michael@0 | 3310 | } |
michael@0 | 3311 | |
michael@0 | 3312 | uint32_t |
michael@0 | 3313 | DistantLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight) |
michael@0 | 3314 | { |
michael@0 | 3315 | return aLightColor; |
michael@0 | 3316 | } |
michael@0 | 3317 | |
michael@0 | 3318 | template<typename CoordType> |
michael@0 | 3319 | static Point3D |
michael@0 | 3320 | GenerateNormal(const uint8_t *data, int32_t stride, |
michael@0 | 3321 | int32_t x, int32_t y, float surfaceScale, |
michael@0 | 3322 | CoordType dx, CoordType dy) |
michael@0 | 3323 | { |
michael@0 | 3324 | const uint8_t *index = data + y * stride + x; |
michael@0 | 3325 | |
michael@0 | 3326 | CoordType zero = 0; |
michael@0 | 3327 | |
michael@0 | 3328 | // See this for source of constants: |
michael@0 | 3329 | // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement |
michael@0 | 3330 | int16_t normalX = |
michael@0 | 3331 | -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) + |
michael@0 | 3332 | 1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) + |
michael@0 | 3333 | -2 * ColorComponentAtPoint(index, stride, -dx, zero, 1, 0) + |
michael@0 | 3334 | 2 * ColorComponentAtPoint(index, stride, dx, zero, 1, 0) + |
michael@0 | 3335 | -1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) + |
michael@0 | 3336 | 1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0); |
michael@0 | 3337 | |
michael@0 | 3338 | int16_t normalY = |
michael@0 | 3339 | -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) + |
michael@0 | 3340 | -2 * ColorComponentAtPoint(index, stride, zero, -dy, 1, 0) + |
michael@0 | 3341 | -1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) + |
michael@0 | 3342 | 1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) + |
michael@0 | 3343 | 2 * ColorComponentAtPoint(index, stride, zero, dy, 1, 0) + |
michael@0 | 3344 | 1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0); |
michael@0 | 3345 | |
michael@0 | 3346 | Point3D normal; |
michael@0 | 3347 | normal.x = -surfaceScale * normalX / 4.0f; |
michael@0 | 3348 | normal.y = -surfaceScale * normalY / 4.0f; |
michael@0 | 3349 | normal.z = 255; |
michael@0 | 3350 | return Normalized(normal); |
michael@0 | 3351 | } |
michael@0 | 3352 | |
michael@0 | 3353 | template<typename LightType, typename LightingType> |
michael@0 | 3354 | TemporaryRef<DataSourceSurface> |
michael@0 | 3355 | FilterNodeLightingSoftware<LightType, LightingType>::Render(const IntRect& aRect) |
michael@0 | 3356 | { |
michael@0 | 3357 | if (mKernelUnitLength.width == floor(mKernelUnitLength.width) && |
michael@0 | 3358 | mKernelUnitLength.height == floor(mKernelUnitLength.height)) { |
michael@0 | 3359 | return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height); |
michael@0 | 3360 | } |
michael@0 | 3361 | return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height); |
michael@0 | 3362 | } |
michael@0 | 3363 | |
michael@0 | 3364 | template<typename LightType, typename LightingType> |
michael@0 | 3365 | void |
michael@0 | 3366 | FilterNodeLightingSoftware<LightType, LightingType>::RequestFromInputsForRect(const IntRect &aRect) |
michael@0 | 3367 | { |
michael@0 | 3368 | IntRect srcRect = aRect; |
michael@0 | 3369 | srcRect.Inflate(ceil(mKernelUnitLength.width), |
michael@0 | 3370 | ceil(mKernelUnitLength.height)); |
michael@0 | 3371 | RequestInputRect(IN_LIGHTING_IN, srcRect); |
michael@0 | 3372 | } |
michael@0 | 3373 | |
michael@0 | 3374 | template<typename LightType, typename LightingType> template<typename CoordType> |
michael@0 | 3375 | TemporaryRef<DataSourceSurface> |
michael@0 | 3376 | FilterNodeLightingSoftware<LightType, LightingType>::DoRender(const IntRect& aRect, |
michael@0 | 3377 | CoordType aKernelUnitLengthX, |
michael@0 | 3378 | CoordType aKernelUnitLengthY) |
michael@0 | 3379 | { |
michael@0 | 3380 | IntRect srcRect = aRect; |
michael@0 | 3381 | IntSize size = aRect.Size(); |
michael@0 | 3382 | srcRect.Inflate(ceil(float(aKernelUnitLengthX)), |
michael@0 | 3383 | ceil(float(aKernelUnitLengthY))); |
michael@0 | 3384 | |
michael@0 | 3385 | // Inflate the source rect by another pixel because the bilinear filtering in |
michael@0 | 3386 | // ColorComponentAtPoint may want to access the margins. |
michael@0 | 3387 | srcRect.Inflate(1); |
michael@0 | 3388 | |
michael@0 | 3389 | RefPtr<DataSourceSurface> input = |
michael@0 | 3390 | GetInputDataSourceSurface(IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8, |
michael@0 | 3391 | EDGE_MODE_DUPLICATE); |
michael@0 | 3392 | |
michael@0 | 3393 | if (!input) { |
michael@0 | 3394 | return nullptr; |
michael@0 | 3395 | } |
michael@0 | 3396 | |
michael@0 | 3397 | if (input->GetFormat() != SurfaceFormat::A8) { |
michael@0 | 3398 | input = FilterProcessing::ExtractAlpha(input); |
michael@0 | 3399 | } |
michael@0 | 3400 | |
michael@0 | 3401 | DebugOnlyAutoColorSamplingAccessControl accessControl(input); |
michael@0 | 3402 | |
michael@0 | 3403 | RefPtr<DataSourceSurface> target = |
michael@0 | 3404 | Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); |
michael@0 | 3405 | if (!target) { |
michael@0 | 3406 | return nullptr; |
michael@0 | 3407 | } |
michael@0 | 3408 | |
michael@0 | 3409 | IntPoint offset = aRect.TopLeft() - srcRect.TopLeft(); |
michael@0 | 3410 | |
michael@0 | 3411 | uint8_t* sourceData = DataAtOffset(input, offset); |
michael@0 | 3412 | int32_t sourceStride = input->Stride(); |
michael@0 | 3413 | uint8_t* targetData = target->GetData(); |
michael@0 | 3414 | int32_t targetStride = target->Stride(); |
michael@0 | 3415 | |
michael@0 | 3416 | uint32_t lightColor = ColorToBGRA(mColor); |
michael@0 | 3417 | mLight.Prepare(); |
michael@0 | 3418 | mLighting.Prepare(); |
michael@0 | 3419 | |
michael@0 | 3420 | for (int32_t y = 0; y < size.height; y++) { |
michael@0 | 3421 | for (int32_t x = 0; x < size.width; x++) { |
michael@0 | 3422 | int32_t sourceIndex = y * sourceStride + x; |
michael@0 | 3423 | int32_t targetIndex = y * targetStride + 4 * x; |
michael@0 | 3424 | |
michael@0 | 3425 | Point3D normal = GenerateNormal(sourceData, sourceStride, |
michael@0 | 3426 | x, y, mSurfaceScale, |
michael@0 | 3427 | aKernelUnitLengthX, aKernelUnitLengthY); |
michael@0 | 3428 | |
michael@0 | 3429 | IntPoint pointInFilterSpace(aRect.x + x, aRect.y + y); |
michael@0 | 3430 | Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f; |
michael@0 | 3431 | Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z); |
michael@0 | 3432 | Point3D rayDir = mLight.GetVectorToLight(pt); |
michael@0 | 3433 | uint32_t color = mLight.GetColor(lightColor, rayDir); |
michael@0 | 3434 | |
michael@0 | 3435 | *(uint32_t*)(targetData + targetIndex) = mLighting.LightPixel(normal, rayDir, color); |
michael@0 | 3436 | } |
michael@0 | 3437 | } |
michael@0 | 3438 | |
michael@0 | 3439 | return target; |
michael@0 | 3440 | } |
michael@0 | 3441 | |
michael@0 | 3442 | DiffuseLightingSoftware::DiffuseLightingSoftware() |
michael@0 | 3443 | : mDiffuseConstant(0) |
michael@0 | 3444 | { |
michael@0 | 3445 | } |
michael@0 | 3446 | |
michael@0 | 3447 | bool |
michael@0 | 3448 | DiffuseLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) |
michael@0 | 3449 | { |
michael@0 | 3450 | switch (aIndex) { |
michael@0 | 3451 | case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT: |
michael@0 | 3452 | mDiffuseConstant = aValue; |
michael@0 | 3453 | break; |
michael@0 | 3454 | default: |
michael@0 | 3455 | return false; |
michael@0 | 3456 | } |
michael@0 | 3457 | return true; |
michael@0 | 3458 | } |
michael@0 | 3459 | |
michael@0 | 3460 | uint32_t |
michael@0 | 3461 | DiffuseLightingSoftware::LightPixel(const Point3D &aNormal, |
michael@0 | 3462 | const Point3D &aVectorToLight, |
michael@0 | 3463 | uint32_t aColor) |
michael@0 | 3464 | { |
michael@0 | 3465 | Float dotNL = std::max(0.0f, aNormal.DotProduct(aVectorToLight)); |
michael@0 | 3466 | Float diffuseNL = mDiffuseConstant * dotNL; |
michael@0 | 3467 | |
michael@0 | 3468 | union { |
michael@0 | 3469 | uint32_t bgra; |
michael@0 | 3470 | uint8_t components[4]; |
michael@0 | 3471 | } color = { aColor }; |
michael@0 | 3472 | color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = |
michael@0 | 3473 | umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]), 255U); |
michael@0 | 3474 | color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = |
michael@0 | 3475 | umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]), 255U); |
michael@0 | 3476 | color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = |
michael@0 | 3477 | umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]), 255U); |
michael@0 | 3478 | color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255; |
michael@0 | 3479 | return color.bgra; |
michael@0 | 3480 | } |
michael@0 | 3481 | |
michael@0 | 3482 | SpecularLightingSoftware::SpecularLightingSoftware() |
michael@0 | 3483 | : mSpecularConstant(0) |
michael@0 | 3484 | , mSpecularExponent(0) |
michael@0 | 3485 | { |
michael@0 | 3486 | } |
michael@0 | 3487 | |
michael@0 | 3488 | bool |
michael@0 | 3489 | SpecularLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) |
michael@0 | 3490 | { |
michael@0 | 3491 | switch (aIndex) { |
michael@0 | 3492 | case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT: |
michael@0 | 3493 | mSpecularConstant = std::min(std::max(aValue, 0.0f), 255.0f); |
michael@0 | 3494 | break; |
michael@0 | 3495 | case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT: |
michael@0 | 3496 | mSpecularExponent = std::min(std::max(aValue, 1.0f), 128.0f); |
michael@0 | 3497 | break; |
michael@0 | 3498 | default: |
michael@0 | 3499 | return false; |
michael@0 | 3500 | } |
michael@0 | 3501 | return true; |
michael@0 | 3502 | } |
michael@0 | 3503 | |
michael@0 | 3504 | void |
michael@0 | 3505 | SpecularLightingSoftware::Prepare() |
michael@0 | 3506 | { |
michael@0 | 3507 | mPowCache.CacheForExponent(mSpecularExponent); |
michael@0 | 3508 | mSpecularConstantInt = uint32_t(mSpecularConstant * (1 << 8)); |
michael@0 | 3509 | } |
michael@0 | 3510 | |
michael@0 | 3511 | uint32_t |
michael@0 | 3512 | SpecularLightingSoftware::LightPixel(const Point3D &aNormal, |
michael@0 | 3513 | const Point3D &aVectorToLight, |
michael@0 | 3514 | uint32_t aColor) |
michael@0 | 3515 | { |
michael@0 | 3516 | Point3D vectorToEye(0, 0, 1); |
michael@0 | 3517 | Point3D halfwayVector = Normalized(aVectorToLight + vectorToEye); |
michael@0 | 3518 | Float dotNH = aNormal.DotProduct(halfwayVector); |
michael@0 | 3519 | uint16_t dotNHi = uint16_t(dotNH * (dotNH >= 0) * (1 << PowCache::sInputIntPrecisionBits)); |
michael@0 | 3520 | uint32_t specularNHi = uint32_t(mSpecularConstantInt) * mPowCache.Pow(dotNHi) >> 8; |
michael@0 | 3521 | |
michael@0 | 3522 | union { |
michael@0 | 3523 | uint32_t bgra; |
michael@0 | 3524 | uint8_t components[4]; |
michael@0 | 3525 | } color = { aColor }; |
michael@0 | 3526 | color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = |
michael@0 | 3527 | umin( |
michael@0 | 3528 | (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]) >> PowCache::sOutputIntPrecisionBits, 255U); |
michael@0 | 3529 | color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = |
michael@0 | 3530 | umin( |
michael@0 | 3531 | (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) >> PowCache::sOutputIntPrecisionBits, 255U); |
michael@0 | 3532 | color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = |
michael@0 | 3533 | umin( |
michael@0 | 3534 | (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) >> PowCache::sOutputIntPrecisionBits, 255U); |
michael@0 | 3535 | |
michael@0 | 3536 | color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = |
michael@0 | 3537 | umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B], |
michael@0 | 3538 | umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G], |
michael@0 | 3539 | color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R])); |
michael@0 | 3540 | return color.bgra; |
michael@0 | 3541 | } |
michael@0 | 3542 | |
michael@0 | 3543 | } // namespace gfx |
michael@0 | 3544 | } // namespace mozilla |