Update configs. IGNORE BROKEN CHANGESETS CLOSED TREE NO BUG a=release ba=release
[gecko.git] / gfx / 2d / FilterNodeSoftware.cpp
blobd7d9d3866a15a7db46b1272154e3037bffd144b5
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include <cmath>
8 #include "DataSurfaceHelpers.h"
9 #include "FilterNodeSoftware.h"
10 #include "2D.h"
11 #include "Tools.h"
12 #include "Blur.h"
13 #include <map>
14 #include "FilterProcessing.h"
15 #include "Logging.h"
16 #include "mozilla/PodOperations.h"
17 #include "mozilla/DebugOnly.h"
19 // #define DEBUG_DUMP_SURFACES
21 #ifdef DEBUG_DUMP_SURFACES
22 # include "gfxUtils.h" // not part of Moz2D
23 #endif
25 namespace mozilla {
26 namespace gfx {
28 namespace {
30 /**
31 * This class provides a way to get a pow() results in constant-time. It works
32 * by caching 129 ((1 << sCacheIndexPrecisionBits) + 1) values for bases between
33 * 0 and 1 and a fixed exponent.
34 **/
35 class PowCache {
36 public:
37 PowCache() : mNumPowTablePreSquares(-1) {}
39 void CacheForExponent(Float aExponent) {
40 // Since we are in the world where we only care about
41 // input and results in [0,1], there is no point in
42 // dealing with non-positive exponents.
43 if (aExponent <= 0) {
44 mNumPowTablePreSquares = -1;
45 return;
47 int numPreSquares = 0;
48 while (numPreSquares < 5 && aExponent > (1 << (numPreSquares + 2))) {
49 numPreSquares++;
51 mNumPowTablePreSquares = numPreSquares;
52 for (size_t i = 0; i < sCacheSize; i++) {
53 // sCacheSize is chosen in such a way that a takes values
54 // from 0.0 to 1.0 inclusive.
55 Float a = i / Float(1 << sCacheIndexPrecisionBits);
56 MOZ_ASSERT(0.0f <= a && a <= 1.0f,
57 "We only want to cache for bases between 0 and 1.");
59 for (int j = 0; j < mNumPowTablePreSquares; j++) {
60 a = sqrt(a);
62 uint32_t cachedInt = pow(a, aExponent) * (1 << sOutputIntPrecisionBits);
63 MOZ_ASSERT(cachedInt < (1 << (sizeof(mPowTable[i]) * 8)),
64 "mPowCache integer type too small");
66 mPowTable[i] = cachedInt;
70 // Only call Pow() if HasPowerTable() would return true, to avoid complicating
71 // this code and having it just return (1 << sOutputIntPrecisionBits))
72 uint16_t Pow(uint16_t aBase) {
73 MOZ_ASSERT(HasPowerTable());
74 // Results should be similar to what the following code would produce:
75 // Float x = Float(aBase) / (1 << sInputIntPrecisionBits);
76 // return uint16_t(pow(x, aExponent) * (1 << sOutputIntPrecisionBits));
78 MOZ_ASSERT(aBase <= (1 << sInputIntPrecisionBits),
79 "aBase needs to be between 0 and 1!");
81 uint32_t a = aBase;
82 for (int j = 0; j < mNumPowTablePreSquares; j++) {
83 a = a * a >> sInputIntPrecisionBits;
85 uint32_t i = a >> (sInputIntPrecisionBits - sCacheIndexPrecisionBits);
86 MOZ_ASSERT(i < sCacheSize, "out-of-bounds mPowTable access");
87 return mPowTable[i];
90 static const int sInputIntPrecisionBits = 15;
91 static const int sOutputIntPrecisionBits = 15;
92 static const int sCacheIndexPrecisionBits = 7;
94 inline bool HasPowerTable() const { return mNumPowTablePreSquares >= 0; }
96 private:
97 static const size_t sCacheSize = (1 << sCacheIndexPrecisionBits) + 1;
99 int mNumPowTablePreSquares;
100 uint16_t mPowTable[sCacheSize];
103 class PointLightSoftware {
104 public:
105 bool SetAttribute(uint32_t aIndex, Float) { return false; }
106 bool SetAttribute(uint32_t aIndex, const Point3D&);
107 void Prepare() {}
108 Point3D GetVectorToLight(const Point3D& aTargetPoint);
109 uint32_t GetColor(uint32_t aLightColor, const Point3D& aVectorToLight);
111 private:
112 Point3D mPosition;
115 class SpotLightSoftware {
116 public:
117 SpotLightSoftware();
118 bool SetAttribute(uint32_t aIndex, Float);
119 bool SetAttribute(uint32_t aIndex, const Point3D&);
120 void Prepare();
121 Point3D GetVectorToLight(const Point3D& aTargetPoint);
122 uint32_t GetColor(uint32_t aLightColor, const Point3D& aVectorToLight);
124 private:
125 Point3D mPosition;
126 Point3D mPointsAt;
127 Point3D mVectorFromFocusPointToLight;
128 Float mSpecularFocus;
129 Float mLimitingConeAngle;
130 Float mLimitingConeCos;
131 PowCache mPowCache;
134 class DistantLightSoftware {
135 public:
136 DistantLightSoftware();
137 bool SetAttribute(uint32_t aIndex, Float);
138 bool SetAttribute(uint32_t aIndex, const Point3D&) { return false; }
139 void Prepare();
140 Point3D GetVectorToLight(const Point3D& aTargetPoint);
141 uint32_t GetColor(uint32_t aLightColor, const Point3D& aVectorToLight);
143 private:
144 Float mAzimuth;
145 Float mElevation;
146 Point3D mVectorToLight;
149 class DiffuseLightingSoftware {
150 public:
151 DiffuseLightingSoftware();
152 bool SetAttribute(uint32_t aIndex, Float);
153 void Prepare() {}
154 uint32_t LightPixel(const Point3D& aNormal, const Point3D& aVectorToLight,
155 uint32_t aColor);
157 private:
158 Float mDiffuseConstant;
161 class SpecularLightingSoftware {
162 public:
163 SpecularLightingSoftware();
164 bool SetAttribute(uint32_t aIndex, Float);
165 void Prepare();
166 uint32_t LightPixel(const Point3D& aNormal, const Point3D& aVectorToLight,
167 uint32_t aColor);
169 private:
170 Float mSpecularConstant;
171 Float mSpecularExponent;
172 uint32_t mSpecularConstantInt;
173 PowCache mPowCache;
176 } // unnamed namespace
178 // from xpcom/ds/nsMathUtils.h
179 static int32_t NS_lround(double x) {
180 return x >= 0.0 ? int32_t(x + 0.5) : int32_t(x - 0.5);
183 static already_AddRefed<DataSourceSurface> CloneAligned(
184 DataSourceSurface* aSource) {
185 return CreateDataSourceSurfaceByCloning(aSource);
188 static void FillRectWithPixel(DataSourceSurface* aSurface,
189 const IntRect& aFillRect, IntPoint aPixelPos) {
190 MOZ_ASSERT(!aFillRect.Overflows());
191 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
192 "aFillRect needs to be completely inside the surface");
193 MOZ_ASSERT(SurfaceContainsPoint(aSurface, aPixelPos),
194 "aPixelPos needs to be inside the surface");
196 DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
197 if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
198 return;
200 uint8_t* sourcePixelData =
201 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aPixelPos);
202 uint8_t* data =
203 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
204 int bpp = BytesPerPixel(aSurface->GetFormat());
206 // Fill the first row by hand.
207 if (bpp == 4) {
208 uint32_t sourcePixel = *(uint32_t*)sourcePixelData;
209 for (int32_t x = 0; x < aFillRect.Width(); x++) {
210 *((uint32_t*)data + x) = sourcePixel;
212 } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
213 uint8_t sourcePixel = *sourcePixelData;
214 memset(data, sourcePixel, aFillRect.Width());
217 // Copy the first row into the other rows.
218 for (int32_t y = 1; y < aFillRect.Height(); y++) {
219 PodCopy(data + y * surfMap.GetStride(), data, aFillRect.Width() * bpp);
223 static void FillRectWithVerticallyRepeatingHorizontalStrip(
224 DataSourceSurface* aSurface, const IntRect& aFillRect,
225 const IntRect& aSampleRect) {
226 MOZ_ASSERT(!aFillRect.Overflows());
227 MOZ_ASSERT(!aSampleRect.Overflows());
228 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
229 "aFillRect needs to be completely inside the surface");
230 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
231 "aSampleRect needs to be completely inside the surface");
233 DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
234 if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
235 return;
238 uint8_t* sampleData =
239 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft());
240 uint8_t* data =
241 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
242 if (BytesPerPixel(aSurface->GetFormat()) == 4) {
243 for (int32_t y = 0; y < aFillRect.Height(); y++) {
244 PodCopy((uint32_t*)data, (uint32_t*)sampleData, aFillRect.Width());
245 data += surfMap.GetStride();
247 } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
248 for (int32_t y = 0; y < aFillRect.Height(); y++) {
249 PodCopy(data, sampleData, aFillRect.Width());
250 data += surfMap.GetStride();
255 static void FillRectWithHorizontallyRepeatingVerticalStrip(
256 DataSourceSurface* aSurface, const IntRect& aFillRect,
257 const IntRect& aSampleRect) {
258 MOZ_ASSERT(!aFillRect.Overflows());
259 MOZ_ASSERT(!aSampleRect.Overflows());
260 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
261 "aFillRect needs to be completely inside the surface");
262 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
263 "aSampleRect needs to be completely inside the surface");
265 DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
266 if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
267 return;
270 uint8_t* sampleData =
271 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft());
272 uint8_t* data =
273 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
274 if (BytesPerPixel(aSurface->GetFormat()) == 4) {
275 for (int32_t y = 0; y < aFillRect.Height(); y++) {
276 int32_t sampleColor = *((uint32_t*)sampleData);
277 for (int32_t x = 0; x < aFillRect.Width(); x++) {
278 *((uint32_t*)data + x) = sampleColor;
280 data += surfMap.GetStride();
281 sampleData += surfMap.GetStride();
283 } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
284 for (int32_t y = 0; y < aFillRect.Height(); y++) {
285 uint8_t sampleColor = *sampleData;
286 memset(data, sampleColor, aFillRect.Width());
287 data += surfMap.GetStride();
288 sampleData += surfMap.GetStride();
293 static void DuplicateEdges(DataSourceSurface* aSurface,
294 const IntRect& aFromRect) {
295 MOZ_ASSERT(!aFromRect.Overflows());
296 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFromRect),
297 "aFromRect needs to be completely inside the surface");
299 IntSize size = aSurface->GetSize();
300 IntRect fill;
301 IntRect sampleRect;
302 for (int32_t ix = 0; ix < 3; ix++) {
303 switch (ix) {
304 case 0:
305 fill.SetRectX(0, aFromRect.X());
306 sampleRect.SetRectX(fill.XMost(), 1);
307 break;
308 case 1:
309 fill.SetRectX(aFromRect.X(), aFromRect.Width());
310 sampleRect.SetRectX(fill.X(), fill.Width());
311 break;
312 case 2:
313 fill.MoveToX(aFromRect.XMost());
314 fill.SetRightEdge(size.width);
315 sampleRect.SetRectX(fill.X() - 1, 1);
316 break;
318 if (fill.Width() <= 0) {
319 continue;
321 bool xIsMiddle = (ix == 1);
322 for (int32_t iy = 0; iy < 3; iy++) {
323 switch (iy) {
324 case 0:
325 fill.SetRectY(0, aFromRect.Y());
326 sampleRect.SetRectY(fill.YMost(), 1);
327 break;
328 case 1:
329 fill.SetRectY(aFromRect.Y(), aFromRect.Height());
330 sampleRect.SetRectY(fill.Y(), fill.Height());
331 break;
332 case 2:
333 fill.MoveToY(aFromRect.YMost());
334 fill.SetBottomEdge(size.height);
335 sampleRect.SetRectY(fill.Y() - 1, 1);
336 break;
338 if (fill.Height() <= 0) {
339 continue;
341 bool yIsMiddle = (iy == 1);
342 if (!xIsMiddle && !yIsMiddle) {
343 // Corner
344 FillRectWithPixel(aSurface, fill, sampleRect.TopLeft());
346 if (xIsMiddle && !yIsMiddle) {
347 // Top middle or bottom middle
348 FillRectWithVerticallyRepeatingHorizontalStrip(aSurface, fill,
349 sampleRect);
351 if (!xIsMiddle && yIsMiddle) {
352 // Left middle or right middle
353 FillRectWithHorizontallyRepeatingVerticalStrip(aSurface, fill,
354 sampleRect);
360 static IntPoint TileIndex(const IntRect& aFirstTileRect,
361 const IntPoint& aPoint) {
362 return IntPoint(int32_t(floor(double(aPoint.x - aFirstTileRect.X()) /
363 aFirstTileRect.Width())),
364 int32_t(floor(double(aPoint.y - aFirstTileRect.Y()) /
365 aFirstTileRect.Height())));
368 static void TileSurface(DataSourceSurface* aSource, DataSourceSurface* aTarget,
369 const IntPoint& aOffset) {
370 IntRect sourceRect(aOffset, aSource->GetSize());
371 IntRect targetRect(IntPoint(0, 0), aTarget->GetSize());
372 IntPoint startIndex = TileIndex(sourceRect, targetRect.TopLeft());
373 IntPoint endIndex = TileIndex(sourceRect, targetRect.BottomRight());
375 for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
376 for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
377 IntPoint destPoint(sourceRect.X() + ix * sourceRect.Width(),
378 sourceRect.Y() + iy * sourceRect.Height());
379 IntRect destRect(destPoint, sourceRect.Size());
380 destRect = destRect.Intersect(targetRect);
381 IntRect srcRect = destRect - destPoint;
382 CopyRect(aSource, aTarget, srcRect, destRect.TopLeft());
387 static already_AddRefed<DataSourceSurface> GetDataSurfaceInRect(
388 SourceSurface* aSurface, const IntRect& aSurfaceRect,
389 const IntRect& aDestRect, ConvolveMatrixEdgeMode aEdgeMode) {
390 MOZ_ASSERT(aSurface ? aSurfaceRect.Size() == aSurface->GetSize()
391 : aSurfaceRect.IsEmpty());
393 if (aSurfaceRect.Overflows() || aDestRect.Overflows()) {
394 // We can't rely on the intersection calculations below to make sense when
395 // XMost() or YMost() overflow. Bail out.
396 return nullptr;
399 IntRect sourceRect = aSurfaceRect;
401 if (sourceRect.IsEqualEdges(aDestRect)) {
402 return aSurface ? aSurface->GetDataSurface() : nullptr;
405 IntRect intersect = sourceRect.Intersect(aDestRect);
407 // create rects that are in surface local space.
408 IntRect intersectInSourceSpace = intersect - sourceRect.TopLeft();
409 IntRect intersectInDestSpace = intersect - aDestRect.TopLeft();
410 SurfaceFormat format =
411 aSurface ? aSurface->GetFormat() : SurfaceFormat(SurfaceFormat::B8G8R8A8);
413 RefPtr<DataSourceSurface> target =
414 Factory::CreateDataSourceSurface(aDestRect.Size(), format, true);
415 if (MOZ2D_WARN_IF(!target)) {
416 return nullptr;
419 if (!aSurface) {
420 return target.forget();
423 RefPtr<DataSourceSurface> dataSource = aSurface->GetDataSurface();
424 MOZ_ASSERT(dataSource);
426 if (aEdgeMode == EDGE_MODE_WRAP) {
427 TileSurface(dataSource, target, intersectInDestSpace.TopLeft());
428 return target.forget();
431 CopyRect(dataSource, target, intersectInSourceSpace,
432 intersectInDestSpace.TopLeft());
434 if (aEdgeMode == EDGE_MODE_DUPLICATE) {
435 DuplicateEdges(target, intersectInDestSpace);
438 return target.forget();
441 /* static */
442 already_AddRefed<FilterNode> FilterNodeSoftware::Create(FilterType aType) {
443 RefPtr<FilterNodeSoftware> filter;
444 switch (aType) {
445 case FilterType::BLEND:
446 filter = new FilterNodeBlendSoftware();
447 break;
448 case FilterType::TRANSFORM:
449 filter = new FilterNodeTransformSoftware();
450 break;
451 case FilterType::MORPHOLOGY:
452 filter = new FilterNodeMorphologySoftware();
453 break;
454 case FilterType::COLOR_MATRIX:
455 filter = new FilterNodeColorMatrixSoftware();
456 break;
457 case FilterType::FLOOD:
458 filter = new FilterNodeFloodSoftware();
459 break;
460 case FilterType::TILE:
461 filter = new FilterNodeTileSoftware();
462 break;
463 case FilterType::TABLE_TRANSFER:
464 filter = new FilterNodeTableTransferSoftware();
465 break;
466 case FilterType::DISCRETE_TRANSFER:
467 filter = new FilterNodeDiscreteTransferSoftware();
468 break;
469 case FilterType::LINEAR_TRANSFER:
470 filter = new FilterNodeLinearTransferSoftware();
471 break;
472 case FilterType::GAMMA_TRANSFER:
473 filter = new FilterNodeGammaTransferSoftware();
474 break;
475 case FilterType::CONVOLVE_MATRIX:
476 filter = new FilterNodeConvolveMatrixSoftware();
477 break;
478 case FilterType::DISPLACEMENT_MAP:
479 filter = new FilterNodeDisplacementMapSoftware();
480 break;
481 case FilterType::TURBULENCE:
482 filter = new FilterNodeTurbulenceSoftware();
483 break;
484 case FilterType::ARITHMETIC_COMBINE:
485 filter = new FilterNodeArithmeticCombineSoftware();
486 break;
487 case FilterType::COMPOSITE:
488 filter = new FilterNodeCompositeSoftware();
489 break;
490 case FilterType::GAUSSIAN_BLUR:
491 filter = new FilterNodeGaussianBlurSoftware();
492 break;
493 case FilterType::DIRECTIONAL_BLUR:
494 filter = new FilterNodeDirectionalBlurSoftware();
495 break;
496 case FilterType::CROP:
497 filter = new FilterNodeCropSoftware();
498 break;
499 case FilterType::PREMULTIPLY:
500 filter = new FilterNodePremultiplySoftware();
501 break;
502 case FilterType::UNPREMULTIPLY:
503 filter = new FilterNodeUnpremultiplySoftware();
504 break;
505 case FilterType::OPACITY:
506 filter = new FilterNodeOpacitySoftware();
507 break;
508 case FilterType::POINT_DIFFUSE:
509 filter = new FilterNodeLightingSoftware<PointLightSoftware,
510 DiffuseLightingSoftware>(
511 "FilterNodeLightingSoftware<PointLight, DiffuseLighting>");
512 break;
513 case FilterType::POINT_SPECULAR:
514 filter = new FilterNodeLightingSoftware<PointLightSoftware,
515 SpecularLightingSoftware>(
516 "FilterNodeLightingSoftware<PointLight, SpecularLighting>");
517 break;
518 case FilterType::SPOT_DIFFUSE:
519 filter = new FilterNodeLightingSoftware<SpotLightSoftware,
520 DiffuseLightingSoftware>(
521 "FilterNodeLightingSoftware<SpotLight, DiffuseLighting>");
522 break;
523 case FilterType::SPOT_SPECULAR:
524 filter = new FilterNodeLightingSoftware<SpotLightSoftware,
525 SpecularLightingSoftware>(
526 "FilterNodeLightingSoftware<SpotLight, SpecularLighting>");
527 break;
528 case FilterType::DISTANT_DIFFUSE:
529 filter = new FilterNodeLightingSoftware<DistantLightSoftware,
530 DiffuseLightingSoftware>(
531 "FilterNodeLightingSoftware<DistantLight, DiffuseLighting>");
532 break;
533 case FilterType::DISTANT_SPECULAR:
534 filter = new FilterNodeLightingSoftware<DistantLightSoftware,
535 SpecularLightingSoftware>(
536 "FilterNodeLightingSoftware<DistantLight, SpecularLighting>");
537 break;
539 return filter.forget();
542 void FilterNodeSoftware::Draw(DrawTarget* aDrawTarget, const Rect& aSourceRect,
543 const Point& aDestPoint,
544 const DrawOptions& aOptions) {
545 #ifdef DEBUG_DUMP_SURFACES
546 printf("<style>section{margin:10px;}</style><pre>\nRendering filter %s...\n",
547 GetName());
548 #endif
550 Rect renderRect = aSourceRect;
551 renderRect.RoundOut();
552 IntRect renderIntRect;
553 if (!renderRect.ToIntRect(&renderIntRect)) {
554 #ifdef DEBUG_DUMP_SURFACES
555 printf("render rect overflowed, not painting anything\n");
556 printf("</pre>\n");
557 #endif
558 return;
561 IntRect outputRect = GetOutputRectInRect(renderIntRect);
562 if (outputRect.Overflows()) {
563 #ifdef DEBUG_DUMP_SURFACES
564 printf("output rect overflowed, not painting anything\n");
565 printf("</pre>\n");
566 #endif
567 return;
570 RefPtr<DataSourceSurface> result;
571 if (!outputRect.IsEmpty()) {
572 result = GetOutput(outputRect);
575 if (!result) {
576 // Null results are allowed and treated as transparent. Don't draw anything.
577 #ifdef DEBUG_DUMP_SURFACES
578 printf("output returned null\n");
579 printf("</pre>\n");
580 #endif
581 return;
584 #ifdef DEBUG_DUMP_SURFACES
585 printf("output from %s:\n", GetName());
586 printf("<img src='");
587 gfxUtils::DumpAsDataURL(result);
588 printf("'>\n");
589 printf("</pre>\n");
590 #endif
592 Point sourceToDestOffset = aDestPoint - aSourceRect.TopLeft();
593 Rect renderedSourceRect = Rect(outputRect).Intersect(aSourceRect);
594 Rect renderedDestRect = renderedSourceRect + sourceToDestOffset;
595 if (result->GetFormat() == SurfaceFormat::A8) {
596 // Interpret the result as having implicitly black color channels.
597 aDrawTarget->PushClipRect(renderedDestRect);
598 aDrawTarget->MaskSurface(
599 ColorPattern(DeviceColor::MaskOpaqueBlack()), result,
600 Point(outputRect.TopLeft()) + sourceToDestOffset, aOptions);
601 aDrawTarget->PopClip();
602 } else {
603 aDrawTarget->DrawSurface(result, renderedDestRect,
604 renderedSourceRect - Point(outputRect.TopLeft()),
605 DrawSurfaceOptions(), aOptions);
609 already_AddRefed<DataSourceSurface> FilterNodeSoftware::GetOutput(
610 const IntRect& aRect) {
611 MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect));
613 if (aRect.Overflows()) {
614 return nullptr;
617 IntRect cachedRect;
618 IntRect requestedRect;
619 RefPtr<DataSourceSurface> cachedOutput;
621 // Retrieve a cached surface if we have one and it can
622 // satisfy this request, or else request a rect we will compute and cache
623 if (!mCachedRect.Contains(aRect)) {
624 RequestRect(aRect);
625 requestedRect = mRequestedRect;
626 } else {
627 MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?");
628 cachedRect = mCachedRect;
629 cachedOutput = mCachedOutput;
632 if (!cachedOutput) {
633 // Compute the output
634 cachedOutput = Render(requestedRect);
636 // Update the cache for future requests
637 mCachedOutput = cachedOutput;
638 if (!mCachedOutput) {
639 mCachedRect = IntRect();
640 mRequestedRect = IntRect();
641 return nullptr;
643 mCachedRect = requestedRect;
644 mRequestedRect = IntRect();
646 cachedRect = mCachedRect;
649 return GetDataSurfaceInRect(cachedOutput, cachedRect, aRect, EDGE_MODE_NONE);
652 void FilterNodeSoftware::RequestRect(const IntRect& aRect) {
653 if (mRequestedRect.Contains(aRect)) {
654 // Bail out now. Otherwise pathological filters can spend time exponential
655 // in the number of primitives, e.g. if each primitive takes the
656 // previous primitive as its two inputs.
657 return;
659 mRequestedRect = mRequestedRect.Union(aRect);
660 RequestFromInputsForRect(aRect);
663 IntRect FilterNodeSoftware::MapInputRectToSource(uint32_t aInputEnumIndex,
664 const IntRect& aRect,
665 const IntRect& aMax,
666 FilterNode* aSourceNode) {
667 int32_t inputIndex = InputIndex(aInputEnumIndex);
668 if (inputIndex < 0) {
669 gfxDevCrash(LogReason::FilterInputError)
670 << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
671 return aMax;
673 if ((uint32_t)inputIndex < NumberOfSetInputs()) {
674 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
675 // If we have any input filters call into them to do the mapping,
676 // otherwise we can assume an input surface will be used
677 // and just return aRect.
678 if (filter) {
679 return filter->MapRectToSource(aRect, aMax, aSourceNode);
682 // We have an input surface instead of a filter
683 // so check if we're the target node.
684 if (this == aSourceNode) {
685 return aRect;
687 return IntRect();
690 void FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex,
691 const IntRect& aRect) {
692 if (aRect.Overflows()) {
693 return;
696 int32_t inputIndex = InputIndex(aInputEnumIndex);
697 if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
698 gfxDevCrash(LogReason::FilterInputError)
699 << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
700 return;
702 if (mInputSurfaces[inputIndex]) {
703 return;
705 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
706 MOZ_ASSERT(filter, "missing input");
708 filter->RequestRect(filter->GetOutputRectInRect(aRect));
711 SurfaceFormat FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat,
712 FormatHint aFormatHint) {
713 if (aCurrentFormat == SurfaceFormat::A8 && aFormatHint == CAN_HANDLE_A8) {
714 return SurfaceFormat::A8;
716 return SurfaceFormat::B8G8R8A8;
719 already_AddRefed<DataSourceSurface>
720 FilterNodeSoftware::GetInputDataSourceSurface(
721 uint32_t aInputEnumIndex, const IntRect& aRect, FormatHint aFormatHint,
722 ConvolveMatrixEdgeMode aEdgeMode,
723 const IntRect* aTransparencyPaddedSourceRect) {
724 if (aRect.Overflows()) {
725 return nullptr;
728 #ifdef DEBUG_DUMP_SURFACES
729 printf(
730 "<section><h1>GetInputDataSourceSurface with aRect: %d, %d, %d, "
731 "%d</h1>\n",
732 aRect.x, aRect.y, aRect.Width(), aRect.Height());
733 #endif
734 int32_t inputIndex = InputIndex(aInputEnumIndex);
735 if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
736 gfxDevCrash(LogReason::FilterInputData)
737 << "Invalid data " << inputIndex << " vs. " << NumberOfSetInputs();
738 return nullptr;
741 if (aRect.IsEmpty()) {
742 return nullptr;
745 RefPtr<SourceSurface> surface;
746 IntRect surfaceRect;
748 if (mInputSurfaces[inputIndex]) {
749 // Input from input surface
750 surface = mInputSurfaces[inputIndex];
751 #ifdef DEBUG_DUMP_SURFACES
752 printf("input from input surface:\n");
753 #endif
754 surfaceRect = surface->GetRect();
755 } else {
756 // Input from input filter
757 #ifdef DEBUG_DUMP_SURFACES
758 printf("getting input from input filter %s...\n",
759 mInputFilters[inputIndex]->GetName());
760 #endif
761 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
762 MOZ_ASSERT(filter, "missing input");
763 IntRect inputFilterOutput = filter->GetOutputRectInRect(aRect);
764 if (!inputFilterOutput.IsEmpty()) {
765 surface = filter->GetOutput(inputFilterOutput);
767 #ifdef DEBUG_DUMP_SURFACES
768 printf("input from input filter %s:\n",
769 mInputFilters[inputIndex]->GetName());
770 #endif
771 surfaceRect = inputFilterOutput;
772 MOZ_ASSERT(!surface || surfaceRect.Size() == surface->GetSize());
775 if (surface && surface->GetFormat() == SurfaceFormat::UNKNOWN) {
776 #ifdef DEBUG_DUMP_SURFACES
777 printf("wrong input format</section>\n\n");
778 #endif
779 return nullptr;
782 if (!surfaceRect.IsEmpty() && !surface) {
783 #ifdef DEBUG_DUMP_SURFACES
784 printf(" -- no input --</section>\n\n");
785 #endif
786 return nullptr;
789 if (aTransparencyPaddedSourceRect &&
790 !aTransparencyPaddedSourceRect->IsEmpty()) {
791 IntRect srcRect = aTransparencyPaddedSourceRect->Intersect(aRect);
792 surface =
793 GetDataSurfaceInRect(surface, surfaceRect, srcRect, EDGE_MODE_NONE);
794 if (surface) {
795 surfaceRect = srcRect;
796 } else {
797 // Padding the surface with transparency failed, probably due to size
798 // restrictions. Since |surface| is now null, set the surfaceRect to
799 // empty so that we're consistent.
800 surfaceRect.SetEmpty();
804 RefPtr<DataSourceSurface> result =
805 GetDataSurfaceInRect(surface, surfaceRect, aRect, aEdgeMode);
807 if (result) {
808 // TODO: This isn't safe since we don't have a guarantee
809 // that future Maps will have the same stride
810 DataSourceSurface::MappedSurface map;
811 if (result->Map(DataSourceSurface::READ, &map)) {
812 // Unmap immediately since CloneAligned hasn't been updated
813 // to use the Map API yet. We can still read the stride/data
814 // values as long as we don't try to dereference them.
815 result->Unmap();
816 if (map.mStride != GetAlignedStride<16>(map.mStride, 1) ||
817 reinterpret_cast<uintptr_t>(map.mData) % 16 != 0) {
818 // Align unaligned surface.
819 result = CloneAligned(result);
821 } else {
822 result = nullptr;
826 if (!result) {
827 #ifdef DEBUG_DUMP_SURFACES
828 printf(" -- no input --</section>\n\n");
829 #endif
830 return nullptr;
833 SurfaceFormat currentFormat = result->GetFormat();
834 if (DesiredFormat(currentFormat, aFormatHint) == SurfaceFormat::B8G8R8A8 &&
835 currentFormat != SurfaceFormat::B8G8R8A8) {
836 result = FilterProcessing::ConvertToB8G8R8A8(result);
839 #ifdef DEBUG_DUMP_SURFACES
840 printf("<img src='");
841 gfxUtils::DumpAsDataURL(result);
842 printf("'></section>");
843 #endif
845 MOZ_ASSERT(!result || result->GetSize() == aRect.Size(),
846 "wrong surface size");
848 return result.forget();
851 IntRect FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex,
852 const IntRect& aInRect) {
853 if (aInRect.Overflows()) {
854 return IntRect();
857 int32_t inputIndex = InputIndex(aInputEnumIndex);
858 if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
859 gfxDevCrash(LogReason::FilterInputRect)
860 << "Invalid rect " << inputIndex << " vs. " << NumberOfSetInputs();
861 return IntRect();
863 if (mInputSurfaces[inputIndex]) {
864 return aInRect.Intersect(mInputSurfaces[inputIndex]->GetRect());
866 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
867 MOZ_ASSERT(filter, "missing input");
868 if (!filter) {
869 return IntRect();
871 return filter->GetOutputRectInRect(aInRect);
874 size_t FilterNodeSoftware::NumberOfSetInputs() {
875 return std::max(mInputSurfaces.size(), mInputFilters.size());
878 void FilterNodeSoftware::AddInvalidationListener(
879 FilterInvalidationListener* aListener) {
880 MOZ_ASSERT(aListener, "null listener");
881 mInvalidationListeners.push_back(aListener);
884 void FilterNodeSoftware::RemoveInvalidationListener(
885 FilterInvalidationListener* aListener) {
886 MOZ_ASSERT(aListener, "null listener");
887 std::vector<FilterInvalidationListener*>::iterator it = std::find(
888 mInvalidationListeners.begin(), mInvalidationListeners.end(), aListener);
889 mInvalidationListeners.erase(it);
892 void FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware* aFilter) {
893 Invalidate();
896 void FilterNodeSoftware::Invalidate() {
897 mCachedOutput = nullptr;
898 mCachedRect = IntRect();
899 for (std::vector<FilterInvalidationListener*>::iterator it =
900 mInvalidationListeners.begin();
901 it != mInvalidationListeners.end(); it++) {
902 (*it)->FilterInvalidated(this);
906 FilterNodeSoftware::FilterNodeSoftware() {}
908 FilterNodeSoftware::~FilterNodeSoftware() {
909 MOZ_ASSERT(
910 mInvalidationListeners.empty(),
911 "All invalidation listeners should have unsubscribed themselves by now!");
913 for (std::vector<RefPtr<FilterNodeSoftware> >::iterator it =
914 mInputFilters.begin();
915 it != mInputFilters.end(); it++) {
916 if (*it) {
917 (*it)->RemoveInvalidationListener(this);
922 void FilterNodeSoftware::SetInput(uint32_t aIndex, FilterNode* aFilter) {
923 if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
924 MOZ_ASSERT(false, "can only take software filters as inputs");
925 return;
927 SetInput(aIndex, nullptr, static_cast<FilterNodeSoftware*>(aFilter));
930 void FilterNodeSoftware::SetInput(uint32_t aIndex, SourceSurface* aSurface) {
931 SetInput(aIndex, aSurface, nullptr);
934 void FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex,
935 SourceSurface* aSurface,
936 FilterNodeSoftware* aFilter) {
937 int32_t inputIndex = InputIndex(aInputEnumIndex);
938 if (inputIndex < 0) {
939 gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex;
940 return;
942 if ((uint32_t)inputIndex >= NumberOfSetInputs()) {
943 mInputSurfaces.resize(inputIndex + 1);
944 mInputFilters.resize(inputIndex + 1);
946 mInputSurfaces[inputIndex] = aSurface;
947 if (mInputFilters[inputIndex]) {
948 mInputFilters[inputIndex]->RemoveInvalidationListener(this);
950 if (aFilter) {
951 aFilter->AddInvalidationListener(this);
953 mInputFilters[inputIndex] = aFilter;
954 if (!aSurface && !aFilter && (size_t)inputIndex == NumberOfSetInputs()) {
955 mInputSurfaces.resize(inputIndex);
956 mInputFilters.resize(inputIndex);
958 Invalidate();
961 FilterNodeBlendSoftware::FilterNodeBlendSoftware()
962 : mBlendMode(BLEND_MODE_MULTIPLY) {}
964 int32_t FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex) {
965 switch (aInputEnumIndex) {
966 case IN_BLEND_IN:
967 return 0;
968 case IN_BLEND_IN2:
969 return 1;
970 default:
971 return -1;
975 void FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex,
976 uint32_t aBlendMode) {
977 MOZ_ASSERT(aIndex == ATT_BLEND_BLENDMODE);
978 mBlendMode = static_cast<BlendMode>(aBlendMode);
979 Invalidate();
982 static CompositionOp ToBlendOp(BlendMode aOp) {
983 switch (aOp) {
984 case BLEND_MODE_MULTIPLY:
985 return CompositionOp::OP_MULTIPLY;
986 case BLEND_MODE_SCREEN:
987 return CompositionOp::OP_SCREEN;
988 case BLEND_MODE_OVERLAY:
989 return CompositionOp::OP_OVERLAY;
990 case BLEND_MODE_DARKEN:
991 return CompositionOp::OP_DARKEN;
992 case BLEND_MODE_LIGHTEN:
993 return CompositionOp::OP_LIGHTEN;
994 case BLEND_MODE_COLOR_DODGE:
995 return CompositionOp::OP_COLOR_DODGE;
996 case BLEND_MODE_COLOR_BURN:
997 return CompositionOp::OP_COLOR_BURN;
998 case BLEND_MODE_HARD_LIGHT:
999 return CompositionOp::OP_HARD_LIGHT;
1000 case BLEND_MODE_SOFT_LIGHT:
1001 return CompositionOp::OP_SOFT_LIGHT;
1002 case BLEND_MODE_DIFFERENCE:
1003 return CompositionOp::OP_DIFFERENCE;
1004 case BLEND_MODE_EXCLUSION:
1005 return CompositionOp::OP_EXCLUSION;
1006 case BLEND_MODE_HUE:
1007 return CompositionOp::OP_HUE;
1008 case BLEND_MODE_SATURATION:
1009 return CompositionOp::OP_SATURATION;
1010 case BLEND_MODE_COLOR:
1011 return CompositionOp::OP_COLOR;
1012 case BLEND_MODE_LUMINOSITY:
1013 return CompositionOp::OP_LUMINOSITY;
1016 MOZ_ASSERT_UNREACHABLE("Unexpected BlendMode");
1017 return CompositionOp::OP_OVER;
1020 already_AddRefed<DataSourceSurface> FilterNodeBlendSoftware::Render(
1021 const IntRect& aRect) {
1022 RefPtr<DataSourceSurface> input1 =
1023 GetInputDataSourceSurface(IN_BLEND_IN, aRect, NEED_COLOR_CHANNELS);
1024 RefPtr<DataSourceSurface> input2 =
1025 GetInputDataSourceSurface(IN_BLEND_IN2, aRect, NEED_COLOR_CHANNELS);
1027 // Null inputs need to be treated as transparent.
1029 // First case: both are transparent.
1030 if (!input1 && !input2) {
1031 // Then the result is transparent, too.
1032 return nullptr;
1035 // Second case: one of them is transparent. Return the non-transparent one.
1036 if (!input1 || !input2) {
1037 return input1 ? input1.forget() : input2.forget();
1040 // Third case: both are non-transparent.
1041 // Apply normal filtering.
1042 RefPtr<DataSourceSurface> target =
1043 FilterProcessing::ApplyBlending(input1, input2, mBlendMode);
1044 if (target != nullptr) {
1045 return target.forget();
1048 IntSize size = input1->GetSize();
1049 target = Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1050 if (MOZ2D_WARN_IF(!target)) {
1051 return nullptr;
1054 CopyRect(input1, target, IntRect(IntPoint(), size), IntPoint());
1056 // This needs to stay in scope until the draw target has been flushed.
1057 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::READ_WRITE);
1058 if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
1059 return nullptr;
1062 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
1063 BackendType::SKIA, targetMap.GetData(), target->GetSize(),
1064 targetMap.GetStride(), target->GetFormat());
1066 if (!dt) {
1067 gfxWarning()
1068 << "FilterNodeBlendSoftware::Render failed in CreateDrawTargetForData";
1069 return nullptr;
1072 Rect r(0, 0, size.width, size.height);
1073 dt->DrawSurface(input2, r, r, DrawSurfaceOptions(),
1074 DrawOptions(1.0f, ToBlendOp(mBlendMode)));
1075 dt->Flush();
1076 return target.forget();
1079 void FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect& aRect) {
1080 RequestInputRect(IN_BLEND_IN, aRect);
1081 RequestInputRect(IN_BLEND_IN2, aRect);
1084 IntRect FilterNodeBlendSoftware::MapRectToSource(const IntRect& aRect,
1085 const IntRect& aMax,
1086 FilterNode* aSourceNode) {
1087 IntRect result = MapInputRectToSource(IN_BLEND_IN, aRect, aMax, aSourceNode);
1088 result.OrWith(MapInputRectToSource(IN_BLEND_IN2, aRect, aMax, aSourceNode));
1089 return result;
1092 IntRect FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect& aRect) {
1093 return GetInputRectInRect(IN_BLEND_IN, aRect)
1094 .Union(GetInputRectInRect(IN_BLEND_IN2, aRect))
1095 .Intersect(aRect);
1098 FilterNodeTransformSoftware::FilterNodeTransformSoftware()
1099 : mSamplingFilter(SamplingFilter::GOOD) {}
1101 int32_t FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex) {
1102 switch (aInputEnumIndex) {
1103 case IN_TRANSFORM_IN:
1104 return 0;
1105 default:
1106 return -1;
1110 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex,
1111 uint32_t aFilter) {
1112 MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER);
1113 mSamplingFilter = static_cast<SamplingFilter>(aFilter);
1114 Invalidate();
1117 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex,
1118 const Matrix& aMatrix) {
1119 MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX);
1120 mMatrix = aMatrix;
1121 Invalidate();
1124 IntRect FilterNodeTransformSoftware::SourceRectForOutputRect(
1125 const IntRect& aRect) {
1126 if (aRect.IsEmpty()) {
1127 return IntRect();
1130 Matrix inverted(mMatrix);
1131 if (!inverted.Invert()) {
1132 return IntRect();
1135 Rect neededRect = inverted.TransformBounds(Rect(aRect));
1136 neededRect.RoundOut();
1137 IntRect neededIntRect;
1138 if (!neededRect.ToIntRect(&neededIntRect)) {
1139 return IntRect();
1141 return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect);
1144 IntRect FilterNodeTransformSoftware::MapRectToSource(const IntRect& aRect,
1145 const IntRect& aMax,
1146 FilterNode* aSourceNode) {
1147 if (aRect.IsEmpty()) {
1148 return IntRect();
1151 Matrix inverted(mMatrix);
1152 if (!inverted.Invert()) {
1153 return aMax;
1156 Rect neededRect = inverted.TransformBounds(Rect(aRect));
1157 neededRect.RoundOut();
1158 IntRect neededIntRect;
1159 if (!neededRect.ToIntRect(&neededIntRect)) {
1160 return aMax;
1162 return MapInputRectToSource(IN_TRANSFORM_IN, neededIntRect, aMax,
1163 aSourceNode);
1166 already_AddRefed<DataSourceSurface> FilterNodeTransformSoftware::Render(
1167 const IntRect& aRect) {
1168 IntRect srcRect = SourceRectForOutputRect(aRect);
1170 RefPtr<DataSourceSurface> input =
1171 GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect);
1173 if (!input) {
1174 return nullptr;
1177 Matrix transform = Matrix::Translation(srcRect.X(), srcRect.Y()) * mMatrix *
1178 Matrix::Translation(-aRect.X(), -aRect.Y());
1179 if (transform.IsIdentity() && srcRect.Size() == aRect.Size()) {
1180 return input.forget();
1183 RefPtr<DataSourceSurface> surf =
1184 Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat(), true);
1186 if (!surf) {
1187 return nullptr;
1190 DataSourceSurface::MappedSurface mapping;
1191 if (!surf->Map(DataSourceSurface::MapType::WRITE, &mapping)) {
1192 gfxCriticalError()
1193 << "FilterNodeTransformSoftware::Render failed to map surface";
1194 return nullptr;
1197 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
1198 BackendType::SKIA, mapping.mData, surf->GetSize(), mapping.mStride,
1199 surf->GetFormat());
1200 if (!dt) {
1201 gfxWarning() << "FilterNodeTransformSoftware::Render failed in "
1202 "CreateDrawTargetForData";
1203 return nullptr;
1206 Rect r(0, 0, srcRect.Width(), srcRect.Height());
1207 dt->SetTransform(transform);
1208 dt->DrawSurface(input, r, r, DrawSurfaceOptions(mSamplingFilter));
1210 dt->Flush();
1211 surf->Unmap();
1212 return surf.forget();
1215 void FilterNodeTransformSoftware::RequestFromInputsForRect(
1216 const IntRect& aRect) {
1217 RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect));
1220 IntRect FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect) {
1221 IntRect srcRect = SourceRectForOutputRect(aRect);
1222 if (srcRect.IsEmpty()) {
1223 return IntRect();
1226 Rect outRect = mMatrix.TransformBounds(Rect(srcRect));
1227 outRect.RoundOut();
1228 IntRect outIntRect;
1229 if (!outRect.ToIntRect(&outIntRect)) {
1230 return IntRect();
1232 return outIntRect.Intersect(aRect);
1235 FilterNodeMorphologySoftware::FilterNodeMorphologySoftware()
1236 : mOperator(MORPHOLOGY_OPERATOR_ERODE) {}
1238 int32_t FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex) {
1239 switch (aInputEnumIndex) {
1240 case IN_MORPHOLOGY_IN:
1241 return 0;
1242 default:
1243 return -1;
1247 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
1248 const IntSize& aRadii) {
1249 MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_RADII);
1250 mRadii.width = std::clamp(aRadii.width, 0, 100000);
1251 mRadii.height = std::clamp(aRadii.height, 0, 100000);
1252 Invalidate();
1255 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
1256 uint32_t aOperator) {
1257 MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_OPERATOR);
1258 mOperator = static_cast<MorphologyOperator>(aOperator);
1259 Invalidate();
1262 static already_AddRefed<DataSourceSurface> ApplyMorphology(
1263 const IntRect& aSourceRect, DataSourceSurface* aInput,
1264 const IntRect& aDestRect, int32_t rx, int32_t ry,
1265 MorphologyOperator aOperator) {
1266 IntRect srcRect = aSourceRect - aDestRect.TopLeft();
1267 IntRect destRect = aDestRect - aDestRect.TopLeft();
1268 IntRect tmpRect(destRect.X(), srcRect.Y(), destRect.Width(),
1269 srcRect.Height());
1270 #ifdef DEBUG
1271 IntMargin margin = srcRect - destRect;
1272 MOZ_ASSERT(margin.top >= ry && margin.right >= rx && margin.bottom >= ry &&
1273 margin.left >= rx,
1274 "insufficient margin");
1275 #endif
1277 RefPtr<DataSourceSurface> tmp;
1278 if (rx == 0) {
1279 tmp = aInput;
1280 } else {
1281 tmp = Factory::CreateDataSourceSurface(tmpRect.Size(),
1282 SurfaceFormat::B8G8R8A8);
1283 if (MOZ2D_WARN_IF(!tmp)) {
1284 return nullptr;
1287 DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
1288 DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::WRITE);
1289 if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !tmpMap.IsMapped())) {
1290 return nullptr;
1292 uint8_t* sourceData = DataAtOffset(aInput, sourceMap.GetMappedSurface(),
1293 destRect.TopLeft() - srcRect.TopLeft());
1294 uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(),
1295 destRect.TopLeft() - tmpRect.TopLeft());
1297 FilterProcessing::ApplyMorphologyHorizontal(
1298 sourceData, sourceMap.GetStride(), tmpData, tmpMap.GetStride(), tmpRect,
1299 rx, aOperator);
1302 RefPtr<DataSourceSurface> dest;
1303 if (ry == 0) {
1304 dest = tmp;
1305 } else {
1306 dest = Factory::CreateDataSourceSurface(destRect.Size(),
1307 SurfaceFormat::B8G8R8A8);
1308 if (MOZ2D_WARN_IF(!dest)) {
1309 return nullptr;
1312 DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::READ);
1313 DataSourceSurface::ScopedMap destMap(dest, DataSourceSurface::WRITE);
1314 if (MOZ2D_WARN_IF(!tmpMap.IsMapped() || !destMap.IsMapped())) {
1315 return nullptr;
1317 int32_t tmpStride = tmpMap.GetStride();
1318 uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(),
1319 destRect.TopLeft() - tmpRect.TopLeft());
1321 int32_t destStride = destMap.GetStride();
1322 uint8_t* destData = destMap.GetData();
1324 FilterProcessing::ApplyMorphologyVertical(
1325 tmpData, tmpStride, destData, destStride, destRect, ry, aOperator);
1328 return dest.forget();
1331 already_AddRefed<DataSourceSurface> FilterNodeMorphologySoftware::Render(
1332 const IntRect& aRect) {
1333 IntRect srcRect = aRect;
1334 srcRect.Inflate(mRadii);
1336 RefPtr<DataSourceSurface> input =
1337 GetInputDataSourceSurface(IN_MORPHOLOGY_IN, srcRect, NEED_COLOR_CHANNELS);
1338 if (!input) {
1339 return nullptr;
1342 int32_t rx = mRadii.width;
1343 int32_t ry = mRadii.height;
1345 if (rx == 0 && ry == 0) {
1346 return input.forget();
1349 return ApplyMorphology(srcRect, input, aRect, rx, ry, mOperator);
1352 void FilterNodeMorphologySoftware::RequestFromInputsForRect(
1353 const IntRect& aRect) {
1354 IntRect srcRect = aRect;
1355 srcRect.Inflate(mRadii);
1356 RequestInputRect(IN_MORPHOLOGY_IN, srcRect);
1359 IntRect FilterNodeMorphologySoftware::GetOutputRectInRect(
1360 const IntRect& aRect) {
1361 IntRect inflatedSourceRect = aRect;
1362 inflatedSourceRect.Inflate(mRadii);
1363 IntRect inputRect = GetInputRectInRect(IN_MORPHOLOGY_IN, inflatedSourceRect);
1364 if (mOperator == MORPHOLOGY_OPERATOR_ERODE) {
1365 inputRect.Deflate(mRadii);
1366 } else {
1367 inputRect.Inflate(mRadii);
1369 return inputRect.Intersect(aRect);
1372 int32_t FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) {
1373 switch (aInputEnumIndex) {
1374 case IN_COLOR_MATRIX_IN:
1375 return 0;
1376 default:
1377 return -1;
1381 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
1382 const Matrix5x4& aMatrix) {
1383 MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX);
1384 mMatrix = aMatrix;
1385 Invalidate();
1388 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
1389 uint32_t aAlphaMode) {
1390 MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE);
1391 mAlphaMode = (AlphaMode)aAlphaMode;
1392 Invalidate();
1395 static already_AddRefed<DataSourceSurface> Premultiply(
1396 DataSourceSurface* aSurface) {
1397 if (aSurface->GetFormat() == SurfaceFormat::A8) {
1398 RefPtr<DataSourceSurface> surface(aSurface);
1399 return surface.forget();
1402 IntSize size = aSurface->GetSize();
1403 RefPtr<DataSourceSurface> target =
1404 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1405 if (MOZ2D_WARN_IF(!target)) {
1406 return nullptr;
1409 DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
1410 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
1411 if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
1412 return nullptr;
1415 uint8_t* inputData = inputMap.GetData();
1416 int32_t inputStride = inputMap.GetStride();
1417 uint8_t* targetData = targetMap.GetData();
1418 int32_t targetStride = targetMap.GetStride();
1420 FilterProcessing::DoPremultiplicationCalculation(
1421 size, targetData, targetStride, inputData, inputStride);
1423 return target.forget();
1426 static already_AddRefed<DataSourceSurface> Unpremultiply(
1427 DataSourceSurface* aSurface) {
1428 if (aSurface->GetFormat() == SurfaceFormat::A8) {
1429 RefPtr<DataSourceSurface> surface(aSurface);
1430 return surface.forget();
1433 IntSize size = aSurface->GetSize();
1434 RefPtr<DataSourceSurface> target =
1435 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1436 if (MOZ2D_WARN_IF(!target)) {
1437 return nullptr;
1440 DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
1441 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
1442 if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
1443 return nullptr;
1446 uint8_t* inputData = inputMap.GetData();
1447 int32_t inputStride = inputMap.GetStride();
1448 uint8_t* targetData = targetMap.GetData();
1449 int32_t targetStride = targetMap.GetStride();
1451 FilterProcessing::DoUnpremultiplicationCalculation(
1452 size, targetData, targetStride, inputData, inputStride);
1454 return target.forget();
1457 static already_AddRefed<DataSourceSurface> Opacity(DataSourceSurface* aSurface,
1458 Float aValue) {
1459 if (aValue == 1.0f) {
1460 RefPtr<DataSourceSurface> surface(aSurface);
1461 return surface.forget();
1464 IntSize size = aSurface->GetSize();
1465 RefPtr<DataSourceSurface> target =
1466 Factory::CreateDataSourceSurface(size, aSurface->GetFormat());
1467 if (MOZ2D_WARN_IF(!target)) {
1468 return nullptr;
1471 DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
1472 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
1473 if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
1474 return nullptr;
1477 uint8_t* inputData = inputMap.GetData();
1478 int32_t inputStride = inputMap.GetStride();
1479 uint8_t* targetData = targetMap.GetData();
1480 int32_t targetStride = targetMap.GetStride();
1482 if (aSurface->GetFormat() == SurfaceFormat::A8) {
1483 FilterProcessing::DoOpacityCalculationA8(size, targetData, targetStride,
1484 inputData, inputStride, aValue);
1485 } else {
1486 MOZ_ASSERT(aSurface->GetFormat() == SurfaceFormat::B8G8R8A8);
1487 FilterProcessing::DoOpacityCalculation(size, targetData, targetStride,
1488 inputData, inputStride, aValue);
1491 return target.forget();
1494 already_AddRefed<DataSourceSurface> FilterNodeColorMatrixSoftware::Render(
1495 const IntRect& aRect) {
1496 RefPtr<DataSourceSurface> input =
1497 GetInputDataSourceSurface(IN_COLOR_MATRIX_IN, aRect, NEED_COLOR_CHANNELS);
1498 if (!input) {
1499 return nullptr;
1502 if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
1503 input = Unpremultiply(input);
1506 RefPtr<DataSourceSurface> result =
1507 FilterProcessing::ApplyColorMatrix(input, mMatrix);
1509 if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
1510 result = Premultiply(result);
1513 return result.forget();
1516 void FilterNodeColorMatrixSoftware::RequestFromInputsForRect(
1517 const IntRect& aRect) {
1518 RequestInputRect(IN_COLOR_MATRIX_IN, aRect);
1521 IntRect FilterNodeColorMatrixSoftware::MapRectToSource(
1522 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
1523 return MapInputRectToSource(IN_COLOR_MATRIX_IN, aRect, aMax, aSourceNode);
1526 IntRect FilterNodeColorMatrixSoftware::GetOutputRectInRect(
1527 const IntRect& aRect) {
1528 if (mMatrix._54 > 0.0f) {
1529 return aRect;
1531 return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect);
1534 void FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex,
1535 const DeviceColor& aColor) {
1536 MOZ_ASSERT(aIndex == ATT_FLOOD_COLOR);
1537 mColor = aColor;
1538 Invalidate();
1541 static uint32_t ColorToBGRA(const DeviceColor& aColor) {
1542 union {
1543 uint32_t color;
1544 uint8_t components[4];
1546 components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
1547 NS_lround(aColor.r * aColor.a * 255.0f);
1548 components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
1549 NS_lround(aColor.g * aColor.a * 255.0f);
1550 components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
1551 NS_lround(aColor.b * aColor.a * 255.0f);
1552 components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = NS_lround(aColor.a * 255.0f);
1553 return color;
1556 static SurfaceFormat FormatForColor(DeviceColor aColor) {
1557 if (aColor.r == 0 && aColor.g == 0 && aColor.b == 0) {
1558 return SurfaceFormat::A8;
1560 return SurfaceFormat::B8G8R8A8;
1563 already_AddRefed<DataSourceSurface> FilterNodeFloodSoftware::Render(
1564 const IntRect& aRect) {
1565 SurfaceFormat format = FormatForColor(mColor);
1566 RefPtr<DataSourceSurface> target =
1567 Factory::CreateDataSourceSurface(aRect.Size(), format);
1568 if (MOZ2D_WARN_IF(!target)) {
1569 return nullptr;
1572 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
1573 if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
1574 return nullptr;
1577 uint8_t* targetData = targetMap.GetData();
1578 int32_t stride = targetMap.GetStride();
1580 if (format == SurfaceFormat::B8G8R8A8) {
1581 uint32_t color = ColorToBGRA(mColor);
1582 for (int32_t y = 0; y < aRect.Height(); y++) {
1583 for (int32_t x = 0; x < aRect.Width(); x++) {
1584 *((uint32_t*)targetData + x) = color;
1586 PodZero(&targetData[aRect.Width() * 4], stride - aRect.Width() * 4);
1587 targetData += stride;
1589 } else if (format == SurfaceFormat::A8) {
1590 uint8_t alpha = NS_lround(mColor.a * 255.0f);
1591 for (int32_t y = 0; y < aRect.Height(); y++) {
1592 for (int32_t x = 0; x < aRect.Width(); x++) {
1593 targetData[x] = alpha;
1595 PodZero(&targetData[aRect.Width()], stride - aRect.Width());
1596 targetData += stride;
1598 } else {
1599 gfxDevCrash(LogReason::FilterInputFormat)
1600 << "Bad format in flood render " << (int)format;
1601 return nullptr;
1604 return target.forget();
1607 // Override GetOutput to get around caching. Rendering simple floods is
1608 // comparatively fast.
1609 already_AddRefed<DataSourceSurface> FilterNodeFloodSoftware::GetOutput(
1610 const IntRect& aRect) {
1611 return Render(aRect);
1614 IntRect FilterNodeFloodSoftware::MapRectToSource(const IntRect& aRect,
1615 const IntRect& aMax,
1616 FilterNode* aSourceNode) {
1617 return IntRect();
1620 IntRect FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect& aRect) {
1621 if (mColor.a == 0.0f) {
1622 return IntRect();
1624 return aRect;
1627 int32_t FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex) {
1628 switch (aInputEnumIndex) {
1629 case IN_TILE_IN:
1630 return 0;
1631 default:
1632 return -1;
1636 void FilterNodeTileSoftware::SetAttribute(uint32_t aIndex,
1637 const IntRect& aSourceRect) {
1638 MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT);
1639 mSourceRect.SetRect(int32_t(aSourceRect.X()), int32_t(aSourceRect.Y()),
1640 int32_t(aSourceRect.Width()),
1641 int32_t(aSourceRect.Height()));
1642 Invalidate();
1645 namespace {
1646 struct CompareIntRects {
1647 bool operator()(const IntRect& a, const IntRect& b) const {
1648 if (a.X() != b.X()) {
1649 return a.X() < b.X();
1651 if (a.Y() != b.Y()) {
1652 return a.Y() < b.Y();
1654 if (a.Width() != b.Width()) {
1655 return a.Width() < b.Width();
1657 return a.Height() < b.Height();
1661 } // namespace
1663 already_AddRefed<DataSourceSurface> FilterNodeTileSoftware::Render(
1664 const IntRect& aRect) {
1665 if (mSourceRect.IsEmpty()) {
1666 return nullptr;
1669 if (mSourceRect.Contains(aRect)) {
1670 return GetInputDataSourceSurface(IN_TILE_IN, aRect);
1673 RefPtr<DataSourceSurface> target;
1675 typedef std::map<IntRect, RefPtr<DataSourceSurface>, CompareIntRects>
1676 InputMap;
1677 InputMap inputs;
1679 IntPoint startIndex = TileIndex(mSourceRect, aRect.TopLeft());
1680 IntPoint endIndex = TileIndex(mSourceRect, aRect.BottomRight());
1681 for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
1682 for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
1683 IntPoint sourceToDestOffset(ix * mSourceRect.Width(),
1684 iy * mSourceRect.Height());
1685 IntRect destRect = aRect.Intersect(mSourceRect + sourceToDestOffset);
1686 IntRect srcRect = destRect - sourceToDestOffset;
1687 if (srcRect.IsEmpty()) {
1688 continue;
1691 RefPtr<DataSourceSurface> input;
1692 InputMap::iterator it = inputs.find(srcRect);
1693 if (it == inputs.end()) {
1694 input = GetInputDataSourceSurface(IN_TILE_IN, srcRect);
1695 inputs[srcRect] = input;
1696 } else {
1697 input = it->second;
1699 if (!input) {
1700 return nullptr;
1702 if (!target) {
1703 // We delay creating the target until now because we want to use the
1704 // same format as our input filter, and we do not actually know the
1705 // input format before we call GetInputDataSourceSurface.
1706 target =
1707 Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat());
1708 if (MOZ2D_WARN_IF(!target)) {
1709 return nullptr;
1713 if (input->GetFormat() != target->GetFormat()) {
1714 // Different rectangles of the input can have different formats. If
1715 // that happens, just convert everything to B8G8R8A8.
1716 target = FilterProcessing::ConvertToB8G8R8A8(target);
1717 input = FilterProcessing::ConvertToB8G8R8A8(input);
1718 if (MOZ2D_WARN_IF(!target) || MOZ2D_WARN_IF(!input)) {
1719 return nullptr;
1723 CopyRect(input, target, srcRect - srcRect.TopLeft(),
1724 destRect.TopLeft() - aRect.TopLeft());
1728 return target.forget();
1731 void FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect& aRect) {
1732 // Do not request anything.
1733 // Source rects for the tile filter can be discontinuous with large gaps
1734 // between them. Requesting those from our input filter might cause it to
1735 // render the whole bounding box of all of them, which would be wasteful.
1738 IntRect FilterNodeTileSoftware::GetOutputRectInRect(const IntRect& aRect) {
1739 return aRect;
1742 FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware()
1743 : mDisableR(true), mDisableG(true), mDisableB(true), mDisableA(true) {}
1745 void FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex,
1746 bool aDisable) {
1747 switch (aIndex) {
1748 case ATT_TRANSFER_DISABLE_R:
1749 mDisableR = aDisable;
1750 break;
1751 case ATT_TRANSFER_DISABLE_G:
1752 mDisableG = aDisable;
1753 break;
1754 case ATT_TRANSFER_DISABLE_B:
1755 mDisableB = aDisable;
1756 break;
1757 case ATT_TRANSFER_DISABLE_A:
1758 mDisableA = aDisable;
1759 break;
1760 default:
1761 MOZ_CRASH("GFX: FilterNodeComponentTransferSoftware::SetAttribute");
1763 Invalidate();
1766 void FilterNodeComponentTransferSoftware::GenerateLookupTable(
1767 ptrdiff_t aComponent, uint8_t aTables[4][256], bool aDisabled) {
1768 if (aDisabled) {
1769 for (int32_t i = 0; i < 256; ++i) {
1770 aTables[aComponent][i] = i;
1772 } else {
1773 FillLookupTable(aComponent, aTables[aComponent]);
1777 template <uint32_t BytesPerPixel>
1778 static void TransferComponents(
1779 DataSourceSurface* aInput, DataSourceSurface* aTarget,
1780 const uint8_t aLookupTables[BytesPerPixel][256]) {
1781 MOZ_ASSERT(aInput->GetFormat() == aTarget->GetFormat(), "different formats");
1782 IntSize size = aInput->GetSize();
1784 DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
1785 DataSourceSurface::ScopedMap targetMap(aTarget, DataSourceSurface::WRITE);
1786 if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
1787 return;
1790 uint8_t* sourceData = sourceMap.GetData();
1791 int32_t sourceStride = sourceMap.GetStride();
1792 uint8_t* targetData = targetMap.GetData();
1793 int32_t targetStride = targetMap.GetStride();
1795 MOZ_ASSERT(sourceStride <= targetStride, "target smaller than source");
1797 for (int32_t y = 0; y < size.height; y++) {
1798 for (int32_t x = 0; x < size.width; x++) {
1799 uint32_t sourceIndex = y * sourceStride + x * BytesPerPixel;
1800 uint32_t targetIndex = y * targetStride + x * BytesPerPixel;
1801 for (uint32_t i = 0; i < BytesPerPixel; i++) {
1802 targetData[targetIndex + i] =
1803 aLookupTables[i][sourceData[sourceIndex + i]];
1807 // Zero padding to keep valgrind happy.
1808 PodZero(&targetData[y * targetStride + size.width * BytesPerPixel],
1809 targetStride - size.width * BytesPerPixel);
1813 static bool IsAllZero(const uint8_t aLookupTable[256]) {
1814 for (int32_t i = 0; i < 256; i++) {
1815 if (aLookupTable[i] != 0) {
1816 return false;
1819 return true;
1822 already_AddRefed<DataSourceSurface> FilterNodeComponentTransferSoftware::Render(
1823 const IntRect& aRect) {
1824 if (mDisableR && mDisableG && mDisableB && mDisableA) {
1825 return GetInputDataSourceSurface(IN_TRANSFER_IN, aRect);
1828 uint8_t lookupTables[4][256];
1829 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R, lookupTables, mDisableR);
1830 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G, lookupTables, mDisableG);
1831 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B, lookupTables, mDisableB);
1832 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A, lookupTables, mDisableA);
1834 bool needColorChannels =
1835 lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R][0] != 0 ||
1836 lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G][0] != 0 ||
1837 lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B][0] != 0;
1839 FormatHint pref = needColorChannels ? NEED_COLOR_CHANNELS : CAN_HANDLE_A8;
1841 RefPtr<DataSourceSurface> input =
1842 GetInputDataSourceSurface(IN_TRANSFER_IN, aRect, pref);
1843 if (!input) {
1844 return nullptr;
1847 if (input->GetFormat() == SurfaceFormat::B8G8R8A8 && !needColorChannels) {
1848 bool colorChannelsBecomeBlack =
1849 IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) &&
1850 IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) &&
1851 IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B]);
1853 if (colorChannelsBecomeBlack) {
1854 input = FilterProcessing::ExtractAlpha(input);
1858 SurfaceFormat format = input->GetFormat();
1859 if (format == SurfaceFormat::A8 && mDisableA) {
1860 return input.forget();
1863 RefPtr<DataSourceSurface> target =
1864 Factory::CreateDataSourceSurface(aRect.Size(), format);
1865 if (MOZ2D_WARN_IF(!target)) {
1866 return nullptr;
1869 if (format == SurfaceFormat::A8) {
1870 TransferComponents<1>(input, target,
1871 &lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_A]);
1872 } else {
1873 TransferComponents<4>(input, target, lookupTables);
1876 return target.forget();
1879 void FilterNodeComponentTransferSoftware::RequestFromInputsForRect(
1880 const IntRect& aRect) {
1881 RequestInputRect(IN_TRANSFER_IN, aRect);
1884 IntRect FilterNodeComponentTransferSoftware::MapRectToSource(
1885 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
1886 return MapInputRectToSource(IN_TRANSFER_IN, aRect, aMax, aSourceNode);
1889 IntRect FilterNodeComponentTransferSoftware::GetOutputRectInRect(
1890 const IntRect& aRect) {
1891 if (mDisableA) {
1892 return GetInputRectInRect(IN_TRANSFER_IN, aRect);
1894 return aRect;
1897 int32_t FilterNodeComponentTransferSoftware::InputIndex(
1898 uint32_t aInputEnumIndex) {
1899 switch (aInputEnumIndex) {
1900 case IN_TRANSFER_IN:
1901 return 0;
1902 default:
1903 return -1;
1907 void FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex,
1908 const Float* aFloat,
1909 uint32_t aSize) {
1910 std::vector<Float> table(aFloat, aFloat + aSize);
1911 switch (aIndex) {
1912 case ATT_TABLE_TRANSFER_TABLE_R:
1913 mTableR = table;
1914 break;
1915 case ATT_TABLE_TRANSFER_TABLE_G:
1916 mTableG = table;
1917 break;
1918 case ATT_TABLE_TRANSFER_TABLE_B:
1919 mTableB = table;
1920 break;
1921 case ATT_TABLE_TRANSFER_TABLE_A:
1922 mTableA = table;
1923 break;
1924 default:
1925 MOZ_CRASH("GFX: FilterNodeTableTransferSoftware::SetAttribute");
1927 Invalidate();
1930 void FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
1931 uint8_t aTable[256]) {
1932 switch (aComponent) {
1933 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
1934 FillLookupTableImpl(mTableR, aTable);
1935 break;
1936 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
1937 FillLookupTableImpl(mTableG, aTable);
1938 break;
1939 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
1940 FillLookupTableImpl(mTableB, aTable);
1941 break;
1942 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
1943 FillLookupTableImpl(mTableA, aTable);
1944 break;
1945 default:
1946 MOZ_ASSERT(false, "unknown component");
1947 break;
1951 void FilterNodeTableTransferSoftware::FillLookupTableImpl(
1952 std::vector<Float>& aTableValues, uint8_t aTable[256]) {
1953 uint32_t tvLength = aTableValues.size();
1954 if (tvLength < 2) {
1955 return;
1958 for (size_t i = 0; i < 256; i++) {
1959 uint32_t k = (i * (tvLength - 1)) / 255;
1960 Float v1 = aTableValues[k];
1961 Float v2 = aTableValues[std::min(k + 1, tvLength - 1)];
1962 int32_t val = int32_t(255 * (v1 + (i / 255.0f - k / float(tvLength - 1)) *
1963 (tvLength - 1) * (v2 - v1)));
1964 val = std::min(255, val);
1965 val = std::max(0, val);
1966 aTable[i] = val;
1970 void FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex,
1971 const Float* aFloat,
1972 uint32_t aSize) {
1973 std::vector<Float> discrete(aFloat, aFloat + aSize);
1974 switch (aIndex) {
1975 case ATT_DISCRETE_TRANSFER_TABLE_R:
1976 mTableR = discrete;
1977 break;
1978 case ATT_DISCRETE_TRANSFER_TABLE_G:
1979 mTableG = discrete;
1980 break;
1981 case ATT_DISCRETE_TRANSFER_TABLE_B:
1982 mTableB = discrete;
1983 break;
1984 case ATT_DISCRETE_TRANSFER_TABLE_A:
1985 mTableA = discrete;
1986 break;
1987 default:
1988 MOZ_CRASH("GFX: FilterNodeDiscreteTransferSoftware::SetAttribute");
1990 Invalidate();
1993 void FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
1994 uint8_t aTable[256]) {
1995 switch (aComponent) {
1996 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
1997 FillLookupTableImpl(mTableR, aTable);
1998 break;
1999 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
2000 FillLookupTableImpl(mTableG, aTable);
2001 break;
2002 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
2003 FillLookupTableImpl(mTableB, aTable);
2004 break;
2005 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
2006 FillLookupTableImpl(mTableA, aTable);
2007 break;
2008 default:
2009 MOZ_ASSERT(false, "unknown component");
2010 break;
2014 void FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(
2015 std::vector<Float>& aTableValues, uint8_t aTable[256]) {
2016 uint32_t tvLength = aTableValues.size();
2017 if (tvLength < 1) {
2018 return;
2021 for (size_t i = 0; i < 256; i++) {
2022 uint32_t k = (i * tvLength) / 255;
2023 k = std::min(k, tvLength - 1);
2024 Float v = aTableValues[k];
2025 int32_t val = NS_lround(255 * v);
2026 val = std::min(255, val);
2027 val = std::max(0, val);
2028 aTable[i] = val;
2032 FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware()
2033 : mSlopeR(0),
2034 mSlopeG(0),
2035 mSlopeB(0),
2036 mSlopeA(0),
2037 mInterceptR(0),
2038 mInterceptG(0),
2039 mInterceptB(0),
2040 mInterceptA(0) {}
2042 void FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex,
2043 Float aValue) {
2044 switch (aIndex) {
2045 case ATT_LINEAR_TRANSFER_SLOPE_R:
2046 mSlopeR = aValue;
2047 break;
2048 case ATT_LINEAR_TRANSFER_INTERCEPT_R:
2049 mInterceptR = aValue;
2050 break;
2051 case ATT_LINEAR_TRANSFER_SLOPE_G:
2052 mSlopeG = aValue;
2053 break;
2054 case ATT_LINEAR_TRANSFER_INTERCEPT_G:
2055 mInterceptG = aValue;
2056 break;
2057 case ATT_LINEAR_TRANSFER_SLOPE_B:
2058 mSlopeB = aValue;
2059 break;
2060 case ATT_LINEAR_TRANSFER_INTERCEPT_B:
2061 mInterceptB = aValue;
2062 break;
2063 case ATT_LINEAR_TRANSFER_SLOPE_A:
2064 mSlopeA = aValue;
2065 break;
2066 case ATT_LINEAR_TRANSFER_INTERCEPT_A:
2067 mInterceptA = aValue;
2068 break;
2069 default:
2070 MOZ_CRASH("GFX: FilterNodeLinearTransferSoftware::SetAttribute");
2072 Invalidate();
2075 void FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
2076 uint8_t aTable[256]) {
2077 switch (aComponent) {
2078 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
2079 FillLookupTableImpl(mSlopeR, mInterceptR, aTable);
2080 break;
2081 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
2082 FillLookupTableImpl(mSlopeG, mInterceptG, aTable);
2083 break;
2084 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
2085 FillLookupTableImpl(mSlopeB, mInterceptB, aTable);
2086 break;
2087 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
2088 FillLookupTableImpl(mSlopeA, mInterceptA, aTable);
2089 break;
2090 default:
2091 MOZ_ASSERT(false, "unknown component");
2092 break;
2096 void FilterNodeLinearTransferSoftware::FillLookupTableImpl(
2097 Float aSlope, Float aIntercept, uint8_t aTable[256]) {
2098 for (size_t i = 0; i < 256; i++) {
2099 int32_t val = NS_lround(aSlope * i + 255 * aIntercept);
2100 val = std::min(255, val);
2101 val = std::max(0, val);
2102 aTable[i] = val;
2106 FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware()
2107 : mAmplitudeR(0),
2108 mAmplitudeG(0),
2109 mAmplitudeB(0),
2110 mAmplitudeA(0),
2111 mExponentR(0),
2112 mExponentG(0),
2113 mExponentB(0),
2114 mExponentA(0),
2115 mOffsetR(0.0),
2116 mOffsetG(0.0),
2117 mOffsetB(0.0),
2118 mOffsetA(0.0) {}
2120 void FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex,
2121 Float aValue) {
2122 switch (aIndex) {
2123 case ATT_GAMMA_TRANSFER_AMPLITUDE_R:
2124 mAmplitudeR = aValue;
2125 break;
2126 case ATT_GAMMA_TRANSFER_EXPONENT_R:
2127 mExponentR = aValue;
2128 break;
2129 case ATT_GAMMA_TRANSFER_OFFSET_R:
2130 mOffsetR = aValue;
2131 break;
2132 case ATT_GAMMA_TRANSFER_AMPLITUDE_G:
2133 mAmplitudeG = aValue;
2134 break;
2135 case ATT_GAMMA_TRANSFER_EXPONENT_G:
2136 mExponentG = aValue;
2137 break;
2138 case ATT_GAMMA_TRANSFER_OFFSET_G:
2139 mOffsetG = aValue;
2140 break;
2141 case ATT_GAMMA_TRANSFER_AMPLITUDE_B:
2142 mAmplitudeB = aValue;
2143 break;
2144 case ATT_GAMMA_TRANSFER_EXPONENT_B:
2145 mExponentB = aValue;
2146 break;
2147 case ATT_GAMMA_TRANSFER_OFFSET_B:
2148 mOffsetB = aValue;
2149 break;
2150 case ATT_GAMMA_TRANSFER_AMPLITUDE_A:
2151 mAmplitudeA = aValue;
2152 break;
2153 case ATT_GAMMA_TRANSFER_EXPONENT_A:
2154 mExponentA = aValue;
2155 break;
2156 case ATT_GAMMA_TRANSFER_OFFSET_A:
2157 mOffsetA = aValue;
2158 break;
2159 default:
2160 MOZ_CRASH("GFX: FilterNodeGammaTransferSoftware::SetAttribute");
2162 Invalidate();
2165 void FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
2166 uint8_t aTable[256]) {
2167 switch (aComponent) {
2168 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
2169 FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable);
2170 break;
2171 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
2172 FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable);
2173 break;
2174 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
2175 FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable);
2176 break;
2177 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
2178 FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable);
2179 break;
2180 default:
2181 MOZ_ASSERT(false, "unknown component");
2182 break;
2186 void FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude,
2187 Float aExponent,
2188 Float aOffset,
2189 uint8_t aTable[256]) {
2190 for (size_t i = 0; i < 256; i++) {
2191 int32_t val =
2192 NS_lround(255 * (aAmplitude * pow(i / 255.0f, aExponent) + aOffset));
2193 val = std::min(255, val);
2194 val = std::max(0, val);
2195 aTable[i] = val;
2199 FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware()
2200 : mDivisor(0),
2201 mBias(0),
2202 mEdgeMode(EDGE_MODE_DUPLICATE),
2203 mPreserveAlpha(false) {}
2205 int32_t FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) {
2206 switch (aInputEnumIndex) {
2207 case IN_CONVOLVE_MATRIX_IN:
2208 return 0;
2209 default:
2210 return -1;
2214 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2215 uint32_t aIndex, const IntSize& aKernelSize) {
2216 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_SIZE);
2217 mKernelSize = aKernelSize;
2218 Invalidate();
2221 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2222 const Float* aMatrix,
2223 uint32_t aSize) {
2224 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_MATRIX);
2225 mKernelMatrix = std::vector<Float>(aMatrix, aMatrix + aSize);
2226 Invalidate();
2229 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2230 Float aValue) {
2231 switch (aIndex) {
2232 case ATT_CONVOLVE_MATRIX_DIVISOR:
2233 mDivisor = aValue;
2234 break;
2235 case ATT_CONVOLVE_MATRIX_BIAS:
2236 mBias = aValue;
2237 break;
2238 default:
2239 MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
2241 Invalidate();
2244 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2245 uint32_t aIndex, const Size& aKernelUnitLength) {
2246 switch (aIndex) {
2247 case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH:
2248 mKernelUnitLength = aKernelUnitLength;
2249 break;
2250 default:
2251 MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
2253 Invalidate();
2256 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2257 const IntPoint& aTarget) {
2258 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_TARGET);
2259 mTarget = aTarget;
2260 Invalidate();
2263 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2264 uint32_t aIndex, const IntRect& aSourceRect) {
2265 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_SOURCE_RECT);
2266 mSourceRect = aSourceRect;
2267 Invalidate();
2270 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2271 uint32_t aEdgeMode) {
2272 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_EDGE_MODE);
2273 mEdgeMode = static_cast<ConvolveMatrixEdgeMode>(aEdgeMode);
2274 Invalidate();
2277 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2278 bool aPreserveAlpha) {
2279 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA);
2280 mPreserveAlpha = aPreserveAlpha;
2281 Invalidate();
2284 #ifdef DEBUG
2285 static inline void DebugOnlyCheckColorSamplingAccess(
2286 const uint8_t* aSampleAddress, const uint8_t* aBoundsBegin,
2287 const uint8_t* aBoundsEnd) {
2288 MOZ_ASSERT(aSampleAddress >= aBoundsBegin, "accessing before start");
2289 MOZ_ASSERT(aSampleAddress < aBoundsEnd, "accessing after end");
2291 #else
2292 # define DebugOnlyCheckColorSamplingAccess(address, boundsBegin, boundsEnd)
2293 #endif
2295 static inline uint8_t ColorComponentAtPoint(const uint8_t* aData,
2296 ptrdiff_t aStride,
2297 const uint8_t* aBoundsBegin,
2298 const uint8_t* aBoundsEnd,
2299 int32_t x, int32_t y, ptrdiff_t bpp,
2300 ptrdiff_t c) {
2301 DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c],
2302 aBoundsBegin, aBoundsEnd);
2303 return aData[y * aStride + bpp * x + c];
2306 static inline int32_t ColorAtPoint(const uint8_t* aData, ptrdiff_t aStride,
2307 const uint8_t* aBoundsBegin,
2308 const uint8_t* aBoundsEnd, int32_t x,
2309 int32_t y) {
2310 DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x, aBoundsBegin,
2311 aBoundsEnd);
2312 return *(uint32_t*)(aData + y * aStride + 4 * x);
2315 // Accepts fractional x & y and does bilinear interpolation.
2316 // Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible.
2317 static inline uint8_t ColorComponentAtPoint(
2318 const uint8_t* aData, ptrdiff_t aStride, const uint8_t* aBoundsBegin,
2319 const uint8_t* aBoundsEnd, Float x, Float y, ptrdiff_t bpp, ptrdiff_t c) {
2320 const uint32_t f = 256;
2321 const int32_t lx = floor(x);
2322 const int32_t ly = floor(y);
2323 const int32_t tux = uint32_t((x - lx) * f);
2324 const int32_t tlx = f - tux;
2325 const int32_t tuy = uint32_t((y - ly) * f);
2326 const int32_t tly = f - tuy;
2327 const uint8_t& cll = ColorComponentAtPoint(aData, aStride, aBoundsBegin,
2328 aBoundsEnd, lx, ly, bpp, c);
2329 const uint8_t& cul = ColorComponentAtPoint(aData, aStride, aBoundsBegin,
2330 aBoundsEnd, lx + 1, ly, bpp, c);
2331 const uint8_t& clu = ColorComponentAtPoint(aData, aStride, aBoundsBegin,
2332 aBoundsEnd, lx, ly + 1, bpp, c);
2333 const uint8_t& cuu = ColorComponentAtPoint(
2334 aData, aStride, aBoundsBegin, aBoundsEnd, lx + 1, ly + 1, bpp, c);
2335 return ((cll * tlx + cul * tux) * tly + (clu * tlx + cuu * tux) * tuy +
2336 f * f / 2) /
2337 (f * f);
2340 static int32_t ClampToNonZero(int32_t a) { return a * (a >= 0); }
2342 template <typename CoordType>
2343 static void ConvolvePixel(const uint8_t* aSourceData, uint8_t* aTargetData,
2344 int32_t aWidth, int32_t aHeight,
2345 int32_t aSourceStride, int32_t aTargetStride,
2346 const uint8_t* aSourceBegin,
2347 const uint8_t* aSourceEnd, int32_t aX, int32_t aY,
2348 const int32_t* aKernel, int32_t aBias, int32_t shiftL,
2349 int32_t shiftR, bool aPreserveAlpha, int32_t aOrderX,
2350 int32_t aOrderY, int32_t aTargetX, int32_t aTargetY,
2351 CoordType aKernelUnitLengthX,
2352 CoordType aKernelUnitLengthY) {
2353 int32_t sum[4] = {0, 0, 0, 0};
2354 int32_t offsets[4] = {
2355 B8G8R8A8_COMPONENT_BYTEOFFSET_R, B8G8R8A8_COMPONENT_BYTEOFFSET_G,
2356 B8G8R8A8_COMPONENT_BYTEOFFSET_B, B8G8R8A8_COMPONENT_BYTEOFFSET_A};
2357 int32_t channels = aPreserveAlpha ? 3 : 4;
2358 int32_t roundingAddition = shiftL == 0 ? 0 : 1 << (shiftL - 1);
2360 for (int32_t y = 0; y < aOrderY; y++) {
2361 CoordType sampleY = aY + (y - aTargetY) * aKernelUnitLengthY;
2362 for (int32_t x = 0; x < aOrderX; x++) {
2363 CoordType sampleX = aX + (x - aTargetX) * aKernelUnitLengthX;
2364 for (int32_t i = 0; i < channels; i++) {
2365 sum[i] +=
2366 aKernel[aOrderX * y + x] *
2367 ColorComponentAtPoint(aSourceData, aSourceStride, aSourceBegin,
2368 aSourceEnd, sampleX, sampleY, 4, offsets[i]);
2372 for (int32_t i = 0; i < channels; i++) {
2373 int32_t clamped =
2374 umin(ClampToNonZero(sum[i] + aBias), 255 << shiftL >> shiftR);
2375 aTargetData[aY * aTargetStride + 4 * aX + offsets[i]] =
2376 (clamped + roundingAddition) << shiftR >> shiftL;
2378 if (aPreserveAlpha) {
2379 aTargetData[aY * aTargetStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
2380 aSourceData[aY * aSourceStride + 4 * aX +
2381 B8G8R8A8_COMPONENT_BYTEOFFSET_A];
2385 already_AddRefed<DataSourceSurface> FilterNodeConvolveMatrixSoftware::Render(
2386 const IntRect& aRect) {
2387 if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
2388 mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
2389 return DoRender(aRect, (int32_t)mKernelUnitLength.width,
2390 (int32_t)mKernelUnitLength.height);
2392 return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
2395 static std::vector<Float> ReversedVector(const std::vector<Float>& aVector) {
2396 size_t length = aVector.size();
2397 std::vector<Float> result(length, 0);
2398 for (size_t i = 0; i < length; i++) {
2399 result[length - 1 - i] = aVector[i];
2401 return result;
2404 static std::vector<Float> ScaledVector(const std::vector<Float>& aVector,
2405 Float aDivisor) {
2406 size_t length = aVector.size();
2407 std::vector<Float> result(length, 0);
2408 for (size_t i = 0; i < length; i++) {
2409 result[i] = aVector[i] / aDivisor;
2411 return result;
2414 static Float MaxVectorSum(const std::vector<Float>& aVector) {
2415 Float sum = 0;
2416 size_t length = aVector.size();
2417 for (size_t i = 0; i < length; i++) {
2418 if (aVector[i] > 0) {
2419 sum += aVector[i];
2422 return sum;
2425 // Returns shiftL and shiftR in such a way that
2426 // a << shiftL >> shiftR is roughly a * aFloat.
2427 static void TranslateDoubleToShifts(double aDouble, int32_t& aShiftL,
2428 int32_t& aShiftR) {
2429 aShiftL = 0;
2430 aShiftR = 0;
2431 if (aDouble <= 0) {
2432 MOZ_CRASH("GFX: TranslateDoubleToShifts");
2434 if (aDouble < 1) {
2435 while (1 << (aShiftR + 1) < 1 / aDouble) {
2436 aShiftR++;
2438 } else {
2439 while (1 << (aShiftL + 1) < aDouble) {
2440 aShiftL++;
2445 template <typename CoordType>
2446 already_AddRefed<DataSourceSurface> FilterNodeConvolveMatrixSoftware::DoRender(
2447 const IntRect& aRect, CoordType aKernelUnitLengthX,
2448 CoordType aKernelUnitLengthY) {
2449 if (mKernelSize.width <= 0 || mKernelSize.height <= 0 ||
2450 mKernelMatrix.size() !=
2451 uint32_t(mKernelSize.width * mKernelSize.height) ||
2452 !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) ||
2453 mDivisor == 0) {
2454 return Factory::CreateDataSourceSurface(aRect.Size(),
2455 SurfaceFormat::B8G8R8A8, true);
2458 IntRect srcRect = InflatedSourceRect(aRect);
2460 // Inflate the source rect by another pixel because the bilinear filtering in
2461 // ColorComponentAtPoint may want to access the margins.
2462 srcRect.Inflate(1);
2464 RefPtr<DataSourceSurface> input =
2465 GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect,
2466 NEED_COLOR_CHANNELS, mEdgeMode, &mSourceRect);
2468 if (!input) {
2469 return nullptr;
2472 RefPtr<DataSourceSurface> target = Factory::CreateDataSourceSurface(
2473 aRect.Size(), SurfaceFormat::B8G8R8A8, true);
2474 if (MOZ2D_WARN_IF(!target)) {
2475 return nullptr;
2478 IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
2480 DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
2481 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
2482 if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
2483 return nullptr;
2486 uint8_t* sourceData =
2487 DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
2488 int32_t sourceStride = sourceMap.GetStride();
2489 uint8_t* sourceBegin = sourceMap.GetData();
2490 uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
2491 uint8_t* targetData = targetMap.GetData();
2492 int32_t targetStride = targetMap.GetStride();
2494 // Why exactly are we reversing the kernel?
2495 std::vector<Float> kernel = ReversedVector(mKernelMatrix);
2496 kernel = ScaledVector(kernel, mDivisor);
2497 Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias,
2498 MaxVectorSum(ScaledVector(kernel, -1)) - mBias);
2499 maxResultAbs = std::max(maxResultAbs, 1.0f);
2501 double idealFactor = INT32_MAX / 2.0 / maxResultAbs / 255.0 * 0.999;
2502 MOZ_ASSERT(255.0 * maxResultAbs * idealFactor <= INT32_MAX / 2.0,
2503 "badly chosen float-to-int scale");
2504 int32_t shiftL, shiftR;
2505 TranslateDoubleToShifts(idealFactor, shiftL, shiftR);
2506 double factorFromShifts = Float(1 << shiftL) / Float(1 << shiftR);
2507 MOZ_ASSERT(255.0 * maxResultAbs * factorFromShifts <= INT32_MAX / 2.0,
2508 "badly chosen float-to-int scale");
2510 int32_t* intKernel = new int32_t[kernel.size()];
2511 for (size_t i = 0; i < kernel.size(); i++) {
2512 intKernel[i] = NS_lround(kernel[i] * factorFromShifts);
2514 int32_t bias = NS_lround(mBias * 255 * factorFromShifts);
2516 for (int32_t y = 0; y < aRect.Height(); y++) {
2517 for (int32_t x = 0; x < aRect.Width(); x++) {
2518 ConvolvePixel(sourceData, targetData, aRect.Width(), aRect.Height(),
2519 sourceStride, targetStride, sourceBegin, sourceEnd, x, y,
2520 intKernel, bias, shiftL, shiftR, mPreserveAlpha,
2521 mKernelSize.width, mKernelSize.height, mTarget.x, mTarget.y,
2522 aKernelUnitLengthX, aKernelUnitLengthY);
2525 delete[] intKernel;
2527 return target.forget();
2530 void FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(
2531 const IntRect& aRect) {
2532 RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect));
2535 IntRect FilterNodeConvolveMatrixSoftware::MapRectToSource(
2536 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
2537 return MapInputRectToSource(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect),
2538 aMax, aSourceNode);
2541 IntRect FilterNodeConvolveMatrixSoftware::InflatedSourceRect(
2542 const IntRect& aDestRect) {
2543 if (aDestRect.IsEmpty()) {
2544 return IntRect();
2547 IntMargin margin;
2548 margin.left = static_cast<int32_t>(ceil(mTarget.x * mKernelUnitLength.width));
2549 margin.top = static_cast<int32_t>(ceil(mTarget.y * mKernelUnitLength.height));
2550 margin.right = static_cast<int32_t>(
2551 ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width));
2552 margin.bottom = static_cast<int32_t>(
2553 ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height));
2555 IntRect srcRect = aDestRect;
2556 srcRect.Inflate(margin);
2557 return srcRect;
2560 IntRect FilterNodeConvolveMatrixSoftware::InflatedDestRect(
2561 const IntRect& aSourceRect) {
2562 if (aSourceRect.IsEmpty()) {
2563 return IntRect();
2566 IntMargin margin;
2567 margin.left = static_cast<int32_t>(
2568 ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width));
2569 margin.top = static_cast<int32_t>(
2570 ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height));
2571 margin.right =
2572 static_cast<int32_t>(ceil(mTarget.x * mKernelUnitLength.width));
2573 margin.bottom =
2574 static_cast<int32_t>(ceil(mTarget.y * mKernelUnitLength.height));
2576 IntRect destRect = aSourceRect;
2577 destRect.Inflate(margin);
2578 return destRect;
2581 IntRect FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(
2582 const IntRect& aRect) {
2583 IntRect srcRequest = InflatedSourceRect(aRect);
2584 IntRect srcOutput = GetInputRectInRect(IN_CONVOLVE_MATRIX_IN, srcRequest);
2585 return InflatedDestRect(srcOutput).Intersect(aRect);
2588 FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware()
2589 : mScale(0.0f), mChannelX(COLOR_CHANNEL_R), mChannelY(COLOR_CHANNEL_G) {}
2591 int32_t FilterNodeDisplacementMapSoftware::InputIndex(
2592 uint32_t aInputEnumIndex) {
2593 switch (aInputEnumIndex) {
2594 case IN_DISPLACEMENT_MAP_IN:
2595 return 0;
2596 case IN_DISPLACEMENT_MAP_IN2:
2597 return 1;
2598 default:
2599 return -1;
2603 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
2604 Float aScale) {
2605 MOZ_ASSERT(aIndex == ATT_DISPLACEMENT_MAP_SCALE);
2606 mScale = aScale;
2607 Invalidate();
2610 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
2611 uint32_t aValue) {
2612 switch (aIndex) {
2613 case ATT_DISPLACEMENT_MAP_X_CHANNEL:
2614 mChannelX = static_cast<ColorChannel>(aValue);
2615 break;
2616 case ATT_DISPLACEMENT_MAP_Y_CHANNEL:
2617 mChannelY = static_cast<ColorChannel>(aValue);
2618 break;
2619 default:
2620 MOZ_CRASH("GFX: FilterNodeDisplacementMapSoftware::SetAttribute");
2622 Invalidate();
2625 already_AddRefed<DataSourceSurface> FilterNodeDisplacementMapSoftware::Render(
2626 const IntRect& aRect) {
2627 IntRect srcRect = InflatedSourceOrDestRect(aRect);
2628 RefPtr<DataSourceSurface> input = GetInputDataSourceSurface(
2629 IN_DISPLACEMENT_MAP_IN, srcRect, NEED_COLOR_CHANNELS);
2630 RefPtr<DataSourceSurface> map = GetInputDataSourceSurface(
2631 IN_DISPLACEMENT_MAP_IN2, aRect, NEED_COLOR_CHANNELS);
2632 RefPtr<DataSourceSurface> target =
2633 Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
2634 if (MOZ2D_WARN_IF(!(input && map && target))) {
2635 return nullptr;
2638 IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
2640 DataSourceSurface::ScopedMap inputMap(input, DataSourceSurface::READ);
2641 DataSourceSurface::ScopedMap mapMap(map, DataSourceSurface::READ);
2642 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
2643 if (MOZ2D_WARN_IF(!(inputMap.IsMapped() && mapMap.IsMapped() &&
2644 targetMap.IsMapped()))) {
2645 return nullptr;
2648 uint8_t* sourceData =
2649 DataAtOffset(input, inputMap.GetMappedSurface(), offset);
2650 int32_t sourceStride = inputMap.GetStride();
2651 uint8_t* sourceBegin = inputMap.GetData();
2652 uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
2653 uint8_t* mapData = mapMap.GetData();
2654 int32_t mapStride = mapMap.GetStride();
2655 uint8_t* targetData = targetMap.GetData();
2656 int32_t targetStride = targetMap.GetStride();
2658 static const ptrdiff_t channelMap[4] = {
2659 B8G8R8A8_COMPONENT_BYTEOFFSET_R, B8G8R8A8_COMPONENT_BYTEOFFSET_G,
2660 B8G8R8A8_COMPONENT_BYTEOFFSET_B, B8G8R8A8_COMPONENT_BYTEOFFSET_A};
2661 uint16_t xChannel = channelMap[mChannelX];
2662 uint16_t yChannel = channelMap[mChannelY];
2664 float scaleOver255 = mScale / 255.0f;
2665 float scaleAdjustment = -0.5f * mScale;
2667 for (int32_t y = 0; y < aRect.Height(); y++) {
2668 for (int32_t x = 0; x < aRect.Width(); x++) {
2669 uint32_t mapIndex = y * mapStride + 4 * x;
2670 uint32_t targIndex = y * targetStride + 4 * x;
2671 int32_t sourceX =
2672 x + scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment;
2673 int32_t sourceY =
2674 y + scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment;
2675 *(uint32_t*)(targetData + targIndex) = ColorAtPoint(
2676 sourceData, sourceStride, sourceBegin, sourceEnd, sourceX, sourceY);
2679 // Keep valgrind happy.
2680 PodZero(&targetData[y * targetStride + 4 * aRect.Width()],
2681 targetStride - 4 * aRect.Width());
2684 return target.forget();
2687 void FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(
2688 const IntRect& aRect) {
2689 RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect));
2690 RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect);
2693 IntRect FilterNodeDisplacementMapSoftware::MapRectToSource(
2694 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
2695 IntRect result =
2696 MapInputRectToSource(IN_DISPLACEMENT_MAP_IN,
2697 InflatedSourceOrDestRect(aRect), aMax, aSourceNode);
2698 result.OrWith(
2699 MapInputRectToSource(IN_DISPLACEMENT_MAP_IN2, aRect, aMax, aSourceNode));
2700 return result;
2703 IntRect FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(
2704 const IntRect& aDestOrSourceRect) {
2705 IntRect sourceOrDestRect = aDestOrSourceRect;
2706 sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2));
2707 return sourceOrDestRect;
2710 IntRect FilterNodeDisplacementMapSoftware::GetOutputRectInRect(
2711 const IntRect& aRect) {
2712 IntRect srcRequest = InflatedSourceOrDestRect(aRect);
2713 IntRect srcOutput = GetInputRectInRect(IN_DISPLACEMENT_MAP_IN, srcRequest);
2714 return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
2717 FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware()
2718 : mNumOctaves(0),
2719 mSeed(0),
2720 mStitchable(false),
2721 mType(TURBULENCE_TYPE_TURBULENCE) {}
2723 int32_t FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex) {
2724 return -1;
2727 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
2728 const Size& aBaseFrequency) {
2729 switch (aIndex) {
2730 case ATT_TURBULENCE_BASE_FREQUENCY:
2731 mBaseFrequency = aBaseFrequency;
2732 break;
2733 default:
2734 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2735 break;
2737 Invalidate();
2740 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
2741 const IntRect& aRect) {
2742 switch (aIndex) {
2743 case ATT_TURBULENCE_RECT:
2744 mRenderRect = aRect;
2745 break;
2746 default:
2747 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2748 break;
2750 Invalidate();
2753 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
2754 bool aStitchable) {
2755 MOZ_ASSERT(aIndex == ATT_TURBULENCE_STITCHABLE);
2756 mStitchable = aStitchable;
2757 Invalidate();
2760 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
2761 uint32_t aValue) {
2762 switch (aIndex) {
2763 case ATT_TURBULENCE_NUM_OCTAVES:
2764 mNumOctaves = aValue;
2765 break;
2766 case ATT_TURBULENCE_SEED:
2767 mSeed = aValue;
2768 break;
2769 case ATT_TURBULENCE_TYPE:
2770 mType = static_cast<TurbulenceType>(aValue);
2771 break;
2772 default:
2773 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2774 break;
2776 Invalidate();
2779 already_AddRefed<DataSourceSurface> FilterNodeTurbulenceSoftware::Render(
2780 const IntRect& aRect) {
2781 return FilterProcessing::RenderTurbulence(
2782 aRect.Size(), aRect.TopLeft(), mBaseFrequency, mSeed, mNumOctaves, mType,
2783 mStitchable, Rect(mRenderRect));
2786 IntRect FilterNodeTurbulenceSoftware::GetOutputRectInRect(
2787 const IntRect& aRect) {
2788 return aRect.Intersect(mRenderRect);
2791 IntRect FilterNodeTurbulenceSoftware::MapRectToSource(const IntRect& aRect,
2792 const IntRect& aMax,
2793 FilterNode* aSourceNode) {
2794 return IntRect();
2797 FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware()
2798 : mK1(0), mK2(0), mK3(0), mK4(0) {}
2800 int32_t FilterNodeArithmeticCombineSoftware::InputIndex(
2801 uint32_t aInputEnumIndex) {
2802 switch (aInputEnumIndex) {
2803 case IN_ARITHMETIC_COMBINE_IN:
2804 return 0;
2805 case IN_ARITHMETIC_COMBINE_IN2:
2806 return 1;
2807 default:
2808 return -1;
2812 void FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex,
2813 const Float* aFloat,
2814 uint32_t aSize) {
2815 MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS);
2816 MOZ_RELEASE_ASSERT(aSize == 4);
2818 mK1 = aFloat[0];
2819 mK2 = aFloat[1];
2820 mK3 = aFloat[2];
2821 mK4 = aFloat[3];
2823 Invalidate();
2826 already_AddRefed<DataSourceSurface> FilterNodeArithmeticCombineSoftware::Render(
2827 const IntRect& aRect) {
2828 RefPtr<DataSourceSurface> input1 = GetInputDataSourceSurface(
2829 IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS);
2830 RefPtr<DataSourceSurface> input2 = GetInputDataSourceSurface(
2831 IN_ARITHMETIC_COMBINE_IN2, aRect, NEED_COLOR_CHANNELS);
2832 if (!input1 && !input2) {
2833 return nullptr;
2836 // If one input is null, treat it as transparent by adjusting the factors.
2837 Float k1 = mK1, k2 = mK2, k3 = mK3, k4 = mK4;
2838 if (!input1) {
2839 k1 = 0.0f;
2840 k2 = 0.0f;
2841 input1 = input2;
2844 if (!input2) {
2845 k1 = 0.0f;
2846 k3 = 0.0f;
2847 input2 = input1;
2850 return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3,
2851 k4);
2854 void FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(
2855 const IntRect& aRect) {
2856 RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect);
2857 RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect);
2860 IntRect FilterNodeArithmeticCombineSoftware::MapRectToSource(
2861 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
2862 IntRect result =
2863 MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN, aRect, aMax, aSourceNode);
2864 result.OrWith(MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN2, aRect, aMax,
2865 aSourceNode));
2866 return result;
2869 IntRect FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(
2870 const IntRect& aRect) {
2871 if (mK4 > 0.0f) {
2872 return aRect;
2874 IntRect rectFrom1 =
2875 GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect);
2876 IntRect rectFrom2 =
2877 GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2, aRect).Intersect(aRect);
2878 IntRect result;
2879 if (mK1 > 0.0f) {
2880 result = rectFrom1.Intersect(rectFrom2);
2882 if (mK2 > 0.0f) {
2883 result = result.Union(rectFrom1);
2885 if (mK3 > 0.0f) {
2886 result = result.Union(rectFrom2);
2888 return result;
2891 FilterNodeCompositeSoftware::FilterNodeCompositeSoftware()
2892 : mOperator(COMPOSITE_OPERATOR_OVER) {}
2894 int32_t FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex) {
2895 return aInputEnumIndex - IN_COMPOSITE_IN_START;
2898 void FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex,
2899 uint32_t aCompositeOperator) {
2900 MOZ_ASSERT(aIndex == ATT_COMPOSITE_OPERATOR);
2901 mOperator = static_cast<CompositeOperator>(aCompositeOperator);
2902 Invalidate();
2905 already_AddRefed<DataSourceSurface> FilterNodeCompositeSoftware::Render(
2906 const IntRect& aRect) {
2907 RefPtr<DataSourceSurface> start = GetInputDataSourceSurface(
2908 IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS);
2909 RefPtr<DataSourceSurface> dest = Factory::CreateDataSourceSurface(
2910 aRect.Size(), SurfaceFormat::B8G8R8A8, true);
2911 if (MOZ2D_WARN_IF(!dest)) {
2912 return nullptr;
2915 if (start) {
2916 CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint());
2919 for (size_t inputIndex = 1; inputIndex < NumberOfSetInputs(); inputIndex++) {
2920 RefPtr<DataSourceSurface> input = GetInputDataSourceSurface(
2921 IN_COMPOSITE_IN_START + inputIndex, aRect, NEED_COLOR_CHANNELS);
2922 if (input) {
2923 FilterProcessing::ApplyComposition(input, dest, mOperator);
2924 } else {
2925 // We need to treat input as transparent. Depending on the composite
2926 // operator, different things happen to dest.
2927 switch (mOperator) {
2928 case COMPOSITE_OPERATOR_OVER:
2929 case COMPOSITE_OPERATOR_ATOP:
2930 case COMPOSITE_OPERATOR_XOR:
2931 case COMPOSITE_OPERATOR_LIGHTER:
2932 // dest is unchanged.
2933 break;
2934 case COMPOSITE_OPERATOR_OUT:
2935 // dest is now transparent, but it can become non-transparent again
2936 // when compositing additional inputs.
2937 ClearDataSourceSurface(dest);
2938 break;
2939 case COMPOSITE_OPERATOR_IN:
2940 // Transparency always wins. We're completely transparent now and
2941 // no additional input can get rid of that transparency.
2942 return nullptr;
2946 return dest.forget();
2949 void FilterNodeCompositeSoftware::RequestFromInputsForRect(
2950 const IntRect& aRect) {
2951 for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
2952 RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
2956 IntRect FilterNodeCompositeSoftware::MapRectToSource(const IntRect& aRect,
2957 const IntRect& aMax,
2958 FilterNode* aSourceNode) {
2959 IntRect result;
2960 for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
2961 result.OrWith(MapInputRectToSource(IN_COMPOSITE_IN_START + inputIndex,
2962 aRect, aMax, aSourceNode));
2964 return result;
2967 IntRect FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect& aRect) {
2968 IntRect rect;
2969 for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
2970 IntRect inputRect =
2971 GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
2972 if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) {
2973 rect = rect.Intersect(inputRect);
2974 } else {
2975 rect = rect.Union(inputRect);
2978 return rect;
2981 int32_t FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex) {
2982 switch (aInputEnumIndex) {
2983 case IN_GAUSSIAN_BLUR_IN:
2984 return 0;
2985 default:
2986 return -1;
2990 already_AddRefed<DataSourceSurface> FilterNodeBlurXYSoftware::Render(
2991 const IntRect& aRect) {
2992 Size sigmaXY = StdDeviationXY();
2993 IntSize d =
2994 AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
2996 if (d.width == 0 && d.height == 0) {
2997 return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, aRect);
3000 IntRect srcRect = InflatedSourceOrDestRect(aRect);
3001 RefPtr<DataSourceSurface> input =
3002 GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, srcRect);
3003 if (!input) {
3004 return nullptr;
3007 RefPtr<DataSourceSurface> target;
3008 Rect r(0, 0, srcRect.Width(), srcRect.Height());
3010 if (input->GetFormat() == SurfaceFormat::A8) {
3011 target =
3012 Factory::CreateDataSourceSurface(srcRect.Size(), SurfaceFormat::A8);
3013 if (MOZ2D_WARN_IF(!target)) {
3014 return nullptr;
3016 CopyRect(input, target, IntRect(IntPoint(), input->GetSize()), IntPoint());
3018 DataSourceSurface::ScopedMap targetMap(target,
3019 DataSourceSurface::READ_WRITE);
3020 if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
3021 return nullptr;
3023 AlphaBoxBlur blur(r, targetMap.GetStride(), sigmaXY.width, sigmaXY.height);
3024 blur.Blur(targetMap.GetData());
3025 } else {
3026 RefPtr<DataSourceSurface> channel0, channel1, channel2, channel3;
3027 FilterProcessing::SeparateColorChannels(input, channel0, channel1, channel2,
3028 channel3);
3029 if (MOZ2D_WARN_IF(!(channel0 && channel1 && channel2 && channel3))) {
3030 return nullptr;
3033 DataSourceSurface::ScopedMap channel0Map(channel0,
3034 DataSourceSurface::READ_WRITE);
3035 DataSourceSurface::ScopedMap channel1Map(channel1,
3036 DataSourceSurface::READ_WRITE);
3037 DataSourceSurface::ScopedMap channel2Map(channel2,
3038 DataSourceSurface::READ_WRITE);
3039 DataSourceSurface::ScopedMap channel3Map(channel3,
3040 DataSourceSurface::READ_WRITE);
3041 if (MOZ2D_WARN_IF(!(channel0Map.IsMapped() && channel1Map.IsMapped() &&
3042 channel2Map.IsMapped() && channel3Map.IsMapped()))) {
3043 return nullptr;
3046 AlphaBoxBlur blur(r, channel0Map.GetStride(), sigmaXY.width,
3047 sigmaXY.height);
3048 blur.Blur(channel0Map.GetData());
3049 blur.Blur(channel1Map.GetData());
3050 blur.Blur(channel2Map.GetData());
3051 blur.Blur(channel3Map.GetData());
3053 target = FilterProcessing::CombineColorChannels(channel0, channel1,
3054 channel2, channel3);
3057 return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE);
3060 void FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect& aRect) {
3061 RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect));
3064 IntRect FilterNodeBlurXYSoftware::MapRectToSource(const IntRect& aRect,
3065 const IntRect& aMax,
3066 FilterNode* aSourceNode) {
3067 return MapInputRectToSource(
3068 IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect), aMax, aSourceNode);
3071 IntRect FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(
3072 const IntRect& aDestRect) {
3073 Size sigmaXY = StdDeviationXY();
3074 IntSize d =
3075 AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
3076 IntRect srcRect = aDestRect;
3077 srcRect.Inflate(d);
3078 return srcRect;
3081 IntRect FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect& aRect) {
3082 IntRect srcRequest = InflatedSourceOrDestRect(aRect);
3083 IntRect srcOutput = GetInputRectInRect(IN_GAUSSIAN_BLUR_IN, srcRequest);
3084 return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
3087 FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware()
3088 : mStdDeviation(0) {}
3090 static float ClampStdDeviation(float aStdDeviation) {
3091 // Cap software blur radius for performance reasons.
3092 return std::clamp(aStdDeviation, 0.f, 100.f);
3095 void FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex,
3096 float aStdDeviation) {
3097 switch (aIndex) {
3098 case ATT_GAUSSIAN_BLUR_STD_DEVIATION:
3099 mStdDeviation = ClampStdDeviation(aStdDeviation);
3100 break;
3101 default:
3102 MOZ_CRASH("GFX: FilterNodeGaussianBlurSoftware::SetAttribute");
3104 Invalidate();
3107 Size FilterNodeGaussianBlurSoftware::StdDeviationXY() {
3108 return Size(mStdDeviation, mStdDeviation);
3111 FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware()
3112 : mStdDeviation(0.0), mBlurDirection(BLUR_DIRECTION_X) {}
3114 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
3115 Float aStdDeviation) {
3116 switch (aIndex) {
3117 case ATT_DIRECTIONAL_BLUR_STD_DEVIATION:
3118 mStdDeviation = ClampStdDeviation(aStdDeviation);
3119 break;
3120 default:
3121 MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
3123 Invalidate();
3126 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
3127 uint32_t aBlurDirection) {
3128 switch (aIndex) {
3129 case ATT_DIRECTIONAL_BLUR_DIRECTION:
3130 mBlurDirection = (BlurDirection)aBlurDirection;
3131 break;
3132 default:
3133 MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
3135 Invalidate();
3138 Size FilterNodeDirectionalBlurSoftware::StdDeviationXY() {
3139 float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0;
3140 float sigmaY = mBlurDirection == BLUR_DIRECTION_Y ? mStdDeviation : 0;
3141 return Size(sigmaX, sigmaY);
3144 int32_t FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex) {
3145 switch (aInputEnumIndex) {
3146 case IN_CROP_IN:
3147 return 0;
3148 default:
3149 return -1;
3153 void FilterNodeCropSoftware::SetAttribute(uint32_t aIndex,
3154 const Rect& aSourceRect) {
3155 MOZ_ASSERT(aIndex == ATT_CROP_RECT);
3156 Rect srcRect = aSourceRect;
3157 srcRect.Round();
3158 if (!srcRect.ToIntRect(&mCropRect)) {
3159 mCropRect = IntRect();
3161 Invalidate();
3164 already_AddRefed<DataSourceSurface> FilterNodeCropSoftware::Render(
3165 const IntRect& aRect) {
3166 return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect));
3169 void FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect& aRect) {
3170 RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect));
3173 IntRect FilterNodeCropSoftware::MapRectToSource(const IntRect& aRect,
3174 const IntRect& aMax,
3175 FilterNode* aSourceNode) {
3176 return MapInputRectToSource(IN_CROP_IN, aRect.Intersect(mCropRect), aMax,
3177 aSourceNode);
3180 IntRect FilterNodeCropSoftware::GetOutputRectInRect(const IntRect& aRect) {
3181 return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect);
3184 int32_t FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) {
3185 switch (aInputEnumIndex) {
3186 case IN_PREMULTIPLY_IN:
3187 return 0;
3188 default:
3189 return -1;
3193 already_AddRefed<DataSourceSurface> FilterNodePremultiplySoftware::Render(
3194 const IntRect& aRect) {
3195 RefPtr<DataSourceSurface> input =
3196 GetInputDataSourceSurface(IN_PREMULTIPLY_IN, aRect);
3197 return input ? Premultiply(input) : nullptr;
3200 void FilterNodePremultiplySoftware::RequestFromInputsForRect(
3201 const IntRect& aRect) {
3202 RequestInputRect(IN_PREMULTIPLY_IN, aRect);
3205 IntRect FilterNodePremultiplySoftware::MapRectToSource(
3206 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
3207 return MapInputRectToSource(IN_PREMULTIPLY_IN, aRect, aMax, aSourceNode);
3210 IntRect FilterNodePremultiplySoftware::GetOutputRectInRect(
3211 const IntRect& aRect) {
3212 return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect);
3215 int32_t FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) {
3216 switch (aInputEnumIndex) {
3217 case IN_UNPREMULTIPLY_IN:
3218 return 0;
3219 default:
3220 return -1;
3224 already_AddRefed<DataSourceSurface> FilterNodeUnpremultiplySoftware::Render(
3225 const IntRect& aRect) {
3226 RefPtr<DataSourceSurface> input =
3227 GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN, aRect);
3228 return input ? Unpremultiply(input) : nullptr;
3231 void FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(
3232 const IntRect& aRect) {
3233 RequestInputRect(IN_UNPREMULTIPLY_IN, aRect);
3236 IntRect FilterNodeUnpremultiplySoftware::MapRectToSource(
3237 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
3238 return MapInputRectToSource(IN_UNPREMULTIPLY_IN, aRect, aMax, aSourceNode);
3241 IntRect FilterNodeUnpremultiplySoftware::GetOutputRectInRect(
3242 const IntRect& aRect) {
3243 return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect);
3246 void FilterNodeOpacitySoftware::SetAttribute(uint32_t aIndex, Float aValue) {
3247 MOZ_ASSERT(aIndex == ATT_OPACITY_VALUE);
3248 mValue = aValue;
3249 Invalidate();
3252 int32_t FilterNodeOpacitySoftware::InputIndex(uint32_t aInputEnumIndex) {
3253 switch (aInputEnumIndex) {
3254 case IN_OPACITY_IN:
3255 return 0;
3256 default:
3257 return -1;
3261 already_AddRefed<DataSourceSurface> FilterNodeOpacitySoftware::Render(
3262 const IntRect& aRect) {
3263 RefPtr<DataSourceSurface> input =
3264 GetInputDataSourceSurface(IN_OPACITY_IN, aRect);
3265 return input ? Opacity(input, mValue) : nullptr;
3268 void FilterNodeOpacitySoftware::RequestFromInputsForRect(const IntRect& aRect) {
3269 RequestInputRect(IN_OPACITY_IN, aRect);
3272 IntRect FilterNodeOpacitySoftware::MapRectToSource(const IntRect& aRect,
3273 const IntRect& aMax,
3274 FilterNode* aSourceNode) {
3275 return MapInputRectToSource(IN_OPACITY_IN, aRect, aMax, aSourceNode);
3278 IntRect FilterNodeOpacitySoftware::GetOutputRectInRect(const IntRect& aRect) {
3279 return GetInputRectInRect(IN_OPACITY_IN, aRect);
3282 bool PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D& aPoint) {
3283 switch (aIndex) {
3284 case ATT_POINT_LIGHT_POSITION:
3285 mPosition = aPoint;
3286 break;
3287 default:
3288 return false;
3290 return true;
3293 SpotLightSoftware::SpotLightSoftware()
3294 : mSpecularFocus(0), mLimitingConeAngle(0), mLimitingConeCos(1) {}
3296 bool SpotLightSoftware::SetAttribute(uint32_t aIndex, const Point3D& aPoint) {
3297 switch (aIndex) {
3298 case ATT_SPOT_LIGHT_POSITION:
3299 mPosition = aPoint;
3300 break;
3301 case ATT_SPOT_LIGHT_POINTS_AT:
3302 mPointsAt = aPoint;
3303 break;
3304 default:
3305 return false;
3307 return true;
3310 bool SpotLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
3311 switch (aIndex) {
3312 case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE:
3313 mLimitingConeAngle = aValue;
3314 break;
3315 case ATT_SPOT_LIGHT_FOCUS:
3316 mSpecularFocus = aValue;
3317 break;
3318 default:
3319 return false;
3321 return true;
3324 DistantLightSoftware::DistantLightSoftware() : mAzimuth(0), mElevation(0) {}
3326 bool DistantLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
3327 switch (aIndex) {
3328 case ATT_DISTANT_LIGHT_AZIMUTH:
3329 mAzimuth = aValue;
3330 break;
3331 case ATT_DISTANT_LIGHT_ELEVATION:
3332 mElevation = aValue;
3333 break;
3334 default:
3335 return false;
3337 return true;
3340 static inline Point3D Normalized(const Point3D& vec) {
3341 Point3D copy(vec);
3342 copy.Normalize();
3343 return copy;
3346 template <typename LightType, typename LightingType>
3347 FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware(
3348 const char* aTypeName)
3349 : mSurfaceScale(0)
3350 #if defined(MOZILLA_INTERNAL_API) && defined(NS_BUILD_REFCNT_LOGGING)
3352 mTypeName(aTypeName)
3353 #endif
3357 template <typename LightType, typename LightingType>
3358 int32_t FilterNodeLightingSoftware<LightType, LightingType>::InputIndex(
3359 uint32_t aInputEnumIndex) {
3360 switch (aInputEnumIndex) {
3361 case IN_LIGHTING_IN:
3362 return 0;
3363 default:
3364 return -1;
3368 template <typename LightType, typename LightingType>
3369 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
3370 uint32_t aIndex, const Point3D& aPoint) {
3371 if (mLight.SetAttribute(aIndex, aPoint)) {
3372 Invalidate();
3373 return;
3375 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute point");
3378 template <typename LightType, typename LightingType>
3379 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
3380 uint32_t aIndex, Float aValue) {
3381 if (mLight.SetAttribute(aIndex, aValue) ||
3382 mLighting.SetAttribute(aIndex, aValue)) {
3383 Invalidate();
3384 return;
3386 switch (aIndex) {
3387 case ATT_LIGHTING_SURFACE_SCALE:
3388 mSurfaceScale = std::fpclassify(aValue) == FP_SUBNORMAL ? 0.0 : aValue;
3389 break;
3390 default:
3391 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float");
3393 Invalidate();
3396 template <typename LightType, typename LightingType>
3397 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
3398 uint32_t aIndex, const Size& aKernelUnitLength) {
3399 switch (aIndex) {
3400 case ATT_LIGHTING_KERNEL_UNIT_LENGTH:
3401 mKernelUnitLength = aKernelUnitLength;
3402 break;
3403 default:
3404 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size");
3406 Invalidate();
3409 template <typename LightType, typename LightingType>
3410 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
3411 uint32_t aIndex, const DeviceColor& aColor) {
3412 MOZ_ASSERT(aIndex == ATT_LIGHTING_COLOR);
3413 mColor = aColor;
3414 Invalidate();
3417 template <typename LightType, typename LightingType>
3418 IntRect
3419 FilterNodeLightingSoftware<LightType, LightingType>::GetOutputRectInRect(
3420 const IntRect& aRect) {
3421 return aRect;
3424 Point3D PointLightSoftware::GetVectorToLight(const Point3D& aTargetPoint) {
3425 return Normalized(mPosition - aTargetPoint);
3428 uint32_t PointLightSoftware::GetColor(uint32_t aLightColor,
3429 const Point3D& aVectorToLight) {
3430 return aLightColor;
3433 void SpotLightSoftware::Prepare() {
3434 mVectorFromFocusPointToLight = Normalized(mPointsAt - mPosition);
3435 mLimitingConeCos =
3436 std::max<double>(cos(mLimitingConeAngle * M_PI / 180.0), 0.0);
3437 mPowCache.CacheForExponent(mSpecularFocus);
3440 Point3D SpotLightSoftware::GetVectorToLight(const Point3D& aTargetPoint) {
3441 return Normalized(mPosition - aTargetPoint);
3444 uint32_t SpotLightSoftware::GetColor(uint32_t aLightColor,
3445 const Point3D& aVectorToLight) {
3446 union {
3447 uint32_t color;
3448 uint8_t colorC[4];
3451 Float dot = -aVectorToLight.DotProduct(mVectorFromFocusPointToLight);
3452 if (!mPowCache.HasPowerTable()) {
3453 dot *= (dot >= mLimitingConeCos);
3454 color = aLightColor;
3455 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] *= dot;
3456 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] *= dot;
3457 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] *= dot;
3458 } else {
3459 color = aLightColor;
3460 uint16_t doti = dot * (dot >= 0) * (1 << PowCache::sInputIntPrecisionBits);
3461 uint32_t tmp = mPowCache.Pow(doti) * (dot >= mLimitingConeCos);
3462 MOZ_ASSERT(tmp <= (1 << PowCache::sOutputIntPrecisionBits),
3463 "pow() result must not exceed 1.0");
3464 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
3465 uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] * tmp) >>
3466 PowCache::sOutputIntPrecisionBits);
3467 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
3468 uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] * tmp) >>
3469 PowCache::sOutputIntPrecisionBits);
3470 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
3471 uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] * tmp) >>
3472 PowCache::sOutputIntPrecisionBits);
3474 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
3475 return color;
3478 void DistantLightSoftware::Prepare() {
3479 const double radPerDeg = M_PI / 180.0;
3480 mVectorToLight.x = cos(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
3481 mVectorToLight.y = sin(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
3482 mVectorToLight.z = sin(mElevation * radPerDeg);
3485 Point3D DistantLightSoftware::GetVectorToLight(const Point3D& aTargetPoint) {
3486 return mVectorToLight;
3489 uint32_t DistantLightSoftware::GetColor(uint32_t aLightColor,
3490 const Point3D& aVectorToLight) {
3491 return aLightColor;
3494 template <typename CoordType>
3495 static Point3D GenerateNormal(const uint8_t* data, int32_t stride,
3496 uint8_t* boundsBegin, uint8_t* boundsEnd,
3497 int32_t x, int32_t y, float surfaceScale,
3498 CoordType dx, CoordType dy) {
3499 const uint8_t* index = data + y * stride + x;
3501 CoordType zero = 0;
3503 // See this for source of constants:
3504 // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
3505 int16_t normalX = -1 * ColorComponentAtPoint(index, stride, boundsBegin,
3506 boundsEnd, -dx, -dy, 1, 0) +
3507 1 * ColorComponentAtPoint(index, stride, boundsBegin,
3508 boundsEnd, dx, -dy, 1, 0) +
3509 -2 * ColorComponentAtPoint(index, stride, boundsBegin,
3510 boundsEnd, -dx, zero, 1, 0) +
3511 2 * ColorComponentAtPoint(index, stride, boundsBegin,
3512 boundsEnd, dx, zero, 1, 0) +
3513 -1 * ColorComponentAtPoint(index, stride, boundsBegin,
3514 boundsEnd, -dx, dy, 1, 0) +
3515 1 * ColorComponentAtPoint(index, stride, boundsBegin,
3516 boundsEnd, dx, dy, 1, 0);
3518 int16_t normalY = -1 * ColorComponentAtPoint(index, stride, boundsBegin,
3519 boundsEnd, -dx, -dy, 1, 0) +
3520 -2 * ColorComponentAtPoint(index, stride, boundsBegin,
3521 boundsEnd, zero, -dy, 1, 0) +
3522 -1 * ColorComponentAtPoint(index, stride, boundsBegin,
3523 boundsEnd, dx, -dy, 1, 0) +
3524 1 * ColorComponentAtPoint(index, stride, boundsBegin,
3525 boundsEnd, -dx, dy, 1, 0) +
3526 2 * ColorComponentAtPoint(index, stride, boundsBegin,
3527 boundsEnd, zero, dy, 1, 0) +
3528 1 * ColorComponentAtPoint(index, stride, boundsBegin,
3529 boundsEnd, dx, dy, 1, 0);
3531 Point3D normal;
3532 normal.x = -surfaceScale * normalX / 4.0f;
3533 normal.y = -surfaceScale * normalY / 4.0f;
3534 normal.z = 255;
3535 return Normalized(normal);
3538 template <typename LightType, typename LightingType>
3539 already_AddRefed<DataSourceSurface>
3540 FilterNodeLightingSoftware<LightType, LightingType>::Render(
3541 const IntRect& aRect) {
3542 if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
3543 mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
3544 return DoRender(aRect, (int32_t)mKernelUnitLength.width,
3545 (int32_t)mKernelUnitLength.height);
3547 return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
3550 template <typename LightType, typename LightingType>
3551 void FilterNodeLightingSoftware<
3552 LightType, LightingType>::RequestFromInputsForRect(const IntRect& aRect) {
3553 IntRect srcRect = aRect;
3554 srcRect.Inflate(ceil(mKernelUnitLength.width),
3555 ceil(mKernelUnitLength.height));
3556 RequestInputRect(IN_LIGHTING_IN, srcRect);
3559 template <typename LightType, typename LightingType>
3560 IntRect FilterNodeLightingSoftware<LightType, LightingType>::MapRectToSource(
3561 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
3562 IntRect srcRect = aRect;
3563 srcRect.Inflate(ceil(mKernelUnitLength.width),
3564 ceil(mKernelUnitLength.height));
3565 return MapInputRectToSource(IN_LIGHTING_IN, srcRect, aMax, aSourceNode);
3568 template <typename LightType, typename LightingType>
3569 template <typename CoordType>
3570 already_AddRefed<DataSourceSurface>
3571 FilterNodeLightingSoftware<LightType, LightingType>::DoRender(
3572 const IntRect& aRect, CoordType aKernelUnitLengthX,
3573 CoordType aKernelUnitLengthY) {
3574 MOZ_ASSERT(aKernelUnitLengthX > 0,
3575 "aKernelUnitLengthX can be a negative or zero value");
3576 MOZ_ASSERT(aKernelUnitLengthY > 0,
3577 "aKernelUnitLengthY can be a negative or zero value");
3579 IntRect srcRect = aRect;
3580 IntSize size = aRect.Size();
3581 srcRect.Inflate(ceil(float(aKernelUnitLengthX)),
3582 ceil(float(aKernelUnitLengthY)));
3584 // Inflate the source rect by another pixel because the bilinear filtering in
3585 // ColorComponentAtPoint may want to access the margins.
3586 srcRect.Inflate(1);
3588 RefPtr<DataSourceSurface> input = GetInputDataSourceSurface(
3589 IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8, EDGE_MODE_NONE);
3591 if (!input) {
3592 return nullptr;
3595 if (input->GetFormat() != SurfaceFormat::A8) {
3596 input = FilterProcessing::ExtractAlpha(input);
3599 RefPtr<DataSourceSurface> target =
3600 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
3601 if (MOZ2D_WARN_IF(!target)) {
3602 return nullptr;
3605 IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
3607 DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
3608 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
3609 if (MOZ2D_WARN_IF(!(sourceMap.IsMapped() && targetMap.IsMapped()))) {
3610 return nullptr;
3613 uint8_t* sourceData =
3614 DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
3615 int32_t sourceStride = sourceMap.GetStride();
3616 uint8_t* sourceBegin = sourceMap.GetData();
3617 uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
3618 uint8_t* targetData = targetMap.GetData();
3619 int32_t targetStride = targetMap.GetStride();
3621 uint32_t lightColor = ColorToBGRA(mColor);
3622 mLight.Prepare();
3623 mLighting.Prepare();
3625 for (int32_t y = 0; y < size.height; y++) {
3626 for (int32_t x = 0; x < size.width; x++) {
3627 int32_t sourceIndex = y * sourceStride + x;
3628 int32_t targetIndex = y * targetStride + 4 * x;
3630 Point3D normal =
3631 GenerateNormal(sourceData, sourceStride, sourceBegin, sourceEnd, x, y,
3632 mSurfaceScale, aKernelUnitLengthX, aKernelUnitLengthY);
3634 IntPoint pointInFilterSpace(aRect.X() + x, aRect.Y() + y);
3635 Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f;
3636 Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z);
3637 Point3D rayDir = mLight.GetVectorToLight(pt);
3638 uint32_t color = mLight.GetColor(lightColor, rayDir);
3640 *(uint32_t*)(targetData + targetIndex) =
3641 mLighting.LightPixel(normal, rayDir, color);
3644 // Zero padding to keep valgrind happy.
3645 PodZero(&targetData[y * targetStride + 4 * size.width],
3646 targetStride - 4 * size.width);
3649 return target.forget();
3652 DiffuseLightingSoftware::DiffuseLightingSoftware() : mDiffuseConstant(0) {}
3654 bool DiffuseLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
3655 switch (aIndex) {
3656 case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT:
3657 mDiffuseConstant = aValue;
3658 break;
3659 default:
3660 return false;
3662 return true;
3665 uint32_t DiffuseLightingSoftware::LightPixel(const Point3D& aNormal,
3666 const Point3D& aVectorToLight,
3667 uint32_t aColor) {
3668 Float dotNL = std::max(0.0f, aNormal.DotProduct(aVectorToLight));
3669 Float diffuseNL = mDiffuseConstant * dotNL;
3671 union {
3672 uint32_t bgra;
3673 uint8_t components[4];
3674 } color = {aColor};
3675 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = umin(
3676 uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]),
3677 255U);
3678 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = umin(
3679 uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]),
3680 255U);
3681 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = umin(
3682 uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]),
3683 255U);
3684 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
3685 return color.bgra;
3688 SpecularLightingSoftware::SpecularLightingSoftware()
3689 : mSpecularConstant(0), mSpecularExponent(0), mSpecularConstantInt(0) {}
3691 bool SpecularLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
3692 switch (aIndex) {
3693 case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT:
3694 mSpecularConstant = std::clamp(aValue, 0.0f, 255.0f);
3695 break;
3696 case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT:
3697 mSpecularExponent = std::clamp(aValue, 1.0f, 128.0f);
3698 break;
3699 default:
3700 return false;
3702 return true;
3705 void SpecularLightingSoftware::Prepare() {
3706 mPowCache.CacheForExponent(mSpecularExponent);
3707 mSpecularConstantInt = uint32_t(mSpecularConstant * (1 << 8));
3710 uint32_t SpecularLightingSoftware::LightPixel(const Point3D& aNormal,
3711 const Point3D& aVectorToLight,
3712 uint32_t aColor) {
3713 Point3D vectorToEye(0, 0, 1);
3714 Point3D halfwayVector = aVectorToLight + vectorToEye;
3715 Float halfwayLength = halfwayVector.Length();
3716 if (halfwayLength > 0) {
3717 halfwayVector /= halfwayLength;
3719 Float dotNH = aNormal.DotProduct(halfwayVector);
3720 uint16_t dotNHi =
3721 uint16_t(dotNH * (dotNH >= 0) * (1 << PowCache::sInputIntPrecisionBits));
3722 // The exponent for specular is in [1,128] range, so we don't need to check
3723 // and optimize for the "default power table" scenario here.
3724 MOZ_ASSERT(mPowCache.HasPowerTable());
3725 uint32_t specularNHi =
3726 uint32_t(mSpecularConstantInt) * mPowCache.Pow(dotNHi) >> 8;
3728 union {
3729 uint32_t bgra;
3730 uint8_t components[4];
3731 } color = {aColor};
3732 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
3733 umin((specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]) >>
3734 PowCache::sOutputIntPrecisionBits,
3735 255U);
3736 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
3737 umin((specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) >>
3738 PowCache::sOutputIntPrecisionBits,
3739 255U);
3740 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
3741 umin((specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) >>
3742 PowCache::sOutputIntPrecisionBits,
3743 255U);
3745 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
3746 umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B],
3747 umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G],
3748 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]));
3749 return color.bgra;
3752 } // namespace gfx
3753 } // namespace mozilla