Bug 1638136 [wpt PR 23617] - Clipboard API Tests: Move permissions tests to WPT....
[gecko.git] / gfx / 2d / FilterNodeSoftware.cpp
blob54bbaf4dc05cc19d3256fce172e7cc614be784e3
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 // Lock the cache and 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
624 MutexAutoLock lock(mCacheMutex);
626 if (!mCachedRect.Contains(aRect)) {
627 RequestRect(aRect);
628 requestedRect = mRequestedRect;
629 } else {
630 MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?");
631 cachedRect = mCachedRect;
632 cachedOutput = mCachedOutput;
636 if (!cachedOutput) {
637 // Compute the output
638 cachedOutput = Render(requestedRect);
640 // Update the cache for future requests
641 MutexAutoLock lock(mCacheMutex);
643 mCachedOutput = cachedOutput;
644 if (!mCachedOutput) {
645 mCachedRect = IntRect();
646 mRequestedRect = IntRect();
647 return nullptr;
649 mCachedRect = requestedRect;
650 mRequestedRect = IntRect();
652 cachedRect = mCachedRect;
655 return GetDataSurfaceInRect(cachedOutput, cachedRect, aRect, EDGE_MODE_NONE);
658 void FilterNodeSoftware::RequestRect(const IntRect& aRect) {
659 if (mRequestedRect.Contains(aRect)) {
660 // Bail out now. Otherwise pathological filters can spend time exponential
661 // in the number of primitives, e.g. if each primitive takes the
662 // previous primitive as its two inputs.
663 return;
665 mRequestedRect = mRequestedRect.Union(aRect);
666 RequestFromInputsForRect(aRect);
669 IntRect FilterNodeSoftware::MapInputRectToSource(uint32_t aInputEnumIndex,
670 const IntRect& aRect,
671 const IntRect& aMax,
672 FilterNode* aSourceNode) {
673 int32_t inputIndex = InputIndex(aInputEnumIndex);
674 if (inputIndex < 0) {
675 gfxDevCrash(LogReason::FilterInputError)
676 << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
677 return aMax;
679 if ((uint32_t)inputIndex < NumberOfSetInputs()) {
680 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
681 // If we have any input filters call into them to do the mapping,
682 // otherwise we can assume an input surface will be used
683 // and just return aRect.
684 if (filter) {
685 return filter->MapRectToSource(aRect, aMax, aSourceNode);
688 // We have an input surface instead of a filter
689 // so check if we're the target node.
690 if (this == aSourceNode) {
691 return aRect;
693 return IntRect();
696 void FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex,
697 const IntRect& aRect) {
698 if (aRect.Overflows()) {
699 return;
702 int32_t inputIndex = InputIndex(aInputEnumIndex);
703 if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
704 gfxDevCrash(LogReason::FilterInputError)
705 << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
706 return;
708 if (mInputSurfaces[inputIndex]) {
709 return;
711 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
712 MOZ_ASSERT(filter, "missing input");
713 filter->RequestRect(filter->GetOutputRectInRect(aRect));
716 SurfaceFormat FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat,
717 FormatHint aFormatHint) {
718 if (aCurrentFormat == SurfaceFormat::A8 && aFormatHint == CAN_HANDLE_A8) {
719 return SurfaceFormat::A8;
721 return SurfaceFormat::B8G8R8A8;
724 already_AddRefed<DataSourceSurface>
725 FilterNodeSoftware::GetInputDataSourceSurface(
726 uint32_t aInputEnumIndex, const IntRect& aRect, FormatHint aFormatHint,
727 ConvolveMatrixEdgeMode aEdgeMode,
728 const IntRect* aTransparencyPaddedSourceRect) {
729 if (aRect.Overflows()) {
730 return nullptr;
733 #ifdef DEBUG_DUMP_SURFACES
734 printf(
735 "<section><h1>GetInputDataSourceSurface with aRect: %d, %d, %d, "
736 "%d</h1>\n",
737 aRect.x, aRect.y, aRect.Width(), aRect.Height());
738 #endif
739 int32_t inputIndex = InputIndex(aInputEnumIndex);
740 if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
741 gfxDevCrash(LogReason::FilterInputData)
742 << "Invalid data " << inputIndex << " vs. " << NumberOfSetInputs();
743 return nullptr;
746 if (aRect.IsEmpty()) {
747 return nullptr;
750 RefPtr<SourceSurface> surface;
751 IntRect surfaceRect;
753 if (mInputSurfaces[inputIndex]) {
754 // Input from input surface
755 surface = mInputSurfaces[inputIndex];
756 #ifdef DEBUG_DUMP_SURFACES
757 printf("input from input surface:\n");
758 #endif
759 surfaceRect = surface->GetRect();
760 } else {
761 // Input from input filter
762 #ifdef DEBUG_DUMP_SURFACES
763 printf("getting input from input filter %s...\n",
764 mInputFilters[inputIndex]->GetName());
765 #endif
766 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
767 MOZ_ASSERT(filter, "missing input");
768 IntRect inputFilterOutput = filter->GetOutputRectInRect(aRect);
769 if (!inputFilterOutput.IsEmpty()) {
770 surface = filter->GetOutput(inputFilterOutput);
772 #ifdef DEBUG_DUMP_SURFACES
773 printf("input from input filter %s:\n",
774 mInputFilters[inputIndex]->GetName());
775 #endif
776 surfaceRect = inputFilterOutput;
777 MOZ_ASSERT(!surface || surfaceRect.Size() == surface->GetSize());
780 if (surface && surface->GetFormat() == SurfaceFormat::UNKNOWN) {
781 #ifdef DEBUG_DUMP_SURFACES
782 printf("wrong input format</section>\n\n");
783 #endif
784 return nullptr;
787 if (!surfaceRect.IsEmpty() && !surface) {
788 #ifdef DEBUG_DUMP_SURFACES
789 printf(" -- no input --</section>\n\n");
790 #endif
791 return nullptr;
794 if (aTransparencyPaddedSourceRect &&
795 !aTransparencyPaddedSourceRect->IsEmpty()) {
796 IntRect srcRect = aTransparencyPaddedSourceRect->Intersect(aRect);
797 surface =
798 GetDataSurfaceInRect(surface, surfaceRect, srcRect, EDGE_MODE_NONE);
799 surfaceRect = srcRect;
802 RefPtr<DataSourceSurface> result =
803 GetDataSurfaceInRect(surface, surfaceRect, aRect, aEdgeMode);
805 if (result) {
806 // TODO: This isn't safe since we don't have a guarantee
807 // that future Maps will have the same stride
808 DataSourceSurface::MappedSurface map;
809 if (result->Map(DataSourceSurface::READ, &map)) {
810 // Unmap immediately since CloneAligned hasn't been updated
811 // to use the Map API yet. We can still read the stride/data
812 // values as long as we don't try to dereference them.
813 result->Unmap();
814 if (map.mStride != GetAlignedStride<16>(map.mStride, 1) ||
815 reinterpret_cast<uintptr_t>(map.mData) % 16 != 0) {
816 // Align unaligned surface.
817 result = CloneAligned(result);
819 } else {
820 result = nullptr;
824 if (!result) {
825 #ifdef DEBUG_DUMP_SURFACES
826 printf(" -- no input --</section>\n\n");
827 #endif
828 return nullptr;
831 SurfaceFormat currentFormat = result->GetFormat();
832 if (DesiredFormat(currentFormat, aFormatHint) == SurfaceFormat::B8G8R8A8 &&
833 currentFormat != SurfaceFormat::B8G8R8A8) {
834 result = FilterProcessing::ConvertToB8G8R8A8(result);
837 #ifdef DEBUG_DUMP_SURFACES
838 printf("<img src='");
839 gfxUtils::DumpAsDataURL(result);
840 printf("'></section>");
841 #endif
843 MOZ_ASSERT(!result || result->GetSize() == aRect.Size(),
844 "wrong surface size");
846 return result.forget();
849 IntRect FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex,
850 const IntRect& aInRect) {
851 if (aInRect.Overflows()) {
852 return IntRect();
855 int32_t inputIndex = InputIndex(aInputEnumIndex);
856 if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
857 gfxDevCrash(LogReason::FilterInputRect)
858 << "Invalid rect " << inputIndex << " vs. " << NumberOfSetInputs();
859 return IntRect();
861 if (mInputSurfaces[inputIndex]) {
862 return aInRect.Intersect(mInputSurfaces[inputIndex]->GetRect());
864 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
865 MOZ_ASSERT(filter, "missing input");
866 return filter->GetOutputRectInRect(aInRect);
869 size_t FilterNodeSoftware::NumberOfSetInputs() {
870 return std::max(mInputSurfaces.size(), mInputFilters.size());
873 void FilterNodeSoftware::AddInvalidationListener(
874 FilterInvalidationListener* aListener) {
875 MOZ_ASSERT(aListener, "null listener");
876 mInvalidationListeners.push_back(aListener);
879 void FilterNodeSoftware::RemoveInvalidationListener(
880 FilterInvalidationListener* aListener) {
881 MOZ_ASSERT(aListener, "null listener");
882 std::vector<FilterInvalidationListener*>::iterator it = std::find(
883 mInvalidationListeners.begin(), mInvalidationListeners.end(), aListener);
884 mInvalidationListeners.erase(it);
887 void FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware* aFilter) {
888 Invalidate();
891 void FilterNodeSoftware::Invalidate() {
892 MutexAutoLock lock(mCacheMutex);
893 mCachedOutput = nullptr;
894 mCachedRect = IntRect();
895 for (std::vector<FilterInvalidationListener*>::iterator it =
896 mInvalidationListeners.begin();
897 it != mInvalidationListeners.end(); it++) {
898 (*it)->FilterInvalidated(this);
902 FilterNodeSoftware::FilterNodeSoftware()
903 : mCacheMutex("FilterNodeSoftware::mCacheMutex") {}
905 FilterNodeSoftware::~FilterNodeSoftware() {
906 MOZ_ASSERT(
907 mInvalidationListeners.empty(),
908 "All invalidation listeners should have unsubscribed themselves by now!");
910 for (std::vector<RefPtr<FilterNodeSoftware> >::iterator it =
911 mInputFilters.begin();
912 it != mInputFilters.end(); it++) {
913 if (*it) {
914 (*it)->RemoveInvalidationListener(this);
919 void FilterNodeSoftware::SetInput(uint32_t aIndex, FilterNode* aFilter) {
920 if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
921 MOZ_ASSERT(false, "can only take software filters as inputs");
922 return;
924 SetInput(aIndex, nullptr, static_cast<FilterNodeSoftware*>(aFilter));
927 void FilterNodeSoftware::SetInput(uint32_t aIndex, SourceSurface* aSurface) {
928 SetInput(aIndex, aSurface, nullptr);
931 void FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex,
932 SourceSurface* aSurface,
933 FilterNodeSoftware* aFilter) {
934 int32_t inputIndex = InputIndex(aInputEnumIndex);
935 if (inputIndex < 0) {
936 gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex;
937 return;
939 if ((uint32_t)inputIndex >= NumberOfSetInputs()) {
940 mInputSurfaces.resize(inputIndex + 1);
941 mInputFilters.resize(inputIndex + 1);
943 mInputSurfaces[inputIndex] = aSurface;
944 if (mInputFilters[inputIndex]) {
945 mInputFilters[inputIndex]->RemoveInvalidationListener(this);
947 if (aFilter) {
948 aFilter->AddInvalidationListener(this);
950 mInputFilters[inputIndex] = aFilter;
951 if (!aSurface && !aFilter && (size_t)inputIndex == NumberOfSetInputs()) {
952 mInputSurfaces.resize(inputIndex);
953 mInputFilters.resize(inputIndex);
955 Invalidate();
958 FilterNodeBlendSoftware::FilterNodeBlendSoftware()
959 : mBlendMode(BLEND_MODE_MULTIPLY) {}
961 int32_t FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex) {
962 switch (aInputEnumIndex) {
963 case IN_BLEND_IN:
964 return 0;
965 case IN_BLEND_IN2:
966 return 1;
967 default:
968 return -1;
972 void FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex,
973 uint32_t aBlendMode) {
974 MOZ_ASSERT(aIndex == ATT_BLEND_BLENDMODE);
975 mBlendMode = static_cast<BlendMode>(aBlendMode);
976 Invalidate();
979 static CompositionOp ToBlendOp(BlendMode aOp) {
980 switch (aOp) {
981 case BLEND_MODE_MULTIPLY:
982 return CompositionOp::OP_MULTIPLY;
983 case BLEND_MODE_SCREEN:
984 return CompositionOp::OP_SCREEN;
985 case BLEND_MODE_OVERLAY:
986 return CompositionOp::OP_OVERLAY;
987 case BLEND_MODE_DARKEN:
988 return CompositionOp::OP_DARKEN;
989 case BLEND_MODE_LIGHTEN:
990 return CompositionOp::OP_LIGHTEN;
991 case BLEND_MODE_COLOR_DODGE:
992 return CompositionOp::OP_COLOR_DODGE;
993 case BLEND_MODE_COLOR_BURN:
994 return CompositionOp::OP_COLOR_BURN;
995 case BLEND_MODE_HARD_LIGHT:
996 return CompositionOp::OP_HARD_LIGHT;
997 case BLEND_MODE_SOFT_LIGHT:
998 return CompositionOp::OP_SOFT_LIGHT;
999 case BLEND_MODE_DIFFERENCE:
1000 return CompositionOp::OP_DIFFERENCE;
1001 case BLEND_MODE_EXCLUSION:
1002 return CompositionOp::OP_EXCLUSION;
1003 case BLEND_MODE_HUE:
1004 return CompositionOp::OP_HUE;
1005 case BLEND_MODE_SATURATION:
1006 return CompositionOp::OP_SATURATION;
1007 case BLEND_MODE_COLOR:
1008 return CompositionOp::OP_COLOR;
1009 case BLEND_MODE_LUMINOSITY:
1010 return CompositionOp::OP_LUMINOSITY;
1011 default:
1012 return CompositionOp::OP_OVER;
1015 return CompositionOp::OP_OVER;
1018 already_AddRefed<DataSourceSurface> FilterNodeBlendSoftware::Render(
1019 const IntRect& aRect) {
1020 RefPtr<DataSourceSurface> input1 =
1021 GetInputDataSourceSurface(IN_BLEND_IN, aRect, NEED_COLOR_CHANNELS);
1022 RefPtr<DataSourceSurface> input2 =
1023 GetInputDataSourceSurface(IN_BLEND_IN2, aRect, NEED_COLOR_CHANNELS);
1025 // Null inputs need to be treated as transparent.
1027 // First case: both are transparent.
1028 if (!input1 && !input2) {
1029 // Then the result is transparent, too.
1030 return nullptr;
1033 // Second case: one of them is transparent. Return the non-transparent one.
1034 if (!input1 || !input2) {
1035 return input1 ? input1.forget() : input2.forget();
1038 // Third case: both are non-transparent.
1039 // Apply normal filtering.
1040 RefPtr<DataSourceSurface> target =
1041 FilterProcessing::ApplyBlending(input1, input2, mBlendMode);
1042 if (target != nullptr) {
1043 return target.forget();
1046 IntSize size = input1->GetSize();
1047 target = Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1048 if (MOZ2D_WARN_IF(!target)) {
1049 return nullptr;
1052 CopyRect(input1, target, IntRect(IntPoint(), size), IntPoint());
1054 // This needs to stay in scope until the draw target has been flushed.
1055 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::READ_WRITE);
1056 if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
1057 return nullptr;
1060 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
1061 BackendType::CAIRO, targetMap.GetData(), target->GetSize(),
1062 targetMap.GetStride(), target->GetFormat());
1064 if (!dt) {
1065 gfxWarning()
1066 << "FilterNodeBlendSoftware::Render failed in CreateDrawTargetForData";
1067 return nullptr;
1070 Rect r(0, 0, size.width, size.height);
1071 dt->DrawSurface(input2, r, r, DrawSurfaceOptions(),
1072 DrawOptions(1.0f, ToBlendOp(mBlendMode)));
1073 dt->Flush();
1074 return target.forget();
1077 void FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect& aRect) {
1078 RequestInputRect(IN_BLEND_IN, aRect);
1079 RequestInputRect(IN_BLEND_IN2, aRect);
1082 IntRect FilterNodeBlendSoftware::MapRectToSource(const IntRect& aRect,
1083 const IntRect& aMax,
1084 FilterNode* aSourceNode) {
1085 IntRect result = MapInputRectToSource(IN_BLEND_IN, aRect, aMax, aSourceNode);
1086 result.OrWith(MapInputRectToSource(IN_BLEND_IN2, aRect, aMax, aSourceNode));
1087 return result;
1090 IntRect FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect& aRect) {
1091 return GetInputRectInRect(IN_BLEND_IN, aRect)
1092 .Union(GetInputRectInRect(IN_BLEND_IN2, aRect))
1093 .Intersect(aRect);
1096 FilterNodeTransformSoftware::FilterNodeTransformSoftware()
1097 : mSamplingFilter(SamplingFilter::GOOD) {}
1099 int32_t FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex) {
1100 switch (aInputEnumIndex) {
1101 case IN_TRANSFORM_IN:
1102 return 0;
1103 default:
1104 return -1;
1108 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex,
1109 uint32_t aFilter) {
1110 MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER);
1111 mSamplingFilter = static_cast<SamplingFilter>(aFilter);
1112 Invalidate();
1115 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex,
1116 const Matrix& aMatrix) {
1117 MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX);
1118 mMatrix = aMatrix;
1119 Invalidate();
1122 IntRect FilterNodeTransformSoftware::SourceRectForOutputRect(
1123 const IntRect& aRect) {
1124 if (aRect.IsEmpty()) {
1125 return IntRect();
1128 Matrix inverted(mMatrix);
1129 if (!inverted.Invert()) {
1130 return IntRect();
1133 Rect neededRect = inverted.TransformBounds(Rect(aRect));
1134 neededRect.RoundOut();
1135 IntRect neededIntRect;
1136 if (!neededRect.ToIntRect(&neededIntRect)) {
1137 return IntRect();
1139 return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect);
1142 IntRect FilterNodeTransformSoftware::MapRectToSource(const IntRect& aRect,
1143 const IntRect& aMax,
1144 FilterNode* aSourceNode) {
1145 if (aRect.IsEmpty()) {
1146 return IntRect();
1149 Matrix inverted(mMatrix);
1150 if (!inverted.Invert()) {
1151 return aMax;
1154 Rect neededRect = inverted.TransformBounds(Rect(aRect));
1155 neededRect.RoundOut();
1156 IntRect neededIntRect;
1157 if (!neededRect.ToIntRect(&neededIntRect)) {
1158 return aMax;
1160 return MapInputRectToSource(IN_TRANSFORM_IN, neededIntRect, aMax,
1161 aSourceNode);
1164 already_AddRefed<DataSourceSurface> FilterNodeTransformSoftware::Render(
1165 const IntRect& aRect) {
1166 IntRect srcRect = SourceRectForOutputRect(aRect);
1168 RefPtr<DataSourceSurface> input =
1169 GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect);
1171 if (!input) {
1172 return nullptr;
1175 Matrix transform = Matrix::Translation(srcRect.X(), srcRect.Y()) * mMatrix *
1176 Matrix::Translation(-aRect.X(), -aRect.Y());
1177 if (transform.IsIdentity() && srcRect.Size() == aRect.Size()) {
1178 return input.forget();
1181 RefPtr<DataSourceSurface> surf =
1182 Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat(), true);
1184 if (!surf) {
1185 return nullptr;
1188 DataSourceSurface::MappedSurface mapping;
1189 if (!surf->Map(DataSourceSurface::MapType::WRITE, &mapping)) {
1190 gfxCriticalError()
1191 << "FilterNodeTransformSoftware::Render failed to map surface";
1192 return nullptr;
1195 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
1196 BackendType::CAIRO, mapping.mData, surf->GetSize(), mapping.mStride,
1197 surf->GetFormat());
1198 if (!dt) {
1199 gfxWarning() << "FilterNodeTransformSoftware::Render failed in "
1200 "CreateDrawTargetForData";
1201 return nullptr;
1204 Rect r(0, 0, srcRect.Width(), srcRect.Height());
1205 dt->SetTransform(transform);
1206 dt->DrawSurface(input, r, r, DrawSurfaceOptions(mSamplingFilter));
1208 dt->Flush();
1209 surf->Unmap();
1210 return surf.forget();
1213 void FilterNodeTransformSoftware::RequestFromInputsForRect(
1214 const IntRect& aRect) {
1215 RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect));
1218 IntRect FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect) {
1219 IntRect srcRect = SourceRectForOutputRect(aRect);
1220 if (srcRect.IsEmpty()) {
1221 return IntRect();
1224 Rect outRect = mMatrix.TransformBounds(Rect(srcRect));
1225 outRect.RoundOut();
1226 IntRect outIntRect;
1227 if (!outRect.ToIntRect(&outIntRect)) {
1228 return IntRect();
1230 return outIntRect.Intersect(aRect);
1233 FilterNodeMorphologySoftware::FilterNodeMorphologySoftware()
1234 : mOperator(MORPHOLOGY_OPERATOR_ERODE) {}
1236 int32_t FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex) {
1237 switch (aInputEnumIndex) {
1238 case IN_MORPHOLOGY_IN:
1239 return 0;
1240 default:
1241 return -1;
1245 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
1246 const IntSize& aRadii) {
1247 MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_RADII);
1248 mRadii.width = std::min(std::max(aRadii.width, 0), 100000);
1249 mRadii.height = std::min(std::max(aRadii.height, 0), 100000);
1250 Invalidate();
1253 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
1254 uint32_t aOperator) {
1255 MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_OPERATOR);
1256 mOperator = static_cast<MorphologyOperator>(aOperator);
1257 Invalidate();
1260 static already_AddRefed<DataSourceSurface> ApplyMorphology(
1261 const IntRect& aSourceRect, DataSourceSurface* aInput,
1262 const IntRect& aDestRect, int32_t rx, int32_t ry,
1263 MorphologyOperator aOperator) {
1264 IntRect srcRect = aSourceRect - aDestRect.TopLeft();
1265 IntRect destRect = aDestRect - aDestRect.TopLeft();
1266 IntRect tmpRect(destRect.X(), srcRect.Y(), destRect.Width(),
1267 srcRect.Height());
1268 #ifdef DEBUG
1269 IntMargin margin = srcRect - destRect;
1270 MOZ_ASSERT(margin.top >= ry && margin.right >= rx && margin.bottom >= ry &&
1271 margin.left >= rx,
1272 "insufficient margin");
1273 #endif
1275 RefPtr<DataSourceSurface> tmp;
1276 if (rx == 0) {
1277 tmp = aInput;
1278 } else {
1279 tmp = Factory::CreateDataSourceSurface(tmpRect.Size(),
1280 SurfaceFormat::B8G8R8A8);
1281 if (MOZ2D_WARN_IF(!tmp)) {
1282 return nullptr;
1285 DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
1286 DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::WRITE);
1287 if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !tmpMap.IsMapped())) {
1288 return nullptr;
1290 uint8_t* sourceData = DataAtOffset(aInput, sourceMap.GetMappedSurface(),
1291 destRect.TopLeft() - srcRect.TopLeft());
1292 uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(),
1293 destRect.TopLeft() - tmpRect.TopLeft());
1295 FilterProcessing::ApplyMorphologyHorizontal(
1296 sourceData, sourceMap.GetStride(), tmpData, tmpMap.GetStride(), tmpRect,
1297 rx, aOperator);
1300 RefPtr<DataSourceSurface> dest;
1301 if (ry == 0) {
1302 dest = tmp;
1303 } else {
1304 dest = Factory::CreateDataSourceSurface(destRect.Size(),
1305 SurfaceFormat::B8G8R8A8);
1306 if (MOZ2D_WARN_IF(!dest)) {
1307 return nullptr;
1310 DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::READ);
1311 DataSourceSurface::ScopedMap destMap(dest, DataSourceSurface::WRITE);
1312 if (MOZ2D_WARN_IF(!tmpMap.IsMapped() || !destMap.IsMapped())) {
1313 return nullptr;
1315 int32_t tmpStride = tmpMap.GetStride();
1316 uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(),
1317 destRect.TopLeft() - tmpRect.TopLeft());
1319 int32_t destStride = destMap.GetStride();
1320 uint8_t* destData = destMap.GetData();
1322 FilterProcessing::ApplyMorphologyVertical(
1323 tmpData, tmpStride, destData, destStride, destRect, ry, aOperator);
1326 return dest.forget();
1329 already_AddRefed<DataSourceSurface> FilterNodeMorphologySoftware::Render(
1330 const IntRect& aRect) {
1331 IntRect srcRect = aRect;
1332 srcRect.Inflate(mRadii);
1334 RefPtr<DataSourceSurface> input =
1335 GetInputDataSourceSurface(IN_MORPHOLOGY_IN, srcRect, NEED_COLOR_CHANNELS);
1336 if (!input) {
1337 return nullptr;
1340 int32_t rx = mRadii.width;
1341 int32_t ry = mRadii.height;
1343 if (rx == 0 && ry == 0) {
1344 return input.forget();
1347 return ApplyMorphology(srcRect, input, aRect, rx, ry, mOperator);
1350 void FilterNodeMorphologySoftware::RequestFromInputsForRect(
1351 const IntRect& aRect) {
1352 IntRect srcRect = aRect;
1353 srcRect.Inflate(mRadii);
1354 RequestInputRect(IN_MORPHOLOGY_IN, srcRect);
1357 IntRect FilterNodeMorphologySoftware::GetOutputRectInRect(
1358 const IntRect& aRect) {
1359 IntRect inflatedSourceRect = aRect;
1360 inflatedSourceRect.Inflate(mRadii);
1361 IntRect inputRect = GetInputRectInRect(IN_MORPHOLOGY_IN, inflatedSourceRect);
1362 if (mOperator == MORPHOLOGY_OPERATOR_ERODE) {
1363 inputRect.Deflate(mRadii);
1364 } else {
1365 inputRect.Inflate(mRadii);
1367 return inputRect.Intersect(aRect);
1370 int32_t FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) {
1371 switch (aInputEnumIndex) {
1372 case IN_COLOR_MATRIX_IN:
1373 return 0;
1374 default:
1375 return -1;
1379 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
1380 const Matrix5x4& aMatrix) {
1381 MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX);
1382 mMatrix = aMatrix;
1383 Invalidate();
1386 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
1387 uint32_t aAlphaMode) {
1388 MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE);
1389 mAlphaMode = (AlphaMode)aAlphaMode;
1390 Invalidate();
1393 static already_AddRefed<DataSourceSurface> Premultiply(
1394 DataSourceSurface* aSurface) {
1395 if (aSurface->GetFormat() == SurfaceFormat::A8) {
1396 RefPtr<DataSourceSurface> surface(aSurface);
1397 return surface.forget();
1400 IntSize size = aSurface->GetSize();
1401 RefPtr<DataSourceSurface> target =
1402 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1403 if (MOZ2D_WARN_IF(!target)) {
1404 return nullptr;
1407 DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
1408 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
1409 if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
1410 return nullptr;
1413 uint8_t* inputData = inputMap.GetData();
1414 int32_t inputStride = inputMap.GetStride();
1415 uint8_t* targetData = targetMap.GetData();
1416 int32_t targetStride = targetMap.GetStride();
1418 FilterProcessing::DoPremultiplicationCalculation(
1419 size, targetData, targetStride, inputData, inputStride);
1421 return target.forget();
1424 static already_AddRefed<DataSourceSurface> Unpremultiply(
1425 DataSourceSurface* aSurface) {
1426 if (aSurface->GetFormat() == SurfaceFormat::A8) {
1427 RefPtr<DataSourceSurface> surface(aSurface);
1428 return surface.forget();
1431 IntSize size = aSurface->GetSize();
1432 RefPtr<DataSourceSurface> target =
1433 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1434 if (MOZ2D_WARN_IF(!target)) {
1435 return nullptr;
1438 DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
1439 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
1440 if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
1441 return nullptr;
1444 uint8_t* inputData = inputMap.GetData();
1445 int32_t inputStride = inputMap.GetStride();
1446 uint8_t* targetData = targetMap.GetData();
1447 int32_t targetStride = targetMap.GetStride();
1449 FilterProcessing::DoUnpremultiplicationCalculation(
1450 size, targetData, targetStride, inputData, inputStride);
1452 return target.forget();
1455 static already_AddRefed<DataSourceSurface> Opacity(DataSourceSurface* aSurface,
1456 Float aValue) {
1457 if (aValue == 1.0f) {
1458 RefPtr<DataSourceSurface> surface(aSurface);
1459 return surface.forget();
1462 IntSize size = aSurface->GetSize();
1463 RefPtr<DataSourceSurface> target =
1464 Factory::CreateDataSourceSurface(size, aSurface->GetFormat());
1465 if (MOZ2D_WARN_IF(!target)) {
1466 return nullptr;
1469 DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
1470 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
1471 if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
1472 return nullptr;
1475 uint8_t* inputData = inputMap.GetData();
1476 int32_t inputStride = inputMap.GetStride();
1477 uint8_t* targetData = targetMap.GetData();
1478 int32_t targetStride = targetMap.GetStride();
1480 if (aSurface->GetFormat() == SurfaceFormat::A8) {
1481 FilterProcessing::DoOpacityCalculationA8(size, targetData, targetStride,
1482 inputData, inputStride, aValue);
1483 } else {
1484 MOZ_ASSERT(aSurface->GetFormat() == SurfaceFormat::B8G8R8A8);
1485 FilterProcessing::DoOpacityCalculation(size, targetData, targetStride,
1486 inputData, inputStride, aValue);
1489 return target.forget();
1492 already_AddRefed<DataSourceSurface> FilterNodeColorMatrixSoftware::Render(
1493 const IntRect& aRect) {
1494 RefPtr<DataSourceSurface> input =
1495 GetInputDataSourceSurface(IN_COLOR_MATRIX_IN, aRect, NEED_COLOR_CHANNELS);
1496 if (!input) {
1497 return nullptr;
1500 if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
1501 input = Unpremultiply(input);
1504 RefPtr<DataSourceSurface> result =
1505 FilterProcessing::ApplyColorMatrix(input, mMatrix);
1507 if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
1508 result = Premultiply(result);
1511 return result.forget();
1514 void FilterNodeColorMatrixSoftware::RequestFromInputsForRect(
1515 const IntRect& aRect) {
1516 RequestInputRect(IN_COLOR_MATRIX_IN, aRect);
1519 IntRect FilterNodeColorMatrixSoftware::MapRectToSource(
1520 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
1521 return MapInputRectToSource(IN_COLOR_MATRIX_IN, aRect, aMax, aSourceNode);
1524 IntRect FilterNodeColorMatrixSoftware::GetOutputRectInRect(
1525 const IntRect& aRect) {
1526 if (mMatrix._54 > 0.0f) {
1527 return aRect;
1529 return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect);
1532 void FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex,
1533 const DeviceColor& aColor) {
1534 MOZ_ASSERT(aIndex == ATT_FLOOD_COLOR);
1535 mColor = aColor;
1536 Invalidate();
1539 static uint32_t ColorToBGRA(const DeviceColor& aColor) {
1540 union {
1541 uint32_t color;
1542 uint8_t components[4];
1544 components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
1545 NS_lround(aColor.r * aColor.a * 255.0f);
1546 components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
1547 NS_lround(aColor.g * aColor.a * 255.0f);
1548 components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
1549 NS_lround(aColor.b * aColor.a * 255.0f);
1550 components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = NS_lround(aColor.a * 255.0f);
1551 return color;
1554 static SurfaceFormat FormatForColor(DeviceColor aColor) {
1555 if (aColor.r == 0 && aColor.g == 0 && aColor.b == 0) {
1556 return SurfaceFormat::A8;
1558 return SurfaceFormat::B8G8R8A8;
1561 already_AddRefed<DataSourceSurface> FilterNodeFloodSoftware::Render(
1562 const IntRect& aRect) {
1563 SurfaceFormat format = FormatForColor(mColor);
1564 RefPtr<DataSourceSurface> target =
1565 Factory::CreateDataSourceSurface(aRect.Size(), format);
1566 if (MOZ2D_WARN_IF(!target)) {
1567 return nullptr;
1570 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
1571 if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
1572 return nullptr;
1575 uint8_t* targetData = targetMap.GetData();
1576 int32_t stride = targetMap.GetStride();
1578 if (format == SurfaceFormat::B8G8R8A8) {
1579 uint32_t color = ColorToBGRA(mColor);
1580 for (int32_t y = 0; y < aRect.Height(); y++) {
1581 for (int32_t x = 0; x < aRect.Width(); x++) {
1582 *((uint32_t*)targetData + x) = color;
1584 PodZero(&targetData[aRect.Width() * 4], stride - aRect.Width() * 4);
1585 targetData += stride;
1587 } else if (format == SurfaceFormat::A8) {
1588 uint8_t alpha = NS_lround(mColor.a * 255.0f);
1589 for (int32_t y = 0; y < aRect.Height(); y++) {
1590 for (int32_t x = 0; x < aRect.Width(); x++) {
1591 targetData[x] = alpha;
1593 PodZero(&targetData[aRect.Width()], stride - aRect.Width());
1594 targetData += stride;
1596 } else {
1597 gfxDevCrash(LogReason::FilterInputFormat)
1598 << "Bad format in flood render " << (int)format;
1599 return nullptr;
1602 return target.forget();
1605 // Override GetOutput to get around caching. Rendering simple floods is
1606 // comparatively fast.
1607 already_AddRefed<DataSourceSurface> FilterNodeFloodSoftware::GetOutput(
1608 const IntRect& aRect) {
1609 return Render(aRect);
1612 IntRect FilterNodeFloodSoftware::MapRectToSource(const IntRect& aRect,
1613 const IntRect& aMax,
1614 FilterNode* aSourceNode) {
1615 return IntRect();
1618 IntRect FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect& aRect) {
1619 if (mColor.a == 0.0f) {
1620 return IntRect();
1622 return aRect;
1625 int32_t FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex) {
1626 switch (aInputEnumIndex) {
1627 case IN_TILE_IN:
1628 return 0;
1629 default:
1630 return -1;
1634 void FilterNodeTileSoftware::SetAttribute(uint32_t aIndex,
1635 const IntRect& aSourceRect) {
1636 MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT);
1637 mSourceRect.SetRect(int32_t(aSourceRect.X()), int32_t(aSourceRect.Y()),
1638 int32_t(aSourceRect.Width()),
1639 int32_t(aSourceRect.Height()));
1640 Invalidate();
1643 namespace {
1644 struct CompareIntRects {
1645 bool operator()(const IntRect& a, const IntRect& b) const {
1646 if (a.X() != b.X()) {
1647 return a.X() < b.X();
1649 if (a.Y() != b.Y()) {
1650 return a.Y() < b.Y();
1652 if (a.Width() != b.Width()) {
1653 return a.Width() < b.Width();
1655 return a.Height() < b.Height();
1659 } // namespace
1661 already_AddRefed<DataSourceSurface> FilterNodeTileSoftware::Render(
1662 const IntRect& aRect) {
1663 if (mSourceRect.IsEmpty()) {
1664 return nullptr;
1667 if (mSourceRect.Contains(aRect)) {
1668 return GetInputDataSourceSurface(IN_TILE_IN, aRect);
1671 RefPtr<DataSourceSurface> target;
1673 typedef std::map<IntRect, RefPtr<DataSourceSurface>, CompareIntRects>
1674 InputMap;
1675 InputMap inputs;
1677 IntPoint startIndex = TileIndex(mSourceRect, aRect.TopLeft());
1678 IntPoint endIndex = TileIndex(mSourceRect, aRect.BottomRight());
1679 for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
1680 for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
1681 IntPoint sourceToDestOffset(ix * mSourceRect.Width(),
1682 iy * mSourceRect.Height());
1683 IntRect destRect = aRect.Intersect(mSourceRect + sourceToDestOffset);
1684 IntRect srcRect = destRect - sourceToDestOffset;
1685 if (srcRect.IsEmpty()) {
1686 continue;
1689 RefPtr<DataSourceSurface> input;
1690 InputMap::iterator it = inputs.find(srcRect);
1691 if (it == inputs.end()) {
1692 input = GetInputDataSourceSurface(IN_TILE_IN, srcRect);
1693 inputs[srcRect] = input;
1694 } else {
1695 input = it->second;
1697 if (!input) {
1698 return nullptr;
1700 if (!target) {
1701 // We delay creating the target until now because we want to use the
1702 // same format as our input filter, and we do not actually know the
1703 // input format before we call GetInputDataSourceSurface.
1704 target =
1705 Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat());
1706 if (MOZ2D_WARN_IF(!target)) {
1707 return nullptr;
1711 if (input->GetFormat() != target->GetFormat()) {
1712 // Different rectangles of the input can have different formats. If
1713 // that happens, just convert everything to B8G8R8A8.
1714 target = FilterProcessing::ConvertToB8G8R8A8(target);
1715 input = FilterProcessing::ConvertToB8G8R8A8(input);
1716 if (MOZ2D_WARN_IF(!target) || MOZ2D_WARN_IF(!input)) {
1717 return nullptr;
1721 CopyRect(input, target, srcRect - srcRect.TopLeft(),
1722 destRect.TopLeft() - aRect.TopLeft());
1726 return target.forget();
1729 void FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect& aRect) {
1730 // Do not request anything.
1731 // Source rects for the tile filter can be discontinuous with large gaps
1732 // between them. Requesting those from our input filter might cause it to
1733 // render the whole bounding box of all of them, which would be wasteful.
1736 IntRect FilterNodeTileSoftware::GetOutputRectInRect(const IntRect& aRect) {
1737 return aRect;
1740 FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware()
1741 : mDisableR(true), mDisableG(true), mDisableB(true), mDisableA(true) {}
1743 void FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex,
1744 bool aDisable) {
1745 switch (aIndex) {
1746 case ATT_TRANSFER_DISABLE_R:
1747 mDisableR = aDisable;
1748 break;
1749 case ATT_TRANSFER_DISABLE_G:
1750 mDisableG = aDisable;
1751 break;
1752 case ATT_TRANSFER_DISABLE_B:
1753 mDisableB = aDisable;
1754 break;
1755 case ATT_TRANSFER_DISABLE_A:
1756 mDisableA = aDisable;
1757 break;
1758 default:
1759 MOZ_CRASH("GFX: FilterNodeComponentTransferSoftware::SetAttribute");
1761 Invalidate();
1764 void FilterNodeComponentTransferSoftware::GenerateLookupTable(
1765 ptrdiff_t aComponent, uint8_t aTables[4][256], bool aDisabled) {
1766 if (aDisabled) {
1767 static uint8_t sIdentityLookupTable[256];
1768 static bool sInitializedIdentityLookupTable = false;
1769 if (!sInitializedIdentityLookupTable) {
1770 for (int32_t i = 0; i < 256; i++) {
1771 sIdentityLookupTable[i] = i;
1773 sInitializedIdentityLookupTable = true;
1775 memcpy(aTables[aComponent], sIdentityLookupTable, 256);
1776 } else {
1777 FillLookupTable(aComponent, aTables[aComponent]);
1781 template <uint32_t BytesPerPixel>
1782 static void TransferComponents(
1783 DataSourceSurface* aInput, DataSourceSurface* aTarget,
1784 const uint8_t aLookupTables[BytesPerPixel][256]) {
1785 MOZ_ASSERT(aInput->GetFormat() == aTarget->GetFormat(), "different formats");
1786 IntSize size = aInput->GetSize();
1788 DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
1789 DataSourceSurface::ScopedMap targetMap(aTarget, DataSourceSurface::WRITE);
1790 if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
1791 return;
1794 uint8_t* sourceData = sourceMap.GetData();
1795 int32_t sourceStride = sourceMap.GetStride();
1796 uint8_t* targetData = targetMap.GetData();
1797 int32_t targetStride = targetMap.GetStride();
1799 MOZ_ASSERT(sourceStride <= targetStride, "target smaller than source");
1801 for (int32_t y = 0; y < size.height; y++) {
1802 for (int32_t x = 0; x < size.width; x++) {
1803 uint32_t sourceIndex = y * sourceStride + x * BytesPerPixel;
1804 uint32_t targetIndex = y * targetStride + x * BytesPerPixel;
1805 for (uint32_t i = 0; i < BytesPerPixel; i++) {
1806 targetData[targetIndex + i] =
1807 aLookupTables[i][sourceData[sourceIndex + i]];
1811 // Zero padding to keep valgrind happy.
1812 PodZero(&targetData[y * targetStride + size.width * BytesPerPixel],
1813 targetStride - size.width * BytesPerPixel);
1817 static bool IsAllZero(const uint8_t aLookupTable[256]) {
1818 for (int32_t i = 0; i < 256; i++) {
1819 if (aLookupTable[i] != 0) {
1820 return false;
1823 return true;
1826 already_AddRefed<DataSourceSurface> FilterNodeComponentTransferSoftware::Render(
1827 const IntRect& aRect) {
1828 if (mDisableR && mDisableG && mDisableB && mDisableA) {
1829 return GetInputDataSourceSurface(IN_TRANSFER_IN, aRect);
1832 uint8_t lookupTables[4][256];
1833 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R, lookupTables, mDisableR);
1834 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G, lookupTables, mDisableG);
1835 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B, lookupTables, mDisableB);
1836 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A, lookupTables, mDisableA);
1838 bool needColorChannels =
1839 lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R][0] != 0 ||
1840 lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G][0] != 0 ||
1841 lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B][0] != 0;
1843 FormatHint pref = needColorChannels ? NEED_COLOR_CHANNELS : CAN_HANDLE_A8;
1845 RefPtr<DataSourceSurface> input =
1846 GetInputDataSourceSurface(IN_TRANSFER_IN, aRect, pref);
1847 if (!input) {
1848 return nullptr;
1851 if (input->GetFormat() == SurfaceFormat::B8G8R8A8 && !needColorChannels) {
1852 bool colorChannelsBecomeBlack =
1853 IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) &&
1854 IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) &&
1855 IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B]);
1857 if (colorChannelsBecomeBlack) {
1858 input = FilterProcessing::ExtractAlpha(input);
1862 SurfaceFormat format = input->GetFormat();
1863 if (format == SurfaceFormat::A8 && mDisableA) {
1864 return input.forget();
1867 RefPtr<DataSourceSurface> target =
1868 Factory::CreateDataSourceSurface(aRect.Size(), format);
1869 if (MOZ2D_WARN_IF(!target)) {
1870 return nullptr;
1873 if (format == SurfaceFormat::A8) {
1874 TransferComponents<1>(input, target,
1875 &lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_A]);
1876 } else {
1877 TransferComponents<4>(input, target, lookupTables);
1880 return target.forget();
1883 void FilterNodeComponentTransferSoftware::RequestFromInputsForRect(
1884 const IntRect& aRect) {
1885 RequestInputRect(IN_TRANSFER_IN, aRect);
1888 IntRect FilterNodeComponentTransferSoftware::MapRectToSource(
1889 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
1890 return MapInputRectToSource(IN_TRANSFER_IN, aRect, aMax, aSourceNode);
1893 IntRect FilterNodeComponentTransferSoftware::GetOutputRectInRect(
1894 const IntRect& aRect) {
1895 if (mDisableA) {
1896 return GetInputRectInRect(IN_TRANSFER_IN, aRect);
1898 return aRect;
1901 int32_t FilterNodeComponentTransferSoftware::InputIndex(
1902 uint32_t aInputEnumIndex) {
1903 switch (aInputEnumIndex) {
1904 case IN_TRANSFER_IN:
1905 return 0;
1906 default:
1907 return -1;
1911 void FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex,
1912 const Float* aFloat,
1913 uint32_t aSize) {
1914 std::vector<Float> table(aFloat, aFloat + aSize);
1915 switch (aIndex) {
1916 case ATT_TABLE_TRANSFER_TABLE_R:
1917 mTableR = table;
1918 break;
1919 case ATT_TABLE_TRANSFER_TABLE_G:
1920 mTableG = table;
1921 break;
1922 case ATT_TABLE_TRANSFER_TABLE_B:
1923 mTableB = table;
1924 break;
1925 case ATT_TABLE_TRANSFER_TABLE_A:
1926 mTableA = table;
1927 break;
1928 default:
1929 MOZ_CRASH("GFX: FilterNodeTableTransferSoftware::SetAttribute");
1931 Invalidate();
1934 void FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
1935 uint8_t aTable[256]) {
1936 switch (aComponent) {
1937 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
1938 FillLookupTableImpl(mTableR, aTable);
1939 break;
1940 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
1941 FillLookupTableImpl(mTableG, aTable);
1942 break;
1943 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
1944 FillLookupTableImpl(mTableB, aTable);
1945 break;
1946 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
1947 FillLookupTableImpl(mTableA, aTable);
1948 break;
1949 default:
1950 MOZ_ASSERT(false, "unknown component");
1951 break;
1955 void FilterNodeTableTransferSoftware::FillLookupTableImpl(
1956 std::vector<Float>& aTableValues, uint8_t aTable[256]) {
1957 uint32_t tvLength = aTableValues.size();
1958 if (tvLength < 2) {
1959 return;
1962 for (size_t i = 0; i < 256; i++) {
1963 uint32_t k = (i * (tvLength - 1)) / 255;
1964 Float v1 = aTableValues[k];
1965 Float v2 = aTableValues[std::min(k + 1, tvLength - 1)];
1966 int32_t val = int32_t(255 * (v1 + (i / 255.0f - k / float(tvLength - 1)) *
1967 (tvLength - 1) * (v2 - v1)));
1968 val = std::min(255, val);
1969 val = std::max(0, val);
1970 aTable[i] = val;
1974 void FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex,
1975 const Float* aFloat,
1976 uint32_t aSize) {
1977 std::vector<Float> discrete(aFloat, aFloat + aSize);
1978 switch (aIndex) {
1979 case ATT_DISCRETE_TRANSFER_TABLE_R:
1980 mTableR = discrete;
1981 break;
1982 case ATT_DISCRETE_TRANSFER_TABLE_G:
1983 mTableG = discrete;
1984 break;
1985 case ATT_DISCRETE_TRANSFER_TABLE_B:
1986 mTableB = discrete;
1987 break;
1988 case ATT_DISCRETE_TRANSFER_TABLE_A:
1989 mTableA = discrete;
1990 break;
1991 default:
1992 MOZ_CRASH("GFX: FilterNodeDiscreteTransferSoftware::SetAttribute");
1994 Invalidate();
1997 void FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
1998 uint8_t aTable[256]) {
1999 switch (aComponent) {
2000 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
2001 FillLookupTableImpl(mTableR, aTable);
2002 break;
2003 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
2004 FillLookupTableImpl(mTableG, aTable);
2005 break;
2006 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
2007 FillLookupTableImpl(mTableB, aTable);
2008 break;
2009 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
2010 FillLookupTableImpl(mTableA, aTable);
2011 break;
2012 default:
2013 MOZ_ASSERT(false, "unknown component");
2014 break;
2018 void FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(
2019 std::vector<Float>& aTableValues, uint8_t aTable[256]) {
2020 uint32_t tvLength = aTableValues.size();
2021 if (tvLength < 1) {
2022 return;
2025 for (size_t i = 0; i < 256; i++) {
2026 uint32_t k = (i * tvLength) / 255;
2027 k = std::min(k, tvLength - 1);
2028 Float v = aTableValues[k];
2029 int32_t val = NS_lround(255 * v);
2030 val = std::min(255, val);
2031 val = std::max(0, val);
2032 aTable[i] = val;
2036 FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware()
2037 : mSlopeR(0),
2038 mSlopeG(0),
2039 mSlopeB(0),
2040 mSlopeA(0),
2041 mInterceptR(0),
2042 mInterceptG(0),
2043 mInterceptB(0),
2044 mInterceptA(0) {}
2046 void FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex,
2047 Float aValue) {
2048 switch (aIndex) {
2049 case ATT_LINEAR_TRANSFER_SLOPE_R:
2050 mSlopeR = aValue;
2051 break;
2052 case ATT_LINEAR_TRANSFER_INTERCEPT_R:
2053 mInterceptR = aValue;
2054 break;
2055 case ATT_LINEAR_TRANSFER_SLOPE_G:
2056 mSlopeG = aValue;
2057 break;
2058 case ATT_LINEAR_TRANSFER_INTERCEPT_G:
2059 mInterceptG = aValue;
2060 break;
2061 case ATT_LINEAR_TRANSFER_SLOPE_B:
2062 mSlopeB = aValue;
2063 break;
2064 case ATT_LINEAR_TRANSFER_INTERCEPT_B:
2065 mInterceptB = aValue;
2066 break;
2067 case ATT_LINEAR_TRANSFER_SLOPE_A:
2068 mSlopeA = aValue;
2069 break;
2070 case ATT_LINEAR_TRANSFER_INTERCEPT_A:
2071 mInterceptA = aValue;
2072 break;
2073 default:
2074 MOZ_CRASH("GFX: FilterNodeLinearTransferSoftware::SetAttribute");
2076 Invalidate();
2079 void FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
2080 uint8_t aTable[256]) {
2081 switch (aComponent) {
2082 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
2083 FillLookupTableImpl(mSlopeR, mInterceptR, aTable);
2084 break;
2085 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
2086 FillLookupTableImpl(mSlopeG, mInterceptG, aTable);
2087 break;
2088 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
2089 FillLookupTableImpl(mSlopeB, mInterceptB, aTable);
2090 break;
2091 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
2092 FillLookupTableImpl(mSlopeA, mInterceptA, aTable);
2093 break;
2094 default:
2095 MOZ_ASSERT(false, "unknown component");
2096 break;
2100 void FilterNodeLinearTransferSoftware::FillLookupTableImpl(
2101 Float aSlope, Float aIntercept, uint8_t aTable[256]) {
2102 for (size_t i = 0; i < 256; i++) {
2103 int32_t val = NS_lround(aSlope * i + 255 * aIntercept);
2104 val = std::min(255, val);
2105 val = std::max(0, val);
2106 aTable[i] = val;
2110 FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware()
2111 : mAmplitudeR(0),
2112 mAmplitudeG(0),
2113 mAmplitudeB(0),
2114 mAmplitudeA(0),
2115 mExponentR(0),
2116 mExponentG(0),
2117 mExponentB(0),
2118 mExponentA(0),
2119 mOffsetR(0.0),
2120 mOffsetG(0.0),
2121 mOffsetB(0.0),
2122 mOffsetA(0.0) {}
2124 void FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex,
2125 Float aValue) {
2126 switch (aIndex) {
2127 case ATT_GAMMA_TRANSFER_AMPLITUDE_R:
2128 mAmplitudeR = aValue;
2129 break;
2130 case ATT_GAMMA_TRANSFER_EXPONENT_R:
2131 mExponentR = aValue;
2132 break;
2133 case ATT_GAMMA_TRANSFER_OFFSET_R:
2134 mOffsetR = aValue;
2135 break;
2136 case ATT_GAMMA_TRANSFER_AMPLITUDE_G:
2137 mAmplitudeG = aValue;
2138 break;
2139 case ATT_GAMMA_TRANSFER_EXPONENT_G:
2140 mExponentG = aValue;
2141 break;
2142 case ATT_GAMMA_TRANSFER_OFFSET_G:
2143 mOffsetG = aValue;
2144 break;
2145 case ATT_GAMMA_TRANSFER_AMPLITUDE_B:
2146 mAmplitudeB = aValue;
2147 break;
2148 case ATT_GAMMA_TRANSFER_EXPONENT_B:
2149 mExponentB = aValue;
2150 break;
2151 case ATT_GAMMA_TRANSFER_OFFSET_B:
2152 mOffsetB = aValue;
2153 break;
2154 case ATT_GAMMA_TRANSFER_AMPLITUDE_A:
2155 mAmplitudeA = aValue;
2156 break;
2157 case ATT_GAMMA_TRANSFER_EXPONENT_A:
2158 mExponentA = aValue;
2159 break;
2160 case ATT_GAMMA_TRANSFER_OFFSET_A:
2161 mOffsetA = aValue;
2162 break;
2163 default:
2164 MOZ_CRASH("GFX: FilterNodeGammaTransferSoftware::SetAttribute");
2166 Invalidate();
2169 void FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
2170 uint8_t aTable[256]) {
2171 switch (aComponent) {
2172 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
2173 FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable);
2174 break;
2175 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
2176 FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable);
2177 break;
2178 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
2179 FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable);
2180 break;
2181 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
2182 FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable);
2183 break;
2184 default:
2185 MOZ_ASSERT(false, "unknown component");
2186 break;
2190 void FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude,
2191 Float aExponent,
2192 Float aOffset,
2193 uint8_t aTable[256]) {
2194 for (size_t i = 0; i < 256; i++) {
2195 int32_t val =
2196 NS_lround(255 * (aAmplitude * pow(i / 255.0f, aExponent) + aOffset));
2197 val = std::min(255, val);
2198 val = std::max(0, val);
2199 aTable[i] = val;
2203 FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware()
2204 : mDivisor(0),
2205 mBias(0),
2206 mEdgeMode(EDGE_MODE_DUPLICATE),
2207 mPreserveAlpha(false) {}
2209 int32_t FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) {
2210 switch (aInputEnumIndex) {
2211 case IN_CONVOLVE_MATRIX_IN:
2212 return 0;
2213 default:
2214 return -1;
2218 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2219 uint32_t aIndex, const IntSize& aKernelSize) {
2220 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_SIZE);
2221 mKernelSize = aKernelSize;
2222 Invalidate();
2225 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2226 const Float* aMatrix,
2227 uint32_t aSize) {
2228 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_MATRIX);
2229 mKernelMatrix = std::vector<Float>(aMatrix, aMatrix + aSize);
2230 Invalidate();
2233 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2234 Float aValue) {
2235 switch (aIndex) {
2236 case ATT_CONVOLVE_MATRIX_DIVISOR:
2237 mDivisor = aValue;
2238 break;
2239 case ATT_CONVOLVE_MATRIX_BIAS:
2240 mBias = aValue;
2241 break;
2242 default:
2243 MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
2245 Invalidate();
2248 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2249 uint32_t aIndex, const Size& aKernelUnitLength) {
2250 switch (aIndex) {
2251 case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH:
2252 mKernelUnitLength = aKernelUnitLength;
2253 break;
2254 default:
2255 MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
2257 Invalidate();
2260 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2261 const IntPoint& aTarget) {
2262 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_TARGET);
2263 mTarget = aTarget;
2264 Invalidate();
2267 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2268 uint32_t aIndex, const IntRect& aSourceRect) {
2269 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_SOURCE_RECT);
2270 mSourceRect = aSourceRect;
2271 Invalidate();
2274 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2275 uint32_t aEdgeMode) {
2276 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_EDGE_MODE);
2277 mEdgeMode = static_cast<ConvolveMatrixEdgeMode>(aEdgeMode);
2278 Invalidate();
2281 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2282 bool aPreserveAlpha) {
2283 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA);
2284 mPreserveAlpha = aPreserveAlpha;
2285 Invalidate();
2288 #ifdef DEBUG
2289 static inline void DebugOnlyCheckColorSamplingAccess(
2290 const uint8_t* aSampleAddress, const uint8_t* aBoundsBegin,
2291 const uint8_t* aBoundsEnd) {
2292 MOZ_ASSERT(aSampleAddress >= aBoundsBegin, "accessing before start");
2293 MOZ_ASSERT(aSampleAddress < aBoundsEnd, "accessing after end");
2295 #else
2296 # define DebugOnlyCheckColorSamplingAccess(address, boundsBegin, boundsEnd)
2297 #endif
2299 static inline uint8_t ColorComponentAtPoint(const uint8_t* aData,
2300 ptrdiff_t aStride,
2301 const uint8_t* aBoundsBegin,
2302 const uint8_t* aBoundsEnd,
2303 int32_t x, int32_t y, ptrdiff_t bpp,
2304 ptrdiff_t c) {
2305 DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c],
2306 aBoundsBegin, aBoundsEnd);
2307 return aData[y * aStride + bpp * x + c];
2310 static inline int32_t ColorAtPoint(const uint8_t* aData, ptrdiff_t aStride,
2311 const uint8_t* aBoundsBegin,
2312 const uint8_t* aBoundsEnd, int32_t x,
2313 int32_t y) {
2314 DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x, aBoundsBegin,
2315 aBoundsEnd);
2316 return *(uint32_t*)(aData + y * aStride + 4 * x);
2319 // Accepts fractional x & y and does bilinear interpolation.
2320 // Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible.
2321 static inline uint8_t ColorComponentAtPoint(
2322 const uint8_t* aData, ptrdiff_t aStride, const uint8_t* aBoundsBegin,
2323 const uint8_t* aBoundsEnd, Float x, Float y, ptrdiff_t bpp, ptrdiff_t c) {
2324 const uint32_t f = 256;
2325 const int32_t lx = floor(x);
2326 const int32_t ly = floor(y);
2327 const int32_t tux = uint32_t((x - lx) * f);
2328 const int32_t tlx = f - tux;
2329 const int32_t tuy = uint32_t((y - ly) * f);
2330 const int32_t tly = f - tuy;
2331 const uint8_t& cll = ColorComponentAtPoint(aData, aStride, aBoundsBegin,
2332 aBoundsEnd, lx, ly, bpp, c);
2333 const uint8_t& cul = ColorComponentAtPoint(aData, aStride, aBoundsBegin,
2334 aBoundsEnd, lx + 1, ly, bpp, c);
2335 const uint8_t& clu = ColorComponentAtPoint(aData, aStride, aBoundsBegin,
2336 aBoundsEnd, lx, ly + 1, bpp, c);
2337 const uint8_t& cuu = ColorComponentAtPoint(
2338 aData, aStride, aBoundsBegin, aBoundsEnd, lx + 1, ly + 1, bpp, c);
2339 return ((cll * tlx + cul * tux) * tly + (clu * tlx + cuu * tux) * tuy +
2340 f * f / 2) /
2341 (f * f);
2344 static int32_t ClampToNonZero(int32_t a) { return a * (a >= 0); }
2346 template <typename CoordType>
2347 static void ConvolvePixel(const uint8_t* aSourceData, uint8_t* aTargetData,
2348 int32_t aWidth, int32_t aHeight,
2349 int32_t aSourceStride, int32_t aTargetStride,
2350 const uint8_t* aSourceBegin,
2351 const uint8_t* aSourceEnd, int32_t aX, int32_t aY,
2352 const int32_t* aKernel, int32_t aBias, int32_t shiftL,
2353 int32_t shiftR, bool aPreserveAlpha, int32_t aOrderX,
2354 int32_t aOrderY, int32_t aTargetX, int32_t aTargetY,
2355 CoordType aKernelUnitLengthX,
2356 CoordType aKernelUnitLengthY) {
2357 int32_t sum[4] = {0, 0, 0, 0};
2358 int32_t offsets[4] = {
2359 B8G8R8A8_COMPONENT_BYTEOFFSET_R, B8G8R8A8_COMPONENT_BYTEOFFSET_G,
2360 B8G8R8A8_COMPONENT_BYTEOFFSET_B, B8G8R8A8_COMPONENT_BYTEOFFSET_A};
2361 int32_t channels = aPreserveAlpha ? 3 : 4;
2362 int32_t roundingAddition = shiftL == 0 ? 0 : 1 << (shiftL - 1);
2364 for (int32_t y = 0; y < aOrderY; y++) {
2365 CoordType sampleY = aY + (y - aTargetY) * aKernelUnitLengthY;
2366 for (int32_t x = 0; x < aOrderX; x++) {
2367 CoordType sampleX = aX + (x - aTargetX) * aKernelUnitLengthX;
2368 for (int32_t i = 0; i < channels; i++) {
2369 sum[i] +=
2370 aKernel[aOrderX * y + x] *
2371 ColorComponentAtPoint(aSourceData, aSourceStride, aSourceBegin,
2372 aSourceEnd, sampleX, sampleY, 4, offsets[i]);
2376 for (int32_t i = 0; i < channels; i++) {
2377 int32_t clamped =
2378 umin(ClampToNonZero(sum[i] + aBias), 255 << shiftL >> shiftR);
2379 aTargetData[aY * aTargetStride + 4 * aX + offsets[i]] =
2380 (clamped + roundingAddition) << shiftR >> shiftL;
2382 if (aPreserveAlpha) {
2383 aTargetData[aY * aTargetStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
2384 aSourceData[aY * aSourceStride + 4 * aX +
2385 B8G8R8A8_COMPONENT_BYTEOFFSET_A];
2389 already_AddRefed<DataSourceSurface> FilterNodeConvolveMatrixSoftware::Render(
2390 const IntRect& aRect) {
2391 if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
2392 mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
2393 return DoRender(aRect, (int32_t)mKernelUnitLength.width,
2394 (int32_t)mKernelUnitLength.height);
2396 return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
2399 static std::vector<Float> ReversedVector(const std::vector<Float>& aVector) {
2400 size_t length = aVector.size();
2401 std::vector<Float> result(length, 0);
2402 for (size_t i = 0; i < length; i++) {
2403 result[length - 1 - i] = aVector[i];
2405 return result;
2408 static std::vector<Float> ScaledVector(const std::vector<Float>& aVector,
2409 Float aDivisor) {
2410 size_t length = aVector.size();
2411 std::vector<Float> result(length, 0);
2412 for (size_t i = 0; i < length; i++) {
2413 result[i] = aVector[i] / aDivisor;
2415 return result;
2418 static Float MaxVectorSum(const std::vector<Float>& aVector) {
2419 Float sum = 0;
2420 size_t length = aVector.size();
2421 for (size_t i = 0; i < length; i++) {
2422 if (aVector[i] > 0) {
2423 sum += aVector[i];
2426 return sum;
2429 // Returns shiftL and shiftR in such a way that
2430 // a << shiftL >> shiftR is roughly a * aFloat.
2431 static void TranslateDoubleToShifts(double aDouble, int32_t& aShiftL,
2432 int32_t& aShiftR) {
2433 aShiftL = 0;
2434 aShiftR = 0;
2435 if (aDouble <= 0) {
2436 MOZ_CRASH("GFX: TranslateDoubleToShifts");
2438 if (aDouble < 1) {
2439 while (1 << (aShiftR + 1) < 1 / aDouble) {
2440 aShiftR++;
2442 } else {
2443 while (1 << (aShiftL + 1) < aDouble) {
2444 aShiftL++;
2449 template <typename CoordType>
2450 already_AddRefed<DataSourceSurface> FilterNodeConvolveMatrixSoftware::DoRender(
2451 const IntRect& aRect, CoordType aKernelUnitLengthX,
2452 CoordType aKernelUnitLengthY) {
2453 if (mKernelSize.width <= 0 || mKernelSize.height <= 0 ||
2454 mKernelMatrix.size() !=
2455 uint32_t(mKernelSize.width * mKernelSize.height) ||
2456 !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) ||
2457 mDivisor == 0) {
2458 return Factory::CreateDataSourceSurface(aRect.Size(),
2459 SurfaceFormat::B8G8R8A8, true);
2462 IntRect srcRect = InflatedSourceRect(aRect);
2464 // Inflate the source rect by another pixel because the bilinear filtering in
2465 // ColorComponentAtPoint may want to access the margins.
2466 srcRect.Inflate(1);
2468 RefPtr<DataSourceSurface> input =
2469 GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect,
2470 NEED_COLOR_CHANNELS, mEdgeMode, &mSourceRect);
2472 if (!input) {
2473 return nullptr;
2476 RefPtr<DataSourceSurface> target = Factory::CreateDataSourceSurface(
2477 aRect.Size(), SurfaceFormat::B8G8R8A8, true);
2478 if (MOZ2D_WARN_IF(!target)) {
2479 return nullptr;
2482 IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
2484 DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
2485 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
2486 if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
2487 return nullptr;
2490 uint8_t* sourceData =
2491 DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
2492 int32_t sourceStride = sourceMap.GetStride();
2493 uint8_t* sourceBegin = sourceMap.GetData();
2494 uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
2495 uint8_t* targetData = targetMap.GetData();
2496 int32_t targetStride = targetMap.GetStride();
2498 // Why exactly are we reversing the kernel?
2499 std::vector<Float> kernel = ReversedVector(mKernelMatrix);
2500 kernel = ScaledVector(kernel, mDivisor);
2501 Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias,
2502 MaxVectorSum(ScaledVector(kernel, -1)) - mBias);
2503 maxResultAbs = std::max(maxResultAbs, 1.0f);
2505 double idealFactor = INT32_MAX / 2.0 / maxResultAbs / 255.0 * 0.999;
2506 MOZ_ASSERT(255.0 * maxResultAbs * idealFactor <= INT32_MAX / 2.0,
2507 "badly chosen float-to-int scale");
2508 int32_t shiftL, shiftR;
2509 TranslateDoubleToShifts(idealFactor, shiftL, shiftR);
2510 double factorFromShifts = Float(1 << shiftL) / Float(1 << shiftR);
2511 MOZ_ASSERT(255.0 * maxResultAbs * factorFromShifts <= INT32_MAX / 2.0,
2512 "badly chosen float-to-int scale");
2514 int32_t* intKernel = new int32_t[kernel.size()];
2515 for (size_t i = 0; i < kernel.size(); i++) {
2516 intKernel[i] = NS_lround(kernel[i] * factorFromShifts);
2518 int32_t bias = NS_lround(mBias * 255 * factorFromShifts);
2520 for (int32_t y = 0; y < aRect.Height(); y++) {
2521 for (int32_t x = 0; x < aRect.Width(); x++) {
2522 ConvolvePixel(sourceData, targetData, aRect.Width(), aRect.Height(),
2523 sourceStride, targetStride, sourceBegin, sourceEnd, x, y,
2524 intKernel, bias, shiftL, shiftR, mPreserveAlpha,
2525 mKernelSize.width, mKernelSize.height, mTarget.x, mTarget.y,
2526 aKernelUnitLengthX, aKernelUnitLengthY);
2529 delete[] intKernel;
2531 return target.forget();
2534 void FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(
2535 const IntRect& aRect) {
2536 RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect));
2539 IntRect FilterNodeConvolveMatrixSoftware::MapRectToSource(
2540 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
2541 return MapInputRectToSource(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect),
2542 aMax, aSourceNode);
2545 IntRect FilterNodeConvolveMatrixSoftware::InflatedSourceRect(
2546 const IntRect& aDestRect) {
2547 if (aDestRect.IsEmpty()) {
2548 return IntRect();
2551 IntMargin margin;
2552 margin.left = ceil(mTarget.x * mKernelUnitLength.width);
2553 margin.top = ceil(mTarget.y * mKernelUnitLength.height);
2554 margin.right =
2555 ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
2556 margin.bottom =
2557 ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
2559 IntRect srcRect = aDestRect;
2560 srcRect.Inflate(margin);
2561 return srcRect;
2564 IntRect FilterNodeConvolveMatrixSoftware::InflatedDestRect(
2565 const IntRect& aSourceRect) {
2566 if (aSourceRect.IsEmpty()) {
2567 return IntRect();
2570 IntMargin margin;
2571 margin.left =
2572 ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
2573 margin.top =
2574 ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
2575 margin.right = ceil(mTarget.x * mKernelUnitLength.width);
2576 margin.bottom = ceil(mTarget.y * mKernelUnitLength.height);
2578 IntRect destRect = aSourceRect;
2579 destRect.Inflate(margin);
2580 return destRect;
2583 IntRect FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(
2584 const IntRect& aRect) {
2585 IntRect srcRequest = InflatedSourceRect(aRect);
2586 IntRect srcOutput = GetInputRectInRect(IN_COLOR_MATRIX_IN, srcRequest);
2587 return InflatedDestRect(srcOutput).Intersect(aRect);
2590 FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware()
2591 : mScale(0.0f), mChannelX(COLOR_CHANNEL_R), mChannelY(COLOR_CHANNEL_G) {}
2593 int32_t FilterNodeDisplacementMapSoftware::InputIndex(
2594 uint32_t aInputEnumIndex) {
2595 switch (aInputEnumIndex) {
2596 case IN_DISPLACEMENT_MAP_IN:
2597 return 0;
2598 case IN_DISPLACEMENT_MAP_IN2:
2599 return 1;
2600 default:
2601 return -1;
2605 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
2606 Float aScale) {
2607 MOZ_ASSERT(aIndex == ATT_DISPLACEMENT_MAP_SCALE);
2608 mScale = aScale;
2609 Invalidate();
2612 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
2613 uint32_t aValue) {
2614 switch (aIndex) {
2615 case ATT_DISPLACEMENT_MAP_X_CHANNEL:
2616 mChannelX = static_cast<ColorChannel>(aValue);
2617 break;
2618 case ATT_DISPLACEMENT_MAP_Y_CHANNEL:
2619 mChannelY = static_cast<ColorChannel>(aValue);
2620 break;
2621 default:
2622 MOZ_CRASH("GFX: FilterNodeDisplacementMapSoftware::SetAttribute");
2624 Invalidate();
2627 already_AddRefed<DataSourceSurface> FilterNodeDisplacementMapSoftware::Render(
2628 const IntRect& aRect) {
2629 IntRect srcRect = InflatedSourceOrDestRect(aRect);
2630 RefPtr<DataSourceSurface> input = GetInputDataSourceSurface(
2631 IN_DISPLACEMENT_MAP_IN, srcRect, NEED_COLOR_CHANNELS);
2632 RefPtr<DataSourceSurface> map = GetInputDataSourceSurface(
2633 IN_DISPLACEMENT_MAP_IN2, aRect, NEED_COLOR_CHANNELS);
2634 RefPtr<DataSourceSurface> target =
2635 Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
2636 if (MOZ2D_WARN_IF(!(input && map && target))) {
2637 return nullptr;
2640 IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
2642 DataSourceSurface::ScopedMap inputMap(input, DataSourceSurface::READ);
2643 DataSourceSurface::ScopedMap mapMap(map, DataSourceSurface::READ);
2644 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
2645 if (MOZ2D_WARN_IF(!(inputMap.IsMapped() && mapMap.IsMapped() &&
2646 targetMap.IsMapped()))) {
2647 return nullptr;
2650 uint8_t* sourceData =
2651 DataAtOffset(input, inputMap.GetMappedSurface(), offset);
2652 int32_t sourceStride = inputMap.GetStride();
2653 uint8_t* sourceBegin = inputMap.GetData();
2654 uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
2655 uint8_t* mapData = mapMap.GetData();
2656 int32_t mapStride = mapMap.GetStride();
2657 uint8_t* targetData = targetMap.GetData();
2658 int32_t targetStride = targetMap.GetStride();
2660 static const ptrdiff_t channelMap[4] = {
2661 B8G8R8A8_COMPONENT_BYTEOFFSET_R, B8G8R8A8_COMPONENT_BYTEOFFSET_G,
2662 B8G8R8A8_COMPONENT_BYTEOFFSET_B, B8G8R8A8_COMPONENT_BYTEOFFSET_A};
2663 uint16_t xChannel = channelMap[mChannelX];
2664 uint16_t yChannel = channelMap[mChannelY];
2666 float scaleOver255 = mScale / 255.0f;
2667 float scaleAdjustment = -0.5f * mScale;
2669 for (int32_t y = 0; y < aRect.Height(); y++) {
2670 for (int32_t x = 0; x < aRect.Width(); x++) {
2671 uint32_t mapIndex = y * mapStride + 4 * x;
2672 uint32_t targIndex = y * targetStride + 4 * x;
2673 int32_t sourceX =
2674 x + scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment;
2675 int32_t sourceY =
2676 y + scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment;
2677 *(uint32_t*)(targetData + targIndex) = ColorAtPoint(
2678 sourceData, sourceStride, sourceBegin, sourceEnd, sourceX, sourceY);
2681 // Keep valgrind happy.
2682 PodZero(&targetData[y * targetStride + 4 * aRect.Width()],
2683 targetStride - 4 * aRect.Width());
2686 return target.forget();
2689 void FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(
2690 const IntRect& aRect) {
2691 RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect));
2692 RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect);
2695 IntRect FilterNodeDisplacementMapSoftware::MapRectToSource(
2696 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
2697 IntRect result =
2698 MapInputRectToSource(IN_DISPLACEMENT_MAP_IN,
2699 InflatedSourceOrDestRect(aRect), aMax, aSourceNode);
2700 result.OrWith(
2701 MapInputRectToSource(IN_DISPLACEMENT_MAP_IN2, aRect, aMax, aSourceNode));
2702 return result;
2705 IntRect FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(
2706 const IntRect& aDestOrSourceRect) {
2707 IntRect sourceOrDestRect = aDestOrSourceRect;
2708 sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2));
2709 return sourceOrDestRect;
2712 IntRect FilterNodeDisplacementMapSoftware::GetOutputRectInRect(
2713 const IntRect& aRect) {
2714 IntRect srcRequest = InflatedSourceOrDestRect(aRect);
2715 IntRect srcOutput = GetInputRectInRect(IN_DISPLACEMENT_MAP_IN, srcRequest);
2716 return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
2719 FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware()
2720 : mNumOctaves(0),
2721 mSeed(0),
2722 mStitchable(false),
2723 mType(TURBULENCE_TYPE_TURBULENCE) {}
2725 int32_t FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex) {
2726 return -1;
2729 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
2730 const Size& aBaseFrequency) {
2731 switch (aIndex) {
2732 case ATT_TURBULENCE_BASE_FREQUENCY:
2733 mBaseFrequency = aBaseFrequency;
2734 break;
2735 default:
2736 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2737 break;
2739 Invalidate();
2742 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
2743 const IntRect& aRect) {
2744 switch (aIndex) {
2745 case ATT_TURBULENCE_RECT:
2746 mRenderRect = aRect;
2747 break;
2748 default:
2749 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2750 break;
2752 Invalidate();
2755 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
2756 bool aStitchable) {
2757 MOZ_ASSERT(aIndex == ATT_TURBULENCE_STITCHABLE);
2758 mStitchable = aStitchable;
2759 Invalidate();
2762 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
2763 uint32_t aValue) {
2764 switch (aIndex) {
2765 case ATT_TURBULENCE_NUM_OCTAVES:
2766 mNumOctaves = aValue;
2767 break;
2768 case ATT_TURBULENCE_SEED:
2769 mSeed = aValue;
2770 break;
2771 case ATT_TURBULENCE_TYPE:
2772 mType = static_cast<TurbulenceType>(aValue);
2773 break;
2774 default:
2775 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2776 break;
2778 Invalidate();
2781 already_AddRefed<DataSourceSurface> FilterNodeTurbulenceSoftware::Render(
2782 const IntRect& aRect) {
2783 return FilterProcessing::RenderTurbulence(
2784 aRect.Size(), aRect.TopLeft(), mBaseFrequency, mSeed, mNumOctaves, mType,
2785 mStitchable, Rect(mRenderRect));
2788 IntRect FilterNodeTurbulenceSoftware::GetOutputRectInRect(
2789 const IntRect& aRect) {
2790 return aRect.Intersect(mRenderRect);
2793 IntRect FilterNodeTurbulenceSoftware::MapRectToSource(const IntRect& aRect,
2794 const IntRect& aMax,
2795 FilterNode* aSourceNode) {
2796 return IntRect();
2799 FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware()
2800 : mK1(0), mK2(0), mK3(0), mK4(0) {}
2802 int32_t FilterNodeArithmeticCombineSoftware::InputIndex(
2803 uint32_t aInputEnumIndex) {
2804 switch (aInputEnumIndex) {
2805 case IN_ARITHMETIC_COMBINE_IN:
2806 return 0;
2807 case IN_ARITHMETIC_COMBINE_IN2:
2808 return 1;
2809 default:
2810 return -1;
2814 void FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex,
2815 const Float* aFloat,
2816 uint32_t aSize) {
2817 MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS);
2818 MOZ_RELEASE_ASSERT(aSize == 4);
2820 mK1 = aFloat[0];
2821 mK2 = aFloat[1];
2822 mK3 = aFloat[2];
2823 mK4 = aFloat[3];
2825 Invalidate();
2828 already_AddRefed<DataSourceSurface> FilterNodeArithmeticCombineSoftware::Render(
2829 const IntRect& aRect) {
2830 RefPtr<DataSourceSurface> input1 = GetInputDataSourceSurface(
2831 IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS);
2832 RefPtr<DataSourceSurface> input2 = GetInputDataSourceSurface(
2833 IN_ARITHMETIC_COMBINE_IN2, aRect, NEED_COLOR_CHANNELS);
2834 if (!input1 && !input2) {
2835 return nullptr;
2838 // If one input is null, treat it as transparent by adjusting the factors.
2839 Float k1 = mK1, k2 = mK2, k3 = mK3, k4 = mK4;
2840 if (!input1) {
2841 k1 = 0.0f;
2842 k2 = 0.0f;
2843 input1 = input2;
2846 if (!input2) {
2847 k1 = 0.0f;
2848 k3 = 0.0f;
2849 input2 = input1;
2852 return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3,
2853 k4);
2856 void FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(
2857 const IntRect& aRect) {
2858 RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect);
2859 RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect);
2862 IntRect FilterNodeArithmeticCombineSoftware::MapRectToSource(
2863 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
2864 IntRect result =
2865 MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN, aRect, aMax, aSourceNode);
2866 result.OrWith(MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN2, aRect, aMax,
2867 aSourceNode));
2868 return result;
2871 IntRect FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(
2872 const IntRect& aRect) {
2873 if (mK4 > 0.0f) {
2874 return aRect;
2876 IntRect rectFrom1 =
2877 GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect);
2878 IntRect rectFrom2 =
2879 GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2, aRect).Intersect(aRect);
2880 IntRect result;
2881 if (mK1 > 0.0f) {
2882 result = rectFrom1.Intersect(rectFrom2);
2884 if (mK2 > 0.0f) {
2885 result = result.Union(rectFrom1);
2887 if (mK3 > 0.0f) {
2888 result = result.Union(rectFrom2);
2890 return result;
2893 FilterNodeCompositeSoftware::FilterNodeCompositeSoftware()
2894 : mOperator(COMPOSITE_OPERATOR_OVER) {}
2896 int32_t FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex) {
2897 return aInputEnumIndex - IN_COMPOSITE_IN_START;
2900 void FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex,
2901 uint32_t aCompositeOperator) {
2902 MOZ_ASSERT(aIndex == ATT_COMPOSITE_OPERATOR);
2903 mOperator = static_cast<CompositeOperator>(aCompositeOperator);
2904 Invalidate();
2907 already_AddRefed<DataSourceSurface> FilterNodeCompositeSoftware::Render(
2908 const IntRect& aRect) {
2909 RefPtr<DataSourceSurface> start = GetInputDataSourceSurface(
2910 IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS);
2911 RefPtr<DataSourceSurface> dest = Factory::CreateDataSourceSurface(
2912 aRect.Size(), SurfaceFormat::B8G8R8A8, true);
2913 if (MOZ2D_WARN_IF(!dest)) {
2914 return nullptr;
2917 if (start) {
2918 CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint());
2921 for (size_t inputIndex = 1; inputIndex < NumberOfSetInputs(); inputIndex++) {
2922 RefPtr<DataSourceSurface> input = GetInputDataSourceSurface(
2923 IN_COMPOSITE_IN_START + inputIndex, aRect, NEED_COLOR_CHANNELS);
2924 if (input) {
2925 FilterProcessing::ApplyComposition(input, dest, mOperator);
2926 } else {
2927 // We need to treat input as transparent. Depending on the composite
2928 // operator, different things happen to dest.
2929 switch (mOperator) {
2930 case COMPOSITE_OPERATOR_OVER:
2931 case COMPOSITE_OPERATOR_ATOP:
2932 case COMPOSITE_OPERATOR_XOR:
2933 // dest is unchanged.
2934 break;
2935 case COMPOSITE_OPERATOR_OUT:
2936 // dest is now transparent, but it can become non-transparent again
2937 // when compositing additional inputs.
2938 ClearDataSourceSurface(dest);
2939 break;
2940 case COMPOSITE_OPERATOR_IN:
2941 // Transparency always wins. We're completely transparent now and
2942 // no additional input can get rid of that transparency.
2943 return nullptr;
2947 return dest.forget();
2950 void FilterNodeCompositeSoftware::RequestFromInputsForRect(
2951 const IntRect& aRect) {
2952 for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
2953 RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
2957 IntRect FilterNodeCompositeSoftware::MapRectToSource(const IntRect& aRect,
2958 const IntRect& aMax,
2959 FilterNode* aSourceNode) {
2960 IntRect result;
2961 for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
2962 result.OrWith(MapInputRectToSource(IN_COMPOSITE_IN_START + inputIndex,
2963 aRect, aMax, aSourceNode));
2965 return result;
2968 IntRect FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect& aRect) {
2969 IntRect rect;
2970 for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
2971 IntRect inputRect =
2972 GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
2973 if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) {
2974 rect = rect.Intersect(inputRect);
2975 } else {
2976 rect = rect.Union(inputRect);
2979 return rect;
2982 int32_t FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex) {
2983 switch (aInputEnumIndex) {
2984 case IN_GAUSSIAN_BLUR_IN:
2985 return 0;
2986 default:
2987 return -1;
2991 already_AddRefed<DataSourceSurface> FilterNodeBlurXYSoftware::Render(
2992 const IntRect& aRect) {
2993 Size sigmaXY = StdDeviationXY();
2994 IntSize d =
2995 AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
2997 if (d.width == 0 && d.height == 0) {
2998 return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, aRect);
3001 IntRect srcRect = InflatedSourceOrDestRect(aRect);
3002 RefPtr<DataSourceSurface> input =
3003 GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, srcRect);
3004 if (!input) {
3005 return nullptr;
3008 RefPtr<DataSourceSurface> target;
3009 Rect r(0, 0, srcRect.Width(), srcRect.Height());
3011 if (input->GetFormat() == SurfaceFormat::A8) {
3012 target =
3013 Factory::CreateDataSourceSurface(srcRect.Size(), SurfaceFormat::A8);
3014 if (MOZ2D_WARN_IF(!target)) {
3015 return nullptr;
3017 CopyRect(input, target, IntRect(IntPoint(), input->GetSize()), IntPoint());
3019 DataSourceSurface::ScopedMap targetMap(target,
3020 DataSourceSurface::READ_WRITE);
3021 if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
3022 return nullptr;
3024 AlphaBoxBlur blur(r, targetMap.GetStride(), sigmaXY.width, sigmaXY.height);
3025 blur.Blur(targetMap.GetData());
3026 } else {
3027 RefPtr<DataSourceSurface> channel0, channel1, channel2, channel3;
3028 FilterProcessing::SeparateColorChannels(input, channel0, channel1, channel2,
3029 channel3);
3030 if (MOZ2D_WARN_IF(!(channel0 && channel1 && channel2 && channel3))) {
3031 return nullptr;
3034 DataSourceSurface::ScopedMap channel0Map(channel0,
3035 DataSourceSurface::READ_WRITE);
3036 DataSourceSurface::ScopedMap channel1Map(channel1,
3037 DataSourceSurface::READ_WRITE);
3038 DataSourceSurface::ScopedMap channel2Map(channel2,
3039 DataSourceSurface::READ_WRITE);
3040 DataSourceSurface::ScopedMap channel3Map(channel3,
3041 DataSourceSurface::READ_WRITE);
3042 if (MOZ2D_WARN_IF(!(channel0Map.IsMapped() && channel1Map.IsMapped() &&
3043 channel2Map.IsMapped() && channel3Map.IsMapped()))) {
3044 return nullptr;
3047 AlphaBoxBlur blur(r, channel0Map.GetStride(), sigmaXY.width,
3048 sigmaXY.height);
3049 blur.Blur(channel0Map.GetData());
3050 blur.Blur(channel1Map.GetData());
3051 blur.Blur(channel2Map.GetData());
3052 blur.Blur(channel3Map.GetData());
3054 target = FilterProcessing::CombineColorChannels(channel0, channel1,
3055 channel2, channel3);
3058 return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE);
3061 void FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect& aRect) {
3062 RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect));
3065 IntRect FilterNodeBlurXYSoftware::MapRectToSource(const IntRect& aRect,
3066 const IntRect& aMax,
3067 FilterNode* aSourceNode) {
3068 return MapInputRectToSource(
3069 IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect), aMax, aSourceNode);
3072 IntRect FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(
3073 const IntRect& aDestRect) {
3074 Size sigmaXY = StdDeviationXY();
3075 IntSize d =
3076 AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
3077 IntRect srcRect = aDestRect;
3078 srcRect.Inflate(d);
3079 return srcRect;
3082 IntRect FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect& aRect) {
3083 IntRect srcRequest = InflatedSourceOrDestRect(aRect);
3084 IntRect srcOutput = GetInputRectInRect(IN_GAUSSIAN_BLUR_IN, srcRequest);
3085 return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
3088 FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware()
3089 : mStdDeviation(0) {}
3091 static float ClampStdDeviation(float aStdDeviation) {
3092 // Cap software blur radius for performance reasons.
3093 return std::min(std::max(0.0f, aStdDeviation), 100.0f);
3096 void FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex,
3097 float aStdDeviation) {
3098 switch (aIndex) {
3099 case ATT_GAUSSIAN_BLUR_STD_DEVIATION:
3100 mStdDeviation = ClampStdDeviation(aStdDeviation);
3101 break;
3102 default:
3103 MOZ_CRASH("GFX: FilterNodeGaussianBlurSoftware::SetAttribute");
3105 Invalidate();
3108 Size FilterNodeGaussianBlurSoftware::StdDeviationXY() {
3109 return Size(mStdDeviation, mStdDeviation);
3112 FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware()
3113 : mStdDeviation(0.0), mBlurDirection(BLUR_DIRECTION_X) {}
3115 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
3116 Float aStdDeviation) {
3117 switch (aIndex) {
3118 case ATT_DIRECTIONAL_BLUR_STD_DEVIATION:
3119 mStdDeviation = ClampStdDeviation(aStdDeviation);
3120 break;
3121 default:
3122 MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
3124 Invalidate();
3127 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
3128 uint32_t aBlurDirection) {
3129 switch (aIndex) {
3130 case ATT_DIRECTIONAL_BLUR_DIRECTION:
3131 mBlurDirection = (BlurDirection)aBlurDirection;
3132 break;
3133 default:
3134 MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
3136 Invalidate();
3139 Size FilterNodeDirectionalBlurSoftware::StdDeviationXY() {
3140 float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0;
3141 float sigmaY = mBlurDirection == BLUR_DIRECTION_Y ? mStdDeviation : 0;
3142 return Size(sigmaX, sigmaY);
3145 int32_t FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex) {
3146 switch (aInputEnumIndex) {
3147 case IN_CROP_IN:
3148 return 0;
3149 default:
3150 return -1;
3154 void FilterNodeCropSoftware::SetAttribute(uint32_t aIndex,
3155 const Rect& aSourceRect) {
3156 MOZ_ASSERT(aIndex == ATT_CROP_RECT);
3157 Rect srcRect = aSourceRect;
3158 srcRect.Round();
3159 if (!srcRect.ToIntRect(&mCropRect)) {
3160 mCropRect = IntRect();
3162 Invalidate();
3165 already_AddRefed<DataSourceSurface> FilterNodeCropSoftware::Render(
3166 const IntRect& aRect) {
3167 return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect));
3170 void FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect& aRect) {
3171 RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect));
3174 IntRect FilterNodeCropSoftware::MapRectToSource(const IntRect& aRect,
3175 const IntRect& aMax,
3176 FilterNode* aSourceNode) {
3177 return MapInputRectToSource(IN_CROP_IN, aRect.Intersect(mCropRect), aMax,
3178 aSourceNode);
3181 IntRect FilterNodeCropSoftware::GetOutputRectInRect(const IntRect& aRect) {
3182 return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect);
3185 int32_t FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) {
3186 switch (aInputEnumIndex) {
3187 case IN_PREMULTIPLY_IN:
3188 return 0;
3189 default:
3190 return -1;
3194 already_AddRefed<DataSourceSurface> FilterNodePremultiplySoftware::Render(
3195 const IntRect& aRect) {
3196 RefPtr<DataSourceSurface> input =
3197 GetInputDataSourceSurface(IN_PREMULTIPLY_IN, aRect);
3198 return input ? Premultiply(input) : nullptr;
3201 void FilterNodePremultiplySoftware::RequestFromInputsForRect(
3202 const IntRect& aRect) {
3203 RequestInputRect(IN_PREMULTIPLY_IN, aRect);
3206 IntRect FilterNodePremultiplySoftware::MapRectToSource(
3207 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
3208 return MapInputRectToSource(IN_PREMULTIPLY_IN, aRect, aMax, aSourceNode);
3211 IntRect FilterNodePremultiplySoftware::GetOutputRectInRect(
3212 const IntRect& aRect) {
3213 return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect);
3216 int32_t FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) {
3217 switch (aInputEnumIndex) {
3218 case IN_UNPREMULTIPLY_IN:
3219 return 0;
3220 default:
3221 return -1;
3225 already_AddRefed<DataSourceSurface> FilterNodeUnpremultiplySoftware::Render(
3226 const IntRect& aRect) {
3227 RefPtr<DataSourceSurface> input =
3228 GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN, aRect);
3229 return input ? Unpremultiply(input) : nullptr;
3232 void FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(
3233 const IntRect& aRect) {
3234 RequestInputRect(IN_UNPREMULTIPLY_IN, aRect);
3237 IntRect FilterNodeUnpremultiplySoftware::MapRectToSource(
3238 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
3239 return MapInputRectToSource(IN_UNPREMULTIPLY_IN, aRect, aMax, aSourceNode);
3242 IntRect FilterNodeUnpremultiplySoftware::GetOutputRectInRect(
3243 const IntRect& aRect) {
3244 return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect);
3247 void FilterNodeOpacitySoftware::SetAttribute(uint32_t aIndex, Float aValue) {
3248 MOZ_ASSERT(aIndex == ATT_OPACITY_VALUE);
3249 mValue = aValue;
3250 Invalidate();
3253 int32_t FilterNodeOpacitySoftware::InputIndex(uint32_t aInputEnumIndex) {
3254 switch (aInputEnumIndex) {
3255 case IN_OPACITY_IN:
3256 return 0;
3257 default:
3258 return -1;
3262 already_AddRefed<DataSourceSurface> FilterNodeOpacitySoftware::Render(
3263 const IntRect& aRect) {
3264 RefPtr<DataSourceSurface> input =
3265 GetInputDataSourceSurface(IN_OPACITY_IN, aRect);
3266 return input ? Opacity(input, mValue) : nullptr;
3269 void FilterNodeOpacitySoftware::RequestFromInputsForRect(const IntRect& aRect) {
3270 RequestInputRect(IN_OPACITY_IN, aRect);
3273 IntRect FilterNodeOpacitySoftware::MapRectToSource(const IntRect& aRect,
3274 const IntRect& aMax,
3275 FilterNode* aSourceNode) {
3276 return MapInputRectToSource(IN_OPACITY_IN, aRect, aMax, aSourceNode);
3279 IntRect FilterNodeOpacitySoftware::GetOutputRectInRect(const IntRect& aRect) {
3280 return GetInputRectInRect(IN_OPACITY_IN, aRect);
3283 bool PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D& aPoint) {
3284 switch (aIndex) {
3285 case ATT_POINT_LIGHT_POSITION:
3286 mPosition = aPoint;
3287 break;
3288 default:
3289 return false;
3291 return true;
3294 SpotLightSoftware::SpotLightSoftware()
3295 : mSpecularFocus(0), mLimitingConeAngle(0), mLimitingConeCos(1) {}
3297 bool SpotLightSoftware::SetAttribute(uint32_t aIndex, const Point3D& aPoint) {
3298 switch (aIndex) {
3299 case ATT_SPOT_LIGHT_POSITION:
3300 mPosition = aPoint;
3301 break;
3302 case ATT_SPOT_LIGHT_POINTS_AT:
3303 mPointsAt = aPoint;
3304 break;
3305 default:
3306 return false;
3308 return true;
3311 bool SpotLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
3312 switch (aIndex) {
3313 case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE:
3314 mLimitingConeAngle = aValue;
3315 break;
3316 case ATT_SPOT_LIGHT_FOCUS:
3317 mSpecularFocus = aValue;
3318 break;
3319 default:
3320 return false;
3322 return true;
3325 DistantLightSoftware::DistantLightSoftware() : mAzimuth(0), mElevation(0) {}
3327 bool DistantLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
3328 switch (aIndex) {
3329 case ATT_DISTANT_LIGHT_AZIMUTH:
3330 mAzimuth = aValue;
3331 break;
3332 case ATT_DISTANT_LIGHT_ELEVATION:
3333 mElevation = aValue;
3334 break;
3335 default:
3336 return false;
3338 return true;
3341 static inline Point3D Normalized(const Point3D& vec) {
3342 Point3D copy(vec);
3343 copy.Normalize();
3344 return copy;
3347 template <typename LightType, typename LightingType>
3348 FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware(
3349 const char* aTypeName)
3350 : mLock("FilterNodeLightingSoftware"),
3351 mSurfaceScale(0)
3352 #if defined(MOZILLA_INTERNAL_API) && \
3353 (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
3355 mTypeName(aTypeName)
3356 #endif
3360 template <typename LightType, typename LightingType>
3361 int32_t FilterNodeLightingSoftware<LightType, LightingType>::InputIndex(
3362 uint32_t aInputEnumIndex) {
3363 switch (aInputEnumIndex) {
3364 case IN_LIGHTING_IN:
3365 return 0;
3366 default:
3367 return -1;
3371 template <typename LightType, typename LightingType>
3372 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
3373 uint32_t aIndex, const Point3D& aPoint) {
3374 if (mLight.SetAttribute(aIndex, aPoint)) {
3375 Invalidate();
3376 return;
3378 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute point");
3381 template <typename LightType, typename LightingType>
3382 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
3383 uint32_t aIndex, Float aValue) {
3384 if (mLight.SetAttribute(aIndex, aValue) ||
3385 mLighting.SetAttribute(aIndex, aValue)) {
3386 Invalidate();
3387 return;
3389 switch (aIndex) {
3390 case ATT_LIGHTING_SURFACE_SCALE:
3391 mSurfaceScale = std::fpclassify(aValue) == FP_SUBNORMAL ? 0.0 : aValue;
3392 break;
3393 default:
3394 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float");
3396 Invalidate();
3399 template <typename LightType, typename LightingType>
3400 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
3401 uint32_t aIndex, const Size& aKernelUnitLength) {
3402 switch (aIndex) {
3403 case ATT_LIGHTING_KERNEL_UNIT_LENGTH:
3404 mKernelUnitLength = aKernelUnitLength;
3405 break;
3406 default:
3407 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size");
3409 Invalidate();
3412 template <typename LightType, typename LightingType>
3413 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
3414 uint32_t aIndex, const DeviceColor& aColor) {
3415 MOZ_ASSERT(aIndex == ATT_LIGHTING_COLOR);
3416 mColor = aColor;
3417 Invalidate();
3420 template <typename LightType, typename LightingType>
3421 IntRect
3422 FilterNodeLightingSoftware<LightType, LightingType>::GetOutputRectInRect(
3423 const IntRect& aRect) {
3424 return aRect;
3427 Point3D PointLightSoftware::GetVectorToLight(const Point3D& aTargetPoint) {
3428 return Normalized(mPosition - aTargetPoint);
3431 uint32_t PointLightSoftware::GetColor(uint32_t aLightColor,
3432 const Point3D& aVectorToLight) {
3433 return aLightColor;
3436 void SpotLightSoftware::Prepare() {
3437 mVectorFromFocusPointToLight = Normalized(mPointsAt - mPosition);
3438 mLimitingConeCos =
3439 std::max<double>(cos(mLimitingConeAngle * M_PI / 180.0), 0.0);
3440 mPowCache.CacheForExponent(mSpecularFocus);
3443 Point3D SpotLightSoftware::GetVectorToLight(const Point3D& aTargetPoint) {
3444 return Normalized(mPosition - aTargetPoint);
3447 uint32_t SpotLightSoftware::GetColor(uint32_t aLightColor,
3448 const Point3D& aVectorToLight) {
3449 union {
3450 uint32_t color;
3451 uint8_t colorC[4];
3454 Float dot = -aVectorToLight.DotProduct(mVectorFromFocusPointToLight);
3455 if (!mPowCache.HasPowerTable()) {
3456 dot *= (dot >= mLimitingConeCos);
3457 color = aLightColor;
3458 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] *= dot;
3459 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] *= dot;
3460 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] *= dot;
3461 } else {
3462 color = aLightColor;
3463 uint16_t doti = dot * (dot >= 0) * (1 << PowCache::sInputIntPrecisionBits);
3464 uint32_t tmp = mPowCache.Pow(doti) * (dot >= mLimitingConeCos);
3465 MOZ_ASSERT(tmp <= (1 << PowCache::sOutputIntPrecisionBits),
3466 "pow() result must not exceed 1.0");
3467 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
3468 uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] * tmp) >>
3469 PowCache::sOutputIntPrecisionBits);
3470 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
3471 uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] * tmp) >>
3472 PowCache::sOutputIntPrecisionBits);
3473 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
3474 uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] * tmp) >>
3475 PowCache::sOutputIntPrecisionBits);
3477 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
3478 return color;
3481 void DistantLightSoftware::Prepare() {
3482 const double radPerDeg = M_PI / 180.0;
3483 mVectorToLight.x = cos(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
3484 mVectorToLight.y = sin(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
3485 mVectorToLight.z = sin(mElevation * radPerDeg);
3488 Point3D DistantLightSoftware::GetVectorToLight(const Point3D& aTargetPoint) {
3489 return mVectorToLight;
3492 uint32_t DistantLightSoftware::GetColor(uint32_t aLightColor,
3493 const Point3D& aVectorToLight) {
3494 return aLightColor;
3497 template <typename CoordType>
3498 static Point3D GenerateNormal(const uint8_t* data, int32_t stride,
3499 uint8_t* boundsBegin, uint8_t* boundsEnd,
3500 int32_t x, int32_t y, float surfaceScale,
3501 CoordType dx, CoordType dy) {
3502 const uint8_t* index = data + y * stride + x;
3504 CoordType zero = 0;
3506 // See this for source of constants:
3507 // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
3508 int16_t normalX = -1 * ColorComponentAtPoint(index, stride, boundsBegin,
3509 boundsEnd, -dx, -dy, 1, 0) +
3510 1 * ColorComponentAtPoint(index, stride, boundsBegin,
3511 boundsEnd, dx, -dy, 1, 0) +
3512 -2 * ColorComponentAtPoint(index, stride, boundsBegin,
3513 boundsEnd, -dx, zero, 1, 0) +
3514 2 * ColorComponentAtPoint(index, stride, boundsBegin,
3515 boundsEnd, dx, zero, 1, 0) +
3516 -1 * ColorComponentAtPoint(index, stride, boundsBegin,
3517 boundsEnd, -dx, dy, 1, 0) +
3518 1 * ColorComponentAtPoint(index, stride, boundsBegin,
3519 boundsEnd, dx, dy, 1, 0);
3521 int16_t normalY = -1 * ColorComponentAtPoint(index, stride, boundsBegin,
3522 boundsEnd, -dx, -dy, 1, 0) +
3523 -2 * ColorComponentAtPoint(index, stride, boundsBegin,
3524 boundsEnd, zero, -dy, 1, 0) +
3525 -1 * ColorComponentAtPoint(index, stride, boundsBegin,
3526 boundsEnd, dx, -dy, 1, 0) +
3527 1 * ColorComponentAtPoint(index, stride, boundsBegin,
3528 boundsEnd, -dx, dy, 1, 0) +
3529 2 * ColorComponentAtPoint(index, stride, boundsBegin,
3530 boundsEnd, zero, dy, 1, 0) +
3531 1 * ColorComponentAtPoint(index, stride, boundsBegin,
3532 boundsEnd, dx, dy, 1, 0);
3534 Point3D normal;
3535 normal.x = -surfaceScale * normalX / 4.0f;
3536 normal.y = -surfaceScale * normalY / 4.0f;
3537 normal.z = 255;
3538 return Normalized(normal);
3541 template <typename LightType, typename LightingType>
3542 already_AddRefed<DataSourceSurface>
3543 FilterNodeLightingSoftware<LightType, LightingType>::Render(
3544 const IntRect& aRect) {
3545 if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
3546 mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
3547 return DoRender(aRect, (int32_t)mKernelUnitLength.width,
3548 (int32_t)mKernelUnitLength.height);
3550 return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
3553 template <typename LightType, typename LightingType>
3554 void FilterNodeLightingSoftware<
3555 LightType, LightingType>::RequestFromInputsForRect(const IntRect& aRect) {
3556 IntRect srcRect = aRect;
3557 srcRect.Inflate(ceil(mKernelUnitLength.width),
3558 ceil(mKernelUnitLength.height));
3559 RequestInputRect(IN_LIGHTING_IN, srcRect);
3562 template <typename LightType, typename LightingType>
3563 IntRect FilterNodeLightingSoftware<LightType, LightingType>::MapRectToSource(
3564 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
3565 IntRect srcRect = aRect;
3566 srcRect.Inflate(ceil(mKernelUnitLength.width),
3567 ceil(mKernelUnitLength.height));
3568 return MapInputRectToSource(IN_LIGHTING_IN, srcRect, aMax, aSourceNode);
3571 template <typename LightType, typename LightingType>
3572 template <typename CoordType>
3573 already_AddRefed<DataSourceSurface>
3574 FilterNodeLightingSoftware<LightType, LightingType>::DoRender(
3575 const IntRect& aRect, CoordType aKernelUnitLengthX,
3576 CoordType aKernelUnitLengthY) {
3577 MOZ_ASSERT(aKernelUnitLengthX > 0,
3578 "aKernelUnitLengthX can be a negative or zero value");
3579 MOZ_ASSERT(aKernelUnitLengthY > 0,
3580 "aKernelUnitLengthY can be a negative or zero value");
3582 IntRect srcRect = aRect;
3583 IntSize size = aRect.Size();
3584 srcRect.Inflate(ceil(float(aKernelUnitLengthX)),
3585 ceil(float(aKernelUnitLengthY)));
3587 // Inflate the source rect by another pixel because the bilinear filtering in
3588 // ColorComponentAtPoint may want to access the margins.
3589 srcRect.Inflate(1);
3591 RefPtr<DataSourceSurface> input = GetInputDataSourceSurface(
3592 IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8, EDGE_MODE_NONE);
3594 if (!input) {
3595 return nullptr;
3598 if (input->GetFormat() != SurfaceFormat::A8) {
3599 input = FilterProcessing::ExtractAlpha(input);
3602 RefPtr<DataSourceSurface> target =
3603 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
3604 if (MOZ2D_WARN_IF(!target)) {
3605 return nullptr;
3608 IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
3610 DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
3611 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
3612 if (MOZ2D_WARN_IF(!(sourceMap.IsMapped() && targetMap.IsMapped()))) {
3613 return nullptr;
3616 uint8_t* sourceData =
3617 DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
3618 int32_t sourceStride = sourceMap.GetStride();
3619 uint8_t* sourceBegin = sourceMap.GetData();
3620 uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
3621 uint8_t* targetData = targetMap.GetData();
3622 int32_t targetStride = targetMap.GetStride();
3624 MutexAutoLock lock(mLock);
3626 uint32_t lightColor = ColorToBGRA(mColor);
3627 mLight.Prepare();
3628 mLighting.Prepare();
3630 for (int32_t y = 0; y < size.height; y++) {
3631 for (int32_t x = 0; x < size.width; x++) {
3632 int32_t sourceIndex = y * sourceStride + x;
3633 int32_t targetIndex = y * targetStride + 4 * x;
3635 Point3D normal =
3636 GenerateNormal(sourceData, sourceStride, sourceBegin, sourceEnd, x, y,
3637 mSurfaceScale, aKernelUnitLengthX, aKernelUnitLengthY);
3639 IntPoint pointInFilterSpace(aRect.X() + x, aRect.Y() + y);
3640 Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f;
3641 Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z);
3642 Point3D rayDir = mLight.GetVectorToLight(pt);
3643 uint32_t color = mLight.GetColor(lightColor, rayDir);
3645 *(uint32_t*)(targetData + targetIndex) =
3646 mLighting.LightPixel(normal, rayDir, color);
3649 // Zero padding to keep valgrind happy.
3650 PodZero(&targetData[y * targetStride + 4 * size.width],
3651 targetStride - 4 * size.width);
3654 return target.forget();
3657 DiffuseLightingSoftware::DiffuseLightingSoftware() : mDiffuseConstant(0) {}
3659 bool DiffuseLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
3660 switch (aIndex) {
3661 case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT:
3662 mDiffuseConstant = aValue;
3663 break;
3664 default:
3665 return false;
3667 return true;
3670 uint32_t DiffuseLightingSoftware::LightPixel(const Point3D& aNormal,
3671 const Point3D& aVectorToLight,
3672 uint32_t aColor) {
3673 Float dotNL = std::max(0.0f, aNormal.DotProduct(aVectorToLight));
3674 Float diffuseNL = mDiffuseConstant * dotNL;
3676 union {
3677 uint32_t bgra;
3678 uint8_t components[4];
3679 } color = {aColor};
3680 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = umin(
3681 uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]),
3682 255U);
3683 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = umin(
3684 uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]),
3685 255U);
3686 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = umin(
3687 uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]),
3688 255U);
3689 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
3690 return color.bgra;
3693 SpecularLightingSoftware::SpecularLightingSoftware()
3694 : mSpecularConstant(0), mSpecularExponent(0), mSpecularConstantInt(0) {}
3696 bool SpecularLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
3697 switch (aIndex) {
3698 case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT:
3699 mSpecularConstant = std::min(std::max(aValue, 0.0f), 255.0f);
3700 break;
3701 case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT:
3702 mSpecularExponent = std::min(std::max(aValue, 1.0f), 128.0f);
3703 break;
3704 default:
3705 return false;
3707 return true;
3710 void SpecularLightingSoftware::Prepare() {
3711 mPowCache.CacheForExponent(mSpecularExponent);
3712 mSpecularConstantInt = uint32_t(mSpecularConstant * (1 << 8));
3715 uint32_t SpecularLightingSoftware::LightPixel(const Point3D& aNormal,
3716 const Point3D& aVectorToLight,
3717 uint32_t aColor) {
3718 Point3D vectorToEye(0, 0, 1);
3719 Point3D halfwayVector = Normalized(aVectorToLight + vectorToEye);
3720 Float dotNH = aNormal.DotProduct(halfwayVector);
3721 uint16_t dotNHi =
3722 uint16_t(dotNH * (dotNH >= 0) * (1 << PowCache::sInputIntPrecisionBits));
3723 // The exponent for specular is in [1,128] range, so we don't need to check
3724 // and optimize for the "default power table" scenario here.
3725 MOZ_ASSERT(mPowCache.HasPowerTable());
3726 uint32_t specularNHi =
3727 uint32_t(mSpecularConstantInt) * mPowCache.Pow(dotNHi) >> 8;
3729 union {
3730 uint32_t bgra;
3731 uint8_t components[4];
3732 } color = {aColor};
3733 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
3734 umin((specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]) >>
3735 PowCache::sOutputIntPrecisionBits,
3736 255U);
3737 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
3738 umin((specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) >>
3739 PowCache::sOutputIntPrecisionBits,
3740 255U);
3741 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
3742 umin((specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) >>
3743 PowCache::sOutputIntPrecisionBits,
3744 255U);
3746 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
3747 umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B],
3748 umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G],
3749 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]));
3750 return color.bgra;
3753 } // namespace gfx
3754 } // namespace mozilla