Bug 1073336 part 14a - Update animation generation when changing animations via the...
[gecko.git] / gfx / 2d / FilterNodeSoftware.cpp
blobbb111c511ef5a835412dbf0b74bc70a9d63827f0
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 "DataSurfaceHelpers.h"
10 #include "FilterNodeSoftware.h"
11 #include "2D.h"
12 #include "Tools.h"
13 #include "Blur.h"
14 #include <map>
15 #include "FilterProcessing.h"
16 #include "Logging.h"
17 #include "mozilla/PodOperations.h"
18 #include "mozilla/DebugOnly.h"
20 // #define DEBUG_DUMP_SURFACES
22 #ifdef DEBUG_DUMP_SURFACES
23 #include "gfxUtils.h" // not part of Moz2D
24 #endif
26 namespace mozilla {
27 namespace gfx {
29 namespace {
31 /**
32 * This class provides a way to get a pow() results in constant-time. It works
33 * by caching 256 values for bases between 0 and 1 and a fixed exponent.
34 **/
35 class PowCache
37 public:
38 PowCache()
40 CacheForExponent(0.0f);
43 void CacheForExponent(Float aExponent)
45 mExponent = aExponent;
46 int numPreSquares = 0;
47 while (numPreSquares < 5 && mExponent > (1 << (numPreSquares + 2))) {
48 numPreSquares++;
50 mNumPowTablePreSquares = numPreSquares;
51 for (size_t i = 0; i < sCacheSize; i++) {
52 // sCacheSize is chosen in such a way that a takes values
53 // from 0.0 to 1.0 inclusive.
54 Float a = i / Float(1 << sCacheIndexPrecisionBits);
55 MOZ_ASSERT(0.0f <= a && a <= 1.0f, "We only want to cache for bases between 0 and 1.");
57 for (int j = 0; j < mNumPowTablePreSquares; j++) {
58 a = sqrt(a);
60 uint32_t cachedInt = pow(a, mExponent) * (1 << sOutputIntPrecisionBits);
61 MOZ_ASSERT(cachedInt < (1 << (sizeof(mPowTable[i]) * 8)), "mPowCache integer type too small");
63 mPowTable[i] = cachedInt;
67 uint16_t Pow(uint16_t aBase)
69 // Results should be similar to what the following code would produce:
70 // Float x = Float(aBase) / (1 << sInputIntPrecisionBits);
71 // return uint16_t(pow(x, mExponent) * (1 << sOutputIntPrecisionBits));
73 MOZ_ASSERT(aBase <= (1 << sInputIntPrecisionBits), "aBase needs to be between 0 and 1!");
75 uint32_t a = aBase;
76 for (int j = 0; j < mNumPowTablePreSquares; j++) {
77 a = a * a >> sInputIntPrecisionBits;
79 uint32_t i = a >> (sInputIntPrecisionBits - sCacheIndexPrecisionBits);
80 MOZ_ASSERT(i < sCacheSize, "out-of-bounds mPowTable access");
81 return mPowTable[i];
84 static const int sInputIntPrecisionBits = 15;
85 static const int sOutputIntPrecisionBits = 15;
86 static const int sCacheIndexPrecisionBits = 7;
88 private:
89 static const size_t sCacheSize = (1 << sCacheIndexPrecisionBits) + 1;
91 Float mExponent;
92 int mNumPowTablePreSquares;
93 uint16_t mPowTable[sCacheSize];
96 class PointLightSoftware
98 public:
99 bool SetAttribute(uint32_t aIndex, Float) { return false; }
100 bool SetAttribute(uint32_t aIndex, const Point3D &);
101 void Prepare() {}
102 Point3D GetVectorToLight(const Point3D &aTargetPoint);
103 uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
105 private:
106 Point3D mPosition;
109 class SpotLightSoftware
111 public:
112 SpotLightSoftware();
113 bool SetAttribute(uint32_t aIndex, Float);
114 bool SetAttribute(uint32_t aIndex, const Point3D &);
115 void Prepare();
116 Point3D GetVectorToLight(const Point3D &aTargetPoint);
117 uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
119 private:
120 Point3D mPosition;
121 Point3D mPointsAt;
122 Point3D mVectorFromFocusPointToLight;
123 Float mSpecularFocus;
124 Float mLimitingConeAngle;
125 Float mLimitingConeCos;
126 PowCache mPowCache;
129 class DistantLightSoftware
131 public:
132 DistantLightSoftware();
133 bool SetAttribute(uint32_t aIndex, Float);
134 bool SetAttribute(uint32_t aIndex, const Point3D &) { return false; }
135 void Prepare();
136 Point3D GetVectorToLight(const Point3D &aTargetPoint);
137 uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
139 private:
140 Float mAzimuth;
141 Float mElevation;
142 Point3D mVectorToLight;
145 class DiffuseLightingSoftware
147 public:
148 DiffuseLightingSoftware();
149 bool SetAttribute(uint32_t aIndex, Float);
150 void Prepare() {}
151 uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight,
152 uint32_t aColor);
154 private:
155 Float mDiffuseConstant;
158 class SpecularLightingSoftware
160 public:
161 SpecularLightingSoftware();
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 mSpecularConstant;
169 Float mSpecularExponent;
170 uint32_t mSpecularConstantInt;
171 PowCache mPowCache;
174 } // unnamed namespace
176 // from xpcom/ds/nsMathUtils.h
177 static int32_t
178 NS_lround(double x)
180 return x >= 0.0 ? int32_t(x + 0.5) : int32_t(x - 0.5);
183 // This check is safe against integer overflow.
184 static bool
185 SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint)
187 IntSize size = aSurface->GetSize();
188 return aPoint.x >= 0 && aPoint.x < size.width &&
189 aPoint.y >= 0 && aPoint.y < size.height;
192 static uint8_t*
193 DataAtOffset(DataSourceSurface* aSurface, IntPoint aPoint)
195 if (!SurfaceContainsPoint(aSurface, aPoint)) {
196 MOZ_CRASH("sample position needs to be inside surface!");
199 MOZ_ASSERT(Factory::CheckSurfaceSize(aSurface->GetSize()),
200 "surface size overflows - this should have been prevented when the surface was created");
202 uint8_t* data = aSurface->GetData() + aPoint.y * aSurface->Stride() +
203 aPoint.x * BytesPerPixel(aSurface->GetFormat());
205 if (data < aSurface->GetData()) {
206 MOZ_CRASH("out-of-range data access");
209 return data;
212 static bool
213 IntRectOverflows(const IntRect& aRect)
215 CheckedInt<int32_t> xMost = aRect.x;
216 xMost += aRect.width;
217 CheckedInt<int32_t> yMost = aRect.y;
218 yMost += aRect.height;
219 return !xMost.isValid() || !yMost.isValid();
223 * aSrcRect: Rect relative to the aSrc surface
224 * aDestPoint: Point inside aDest surface
226 static void
227 CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest,
228 IntRect aSrcRect, IntPoint aDestPoint)
230 if (IntRectOverflows(aSrcRect) ||
231 IntRectOverflows(IntRect(aDestPoint, aSrcRect.Size()))) {
232 MOZ_CRASH("we should never be getting invalid rects at this point");
235 MOZ_ASSERT(aSrc->GetFormat() == aDest->GetFormat(), "different surface formats");
236 MOZ_ASSERT(IntRect(IntPoint(), aSrc->GetSize()).Contains(aSrcRect), "source rect too big for source surface");
237 MOZ_ASSERT(IntRect(IntPoint(), aDest->GetSize()).Contains(aSrcRect - aSrcRect.TopLeft() + aDestPoint), "dest surface too small");
239 if (aSrcRect.IsEmpty()) {
240 return;
243 uint8_t* sourceData = DataAtOffset(aSrc, aSrcRect.TopLeft());
244 uint32_t sourceStride = aSrc->Stride();
245 uint8_t* destData = DataAtOffset(aDest, aDestPoint);
246 uint32_t destStride = aDest->Stride();
248 if (BytesPerPixel(aSrc->GetFormat()) == 4) {
249 for (int32_t y = 0; y < aSrcRect.height; y++) {
250 PodCopy((int32_t*)destData, (int32_t*)sourceData, aSrcRect.width);
251 sourceData += sourceStride;
252 destData += destStride;
254 } else if (BytesPerPixel(aSrc->GetFormat()) == 1) {
255 for (int32_t y = 0; y < aSrcRect.height; y++) {
256 PodCopy(destData, sourceData, aSrcRect.width);
257 sourceData += sourceStride;
258 destData += destStride;
263 TemporaryRef<DataSourceSurface>
264 CloneAligned(DataSourceSurface* aSource)
266 RefPtr<DataSourceSurface> copy =
267 Factory::CreateDataSourceSurface(aSource->GetSize(), aSource->GetFormat());
268 if (copy) {
269 CopyRect(aSource, copy, IntRect(IntPoint(), aSource->GetSize()), IntPoint());
271 return copy.forget();
274 static void
275 FillRectWithPixel(DataSourceSurface *aSurface, const IntRect &aFillRect, IntPoint aPixelPos)
277 MOZ_ASSERT(!IntRectOverflows(aFillRect));
278 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
279 "aFillRect needs to be completely inside the surface");
280 MOZ_ASSERT(SurfaceContainsPoint(aSurface, aPixelPos),
281 "aPixelPos needs to be inside the surface");
283 int32_t stride = aSurface->Stride();
284 uint8_t* sourcePixelData = DataAtOffset(aSurface, aPixelPos);
285 uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft());
286 int bpp = BytesPerPixel(aSurface->GetFormat());
288 // Fill the first row by hand.
289 if (bpp == 4) {
290 uint32_t sourcePixel = *(uint32_t*)sourcePixelData;
291 for (int32_t x = 0; x < aFillRect.width; x++) {
292 *((uint32_t*)data + x) = sourcePixel;
294 } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
295 uint8_t sourcePixel = *sourcePixelData;
296 memset(data, sourcePixel, aFillRect.width);
299 // Copy the first row into the other rows.
300 for (int32_t y = 1; y < aFillRect.height; y++) {
301 PodCopy(data + y * stride, data, aFillRect.width * bpp);
305 static void
306 FillRectWithVerticallyRepeatingHorizontalStrip(DataSourceSurface *aSurface,
307 const IntRect &aFillRect,
308 const IntRect &aSampleRect)
310 MOZ_ASSERT(!IntRectOverflows(aFillRect));
311 MOZ_ASSERT(!IntRectOverflows(aSampleRect));
312 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
313 "aFillRect needs to be completely inside the surface");
314 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
315 "aSampleRect needs to be completely inside the surface");
317 int32_t stride = aSurface->Stride();
318 uint8_t* sampleData = DataAtOffset(aSurface, aSampleRect.TopLeft());
319 uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft());
320 if (BytesPerPixel(aSurface->GetFormat()) == 4) {
321 for (int32_t y = 0; y < aFillRect.height; y++) {
322 PodCopy((uint32_t*)data, (uint32_t*)sampleData, aFillRect.width);
323 data += stride;
325 } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
326 for (int32_t y = 0; y < aFillRect.height; y++) {
327 PodCopy(data, sampleData, aFillRect.width);
328 data += stride;
333 static void
334 FillRectWithHorizontallyRepeatingVerticalStrip(DataSourceSurface *aSurface,
335 const IntRect &aFillRect,
336 const IntRect &aSampleRect)
338 MOZ_ASSERT(!IntRectOverflows(aFillRect));
339 MOZ_ASSERT(!IntRectOverflows(aSampleRect));
340 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
341 "aFillRect needs to be completely inside the surface");
342 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
343 "aSampleRect needs to be completely inside the surface");
345 int32_t stride = aSurface->Stride();
346 uint8_t* sampleData = DataAtOffset(aSurface, aSampleRect.TopLeft());
347 uint8_t* data = DataAtOffset(aSurface, aFillRect.TopLeft());
348 if (BytesPerPixel(aSurface->GetFormat()) == 4) {
349 for (int32_t y = 0; y < aFillRect.height; y++) {
350 int32_t sampleColor = *((uint32_t*)sampleData);
351 for (int32_t x = 0; x < aFillRect.width; x++) {
352 *((uint32_t*)data + x) = sampleColor;
354 data += stride;
355 sampleData += stride;
357 } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
358 for (int32_t y = 0; y < aFillRect.height; y++) {
359 uint8_t sampleColor = *sampleData;
360 memset(data, sampleColor, aFillRect.width);
361 data += stride;
362 sampleData += stride;
367 static void
368 DuplicateEdges(DataSourceSurface* aSurface, const IntRect &aFromRect)
370 MOZ_ASSERT(!IntRectOverflows(aFromRect));
371 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFromRect),
372 "aFromRect needs to be completely inside the surface");
374 IntSize size = aSurface->GetSize();
375 IntRect fill;
376 IntRect sampleRect;
377 for (int32_t ix = 0; ix < 3; ix++) {
378 switch (ix) {
379 case 0:
380 fill.x = 0;
381 fill.width = aFromRect.x;
382 sampleRect.x = fill.XMost();
383 sampleRect.width = 1;
384 break;
385 case 1:
386 fill.x = aFromRect.x;
387 fill.width = aFromRect.width;
388 sampleRect.x = fill.x;
389 sampleRect.width = fill.width;
390 break;
391 case 2:
392 fill.x = aFromRect.XMost();
393 fill.width = size.width - fill.x;
394 sampleRect.x = fill.x - 1;
395 sampleRect.width = 1;
396 break;
398 if (fill.width <= 0) {
399 continue;
401 bool xIsMiddle = (ix == 1);
402 for (int32_t iy = 0; iy < 3; iy++) {
403 switch (iy) {
404 case 0:
405 fill.y = 0;
406 fill.height = aFromRect.y;
407 sampleRect.y = fill.YMost();
408 sampleRect.height = 1;
409 break;
410 case 1:
411 fill.y = aFromRect.y;
412 fill.height = aFromRect.height;
413 sampleRect.y = fill.y;
414 sampleRect.height = fill.height;
415 break;
416 case 2:
417 fill.y = aFromRect.YMost();
418 fill.height = size.height - fill.y;
419 sampleRect.y = fill.y - 1;
420 sampleRect.height = 1;
421 break;
423 if (fill.height <= 0) {
424 continue;
426 bool yIsMiddle = (iy == 1);
427 if (!xIsMiddle && !yIsMiddle) {
428 // Corner
429 FillRectWithPixel(aSurface, fill, sampleRect.TopLeft());
431 if (xIsMiddle && !yIsMiddle) {
432 // Top middle or bottom middle
433 FillRectWithVerticallyRepeatingHorizontalStrip(aSurface, fill, sampleRect);
435 if (!xIsMiddle && yIsMiddle) {
436 // Left middle or right middle
437 FillRectWithHorizontallyRepeatingVerticalStrip(aSurface, fill, sampleRect);
443 static IntPoint
444 TileIndex(const IntRect &aFirstTileRect, const IntPoint &aPoint)
446 return IntPoint(int32_t(floor(double(aPoint.x - aFirstTileRect.x) / aFirstTileRect.width)),
447 int32_t(floor(double(aPoint.y - aFirstTileRect.y) / aFirstTileRect.height)));
450 static void
451 TileSurface(DataSourceSurface* aSource, DataSourceSurface* aTarget, const IntPoint &aOffset)
453 IntRect sourceRect(aOffset, aSource->GetSize());
454 IntRect targetRect(IntPoint(0, 0), aTarget->GetSize());
455 IntPoint startIndex = TileIndex(sourceRect, targetRect.TopLeft());
456 IntPoint endIndex = TileIndex(sourceRect, targetRect.BottomRight());
458 for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
459 for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
460 IntPoint destPoint(sourceRect.x + ix * sourceRect.width,
461 sourceRect.y + iy * sourceRect.height);
462 IntRect destRect(destPoint, sourceRect.Size());
463 destRect = destRect.Intersect(targetRect);
464 IntRect srcRect = destRect - destPoint;
465 CopyRect(aSource, aTarget, srcRect, destRect.TopLeft());
470 static TemporaryRef<DataSourceSurface>
471 GetDataSurfaceInRect(SourceSurface *aSurface,
472 const IntRect &aSurfaceRect,
473 const IntRect &aDestRect,
474 ConvolveMatrixEdgeMode aEdgeMode)
476 MOZ_ASSERT(aSurface ? aSurfaceRect.Size() == aSurface->GetSize() : aSurfaceRect.IsEmpty());
478 if (IntRectOverflows(aSurfaceRect) || IntRectOverflows(aDestRect)) {
479 // We can't rely on the intersection calculations below to make sense when
480 // XMost() or YMost() overflow. Bail out.
481 return nullptr;
484 IntRect sourceRect = aSurfaceRect;
486 if (sourceRect.IsEqualEdges(aDestRect)) {
487 return aSurface ? aSurface->GetDataSurface() : nullptr;
490 IntRect intersect = sourceRect.Intersect(aDestRect);
491 IntRect intersectInSourceSpace = intersect - sourceRect.TopLeft();
492 IntRect intersectInDestSpace = intersect - aDestRect.TopLeft();
493 SurfaceFormat format = aSurface ? aSurface->GetFormat() : SurfaceFormat(SurfaceFormat::B8G8R8A8);
495 bool clear = aEdgeMode == EDGE_MODE_NONE && !aSurfaceRect.Contains(aDestRect);
496 RefPtr<DataSourceSurface> target =
497 Factory::CreateDataSourceSurface(aDestRect.Size(), format, clear);
498 if (MOZ2D_WARN_IF(!target)) {
499 return nullptr;
502 if (!aSurface) {
503 return target.forget();
506 RefPtr<DataSourceSurface> dataSource = aSurface->GetDataSurface();
507 MOZ_ASSERT(dataSource);
509 if (aEdgeMode == EDGE_MODE_WRAP) {
510 TileSurface(dataSource, target, intersectInDestSpace.TopLeft());
511 return target.forget();
514 CopyRect(dataSource, target, intersectInSourceSpace,
515 intersectInDestSpace.TopLeft());
517 if (aEdgeMode == EDGE_MODE_DUPLICATE) {
518 DuplicateEdges(target, intersectInDestSpace);
521 return target.forget();
524 /* static */ TemporaryRef<FilterNode>
525 FilterNodeSoftware::Create(FilterType aType)
527 RefPtr<FilterNodeSoftware> filter;
528 switch (aType) {
529 case FilterType::BLEND:
530 filter = new FilterNodeBlendSoftware();
531 break;
532 case FilterType::TRANSFORM:
533 filter = new FilterNodeTransformSoftware();
534 break;
535 case FilterType::MORPHOLOGY:
536 filter = new FilterNodeMorphologySoftware();
537 break;
538 case FilterType::COLOR_MATRIX:
539 filter = new FilterNodeColorMatrixSoftware();
540 break;
541 case FilterType::FLOOD:
542 filter = new FilterNodeFloodSoftware();
543 break;
544 case FilterType::TILE:
545 filter = new FilterNodeTileSoftware();
546 break;
547 case FilterType::TABLE_TRANSFER:
548 filter = new FilterNodeTableTransferSoftware();
549 break;
550 case FilterType::DISCRETE_TRANSFER:
551 filter = new FilterNodeDiscreteTransferSoftware();
552 break;
553 case FilterType::LINEAR_TRANSFER:
554 filter = new FilterNodeLinearTransferSoftware();
555 break;
556 case FilterType::GAMMA_TRANSFER:
557 filter = new FilterNodeGammaTransferSoftware();
558 break;
559 case FilterType::CONVOLVE_MATRIX:
560 filter = new FilterNodeConvolveMatrixSoftware();
561 break;
562 case FilterType::DISPLACEMENT_MAP:
563 filter = new FilterNodeDisplacementMapSoftware();
564 break;
565 case FilterType::TURBULENCE:
566 filter = new FilterNodeTurbulenceSoftware();
567 break;
568 case FilterType::ARITHMETIC_COMBINE:
569 filter = new FilterNodeArithmeticCombineSoftware();
570 break;
571 case FilterType::COMPOSITE:
572 filter = new FilterNodeCompositeSoftware();
573 break;
574 case FilterType::GAUSSIAN_BLUR:
575 filter = new FilterNodeGaussianBlurSoftware();
576 break;
577 case FilterType::DIRECTIONAL_BLUR:
578 filter = new FilterNodeDirectionalBlurSoftware();
579 break;
580 case FilterType::CROP:
581 filter = new FilterNodeCropSoftware();
582 break;
583 case FilterType::PREMULTIPLY:
584 filter = new FilterNodePremultiplySoftware();
585 break;
586 case FilterType::UNPREMULTIPLY:
587 filter = new FilterNodeUnpremultiplySoftware();
588 break;
589 case FilterType::POINT_DIFFUSE:
590 filter = new FilterNodeLightingSoftware<PointLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<PointLight, DiffuseLighting>");
591 break;
592 case FilterType::POINT_SPECULAR:
593 filter = new FilterNodeLightingSoftware<PointLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<PointLight, SpecularLighting>");
594 break;
595 case FilterType::SPOT_DIFFUSE:
596 filter = new FilterNodeLightingSoftware<SpotLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<SpotLight, DiffuseLighting>");
597 break;
598 case FilterType::SPOT_SPECULAR:
599 filter = new FilterNodeLightingSoftware<SpotLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<SpotLight, SpecularLighting>");
600 break;
601 case FilterType::DISTANT_DIFFUSE:
602 filter = new FilterNodeLightingSoftware<DistantLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<DistantLight, DiffuseLighting>");
603 break;
604 case FilterType::DISTANT_SPECULAR:
605 filter = new FilterNodeLightingSoftware<DistantLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<DistantLight, SpecularLighting>");
606 break;
608 return filter.forget();
611 void
612 FilterNodeSoftware::Draw(DrawTarget* aDrawTarget,
613 const Rect &aSourceRect,
614 const Point &aDestPoint,
615 const DrawOptions &aOptions)
617 #ifdef DEBUG_DUMP_SURFACES
618 printf("<style>section{margin:10px;}</style><pre>\nRendering filter %s...\n", GetName());
619 #endif
621 Rect renderRect = aSourceRect;
622 renderRect.RoundOut();
623 IntRect renderIntRect;
624 if (!renderRect.ToIntRect(&renderIntRect)) {
625 #ifdef DEBUG_DUMP_SURFACES
626 printf("render rect overflowed, not painting anything\n");
627 printf("</pre>\n");
628 #endif
629 return;
632 IntRect outputRect = GetOutputRectInRect(renderIntRect);
633 if (IntRectOverflows(outputRect)) {
634 #ifdef DEBUG_DUMP_SURFACES
635 printf("output rect overflowed, not painting anything\n");
636 printf("</pre>\n");
637 #endif
638 return;
641 RefPtr<DataSourceSurface> result;
642 if (!outputRect.IsEmpty()) {
643 result = GetOutput(outputRect);
646 if (!result) {
647 // Null results are allowed and treated as transparent. Don't draw anything.
648 #ifdef DEBUG_DUMP_SURFACES
649 printf("output returned null\n");
650 printf("</pre>\n");
651 #endif
652 return;
655 #ifdef DEBUG_DUMP_SURFACES
656 printf("output from %s:\n", GetName());
657 printf("<img src='"); gfxUtils::DumpAsDataURL(result); printf("'>\n");
658 printf("</pre>\n");
659 #endif
661 Point sourceToDestOffset = aDestPoint - aSourceRect.TopLeft();
662 Rect renderedSourceRect = Rect(outputRect).Intersect(aSourceRect);
663 Rect renderedDestRect = renderedSourceRect + sourceToDestOffset;
664 if (result->GetFormat() == SurfaceFormat::A8) {
665 // Interpret the result as having implicitly black color channels.
666 aDrawTarget->PushClipRect(renderedDestRect);
667 aDrawTarget->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)),
668 result,
669 Point(outputRect.TopLeft()) + sourceToDestOffset,
670 aOptions);
671 aDrawTarget->PopClip();
672 } else {
673 aDrawTarget->DrawSurface(result, renderedDestRect,
674 renderedSourceRect - Point(outputRect.TopLeft()),
675 DrawSurfaceOptions(), aOptions);
679 TemporaryRef<DataSourceSurface>
680 FilterNodeSoftware::GetOutput(const IntRect &aRect)
682 MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect));
684 if (IntRectOverflows(aRect)) {
685 return nullptr;
688 if (!mCachedRect.Contains(aRect)) {
689 RequestRect(aRect);
690 mCachedOutput = Render(mRequestedRect);
691 if (!mCachedOutput) {
692 mCachedRect = IntRect();
693 mRequestedRect = IntRect();
694 return nullptr;
696 mCachedRect = mRequestedRect;
697 mRequestedRect = IntRect();
698 } else {
699 MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?");
701 return GetDataSurfaceInRect(mCachedOutput, mCachedRect, aRect, EDGE_MODE_NONE);
704 void
705 FilterNodeSoftware::RequestRect(const IntRect &aRect)
707 mRequestedRect = mRequestedRect.Union(aRect);
708 RequestFromInputsForRect(aRect);
711 void
712 FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex, const IntRect &aRect)
714 if (IntRectOverflows(aRect)) {
715 return;
718 int32_t inputIndex = InputIndex(aInputEnumIndex);
719 if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
720 MOZ_CRASH();
722 if (mInputSurfaces[inputIndex]) {
723 return;
725 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
726 MOZ_ASSERT(filter, "missing input");
727 filter->RequestRect(filter->GetOutputRectInRect(aRect));
730 SurfaceFormat
731 FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat,
732 FormatHint aFormatHint)
734 if (aCurrentFormat == SurfaceFormat::A8 && aFormatHint == CAN_HANDLE_A8) {
735 return SurfaceFormat::A8;
737 return SurfaceFormat::B8G8R8A8;
740 TemporaryRef<DataSourceSurface>
741 FilterNodeSoftware::GetInputDataSourceSurface(uint32_t aInputEnumIndex,
742 const IntRect& aRect,
743 FormatHint aFormatHint,
744 ConvolveMatrixEdgeMode aEdgeMode,
745 const IntRect *aTransparencyPaddedSourceRect)
747 if (IntRectOverflows(aRect)) {
748 return nullptr;
751 #ifdef DEBUG_DUMP_SURFACES
752 printf("<section><h1>GetInputDataSourceSurface with aRect: %d, %d, %d, %d</h1>\n",
753 aRect.x, aRect.y, aRect.width, aRect.height);
754 #endif
755 int32_t inputIndex = InputIndex(aInputEnumIndex);
756 if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
757 MOZ_CRASH();
758 return nullptr;
761 if (aRect.IsEmpty()) {
762 return nullptr;
765 RefPtr<SourceSurface> surface;
766 IntRect surfaceRect;
768 if (mInputSurfaces[inputIndex]) {
769 // Input from input surface
770 surface = mInputSurfaces[inputIndex];
771 #ifdef DEBUG_DUMP_SURFACES
772 printf("input from input surface:\n");
773 #endif
774 surfaceRect = IntRect(IntPoint(0, 0), surface->GetSize());
775 } else {
776 // Input from input filter
777 #ifdef DEBUG_DUMP_SURFACES
778 printf("getting input from input filter %s...\n", mInputFilters[inputIndex]->GetName());
779 #endif
780 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
781 MOZ_ASSERT(filter, "missing input");
782 IntRect inputFilterOutput = filter->GetOutputRectInRect(aRect);
783 if (!inputFilterOutput.IsEmpty()) {
784 surface = filter->GetOutput(inputFilterOutput);
786 #ifdef DEBUG_DUMP_SURFACES
787 printf("input from input filter %s:\n", mInputFilters[inputIndex]->GetName());
788 #endif
789 surfaceRect = inputFilterOutput;
790 MOZ_ASSERT(!surface || surfaceRect.Size() == surface->GetSize());
793 if (surface && surface->GetFormat() == SurfaceFormat::UNKNOWN) {
794 #ifdef DEBUG_DUMP_SURFACES
795 printf("wrong input format</section>\n\n");
796 #endif
797 return nullptr;
800 if (!surfaceRect.IsEmpty() && !surface) {
801 #ifdef DEBUG_DUMP_SURFACES
802 printf(" -- no input --</section>\n\n");
803 #endif
804 return nullptr;
807 if (aTransparencyPaddedSourceRect && !aTransparencyPaddedSourceRect->IsEmpty()) {
808 IntRect srcRect = aTransparencyPaddedSourceRect->Intersect(aRect);
809 surface = GetDataSurfaceInRect(surface, surfaceRect, srcRect, EDGE_MODE_NONE);
810 surfaceRect = srcRect;
813 RefPtr<DataSourceSurface> result =
814 GetDataSurfaceInRect(surface, surfaceRect, aRect, aEdgeMode);
816 if (result) {
817 // TODO: This isn't safe since we don't have a guarantee
818 // that future Maps will have the same stride
819 DataSourceSurface::MappedSurface map;
820 if (result->Map(DataSourceSurface::READ, &map)) {
821 // Unmap immediately since CloneAligned hasn't been updated
822 // to use the Map API yet. We can still read the stride/data
823 // values as long as we don't try to dereference them.
824 result->Unmap();
825 if (map.mStride != GetAlignedStride<16>(map.mStride) ||
826 reinterpret_cast<uintptr_t>(map.mData) % 16 != 0) {
827 // Align unaligned surface.
828 result = CloneAligned(result);
830 } else {
831 result = nullptr;
836 if (!result) {
837 #ifdef DEBUG_DUMP_SURFACES
838 printf(" -- no input --</section>\n\n");
839 #endif
840 return nullptr;
843 SurfaceFormat currentFormat = result->GetFormat();
844 if (DesiredFormat(currentFormat, aFormatHint) == SurfaceFormat::B8G8R8A8 &&
845 currentFormat != SurfaceFormat::B8G8R8A8) {
846 result = FilterProcessing::ConvertToB8G8R8A8(result);
849 #ifdef DEBUG_DUMP_SURFACES
850 printf("<img src='"); gfxUtils::DumpAsDataURL(result); printf("'></section>");
851 #endif
853 MOZ_ASSERT(!result || result->GetSize() == aRect.Size(), "wrong surface size");
855 return result.forget();
858 IntRect
859 FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex,
860 const IntRect &aInRect)
862 if (IntRectOverflows(aInRect)) {
863 return IntRect();
866 int32_t inputIndex = InputIndex(aInputEnumIndex);
867 if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
868 MOZ_CRASH();
869 return IntRect();
871 if (mInputSurfaces[inputIndex]) {
872 return aInRect.Intersect(IntRect(IntPoint(0, 0),
873 mInputSurfaces[inputIndex]->GetSize()));
875 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
876 MOZ_ASSERT(filter, "missing input");
877 return filter->GetOutputRectInRect(aInRect);
880 size_t
881 FilterNodeSoftware::NumberOfSetInputs()
883 return std::max(mInputSurfaces.size(), mInputFilters.size());
886 void
887 FilterNodeSoftware::AddInvalidationListener(FilterInvalidationListener* aListener)
889 MOZ_ASSERT(aListener, "null listener");
890 mInvalidationListeners.push_back(aListener);
893 void
894 FilterNodeSoftware::RemoveInvalidationListener(FilterInvalidationListener* aListener)
896 MOZ_ASSERT(aListener, "null listener");
897 std::vector<FilterInvalidationListener*>::iterator it =
898 std::find(mInvalidationListeners.begin(), mInvalidationListeners.end(), aListener);
899 mInvalidationListeners.erase(it);
902 void
903 FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware* aFilter)
905 Invalidate();
908 void
909 FilterNodeSoftware::Invalidate()
911 mCachedOutput = nullptr;
912 mCachedRect = IntRect();
913 for (std::vector<FilterInvalidationListener*>::iterator it = mInvalidationListeners.begin();
914 it != mInvalidationListeners.end(); it++) {
915 (*it)->FilterInvalidated(this);
919 FilterNodeSoftware::~FilterNodeSoftware()
921 MOZ_ASSERT(!mInvalidationListeners.size(),
922 "All invalidation listeners should have unsubscribed themselves by now!");
924 for (std::vector<RefPtr<FilterNodeSoftware> >::iterator it = mInputFilters.begin();
925 it != mInputFilters.end(); it++) {
926 if (*it) {
927 (*it)->RemoveInvalidationListener(this);
932 void
933 FilterNodeSoftware::SetInput(uint32_t aIndex, FilterNode *aFilter)
935 if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
936 MOZ_ASSERT(false, "can only take software filters as inputs");
937 return;
939 SetInput(aIndex, nullptr, static_cast<FilterNodeSoftware*>(aFilter));
942 void
943 FilterNodeSoftware::SetInput(uint32_t aIndex, SourceSurface *aSurface)
945 SetInput(aIndex, aSurface, nullptr);
948 void
949 FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex,
950 SourceSurface *aSurface,
951 FilterNodeSoftware *aFilter)
953 int32_t inputIndex = InputIndex(aInputEnumIndex);
954 if (inputIndex < 0) {
955 MOZ_CRASH();
956 return;
958 if ((uint32_t)inputIndex >= NumberOfSetInputs()) {
959 mInputSurfaces.resize(inputIndex + 1);
960 mInputFilters.resize(inputIndex + 1);
962 mInputSurfaces[inputIndex] = aSurface;
963 if (mInputFilters[inputIndex]) {
964 mInputFilters[inputIndex]->RemoveInvalidationListener(this);
966 if (aFilter) {
967 aFilter->AddInvalidationListener(this);
969 mInputFilters[inputIndex] = aFilter;
970 if (!aSurface && !aFilter && (size_t)inputIndex == NumberOfSetInputs()) {
971 mInputSurfaces.resize(inputIndex);
972 mInputFilters.resize(inputIndex);
974 Invalidate();
977 FilterNodeBlendSoftware::FilterNodeBlendSoftware()
978 : mBlendMode(BLEND_MODE_MULTIPLY)
981 int32_t
982 FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex)
984 switch (aInputEnumIndex) {
985 case IN_BLEND_IN: return 0;
986 case IN_BLEND_IN2: return 1;
987 default: return -1;
991 void
992 FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex, uint32_t aBlendMode)
994 MOZ_ASSERT(aIndex == ATT_BLEND_BLENDMODE);
995 mBlendMode = static_cast<BlendMode>(aBlendMode);
996 Invalidate();
999 static CompositionOp ToBlendOp(BlendMode aOp)
1001 switch (aOp) {
1002 case BLEND_MODE_MULTIPLY:
1003 return CompositionOp::OP_MULTIPLY;
1004 case BLEND_MODE_SCREEN:
1005 return CompositionOp::OP_SCREEN;
1006 case BLEND_MODE_OVERLAY:
1007 return CompositionOp::OP_OVERLAY;
1008 case BLEND_MODE_DARKEN:
1009 return CompositionOp::OP_DARKEN;
1010 case BLEND_MODE_LIGHTEN:
1011 return CompositionOp::OP_LIGHTEN;
1012 case BLEND_MODE_COLOR_DODGE:
1013 return CompositionOp::OP_COLOR_DODGE;
1014 case BLEND_MODE_COLOR_BURN:
1015 return CompositionOp::OP_COLOR_BURN;
1016 case BLEND_MODE_HARD_LIGHT:
1017 return CompositionOp::OP_HARD_LIGHT;
1018 case BLEND_MODE_SOFT_LIGHT:
1019 return CompositionOp::OP_SOFT_LIGHT;
1020 case BLEND_MODE_DIFFERENCE:
1021 return CompositionOp::OP_DIFFERENCE;
1022 case BLEND_MODE_EXCLUSION:
1023 return CompositionOp::OP_EXCLUSION;
1024 case BLEND_MODE_HUE:
1025 return CompositionOp::OP_HUE;
1026 case BLEND_MODE_SATURATION:
1027 return CompositionOp::OP_SATURATION;
1028 case BLEND_MODE_COLOR:
1029 return CompositionOp::OP_COLOR;
1030 case BLEND_MODE_LUMINOSITY:
1031 return CompositionOp::OP_LUMINOSITY;
1032 default:
1033 return CompositionOp::OP_OVER;
1036 return CompositionOp::OP_OVER;
1039 TemporaryRef<DataSourceSurface>
1040 FilterNodeBlendSoftware::Render(const IntRect& aRect)
1042 RefPtr<DataSourceSurface> input1 =
1043 GetInputDataSourceSurface(IN_BLEND_IN, aRect, NEED_COLOR_CHANNELS);
1044 RefPtr<DataSourceSurface> input2 =
1045 GetInputDataSourceSurface(IN_BLEND_IN2, aRect, NEED_COLOR_CHANNELS);
1047 // Null inputs need to be treated as transparent.
1049 // First case: both are transparent.
1050 if (!input1 && !input2) {
1051 // Then the result is transparent, too.
1052 return nullptr;
1055 // Second case: one of them is transparent. Return the non-transparent one.
1056 if (!input1 || !input2) {
1057 return input1 ? input1.forget() : input2.forget();
1060 // Third case: both are non-transparent.
1061 // Apply normal filtering.
1062 RefPtr<DataSourceSurface> target = FilterProcessing::ApplyBlending(input1, input2, mBlendMode);
1063 if (target != nullptr) {
1064 return target.forget();
1067 IntSize size = input1->GetSize();
1068 target =
1069 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1070 if (MOZ2D_WARN_IF(!target)) {
1071 return nullptr;
1074 CopyRect(input1, target, IntRect(IntPoint(), size), IntPoint());
1076 RefPtr<DrawTarget> dt =
1077 Factory::CreateDrawTargetForData(BackendType::CAIRO,
1078 target->GetData(),
1079 target->GetSize(),
1080 target->Stride(),
1081 target->GetFormat());
1083 Rect r(0, 0, size.width, size.height);
1084 dt->DrawSurface(input2, r, r, DrawSurfaceOptions(), DrawOptions(1.0f, ToBlendOp(mBlendMode)));
1085 dt->Flush();
1086 return target.forget();
1089 void
1090 FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect &aRect)
1092 RequestInputRect(IN_BLEND_IN, aRect);
1093 RequestInputRect(IN_BLEND_IN2, aRect);
1096 IntRect
1097 FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect& aRect)
1099 return GetInputRectInRect(IN_BLEND_IN, aRect).Union(
1100 GetInputRectInRect(IN_BLEND_IN2, aRect)).Intersect(aRect);
1103 FilterNodeTransformSoftware::FilterNodeTransformSoftware()
1104 : mFilter(Filter::GOOD)
1107 int32_t
1108 FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex)
1110 switch (aInputEnumIndex) {
1111 case IN_TRANSFORM_IN: return 0;
1112 default: return -1;
1116 void
1117 FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, uint32_t aFilter)
1119 MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER);
1120 mFilter = static_cast<Filter>(aFilter);
1121 Invalidate();
1124 void
1125 FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, const Matrix &aMatrix)
1127 MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX);
1128 mMatrix = aMatrix;
1129 Invalidate();
1132 IntRect
1133 FilterNodeTransformSoftware::SourceRectForOutputRect(const IntRect &aRect)
1135 if (aRect.IsEmpty()) {
1136 return IntRect();
1139 Matrix inverted(mMatrix);
1140 if (!inverted.Invert()) {
1141 return IntRect();
1144 Rect neededRect = inverted.TransformBounds(Rect(aRect));
1145 neededRect.RoundOut();
1146 IntRect neededIntRect;
1147 if (!neededRect.ToIntRect(&neededIntRect)) {
1148 return IntRect();
1150 return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect);
1153 TemporaryRef<DataSourceSurface>
1154 FilterNodeTransformSoftware::Render(const IntRect& aRect)
1156 IntRect srcRect = SourceRectForOutputRect(aRect);
1158 RefPtr<DataSourceSurface> input =
1159 GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect);
1161 if (!input) {
1162 return nullptr;
1165 Matrix transform = Matrix::Translation(srcRect.x, srcRect.y) * mMatrix *
1166 Matrix::Translation(-aRect.x, -aRect.y);
1167 if (transform.IsIdentity() && srcRect.Size() == aRect.Size()) {
1168 return input.forget();
1171 RefPtr<DataSourceSurface> surf =
1172 Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat(), true);
1174 if (!surf) {
1175 return nullptr;
1178 DataSourceSurface::MappedSurface mapping;
1179 surf->Map(DataSourceSurface::MapType::WRITE, &mapping);
1181 RefPtr<DrawTarget> dt =
1182 Factory::CreateDrawTargetForData(BackendType::CAIRO,
1183 mapping.mData,
1184 surf->GetSize(),
1185 mapping.mStride,
1186 surf->GetFormat());
1187 if (!dt) {
1188 return nullptr;
1191 Rect r(0, 0, srcRect.width, srcRect.height);
1192 dt->SetTransform(transform);
1193 dt->DrawSurface(input, r, r, DrawSurfaceOptions(mFilter));
1195 dt->Flush();
1196 surf->Unmap();
1197 return surf.forget();
1200 void
1201 FilterNodeTransformSoftware::RequestFromInputsForRect(const IntRect &aRect)
1203 RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect));
1206 IntRect
1207 FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect)
1209 IntRect srcRect = SourceRectForOutputRect(aRect);
1210 if (srcRect.IsEmpty()) {
1211 return IntRect();
1214 Rect outRect = mMatrix.TransformBounds(Rect(srcRect));
1215 outRect.RoundOut();
1216 IntRect outIntRect;
1217 if (!outRect.ToIntRect(&outIntRect)) {
1218 return IntRect();
1220 return outIntRect.Intersect(aRect);
1223 FilterNodeMorphologySoftware::FilterNodeMorphologySoftware()
1224 : mOperator(MORPHOLOGY_OPERATOR_ERODE)
1227 int32_t
1228 FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex)
1230 switch (aInputEnumIndex) {
1231 case IN_MORPHOLOGY_IN: return 0;
1232 default: return -1;
1236 void
1237 FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
1238 const IntSize &aRadii)
1240 MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_RADII);
1241 mRadii.width = std::min(std::max(aRadii.width, 0), 100000);
1242 mRadii.height = std::min(std::max(aRadii.height, 0), 100000);
1243 Invalidate();
1246 void
1247 FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
1248 uint32_t aOperator)
1250 MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_OPERATOR);
1251 mOperator = static_cast<MorphologyOperator>(aOperator);
1252 Invalidate();
1255 static TemporaryRef<DataSourceSurface>
1256 ApplyMorphology(const IntRect& aSourceRect, DataSourceSurface* aInput,
1257 const IntRect& aDestRect, int32_t rx, int32_t ry,
1258 MorphologyOperator aOperator)
1260 IntRect srcRect = aSourceRect - aDestRect.TopLeft();
1261 IntRect destRect = aDestRect - aDestRect.TopLeft();
1262 IntRect tmpRect(destRect.x, srcRect.y, destRect.width, srcRect.height);
1263 #ifdef DEBUG
1264 IntMargin margin = srcRect - destRect;
1265 MOZ_ASSERT(margin.top >= ry && margin.right >= rx &&
1266 margin.bottom >= ry && margin.left >= rx, "insufficient margin");
1267 #endif
1269 RefPtr<DataSourceSurface> tmp;
1270 if (rx == 0) {
1271 tmp = aInput;
1272 } else {
1273 tmp = Factory::CreateDataSourceSurface(tmpRect.Size(), SurfaceFormat::B8G8R8A8);
1274 if (MOZ2D_WARN_IF(!tmp)) {
1275 return nullptr;
1278 int32_t sourceStride = aInput->Stride();
1279 uint8_t* sourceData = DataAtOffset(aInput, destRect.TopLeft() - srcRect.TopLeft());
1281 int32_t tmpStride = tmp->Stride();
1282 uint8_t* tmpData = DataAtOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft());
1284 FilterProcessing::ApplyMorphologyHorizontal(
1285 sourceData, sourceStride, tmpData, tmpStride, tmpRect, rx, aOperator);
1288 RefPtr<DataSourceSurface> dest;
1289 if (ry == 0) {
1290 dest = tmp;
1291 } else {
1292 dest = Factory::CreateDataSourceSurface(destRect.Size(), SurfaceFormat::B8G8R8A8);
1293 if (MOZ2D_WARN_IF(!dest)) {
1294 return nullptr;
1297 int32_t tmpStride = tmp->Stride();
1298 uint8_t* tmpData = DataAtOffset(tmp, destRect.TopLeft() - tmpRect.TopLeft());
1300 int32_t destStride = dest->Stride();
1301 uint8_t* destData = dest->GetData();
1303 FilterProcessing::ApplyMorphologyVertical(
1304 tmpData, tmpStride, destData, destStride, destRect, ry, aOperator);
1307 return dest.forget();
1310 TemporaryRef<DataSourceSurface>
1311 FilterNodeMorphologySoftware::Render(const IntRect& aRect)
1313 IntRect srcRect = aRect;
1314 srcRect.Inflate(mRadii);
1316 RefPtr<DataSourceSurface> input =
1317 GetInputDataSourceSurface(IN_MORPHOLOGY_IN, srcRect, NEED_COLOR_CHANNELS);
1318 if (!input) {
1319 return nullptr;
1322 int32_t rx = mRadii.width;
1323 int32_t ry = mRadii.height;
1325 if (rx == 0 && ry == 0) {
1326 return input.forget();
1329 return ApplyMorphology(srcRect, input, aRect, rx, ry, mOperator);
1332 void
1333 FilterNodeMorphologySoftware::RequestFromInputsForRect(const IntRect &aRect)
1335 IntRect srcRect = aRect;
1336 srcRect.Inflate(mRadii);
1337 RequestInputRect(IN_MORPHOLOGY_IN, srcRect);
1340 IntRect
1341 FilterNodeMorphologySoftware::GetOutputRectInRect(const IntRect& aRect)
1343 IntRect inflatedSourceRect = aRect;
1344 inflatedSourceRect.Inflate(mRadii);
1345 IntRect inputRect = GetInputRectInRect(IN_MORPHOLOGY_IN, inflatedSourceRect);
1346 if (mOperator == MORPHOLOGY_OPERATOR_ERODE) {
1347 inputRect.Deflate(mRadii);
1348 } else {
1349 inputRect.Inflate(mRadii);
1351 return inputRect.Intersect(aRect);
1354 int32_t
1355 FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex)
1357 switch (aInputEnumIndex) {
1358 case IN_COLOR_MATRIX_IN: return 0;
1359 default: return -1;
1363 void
1364 FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
1365 const Matrix5x4 &aMatrix)
1367 MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX);
1368 mMatrix = aMatrix;
1369 Invalidate();
1372 void
1373 FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
1374 uint32_t aAlphaMode)
1376 MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE);
1377 mAlphaMode = (AlphaMode)aAlphaMode;
1378 Invalidate();
1381 static TemporaryRef<DataSourceSurface>
1382 Premultiply(DataSourceSurface* aSurface)
1384 if (aSurface->GetFormat() == SurfaceFormat::A8) {
1385 return aSurface;
1388 IntSize size = aSurface->GetSize();
1389 RefPtr<DataSourceSurface> target =
1390 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1391 if (MOZ2D_WARN_IF(!target)) {
1392 return nullptr;
1395 uint8_t* inputData = aSurface->GetData();
1396 int32_t inputStride = aSurface->Stride();
1397 uint8_t* targetData = target->GetData();
1398 int32_t targetStride = target->Stride();
1400 FilterProcessing::DoPremultiplicationCalculation(
1401 size, targetData, targetStride, inputData, inputStride);
1403 return target.forget();
1406 static TemporaryRef<DataSourceSurface>
1407 Unpremultiply(DataSourceSurface* aSurface)
1409 if (aSurface->GetFormat() == SurfaceFormat::A8) {
1410 return aSurface;
1413 IntSize size = aSurface->GetSize();
1414 RefPtr<DataSourceSurface> target =
1415 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1416 if (MOZ2D_WARN_IF(!target)) {
1417 return nullptr;
1420 uint8_t* inputData = aSurface->GetData();
1421 int32_t inputStride = aSurface->Stride();
1422 uint8_t* targetData = target->GetData();
1423 int32_t targetStride = target->Stride();
1425 FilterProcessing::DoUnpremultiplicationCalculation(
1426 size, targetData, targetStride, inputData, inputStride);
1428 return target.forget();
1431 TemporaryRef<DataSourceSurface>
1432 FilterNodeColorMatrixSoftware::Render(const IntRect& aRect)
1434 RefPtr<DataSourceSurface> input =
1435 GetInputDataSourceSurface(IN_COLOR_MATRIX_IN, aRect, NEED_COLOR_CHANNELS);
1436 if (!input) {
1437 return nullptr;
1440 if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
1441 input = Unpremultiply(input);
1444 RefPtr<DataSourceSurface> result =
1445 FilterProcessing::ApplyColorMatrix(input, mMatrix);
1447 if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
1448 result = Premultiply(result);
1451 return result.forget();
1454 void
1455 FilterNodeColorMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
1457 RequestInputRect(IN_COLOR_MATRIX_IN, aRect);
1460 IntRect
1461 FilterNodeColorMatrixSoftware::GetOutputRectInRect(const IntRect& aRect)
1463 return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect);
1466 void
1467 FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex, const Color &aColor)
1469 MOZ_ASSERT(aIndex == ATT_FLOOD_COLOR);
1470 mColor = aColor;
1471 Invalidate();
1474 static uint32_t
1475 ColorToBGRA(const Color& aColor)
1477 union {
1478 uint32_t color;
1479 uint8_t components[4];
1481 components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = NS_lround(aColor.r * aColor.a * 255.0f);
1482 components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = NS_lround(aColor.g * aColor.a * 255.0f);
1483 components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = NS_lround(aColor.b * aColor.a * 255.0f);
1484 components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = NS_lround(aColor.a * 255.0f);
1485 return color;
1488 static SurfaceFormat
1489 FormatForColor(Color aColor)
1491 if (aColor.r == 0 && aColor.g == 0 && aColor.b == 0) {
1492 return SurfaceFormat::A8;
1494 return SurfaceFormat::B8G8R8A8;
1497 TemporaryRef<DataSourceSurface>
1498 FilterNodeFloodSoftware::Render(const IntRect& aRect)
1500 SurfaceFormat format = FormatForColor(mColor);
1501 RefPtr<DataSourceSurface> target =
1502 Factory::CreateDataSourceSurface(aRect.Size(), format);
1503 if (MOZ2D_WARN_IF(!target)) {
1504 return nullptr;
1507 uint8_t* targetData = target->GetData();
1508 uint32_t stride = target->Stride();
1510 if (format == SurfaceFormat::B8G8R8A8) {
1511 uint32_t color = ColorToBGRA(mColor);
1512 for (int32_t y = 0; y < aRect.height; y++) {
1513 for (int32_t x = 0; x < aRect.width; x++) {
1514 *((uint32_t*)targetData + x) = color;
1516 targetData += stride;
1518 } else if (format == SurfaceFormat::A8) {
1519 uint8_t alpha = NS_lround(mColor.a * 255.0f);
1520 for (int32_t y = 0; y < aRect.height; y++) {
1521 for (int32_t x = 0; x < aRect.width; x++) {
1522 targetData[x] = alpha;
1524 targetData += stride;
1526 } else {
1527 MOZ_CRASH();
1530 return target.forget();
1533 // Override GetOutput to get around caching. Rendering simple floods is
1534 // comparatively fast.
1535 TemporaryRef<DataSourceSurface>
1536 FilterNodeFloodSoftware::GetOutput(const IntRect& aRect)
1538 return Render(aRect);
1541 IntRect
1542 FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect& aRect)
1544 if (mColor.a == 0.0f) {
1545 return IntRect();
1547 return aRect;
1550 int32_t
1551 FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex)
1553 switch (aInputEnumIndex) {
1554 case IN_TILE_IN: return 0;
1555 default: return -1;
1559 void
1560 FilterNodeTileSoftware::SetAttribute(uint32_t aIndex,
1561 const IntRect &aSourceRect)
1563 MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT);
1564 mSourceRect = IntRect(int32_t(aSourceRect.x), int32_t(aSourceRect.y),
1565 int32_t(aSourceRect.width), int32_t(aSourceRect.height));
1566 Invalidate();
1569 namespace {
1570 struct CompareIntRects
1572 bool operator()(const IntRect& a, const IntRect& b) const
1574 if (a.x != b.x) {
1575 return a.x < b.x;
1577 if (a.y != b.y) {
1578 return a.y < b.y;
1580 if (a.width != b.width) {
1581 return a.width < b.width;
1583 return a.height < b.height;
1588 TemporaryRef<DataSourceSurface>
1589 FilterNodeTileSoftware::Render(const IntRect& aRect)
1591 if (mSourceRect.IsEmpty()) {
1592 return nullptr;
1595 if (mSourceRect.Contains(aRect)) {
1596 return GetInputDataSourceSurface(IN_TILE_IN, aRect);
1599 RefPtr<DataSourceSurface> target;
1601 typedef std::map<IntRect, RefPtr<DataSourceSurface>, CompareIntRects> InputMap;
1602 InputMap inputs;
1604 IntPoint startIndex = TileIndex(mSourceRect, aRect.TopLeft());
1605 IntPoint endIndex = TileIndex(mSourceRect, aRect.BottomRight());
1606 for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
1607 for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
1608 IntPoint sourceToDestOffset(ix * mSourceRect.width,
1609 iy * mSourceRect.height);
1610 IntRect destRect = aRect.Intersect(mSourceRect + sourceToDestOffset);
1611 IntRect srcRect = destRect - sourceToDestOffset;
1612 if (srcRect.IsEmpty()) {
1613 continue;
1616 RefPtr<DataSourceSurface> input;
1617 InputMap::iterator it = inputs.find(srcRect);
1618 if (it == inputs.end()) {
1619 input = GetInputDataSourceSurface(IN_TILE_IN, srcRect);
1620 inputs[srcRect] = input;
1621 } else {
1622 input = it->second;
1624 if (!input) {
1625 return nullptr;
1627 if (!target) {
1628 // We delay creating the target until now because we want to use the
1629 // same format as our input filter, and we do not actually know the
1630 // input format before we call GetInputDataSourceSurface.
1631 target = Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat());
1632 if (MOZ2D_WARN_IF(!target)) {
1633 return nullptr;
1636 MOZ_ASSERT(input->GetFormat() == target->GetFormat(), "different surface formats from the same input?");
1638 CopyRect(input, target, srcRect - srcRect.TopLeft(), destRect.TopLeft() - aRect.TopLeft());
1642 return target.forget();
1645 void
1646 FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect &aRect)
1648 // Do not request anything.
1649 // Source rects for the tile filter can be discontinuous with large gaps
1650 // between them. Requesting those from our input filter might cause it to
1651 // render the whole bounding box of all of them, which would be wasteful.
1654 IntRect
1655 FilterNodeTileSoftware::GetOutputRectInRect(const IntRect& aRect)
1657 return aRect;
1660 FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware()
1661 : mDisableR(true)
1662 , mDisableG(true)
1663 , mDisableB(true)
1664 , mDisableA(true)
1667 void
1668 FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex,
1669 bool aDisable)
1671 switch (aIndex) {
1672 case ATT_TRANSFER_DISABLE_R:
1673 mDisableR = aDisable;
1674 break;
1675 case ATT_TRANSFER_DISABLE_G:
1676 mDisableG = aDisable;
1677 break;
1678 case ATT_TRANSFER_DISABLE_B:
1679 mDisableB = aDisable;
1680 break;
1681 case ATT_TRANSFER_DISABLE_A:
1682 mDisableA = aDisable;
1683 break;
1684 default:
1685 MOZ_CRASH();
1687 Invalidate();
1690 void
1691 FilterNodeComponentTransferSoftware::GenerateLookupTable(ptrdiff_t aComponent,
1692 uint8_t aTables[4][256],
1693 bool aDisabled)
1695 if (aDisabled) {
1696 static uint8_t sIdentityLookupTable[256];
1697 static bool sInitializedIdentityLookupTable = false;
1698 if (!sInitializedIdentityLookupTable) {
1699 for (int32_t i = 0; i < 256; i++) {
1700 sIdentityLookupTable[i] = i;
1702 sInitializedIdentityLookupTable = true;
1704 memcpy(aTables[aComponent], sIdentityLookupTable, 256);
1705 } else {
1706 FillLookupTable(aComponent, aTables[aComponent]);
1710 template<uint32_t BytesPerPixel>
1711 static void TransferComponents(DataSourceSurface* aInput,
1712 DataSourceSurface* aTarget,
1713 const uint8_t aLookupTables[BytesPerPixel][256])
1715 MOZ_ASSERT(aInput->GetFormat() == aTarget->GetFormat(), "different formats");
1716 IntSize size = aInput->GetSize();
1718 uint8_t* sourceData = aInput->GetData();
1719 uint8_t* targetData = aTarget->GetData();
1720 uint32_t sourceStride = aInput->Stride();
1721 uint32_t targetStride = aTarget->Stride();
1723 for (int32_t y = 0; y < size.height; y++) {
1724 for (int32_t x = 0; x < size.width; x++) {
1725 uint32_t sourceIndex = y * sourceStride + x * BytesPerPixel;
1726 uint32_t targetIndex = y * targetStride + x * BytesPerPixel;
1727 for (uint32_t i = 0; i < BytesPerPixel; i++) {
1728 targetData[targetIndex + i] = aLookupTables[i][sourceData[sourceIndex + i]];
1734 bool
1735 IsAllZero(uint8_t aLookupTable[256])
1737 for (int32_t i = 0; i < 256; i++) {
1738 if (aLookupTable[i] != 0) {
1739 return false;
1742 return true;
1745 TemporaryRef<DataSourceSurface>
1746 FilterNodeComponentTransferSoftware::Render(const IntRect& aRect)
1748 if (mDisableR && mDisableG && mDisableB && mDisableA) {
1749 return GetInputDataSourceSurface(IN_TRANSFER_IN, aRect);
1752 uint8_t lookupTables[4][256];
1753 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R, lookupTables, mDisableR);
1754 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G, lookupTables, mDisableG);
1755 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B, lookupTables, mDisableB);
1756 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A, lookupTables, mDisableA);
1758 bool needColorChannels =
1759 lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R][0] != 0 ||
1760 lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G][0] != 0 ||
1761 lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B][0] != 0;
1763 FormatHint pref = needColorChannels ? NEED_COLOR_CHANNELS : CAN_HANDLE_A8;
1765 RefPtr<DataSourceSurface> input =
1766 GetInputDataSourceSurface(IN_TRANSFER_IN, aRect, pref);
1767 if (!input) {
1768 return nullptr;
1771 if (input->GetFormat() == SurfaceFormat::B8G8R8A8 && !needColorChannels) {
1772 bool colorChannelsBecomeBlack =
1773 IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) &&
1774 IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) &&
1775 IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B]);
1777 if (colorChannelsBecomeBlack) {
1778 input = FilterProcessing::ExtractAlpha(input);
1782 SurfaceFormat format = input->GetFormat();
1783 if (format == SurfaceFormat::A8 && mDisableA) {
1784 return input.forget();
1787 RefPtr<DataSourceSurface> target =
1788 Factory::CreateDataSourceSurface(aRect.Size(), format);
1789 if (MOZ2D_WARN_IF(!target)) {
1790 return nullptr;
1793 if (format == SurfaceFormat::A8) {
1794 TransferComponents<1>(input, target, &lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_A]);
1795 } else {
1796 TransferComponents<4>(input, target, lookupTables);
1799 return target.forget();
1802 void
1803 FilterNodeComponentTransferSoftware::RequestFromInputsForRect(const IntRect &aRect)
1805 RequestInputRect(IN_TRANSFER_IN, aRect);
1808 IntRect
1809 FilterNodeComponentTransferSoftware::GetOutputRectInRect(const IntRect& aRect)
1811 return GetInputRectInRect(IN_TRANSFER_IN, aRect);
1814 int32_t
1815 FilterNodeComponentTransferSoftware::InputIndex(uint32_t aInputEnumIndex)
1817 switch (aInputEnumIndex) {
1818 case IN_TRANSFER_IN: return 0;
1819 default: return -1;
1823 void
1824 FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex,
1825 const Float* aFloat,
1826 uint32_t aSize)
1828 std::vector<Float> table(aFloat, aFloat + aSize);
1829 switch (aIndex) {
1830 case ATT_TABLE_TRANSFER_TABLE_R:
1831 mTableR = table;
1832 break;
1833 case ATT_TABLE_TRANSFER_TABLE_G:
1834 mTableG = table;
1835 break;
1836 case ATT_TABLE_TRANSFER_TABLE_B:
1837 mTableB = table;
1838 break;
1839 case ATT_TABLE_TRANSFER_TABLE_A:
1840 mTableA = table;
1841 break;
1842 default:
1843 MOZ_CRASH();
1845 Invalidate();
1848 void
1849 FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
1850 uint8_t aTable[256])
1852 switch (aComponent) {
1853 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
1854 FillLookupTableImpl(mTableR, aTable);
1855 break;
1856 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
1857 FillLookupTableImpl(mTableG, aTable);
1858 break;
1859 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
1860 FillLookupTableImpl(mTableB, aTable);
1861 break;
1862 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
1863 FillLookupTableImpl(mTableA, aTable);
1864 break;
1865 default:
1866 MOZ_ASSERT(false, "unknown component");
1867 break;
1871 void
1872 FilterNodeTableTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues,
1873 uint8_t aTable[256])
1875 uint32_t tvLength = aTableValues.size();
1876 if (tvLength < 2) {
1877 return;
1880 for (size_t i = 0; i < 256; i++) {
1881 uint32_t k = (i * (tvLength - 1)) / 255;
1882 Float v1 = aTableValues[k];
1883 Float v2 = aTableValues[std::min(k + 1, tvLength - 1)];
1884 int32_t val =
1885 int32_t(255 * (v1 + (i/255.0f - k/float(tvLength-1))*(tvLength - 1)*(v2 - v1)));
1886 val = std::min(255, val);
1887 val = std::max(0, val);
1888 aTable[i] = val;
1892 void
1893 FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex,
1894 const Float* aFloat,
1895 uint32_t aSize)
1897 std::vector<Float> discrete(aFloat, aFloat + aSize);
1898 switch (aIndex) {
1899 case ATT_DISCRETE_TRANSFER_TABLE_R:
1900 mTableR = discrete;
1901 break;
1902 case ATT_DISCRETE_TRANSFER_TABLE_G:
1903 mTableG = discrete;
1904 break;
1905 case ATT_DISCRETE_TRANSFER_TABLE_B:
1906 mTableB = discrete;
1907 break;
1908 case ATT_DISCRETE_TRANSFER_TABLE_A:
1909 mTableA = discrete;
1910 break;
1911 default:
1912 MOZ_CRASH();
1914 Invalidate();
1917 void
1918 FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
1919 uint8_t aTable[256])
1921 switch (aComponent) {
1922 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
1923 FillLookupTableImpl(mTableR, aTable);
1924 break;
1925 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
1926 FillLookupTableImpl(mTableG, aTable);
1927 break;
1928 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
1929 FillLookupTableImpl(mTableB, aTable);
1930 break;
1931 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
1932 FillLookupTableImpl(mTableA, aTable);
1933 break;
1934 default:
1935 MOZ_ASSERT(false, "unknown component");
1936 break;
1940 void
1941 FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues,
1942 uint8_t aTable[256])
1944 uint32_t tvLength = aTableValues.size();
1945 if (tvLength < 1) {
1946 return;
1949 for (size_t i = 0; i < 256; i++) {
1950 uint32_t k = (i * tvLength) / 255;
1951 k = std::min(k, tvLength - 1);
1952 Float v = aTableValues[k];
1953 int32_t val = NS_lround(255 * v);
1954 val = std::min(255, val);
1955 val = std::max(0, val);
1956 aTable[i] = val;
1960 FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware()
1961 : mSlopeR(0)
1962 , mSlopeG(0)
1963 , mSlopeB(0)
1964 , mSlopeA(0)
1965 , mInterceptR(0)
1966 , mInterceptG(0)
1967 , mInterceptB(0)
1968 , mInterceptA(0)
1971 void
1972 FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex,
1973 Float aValue)
1975 switch (aIndex) {
1976 case ATT_LINEAR_TRANSFER_SLOPE_R:
1977 mSlopeR = aValue;
1978 break;
1979 case ATT_LINEAR_TRANSFER_INTERCEPT_R:
1980 mInterceptR = aValue;
1981 break;
1982 case ATT_LINEAR_TRANSFER_SLOPE_G:
1983 mSlopeG = aValue;
1984 break;
1985 case ATT_LINEAR_TRANSFER_INTERCEPT_G:
1986 mInterceptG = aValue;
1987 break;
1988 case ATT_LINEAR_TRANSFER_SLOPE_B:
1989 mSlopeB = aValue;
1990 break;
1991 case ATT_LINEAR_TRANSFER_INTERCEPT_B:
1992 mInterceptB = aValue;
1993 break;
1994 case ATT_LINEAR_TRANSFER_SLOPE_A:
1995 mSlopeA = aValue;
1996 break;
1997 case ATT_LINEAR_TRANSFER_INTERCEPT_A:
1998 mInterceptA = aValue;
1999 break;
2000 default:
2001 MOZ_CRASH();
2003 Invalidate();
2006 void
2007 FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
2008 uint8_t aTable[256])
2010 switch (aComponent) {
2011 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
2012 FillLookupTableImpl(mSlopeR, mInterceptR, aTable);
2013 break;
2014 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
2015 FillLookupTableImpl(mSlopeG, mInterceptG, aTable);
2016 break;
2017 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
2018 FillLookupTableImpl(mSlopeB, mInterceptB, aTable);
2019 break;
2020 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
2021 FillLookupTableImpl(mSlopeA, mInterceptA, aTable);
2022 break;
2023 default:
2024 MOZ_ASSERT(false, "unknown component");
2025 break;
2029 void
2030 FilterNodeLinearTransferSoftware::FillLookupTableImpl(Float aSlope,
2031 Float aIntercept,
2032 uint8_t aTable[256])
2034 for (size_t i = 0; i < 256; i++) {
2035 int32_t val = NS_lround(aSlope * i + 255 * aIntercept);
2036 val = std::min(255, val);
2037 val = std::max(0, val);
2038 aTable[i] = val;
2042 FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware()
2043 : mAmplitudeR(0)
2044 , mAmplitudeG(0)
2045 , mAmplitudeB(0)
2046 , mAmplitudeA(0)
2047 , mExponentR(0)
2048 , mExponentG(0)
2049 , mExponentB(0)
2050 , mExponentA(0)
2053 void
2054 FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex,
2055 Float aValue)
2057 switch (aIndex) {
2058 case ATT_GAMMA_TRANSFER_AMPLITUDE_R:
2059 mAmplitudeR = aValue;
2060 break;
2061 case ATT_GAMMA_TRANSFER_EXPONENT_R:
2062 mExponentR = aValue;
2063 break;
2064 case ATT_GAMMA_TRANSFER_OFFSET_R:
2065 mOffsetR = aValue;
2066 break;
2067 case ATT_GAMMA_TRANSFER_AMPLITUDE_G:
2068 mAmplitudeG = aValue;
2069 break;
2070 case ATT_GAMMA_TRANSFER_EXPONENT_G:
2071 mExponentG = aValue;
2072 break;
2073 case ATT_GAMMA_TRANSFER_OFFSET_G:
2074 mOffsetG = aValue;
2075 break;
2076 case ATT_GAMMA_TRANSFER_AMPLITUDE_B:
2077 mAmplitudeB = aValue;
2078 break;
2079 case ATT_GAMMA_TRANSFER_EXPONENT_B:
2080 mExponentB = aValue;
2081 break;
2082 case ATT_GAMMA_TRANSFER_OFFSET_B:
2083 mOffsetB = aValue;
2084 break;
2085 case ATT_GAMMA_TRANSFER_AMPLITUDE_A:
2086 mAmplitudeA = aValue;
2087 break;
2088 case ATT_GAMMA_TRANSFER_EXPONENT_A:
2089 mExponentA = aValue;
2090 break;
2091 case ATT_GAMMA_TRANSFER_OFFSET_A:
2092 mOffsetA = aValue;
2093 break;
2094 default:
2095 MOZ_CRASH();
2097 Invalidate();
2100 void
2101 FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
2102 uint8_t aTable[256])
2104 switch (aComponent) {
2105 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
2106 FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable);
2107 break;
2108 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
2109 FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable);
2110 break;
2111 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
2112 FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable);
2113 break;
2114 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
2115 FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable);
2116 break;
2117 default:
2118 MOZ_ASSERT(false, "unknown component");
2119 break;
2123 void
2124 FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude,
2125 Float aExponent,
2126 Float aOffset,
2127 uint8_t aTable[256])
2129 for (size_t i = 0; i < 256; i++) {
2130 int32_t val = NS_lround(255 * (aAmplitude * pow(i / 255.0f, aExponent) + aOffset));
2131 val = std::min(255, val);
2132 val = std::max(0, val);
2133 aTable[i] = val;
2137 FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware()
2138 : mDivisor(0)
2139 , mBias(0)
2140 , mEdgeMode(EDGE_MODE_DUPLICATE)
2141 , mPreserveAlpha(false)
2144 int32_t
2145 FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex)
2147 switch (aInputEnumIndex) {
2148 case IN_CONVOLVE_MATRIX_IN: return 0;
2149 default: return -1;
2153 void
2154 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2155 const IntSize &aKernelSize)
2157 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_SIZE);
2158 mKernelSize = aKernelSize;
2159 Invalidate();
2162 void
2163 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2164 const Float *aMatrix,
2165 uint32_t aSize)
2167 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_MATRIX);
2168 mKernelMatrix = std::vector<Float>(aMatrix, aMatrix + aSize);
2169 Invalidate();
2172 void
2173 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, Float aValue)
2175 switch (aIndex) {
2176 case ATT_CONVOLVE_MATRIX_DIVISOR:
2177 mDivisor = aValue;
2178 break;
2179 case ATT_CONVOLVE_MATRIX_BIAS:
2180 mBias = aValue;
2181 break;
2182 default:
2183 MOZ_CRASH();
2185 Invalidate();
2188 void
2189 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
2191 switch (aIndex) {
2192 case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH:
2193 mKernelUnitLength = aKernelUnitLength;
2194 break;
2195 default:
2196 MOZ_CRASH();
2198 Invalidate();
2201 void
2202 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2203 const IntPoint &aTarget)
2205 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_TARGET);
2206 mTarget = aTarget;
2207 Invalidate();
2210 void
2211 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2212 const IntRect &aSourceRect)
2214 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_SOURCE_RECT);
2215 mSourceRect = aSourceRect;
2216 Invalidate();
2219 void
2220 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2221 uint32_t aEdgeMode)
2223 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_EDGE_MODE);
2224 mEdgeMode = static_cast<ConvolveMatrixEdgeMode>(aEdgeMode);
2225 Invalidate();
2228 void
2229 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2230 bool aPreserveAlpha)
2232 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA);
2233 mPreserveAlpha = aPreserveAlpha;
2234 Invalidate();
2237 #ifdef DEBUG
2238 static bool sColorSamplingAccessControlEnabled = false;
2239 static uint8_t* sColorSamplingAccessControlStart = nullptr;
2240 static uint8_t* sColorSamplingAccessControlEnd = nullptr;
2242 struct DebugOnlyAutoColorSamplingAccessControl
2244 explicit DebugOnlyAutoColorSamplingAccessControl(DataSourceSurface* aSurface)
2246 sColorSamplingAccessControlStart = aSurface->GetData();
2247 sColorSamplingAccessControlEnd = sColorSamplingAccessControlStart +
2248 aSurface->Stride() * aSurface->GetSize().height;
2249 sColorSamplingAccessControlEnabled = true;
2252 ~DebugOnlyAutoColorSamplingAccessControl()
2254 sColorSamplingAccessControlEnabled = false;
2258 static inline void
2259 DebugOnlyCheckColorSamplingAccess(const uint8_t* aSampleAddress)
2261 if (sColorSamplingAccessControlEnabled) {
2262 MOZ_ASSERT(aSampleAddress >= sColorSamplingAccessControlStart, "accessing before start");
2263 MOZ_ASSERT(aSampleAddress < sColorSamplingAccessControlEnd, "accessing after end");
2266 #else
2267 typedef DebugOnly<DataSourceSurface*> DebugOnlyAutoColorSamplingAccessControl;
2268 #define DebugOnlyCheckColorSamplingAccess(address)
2269 #endif
2271 static inline uint8_t
2272 ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y, size_t bpp, ptrdiff_t c)
2274 DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c]);
2275 return aData[y * aStride + bpp * x + c];
2278 static inline int32_t
2279 ColorAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y)
2281 DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x);
2282 return *(uint32_t*)(aData + y * aStride + 4 * x);
2285 // Accepts fractional x & y and does bilinear interpolation.
2286 // Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible.
2287 static inline uint8_t
2288 ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, Float x, Float y, size_t bpp, ptrdiff_t c)
2290 const uint32_t f = 256;
2291 const int32_t lx = floor(x);
2292 const int32_t ly = floor(y);
2293 const int32_t tux = uint32_t((x - lx) * f);
2294 const int32_t tlx = f - tux;
2295 const int32_t tuy = uint32_t((y - ly) * f);
2296 const int32_t tly = f - tuy;
2297 const uint8_t &cll = ColorComponentAtPoint(aData, aStride, lx, ly, bpp, c);
2298 const uint8_t &cul = ColorComponentAtPoint(aData, aStride, lx + 1, ly, bpp, c);
2299 const uint8_t &clu = ColorComponentAtPoint(aData, aStride, lx, ly + 1, bpp, c);
2300 const uint8_t &cuu = ColorComponentAtPoint(aData, aStride, lx + 1, ly + 1, bpp, c);
2301 return ((cll * tlx + cul * tux) * tly +
2302 (clu * tlx + cuu * tux) * tuy + f * f / 2) / (f * f);
2305 static int32_t
2306 ClampToNonZero(int32_t a)
2308 return a * (a >= 0);
2311 template<typename CoordType>
2312 static void
2313 ConvolvePixel(const uint8_t *aSourceData,
2314 uint8_t *aTargetData,
2315 int32_t aWidth, int32_t aHeight,
2316 int32_t aSourceStride, int32_t aTargetStride,
2317 int32_t aX, int32_t aY,
2318 const int32_t *aKernel,
2319 int32_t aBias, int32_t shiftL, int32_t shiftR,
2320 bool aPreserveAlpha,
2321 int32_t aOrderX, int32_t aOrderY,
2322 int32_t aTargetX, int32_t aTargetY,
2323 CoordType aKernelUnitLengthX,
2324 CoordType aKernelUnitLengthY)
2326 int32_t sum[4] = {0, 0, 0, 0};
2327 int32_t offsets[4] = { B8G8R8A8_COMPONENT_BYTEOFFSET_R,
2328 B8G8R8A8_COMPONENT_BYTEOFFSET_G,
2329 B8G8R8A8_COMPONENT_BYTEOFFSET_B,
2330 B8G8R8A8_COMPONENT_BYTEOFFSET_A };
2331 int32_t channels = aPreserveAlpha ? 3 : 4;
2332 int32_t roundingAddition = shiftL == 0 ? 0 : 1 << (shiftL - 1);
2334 for (int32_t y = 0; y < aOrderY; y++) {
2335 CoordType sampleY = aY + (y - aTargetY) * aKernelUnitLengthY;
2336 for (int32_t x = 0; x < aOrderX; x++) {
2337 CoordType sampleX = aX + (x - aTargetX) * aKernelUnitLengthX;
2338 for (int32_t i = 0; i < channels; i++) {
2339 sum[i] += aKernel[aOrderX * y + x] *
2340 ColorComponentAtPoint(aSourceData, aSourceStride,
2341 sampleX, sampleY, 4, offsets[i]);
2345 for (int32_t i = 0; i < channels; i++) {
2346 int32_t clamped = umin(ClampToNonZero(sum[i] + aBias), 255 << shiftL >> shiftR);
2347 aTargetData[aY * aTargetStride + 4 * aX + offsets[i]] =
2348 (clamped + roundingAddition) << shiftR >> shiftL;
2350 if (aPreserveAlpha) {
2351 aTargetData[aY * aTargetStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
2352 aSourceData[aY * aSourceStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
2356 TemporaryRef<DataSourceSurface>
2357 FilterNodeConvolveMatrixSoftware::Render(const IntRect& aRect)
2359 if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
2360 mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
2361 return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height);
2363 return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
2366 static std::vector<Float>
2367 ReversedVector(const std::vector<Float> &aVector)
2369 size_t length = aVector.size();
2370 std::vector<Float> result(length, 0);
2371 for (size_t i = 0; i < length; i++) {
2372 result[length - 1 - i] = aVector[i];
2374 return result;
2377 static std::vector<Float>
2378 ScaledVector(const std::vector<Float> &aVector, Float aDivisor)
2380 size_t length = aVector.size();
2381 std::vector<Float> result(length, 0);
2382 for (size_t i = 0; i < length; i++) {
2383 result[i] = aVector[i] / aDivisor;
2385 return result;
2388 static Float
2389 MaxVectorSum(const std::vector<Float> &aVector)
2391 Float sum = 0;
2392 size_t length = aVector.size();
2393 for (size_t i = 0; i < length; i++) {
2394 if (aVector[i] > 0) {
2395 sum += aVector[i];
2398 return sum;
2401 // Returns shiftL and shiftR in such a way that
2402 // a << shiftL >> shiftR is roughly a * aFloat.
2403 static void
2404 TranslateDoubleToShifts(double aDouble, int32_t &aShiftL, int32_t &aShiftR)
2406 aShiftL = 0;
2407 aShiftR = 0;
2408 if (aDouble <= 0) {
2409 MOZ_CRASH();
2411 if (aDouble < 1) {
2412 while (1 << (aShiftR + 1) < 1 / aDouble) {
2413 aShiftR++;
2415 } else {
2416 while (1 << (aShiftL + 1) < aDouble) {
2417 aShiftL++;
2422 template<typename CoordType>
2423 TemporaryRef<DataSourceSurface>
2424 FilterNodeConvolveMatrixSoftware::DoRender(const IntRect& aRect,
2425 CoordType aKernelUnitLengthX,
2426 CoordType aKernelUnitLengthY)
2428 if (mKernelSize.width <= 0 || mKernelSize.height <= 0 ||
2429 mKernelMatrix.size() != uint32_t(mKernelSize.width * mKernelSize.height) ||
2430 !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) ||
2431 mDivisor == 0) {
2432 return Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
2435 IntRect srcRect = InflatedSourceRect(aRect);
2437 // Inflate the source rect by another pixel because the bilinear filtering in
2438 // ColorComponentAtPoint may want to access the margins.
2439 srcRect.Inflate(1);
2441 RefPtr<DataSourceSurface> input =
2442 GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect, NEED_COLOR_CHANNELS, mEdgeMode, &mSourceRect);
2444 if (!input) {
2445 return nullptr;
2448 DebugOnlyAutoColorSamplingAccessControl accessControl(input);
2450 RefPtr<DataSourceSurface> target =
2451 Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true);
2452 if (MOZ2D_WARN_IF(!target)) {
2453 return nullptr;
2456 IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
2458 uint8_t* sourceData = DataAtOffset(input, offset);
2459 int32_t sourceStride = input->Stride();
2460 uint8_t* targetData = target->GetData();
2461 int32_t targetStride = target->Stride();
2463 // Why exactly are we reversing the kernel?
2464 std::vector<Float> kernel = ReversedVector(mKernelMatrix);
2465 kernel = ScaledVector(kernel, mDivisor);
2466 Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias,
2467 MaxVectorSum(ScaledVector(kernel, -1)) - mBias);
2468 maxResultAbs = std::max(maxResultAbs, 1.0f);
2470 double idealFactor = INT32_MAX / 2.0 / maxResultAbs / 255.0 * 0.999;
2471 MOZ_ASSERT(255.0 * maxResultAbs * idealFactor <= INT32_MAX / 2.0, "badly chosen float-to-int scale");
2472 int32_t shiftL, shiftR;
2473 TranslateDoubleToShifts(idealFactor, shiftL, shiftR);
2474 double factorFromShifts = Float(1 << shiftL) / Float(1 << shiftR);
2475 MOZ_ASSERT(255.0 * maxResultAbs * factorFromShifts <= INT32_MAX / 2.0, "badly chosen float-to-int scale");
2477 int32_t* intKernel = new int32_t[kernel.size()];
2478 for (size_t i = 0; i < kernel.size(); i++) {
2479 intKernel[i] = NS_lround(kernel[i] * factorFromShifts);
2481 int32_t bias = NS_lround(mBias * 255 * factorFromShifts);
2483 for (int32_t y = 0; y < aRect.height; y++) {
2484 for (int32_t x = 0; x < aRect.width; x++) {
2485 ConvolvePixel(sourceData, targetData,
2486 aRect.width, aRect.height, sourceStride, targetStride,
2487 x, y, intKernel, bias, shiftL, shiftR, mPreserveAlpha,
2488 mKernelSize.width, mKernelSize.height, mTarget.x, mTarget.y,
2489 aKernelUnitLengthX, aKernelUnitLengthY);
2492 delete[] intKernel;
2494 return target.forget();
2497 void
2498 FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
2500 RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect));
2503 IntRect
2504 FilterNodeConvolveMatrixSoftware::InflatedSourceRect(const IntRect &aDestRect)
2506 if (aDestRect.IsEmpty()) {
2507 return IntRect();
2510 IntMargin margin;
2511 margin.left = ceil(mTarget.x * mKernelUnitLength.width);
2512 margin.top = ceil(mTarget.y * mKernelUnitLength.height);
2513 margin.right = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
2514 margin.bottom = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
2516 IntRect srcRect = aDestRect;
2517 srcRect.Inflate(margin);
2518 return srcRect;
2521 IntRect
2522 FilterNodeConvolveMatrixSoftware::InflatedDestRect(const IntRect &aSourceRect)
2524 if (aSourceRect.IsEmpty()) {
2525 return IntRect();
2528 IntMargin margin;
2529 margin.left = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
2530 margin.top = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
2531 margin.right = ceil(mTarget.x * mKernelUnitLength.width);
2532 margin.bottom = ceil(mTarget.y * mKernelUnitLength.height);
2534 IntRect destRect = aSourceRect;
2535 destRect.Inflate(margin);
2536 return destRect;
2539 IntRect
2540 FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(const IntRect& aRect)
2542 IntRect srcRequest = InflatedSourceRect(aRect);
2543 IntRect srcOutput = GetInputRectInRect(IN_COLOR_MATRIX_IN, srcRequest);
2544 return InflatedDestRect(srcOutput).Intersect(aRect);
2547 FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware()
2548 : mScale(0.0f)
2549 , mChannelX(COLOR_CHANNEL_R)
2550 , mChannelY(COLOR_CHANNEL_G)
2553 int32_t
2554 FilterNodeDisplacementMapSoftware::InputIndex(uint32_t aInputEnumIndex)
2556 switch (aInputEnumIndex) {
2557 case IN_DISPLACEMENT_MAP_IN: return 0;
2558 case IN_DISPLACEMENT_MAP_IN2: return 1;
2559 default: return -1;
2563 void
2564 FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
2565 Float aScale)
2567 MOZ_ASSERT(aIndex == ATT_DISPLACEMENT_MAP_SCALE);
2568 mScale = aScale;
2569 Invalidate();
2572 void
2573 FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue)
2575 switch (aIndex) {
2576 case ATT_DISPLACEMENT_MAP_X_CHANNEL:
2577 mChannelX = static_cast<ColorChannel>(aValue);
2578 break;
2579 case ATT_DISPLACEMENT_MAP_Y_CHANNEL:
2580 mChannelY = static_cast<ColorChannel>(aValue);
2581 break;
2582 default:
2583 MOZ_CRASH();
2585 Invalidate();
2588 TemporaryRef<DataSourceSurface>
2589 FilterNodeDisplacementMapSoftware::Render(const IntRect& aRect)
2591 IntRect srcRect = InflatedSourceOrDestRect(aRect);
2592 RefPtr<DataSourceSurface> input =
2593 GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN, srcRect, NEED_COLOR_CHANNELS);
2594 RefPtr<DataSourceSurface> map =
2595 GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN2, aRect, NEED_COLOR_CHANNELS);
2596 RefPtr<DataSourceSurface> target =
2597 Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
2598 if (MOZ2D_WARN_IF(!(input && map && target))) {
2599 return nullptr;
2602 IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
2604 uint8_t* sourceData = DataAtOffset(input, offset);
2605 int32_t sourceStride = input->Stride();
2606 uint8_t* mapData = map->GetData();
2607 int32_t mapStride = map->Stride();
2608 uint8_t* targetData = target->GetData();
2609 int32_t targetStride = target->Stride();
2611 static const ptrdiff_t channelMap[4] = {
2612 B8G8R8A8_COMPONENT_BYTEOFFSET_R,
2613 B8G8R8A8_COMPONENT_BYTEOFFSET_G,
2614 B8G8R8A8_COMPONENT_BYTEOFFSET_B,
2615 B8G8R8A8_COMPONENT_BYTEOFFSET_A };
2616 uint16_t xChannel = channelMap[mChannelX];
2617 uint16_t yChannel = channelMap[mChannelY];
2619 float scaleOver255 = mScale / 255.0f;
2620 float scaleAdjustment = -0.5f * mScale;
2622 for (int32_t y = 0; y < aRect.height; y++) {
2623 for (int32_t x = 0; x < aRect.width; x++) {
2624 uint32_t mapIndex = y * mapStride + 4 * x;
2625 uint32_t targIndex = y * targetStride + 4 * x;
2626 int32_t sourceX = x +
2627 scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment;
2628 int32_t sourceY = y +
2629 scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment;
2630 *(uint32_t*)(targetData + targIndex) =
2631 ColorAtPoint(sourceData, sourceStride, sourceX, sourceY);
2635 return target.forget();
2638 void
2639 FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(const IntRect &aRect)
2641 RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect));
2642 RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect);
2645 IntRect
2646 FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect)
2648 IntRect sourceOrDestRect = aDestOrSourceRect;
2649 sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2));
2650 return sourceOrDestRect;
2653 IntRect
2654 FilterNodeDisplacementMapSoftware::GetOutputRectInRect(const IntRect& aRect)
2656 IntRect srcRequest = InflatedSourceOrDestRect(aRect);
2657 IntRect srcOutput = GetInputRectInRect(IN_DISPLACEMENT_MAP_IN, srcRequest);
2658 return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
2661 FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware()
2662 : mNumOctaves(0)
2663 , mSeed(0)
2664 , mStitchable(false)
2665 , mType(TURBULENCE_TYPE_TURBULENCE)
2668 int32_t
2669 FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex)
2671 return -1;
2674 void
2675 FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const Size &aBaseFrequency)
2677 switch (aIndex) {
2678 case ATT_TURBULENCE_BASE_FREQUENCY:
2679 mBaseFrequency = aBaseFrequency;
2680 break;
2681 default:
2682 MOZ_CRASH();
2683 break;
2685 Invalidate();
2688 void
2689 FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const IntRect &aRect)
2691 switch (aIndex) {
2692 case ATT_TURBULENCE_RECT:
2693 mRenderRect = aRect;
2694 break;
2695 default:
2696 MOZ_CRASH();
2697 break;
2699 Invalidate();
2702 void
2703 FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, bool aStitchable)
2705 MOZ_ASSERT(aIndex == ATT_TURBULENCE_STITCHABLE);
2706 mStitchable = aStitchable;
2707 Invalidate();
2710 void
2711 FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue)
2713 switch (aIndex) {
2714 case ATT_TURBULENCE_NUM_OCTAVES:
2715 mNumOctaves = aValue;
2716 break;
2717 case ATT_TURBULENCE_SEED:
2718 mSeed = aValue;
2719 break;
2720 case ATT_TURBULENCE_TYPE:
2721 mType = static_cast<TurbulenceType>(aValue);
2722 break;
2723 default:
2724 MOZ_CRASH();
2725 break;
2727 Invalidate();
2730 TemporaryRef<DataSourceSurface>
2731 FilterNodeTurbulenceSoftware::Render(const IntRect& aRect)
2733 return FilterProcessing::RenderTurbulence(
2734 aRect.Size(), aRect.TopLeft(), mBaseFrequency,
2735 mSeed, mNumOctaves, mType, mStitchable, Rect(mRenderRect));
2738 IntRect
2739 FilterNodeTurbulenceSoftware::GetOutputRectInRect(const IntRect& aRect)
2741 return aRect.Intersect(mRenderRect);
2744 FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware()
2745 : mK1(0), mK2(0), mK3(0), mK4(0)
2749 int32_t
2750 FilterNodeArithmeticCombineSoftware::InputIndex(uint32_t aInputEnumIndex)
2752 switch (aInputEnumIndex) {
2753 case IN_ARITHMETIC_COMBINE_IN: return 0;
2754 case IN_ARITHMETIC_COMBINE_IN2: return 1;
2755 default: return -1;
2759 void
2760 FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex,
2761 const Float* aFloat,
2762 uint32_t aSize)
2764 MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS);
2765 MOZ_ASSERT(aSize == 4);
2767 mK1 = aFloat[0];
2768 mK2 = aFloat[1];
2769 mK3 = aFloat[2];
2770 mK4 = aFloat[3];
2772 Invalidate();
2775 TemporaryRef<DataSourceSurface>
2776 FilterNodeArithmeticCombineSoftware::Render(const IntRect& aRect)
2778 RefPtr<DataSourceSurface> input1 =
2779 GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS);
2780 RefPtr<DataSourceSurface> input2 =
2781 GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN2, aRect, NEED_COLOR_CHANNELS);
2782 if (!input1 && !input2) {
2783 return nullptr;
2786 // If one input is null, treat it as transparent by adjusting the factors.
2787 Float k1 = mK1, k2 = mK2, k3 = mK3, k4 = mK4;
2788 if (!input1) {
2789 k1 = 0.0f;
2790 k2 = 0.0f;
2791 input1 = input2;
2794 if (!input2) {
2795 k1 = 0.0f;
2796 k3 = 0.0f;
2797 input2 = input1;
2800 return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3, k4);
2803 void
2804 FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(const IntRect &aRect)
2806 RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect);
2807 RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect);
2810 IntRect
2811 FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(const IntRect& aRect)
2813 if (mK4 > 0.0f) {
2814 return aRect;
2816 IntRect rectFrom1 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect);
2817 IntRect rectFrom2 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2, aRect).Intersect(aRect);
2818 IntRect result;
2819 if (mK1 > 0.0f) {
2820 result = rectFrom1.Intersect(rectFrom2);
2822 if (mK2 > 0.0f) {
2823 result = result.Union(rectFrom1);
2825 if (mK3 > 0.0f) {
2826 result = result.Union(rectFrom2);
2828 return result;
2831 FilterNodeCompositeSoftware::FilterNodeCompositeSoftware()
2832 : mOperator(COMPOSITE_OPERATOR_OVER)
2835 int32_t
2836 FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex)
2838 return aInputEnumIndex - IN_COMPOSITE_IN_START;
2841 void
2842 FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex, uint32_t aCompositeOperator)
2844 MOZ_ASSERT(aIndex == ATT_COMPOSITE_OPERATOR);
2845 mOperator = static_cast<CompositeOperator>(aCompositeOperator);
2846 Invalidate();
2849 TemporaryRef<DataSourceSurface>
2850 FilterNodeCompositeSoftware::Render(const IntRect& aRect)
2852 RefPtr<DataSourceSurface> start =
2853 GetInputDataSourceSurface(IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS);
2854 RefPtr<DataSourceSurface> dest =
2855 Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, !start);
2856 if (MOZ2D_WARN_IF(!dest)) {
2857 return nullptr;
2860 if (start) {
2861 CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint());
2864 for (size_t inputIndex = 1; inputIndex < NumberOfSetInputs(); inputIndex++) {
2865 RefPtr<DataSourceSurface> input =
2866 GetInputDataSourceSurface(IN_COMPOSITE_IN_START + inputIndex, aRect, NEED_COLOR_CHANNELS);
2867 if (input) {
2868 FilterProcessing::ApplyComposition(input, dest, mOperator);
2869 } else {
2870 // We need to treat input as transparent. Depending on the composite
2871 // operator, different things happen to dest.
2872 switch (mOperator) {
2873 case COMPOSITE_OPERATOR_OVER:
2874 case COMPOSITE_OPERATOR_ATOP:
2875 case COMPOSITE_OPERATOR_XOR:
2876 // dest is unchanged.
2877 break;
2878 case COMPOSITE_OPERATOR_OUT:
2879 // dest is now transparent, but it can become non-transparent again
2880 // when compositing additional inputs.
2881 ClearDataSourceSurface(dest);
2882 break;
2883 case COMPOSITE_OPERATOR_IN:
2884 // Transparency always wins. We're completely transparent now and
2885 // no additional input can get rid of that transparency.
2886 return nullptr;
2890 return dest.forget();
2893 void
2894 FilterNodeCompositeSoftware::RequestFromInputsForRect(const IntRect &aRect)
2896 for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
2897 RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
2901 IntRect
2902 FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect& aRect)
2904 IntRect rect;
2905 for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
2906 IntRect inputRect = GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
2907 if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) {
2908 rect = rect.Intersect(inputRect);
2909 } else {
2910 rect = rect.Union(inputRect);
2913 return rect;
2916 int32_t
2917 FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex)
2919 switch (aInputEnumIndex) {
2920 case IN_GAUSSIAN_BLUR_IN: return 0;
2921 default: return -1;
2925 TemporaryRef<DataSourceSurface>
2926 FilterNodeBlurXYSoftware::Render(const IntRect& aRect)
2928 Size sigmaXY = StdDeviationXY();
2929 IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
2931 if (d.width == 0 && d.height == 0) {
2932 return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, aRect);
2935 IntRect srcRect = InflatedSourceOrDestRect(aRect);
2936 RefPtr<DataSourceSurface> input =
2937 GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, srcRect);
2938 if (!input) {
2939 return nullptr;
2942 RefPtr<DataSourceSurface> target;
2943 Rect r(0, 0, srcRect.width, srcRect.height);
2945 if (input->GetFormat() == SurfaceFormat::A8) {
2946 target = Factory::CreateDataSourceSurface(srcRect.Size(), SurfaceFormat::A8);
2947 if (MOZ2D_WARN_IF(!target)) {
2948 return nullptr;
2950 CopyRect(input, target, IntRect(IntPoint(), input->GetSize()), IntPoint());
2951 AlphaBoxBlur blur(r, target->Stride(), sigmaXY.width, sigmaXY.height);
2952 blur.Blur(target->GetData());
2953 } else {
2954 RefPtr<DataSourceSurface> channel0, channel1, channel2, channel3;
2955 FilterProcessing::SeparateColorChannels(input, channel0, channel1, channel2, channel3);
2956 if (MOZ2D_WARN_IF(!(channel0 && channel1 && channel2))) {
2957 return nullptr;
2959 AlphaBoxBlur blur(r, channel0->Stride(), sigmaXY.width, sigmaXY.height);
2960 blur.Blur(channel0->GetData());
2961 blur.Blur(channel1->GetData());
2962 blur.Blur(channel2->GetData());
2963 blur.Blur(channel3->GetData());
2964 target = FilterProcessing::CombineColorChannels(channel0, channel1, channel2, channel3);
2967 return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE);
2970 void
2971 FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect &aRect)
2973 RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect));
2976 IntRect
2977 FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(const IntRect &aDestRect)
2979 Size sigmaXY = StdDeviationXY();
2980 IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
2981 IntRect srcRect = aDestRect;
2982 srcRect.Inflate(d);
2983 return srcRect;
2986 IntRect
2987 FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect& aRect)
2989 IntRect srcRequest = InflatedSourceOrDestRect(aRect);
2990 IntRect srcOutput = GetInputRectInRect(IN_GAUSSIAN_BLUR_IN, srcRequest);
2991 return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
2994 FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware()
2995 : mStdDeviation(0)
2998 static float
2999 ClampStdDeviation(float aStdDeviation)
3001 // Cap software blur radius for performance reasons.
3002 return std::min(std::max(0.0f, aStdDeviation), 100.0f);
3005 void
3006 FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex,
3007 float aStdDeviation)
3009 switch (aIndex) {
3010 case ATT_GAUSSIAN_BLUR_STD_DEVIATION:
3011 mStdDeviation = ClampStdDeviation(aStdDeviation);
3012 break;
3013 default:
3014 MOZ_CRASH();
3016 Invalidate();
3019 Size
3020 FilterNodeGaussianBlurSoftware::StdDeviationXY()
3022 return Size(mStdDeviation, mStdDeviation);
3025 FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware()
3026 : mBlurDirection(BLUR_DIRECTION_X)
3029 void
3030 FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
3031 Float aStdDeviation)
3033 switch (aIndex) {
3034 case ATT_DIRECTIONAL_BLUR_STD_DEVIATION:
3035 mStdDeviation = ClampStdDeviation(aStdDeviation);
3036 break;
3037 default:
3038 MOZ_CRASH();
3040 Invalidate();
3043 void
3044 FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
3045 uint32_t aBlurDirection)
3047 switch (aIndex) {
3048 case ATT_DIRECTIONAL_BLUR_DIRECTION:
3049 mBlurDirection = (BlurDirection)aBlurDirection;
3050 break;
3051 default:
3052 MOZ_CRASH();
3054 Invalidate();
3057 Size
3058 FilterNodeDirectionalBlurSoftware::StdDeviationXY()
3060 float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0;
3061 float sigmaY = mBlurDirection == BLUR_DIRECTION_Y ? mStdDeviation : 0;
3062 return Size(sigmaX, sigmaY);
3065 int32_t
3066 FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex)
3068 switch (aInputEnumIndex) {
3069 case IN_CROP_IN: return 0;
3070 default: return -1;
3074 void
3075 FilterNodeCropSoftware::SetAttribute(uint32_t aIndex,
3076 const Rect &aSourceRect)
3078 MOZ_ASSERT(aIndex == ATT_CROP_RECT);
3079 Rect srcRect = aSourceRect;
3080 srcRect.Round();
3081 if (!srcRect.ToIntRect(&mCropRect)) {
3082 mCropRect = IntRect();
3084 Invalidate();
3087 TemporaryRef<DataSourceSurface>
3088 FilterNodeCropSoftware::Render(const IntRect& aRect)
3090 return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect));
3093 void
3094 FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect &aRect)
3096 RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect));
3099 IntRect
3100 FilterNodeCropSoftware::GetOutputRectInRect(const IntRect& aRect)
3102 return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect);
3105 int32_t
3106 FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex)
3108 switch (aInputEnumIndex) {
3109 case IN_PREMULTIPLY_IN: return 0;
3110 default: return -1;
3114 TemporaryRef<DataSourceSurface>
3115 FilterNodePremultiplySoftware::Render(const IntRect& aRect)
3117 RefPtr<DataSourceSurface> input =
3118 GetInputDataSourceSurface(IN_PREMULTIPLY_IN, aRect);
3119 return input ? Premultiply(input) : nullptr;
3122 void
3123 FilterNodePremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect)
3125 RequestInputRect(IN_PREMULTIPLY_IN, aRect);
3128 IntRect
3129 FilterNodePremultiplySoftware::GetOutputRectInRect(const IntRect& aRect)
3131 return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect);
3134 int32_t
3135 FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex)
3137 switch (aInputEnumIndex) {
3138 case IN_UNPREMULTIPLY_IN: return 0;
3139 default: return -1;
3143 TemporaryRef<DataSourceSurface>
3144 FilterNodeUnpremultiplySoftware::Render(const IntRect& aRect)
3146 RefPtr<DataSourceSurface> input =
3147 GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN, aRect);
3148 return input ? Unpremultiply(input) : nullptr;
3151 void
3152 FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect)
3154 RequestInputRect(IN_UNPREMULTIPLY_IN, aRect);
3157 IntRect
3158 FilterNodeUnpremultiplySoftware::GetOutputRectInRect(const IntRect& aRect)
3160 return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect);
3163 bool
3164 PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
3166 switch (aIndex) {
3167 case ATT_POINT_LIGHT_POSITION:
3168 mPosition = aPoint;
3169 break;
3170 default:
3171 return false;
3173 return true;
3176 SpotLightSoftware::SpotLightSoftware()
3177 : mSpecularFocus(0)
3178 , mLimitingConeAngle(0)
3179 , mLimitingConeCos(1)
3183 bool
3184 SpotLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
3186 switch (aIndex) {
3187 case ATT_SPOT_LIGHT_POSITION:
3188 mPosition = aPoint;
3189 break;
3190 case ATT_SPOT_LIGHT_POINTS_AT:
3191 mPointsAt = aPoint;
3192 break;
3193 default:
3194 return false;
3196 return true;
3199 bool
3200 SpotLightSoftware::SetAttribute(uint32_t aIndex, Float aValue)
3202 switch (aIndex) {
3203 case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE:
3204 mLimitingConeAngle = aValue;
3205 break;
3206 case ATT_SPOT_LIGHT_FOCUS:
3207 mSpecularFocus = aValue;
3208 break;
3209 default:
3210 return false;
3212 return true;
3215 DistantLightSoftware::DistantLightSoftware()
3216 : mAzimuth(0)
3217 , mElevation(0)
3221 bool
3222 DistantLightSoftware::SetAttribute(uint32_t aIndex, Float aValue)
3224 switch (aIndex) {
3225 case ATT_DISTANT_LIGHT_AZIMUTH:
3226 mAzimuth = aValue;
3227 break;
3228 case ATT_DISTANT_LIGHT_ELEVATION:
3229 mElevation = aValue;
3230 break;
3231 default:
3232 return false;
3234 return true;
3237 static inline Point3D Normalized(const Point3D &vec) {
3238 Point3D copy(vec);
3239 copy.Normalize();
3240 return copy;
3243 template<typename LightType, typename LightingType>
3244 FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware(const char* aTypeName)
3245 : mSurfaceScale(0)
3246 #if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
3247 , mTypeName(aTypeName)
3248 #endif
3251 template<typename LightType, typename LightingType>
3252 int32_t
3253 FilterNodeLightingSoftware<LightType, LightingType>::InputIndex(uint32_t aInputEnumIndex)
3255 switch (aInputEnumIndex) {
3256 case IN_LIGHTING_IN: return 0;
3257 default: return -1;
3261 template<typename LightType, typename LightingType>
3262 void
3263 FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
3265 if (mLight.SetAttribute(aIndex, aPoint)) {
3266 Invalidate();
3267 return;
3269 MOZ_CRASH();
3272 template<typename LightType, typename LightingType>
3273 void
3274 FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, Float aValue)
3276 if (mLight.SetAttribute(aIndex, aValue) ||
3277 mLighting.SetAttribute(aIndex, aValue)) {
3278 Invalidate();
3279 return;
3281 switch (aIndex) {
3282 case ATT_LIGHTING_SURFACE_SCALE:
3283 mSurfaceScale = aValue;
3284 break;
3285 default:
3286 MOZ_CRASH();
3288 Invalidate();
3291 template<typename LightType, typename LightingType>
3292 void
3293 FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
3295 switch (aIndex) {
3296 case ATT_LIGHTING_KERNEL_UNIT_LENGTH:
3297 mKernelUnitLength = aKernelUnitLength;
3298 break;
3299 default:
3300 MOZ_CRASH();
3302 Invalidate();
3305 template<typename LightType, typename LightingType>
3306 void
3307 FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Color &aColor)
3309 MOZ_ASSERT(aIndex == ATT_LIGHTING_COLOR);
3310 mColor = aColor;
3311 Invalidate();
3314 template<typename LightType, typename LightingType>
3315 IntRect
3316 FilterNodeLightingSoftware<LightType, LightingType>::GetOutputRectInRect(const IntRect& aRect)
3318 return GetInputRectInRect(IN_LIGHTING_IN, aRect);
3321 Point3D
3322 PointLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
3324 return Normalized(mPosition - aTargetPoint);
3327 uint32_t
3328 PointLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
3330 return aLightColor;
3333 void
3334 SpotLightSoftware::Prepare()
3336 mVectorFromFocusPointToLight = Normalized(mPointsAt - mPosition);
3337 mLimitingConeCos = std::max<double>(cos(mLimitingConeAngle * M_PI/180.0), 0.0);
3338 mPowCache.CacheForExponent(mSpecularFocus);
3341 Point3D
3342 SpotLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
3344 return Normalized(mPosition - aTargetPoint);
3347 uint32_t
3348 SpotLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
3350 union {
3351 uint32_t color;
3352 uint8_t colorC[4];
3354 color = aLightColor;
3355 Float dot = -aVectorToLight.DotProduct(mVectorFromFocusPointToLight);
3356 uint16_t doti = dot * (dot >= 0) * (1 << PowCache::sInputIntPrecisionBits);
3357 uint32_t tmp = mPowCache.Pow(doti) * (dot >= mLimitingConeCos);
3358 MOZ_ASSERT(tmp <= (1 << PowCache::sOutputIntPrecisionBits), "pow() result must not exceed 1.0");
3359 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] * tmp) >> PowCache::sOutputIntPrecisionBits);
3360 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] * tmp) >> PowCache::sOutputIntPrecisionBits);
3361 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] * tmp) >> PowCache::sOutputIntPrecisionBits);
3362 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
3363 return color;
3366 void
3367 DistantLightSoftware::Prepare()
3369 const double radPerDeg = M_PI / 180.0;
3370 mVectorToLight.x = cos(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
3371 mVectorToLight.y = sin(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
3372 mVectorToLight.z = sin(mElevation * radPerDeg);
3375 Point3D
3376 DistantLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
3378 return mVectorToLight;
3381 uint32_t
3382 DistantLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
3384 return aLightColor;
3387 template<typename CoordType>
3388 static Point3D
3389 GenerateNormal(const uint8_t *data, int32_t stride,
3390 int32_t x, int32_t y, float surfaceScale,
3391 CoordType dx, CoordType dy)
3393 const uint8_t *index = data + y * stride + x;
3395 CoordType zero = 0;
3397 // See this for source of constants:
3398 // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
3399 int16_t normalX =
3400 -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) +
3401 1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) +
3402 -2 * ColorComponentAtPoint(index, stride, -dx, zero, 1, 0) +
3403 2 * ColorComponentAtPoint(index, stride, dx, zero, 1, 0) +
3404 -1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) +
3405 1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0);
3407 int16_t normalY =
3408 -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) +
3409 -2 * ColorComponentAtPoint(index, stride, zero, -dy, 1, 0) +
3410 -1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) +
3411 1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) +
3412 2 * ColorComponentAtPoint(index, stride, zero, dy, 1, 0) +
3413 1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0);
3415 Point3D normal;
3416 normal.x = -surfaceScale * normalX / 4.0f;
3417 normal.y = -surfaceScale * normalY / 4.0f;
3418 normal.z = 255;
3419 return Normalized(normal);
3422 template<typename LightType, typename LightingType>
3423 TemporaryRef<DataSourceSurface>
3424 FilterNodeLightingSoftware<LightType, LightingType>::Render(const IntRect& aRect)
3426 if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
3427 mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
3428 return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height);
3430 return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
3433 template<typename LightType, typename LightingType>
3434 void
3435 FilterNodeLightingSoftware<LightType, LightingType>::RequestFromInputsForRect(const IntRect &aRect)
3437 IntRect srcRect = aRect;
3438 srcRect.Inflate(ceil(mKernelUnitLength.width),
3439 ceil(mKernelUnitLength.height));
3440 RequestInputRect(IN_LIGHTING_IN, srcRect);
3443 template<typename LightType, typename LightingType> template<typename CoordType>
3444 TemporaryRef<DataSourceSurface>
3445 FilterNodeLightingSoftware<LightType, LightingType>::DoRender(const IntRect& aRect,
3446 CoordType aKernelUnitLengthX,
3447 CoordType aKernelUnitLengthY)
3449 IntRect srcRect = aRect;
3450 IntSize size = aRect.Size();
3451 srcRect.Inflate(ceil(float(aKernelUnitLengthX)),
3452 ceil(float(aKernelUnitLengthY)));
3454 // Inflate the source rect by another pixel because the bilinear filtering in
3455 // ColorComponentAtPoint may want to access the margins.
3456 srcRect.Inflate(1);
3458 RefPtr<DataSourceSurface> input =
3459 GetInputDataSourceSurface(IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8,
3460 EDGE_MODE_DUPLICATE);
3462 if (!input) {
3463 return nullptr;
3466 if (input->GetFormat() != SurfaceFormat::A8) {
3467 input = FilterProcessing::ExtractAlpha(input);
3470 DebugOnlyAutoColorSamplingAccessControl accessControl(input);
3472 RefPtr<DataSourceSurface> target =
3473 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
3474 if (MOZ2D_WARN_IF(!target)) {
3475 return nullptr;
3478 IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
3480 uint8_t* sourceData = DataAtOffset(input, offset);
3481 int32_t sourceStride = input->Stride();
3482 uint8_t* targetData = target->GetData();
3483 int32_t targetStride = target->Stride();
3485 uint32_t lightColor = ColorToBGRA(mColor);
3486 mLight.Prepare();
3487 mLighting.Prepare();
3489 for (int32_t y = 0; y < size.height; y++) {
3490 for (int32_t x = 0; x < size.width; x++) {
3491 int32_t sourceIndex = y * sourceStride + x;
3492 int32_t targetIndex = y * targetStride + 4 * x;
3494 Point3D normal = GenerateNormal(sourceData, sourceStride,
3495 x, y, mSurfaceScale,
3496 aKernelUnitLengthX, aKernelUnitLengthY);
3498 IntPoint pointInFilterSpace(aRect.x + x, aRect.y + y);
3499 Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f;
3500 Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z);
3501 Point3D rayDir = mLight.GetVectorToLight(pt);
3502 uint32_t color = mLight.GetColor(lightColor, rayDir);
3504 *(uint32_t*)(targetData + targetIndex) = mLighting.LightPixel(normal, rayDir, color);
3508 return target.forget();
3511 DiffuseLightingSoftware::DiffuseLightingSoftware()
3512 : mDiffuseConstant(0)
3516 bool
3517 DiffuseLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue)
3519 switch (aIndex) {
3520 case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT:
3521 mDiffuseConstant = aValue;
3522 break;
3523 default:
3524 return false;
3526 return true;
3529 uint32_t
3530 DiffuseLightingSoftware::LightPixel(const Point3D &aNormal,
3531 const Point3D &aVectorToLight,
3532 uint32_t aColor)
3534 Float dotNL = std::max(0.0f, aNormal.DotProduct(aVectorToLight));
3535 Float diffuseNL = mDiffuseConstant * dotNL;
3537 union {
3538 uint32_t bgra;
3539 uint8_t components[4];
3540 } color = { aColor };
3541 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
3542 umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]), 255U);
3543 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
3544 umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]), 255U);
3545 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
3546 umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]), 255U);
3547 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
3548 return color.bgra;
3551 SpecularLightingSoftware::SpecularLightingSoftware()
3552 : mSpecularConstant(0)
3553 , mSpecularExponent(0)
3557 bool
3558 SpecularLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue)
3560 switch (aIndex) {
3561 case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT:
3562 mSpecularConstant = std::min(std::max(aValue, 0.0f), 255.0f);
3563 break;
3564 case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT:
3565 mSpecularExponent = std::min(std::max(aValue, 1.0f), 128.0f);
3566 break;
3567 default:
3568 return false;
3570 return true;
3573 void
3574 SpecularLightingSoftware::Prepare()
3576 mPowCache.CacheForExponent(mSpecularExponent);
3577 mSpecularConstantInt = uint32_t(mSpecularConstant * (1 << 8));
3580 uint32_t
3581 SpecularLightingSoftware::LightPixel(const Point3D &aNormal,
3582 const Point3D &aVectorToLight,
3583 uint32_t aColor)
3585 Point3D vectorToEye(0, 0, 1);
3586 Point3D halfwayVector = Normalized(aVectorToLight + vectorToEye);
3587 Float dotNH = aNormal.DotProduct(halfwayVector);
3588 uint16_t dotNHi = uint16_t(dotNH * (dotNH >= 0) * (1 << PowCache::sInputIntPrecisionBits));
3589 uint32_t specularNHi = uint32_t(mSpecularConstantInt) * mPowCache.Pow(dotNHi) >> 8;
3591 union {
3592 uint32_t bgra;
3593 uint8_t components[4];
3594 } color = { aColor };
3595 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
3596 umin(
3597 (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]) >> PowCache::sOutputIntPrecisionBits, 255U);
3598 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
3599 umin(
3600 (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) >> PowCache::sOutputIntPrecisionBits, 255U);
3601 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
3602 umin(
3603 (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) >> PowCache::sOutputIntPrecisionBits, 255U);
3605 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
3606 umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B],
3607 umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G],
3608 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]));
3609 return color.bgra;
3612 } // namespace gfx
3613 } // namespace mozilla