gfx/2d/FilterNodeSoftware.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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

mercurial