no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / gfx / 2d / FilterNodeSoftware.cpp
blob0453d86869589f2c26aaf78c58c82ba03c7dd77f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include <cmath>
8 #include "DataSurfaceHelpers.h"
9 #include "FilterNodeSoftware.h"
10 #include "2D.h"
11 #include "Tools.h"
12 #include "Blur.h"
13 #include <map>
14 #include "FilterProcessing.h"
15 #include "Logging.h"
16 #include "mozilla/PodOperations.h"
17 #include "mozilla/DebugOnly.h"
19 // #define DEBUG_DUMP_SURFACES
21 #ifdef DEBUG_DUMP_SURFACES
22 # include "gfxUtils.h" // not part of Moz2D
23 #endif
25 namespace mozilla {
26 namespace gfx {
28 namespace {
30 /**
31 * This class provides a way to get a pow() results in constant-time. It works
32 * by caching 129 ((1 << sCacheIndexPrecisionBits) + 1) values for bases between
33 * 0 and 1 and a fixed exponent.
34 **/
35 class PowCache {
36 public:
37 PowCache() : mNumPowTablePreSquares(-1) {}
39 void CacheForExponent(Float aExponent) {
40 // Since we are in the world where we only care about
41 // input and results in [0,1], there is no point in
42 // dealing with non-positive exponents.
43 if (aExponent <= 0) {
44 mNumPowTablePreSquares = -1;
45 return;
47 int numPreSquares = 0;
48 while (numPreSquares < 5 && aExponent > (1 << (numPreSquares + 2))) {
49 numPreSquares++;
51 mNumPowTablePreSquares = numPreSquares;
52 for (size_t i = 0; i < sCacheSize; i++) {
53 // sCacheSize is chosen in such a way that a takes values
54 // from 0.0 to 1.0 inclusive.
55 Float a = i / Float(1 << sCacheIndexPrecisionBits);
56 MOZ_ASSERT(0.0f <= a && a <= 1.0f,
57 "We only want to cache for bases between 0 and 1.");
59 for (int j = 0; j < mNumPowTablePreSquares; j++) {
60 a = sqrt(a);
62 uint32_t cachedInt = pow(a, aExponent) * (1 << sOutputIntPrecisionBits);
63 MOZ_ASSERT(cachedInt < (1 << (sizeof(mPowTable[i]) * 8)),
64 "mPowCache integer type too small");
66 mPowTable[i] = cachedInt;
70 // Only call Pow() if HasPowerTable() would return true, to avoid complicating
71 // this code and having it just return (1 << sOutputIntPrecisionBits))
72 uint16_t Pow(uint16_t aBase) {
73 MOZ_ASSERT(HasPowerTable());
74 // Results should be similar to what the following code would produce:
75 // Float x = Float(aBase) / (1 << sInputIntPrecisionBits);
76 // return uint16_t(pow(x, aExponent) * (1 << sOutputIntPrecisionBits));
78 MOZ_ASSERT(aBase <= (1 << sInputIntPrecisionBits),
79 "aBase needs to be between 0 and 1!");
81 uint32_t a = aBase;
82 for (int j = 0; j < mNumPowTablePreSquares; j++) {
83 a = a * a >> sInputIntPrecisionBits;
85 uint32_t i = a >> (sInputIntPrecisionBits - sCacheIndexPrecisionBits);
86 MOZ_ASSERT(i < sCacheSize, "out-of-bounds mPowTable access");
87 return mPowTable[i];
90 static const int sInputIntPrecisionBits = 15;
91 static const int sOutputIntPrecisionBits = 15;
92 static const int sCacheIndexPrecisionBits = 7;
94 inline bool HasPowerTable() const { return mNumPowTablePreSquares >= 0; }
96 private:
97 static const size_t sCacheSize = (1 << sCacheIndexPrecisionBits) + 1;
99 int mNumPowTablePreSquares;
100 uint16_t mPowTable[sCacheSize];
103 class PointLightSoftware {
104 public:
105 bool SetAttribute(uint32_t aIndex, Float) { return false; }
106 bool SetAttribute(uint32_t aIndex, const Point3D&);
107 void Prepare() {}
108 Point3D GetVectorToLight(const Point3D& aTargetPoint);
109 uint32_t GetColor(uint32_t aLightColor, const Point3D& aVectorToLight);
111 private:
112 Point3D mPosition;
115 class SpotLightSoftware {
116 public:
117 SpotLightSoftware();
118 bool SetAttribute(uint32_t aIndex, Float);
119 bool SetAttribute(uint32_t aIndex, const Point3D&);
120 void Prepare();
121 Point3D GetVectorToLight(const Point3D& aTargetPoint);
122 uint32_t GetColor(uint32_t aLightColor, const Point3D& aVectorToLight);
124 private:
125 Point3D mPosition;
126 Point3D mPointsAt;
127 Point3D mVectorFromFocusPointToLight;
128 Float mSpecularFocus;
129 Float mLimitingConeAngle;
130 Float mLimitingConeCos;
131 PowCache mPowCache;
134 class DistantLightSoftware {
135 public:
136 DistantLightSoftware();
137 bool SetAttribute(uint32_t aIndex, Float);
138 bool SetAttribute(uint32_t aIndex, const Point3D&) { return false; }
139 void Prepare();
140 Point3D GetVectorToLight(const Point3D& aTargetPoint);
141 uint32_t GetColor(uint32_t aLightColor, const Point3D& aVectorToLight);
143 private:
144 Float mAzimuth;
145 Float mElevation;
146 Point3D mVectorToLight;
149 class DiffuseLightingSoftware {
150 public:
151 DiffuseLightingSoftware();
152 bool SetAttribute(uint32_t aIndex, Float);
153 void Prepare() {}
154 uint32_t LightPixel(const Point3D& aNormal, const Point3D& aVectorToLight,
155 uint32_t aColor);
157 private:
158 Float mDiffuseConstant;
161 class SpecularLightingSoftware {
162 public:
163 SpecularLightingSoftware();
164 bool SetAttribute(uint32_t aIndex, Float);
165 void Prepare();
166 uint32_t LightPixel(const Point3D& aNormal, const Point3D& aVectorToLight,
167 uint32_t aColor);
169 private:
170 Float mSpecularConstant;
171 Float mSpecularExponent;
172 uint32_t mSpecularConstantInt;
173 PowCache mPowCache;
176 } // unnamed namespace
178 // from xpcom/ds/nsMathUtils.h
179 static int32_t NS_lround(double x) {
180 return x >= 0.0 ? int32_t(x + 0.5) : int32_t(x - 0.5);
183 static already_AddRefed<DataSourceSurface> CloneAligned(
184 DataSourceSurface* aSource) {
185 return CreateDataSourceSurfaceByCloning(aSource);
188 static void FillRectWithPixel(DataSourceSurface* aSurface,
189 const IntRect& aFillRect, IntPoint aPixelPos) {
190 MOZ_ASSERT(!aFillRect.Overflows());
191 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
192 "aFillRect needs to be completely inside the surface");
193 MOZ_ASSERT(SurfaceContainsPoint(aSurface, aPixelPos),
194 "aPixelPos needs to be inside the surface");
196 DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
197 if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
198 return;
200 uint8_t* sourcePixelData =
201 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aPixelPos);
202 uint8_t* data =
203 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
204 int bpp = BytesPerPixel(aSurface->GetFormat());
206 // Fill the first row by hand.
207 if (bpp == 4) {
208 uint32_t sourcePixel = *(uint32_t*)sourcePixelData;
209 for (int32_t x = 0; x < aFillRect.Width(); x++) {
210 *((uint32_t*)data + x) = sourcePixel;
212 } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
213 uint8_t sourcePixel = *sourcePixelData;
214 memset(data, sourcePixel, aFillRect.Width());
217 // Copy the first row into the other rows.
218 for (int32_t y = 1; y < aFillRect.Height(); y++) {
219 PodCopy(data + y * surfMap.GetStride(), data, aFillRect.Width() * bpp);
223 static void FillRectWithVerticallyRepeatingHorizontalStrip(
224 DataSourceSurface* aSurface, const IntRect& aFillRect,
225 const IntRect& aSampleRect) {
226 MOZ_ASSERT(!aFillRect.Overflows());
227 MOZ_ASSERT(!aSampleRect.Overflows());
228 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
229 "aFillRect needs to be completely inside the surface");
230 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
231 "aSampleRect needs to be completely inside the surface");
233 DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
234 if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
235 return;
238 uint8_t* sampleData =
239 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft());
240 uint8_t* data =
241 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
242 if (BytesPerPixel(aSurface->GetFormat()) == 4) {
243 for (int32_t y = 0; y < aFillRect.Height(); y++) {
244 PodCopy((uint32_t*)data, (uint32_t*)sampleData, aFillRect.Width());
245 data += surfMap.GetStride();
247 } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
248 for (int32_t y = 0; y < aFillRect.Height(); y++) {
249 PodCopy(data, sampleData, aFillRect.Width());
250 data += surfMap.GetStride();
255 static void FillRectWithHorizontallyRepeatingVerticalStrip(
256 DataSourceSurface* aSurface, const IntRect& aFillRect,
257 const IntRect& aSampleRect) {
258 MOZ_ASSERT(!aFillRect.Overflows());
259 MOZ_ASSERT(!aSampleRect.Overflows());
260 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
261 "aFillRect needs to be completely inside the surface");
262 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
263 "aSampleRect needs to be completely inside the surface");
265 DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
266 if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
267 return;
270 uint8_t* sampleData =
271 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft());
272 uint8_t* data =
273 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
274 if (BytesPerPixel(aSurface->GetFormat()) == 4) {
275 for (int32_t y = 0; y < aFillRect.Height(); y++) {
276 int32_t sampleColor = *((uint32_t*)sampleData);
277 for (int32_t x = 0; x < aFillRect.Width(); x++) {
278 *((uint32_t*)data + x) = sampleColor;
280 data += surfMap.GetStride();
281 sampleData += surfMap.GetStride();
283 } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
284 for (int32_t y = 0; y < aFillRect.Height(); y++) {
285 uint8_t sampleColor = *sampleData;
286 memset(data, sampleColor, aFillRect.Width());
287 data += surfMap.GetStride();
288 sampleData += surfMap.GetStride();
293 static void DuplicateEdges(DataSourceSurface* aSurface,
294 const IntRect& aFromRect) {
295 MOZ_ASSERT(!aFromRect.Overflows());
296 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFromRect),
297 "aFromRect needs to be completely inside the surface");
299 IntSize size = aSurface->GetSize();
300 IntRect fill;
301 IntRect sampleRect;
302 for (int32_t ix = 0; ix < 3; ix++) {
303 switch (ix) {
304 case 0:
305 fill.SetRectX(0, aFromRect.X());
306 sampleRect.SetRectX(fill.XMost(), 1);
307 break;
308 case 1:
309 fill.SetRectX(aFromRect.X(), aFromRect.Width());
310 sampleRect.SetRectX(fill.X(), fill.Width());
311 break;
312 case 2:
313 fill.MoveToX(aFromRect.XMost());
314 fill.SetRightEdge(size.width);
315 sampleRect.SetRectX(fill.X() - 1, 1);
316 break;
318 if (fill.Width() <= 0) {
319 continue;
321 bool xIsMiddle = (ix == 1);
322 for (int32_t iy = 0; iy < 3; iy++) {
323 switch (iy) {
324 case 0:
325 fill.SetRectY(0, aFromRect.Y());
326 sampleRect.SetRectY(fill.YMost(), 1);
327 break;
328 case 1:
329 fill.SetRectY(aFromRect.Y(), aFromRect.Height());
330 sampleRect.SetRectY(fill.Y(), fill.Height());
331 break;
332 case 2:
333 fill.MoveToY(aFromRect.YMost());
334 fill.SetBottomEdge(size.height);
335 sampleRect.SetRectY(fill.Y() - 1, 1);
336 break;
338 if (fill.Height() <= 0) {
339 continue;
341 bool yIsMiddle = (iy == 1);
342 if (!xIsMiddle && !yIsMiddle) {
343 // Corner
344 FillRectWithPixel(aSurface, fill, sampleRect.TopLeft());
346 if (xIsMiddle && !yIsMiddle) {
347 // Top middle or bottom middle
348 FillRectWithVerticallyRepeatingHorizontalStrip(aSurface, fill,
349 sampleRect);
351 if (!xIsMiddle && yIsMiddle) {
352 // Left middle or right middle
353 FillRectWithHorizontallyRepeatingVerticalStrip(aSurface, fill,
354 sampleRect);
360 static IntPoint TileIndex(const IntRect& aFirstTileRect,
361 const IntPoint& aPoint) {
362 return IntPoint(int32_t(floor(double(aPoint.x - aFirstTileRect.X()) /
363 aFirstTileRect.Width())),
364 int32_t(floor(double(aPoint.y - aFirstTileRect.Y()) /
365 aFirstTileRect.Height())));
368 static void TileSurface(DataSourceSurface* aSource, DataSourceSurface* aTarget,
369 const IntPoint& aOffset) {
370 IntRect sourceRect(aOffset, aSource->GetSize());
371 IntRect targetRect(IntPoint(0, 0), aTarget->GetSize());
372 IntPoint startIndex = TileIndex(sourceRect, targetRect.TopLeft());
373 IntPoint endIndex = TileIndex(sourceRect, targetRect.BottomRight());
375 for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
376 for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
377 IntPoint destPoint(sourceRect.X() + ix * sourceRect.Width(),
378 sourceRect.Y() + iy * sourceRect.Height());
379 IntRect destRect(destPoint, sourceRect.Size());
380 destRect = destRect.Intersect(targetRect);
381 IntRect srcRect = destRect - destPoint;
382 CopyRect(aSource, aTarget, srcRect, destRect.TopLeft());
387 static already_AddRefed<DataSourceSurface> GetDataSurfaceInRect(
388 SourceSurface* aSurface, const IntRect& aSurfaceRect,
389 const IntRect& aDestRect, ConvolveMatrixEdgeMode aEdgeMode) {
390 MOZ_ASSERT(aSurface ? aSurfaceRect.Size() == aSurface->GetSize()
391 : aSurfaceRect.IsEmpty());
393 if (aSurfaceRect.Overflows() || aDestRect.Overflows()) {
394 // We can't rely on the intersection calculations below to make sense when
395 // XMost() or YMost() overflow. Bail out.
396 return nullptr;
399 IntRect sourceRect = aSurfaceRect;
401 if (sourceRect.IsEqualEdges(aDestRect)) {
402 return aSurface ? aSurface->GetDataSurface() : nullptr;
405 IntRect intersect = sourceRect.Intersect(aDestRect);
407 // create rects that are in surface local space.
408 IntRect intersectInSourceSpace = intersect - sourceRect.TopLeft();
409 IntRect intersectInDestSpace = intersect - aDestRect.TopLeft();
410 SurfaceFormat format =
411 aSurface ? aSurface->GetFormat() : SurfaceFormat(SurfaceFormat::B8G8R8A8);
413 RefPtr<DataSourceSurface> target =
414 Factory::CreateDataSourceSurface(aDestRect.Size(), format, true);
415 if (MOZ2D_WARN_IF(!target)) {
416 return nullptr;
419 if (!aSurface) {
420 return target.forget();
423 RefPtr<DataSourceSurface> dataSource = aSurface->GetDataSurface();
424 MOZ_ASSERT(dataSource);
426 if (aEdgeMode == EDGE_MODE_WRAP) {
427 TileSurface(dataSource, target, intersectInDestSpace.TopLeft());
428 return target.forget();
431 CopyRect(dataSource, target, intersectInSourceSpace,
432 intersectInDestSpace.TopLeft());
434 if (aEdgeMode == EDGE_MODE_DUPLICATE) {
435 DuplicateEdges(target, intersectInDestSpace);
438 return target.forget();
441 /* static */
442 already_AddRefed<FilterNode> FilterNodeSoftware::Create(FilterType aType) {
443 RefPtr<FilterNodeSoftware> filter;
444 switch (aType) {
445 case FilterType::BLEND:
446 filter = new FilterNodeBlendSoftware();
447 break;
448 case FilterType::TRANSFORM:
449 filter = new FilterNodeTransformSoftware();
450 break;
451 case FilterType::MORPHOLOGY:
452 filter = new FilterNodeMorphologySoftware();
453 break;
454 case FilterType::COLOR_MATRIX:
455 filter = new FilterNodeColorMatrixSoftware();
456 break;
457 case FilterType::FLOOD:
458 filter = new FilterNodeFloodSoftware();
459 break;
460 case FilterType::TILE:
461 filter = new FilterNodeTileSoftware();
462 break;
463 case FilterType::TABLE_TRANSFER:
464 filter = new FilterNodeTableTransferSoftware();
465 break;
466 case FilterType::DISCRETE_TRANSFER:
467 filter = new FilterNodeDiscreteTransferSoftware();
468 break;
469 case FilterType::LINEAR_TRANSFER:
470 filter = new FilterNodeLinearTransferSoftware();
471 break;
472 case FilterType::GAMMA_TRANSFER:
473 filter = new FilterNodeGammaTransferSoftware();
474 break;
475 case FilterType::CONVOLVE_MATRIX:
476 filter = new FilterNodeConvolveMatrixSoftware();
477 break;
478 case FilterType::DISPLACEMENT_MAP:
479 filter = new FilterNodeDisplacementMapSoftware();
480 break;
481 case FilterType::TURBULENCE:
482 filter = new FilterNodeTurbulenceSoftware();
483 break;
484 case FilterType::ARITHMETIC_COMBINE:
485 filter = new FilterNodeArithmeticCombineSoftware();
486 break;
487 case FilterType::COMPOSITE:
488 filter = new FilterNodeCompositeSoftware();
489 break;
490 case FilterType::GAUSSIAN_BLUR:
491 filter = new FilterNodeGaussianBlurSoftware();
492 break;
493 case FilterType::DIRECTIONAL_BLUR:
494 filter = new FilterNodeDirectionalBlurSoftware();
495 break;
496 case FilterType::CROP:
497 filter = new FilterNodeCropSoftware();
498 break;
499 case FilterType::PREMULTIPLY:
500 filter = new FilterNodePremultiplySoftware();
501 break;
502 case FilterType::UNPREMULTIPLY:
503 filter = new FilterNodeUnpremultiplySoftware();
504 break;
505 case FilterType::OPACITY:
506 filter = new FilterNodeOpacitySoftware();
507 break;
508 case FilterType::POINT_DIFFUSE:
509 filter = new FilterNodeLightingSoftware<PointLightSoftware,
510 DiffuseLightingSoftware>(
511 "FilterNodeLightingSoftware<PointLight, DiffuseLighting>");
512 break;
513 case FilterType::POINT_SPECULAR:
514 filter = new FilterNodeLightingSoftware<PointLightSoftware,
515 SpecularLightingSoftware>(
516 "FilterNodeLightingSoftware<PointLight, SpecularLighting>");
517 break;
518 case FilterType::SPOT_DIFFUSE:
519 filter = new FilterNodeLightingSoftware<SpotLightSoftware,
520 DiffuseLightingSoftware>(
521 "FilterNodeLightingSoftware<SpotLight, DiffuseLighting>");
522 break;
523 case FilterType::SPOT_SPECULAR:
524 filter = new FilterNodeLightingSoftware<SpotLightSoftware,
525 SpecularLightingSoftware>(
526 "FilterNodeLightingSoftware<SpotLight, SpecularLighting>");
527 break;
528 case FilterType::DISTANT_DIFFUSE:
529 filter = new FilterNodeLightingSoftware<DistantLightSoftware,
530 DiffuseLightingSoftware>(
531 "FilterNodeLightingSoftware<DistantLight, DiffuseLighting>");
532 break;
533 case FilterType::DISTANT_SPECULAR:
534 filter = new FilterNodeLightingSoftware<DistantLightSoftware,
535 SpecularLightingSoftware>(
536 "FilterNodeLightingSoftware<DistantLight, SpecularLighting>");
537 break;
539 return filter.forget();
542 void FilterNodeSoftware::Draw(DrawTarget* aDrawTarget, const Rect& aSourceRect,
543 const Point& aDestPoint,
544 const DrawOptions& aOptions) {
545 #ifdef DEBUG_DUMP_SURFACES
546 printf("<style>section{margin:10px;}</style><pre>\nRendering filter %s...\n",
547 GetName());
548 #endif
550 Rect renderRect = aSourceRect;
551 renderRect.RoundOut();
552 IntRect renderIntRect;
553 if (!renderRect.ToIntRect(&renderIntRect)) {
554 #ifdef DEBUG_DUMP_SURFACES
555 printf("render rect overflowed, not painting anything\n");
556 printf("</pre>\n");
557 #endif
558 return;
561 IntRect outputRect = GetOutputRectInRect(renderIntRect);
562 if (outputRect.Overflows()) {
563 #ifdef DEBUG_DUMP_SURFACES
564 printf("output rect overflowed, not painting anything\n");
565 printf("</pre>\n");
566 #endif
567 return;
570 RefPtr<DataSourceSurface> result;
571 if (!outputRect.IsEmpty()) {
572 result = GetOutput(outputRect);
575 if (!result) {
576 // Null results are allowed and treated as transparent. Don't draw anything.
577 #ifdef DEBUG_DUMP_SURFACES
578 printf("output returned null\n");
579 printf("</pre>\n");
580 #endif
581 return;
584 #ifdef DEBUG_DUMP_SURFACES
585 printf("output from %s:\n", GetName());
586 printf("<img src='");
587 gfxUtils::DumpAsDataURL(result);
588 printf("'>\n");
589 printf("</pre>\n");
590 #endif
592 Point sourceToDestOffset = aDestPoint - aSourceRect.TopLeft();
593 Rect renderedSourceRect = Rect(outputRect).Intersect(aSourceRect);
594 Rect renderedDestRect = renderedSourceRect + sourceToDestOffset;
595 if (result->GetFormat() == SurfaceFormat::A8) {
596 // Interpret the result as having implicitly black color channels.
597 aDrawTarget->PushClipRect(renderedDestRect);
598 aDrawTarget->MaskSurface(
599 ColorPattern(DeviceColor::MaskOpaqueBlack()), result,
600 Point(outputRect.TopLeft()) + sourceToDestOffset, aOptions);
601 aDrawTarget->PopClip();
602 } else {
603 aDrawTarget->DrawSurface(result, renderedDestRect,
604 renderedSourceRect - Point(outputRect.TopLeft()),
605 DrawSurfaceOptions(), aOptions);
609 already_AddRefed<DataSourceSurface> FilterNodeSoftware::GetOutput(
610 const IntRect& aRect) {
611 MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect));
613 if (aRect.Overflows()) {
614 return nullptr;
617 IntRect cachedRect;
618 IntRect requestedRect;
619 RefPtr<DataSourceSurface> cachedOutput;
621 // Retrieve a cached surface if we have one and it can
622 // satisfy this request, or else request a rect we will compute and cache
623 if (!mCachedRect.Contains(aRect)) {
624 RequestRect(aRect);
625 requestedRect = mRequestedRect;
626 } else {
627 MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?");
628 cachedRect = mCachedRect;
629 cachedOutput = mCachedOutput;
632 if (!cachedOutput) {
633 // Compute the output
634 cachedOutput = Render(requestedRect);
636 // Update the cache for future requests
637 mCachedOutput = cachedOutput;
638 if (!mCachedOutput) {
639 mCachedRect = IntRect();
640 mRequestedRect = IntRect();
641 return nullptr;
643 mCachedRect = requestedRect;
644 mRequestedRect = IntRect();
646 cachedRect = mCachedRect;
649 return GetDataSurfaceInRect(cachedOutput, cachedRect, aRect, EDGE_MODE_NONE);
652 void FilterNodeSoftware::RequestRect(const IntRect& aRect) {
653 if (mRequestedRect.Contains(aRect)) {
654 // Bail out now. Otherwise pathological filters can spend time exponential
655 // in the number of primitives, e.g. if each primitive takes the
656 // previous primitive as its two inputs.
657 return;
659 mRequestedRect = mRequestedRect.Union(aRect);
660 RequestFromInputsForRect(aRect);
663 IntRect FilterNodeSoftware::MapInputRectToSource(uint32_t aInputEnumIndex,
664 const IntRect& aRect,
665 const IntRect& aMax,
666 FilterNode* aSourceNode) {
667 int32_t inputIndex = InputIndex(aInputEnumIndex);
668 if (inputIndex < 0) {
669 gfxDevCrash(LogReason::FilterInputError)
670 << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
671 return aMax;
673 if ((uint32_t)inputIndex < NumberOfSetInputs()) {
674 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
675 // If we have any input filters call into them to do the mapping,
676 // otherwise we can assume an input surface will be used
677 // and just return aRect.
678 if (filter) {
679 return filter->MapRectToSource(aRect, aMax, aSourceNode);
682 // We have an input surface instead of a filter
683 // so check if we're the target node.
684 if (this == aSourceNode) {
685 return aRect;
687 return IntRect();
690 void FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex,
691 const IntRect& aRect) {
692 if (aRect.Overflows()) {
693 return;
696 int32_t inputIndex = InputIndex(aInputEnumIndex);
697 if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
698 gfxDevCrash(LogReason::FilterInputError)
699 << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
700 return;
702 if (mInputSurfaces[inputIndex]) {
703 return;
705 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
706 MOZ_ASSERT(filter, "missing input");
708 filter->RequestRect(filter->GetOutputRectInRect(aRect));
711 SurfaceFormat FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat,
712 FormatHint aFormatHint) {
713 if (aCurrentFormat == SurfaceFormat::A8 && aFormatHint == CAN_HANDLE_A8) {
714 return SurfaceFormat::A8;
716 return SurfaceFormat::B8G8R8A8;
719 already_AddRefed<DataSourceSurface>
720 FilterNodeSoftware::GetInputDataSourceSurface(
721 uint32_t aInputEnumIndex, const IntRect& aRect, FormatHint aFormatHint,
722 ConvolveMatrixEdgeMode aEdgeMode,
723 const IntRect* aTransparencyPaddedSourceRect) {
724 if (aRect.Overflows()) {
725 return nullptr;
728 #ifdef DEBUG_DUMP_SURFACES
729 printf(
730 "<section><h1>GetInputDataSourceSurface with aRect: %d, %d, %d, "
731 "%d</h1>\n",
732 aRect.x, aRect.y, aRect.Width(), aRect.Height());
733 #endif
734 int32_t inputIndex = InputIndex(aInputEnumIndex);
735 if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
736 gfxDevCrash(LogReason::FilterInputData)
737 << "Invalid data " << inputIndex << " vs. " << NumberOfSetInputs();
738 return nullptr;
741 if (aRect.IsEmpty()) {
742 return nullptr;
745 RefPtr<SourceSurface> surface;
746 IntRect surfaceRect;
748 if (mInputSurfaces[inputIndex]) {
749 // Input from input surface
750 surface = mInputSurfaces[inputIndex];
751 #ifdef DEBUG_DUMP_SURFACES
752 printf("input from input surface:\n");
753 #endif
754 surfaceRect = surface->GetRect();
755 } else {
756 // Input from input filter
757 #ifdef DEBUG_DUMP_SURFACES
758 printf("getting input from input filter %s...\n",
759 mInputFilters[inputIndex]->GetName());
760 #endif
761 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
762 MOZ_ASSERT(filter, "missing input");
763 IntRect inputFilterOutput = filter->GetOutputRectInRect(aRect);
764 if (!inputFilterOutput.IsEmpty()) {
765 surface = filter->GetOutput(inputFilterOutput);
767 #ifdef DEBUG_DUMP_SURFACES
768 printf("input from input filter %s:\n",
769 mInputFilters[inputIndex]->GetName());
770 #endif
771 surfaceRect = inputFilterOutput;
772 MOZ_ASSERT(!surface || surfaceRect.Size() == surface->GetSize());
775 if (surface && surface->GetFormat() == SurfaceFormat::UNKNOWN) {
776 #ifdef DEBUG_DUMP_SURFACES
777 printf("wrong input format</section>\n\n");
778 #endif
779 return nullptr;
782 if (!surfaceRect.IsEmpty() && !surface) {
783 #ifdef DEBUG_DUMP_SURFACES
784 printf(" -- no input --</section>\n\n");
785 #endif
786 return nullptr;
789 if (aTransparencyPaddedSourceRect &&
790 !aTransparencyPaddedSourceRect->IsEmpty()) {
791 IntRect srcRect = aTransparencyPaddedSourceRect->Intersect(aRect);
792 surface =
793 GetDataSurfaceInRect(surface, surfaceRect, srcRect, EDGE_MODE_NONE);
794 if (surface) {
795 surfaceRect = srcRect;
796 } else {
797 // Padding the surface with transparency failed, probably due to size
798 // restrictions. Since |surface| is now null, set the surfaceRect to
799 // empty so that we're consistent.
800 surfaceRect.SetEmpty();
804 RefPtr<DataSourceSurface> result =
805 GetDataSurfaceInRect(surface, surfaceRect, aRect, aEdgeMode);
807 if (result) {
808 // TODO: This isn't safe since we don't have a guarantee
809 // that future Maps will have the same stride
810 DataSourceSurface::MappedSurface map;
811 if (result->Map(DataSourceSurface::READ, &map)) {
812 // Unmap immediately since CloneAligned hasn't been updated
813 // to use the Map API yet. We can still read the stride/data
814 // values as long as we don't try to dereference them.
815 result->Unmap();
816 if (map.mStride != GetAlignedStride<16>(map.mStride, 1) ||
817 reinterpret_cast<uintptr_t>(map.mData) % 16 != 0) {
818 // Align unaligned surface.
819 result = CloneAligned(result);
821 } else {
822 result = nullptr;
826 if (!result) {
827 #ifdef DEBUG_DUMP_SURFACES
828 printf(" -- no input --</section>\n\n");
829 #endif
830 return nullptr;
833 SurfaceFormat currentFormat = result->GetFormat();
834 if (DesiredFormat(currentFormat, aFormatHint) == SurfaceFormat::B8G8R8A8 &&
835 currentFormat != SurfaceFormat::B8G8R8A8) {
836 result = FilterProcessing::ConvertToB8G8R8A8(result);
839 #ifdef DEBUG_DUMP_SURFACES
840 printf("<img src='");
841 gfxUtils::DumpAsDataURL(result);
842 printf("'></section>");
843 #endif
845 MOZ_ASSERT(!result || result->GetSize() == aRect.Size(),
846 "wrong surface size");
848 return result.forget();
851 IntRect FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex,
852 const IntRect& aInRect) {
853 if (aInRect.Overflows()) {
854 return IntRect();
857 int32_t inputIndex = InputIndex(aInputEnumIndex);
858 if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
859 gfxDevCrash(LogReason::FilterInputRect)
860 << "Invalid rect " << inputIndex << " vs. " << NumberOfSetInputs();
861 return IntRect();
863 if (mInputSurfaces[inputIndex]) {
864 return aInRect.Intersect(mInputSurfaces[inputIndex]->GetRect());
866 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
867 MOZ_ASSERT(filter, "missing input");
868 return filter->GetOutputRectInRect(aInRect);
871 size_t FilterNodeSoftware::NumberOfSetInputs() {
872 return std::max(mInputSurfaces.size(), mInputFilters.size());
875 void FilterNodeSoftware::AddInvalidationListener(
876 FilterInvalidationListener* aListener) {
877 MOZ_ASSERT(aListener, "null listener");
878 mInvalidationListeners.push_back(aListener);
881 void FilterNodeSoftware::RemoveInvalidationListener(
882 FilterInvalidationListener* aListener) {
883 MOZ_ASSERT(aListener, "null listener");
884 std::vector<FilterInvalidationListener*>::iterator it = std::find(
885 mInvalidationListeners.begin(), mInvalidationListeners.end(), aListener);
886 mInvalidationListeners.erase(it);
889 void FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware* aFilter) {
890 Invalidate();
893 void FilterNodeSoftware::Invalidate() {
894 mCachedOutput = nullptr;
895 mCachedRect = IntRect();
896 for (std::vector<FilterInvalidationListener*>::iterator it =
897 mInvalidationListeners.begin();
898 it != mInvalidationListeners.end(); it++) {
899 (*it)->FilterInvalidated(this);
903 FilterNodeSoftware::FilterNodeSoftware() {}
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;
1013 MOZ_ASSERT_UNREACHABLE("Unexpected BlendMode");
1014 return CompositionOp::OP_OVER;
1017 already_AddRefed<DataSourceSurface> FilterNodeBlendSoftware::Render(
1018 const IntRect& aRect) {
1019 RefPtr<DataSourceSurface> input1 =
1020 GetInputDataSourceSurface(IN_BLEND_IN, aRect, NEED_COLOR_CHANNELS);
1021 RefPtr<DataSourceSurface> input2 =
1022 GetInputDataSourceSurface(IN_BLEND_IN2, aRect, NEED_COLOR_CHANNELS);
1024 // Null inputs need to be treated as transparent.
1026 // First case: both are transparent.
1027 if (!input1 && !input2) {
1028 // Then the result is transparent, too.
1029 return nullptr;
1032 // Second case: one of them is transparent. Return the non-transparent one.
1033 if (!input1 || !input2) {
1034 return input1 ? input1.forget() : input2.forget();
1037 // Third case: both are non-transparent.
1038 // Apply normal filtering.
1039 RefPtr<DataSourceSurface> target =
1040 FilterProcessing::ApplyBlending(input1, input2, mBlendMode);
1041 if (target != nullptr) {
1042 return target.forget();
1045 IntSize size = input1->GetSize();
1046 target = Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1047 if (MOZ2D_WARN_IF(!target)) {
1048 return nullptr;
1051 CopyRect(input1, target, IntRect(IntPoint(), size), IntPoint());
1053 // This needs to stay in scope until the draw target has been flushed.
1054 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::READ_WRITE);
1055 if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
1056 return nullptr;
1059 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
1060 BackendType::SKIA, targetMap.GetData(), target->GetSize(),
1061 targetMap.GetStride(), target->GetFormat());
1063 if (!dt) {
1064 gfxWarning()
1065 << "FilterNodeBlendSoftware::Render failed in CreateDrawTargetForData";
1066 return nullptr;
1069 Rect r(0, 0, size.width, size.height);
1070 dt->DrawSurface(input2, r, r, DrawSurfaceOptions(),
1071 DrawOptions(1.0f, ToBlendOp(mBlendMode)));
1072 dt->Flush();
1073 return target.forget();
1076 void FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect& aRect) {
1077 RequestInputRect(IN_BLEND_IN, aRect);
1078 RequestInputRect(IN_BLEND_IN2, aRect);
1081 IntRect FilterNodeBlendSoftware::MapRectToSource(const IntRect& aRect,
1082 const IntRect& aMax,
1083 FilterNode* aSourceNode) {
1084 IntRect result = MapInputRectToSource(IN_BLEND_IN, aRect, aMax, aSourceNode);
1085 result.OrWith(MapInputRectToSource(IN_BLEND_IN2, aRect, aMax, aSourceNode));
1086 return result;
1089 IntRect FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect& aRect) {
1090 return GetInputRectInRect(IN_BLEND_IN, aRect)
1091 .Union(GetInputRectInRect(IN_BLEND_IN2, aRect))
1092 .Intersect(aRect);
1095 FilterNodeTransformSoftware::FilterNodeTransformSoftware()
1096 : mSamplingFilter(SamplingFilter::GOOD) {}
1098 int32_t FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex) {
1099 switch (aInputEnumIndex) {
1100 case IN_TRANSFORM_IN:
1101 return 0;
1102 default:
1103 return -1;
1107 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex,
1108 uint32_t aFilter) {
1109 MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER);
1110 mSamplingFilter = static_cast<SamplingFilter>(aFilter);
1111 Invalidate();
1114 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex,
1115 const Matrix& aMatrix) {
1116 MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX);
1117 mMatrix = aMatrix;
1118 Invalidate();
1121 IntRect FilterNodeTransformSoftware::SourceRectForOutputRect(
1122 const IntRect& aRect) {
1123 if (aRect.IsEmpty()) {
1124 return IntRect();
1127 Matrix inverted(mMatrix);
1128 if (!inverted.Invert()) {
1129 return IntRect();
1132 Rect neededRect = inverted.TransformBounds(Rect(aRect));
1133 neededRect.RoundOut();
1134 IntRect neededIntRect;
1135 if (!neededRect.ToIntRect(&neededIntRect)) {
1136 return IntRect();
1138 return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect);
1141 IntRect FilterNodeTransformSoftware::MapRectToSource(const IntRect& aRect,
1142 const IntRect& aMax,
1143 FilterNode* aSourceNode) {
1144 if (aRect.IsEmpty()) {
1145 return IntRect();
1148 Matrix inverted(mMatrix);
1149 if (!inverted.Invert()) {
1150 return aMax;
1153 Rect neededRect = inverted.TransformBounds(Rect(aRect));
1154 neededRect.RoundOut();
1155 IntRect neededIntRect;
1156 if (!neededRect.ToIntRect(&neededIntRect)) {
1157 return aMax;
1159 return MapInputRectToSource(IN_TRANSFORM_IN, neededIntRect, aMax,
1160 aSourceNode);
1163 already_AddRefed<DataSourceSurface> FilterNodeTransformSoftware::Render(
1164 const IntRect& aRect) {
1165 IntRect srcRect = SourceRectForOutputRect(aRect);
1167 RefPtr<DataSourceSurface> input =
1168 GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect);
1170 if (!input) {
1171 return nullptr;
1174 Matrix transform = Matrix::Translation(srcRect.X(), srcRect.Y()) * mMatrix *
1175 Matrix::Translation(-aRect.X(), -aRect.Y());
1176 if (transform.IsIdentity() && srcRect.Size() == aRect.Size()) {
1177 return input.forget();
1180 RefPtr<DataSourceSurface> surf =
1181 Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat(), true);
1183 if (!surf) {
1184 return nullptr;
1187 DataSourceSurface::MappedSurface mapping;
1188 if (!surf->Map(DataSourceSurface::MapType::WRITE, &mapping)) {
1189 gfxCriticalError()
1190 << "FilterNodeTransformSoftware::Render failed to map surface";
1191 return nullptr;
1194 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
1195 BackendType::SKIA, mapping.mData, surf->GetSize(), mapping.mStride,
1196 surf->GetFormat());
1197 if (!dt) {
1198 gfxWarning() << "FilterNodeTransformSoftware::Render failed in "
1199 "CreateDrawTargetForData";
1200 return nullptr;
1203 Rect r(0, 0, srcRect.Width(), srcRect.Height());
1204 dt->SetTransform(transform);
1205 dt->DrawSurface(input, r, r, DrawSurfaceOptions(mSamplingFilter));
1207 dt->Flush();
1208 surf->Unmap();
1209 return surf.forget();
1212 void FilterNodeTransformSoftware::RequestFromInputsForRect(
1213 const IntRect& aRect) {
1214 RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect));
1217 IntRect FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect) {
1218 IntRect srcRect = SourceRectForOutputRect(aRect);
1219 if (srcRect.IsEmpty()) {
1220 return IntRect();
1223 Rect outRect = mMatrix.TransformBounds(Rect(srcRect));
1224 outRect.RoundOut();
1225 IntRect outIntRect;
1226 if (!outRect.ToIntRect(&outIntRect)) {
1227 return IntRect();
1229 return outIntRect.Intersect(aRect);
1232 FilterNodeMorphologySoftware::FilterNodeMorphologySoftware()
1233 : mOperator(MORPHOLOGY_OPERATOR_ERODE) {}
1235 int32_t FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex) {
1236 switch (aInputEnumIndex) {
1237 case IN_MORPHOLOGY_IN:
1238 return 0;
1239 default:
1240 return -1;
1244 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
1245 const IntSize& aRadii) {
1246 MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_RADII);
1247 mRadii.width = std::min(std::max(aRadii.width, 0), 100000);
1248 mRadii.height = std::min(std::max(aRadii.height, 0), 100000);
1249 Invalidate();
1252 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
1253 uint32_t aOperator) {
1254 MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_OPERATOR);
1255 mOperator = static_cast<MorphologyOperator>(aOperator);
1256 Invalidate();
1259 static already_AddRefed<DataSourceSurface> ApplyMorphology(
1260 const IntRect& aSourceRect, DataSourceSurface* aInput,
1261 const IntRect& aDestRect, int32_t rx, int32_t ry,
1262 MorphologyOperator aOperator) {
1263 IntRect srcRect = aSourceRect - aDestRect.TopLeft();
1264 IntRect destRect = aDestRect - aDestRect.TopLeft();
1265 IntRect tmpRect(destRect.X(), srcRect.Y(), destRect.Width(),
1266 srcRect.Height());
1267 #ifdef DEBUG
1268 IntMargin margin = srcRect - destRect;
1269 MOZ_ASSERT(margin.top >= ry && margin.right >= rx && margin.bottom >= ry &&
1270 margin.left >= rx,
1271 "insufficient margin");
1272 #endif
1274 RefPtr<DataSourceSurface> tmp;
1275 if (rx == 0) {
1276 tmp = aInput;
1277 } else {
1278 tmp = Factory::CreateDataSourceSurface(tmpRect.Size(),
1279 SurfaceFormat::B8G8R8A8);
1280 if (MOZ2D_WARN_IF(!tmp)) {
1281 return nullptr;
1284 DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
1285 DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::WRITE);
1286 if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !tmpMap.IsMapped())) {
1287 return nullptr;
1289 uint8_t* sourceData = DataAtOffset(aInput, sourceMap.GetMappedSurface(),
1290 destRect.TopLeft() - srcRect.TopLeft());
1291 uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(),
1292 destRect.TopLeft() - tmpRect.TopLeft());
1294 FilterProcessing::ApplyMorphologyHorizontal(
1295 sourceData, sourceMap.GetStride(), tmpData, tmpMap.GetStride(), tmpRect,
1296 rx, aOperator);
1299 RefPtr<DataSourceSurface> dest;
1300 if (ry == 0) {
1301 dest = tmp;
1302 } else {
1303 dest = Factory::CreateDataSourceSurface(destRect.Size(),
1304 SurfaceFormat::B8G8R8A8);
1305 if (MOZ2D_WARN_IF(!dest)) {
1306 return nullptr;
1309 DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::READ);
1310 DataSourceSurface::ScopedMap destMap(dest, DataSourceSurface::WRITE);
1311 if (MOZ2D_WARN_IF(!tmpMap.IsMapped() || !destMap.IsMapped())) {
1312 return nullptr;
1314 int32_t tmpStride = tmpMap.GetStride();
1315 uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(),
1316 destRect.TopLeft() - tmpRect.TopLeft());
1318 int32_t destStride = destMap.GetStride();
1319 uint8_t* destData = destMap.GetData();
1321 FilterProcessing::ApplyMorphologyVertical(
1322 tmpData, tmpStride, destData, destStride, destRect, ry, aOperator);
1325 return dest.forget();
1328 already_AddRefed<DataSourceSurface> FilterNodeMorphologySoftware::Render(
1329 const IntRect& aRect) {
1330 IntRect srcRect = aRect;
1331 srcRect.Inflate(mRadii);
1333 RefPtr<DataSourceSurface> input =
1334 GetInputDataSourceSurface(IN_MORPHOLOGY_IN, srcRect, NEED_COLOR_CHANNELS);
1335 if (!input) {
1336 return nullptr;
1339 int32_t rx = mRadii.width;
1340 int32_t ry = mRadii.height;
1342 if (rx == 0 && ry == 0) {
1343 return input.forget();
1346 return ApplyMorphology(srcRect, input, aRect, rx, ry, mOperator);
1349 void FilterNodeMorphologySoftware::RequestFromInputsForRect(
1350 const IntRect& aRect) {
1351 IntRect srcRect = aRect;
1352 srcRect.Inflate(mRadii);
1353 RequestInputRect(IN_MORPHOLOGY_IN, srcRect);
1356 IntRect FilterNodeMorphologySoftware::GetOutputRectInRect(
1357 const IntRect& aRect) {
1358 IntRect inflatedSourceRect = aRect;
1359 inflatedSourceRect.Inflate(mRadii);
1360 IntRect inputRect = GetInputRectInRect(IN_MORPHOLOGY_IN, inflatedSourceRect);
1361 if (mOperator == MORPHOLOGY_OPERATOR_ERODE) {
1362 inputRect.Deflate(mRadii);
1363 } else {
1364 inputRect.Inflate(mRadii);
1366 return inputRect.Intersect(aRect);
1369 int32_t FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) {
1370 switch (aInputEnumIndex) {
1371 case IN_COLOR_MATRIX_IN:
1372 return 0;
1373 default:
1374 return -1;
1378 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
1379 const Matrix5x4& aMatrix) {
1380 MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX);
1381 mMatrix = aMatrix;
1382 Invalidate();
1385 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
1386 uint32_t aAlphaMode) {
1387 MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE);
1388 mAlphaMode = (AlphaMode)aAlphaMode;
1389 Invalidate();
1392 static already_AddRefed<DataSourceSurface> Premultiply(
1393 DataSourceSurface* aSurface) {
1394 if (aSurface->GetFormat() == SurfaceFormat::A8) {
1395 RefPtr<DataSourceSurface> surface(aSurface);
1396 return surface.forget();
1399 IntSize size = aSurface->GetSize();
1400 RefPtr<DataSourceSurface> target =
1401 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1402 if (MOZ2D_WARN_IF(!target)) {
1403 return nullptr;
1406 DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
1407 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
1408 if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
1409 return nullptr;
1412 uint8_t* inputData = inputMap.GetData();
1413 int32_t inputStride = inputMap.GetStride();
1414 uint8_t* targetData = targetMap.GetData();
1415 int32_t targetStride = targetMap.GetStride();
1417 FilterProcessing::DoPremultiplicationCalculation(
1418 size, targetData, targetStride, inputData, inputStride);
1420 return target.forget();
1423 static already_AddRefed<DataSourceSurface> Unpremultiply(
1424 DataSourceSurface* aSurface) {
1425 if (aSurface->GetFormat() == SurfaceFormat::A8) {
1426 RefPtr<DataSourceSurface> surface(aSurface);
1427 return surface.forget();
1430 IntSize size = aSurface->GetSize();
1431 RefPtr<DataSourceSurface> target =
1432 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1433 if (MOZ2D_WARN_IF(!target)) {
1434 return nullptr;
1437 DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
1438 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
1439 if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
1440 return nullptr;
1443 uint8_t* inputData = inputMap.GetData();
1444 int32_t inputStride = inputMap.GetStride();
1445 uint8_t* targetData = targetMap.GetData();
1446 int32_t targetStride = targetMap.GetStride();
1448 FilterProcessing::DoUnpremultiplicationCalculation(
1449 size, targetData, targetStride, inputData, inputStride);
1451 return target.forget();
1454 static already_AddRefed<DataSourceSurface> Opacity(DataSourceSurface* aSurface,
1455 Float aValue) {
1456 if (aValue == 1.0f) {
1457 RefPtr<DataSourceSurface> surface(aSurface);
1458 return surface.forget();
1461 IntSize size = aSurface->GetSize();
1462 RefPtr<DataSourceSurface> target =
1463 Factory::CreateDataSourceSurface(size, aSurface->GetFormat());
1464 if (MOZ2D_WARN_IF(!target)) {
1465 return nullptr;
1468 DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
1469 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
1470 if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
1471 return nullptr;
1474 uint8_t* inputData = inputMap.GetData();
1475 int32_t inputStride = inputMap.GetStride();
1476 uint8_t* targetData = targetMap.GetData();
1477 int32_t targetStride = targetMap.GetStride();
1479 if (aSurface->GetFormat() == SurfaceFormat::A8) {
1480 FilterProcessing::DoOpacityCalculationA8(size, targetData, targetStride,
1481 inputData, inputStride, aValue);
1482 } else {
1483 MOZ_ASSERT(aSurface->GetFormat() == SurfaceFormat::B8G8R8A8);
1484 FilterProcessing::DoOpacityCalculation(size, targetData, targetStride,
1485 inputData, inputStride, aValue);
1488 return target.forget();
1491 already_AddRefed<DataSourceSurface> FilterNodeColorMatrixSoftware::Render(
1492 const IntRect& aRect) {
1493 RefPtr<DataSourceSurface> input =
1494 GetInputDataSourceSurface(IN_COLOR_MATRIX_IN, aRect, NEED_COLOR_CHANNELS);
1495 if (!input) {
1496 return nullptr;
1499 if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
1500 input = Unpremultiply(input);
1503 RefPtr<DataSourceSurface> result =
1504 FilterProcessing::ApplyColorMatrix(input, mMatrix);
1506 if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
1507 result = Premultiply(result);
1510 return result.forget();
1513 void FilterNodeColorMatrixSoftware::RequestFromInputsForRect(
1514 const IntRect& aRect) {
1515 RequestInputRect(IN_COLOR_MATRIX_IN, aRect);
1518 IntRect FilterNodeColorMatrixSoftware::MapRectToSource(
1519 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
1520 return MapInputRectToSource(IN_COLOR_MATRIX_IN, aRect, aMax, aSourceNode);
1523 IntRect FilterNodeColorMatrixSoftware::GetOutputRectInRect(
1524 const IntRect& aRect) {
1525 if (mMatrix._54 > 0.0f) {
1526 return aRect;
1528 return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect);
1531 void FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex,
1532 const DeviceColor& aColor) {
1533 MOZ_ASSERT(aIndex == ATT_FLOOD_COLOR);
1534 mColor = aColor;
1535 Invalidate();
1538 static uint32_t ColorToBGRA(const DeviceColor& aColor) {
1539 union {
1540 uint32_t color;
1541 uint8_t components[4];
1543 components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
1544 NS_lround(aColor.r * aColor.a * 255.0f);
1545 components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
1546 NS_lround(aColor.g * aColor.a * 255.0f);
1547 components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
1548 NS_lround(aColor.b * aColor.a * 255.0f);
1549 components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = NS_lround(aColor.a * 255.0f);
1550 return color;
1553 static SurfaceFormat FormatForColor(DeviceColor aColor) {
1554 if (aColor.r == 0 && aColor.g == 0 && aColor.b == 0) {
1555 return SurfaceFormat::A8;
1557 return SurfaceFormat::B8G8R8A8;
1560 already_AddRefed<DataSourceSurface> FilterNodeFloodSoftware::Render(
1561 const IntRect& aRect) {
1562 SurfaceFormat format = FormatForColor(mColor);
1563 RefPtr<DataSourceSurface> target =
1564 Factory::CreateDataSourceSurface(aRect.Size(), format);
1565 if (MOZ2D_WARN_IF(!target)) {
1566 return nullptr;
1569 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
1570 if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
1571 return nullptr;
1574 uint8_t* targetData = targetMap.GetData();
1575 int32_t stride = targetMap.GetStride();
1577 if (format == SurfaceFormat::B8G8R8A8) {
1578 uint32_t color = ColorToBGRA(mColor);
1579 for (int32_t y = 0; y < aRect.Height(); y++) {
1580 for (int32_t x = 0; x < aRect.Width(); x++) {
1581 *((uint32_t*)targetData + x) = color;
1583 PodZero(&targetData[aRect.Width() * 4], stride - aRect.Width() * 4);
1584 targetData += stride;
1586 } else if (format == SurfaceFormat::A8) {
1587 uint8_t alpha = NS_lround(mColor.a * 255.0f);
1588 for (int32_t y = 0; y < aRect.Height(); y++) {
1589 for (int32_t x = 0; x < aRect.Width(); x++) {
1590 targetData[x] = alpha;
1592 PodZero(&targetData[aRect.Width()], stride - aRect.Width());
1593 targetData += stride;
1595 } else {
1596 gfxDevCrash(LogReason::FilterInputFormat)
1597 << "Bad format in flood render " << (int)format;
1598 return nullptr;
1601 return target.forget();
1604 // Override GetOutput to get around caching. Rendering simple floods is
1605 // comparatively fast.
1606 already_AddRefed<DataSourceSurface> FilterNodeFloodSoftware::GetOutput(
1607 const IntRect& aRect) {
1608 return Render(aRect);
1611 IntRect FilterNodeFloodSoftware::MapRectToSource(const IntRect& aRect,
1612 const IntRect& aMax,
1613 FilterNode* aSourceNode) {
1614 return IntRect();
1617 IntRect FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect& aRect) {
1618 if (mColor.a == 0.0f) {
1619 return IntRect();
1621 return aRect;
1624 int32_t FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex) {
1625 switch (aInputEnumIndex) {
1626 case IN_TILE_IN:
1627 return 0;
1628 default:
1629 return -1;
1633 void FilterNodeTileSoftware::SetAttribute(uint32_t aIndex,
1634 const IntRect& aSourceRect) {
1635 MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT);
1636 mSourceRect.SetRect(int32_t(aSourceRect.X()), int32_t(aSourceRect.Y()),
1637 int32_t(aSourceRect.Width()),
1638 int32_t(aSourceRect.Height()));
1639 Invalidate();
1642 namespace {
1643 struct CompareIntRects {
1644 bool operator()(const IntRect& a, const IntRect& b) const {
1645 if (a.X() != b.X()) {
1646 return a.X() < b.X();
1648 if (a.Y() != b.Y()) {
1649 return a.Y() < b.Y();
1651 if (a.Width() != b.Width()) {
1652 return a.Width() < b.Width();
1654 return a.Height() < b.Height();
1658 } // namespace
1660 already_AddRefed<DataSourceSurface> FilterNodeTileSoftware::Render(
1661 const IntRect& aRect) {
1662 if (mSourceRect.IsEmpty()) {
1663 return nullptr;
1666 if (mSourceRect.Contains(aRect)) {
1667 return GetInputDataSourceSurface(IN_TILE_IN, aRect);
1670 RefPtr<DataSourceSurface> target;
1672 typedef std::map<IntRect, RefPtr<DataSourceSurface>, CompareIntRects>
1673 InputMap;
1674 InputMap inputs;
1676 IntPoint startIndex = TileIndex(mSourceRect, aRect.TopLeft());
1677 IntPoint endIndex = TileIndex(mSourceRect, aRect.BottomRight());
1678 for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
1679 for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
1680 IntPoint sourceToDestOffset(ix * mSourceRect.Width(),
1681 iy * mSourceRect.Height());
1682 IntRect destRect = aRect.Intersect(mSourceRect + sourceToDestOffset);
1683 IntRect srcRect = destRect - sourceToDestOffset;
1684 if (srcRect.IsEmpty()) {
1685 continue;
1688 RefPtr<DataSourceSurface> input;
1689 InputMap::iterator it = inputs.find(srcRect);
1690 if (it == inputs.end()) {
1691 input = GetInputDataSourceSurface(IN_TILE_IN, srcRect);
1692 inputs[srcRect] = input;
1693 } else {
1694 input = it->second;
1696 if (!input) {
1697 return nullptr;
1699 if (!target) {
1700 // We delay creating the target until now because we want to use the
1701 // same format as our input filter, and we do not actually know the
1702 // input format before we call GetInputDataSourceSurface.
1703 target =
1704 Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat());
1705 if (MOZ2D_WARN_IF(!target)) {
1706 return nullptr;
1710 if (input->GetFormat() != target->GetFormat()) {
1711 // Different rectangles of the input can have different formats. If
1712 // that happens, just convert everything to B8G8R8A8.
1713 target = FilterProcessing::ConvertToB8G8R8A8(target);
1714 input = FilterProcessing::ConvertToB8G8R8A8(input);
1715 if (MOZ2D_WARN_IF(!target) || MOZ2D_WARN_IF(!input)) {
1716 return nullptr;
1720 CopyRect(input, target, srcRect - srcRect.TopLeft(),
1721 destRect.TopLeft() - aRect.TopLeft());
1725 return target.forget();
1728 void FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect& aRect) {
1729 // Do not request anything.
1730 // Source rects for the tile filter can be discontinuous with large gaps
1731 // between them. Requesting those from our input filter might cause it to
1732 // render the whole bounding box of all of them, which would be wasteful.
1735 IntRect FilterNodeTileSoftware::GetOutputRectInRect(const IntRect& aRect) {
1736 return aRect;
1739 FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware()
1740 : mDisableR(true), mDisableG(true), mDisableB(true), mDisableA(true) {}
1742 void FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex,
1743 bool aDisable) {
1744 switch (aIndex) {
1745 case ATT_TRANSFER_DISABLE_R:
1746 mDisableR = aDisable;
1747 break;
1748 case ATT_TRANSFER_DISABLE_G:
1749 mDisableG = aDisable;
1750 break;
1751 case ATT_TRANSFER_DISABLE_B:
1752 mDisableB = aDisable;
1753 break;
1754 case ATT_TRANSFER_DISABLE_A:
1755 mDisableA = aDisable;
1756 break;
1757 default:
1758 MOZ_CRASH("GFX: FilterNodeComponentTransferSoftware::SetAttribute");
1760 Invalidate();
1763 void FilterNodeComponentTransferSoftware::GenerateLookupTable(
1764 ptrdiff_t aComponent, uint8_t aTables[4][256], bool aDisabled) {
1765 if (aDisabled) {
1766 for (int32_t i = 0; i < 256; ++i) {
1767 aTables[aComponent][i] = i;
1769 } else {
1770 FillLookupTable(aComponent, aTables[aComponent]);
1774 template <uint32_t BytesPerPixel>
1775 static void TransferComponents(
1776 DataSourceSurface* aInput, DataSourceSurface* aTarget,
1777 const uint8_t aLookupTables[BytesPerPixel][256]) {
1778 MOZ_ASSERT(aInput->GetFormat() == aTarget->GetFormat(), "different formats");
1779 IntSize size = aInput->GetSize();
1781 DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
1782 DataSourceSurface::ScopedMap targetMap(aTarget, DataSourceSurface::WRITE);
1783 if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
1784 return;
1787 uint8_t* sourceData = sourceMap.GetData();
1788 int32_t sourceStride = sourceMap.GetStride();
1789 uint8_t* targetData = targetMap.GetData();
1790 int32_t targetStride = targetMap.GetStride();
1792 MOZ_ASSERT(sourceStride <= targetStride, "target smaller than source");
1794 for (int32_t y = 0; y < size.height; y++) {
1795 for (int32_t x = 0; x < size.width; x++) {
1796 uint32_t sourceIndex = y * sourceStride + x * BytesPerPixel;
1797 uint32_t targetIndex = y * targetStride + x * BytesPerPixel;
1798 for (uint32_t i = 0; i < BytesPerPixel; i++) {
1799 targetData[targetIndex + i] =
1800 aLookupTables[i][sourceData[sourceIndex + i]];
1804 // Zero padding to keep valgrind happy.
1805 PodZero(&targetData[y * targetStride + size.width * BytesPerPixel],
1806 targetStride - size.width * BytesPerPixel);
1810 static bool IsAllZero(const uint8_t aLookupTable[256]) {
1811 for (int32_t i = 0; i < 256; i++) {
1812 if (aLookupTable[i] != 0) {
1813 return false;
1816 return true;
1819 already_AddRefed<DataSourceSurface> FilterNodeComponentTransferSoftware::Render(
1820 const IntRect& aRect) {
1821 if (mDisableR && mDisableG && mDisableB && mDisableA) {
1822 return GetInputDataSourceSurface(IN_TRANSFER_IN, aRect);
1825 uint8_t lookupTables[4][256];
1826 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R, lookupTables, mDisableR);
1827 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G, lookupTables, mDisableG);
1828 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B, lookupTables, mDisableB);
1829 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A, lookupTables, mDisableA);
1831 bool needColorChannels =
1832 lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R][0] != 0 ||
1833 lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G][0] != 0 ||
1834 lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B][0] != 0;
1836 FormatHint pref = needColorChannels ? NEED_COLOR_CHANNELS : CAN_HANDLE_A8;
1838 RefPtr<DataSourceSurface> input =
1839 GetInputDataSourceSurface(IN_TRANSFER_IN, aRect, pref);
1840 if (!input) {
1841 return nullptr;
1844 if (input->GetFormat() == SurfaceFormat::B8G8R8A8 && !needColorChannels) {
1845 bool colorChannelsBecomeBlack =
1846 IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) &&
1847 IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) &&
1848 IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B]);
1850 if (colorChannelsBecomeBlack) {
1851 input = FilterProcessing::ExtractAlpha(input);
1855 SurfaceFormat format = input->GetFormat();
1856 if (format == SurfaceFormat::A8 && mDisableA) {
1857 return input.forget();
1860 RefPtr<DataSourceSurface> target =
1861 Factory::CreateDataSourceSurface(aRect.Size(), format);
1862 if (MOZ2D_WARN_IF(!target)) {
1863 return nullptr;
1866 if (format == SurfaceFormat::A8) {
1867 TransferComponents<1>(input, target,
1868 &lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_A]);
1869 } else {
1870 TransferComponents<4>(input, target, lookupTables);
1873 return target.forget();
1876 void FilterNodeComponentTransferSoftware::RequestFromInputsForRect(
1877 const IntRect& aRect) {
1878 RequestInputRect(IN_TRANSFER_IN, aRect);
1881 IntRect FilterNodeComponentTransferSoftware::MapRectToSource(
1882 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
1883 return MapInputRectToSource(IN_TRANSFER_IN, aRect, aMax, aSourceNode);
1886 IntRect FilterNodeComponentTransferSoftware::GetOutputRectInRect(
1887 const IntRect& aRect) {
1888 if (mDisableA) {
1889 return GetInputRectInRect(IN_TRANSFER_IN, aRect);
1891 return aRect;
1894 int32_t FilterNodeComponentTransferSoftware::InputIndex(
1895 uint32_t aInputEnumIndex) {
1896 switch (aInputEnumIndex) {
1897 case IN_TRANSFER_IN:
1898 return 0;
1899 default:
1900 return -1;
1904 void FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex,
1905 const Float* aFloat,
1906 uint32_t aSize) {
1907 std::vector<Float> table(aFloat, aFloat + aSize);
1908 switch (aIndex) {
1909 case ATT_TABLE_TRANSFER_TABLE_R:
1910 mTableR = table;
1911 break;
1912 case ATT_TABLE_TRANSFER_TABLE_G:
1913 mTableG = table;
1914 break;
1915 case ATT_TABLE_TRANSFER_TABLE_B:
1916 mTableB = table;
1917 break;
1918 case ATT_TABLE_TRANSFER_TABLE_A:
1919 mTableA = table;
1920 break;
1921 default:
1922 MOZ_CRASH("GFX: FilterNodeTableTransferSoftware::SetAttribute");
1924 Invalidate();
1927 void FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
1928 uint8_t aTable[256]) {
1929 switch (aComponent) {
1930 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
1931 FillLookupTableImpl(mTableR, aTable);
1932 break;
1933 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
1934 FillLookupTableImpl(mTableG, aTable);
1935 break;
1936 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
1937 FillLookupTableImpl(mTableB, aTable);
1938 break;
1939 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
1940 FillLookupTableImpl(mTableA, aTable);
1941 break;
1942 default:
1943 MOZ_ASSERT(false, "unknown component");
1944 break;
1948 void FilterNodeTableTransferSoftware::FillLookupTableImpl(
1949 std::vector<Float>& aTableValues, uint8_t aTable[256]) {
1950 uint32_t tvLength = aTableValues.size();
1951 if (tvLength < 2) {
1952 return;
1955 for (size_t i = 0; i < 256; i++) {
1956 uint32_t k = (i * (tvLength - 1)) / 255;
1957 Float v1 = aTableValues[k];
1958 Float v2 = aTableValues[std::min(k + 1, tvLength - 1)];
1959 int32_t val = int32_t(255 * (v1 + (i / 255.0f - k / float(tvLength - 1)) *
1960 (tvLength - 1) * (v2 - v1)));
1961 val = std::min(255, val);
1962 val = std::max(0, val);
1963 aTable[i] = val;
1967 void FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex,
1968 const Float* aFloat,
1969 uint32_t aSize) {
1970 std::vector<Float> discrete(aFloat, aFloat + aSize);
1971 switch (aIndex) {
1972 case ATT_DISCRETE_TRANSFER_TABLE_R:
1973 mTableR = discrete;
1974 break;
1975 case ATT_DISCRETE_TRANSFER_TABLE_G:
1976 mTableG = discrete;
1977 break;
1978 case ATT_DISCRETE_TRANSFER_TABLE_B:
1979 mTableB = discrete;
1980 break;
1981 case ATT_DISCRETE_TRANSFER_TABLE_A:
1982 mTableA = discrete;
1983 break;
1984 default:
1985 MOZ_CRASH("GFX: FilterNodeDiscreteTransferSoftware::SetAttribute");
1987 Invalidate();
1990 void FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
1991 uint8_t aTable[256]) {
1992 switch (aComponent) {
1993 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
1994 FillLookupTableImpl(mTableR, aTable);
1995 break;
1996 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
1997 FillLookupTableImpl(mTableG, aTable);
1998 break;
1999 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
2000 FillLookupTableImpl(mTableB, aTable);
2001 break;
2002 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
2003 FillLookupTableImpl(mTableA, aTable);
2004 break;
2005 default:
2006 MOZ_ASSERT(false, "unknown component");
2007 break;
2011 void FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(
2012 std::vector<Float>& aTableValues, uint8_t aTable[256]) {
2013 uint32_t tvLength = aTableValues.size();
2014 if (tvLength < 1) {
2015 return;
2018 for (size_t i = 0; i < 256; i++) {
2019 uint32_t k = (i * tvLength) / 255;
2020 k = std::min(k, tvLength - 1);
2021 Float v = aTableValues[k];
2022 int32_t val = NS_lround(255 * v);
2023 val = std::min(255, val);
2024 val = std::max(0, val);
2025 aTable[i] = val;
2029 FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware()
2030 : mSlopeR(0),
2031 mSlopeG(0),
2032 mSlopeB(0),
2033 mSlopeA(0),
2034 mInterceptR(0),
2035 mInterceptG(0),
2036 mInterceptB(0),
2037 mInterceptA(0) {}
2039 void FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex,
2040 Float aValue) {
2041 switch (aIndex) {
2042 case ATT_LINEAR_TRANSFER_SLOPE_R:
2043 mSlopeR = aValue;
2044 break;
2045 case ATT_LINEAR_TRANSFER_INTERCEPT_R:
2046 mInterceptR = aValue;
2047 break;
2048 case ATT_LINEAR_TRANSFER_SLOPE_G:
2049 mSlopeG = aValue;
2050 break;
2051 case ATT_LINEAR_TRANSFER_INTERCEPT_G:
2052 mInterceptG = aValue;
2053 break;
2054 case ATT_LINEAR_TRANSFER_SLOPE_B:
2055 mSlopeB = aValue;
2056 break;
2057 case ATT_LINEAR_TRANSFER_INTERCEPT_B:
2058 mInterceptB = aValue;
2059 break;
2060 case ATT_LINEAR_TRANSFER_SLOPE_A:
2061 mSlopeA = aValue;
2062 break;
2063 case ATT_LINEAR_TRANSFER_INTERCEPT_A:
2064 mInterceptA = aValue;
2065 break;
2066 default:
2067 MOZ_CRASH("GFX: FilterNodeLinearTransferSoftware::SetAttribute");
2069 Invalidate();
2072 void FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
2073 uint8_t aTable[256]) {
2074 switch (aComponent) {
2075 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
2076 FillLookupTableImpl(mSlopeR, mInterceptR, aTable);
2077 break;
2078 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
2079 FillLookupTableImpl(mSlopeG, mInterceptG, aTable);
2080 break;
2081 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
2082 FillLookupTableImpl(mSlopeB, mInterceptB, aTable);
2083 break;
2084 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
2085 FillLookupTableImpl(mSlopeA, mInterceptA, aTable);
2086 break;
2087 default:
2088 MOZ_ASSERT(false, "unknown component");
2089 break;
2093 void FilterNodeLinearTransferSoftware::FillLookupTableImpl(
2094 Float aSlope, Float aIntercept, uint8_t aTable[256]) {
2095 for (size_t i = 0; i < 256; i++) {
2096 int32_t val = NS_lround(aSlope * i + 255 * aIntercept);
2097 val = std::min(255, val);
2098 val = std::max(0, val);
2099 aTable[i] = val;
2103 FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware()
2104 : mAmplitudeR(0),
2105 mAmplitudeG(0),
2106 mAmplitudeB(0),
2107 mAmplitudeA(0),
2108 mExponentR(0),
2109 mExponentG(0),
2110 mExponentB(0),
2111 mExponentA(0),
2112 mOffsetR(0.0),
2113 mOffsetG(0.0),
2114 mOffsetB(0.0),
2115 mOffsetA(0.0) {}
2117 void FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex,
2118 Float aValue) {
2119 switch (aIndex) {
2120 case ATT_GAMMA_TRANSFER_AMPLITUDE_R:
2121 mAmplitudeR = aValue;
2122 break;
2123 case ATT_GAMMA_TRANSFER_EXPONENT_R:
2124 mExponentR = aValue;
2125 break;
2126 case ATT_GAMMA_TRANSFER_OFFSET_R:
2127 mOffsetR = aValue;
2128 break;
2129 case ATT_GAMMA_TRANSFER_AMPLITUDE_G:
2130 mAmplitudeG = aValue;
2131 break;
2132 case ATT_GAMMA_TRANSFER_EXPONENT_G:
2133 mExponentG = aValue;
2134 break;
2135 case ATT_GAMMA_TRANSFER_OFFSET_G:
2136 mOffsetG = aValue;
2137 break;
2138 case ATT_GAMMA_TRANSFER_AMPLITUDE_B:
2139 mAmplitudeB = aValue;
2140 break;
2141 case ATT_GAMMA_TRANSFER_EXPONENT_B:
2142 mExponentB = aValue;
2143 break;
2144 case ATT_GAMMA_TRANSFER_OFFSET_B:
2145 mOffsetB = aValue;
2146 break;
2147 case ATT_GAMMA_TRANSFER_AMPLITUDE_A:
2148 mAmplitudeA = aValue;
2149 break;
2150 case ATT_GAMMA_TRANSFER_EXPONENT_A:
2151 mExponentA = aValue;
2152 break;
2153 case ATT_GAMMA_TRANSFER_OFFSET_A:
2154 mOffsetA = aValue;
2155 break;
2156 default:
2157 MOZ_CRASH("GFX: FilterNodeGammaTransferSoftware::SetAttribute");
2159 Invalidate();
2162 void FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
2163 uint8_t aTable[256]) {
2164 switch (aComponent) {
2165 case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
2166 FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable);
2167 break;
2168 case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
2169 FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable);
2170 break;
2171 case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
2172 FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable);
2173 break;
2174 case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
2175 FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable);
2176 break;
2177 default:
2178 MOZ_ASSERT(false, "unknown component");
2179 break;
2183 void FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude,
2184 Float aExponent,
2185 Float aOffset,
2186 uint8_t aTable[256]) {
2187 for (size_t i = 0; i < 256; i++) {
2188 int32_t val =
2189 NS_lround(255 * (aAmplitude * pow(i / 255.0f, aExponent) + aOffset));
2190 val = std::min(255, val);
2191 val = std::max(0, val);
2192 aTable[i] = val;
2196 FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware()
2197 : mDivisor(0),
2198 mBias(0),
2199 mEdgeMode(EDGE_MODE_DUPLICATE),
2200 mPreserveAlpha(false) {}
2202 int32_t FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) {
2203 switch (aInputEnumIndex) {
2204 case IN_CONVOLVE_MATRIX_IN:
2205 return 0;
2206 default:
2207 return -1;
2211 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2212 uint32_t aIndex, const IntSize& aKernelSize) {
2213 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_SIZE);
2214 mKernelSize = aKernelSize;
2215 Invalidate();
2218 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2219 const Float* aMatrix,
2220 uint32_t aSize) {
2221 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_MATRIX);
2222 mKernelMatrix = std::vector<Float>(aMatrix, aMatrix + aSize);
2223 Invalidate();
2226 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2227 Float aValue) {
2228 switch (aIndex) {
2229 case ATT_CONVOLVE_MATRIX_DIVISOR:
2230 mDivisor = aValue;
2231 break;
2232 case ATT_CONVOLVE_MATRIX_BIAS:
2233 mBias = aValue;
2234 break;
2235 default:
2236 MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
2238 Invalidate();
2241 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2242 uint32_t aIndex, const Size& aKernelUnitLength) {
2243 switch (aIndex) {
2244 case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH:
2245 mKernelUnitLength = aKernelUnitLength;
2246 break;
2247 default:
2248 MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
2250 Invalidate();
2253 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2254 const IntPoint& aTarget) {
2255 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_TARGET);
2256 mTarget = aTarget;
2257 Invalidate();
2260 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2261 uint32_t aIndex, const IntRect& aSourceRect) {
2262 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_SOURCE_RECT);
2263 mSourceRect = aSourceRect;
2264 Invalidate();
2267 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2268 uint32_t aEdgeMode) {
2269 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_EDGE_MODE);
2270 mEdgeMode = static_cast<ConvolveMatrixEdgeMode>(aEdgeMode);
2271 Invalidate();
2274 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2275 bool aPreserveAlpha) {
2276 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA);
2277 mPreserveAlpha = aPreserveAlpha;
2278 Invalidate();
2281 #ifdef DEBUG
2282 static inline void DebugOnlyCheckColorSamplingAccess(
2283 const uint8_t* aSampleAddress, const uint8_t* aBoundsBegin,
2284 const uint8_t* aBoundsEnd) {
2285 MOZ_ASSERT(aSampleAddress >= aBoundsBegin, "accessing before start");
2286 MOZ_ASSERT(aSampleAddress < aBoundsEnd, "accessing after end");
2288 #else
2289 # define DebugOnlyCheckColorSamplingAccess(address, boundsBegin, boundsEnd)
2290 #endif
2292 static inline uint8_t ColorComponentAtPoint(const uint8_t* aData,
2293 ptrdiff_t aStride,
2294 const uint8_t* aBoundsBegin,
2295 const uint8_t* aBoundsEnd,
2296 int32_t x, int32_t y, ptrdiff_t bpp,
2297 ptrdiff_t c) {
2298 DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c],
2299 aBoundsBegin, aBoundsEnd);
2300 return aData[y * aStride + bpp * x + c];
2303 static inline int32_t ColorAtPoint(const uint8_t* aData, ptrdiff_t aStride,
2304 const uint8_t* aBoundsBegin,
2305 const uint8_t* aBoundsEnd, int32_t x,
2306 int32_t y) {
2307 DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x, aBoundsBegin,
2308 aBoundsEnd);
2309 return *(uint32_t*)(aData + y * aStride + 4 * x);
2312 // Accepts fractional x & y and does bilinear interpolation.
2313 // Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible.
2314 static inline uint8_t ColorComponentAtPoint(
2315 const uint8_t* aData, ptrdiff_t aStride, const uint8_t* aBoundsBegin,
2316 const uint8_t* aBoundsEnd, Float x, Float y, ptrdiff_t bpp, ptrdiff_t c) {
2317 const uint32_t f = 256;
2318 const int32_t lx = floor(x);
2319 const int32_t ly = floor(y);
2320 const int32_t tux = uint32_t((x - lx) * f);
2321 const int32_t tlx = f - tux;
2322 const int32_t tuy = uint32_t((y - ly) * f);
2323 const int32_t tly = f - tuy;
2324 const uint8_t& cll = ColorComponentAtPoint(aData, aStride, aBoundsBegin,
2325 aBoundsEnd, lx, ly, bpp, c);
2326 const uint8_t& cul = ColorComponentAtPoint(aData, aStride, aBoundsBegin,
2327 aBoundsEnd, lx + 1, ly, bpp, c);
2328 const uint8_t& clu = ColorComponentAtPoint(aData, aStride, aBoundsBegin,
2329 aBoundsEnd, lx, ly + 1, bpp, c);
2330 const uint8_t& cuu = ColorComponentAtPoint(
2331 aData, aStride, aBoundsBegin, aBoundsEnd, lx + 1, ly + 1, bpp, c);
2332 return ((cll * tlx + cul * tux) * tly + (clu * tlx + cuu * tux) * tuy +
2333 f * f / 2) /
2334 (f * f);
2337 static int32_t ClampToNonZero(int32_t a) { return a * (a >= 0); }
2339 template <typename CoordType>
2340 static void ConvolvePixel(const uint8_t* aSourceData, uint8_t* aTargetData,
2341 int32_t aWidth, int32_t aHeight,
2342 int32_t aSourceStride, int32_t aTargetStride,
2343 const uint8_t* aSourceBegin,
2344 const uint8_t* aSourceEnd, int32_t aX, int32_t aY,
2345 const int32_t* aKernel, int32_t aBias, int32_t shiftL,
2346 int32_t shiftR, bool aPreserveAlpha, int32_t aOrderX,
2347 int32_t aOrderY, int32_t aTargetX, int32_t aTargetY,
2348 CoordType aKernelUnitLengthX,
2349 CoordType aKernelUnitLengthY) {
2350 int32_t sum[4] = {0, 0, 0, 0};
2351 int32_t offsets[4] = {
2352 B8G8R8A8_COMPONENT_BYTEOFFSET_R, B8G8R8A8_COMPONENT_BYTEOFFSET_G,
2353 B8G8R8A8_COMPONENT_BYTEOFFSET_B, B8G8R8A8_COMPONENT_BYTEOFFSET_A};
2354 int32_t channels = aPreserveAlpha ? 3 : 4;
2355 int32_t roundingAddition = shiftL == 0 ? 0 : 1 << (shiftL - 1);
2357 for (int32_t y = 0; y < aOrderY; y++) {
2358 CoordType sampleY = aY + (y - aTargetY) * aKernelUnitLengthY;
2359 for (int32_t x = 0; x < aOrderX; x++) {
2360 CoordType sampleX = aX + (x - aTargetX) * aKernelUnitLengthX;
2361 for (int32_t i = 0; i < channels; i++) {
2362 sum[i] +=
2363 aKernel[aOrderX * y + x] *
2364 ColorComponentAtPoint(aSourceData, aSourceStride, aSourceBegin,
2365 aSourceEnd, sampleX, sampleY, 4, offsets[i]);
2369 for (int32_t i = 0; i < channels; i++) {
2370 int32_t clamped =
2371 umin(ClampToNonZero(sum[i] + aBias), 255 << shiftL >> shiftR);
2372 aTargetData[aY * aTargetStride + 4 * aX + offsets[i]] =
2373 (clamped + roundingAddition) << shiftR >> shiftL;
2375 if (aPreserveAlpha) {
2376 aTargetData[aY * aTargetStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
2377 aSourceData[aY * aSourceStride + 4 * aX +
2378 B8G8R8A8_COMPONENT_BYTEOFFSET_A];
2382 already_AddRefed<DataSourceSurface> FilterNodeConvolveMatrixSoftware::Render(
2383 const IntRect& aRect) {
2384 if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
2385 mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
2386 return DoRender(aRect, (int32_t)mKernelUnitLength.width,
2387 (int32_t)mKernelUnitLength.height);
2389 return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
2392 static std::vector<Float> ReversedVector(const std::vector<Float>& aVector) {
2393 size_t length = aVector.size();
2394 std::vector<Float> result(length, 0);
2395 for (size_t i = 0; i < length; i++) {
2396 result[length - 1 - i] = aVector[i];
2398 return result;
2401 static std::vector<Float> ScaledVector(const std::vector<Float>& aVector,
2402 Float aDivisor) {
2403 size_t length = aVector.size();
2404 std::vector<Float> result(length, 0);
2405 for (size_t i = 0; i < length; i++) {
2406 result[i] = aVector[i] / aDivisor;
2408 return result;
2411 static Float MaxVectorSum(const std::vector<Float>& aVector) {
2412 Float sum = 0;
2413 size_t length = aVector.size();
2414 for (size_t i = 0; i < length; i++) {
2415 if (aVector[i] > 0) {
2416 sum += aVector[i];
2419 return sum;
2422 // Returns shiftL and shiftR in such a way that
2423 // a << shiftL >> shiftR is roughly a * aFloat.
2424 static void TranslateDoubleToShifts(double aDouble, int32_t& aShiftL,
2425 int32_t& aShiftR) {
2426 aShiftL = 0;
2427 aShiftR = 0;
2428 if (aDouble <= 0) {
2429 MOZ_CRASH("GFX: TranslateDoubleToShifts");
2431 if (aDouble < 1) {
2432 while (1 << (aShiftR + 1) < 1 / aDouble) {
2433 aShiftR++;
2435 } else {
2436 while (1 << (aShiftL + 1) < aDouble) {
2437 aShiftL++;
2442 template <typename CoordType>
2443 already_AddRefed<DataSourceSurface> FilterNodeConvolveMatrixSoftware::DoRender(
2444 const IntRect& aRect, CoordType aKernelUnitLengthX,
2445 CoordType aKernelUnitLengthY) {
2446 if (mKernelSize.width <= 0 || mKernelSize.height <= 0 ||
2447 mKernelMatrix.size() !=
2448 uint32_t(mKernelSize.width * mKernelSize.height) ||
2449 !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) ||
2450 mDivisor == 0) {
2451 return Factory::CreateDataSourceSurface(aRect.Size(),
2452 SurfaceFormat::B8G8R8A8, true);
2455 IntRect srcRect = InflatedSourceRect(aRect);
2457 // Inflate the source rect by another pixel because the bilinear filtering in
2458 // ColorComponentAtPoint may want to access the margins.
2459 srcRect.Inflate(1);
2461 RefPtr<DataSourceSurface> input =
2462 GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect,
2463 NEED_COLOR_CHANNELS, mEdgeMode, &mSourceRect);
2465 if (!input) {
2466 return nullptr;
2469 RefPtr<DataSourceSurface> target = Factory::CreateDataSourceSurface(
2470 aRect.Size(), SurfaceFormat::B8G8R8A8, true);
2471 if (MOZ2D_WARN_IF(!target)) {
2472 return nullptr;
2475 IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
2477 DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
2478 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
2479 if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
2480 return nullptr;
2483 uint8_t* sourceData =
2484 DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
2485 int32_t sourceStride = sourceMap.GetStride();
2486 uint8_t* sourceBegin = sourceMap.GetData();
2487 uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
2488 uint8_t* targetData = targetMap.GetData();
2489 int32_t targetStride = targetMap.GetStride();
2491 // Why exactly are we reversing the kernel?
2492 std::vector<Float> kernel = ReversedVector(mKernelMatrix);
2493 kernel = ScaledVector(kernel, mDivisor);
2494 Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias,
2495 MaxVectorSum(ScaledVector(kernel, -1)) - mBias);
2496 maxResultAbs = std::max(maxResultAbs, 1.0f);
2498 double idealFactor = INT32_MAX / 2.0 / maxResultAbs / 255.0 * 0.999;
2499 MOZ_ASSERT(255.0 * maxResultAbs * idealFactor <= INT32_MAX / 2.0,
2500 "badly chosen float-to-int scale");
2501 int32_t shiftL, shiftR;
2502 TranslateDoubleToShifts(idealFactor, shiftL, shiftR);
2503 double factorFromShifts = Float(1 << shiftL) / Float(1 << shiftR);
2504 MOZ_ASSERT(255.0 * maxResultAbs * factorFromShifts <= INT32_MAX / 2.0,
2505 "badly chosen float-to-int scale");
2507 int32_t* intKernel = new int32_t[kernel.size()];
2508 for (size_t i = 0; i < kernel.size(); i++) {
2509 intKernel[i] = NS_lround(kernel[i] * factorFromShifts);
2511 int32_t bias = NS_lround(mBias * 255 * factorFromShifts);
2513 for (int32_t y = 0; y < aRect.Height(); y++) {
2514 for (int32_t x = 0; x < aRect.Width(); x++) {
2515 ConvolvePixel(sourceData, targetData, aRect.Width(), aRect.Height(),
2516 sourceStride, targetStride, sourceBegin, sourceEnd, x, y,
2517 intKernel, bias, shiftL, shiftR, mPreserveAlpha,
2518 mKernelSize.width, mKernelSize.height, mTarget.x, mTarget.y,
2519 aKernelUnitLengthX, aKernelUnitLengthY);
2522 delete[] intKernel;
2524 return target.forget();
2527 void FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(
2528 const IntRect& aRect) {
2529 RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect));
2532 IntRect FilterNodeConvolveMatrixSoftware::MapRectToSource(
2533 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
2534 return MapInputRectToSource(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect),
2535 aMax, aSourceNode);
2538 IntRect FilterNodeConvolveMatrixSoftware::InflatedSourceRect(
2539 const IntRect& aDestRect) {
2540 if (aDestRect.IsEmpty()) {
2541 return IntRect();
2544 IntMargin margin;
2545 margin.left = static_cast<int32_t>(ceil(mTarget.x * mKernelUnitLength.width));
2546 margin.top = static_cast<int32_t>(ceil(mTarget.y * mKernelUnitLength.height));
2547 margin.right = static_cast<int32_t>(
2548 ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width));
2549 margin.bottom = static_cast<int32_t>(
2550 ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height));
2552 IntRect srcRect = aDestRect;
2553 srcRect.Inflate(margin);
2554 return srcRect;
2557 IntRect FilterNodeConvolveMatrixSoftware::InflatedDestRect(
2558 const IntRect& aSourceRect) {
2559 if (aSourceRect.IsEmpty()) {
2560 return IntRect();
2563 IntMargin margin;
2564 margin.left = static_cast<int32_t>(
2565 ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width));
2566 margin.top = static_cast<int32_t>(
2567 ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height));
2568 margin.right =
2569 static_cast<int32_t>(ceil(mTarget.x * mKernelUnitLength.width));
2570 margin.bottom =
2571 static_cast<int32_t>(ceil(mTarget.y * mKernelUnitLength.height));
2573 IntRect destRect = aSourceRect;
2574 destRect.Inflate(margin);
2575 return destRect;
2578 IntRect FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(
2579 const IntRect& aRect) {
2580 IntRect srcRequest = InflatedSourceRect(aRect);
2581 IntRect srcOutput = GetInputRectInRect(IN_CONVOLVE_MATRIX_IN, srcRequest);
2582 return InflatedDestRect(srcOutput).Intersect(aRect);
2585 FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware()
2586 : mScale(0.0f), mChannelX(COLOR_CHANNEL_R), mChannelY(COLOR_CHANNEL_G) {}
2588 int32_t FilterNodeDisplacementMapSoftware::InputIndex(
2589 uint32_t aInputEnumIndex) {
2590 switch (aInputEnumIndex) {
2591 case IN_DISPLACEMENT_MAP_IN:
2592 return 0;
2593 case IN_DISPLACEMENT_MAP_IN2:
2594 return 1;
2595 default:
2596 return -1;
2600 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
2601 Float aScale) {
2602 MOZ_ASSERT(aIndex == ATT_DISPLACEMENT_MAP_SCALE);
2603 mScale = aScale;
2604 Invalidate();
2607 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
2608 uint32_t aValue) {
2609 switch (aIndex) {
2610 case ATT_DISPLACEMENT_MAP_X_CHANNEL:
2611 mChannelX = static_cast<ColorChannel>(aValue);
2612 break;
2613 case ATT_DISPLACEMENT_MAP_Y_CHANNEL:
2614 mChannelY = static_cast<ColorChannel>(aValue);
2615 break;
2616 default:
2617 MOZ_CRASH("GFX: FilterNodeDisplacementMapSoftware::SetAttribute");
2619 Invalidate();
2622 already_AddRefed<DataSourceSurface> FilterNodeDisplacementMapSoftware::Render(
2623 const IntRect& aRect) {
2624 IntRect srcRect = InflatedSourceOrDestRect(aRect);
2625 RefPtr<DataSourceSurface> input = GetInputDataSourceSurface(
2626 IN_DISPLACEMENT_MAP_IN, srcRect, NEED_COLOR_CHANNELS);
2627 RefPtr<DataSourceSurface> map = GetInputDataSourceSurface(
2628 IN_DISPLACEMENT_MAP_IN2, aRect, NEED_COLOR_CHANNELS);
2629 RefPtr<DataSourceSurface> target =
2630 Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
2631 if (MOZ2D_WARN_IF(!(input && map && target))) {
2632 return nullptr;
2635 IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
2637 DataSourceSurface::ScopedMap inputMap(input, DataSourceSurface::READ);
2638 DataSourceSurface::ScopedMap mapMap(map, DataSourceSurface::READ);
2639 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
2640 if (MOZ2D_WARN_IF(!(inputMap.IsMapped() && mapMap.IsMapped() &&
2641 targetMap.IsMapped()))) {
2642 return nullptr;
2645 uint8_t* sourceData =
2646 DataAtOffset(input, inputMap.GetMappedSurface(), offset);
2647 int32_t sourceStride = inputMap.GetStride();
2648 uint8_t* sourceBegin = inputMap.GetData();
2649 uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
2650 uint8_t* mapData = mapMap.GetData();
2651 int32_t mapStride = mapMap.GetStride();
2652 uint8_t* targetData = targetMap.GetData();
2653 int32_t targetStride = targetMap.GetStride();
2655 static const ptrdiff_t channelMap[4] = {
2656 B8G8R8A8_COMPONENT_BYTEOFFSET_R, B8G8R8A8_COMPONENT_BYTEOFFSET_G,
2657 B8G8R8A8_COMPONENT_BYTEOFFSET_B, B8G8R8A8_COMPONENT_BYTEOFFSET_A};
2658 uint16_t xChannel = channelMap[mChannelX];
2659 uint16_t yChannel = channelMap[mChannelY];
2661 float scaleOver255 = mScale / 255.0f;
2662 float scaleAdjustment = -0.5f * mScale;
2664 for (int32_t y = 0; y < aRect.Height(); y++) {
2665 for (int32_t x = 0; x < aRect.Width(); x++) {
2666 uint32_t mapIndex = y * mapStride + 4 * x;
2667 uint32_t targIndex = y * targetStride + 4 * x;
2668 int32_t sourceX =
2669 x + scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment;
2670 int32_t sourceY =
2671 y + scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment;
2672 *(uint32_t*)(targetData + targIndex) = ColorAtPoint(
2673 sourceData, sourceStride, sourceBegin, sourceEnd, sourceX, sourceY);
2676 // Keep valgrind happy.
2677 PodZero(&targetData[y * targetStride + 4 * aRect.Width()],
2678 targetStride - 4 * aRect.Width());
2681 return target.forget();
2684 void FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(
2685 const IntRect& aRect) {
2686 RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect));
2687 RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect);
2690 IntRect FilterNodeDisplacementMapSoftware::MapRectToSource(
2691 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
2692 IntRect result =
2693 MapInputRectToSource(IN_DISPLACEMENT_MAP_IN,
2694 InflatedSourceOrDestRect(aRect), aMax, aSourceNode);
2695 result.OrWith(
2696 MapInputRectToSource(IN_DISPLACEMENT_MAP_IN2, aRect, aMax, aSourceNode));
2697 return result;
2700 IntRect FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(
2701 const IntRect& aDestOrSourceRect) {
2702 IntRect sourceOrDestRect = aDestOrSourceRect;
2703 sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2));
2704 return sourceOrDestRect;
2707 IntRect FilterNodeDisplacementMapSoftware::GetOutputRectInRect(
2708 const IntRect& aRect) {
2709 IntRect srcRequest = InflatedSourceOrDestRect(aRect);
2710 IntRect srcOutput = GetInputRectInRect(IN_DISPLACEMENT_MAP_IN, srcRequest);
2711 return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
2714 FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware()
2715 : mNumOctaves(0),
2716 mSeed(0),
2717 mStitchable(false),
2718 mType(TURBULENCE_TYPE_TURBULENCE) {}
2720 int32_t FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex) {
2721 return -1;
2724 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
2725 const Size& aBaseFrequency) {
2726 switch (aIndex) {
2727 case ATT_TURBULENCE_BASE_FREQUENCY:
2728 mBaseFrequency = aBaseFrequency;
2729 break;
2730 default:
2731 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2732 break;
2734 Invalidate();
2737 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
2738 const IntRect& aRect) {
2739 switch (aIndex) {
2740 case ATT_TURBULENCE_RECT:
2741 mRenderRect = aRect;
2742 break;
2743 default:
2744 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2745 break;
2747 Invalidate();
2750 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
2751 bool aStitchable) {
2752 MOZ_ASSERT(aIndex == ATT_TURBULENCE_STITCHABLE);
2753 mStitchable = aStitchable;
2754 Invalidate();
2757 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex,
2758 uint32_t aValue) {
2759 switch (aIndex) {
2760 case ATT_TURBULENCE_NUM_OCTAVES:
2761 mNumOctaves = aValue;
2762 break;
2763 case ATT_TURBULENCE_SEED:
2764 mSeed = aValue;
2765 break;
2766 case ATT_TURBULENCE_TYPE:
2767 mType = static_cast<TurbulenceType>(aValue);
2768 break;
2769 default:
2770 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2771 break;
2773 Invalidate();
2776 already_AddRefed<DataSourceSurface> FilterNodeTurbulenceSoftware::Render(
2777 const IntRect& aRect) {
2778 return FilterProcessing::RenderTurbulence(
2779 aRect.Size(), aRect.TopLeft(), mBaseFrequency, mSeed, mNumOctaves, mType,
2780 mStitchable, Rect(mRenderRect));
2783 IntRect FilterNodeTurbulenceSoftware::GetOutputRectInRect(
2784 const IntRect& aRect) {
2785 return aRect.Intersect(mRenderRect);
2788 IntRect FilterNodeTurbulenceSoftware::MapRectToSource(const IntRect& aRect,
2789 const IntRect& aMax,
2790 FilterNode* aSourceNode) {
2791 return IntRect();
2794 FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware()
2795 : mK1(0), mK2(0), mK3(0), mK4(0) {}
2797 int32_t FilterNodeArithmeticCombineSoftware::InputIndex(
2798 uint32_t aInputEnumIndex) {
2799 switch (aInputEnumIndex) {
2800 case IN_ARITHMETIC_COMBINE_IN:
2801 return 0;
2802 case IN_ARITHMETIC_COMBINE_IN2:
2803 return 1;
2804 default:
2805 return -1;
2809 void FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex,
2810 const Float* aFloat,
2811 uint32_t aSize) {
2812 MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS);
2813 MOZ_RELEASE_ASSERT(aSize == 4);
2815 mK1 = aFloat[0];
2816 mK2 = aFloat[1];
2817 mK3 = aFloat[2];
2818 mK4 = aFloat[3];
2820 Invalidate();
2823 already_AddRefed<DataSourceSurface> FilterNodeArithmeticCombineSoftware::Render(
2824 const IntRect& aRect) {
2825 RefPtr<DataSourceSurface> input1 = GetInputDataSourceSurface(
2826 IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS);
2827 RefPtr<DataSourceSurface> input2 = GetInputDataSourceSurface(
2828 IN_ARITHMETIC_COMBINE_IN2, aRect, NEED_COLOR_CHANNELS);
2829 if (!input1 && !input2) {
2830 return nullptr;
2833 // If one input is null, treat it as transparent by adjusting the factors.
2834 Float k1 = mK1, k2 = mK2, k3 = mK3, k4 = mK4;
2835 if (!input1) {
2836 k1 = 0.0f;
2837 k2 = 0.0f;
2838 input1 = input2;
2841 if (!input2) {
2842 k1 = 0.0f;
2843 k3 = 0.0f;
2844 input2 = input1;
2847 return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3,
2848 k4);
2851 void FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(
2852 const IntRect& aRect) {
2853 RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect);
2854 RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect);
2857 IntRect FilterNodeArithmeticCombineSoftware::MapRectToSource(
2858 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
2859 IntRect result =
2860 MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN, aRect, aMax, aSourceNode);
2861 result.OrWith(MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN2, aRect, aMax,
2862 aSourceNode));
2863 return result;
2866 IntRect FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(
2867 const IntRect& aRect) {
2868 if (mK4 > 0.0f) {
2869 return aRect;
2871 IntRect rectFrom1 =
2872 GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect);
2873 IntRect rectFrom2 =
2874 GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2, aRect).Intersect(aRect);
2875 IntRect result;
2876 if (mK1 > 0.0f) {
2877 result = rectFrom1.Intersect(rectFrom2);
2879 if (mK2 > 0.0f) {
2880 result = result.Union(rectFrom1);
2882 if (mK3 > 0.0f) {
2883 result = result.Union(rectFrom2);
2885 return result;
2888 FilterNodeCompositeSoftware::FilterNodeCompositeSoftware()
2889 : mOperator(COMPOSITE_OPERATOR_OVER) {}
2891 int32_t FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex) {
2892 return aInputEnumIndex - IN_COMPOSITE_IN_START;
2895 void FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex,
2896 uint32_t aCompositeOperator) {
2897 MOZ_ASSERT(aIndex == ATT_COMPOSITE_OPERATOR);
2898 mOperator = static_cast<CompositeOperator>(aCompositeOperator);
2899 Invalidate();
2902 already_AddRefed<DataSourceSurface> FilterNodeCompositeSoftware::Render(
2903 const IntRect& aRect) {
2904 RefPtr<DataSourceSurface> start = GetInputDataSourceSurface(
2905 IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS);
2906 RefPtr<DataSourceSurface> dest = Factory::CreateDataSourceSurface(
2907 aRect.Size(), SurfaceFormat::B8G8R8A8, true);
2908 if (MOZ2D_WARN_IF(!dest)) {
2909 return nullptr;
2912 if (start) {
2913 CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint());
2916 for (size_t inputIndex = 1; inputIndex < NumberOfSetInputs(); inputIndex++) {
2917 RefPtr<DataSourceSurface> input = GetInputDataSourceSurface(
2918 IN_COMPOSITE_IN_START + inputIndex, aRect, NEED_COLOR_CHANNELS);
2919 if (input) {
2920 FilterProcessing::ApplyComposition(input, dest, mOperator);
2921 } else {
2922 // We need to treat input as transparent. Depending on the composite
2923 // operator, different things happen to dest.
2924 switch (mOperator) {
2925 case COMPOSITE_OPERATOR_OVER:
2926 case COMPOSITE_OPERATOR_ATOP:
2927 case COMPOSITE_OPERATOR_XOR:
2928 case COMPOSITE_OPERATOR_LIGHTER:
2929 // dest is unchanged.
2930 break;
2931 case COMPOSITE_OPERATOR_OUT:
2932 // dest is now transparent, but it can become non-transparent again
2933 // when compositing additional inputs.
2934 ClearDataSourceSurface(dest);
2935 break;
2936 case COMPOSITE_OPERATOR_IN:
2937 // Transparency always wins. We're completely transparent now and
2938 // no additional input can get rid of that transparency.
2939 return nullptr;
2943 return dest.forget();
2946 void FilterNodeCompositeSoftware::RequestFromInputsForRect(
2947 const IntRect& aRect) {
2948 for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
2949 RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
2953 IntRect FilterNodeCompositeSoftware::MapRectToSource(const IntRect& aRect,
2954 const IntRect& aMax,
2955 FilterNode* aSourceNode) {
2956 IntRect result;
2957 for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
2958 result.OrWith(MapInputRectToSource(IN_COMPOSITE_IN_START + inputIndex,
2959 aRect, aMax, aSourceNode));
2961 return result;
2964 IntRect FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect& aRect) {
2965 IntRect rect;
2966 for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
2967 IntRect inputRect =
2968 GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
2969 if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) {
2970 rect = rect.Intersect(inputRect);
2971 } else {
2972 rect = rect.Union(inputRect);
2975 return rect;
2978 int32_t FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex) {
2979 switch (aInputEnumIndex) {
2980 case IN_GAUSSIAN_BLUR_IN:
2981 return 0;
2982 default:
2983 return -1;
2987 already_AddRefed<DataSourceSurface> FilterNodeBlurXYSoftware::Render(
2988 const IntRect& aRect) {
2989 Size sigmaXY = StdDeviationXY();
2990 IntSize d =
2991 AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
2993 if (d.width == 0 && d.height == 0) {
2994 return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, aRect);
2997 IntRect srcRect = InflatedSourceOrDestRect(aRect);
2998 RefPtr<DataSourceSurface> input =
2999 GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, srcRect);
3000 if (!input) {
3001 return nullptr;
3004 RefPtr<DataSourceSurface> target;
3005 Rect r(0, 0, srcRect.Width(), srcRect.Height());
3007 if (input->GetFormat() == SurfaceFormat::A8) {
3008 target =
3009 Factory::CreateDataSourceSurface(srcRect.Size(), SurfaceFormat::A8);
3010 if (MOZ2D_WARN_IF(!target)) {
3011 return nullptr;
3013 CopyRect(input, target, IntRect(IntPoint(), input->GetSize()), IntPoint());
3015 DataSourceSurface::ScopedMap targetMap(target,
3016 DataSourceSurface::READ_WRITE);
3017 if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
3018 return nullptr;
3020 AlphaBoxBlur blur(r, targetMap.GetStride(), sigmaXY.width, sigmaXY.height);
3021 blur.Blur(targetMap.GetData());
3022 } else {
3023 RefPtr<DataSourceSurface> channel0, channel1, channel2, channel3;
3024 FilterProcessing::SeparateColorChannels(input, channel0, channel1, channel2,
3025 channel3);
3026 if (MOZ2D_WARN_IF(!(channel0 && channel1 && channel2 && channel3))) {
3027 return nullptr;
3030 DataSourceSurface::ScopedMap channel0Map(channel0,
3031 DataSourceSurface::READ_WRITE);
3032 DataSourceSurface::ScopedMap channel1Map(channel1,
3033 DataSourceSurface::READ_WRITE);
3034 DataSourceSurface::ScopedMap channel2Map(channel2,
3035 DataSourceSurface::READ_WRITE);
3036 DataSourceSurface::ScopedMap channel3Map(channel3,
3037 DataSourceSurface::READ_WRITE);
3038 if (MOZ2D_WARN_IF(!(channel0Map.IsMapped() && channel1Map.IsMapped() &&
3039 channel2Map.IsMapped() && channel3Map.IsMapped()))) {
3040 return nullptr;
3043 AlphaBoxBlur blur(r, channel0Map.GetStride(), sigmaXY.width,
3044 sigmaXY.height);
3045 blur.Blur(channel0Map.GetData());
3046 blur.Blur(channel1Map.GetData());
3047 blur.Blur(channel2Map.GetData());
3048 blur.Blur(channel3Map.GetData());
3050 target = FilterProcessing::CombineColorChannels(channel0, channel1,
3051 channel2, channel3);
3054 return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE);
3057 void FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect& aRect) {
3058 RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect));
3061 IntRect FilterNodeBlurXYSoftware::MapRectToSource(const IntRect& aRect,
3062 const IntRect& aMax,
3063 FilterNode* aSourceNode) {
3064 return MapInputRectToSource(
3065 IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect), aMax, aSourceNode);
3068 IntRect FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(
3069 const IntRect& aDestRect) {
3070 Size sigmaXY = StdDeviationXY();
3071 IntSize d =
3072 AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
3073 IntRect srcRect = aDestRect;
3074 srcRect.Inflate(d);
3075 return srcRect;
3078 IntRect FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect& aRect) {
3079 IntRect srcRequest = InflatedSourceOrDestRect(aRect);
3080 IntRect srcOutput = GetInputRectInRect(IN_GAUSSIAN_BLUR_IN, srcRequest);
3081 return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
3084 FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware()
3085 : mStdDeviation(0) {}
3087 static float ClampStdDeviation(float aStdDeviation) {
3088 // Cap software blur radius for performance reasons.
3089 return std::min(std::max(0.0f, aStdDeviation), 100.0f);
3092 void FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex,
3093 float aStdDeviation) {
3094 switch (aIndex) {
3095 case ATT_GAUSSIAN_BLUR_STD_DEVIATION:
3096 mStdDeviation = ClampStdDeviation(aStdDeviation);
3097 break;
3098 default:
3099 MOZ_CRASH("GFX: FilterNodeGaussianBlurSoftware::SetAttribute");
3101 Invalidate();
3104 Size FilterNodeGaussianBlurSoftware::StdDeviationXY() {
3105 return Size(mStdDeviation, mStdDeviation);
3108 FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware()
3109 : mStdDeviation(0.0), mBlurDirection(BLUR_DIRECTION_X) {}
3111 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
3112 Float aStdDeviation) {
3113 switch (aIndex) {
3114 case ATT_DIRECTIONAL_BLUR_STD_DEVIATION:
3115 mStdDeviation = ClampStdDeviation(aStdDeviation);
3116 break;
3117 default:
3118 MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
3120 Invalidate();
3123 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
3124 uint32_t aBlurDirection) {
3125 switch (aIndex) {
3126 case ATT_DIRECTIONAL_BLUR_DIRECTION:
3127 mBlurDirection = (BlurDirection)aBlurDirection;
3128 break;
3129 default:
3130 MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
3132 Invalidate();
3135 Size FilterNodeDirectionalBlurSoftware::StdDeviationXY() {
3136 float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0;
3137 float sigmaY = mBlurDirection == BLUR_DIRECTION_Y ? mStdDeviation : 0;
3138 return Size(sigmaX, sigmaY);
3141 int32_t FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex) {
3142 switch (aInputEnumIndex) {
3143 case IN_CROP_IN:
3144 return 0;
3145 default:
3146 return -1;
3150 void FilterNodeCropSoftware::SetAttribute(uint32_t aIndex,
3151 const Rect& aSourceRect) {
3152 MOZ_ASSERT(aIndex == ATT_CROP_RECT);
3153 Rect srcRect = aSourceRect;
3154 srcRect.Round();
3155 if (!srcRect.ToIntRect(&mCropRect)) {
3156 mCropRect = IntRect();
3158 Invalidate();
3161 already_AddRefed<DataSourceSurface> FilterNodeCropSoftware::Render(
3162 const IntRect& aRect) {
3163 return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect));
3166 void FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect& aRect) {
3167 RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect));
3170 IntRect FilterNodeCropSoftware::MapRectToSource(const IntRect& aRect,
3171 const IntRect& aMax,
3172 FilterNode* aSourceNode) {
3173 return MapInputRectToSource(IN_CROP_IN, aRect.Intersect(mCropRect), aMax,
3174 aSourceNode);
3177 IntRect FilterNodeCropSoftware::GetOutputRectInRect(const IntRect& aRect) {
3178 return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect);
3181 int32_t FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) {
3182 switch (aInputEnumIndex) {
3183 case IN_PREMULTIPLY_IN:
3184 return 0;
3185 default:
3186 return -1;
3190 already_AddRefed<DataSourceSurface> FilterNodePremultiplySoftware::Render(
3191 const IntRect& aRect) {
3192 RefPtr<DataSourceSurface> input =
3193 GetInputDataSourceSurface(IN_PREMULTIPLY_IN, aRect);
3194 return input ? Premultiply(input) : nullptr;
3197 void FilterNodePremultiplySoftware::RequestFromInputsForRect(
3198 const IntRect& aRect) {
3199 RequestInputRect(IN_PREMULTIPLY_IN, aRect);
3202 IntRect FilterNodePremultiplySoftware::MapRectToSource(
3203 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
3204 return MapInputRectToSource(IN_PREMULTIPLY_IN, aRect, aMax, aSourceNode);
3207 IntRect FilterNodePremultiplySoftware::GetOutputRectInRect(
3208 const IntRect& aRect) {
3209 return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect);
3212 int32_t FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) {
3213 switch (aInputEnumIndex) {
3214 case IN_UNPREMULTIPLY_IN:
3215 return 0;
3216 default:
3217 return -1;
3221 already_AddRefed<DataSourceSurface> FilterNodeUnpremultiplySoftware::Render(
3222 const IntRect& aRect) {
3223 RefPtr<DataSourceSurface> input =
3224 GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN, aRect);
3225 return input ? Unpremultiply(input) : nullptr;
3228 void FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(
3229 const IntRect& aRect) {
3230 RequestInputRect(IN_UNPREMULTIPLY_IN, aRect);
3233 IntRect FilterNodeUnpremultiplySoftware::MapRectToSource(
3234 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
3235 return MapInputRectToSource(IN_UNPREMULTIPLY_IN, aRect, aMax, aSourceNode);
3238 IntRect FilterNodeUnpremultiplySoftware::GetOutputRectInRect(
3239 const IntRect& aRect) {
3240 return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect);
3243 void FilterNodeOpacitySoftware::SetAttribute(uint32_t aIndex, Float aValue) {
3244 MOZ_ASSERT(aIndex == ATT_OPACITY_VALUE);
3245 mValue = aValue;
3246 Invalidate();
3249 int32_t FilterNodeOpacitySoftware::InputIndex(uint32_t aInputEnumIndex) {
3250 switch (aInputEnumIndex) {
3251 case IN_OPACITY_IN:
3252 return 0;
3253 default:
3254 return -1;
3258 already_AddRefed<DataSourceSurface> FilterNodeOpacitySoftware::Render(
3259 const IntRect& aRect) {
3260 RefPtr<DataSourceSurface> input =
3261 GetInputDataSourceSurface(IN_OPACITY_IN, aRect);
3262 return input ? Opacity(input, mValue) : nullptr;
3265 void FilterNodeOpacitySoftware::RequestFromInputsForRect(const IntRect& aRect) {
3266 RequestInputRect(IN_OPACITY_IN, aRect);
3269 IntRect FilterNodeOpacitySoftware::MapRectToSource(const IntRect& aRect,
3270 const IntRect& aMax,
3271 FilterNode* aSourceNode) {
3272 return MapInputRectToSource(IN_OPACITY_IN, aRect, aMax, aSourceNode);
3275 IntRect FilterNodeOpacitySoftware::GetOutputRectInRect(const IntRect& aRect) {
3276 return GetInputRectInRect(IN_OPACITY_IN, aRect);
3279 bool PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D& aPoint) {
3280 switch (aIndex) {
3281 case ATT_POINT_LIGHT_POSITION:
3282 mPosition = aPoint;
3283 break;
3284 default:
3285 return false;
3287 return true;
3290 SpotLightSoftware::SpotLightSoftware()
3291 : mSpecularFocus(0), mLimitingConeAngle(0), mLimitingConeCos(1) {}
3293 bool SpotLightSoftware::SetAttribute(uint32_t aIndex, const Point3D& aPoint) {
3294 switch (aIndex) {
3295 case ATT_SPOT_LIGHT_POSITION:
3296 mPosition = aPoint;
3297 break;
3298 case ATT_SPOT_LIGHT_POINTS_AT:
3299 mPointsAt = aPoint;
3300 break;
3301 default:
3302 return false;
3304 return true;
3307 bool SpotLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
3308 switch (aIndex) {
3309 case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE:
3310 mLimitingConeAngle = aValue;
3311 break;
3312 case ATT_SPOT_LIGHT_FOCUS:
3313 mSpecularFocus = aValue;
3314 break;
3315 default:
3316 return false;
3318 return true;
3321 DistantLightSoftware::DistantLightSoftware() : mAzimuth(0), mElevation(0) {}
3323 bool DistantLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
3324 switch (aIndex) {
3325 case ATT_DISTANT_LIGHT_AZIMUTH:
3326 mAzimuth = aValue;
3327 break;
3328 case ATT_DISTANT_LIGHT_ELEVATION:
3329 mElevation = aValue;
3330 break;
3331 default:
3332 return false;
3334 return true;
3337 static inline Point3D Normalized(const Point3D& vec) {
3338 Point3D copy(vec);
3339 copy.Normalize();
3340 return copy;
3343 template <typename LightType, typename LightingType>
3344 FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware(
3345 const char* aTypeName)
3346 : mSurfaceScale(0)
3347 #if defined(MOZILLA_INTERNAL_API) && defined(NS_BUILD_REFCNT_LOGGING)
3349 mTypeName(aTypeName)
3350 #endif
3354 template <typename LightType, typename LightingType>
3355 int32_t FilterNodeLightingSoftware<LightType, LightingType>::InputIndex(
3356 uint32_t aInputEnumIndex) {
3357 switch (aInputEnumIndex) {
3358 case IN_LIGHTING_IN:
3359 return 0;
3360 default:
3361 return -1;
3365 template <typename LightType, typename LightingType>
3366 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
3367 uint32_t aIndex, const Point3D& aPoint) {
3368 if (mLight.SetAttribute(aIndex, aPoint)) {
3369 Invalidate();
3370 return;
3372 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute point");
3375 template <typename LightType, typename LightingType>
3376 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
3377 uint32_t aIndex, Float aValue) {
3378 if (mLight.SetAttribute(aIndex, aValue) ||
3379 mLighting.SetAttribute(aIndex, aValue)) {
3380 Invalidate();
3381 return;
3383 switch (aIndex) {
3384 case ATT_LIGHTING_SURFACE_SCALE:
3385 mSurfaceScale = std::fpclassify(aValue) == FP_SUBNORMAL ? 0.0 : aValue;
3386 break;
3387 default:
3388 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float");
3390 Invalidate();
3393 template <typename LightType, typename LightingType>
3394 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
3395 uint32_t aIndex, const Size& aKernelUnitLength) {
3396 switch (aIndex) {
3397 case ATT_LIGHTING_KERNEL_UNIT_LENGTH:
3398 mKernelUnitLength = aKernelUnitLength;
3399 break;
3400 default:
3401 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size");
3403 Invalidate();
3406 template <typename LightType, typename LightingType>
3407 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(
3408 uint32_t aIndex, const DeviceColor& aColor) {
3409 MOZ_ASSERT(aIndex == ATT_LIGHTING_COLOR);
3410 mColor = aColor;
3411 Invalidate();
3414 template <typename LightType, typename LightingType>
3415 IntRect
3416 FilterNodeLightingSoftware<LightType, LightingType>::GetOutputRectInRect(
3417 const IntRect& aRect) {
3418 return aRect;
3421 Point3D PointLightSoftware::GetVectorToLight(const Point3D& aTargetPoint) {
3422 return Normalized(mPosition - aTargetPoint);
3425 uint32_t PointLightSoftware::GetColor(uint32_t aLightColor,
3426 const Point3D& aVectorToLight) {
3427 return aLightColor;
3430 void SpotLightSoftware::Prepare() {
3431 mVectorFromFocusPointToLight = Normalized(mPointsAt - mPosition);
3432 mLimitingConeCos =
3433 std::max<double>(cos(mLimitingConeAngle * M_PI / 180.0), 0.0);
3434 mPowCache.CacheForExponent(mSpecularFocus);
3437 Point3D SpotLightSoftware::GetVectorToLight(const Point3D& aTargetPoint) {
3438 return Normalized(mPosition - aTargetPoint);
3441 uint32_t SpotLightSoftware::GetColor(uint32_t aLightColor,
3442 const Point3D& aVectorToLight) {
3443 union {
3444 uint32_t color;
3445 uint8_t colorC[4];
3448 Float dot = -aVectorToLight.DotProduct(mVectorFromFocusPointToLight);
3449 if (!mPowCache.HasPowerTable()) {
3450 dot *= (dot >= mLimitingConeCos);
3451 color = aLightColor;
3452 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] *= dot;
3453 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] *= dot;
3454 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] *= dot;
3455 } else {
3456 color = aLightColor;
3457 uint16_t doti = dot * (dot >= 0) * (1 << PowCache::sInputIntPrecisionBits);
3458 uint32_t tmp = mPowCache.Pow(doti) * (dot >= mLimitingConeCos);
3459 MOZ_ASSERT(tmp <= (1 << PowCache::sOutputIntPrecisionBits),
3460 "pow() result must not exceed 1.0");
3461 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
3462 uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] * tmp) >>
3463 PowCache::sOutputIntPrecisionBits);
3464 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
3465 uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] * tmp) >>
3466 PowCache::sOutputIntPrecisionBits);
3467 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
3468 uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] * tmp) >>
3469 PowCache::sOutputIntPrecisionBits);
3471 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
3472 return color;
3475 void DistantLightSoftware::Prepare() {
3476 const double radPerDeg = M_PI / 180.0;
3477 mVectorToLight.x = cos(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
3478 mVectorToLight.y = sin(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
3479 mVectorToLight.z = sin(mElevation * radPerDeg);
3482 Point3D DistantLightSoftware::GetVectorToLight(const Point3D& aTargetPoint) {
3483 return mVectorToLight;
3486 uint32_t DistantLightSoftware::GetColor(uint32_t aLightColor,
3487 const Point3D& aVectorToLight) {
3488 return aLightColor;
3491 template <typename CoordType>
3492 static Point3D GenerateNormal(const uint8_t* data, int32_t stride,
3493 uint8_t* boundsBegin, uint8_t* boundsEnd,
3494 int32_t x, int32_t y, float surfaceScale,
3495 CoordType dx, CoordType dy) {
3496 const uint8_t* index = data + y * stride + x;
3498 CoordType zero = 0;
3500 // See this for source of constants:
3501 // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
3502 int16_t normalX = -1 * ColorComponentAtPoint(index, stride, boundsBegin,
3503 boundsEnd, -dx, -dy, 1, 0) +
3504 1 * ColorComponentAtPoint(index, stride, boundsBegin,
3505 boundsEnd, dx, -dy, 1, 0) +
3506 -2 * ColorComponentAtPoint(index, stride, boundsBegin,
3507 boundsEnd, -dx, zero, 1, 0) +
3508 2 * ColorComponentAtPoint(index, stride, boundsBegin,
3509 boundsEnd, dx, zero, 1, 0) +
3510 -1 * ColorComponentAtPoint(index, stride, boundsBegin,
3511 boundsEnd, -dx, dy, 1, 0) +
3512 1 * ColorComponentAtPoint(index, stride, boundsBegin,
3513 boundsEnd, dx, dy, 1, 0);
3515 int16_t normalY = -1 * ColorComponentAtPoint(index, stride, boundsBegin,
3516 boundsEnd, -dx, -dy, 1, 0) +
3517 -2 * ColorComponentAtPoint(index, stride, boundsBegin,
3518 boundsEnd, zero, -dy, 1, 0) +
3519 -1 * ColorComponentAtPoint(index, stride, boundsBegin,
3520 boundsEnd, dx, -dy, 1, 0) +
3521 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);
3528 Point3D normal;
3529 normal.x = -surfaceScale * normalX / 4.0f;
3530 normal.y = -surfaceScale * normalY / 4.0f;
3531 normal.z = 255;
3532 return Normalized(normal);
3535 template <typename LightType, typename LightingType>
3536 already_AddRefed<DataSourceSurface>
3537 FilterNodeLightingSoftware<LightType, LightingType>::Render(
3538 const IntRect& aRect) {
3539 if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
3540 mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
3541 return DoRender(aRect, (int32_t)mKernelUnitLength.width,
3542 (int32_t)mKernelUnitLength.height);
3544 return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
3547 template <typename LightType, typename LightingType>
3548 void FilterNodeLightingSoftware<
3549 LightType, LightingType>::RequestFromInputsForRect(const IntRect& aRect) {
3550 IntRect srcRect = aRect;
3551 srcRect.Inflate(ceil(mKernelUnitLength.width),
3552 ceil(mKernelUnitLength.height));
3553 RequestInputRect(IN_LIGHTING_IN, srcRect);
3556 template <typename LightType, typename LightingType>
3557 IntRect FilterNodeLightingSoftware<LightType, LightingType>::MapRectToSource(
3558 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) {
3559 IntRect srcRect = aRect;
3560 srcRect.Inflate(ceil(mKernelUnitLength.width),
3561 ceil(mKernelUnitLength.height));
3562 return MapInputRectToSource(IN_LIGHTING_IN, srcRect, aMax, aSourceNode);
3565 template <typename LightType, typename LightingType>
3566 template <typename CoordType>
3567 already_AddRefed<DataSourceSurface>
3568 FilterNodeLightingSoftware<LightType, LightingType>::DoRender(
3569 const IntRect& aRect, CoordType aKernelUnitLengthX,
3570 CoordType aKernelUnitLengthY) {
3571 MOZ_ASSERT(aKernelUnitLengthX > 0,
3572 "aKernelUnitLengthX can be a negative or zero value");
3573 MOZ_ASSERT(aKernelUnitLengthY > 0,
3574 "aKernelUnitLengthY can be a negative or zero value");
3576 IntRect srcRect = aRect;
3577 IntSize size = aRect.Size();
3578 srcRect.Inflate(ceil(float(aKernelUnitLengthX)),
3579 ceil(float(aKernelUnitLengthY)));
3581 // Inflate the source rect by another pixel because the bilinear filtering in
3582 // ColorComponentAtPoint may want to access the margins.
3583 srcRect.Inflate(1);
3585 RefPtr<DataSourceSurface> input = GetInputDataSourceSurface(
3586 IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8, EDGE_MODE_NONE);
3588 if (!input) {
3589 return nullptr;
3592 if (input->GetFormat() != SurfaceFormat::A8) {
3593 input = FilterProcessing::ExtractAlpha(input);
3596 RefPtr<DataSourceSurface> target =
3597 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
3598 if (MOZ2D_WARN_IF(!target)) {
3599 return nullptr;
3602 IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
3604 DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
3605 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
3606 if (MOZ2D_WARN_IF(!(sourceMap.IsMapped() && targetMap.IsMapped()))) {
3607 return nullptr;
3610 uint8_t* sourceData =
3611 DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
3612 int32_t sourceStride = sourceMap.GetStride();
3613 uint8_t* sourceBegin = sourceMap.GetData();
3614 uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
3615 uint8_t* targetData = targetMap.GetData();
3616 int32_t targetStride = targetMap.GetStride();
3618 uint32_t lightColor = ColorToBGRA(mColor);
3619 mLight.Prepare();
3620 mLighting.Prepare();
3622 for (int32_t y = 0; y < size.height; y++) {
3623 for (int32_t x = 0; x < size.width; x++) {
3624 int32_t sourceIndex = y * sourceStride + x;
3625 int32_t targetIndex = y * targetStride + 4 * x;
3627 Point3D normal =
3628 GenerateNormal(sourceData, sourceStride, sourceBegin, sourceEnd, x, y,
3629 mSurfaceScale, aKernelUnitLengthX, aKernelUnitLengthY);
3631 IntPoint pointInFilterSpace(aRect.X() + x, aRect.Y() + y);
3632 Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f;
3633 Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z);
3634 Point3D rayDir = mLight.GetVectorToLight(pt);
3635 uint32_t color = mLight.GetColor(lightColor, rayDir);
3637 *(uint32_t*)(targetData + targetIndex) =
3638 mLighting.LightPixel(normal, rayDir, color);
3641 // Zero padding to keep valgrind happy.
3642 PodZero(&targetData[y * targetStride + 4 * size.width],
3643 targetStride - 4 * size.width);
3646 return target.forget();
3649 DiffuseLightingSoftware::DiffuseLightingSoftware() : mDiffuseConstant(0) {}
3651 bool DiffuseLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
3652 switch (aIndex) {
3653 case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT:
3654 mDiffuseConstant = aValue;
3655 break;
3656 default:
3657 return false;
3659 return true;
3662 uint32_t DiffuseLightingSoftware::LightPixel(const Point3D& aNormal,
3663 const Point3D& aVectorToLight,
3664 uint32_t aColor) {
3665 Float dotNL = std::max(0.0f, aNormal.DotProduct(aVectorToLight));
3666 Float diffuseNL = mDiffuseConstant * dotNL;
3668 union {
3669 uint32_t bgra;
3670 uint8_t components[4];
3671 } color = {aColor};
3672 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = umin(
3673 uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]),
3674 255U);
3675 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = umin(
3676 uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]),
3677 255U);
3678 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = umin(
3679 uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]),
3680 255U);
3681 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
3682 return color.bgra;
3685 SpecularLightingSoftware::SpecularLightingSoftware()
3686 : mSpecularConstant(0), mSpecularExponent(0), mSpecularConstantInt(0) {}
3688 bool SpecularLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) {
3689 switch (aIndex) {
3690 case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT:
3691 mSpecularConstant = std::min(std::max(aValue, 0.0f), 255.0f);
3692 break;
3693 case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT:
3694 mSpecularExponent = std::min(std::max(aValue, 1.0f), 128.0f);
3695 break;
3696 default:
3697 return false;
3699 return true;
3702 void SpecularLightingSoftware::Prepare() {
3703 mPowCache.CacheForExponent(mSpecularExponent);
3704 mSpecularConstantInt = uint32_t(mSpecularConstant * (1 << 8));
3707 uint32_t SpecularLightingSoftware::LightPixel(const Point3D& aNormal,
3708 const Point3D& aVectorToLight,
3709 uint32_t aColor) {
3710 Point3D vectorToEye(0, 0, 1);
3711 Point3D halfwayVector = aVectorToLight + vectorToEye;
3712 Float halfwayLength = halfwayVector.Length();
3713 if (halfwayLength > 0) {
3714 halfwayVector /= halfwayLength;
3716 Float dotNH = aNormal.DotProduct(halfwayVector);
3717 uint16_t dotNHi =
3718 uint16_t(dotNH * (dotNH >= 0) * (1 << PowCache::sInputIntPrecisionBits));
3719 // The exponent for specular is in [1,128] range, so we don't need to check
3720 // and optimize for the "default power table" scenario here.
3721 MOZ_ASSERT(mPowCache.HasPowerTable());
3722 uint32_t specularNHi =
3723 uint32_t(mSpecularConstantInt) * mPowCache.Pow(dotNHi) >> 8;
3725 union {
3726 uint32_t bgra;
3727 uint8_t components[4];
3728 } color = {aColor};
3729 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
3730 umin((specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]) >>
3731 PowCache::sOutputIntPrecisionBits,
3732 255U);
3733 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
3734 umin((specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) >>
3735 PowCache::sOutputIntPrecisionBits,
3736 255U);
3737 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
3738 umin((specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) >>
3739 PowCache::sOutputIntPrecisionBits,
3740 255U);
3742 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
3743 umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B],
3744 umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G],
3745 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]));
3746 return color.bgra;
3749 } // namespace gfx
3750 } // namespace mozilla