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