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/. */
8 #include "DataSurfaceHelpers.h"
9 #include "FilterNodeSoftware.h"
14 #include "FilterProcessing.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
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.
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.
44 mNumPowTablePreSquares
= -1;
47 int numPreSquares
= 0;
48 while (numPreSquares
< 5 && aExponent
> (1 << (numPreSquares
+ 2))) {
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
++) {
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!");
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");
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; }
97 static const size_t sCacheSize
= (1 << sCacheIndexPrecisionBits
) + 1;
99 int mNumPowTablePreSquares
;
100 uint16_t mPowTable
[sCacheSize
];
103 class PointLightSoftware
{
105 bool SetAttribute(uint32_t aIndex
, Float
) { return false; }
106 bool SetAttribute(uint32_t aIndex
, const Point3D
&);
108 Point3D
GetVectorToLight(const Point3D
& aTargetPoint
);
109 uint32_t GetColor(uint32_t aLightColor
, const Point3D
& aVectorToLight
);
115 class SpotLightSoftware
{
118 bool SetAttribute(uint32_t aIndex
, Float
);
119 bool SetAttribute(uint32_t aIndex
, const Point3D
&);
121 Point3D
GetVectorToLight(const Point3D
& aTargetPoint
);
122 uint32_t GetColor(uint32_t aLightColor
, const Point3D
& aVectorToLight
);
127 Point3D mVectorFromFocusPointToLight
;
128 Float mSpecularFocus
;
129 Float mLimitingConeAngle
;
130 Float mLimitingConeCos
;
134 class DistantLightSoftware
{
136 DistantLightSoftware();
137 bool SetAttribute(uint32_t aIndex
, Float
);
138 bool SetAttribute(uint32_t aIndex
, const Point3D
&) { return false; }
140 Point3D
GetVectorToLight(const Point3D
& aTargetPoint
);
141 uint32_t GetColor(uint32_t aLightColor
, const Point3D
& aVectorToLight
);
146 Point3D mVectorToLight
;
149 class DiffuseLightingSoftware
{
151 DiffuseLightingSoftware();
152 bool SetAttribute(uint32_t aIndex
, Float
);
154 uint32_t LightPixel(const Point3D
& aNormal
, const Point3D
& aVectorToLight
,
158 Float mDiffuseConstant
;
161 class SpecularLightingSoftware
{
163 SpecularLightingSoftware();
164 bool SetAttribute(uint32_t aIndex
, Float
);
166 uint32_t LightPixel(const Point3D
& aNormal
, const Point3D
& aVectorToLight
,
170 Float mSpecularConstant
;
171 Float mSpecularExponent
;
172 uint32_t mSpecularConstantInt
;
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())) {
200 uint8_t* sourcePixelData
=
201 DataAtOffset(aSurface
, surfMap
.GetMappedSurface(), aPixelPos
);
203 DataAtOffset(aSurface
, surfMap
.GetMappedSurface(), aFillRect
.TopLeft());
204 int bpp
= BytesPerPixel(aSurface
->GetFormat());
206 // Fill the first row by hand.
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())) {
238 uint8_t* sampleData
=
239 DataAtOffset(aSurface
, surfMap
.GetMappedSurface(), aSampleRect
.TopLeft());
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())) {
270 uint8_t* sampleData
=
271 DataAtOffset(aSurface
, surfMap
.GetMappedSurface(), aSampleRect
.TopLeft());
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();
302 for (int32_t ix
= 0; ix
< 3; ix
++) {
305 fill
.SetRectX(0, aFromRect
.X());
306 sampleRect
.SetRectX(fill
.XMost(), 1);
309 fill
.SetRectX(aFromRect
.X(), aFromRect
.Width());
310 sampleRect
.SetRectX(fill
.X(), fill
.Width());
313 fill
.MoveToX(aFromRect
.XMost());
314 fill
.SetRightEdge(size
.width
);
315 sampleRect
.SetRectX(fill
.X() - 1, 1);
318 if (fill
.Width() <= 0) {
321 bool xIsMiddle
= (ix
== 1);
322 for (int32_t iy
= 0; iy
< 3; iy
++) {
325 fill
.SetRectY(0, aFromRect
.Y());
326 sampleRect
.SetRectY(fill
.YMost(), 1);
329 fill
.SetRectY(aFromRect
.Y(), aFromRect
.Height());
330 sampleRect
.SetRectY(fill
.Y(), fill
.Height());
333 fill
.MoveToY(aFromRect
.YMost());
334 fill
.SetBottomEdge(size
.height
);
335 sampleRect
.SetRectY(fill
.Y() - 1, 1);
338 if (fill
.Height() <= 0) {
341 bool yIsMiddle
= (iy
== 1);
342 if (!xIsMiddle
&& !yIsMiddle
) {
344 FillRectWithPixel(aSurface
, fill
, sampleRect
.TopLeft());
346 if (xIsMiddle
&& !yIsMiddle
) {
347 // Top middle or bottom middle
348 FillRectWithVerticallyRepeatingHorizontalStrip(aSurface
, fill
,
351 if (!xIsMiddle
&& yIsMiddle
) {
352 // Left middle or right middle
353 FillRectWithHorizontallyRepeatingVerticalStrip(aSurface
, fill
,
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.
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
)) {
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();
442 already_AddRefed
<FilterNode
> FilterNodeSoftware::Create(FilterType aType
) {
443 RefPtr
<FilterNodeSoftware
> filter
;
445 case FilterType::BLEND
:
446 filter
= new FilterNodeBlendSoftware();
448 case FilterType::TRANSFORM
:
449 filter
= new FilterNodeTransformSoftware();
451 case FilterType::MORPHOLOGY
:
452 filter
= new FilterNodeMorphologySoftware();
454 case FilterType::COLOR_MATRIX
:
455 filter
= new FilterNodeColorMatrixSoftware();
457 case FilterType::FLOOD
:
458 filter
= new FilterNodeFloodSoftware();
460 case FilterType::TILE
:
461 filter
= new FilterNodeTileSoftware();
463 case FilterType::TABLE_TRANSFER
:
464 filter
= new FilterNodeTableTransferSoftware();
466 case FilterType::DISCRETE_TRANSFER
:
467 filter
= new FilterNodeDiscreteTransferSoftware();
469 case FilterType::LINEAR_TRANSFER
:
470 filter
= new FilterNodeLinearTransferSoftware();
472 case FilterType::GAMMA_TRANSFER
:
473 filter
= new FilterNodeGammaTransferSoftware();
475 case FilterType::CONVOLVE_MATRIX
:
476 filter
= new FilterNodeConvolveMatrixSoftware();
478 case FilterType::DISPLACEMENT_MAP
:
479 filter
= new FilterNodeDisplacementMapSoftware();
481 case FilterType::TURBULENCE
:
482 filter
= new FilterNodeTurbulenceSoftware();
484 case FilterType::ARITHMETIC_COMBINE
:
485 filter
= new FilterNodeArithmeticCombineSoftware();
487 case FilterType::COMPOSITE
:
488 filter
= new FilterNodeCompositeSoftware();
490 case FilterType::GAUSSIAN_BLUR
:
491 filter
= new FilterNodeGaussianBlurSoftware();
493 case FilterType::DIRECTIONAL_BLUR
:
494 filter
= new FilterNodeDirectionalBlurSoftware();
496 case FilterType::CROP
:
497 filter
= new FilterNodeCropSoftware();
499 case FilterType::PREMULTIPLY
:
500 filter
= new FilterNodePremultiplySoftware();
502 case FilterType::UNPREMULTIPLY
:
503 filter
= new FilterNodeUnpremultiplySoftware();
505 case FilterType::OPACITY
:
506 filter
= new FilterNodeOpacitySoftware();
508 case FilterType::POINT_DIFFUSE
:
509 filter
= new FilterNodeLightingSoftware
<PointLightSoftware
,
510 DiffuseLightingSoftware
>(
511 "FilterNodeLightingSoftware<PointLight, DiffuseLighting>");
513 case FilterType::POINT_SPECULAR
:
514 filter
= new FilterNodeLightingSoftware
<PointLightSoftware
,
515 SpecularLightingSoftware
>(
516 "FilterNodeLightingSoftware<PointLight, SpecularLighting>");
518 case FilterType::SPOT_DIFFUSE
:
519 filter
= new FilterNodeLightingSoftware
<SpotLightSoftware
,
520 DiffuseLightingSoftware
>(
521 "FilterNodeLightingSoftware<SpotLight, DiffuseLighting>");
523 case FilterType::SPOT_SPECULAR
:
524 filter
= new FilterNodeLightingSoftware
<SpotLightSoftware
,
525 SpecularLightingSoftware
>(
526 "FilterNodeLightingSoftware<SpotLight, SpecularLighting>");
528 case FilterType::DISTANT_DIFFUSE
:
529 filter
= new FilterNodeLightingSoftware
<DistantLightSoftware
,
530 DiffuseLightingSoftware
>(
531 "FilterNodeLightingSoftware<DistantLight, DiffuseLighting>");
533 case FilterType::DISTANT_SPECULAR
:
534 filter
= new FilterNodeLightingSoftware
<DistantLightSoftware
,
535 SpecularLightingSoftware
>(
536 "FilterNodeLightingSoftware<DistantLight, SpecularLighting>");
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",
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");
561 IntRect outputRect
= GetOutputRectInRect(renderIntRect
);
562 if (outputRect
.Overflows()) {
563 #ifdef DEBUG_DUMP_SURFACES
564 printf("output rect overflowed, not painting anything\n");
570 RefPtr
<DataSourceSurface
> result
;
571 if (!outputRect
.IsEmpty()) {
572 result
= GetOutput(outputRect
);
576 // Null results are allowed and treated as transparent. Don't draw anything.
577 #ifdef DEBUG_DUMP_SURFACES
578 printf("output returned null\n");
584 #ifdef DEBUG_DUMP_SURFACES
585 printf("output from %s:\n", GetName());
586 printf("<img src='");
587 gfxUtils::DumpAsDataURL(result
);
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();
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()) {
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
)) {
625 requestedRect
= mRequestedRect
;
627 MOZ_ASSERT(mCachedOutput
, "cached rect but no cached output?");
628 cachedRect
= mCachedRect
;
629 cachedOutput
= mCachedOutput
;
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();
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.
659 mRequestedRect
= mRequestedRect
.Union(aRect
);
660 RequestFromInputsForRect(aRect
);
663 IntRect
FilterNodeSoftware::MapInputRectToSource(uint32_t aInputEnumIndex
,
664 const IntRect
& aRect
,
666 FilterNode
* aSourceNode
) {
667 int32_t inputIndex
= InputIndex(aInputEnumIndex
);
668 if (inputIndex
< 0) {
669 gfxDevCrash(LogReason::FilterInputError
)
670 << "Invalid input " << inputIndex
<< " vs. " << NumberOfSetInputs();
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.
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
) {
690 void FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex
,
691 const IntRect
& aRect
) {
692 if (aRect
.Overflows()) {
696 int32_t inputIndex
= InputIndex(aInputEnumIndex
);
697 if (inputIndex
< 0 || (uint32_t)inputIndex
>= NumberOfSetInputs()) {
698 gfxDevCrash(LogReason::FilterInputError
)
699 << "Invalid input " << inputIndex
<< " vs. " << NumberOfSetInputs();
702 if (mInputSurfaces
[inputIndex
]) {
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()) {
728 #ifdef DEBUG_DUMP_SURFACES
730 "<section><h1>GetInputDataSourceSurface with aRect: %d, %d, %d, "
732 aRect
.x
, aRect
.y
, aRect
.Width(), aRect
.Height());
734 int32_t inputIndex
= InputIndex(aInputEnumIndex
);
735 if (inputIndex
< 0 || (uint32_t)inputIndex
>= NumberOfSetInputs()) {
736 gfxDevCrash(LogReason::FilterInputData
)
737 << "Invalid data " << inputIndex
<< " vs. " << NumberOfSetInputs();
741 if (aRect
.IsEmpty()) {
745 RefPtr
<SourceSurface
> surface
;
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");
754 surfaceRect
= surface
->GetRect();
756 // Input from input filter
757 #ifdef DEBUG_DUMP_SURFACES
758 printf("getting input from input filter %s...\n",
759 mInputFilters
[inputIndex
]->GetName());
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());
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");
782 if (!surfaceRect
.IsEmpty() && !surface
) {
783 #ifdef DEBUG_DUMP_SURFACES
784 printf(" -- no input --</section>\n\n");
789 if (aTransparencyPaddedSourceRect
&&
790 !aTransparencyPaddedSourceRect
->IsEmpty()) {
791 IntRect srcRect
= aTransparencyPaddedSourceRect
->Intersect(aRect
);
793 GetDataSurfaceInRect(surface
, surfaceRect
, srcRect
, EDGE_MODE_NONE
);
795 surfaceRect
= srcRect
;
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
);
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.
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
);
827 #ifdef DEBUG_DUMP_SURFACES
828 printf(" -- no input --</section>\n\n");
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>");
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()) {
857 int32_t inputIndex
= InputIndex(aInputEnumIndex
);
858 if (inputIndex
< 0 || (uint32_t)inputIndex
>= NumberOfSetInputs()) {
859 gfxDevCrash(LogReason::FilterInputRect
)
860 << "Invalid rect " << inputIndex
<< " vs. " << NumberOfSetInputs();
863 if (mInputSurfaces
[inputIndex
]) {
864 return aInRect
.Intersect(mInputSurfaces
[inputIndex
]->GetRect());
866 RefPtr
<FilterNodeSoftware
> filter
= mInputFilters
[inputIndex
];
867 MOZ_ASSERT(filter
, "missing input");
871 return filter
->GetOutputRectInRect(aInRect
);
874 size_t FilterNodeSoftware::NumberOfSetInputs() {
875 return std::max(mInputSurfaces
.size(), mInputFilters
.size());
878 void FilterNodeSoftware::AddInvalidationListener(
879 FilterInvalidationListener
* aListener
) {
880 MOZ_ASSERT(aListener
, "null listener");
881 mInvalidationListeners
.push_back(aListener
);
884 void FilterNodeSoftware::RemoveInvalidationListener(
885 FilterInvalidationListener
* aListener
) {
886 MOZ_ASSERT(aListener
, "null listener");
887 std::vector
<FilterInvalidationListener
*>::iterator it
= std::find(
888 mInvalidationListeners
.begin(), mInvalidationListeners
.end(), aListener
);
889 mInvalidationListeners
.erase(it
);
892 void FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware
* aFilter
) {
896 void FilterNodeSoftware::Invalidate() {
897 mCachedOutput
= nullptr;
898 mCachedRect
= IntRect();
899 for (std::vector
<FilterInvalidationListener
*>::iterator it
=
900 mInvalidationListeners
.begin();
901 it
!= mInvalidationListeners
.end(); it
++) {
902 (*it
)->FilterInvalidated(this);
906 FilterNodeSoftware::FilterNodeSoftware() {}
908 FilterNodeSoftware::~FilterNodeSoftware() {
910 mInvalidationListeners
.empty(),
911 "All invalidation listeners should have unsubscribed themselves by now!");
913 for (std::vector
<RefPtr
<FilterNodeSoftware
> >::iterator it
=
914 mInputFilters
.begin();
915 it
!= mInputFilters
.end(); it
++) {
917 (*it
)->RemoveInvalidationListener(this);
922 void FilterNodeSoftware::SetInput(uint32_t aIndex
, FilterNode
* aFilter
) {
923 if (aFilter
&& aFilter
->GetBackendType() != FILTER_BACKEND_SOFTWARE
) {
924 MOZ_ASSERT(false, "can only take software filters as inputs");
927 SetInput(aIndex
, nullptr, static_cast<FilterNodeSoftware
*>(aFilter
));
930 void FilterNodeSoftware::SetInput(uint32_t aIndex
, SourceSurface
* aSurface
) {
931 SetInput(aIndex
, aSurface
, nullptr);
934 void FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex
,
935 SourceSurface
* aSurface
,
936 FilterNodeSoftware
* aFilter
) {
937 int32_t inputIndex
= InputIndex(aInputEnumIndex
);
938 if (inputIndex
< 0) {
939 gfxDevCrash(LogReason::FilterInputSet
) << "Invalid set " << inputIndex
;
942 if ((uint32_t)inputIndex
>= NumberOfSetInputs()) {
943 mInputSurfaces
.resize(inputIndex
+ 1);
944 mInputFilters
.resize(inputIndex
+ 1);
946 mInputSurfaces
[inputIndex
] = aSurface
;
947 if (mInputFilters
[inputIndex
]) {
948 mInputFilters
[inputIndex
]->RemoveInvalidationListener(this);
951 aFilter
->AddInvalidationListener(this);
953 mInputFilters
[inputIndex
] = aFilter
;
954 if (!aSurface
&& !aFilter
&& (size_t)inputIndex
== NumberOfSetInputs()) {
955 mInputSurfaces
.resize(inputIndex
);
956 mInputFilters
.resize(inputIndex
);
961 FilterNodeBlendSoftware::FilterNodeBlendSoftware()
962 : mBlendMode(BLEND_MODE_MULTIPLY
) {}
964 int32_t FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex
) {
965 switch (aInputEnumIndex
) {
975 void FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex
,
976 uint32_t aBlendMode
) {
977 MOZ_ASSERT(aIndex
== ATT_BLEND_BLENDMODE
);
978 mBlendMode
= static_cast<BlendMode
>(aBlendMode
);
982 static CompositionOp
ToBlendOp(BlendMode aOp
) {
984 case BLEND_MODE_MULTIPLY
:
985 return CompositionOp::OP_MULTIPLY
;
986 case BLEND_MODE_SCREEN
:
987 return CompositionOp::OP_SCREEN
;
988 case BLEND_MODE_OVERLAY
:
989 return CompositionOp::OP_OVERLAY
;
990 case BLEND_MODE_DARKEN
:
991 return CompositionOp::OP_DARKEN
;
992 case BLEND_MODE_LIGHTEN
:
993 return CompositionOp::OP_LIGHTEN
;
994 case BLEND_MODE_COLOR_DODGE
:
995 return CompositionOp::OP_COLOR_DODGE
;
996 case BLEND_MODE_COLOR_BURN
:
997 return CompositionOp::OP_COLOR_BURN
;
998 case BLEND_MODE_HARD_LIGHT
:
999 return CompositionOp::OP_HARD_LIGHT
;
1000 case BLEND_MODE_SOFT_LIGHT
:
1001 return CompositionOp::OP_SOFT_LIGHT
;
1002 case BLEND_MODE_DIFFERENCE
:
1003 return CompositionOp::OP_DIFFERENCE
;
1004 case BLEND_MODE_EXCLUSION
:
1005 return CompositionOp::OP_EXCLUSION
;
1006 case BLEND_MODE_HUE
:
1007 return CompositionOp::OP_HUE
;
1008 case BLEND_MODE_SATURATION
:
1009 return CompositionOp::OP_SATURATION
;
1010 case BLEND_MODE_COLOR
:
1011 return CompositionOp::OP_COLOR
;
1012 case BLEND_MODE_LUMINOSITY
:
1013 return CompositionOp::OP_LUMINOSITY
;
1016 MOZ_ASSERT_UNREACHABLE("Unexpected BlendMode");
1017 return CompositionOp::OP_OVER
;
1020 already_AddRefed
<DataSourceSurface
> FilterNodeBlendSoftware::Render(
1021 const IntRect
& aRect
) {
1022 RefPtr
<DataSourceSurface
> input1
=
1023 GetInputDataSourceSurface(IN_BLEND_IN
, aRect
, NEED_COLOR_CHANNELS
);
1024 RefPtr
<DataSourceSurface
> input2
=
1025 GetInputDataSourceSurface(IN_BLEND_IN2
, aRect
, NEED_COLOR_CHANNELS
);
1027 // Null inputs need to be treated as transparent.
1029 // First case: both are transparent.
1030 if (!input1
&& !input2
) {
1031 // Then the result is transparent, too.
1035 // Second case: one of them is transparent. Return the non-transparent one.
1036 if (!input1
|| !input2
) {
1037 return input1
? input1
.forget() : input2
.forget();
1040 // Third case: both are non-transparent.
1041 // Apply normal filtering.
1042 RefPtr
<DataSourceSurface
> target
=
1043 FilterProcessing::ApplyBlending(input1
, input2
, mBlendMode
);
1044 if (target
!= nullptr) {
1045 return target
.forget();
1048 IntSize size
= input1
->GetSize();
1049 target
= Factory::CreateDataSourceSurface(size
, SurfaceFormat::B8G8R8A8
);
1050 if (MOZ2D_WARN_IF(!target
)) {
1054 CopyRect(input1
, target
, IntRect(IntPoint(), size
), IntPoint());
1056 // This needs to stay in scope until the draw target has been flushed.
1057 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::READ_WRITE
);
1058 if (MOZ2D_WARN_IF(!targetMap
.IsMapped())) {
1062 RefPtr
<DrawTarget
> dt
= Factory::CreateDrawTargetForData(
1063 BackendType::SKIA
, targetMap
.GetData(), target
->GetSize(),
1064 targetMap
.GetStride(), target
->GetFormat());
1068 << "FilterNodeBlendSoftware::Render failed in CreateDrawTargetForData";
1072 Rect
r(0, 0, size
.width
, size
.height
);
1073 dt
->DrawSurface(input2
, r
, r
, DrawSurfaceOptions(),
1074 DrawOptions(1.0f
, ToBlendOp(mBlendMode
)));
1076 return target
.forget();
1079 void FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect
& aRect
) {
1080 RequestInputRect(IN_BLEND_IN
, aRect
);
1081 RequestInputRect(IN_BLEND_IN2
, aRect
);
1084 IntRect
FilterNodeBlendSoftware::MapRectToSource(const IntRect
& aRect
,
1085 const IntRect
& aMax
,
1086 FilterNode
* aSourceNode
) {
1087 IntRect result
= MapInputRectToSource(IN_BLEND_IN
, aRect
, aMax
, aSourceNode
);
1088 result
.OrWith(MapInputRectToSource(IN_BLEND_IN2
, aRect
, aMax
, aSourceNode
));
1092 IntRect
FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
1093 return GetInputRectInRect(IN_BLEND_IN
, aRect
)
1094 .Union(GetInputRectInRect(IN_BLEND_IN2
, aRect
))
1098 FilterNodeTransformSoftware::FilterNodeTransformSoftware()
1099 : mSamplingFilter(SamplingFilter::GOOD
) {}
1101 int32_t FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex
) {
1102 switch (aInputEnumIndex
) {
1103 case IN_TRANSFORM_IN
:
1110 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex
,
1112 MOZ_ASSERT(aIndex
== ATT_TRANSFORM_FILTER
);
1113 mSamplingFilter
= static_cast<SamplingFilter
>(aFilter
);
1117 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex
,
1118 const Matrix
& aMatrix
) {
1119 MOZ_ASSERT(aIndex
== ATT_TRANSFORM_MATRIX
);
1124 IntRect
FilterNodeTransformSoftware::SourceRectForOutputRect(
1125 const IntRect
& aRect
) {
1126 if (aRect
.IsEmpty()) {
1130 Matrix
inverted(mMatrix
);
1131 if (!inverted
.Invert()) {
1135 Rect neededRect
= inverted
.TransformBounds(Rect(aRect
));
1136 neededRect
.RoundOut();
1137 IntRect neededIntRect
;
1138 if (!neededRect
.ToIntRect(&neededIntRect
)) {
1141 return GetInputRectInRect(IN_TRANSFORM_IN
, neededIntRect
);
1144 IntRect
FilterNodeTransformSoftware::MapRectToSource(const IntRect
& aRect
,
1145 const IntRect
& aMax
,
1146 FilterNode
* aSourceNode
) {
1147 if (aRect
.IsEmpty()) {
1151 Matrix
inverted(mMatrix
);
1152 if (!inverted
.Invert()) {
1156 Rect neededRect
= inverted
.TransformBounds(Rect(aRect
));
1157 neededRect
.RoundOut();
1158 IntRect neededIntRect
;
1159 if (!neededRect
.ToIntRect(&neededIntRect
)) {
1162 return MapInputRectToSource(IN_TRANSFORM_IN
, neededIntRect
, aMax
,
1166 already_AddRefed
<DataSourceSurface
> FilterNodeTransformSoftware::Render(
1167 const IntRect
& aRect
) {
1168 IntRect srcRect
= SourceRectForOutputRect(aRect
);
1170 RefPtr
<DataSourceSurface
> input
=
1171 GetInputDataSourceSurface(IN_TRANSFORM_IN
, srcRect
);
1177 Matrix transform
= Matrix::Translation(srcRect
.X(), srcRect
.Y()) * mMatrix
*
1178 Matrix::Translation(-aRect
.X(), -aRect
.Y());
1179 if (transform
.IsIdentity() && srcRect
.Size() == aRect
.Size()) {
1180 return input
.forget();
1183 RefPtr
<DataSourceSurface
> surf
=
1184 Factory::CreateDataSourceSurface(aRect
.Size(), input
->GetFormat(), true);
1190 DataSourceSurface::MappedSurface mapping
;
1191 if (!surf
->Map(DataSourceSurface::MapType::WRITE
, &mapping
)) {
1193 << "FilterNodeTransformSoftware::Render failed to map surface";
1197 RefPtr
<DrawTarget
> dt
= Factory::CreateDrawTargetForData(
1198 BackendType::SKIA
, mapping
.mData
, surf
->GetSize(), mapping
.mStride
,
1201 gfxWarning() << "FilterNodeTransformSoftware::Render failed in "
1202 "CreateDrawTargetForData";
1206 Rect
r(0, 0, srcRect
.Width(), srcRect
.Height());
1207 dt
->SetTransform(transform
);
1208 dt
->DrawSurface(input
, r
, r
, DrawSurfaceOptions(mSamplingFilter
));
1212 return surf
.forget();
1215 void FilterNodeTransformSoftware::RequestFromInputsForRect(
1216 const IntRect
& aRect
) {
1217 RequestInputRect(IN_TRANSFORM_IN
, SourceRectForOutputRect(aRect
));
1220 IntRect
FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
1221 IntRect srcRect
= SourceRectForOutputRect(aRect
);
1222 if (srcRect
.IsEmpty()) {
1226 Rect outRect
= mMatrix
.TransformBounds(Rect(srcRect
));
1229 if (!outRect
.ToIntRect(&outIntRect
)) {
1232 return outIntRect
.Intersect(aRect
);
1235 FilterNodeMorphologySoftware::FilterNodeMorphologySoftware()
1236 : mOperator(MORPHOLOGY_OPERATOR_ERODE
) {}
1238 int32_t FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex
) {
1239 switch (aInputEnumIndex
) {
1240 case IN_MORPHOLOGY_IN
:
1247 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex
,
1248 const IntSize
& aRadii
) {
1249 MOZ_ASSERT(aIndex
== ATT_MORPHOLOGY_RADII
);
1250 mRadii
.width
= std::clamp(aRadii
.width
, 0, 100000);
1251 mRadii
.height
= std::clamp(aRadii
.height
, 0, 100000);
1255 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex
,
1256 uint32_t aOperator
) {
1257 MOZ_ASSERT(aIndex
== ATT_MORPHOLOGY_OPERATOR
);
1258 mOperator
= static_cast<MorphologyOperator
>(aOperator
);
1262 static already_AddRefed
<DataSourceSurface
> ApplyMorphology(
1263 const IntRect
& aSourceRect
, DataSourceSurface
* aInput
,
1264 const IntRect
& aDestRect
, int32_t rx
, int32_t ry
,
1265 MorphologyOperator aOperator
) {
1266 IntRect srcRect
= aSourceRect
- aDestRect
.TopLeft();
1267 IntRect destRect
= aDestRect
- aDestRect
.TopLeft();
1268 IntRect
tmpRect(destRect
.X(), srcRect
.Y(), destRect
.Width(),
1271 IntMargin margin
= srcRect
- destRect
;
1272 MOZ_ASSERT(margin
.top
>= ry
&& margin
.right
>= rx
&& margin
.bottom
>= ry
&&
1274 "insufficient margin");
1277 RefPtr
<DataSourceSurface
> tmp
;
1281 tmp
= Factory::CreateDataSourceSurface(tmpRect
.Size(),
1282 SurfaceFormat::B8G8R8A8
);
1283 if (MOZ2D_WARN_IF(!tmp
)) {
1287 DataSourceSurface::ScopedMap
sourceMap(aInput
, DataSourceSurface::READ
);
1288 DataSourceSurface::ScopedMap
tmpMap(tmp
, DataSourceSurface::WRITE
);
1289 if (MOZ2D_WARN_IF(!sourceMap
.IsMapped() || !tmpMap
.IsMapped())) {
1292 uint8_t* sourceData
= DataAtOffset(aInput
, sourceMap
.GetMappedSurface(),
1293 destRect
.TopLeft() - srcRect
.TopLeft());
1294 uint8_t* tmpData
= DataAtOffset(tmp
, tmpMap
.GetMappedSurface(),
1295 destRect
.TopLeft() - tmpRect
.TopLeft());
1297 FilterProcessing::ApplyMorphologyHorizontal(
1298 sourceData
, sourceMap
.GetStride(), tmpData
, tmpMap
.GetStride(), tmpRect
,
1302 RefPtr
<DataSourceSurface
> dest
;
1306 dest
= Factory::CreateDataSourceSurface(destRect
.Size(),
1307 SurfaceFormat::B8G8R8A8
);
1308 if (MOZ2D_WARN_IF(!dest
)) {
1312 DataSourceSurface::ScopedMap
tmpMap(tmp
, DataSourceSurface::READ
);
1313 DataSourceSurface::ScopedMap
destMap(dest
, DataSourceSurface::WRITE
);
1314 if (MOZ2D_WARN_IF(!tmpMap
.IsMapped() || !destMap
.IsMapped())) {
1317 int32_t tmpStride
= tmpMap
.GetStride();
1318 uint8_t* tmpData
= DataAtOffset(tmp
, tmpMap
.GetMappedSurface(),
1319 destRect
.TopLeft() - tmpRect
.TopLeft());
1321 int32_t destStride
= destMap
.GetStride();
1322 uint8_t* destData
= destMap
.GetData();
1324 FilterProcessing::ApplyMorphologyVertical(
1325 tmpData
, tmpStride
, destData
, destStride
, destRect
, ry
, aOperator
);
1328 return dest
.forget();
1331 already_AddRefed
<DataSourceSurface
> FilterNodeMorphologySoftware::Render(
1332 const IntRect
& aRect
) {
1333 IntRect srcRect
= aRect
;
1334 srcRect
.Inflate(mRadii
);
1336 RefPtr
<DataSourceSurface
> input
=
1337 GetInputDataSourceSurface(IN_MORPHOLOGY_IN
, srcRect
, NEED_COLOR_CHANNELS
);
1342 int32_t rx
= mRadii
.width
;
1343 int32_t ry
= mRadii
.height
;
1345 if (rx
== 0 && ry
== 0) {
1346 return input
.forget();
1349 return ApplyMorphology(srcRect
, input
, aRect
, rx
, ry
, mOperator
);
1352 void FilterNodeMorphologySoftware::RequestFromInputsForRect(
1353 const IntRect
& aRect
) {
1354 IntRect srcRect
= aRect
;
1355 srcRect
.Inflate(mRadii
);
1356 RequestInputRect(IN_MORPHOLOGY_IN
, srcRect
);
1359 IntRect
FilterNodeMorphologySoftware::GetOutputRectInRect(
1360 const IntRect
& aRect
) {
1361 IntRect inflatedSourceRect
= aRect
;
1362 inflatedSourceRect
.Inflate(mRadii
);
1363 IntRect inputRect
= GetInputRectInRect(IN_MORPHOLOGY_IN
, inflatedSourceRect
);
1364 if (mOperator
== MORPHOLOGY_OPERATOR_ERODE
) {
1365 inputRect
.Deflate(mRadii
);
1367 inputRect
.Inflate(mRadii
);
1369 return inputRect
.Intersect(aRect
);
1372 int32_t FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex
) {
1373 switch (aInputEnumIndex
) {
1374 case IN_COLOR_MATRIX_IN
:
1381 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex
,
1382 const Matrix5x4
& aMatrix
) {
1383 MOZ_ASSERT(aIndex
== ATT_COLOR_MATRIX_MATRIX
);
1388 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex
,
1389 uint32_t aAlphaMode
) {
1390 MOZ_ASSERT(aIndex
== ATT_COLOR_MATRIX_ALPHA_MODE
);
1391 mAlphaMode
= (AlphaMode
)aAlphaMode
;
1395 static already_AddRefed
<DataSourceSurface
> Premultiply(
1396 DataSourceSurface
* aSurface
) {
1397 if (aSurface
->GetFormat() == SurfaceFormat::A8
) {
1398 RefPtr
<DataSourceSurface
> surface(aSurface
);
1399 return surface
.forget();
1402 IntSize size
= aSurface
->GetSize();
1403 RefPtr
<DataSourceSurface
> target
=
1404 Factory::CreateDataSourceSurface(size
, SurfaceFormat::B8G8R8A8
);
1405 if (MOZ2D_WARN_IF(!target
)) {
1409 DataSourceSurface::ScopedMap
inputMap(aSurface
, DataSourceSurface::READ
);
1410 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
1411 if (MOZ2D_WARN_IF(!inputMap
.IsMapped() || !targetMap
.IsMapped())) {
1415 uint8_t* inputData
= inputMap
.GetData();
1416 int32_t inputStride
= inputMap
.GetStride();
1417 uint8_t* targetData
= targetMap
.GetData();
1418 int32_t targetStride
= targetMap
.GetStride();
1420 FilterProcessing::DoPremultiplicationCalculation(
1421 size
, targetData
, targetStride
, inputData
, inputStride
);
1423 return target
.forget();
1426 static already_AddRefed
<DataSourceSurface
> Unpremultiply(
1427 DataSourceSurface
* aSurface
) {
1428 if (aSurface
->GetFormat() == SurfaceFormat::A8
) {
1429 RefPtr
<DataSourceSurface
> surface(aSurface
);
1430 return surface
.forget();
1433 IntSize size
= aSurface
->GetSize();
1434 RefPtr
<DataSourceSurface
> target
=
1435 Factory::CreateDataSourceSurface(size
, SurfaceFormat::B8G8R8A8
);
1436 if (MOZ2D_WARN_IF(!target
)) {
1440 DataSourceSurface::ScopedMap
inputMap(aSurface
, DataSourceSurface::READ
);
1441 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
1442 if (MOZ2D_WARN_IF(!inputMap
.IsMapped() || !targetMap
.IsMapped())) {
1446 uint8_t* inputData
= inputMap
.GetData();
1447 int32_t inputStride
= inputMap
.GetStride();
1448 uint8_t* targetData
= targetMap
.GetData();
1449 int32_t targetStride
= targetMap
.GetStride();
1451 FilterProcessing::DoUnpremultiplicationCalculation(
1452 size
, targetData
, targetStride
, inputData
, inputStride
);
1454 return target
.forget();
1457 static already_AddRefed
<DataSourceSurface
> Opacity(DataSourceSurface
* aSurface
,
1459 if (aValue
== 1.0f
) {
1460 RefPtr
<DataSourceSurface
> surface(aSurface
);
1461 return surface
.forget();
1464 IntSize size
= aSurface
->GetSize();
1465 RefPtr
<DataSourceSurface
> target
=
1466 Factory::CreateDataSourceSurface(size
, aSurface
->GetFormat());
1467 if (MOZ2D_WARN_IF(!target
)) {
1471 DataSourceSurface::ScopedMap
inputMap(aSurface
, DataSourceSurface::READ
);
1472 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
1473 if (MOZ2D_WARN_IF(!inputMap
.IsMapped() || !targetMap
.IsMapped())) {
1477 uint8_t* inputData
= inputMap
.GetData();
1478 int32_t inputStride
= inputMap
.GetStride();
1479 uint8_t* targetData
= targetMap
.GetData();
1480 int32_t targetStride
= targetMap
.GetStride();
1482 if (aSurface
->GetFormat() == SurfaceFormat::A8
) {
1483 FilterProcessing::DoOpacityCalculationA8(size
, targetData
, targetStride
,
1484 inputData
, inputStride
, aValue
);
1486 MOZ_ASSERT(aSurface
->GetFormat() == SurfaceFormat::B8G8R8A8
);
1487 FilterProcessing::DoOpacityCalculation(size
, targetData
, targetStride
,
1488 inputData
, inputStride
, aValue
);
1491 return target
.forget();
1494 already_AddRefed
<DataSourceSurface
> FilterNodeColorMatrixSoftware::Render(
1495 const IntRect
& aRect
) {
1496 RefPtr
<DataSourceSurface
> input
=
1497 GetInputDataSourceSurface(IN_COLOR_MATRIX_IN
, aRect
, NEED_COLOR_CHANNELS
);
1502 if (mAlphaMode
== ALPHA_MODE_PREMULTIPLIED
) {
1503 input
= Unpremultiply(input
);
1506 RefPtr
<DataSourceSurface
> result
=
1507 FilterProcessing::ApplyColorMatrix(input
, mMatrix
);
1509 if (mAlphaMode
== ALPHA_MODE_PREMULTIPLIED
) {
1510 result
= Premultiply(result
);
1513 return result
.forget();
1516 void FilterNodeColorMatrixSoftware::RequestFromInputsForRect(
1517 const IntRect
& aRect
) {
1518 RequestInputRect(IN_COLOR_MATRIX_IN
, aRect
);
1521 IntRect
FilterNodeColorMatrixSoftware::MapRectToSource(
1522 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
1523 return MapInputRectToSource(IN_COLOR_MATRIX_IN
, aRect
, aMax
, aSourceNode
);
1526 IntRect
FilterNodeColorMatrixSoftware::GetOutputRectInRect(
1527 const IntRect
& aRect
) {
1528 if (mMatrix
._54
> 0.0f
) {
1531 return GetInputRectInRect(IN_COLOR_MATRIX_IN
, aRect
);
1534 void FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex
,
1535 const DeviceColor
& aColor
) {
1536 MOZ_ASSERT(aIndex
== ATT_FLOOD_COLOR
);
1541 static uint32_t ColorToBGRA(const DeviceColor
& aColor
) {
1544 uint8_t components
[4];
1546 components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] =
1547 NS_lround(aColor
.r
* aColor
.a
* 255.0f
);
1548 components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] =
1549 NS_lround(aColor
.g
* aColor
.a
* 255.0f
);
1550 components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] =
1551 NS_lround(aColor
.b
* aColor
.a
* 255.0f
);
1552 components
[B8G8R8A8_COMPONENT_BYTEOFFSET_A
] = NS_lround(aColor
.a
* 255.0f
);
1556 static SurfaceFormat
FormatForColor(DeviceColor aColor
) {
1557 if (aColor
.r
== 0 && aColor
.g
== 0 && aColor
.b
== 0) {
1558 return SurfaceFormat::A8
;
1560 return SurfaceFormat::B8G8R8A8
;
1563 already_AddRefed
<DataSourceSurface
> FilterNodeFloodSoftware::Render(
1564 const IntRect
& aRect
) {
1565 SurfaceFormat format
= FormatForColor(mColor
);
1566 RefPtr
<DataSourceSurface
> target
=
1567 Factory::CreateDataSourceSurface(aRect
.Size(), format
);
1568 if (MOZ2D_WARN_IF(!target
)) {
1572 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
1573 if (MOZ2D_WARN_IF(!targetMap
.IsMapped())) {
1577 uint8_t* targetData
= targetMap
.GetData();
1578 int32_t stride
= targetMap
.GetStride();
1580 if (format
== SurfaceFormat::B8G8R8A8
) {
1581 uint32_t color
= ColorToBGRA(mColor
);
1582 for (int32_t y
= 0; y
< aRect
.Height(); y
++) {
1583 for (int32_t x
= 0; x
< aRect
.Width(); x
++) {
1584 *((uint32_t*)targetData
+ x
) = color
;
1586 PodZero(&targetData
[aRect
.Width() * 4], stride
- aRect
.Width() * 4);
1587 targetData
+= stride
;
1589 } else if (format
== SurfaceFormat::A8
) {
1590 uint8_t alpha
= NS_lround(mColor
.a
* 255.0f
);
1591 for (int32_t y
= 0; y
< aRect
.Height(); y
++) {
1592 for (int32_t x
= 0; x
< aRect
.Width(); x
++) {
1593 targetData
[x
] = alpha
;
1595 PodZero(&targetData
[aRect
.Width()], stride
- aRect
.Width());
1596 targetData
+= stride
;
1599 gfxDevCrash(LogReason::FilterInputFormat
)
1600 << "Bad format in flood render " << (int)format
;
1604 return target
.forget();
1607 // Override GetOutput to get around caching. Rendering simple floods is
1608 // comparatively fast.
1609 already_AddRefed
<DataSourceSurface
> FilterNodeFloodSoftware::GetOutput(
1610 const IntRect
& aRect
) {
1611 return Render(aRect
);
1614 IntRect
FilterNodeFloodSoftware::MapRectToSource(const IntRect
& aRect
,
1615 const IntRect
& aMax
,
1616 FilterNode
* aSourceNode
) {
1620 IntRect
FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
1621 if (mColor
.a
== 0.0f
) {
1627 int32_t FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex
) {
1628 switch (aInputEnumIndex
) {
1636 void FilterNodeTileSoftware::SetAttribute(uint32_t aIndex
,
1637 const IntRect
& aSourceRect
) {
1638 MOZ_ASSERT(aIndex
== ATT_TILE_SOURCE_RECT
);
1639 mSourceRect
.SetRect(int32_t(aSourceRect
.X()), int32_t(aSourceRect
.Y()),
1640 int32_t(aSourceRect
.Width()),
1641 int32_t(aSourceRect
.Height()));
1646 struct CompareIntRects
{
1647 bool operator()(const IntRect
& a
, const IntRect
& b
) const {
1648 if (a
.X() != b
.X()) {
1649 return a
.X() < b
.X();
1651 if (a
.Y() != b
.Y()) {
1652 return a
.Y() < b
.Y();
1654 if (a
.Width() != b
.Width()) {
1655 return a
.Width() < b
.Width();
1657 return a
.Height() < b
.Height();
1663 already_AddRefed
<DataSourceSurface
> FilterNodeTileSoftware::Render(
1664 const IntRect
& aRect
) {
1665 if (mSourceRect
.IsEmpty()) {
1669 if (mSourceRect
.Contains(aRect
)) {
1670 return GetInputDataSourceSurface(IN_TILE_IN
, aRect
);
1673 RefPtr
<DataSourceSurface
> target
;
1675 typedef std::map
<IntRect
, RefPtr
<DataSourceSurface
>, CompareIntRects
>
1679 IntPoint startIndex
= TileIndex(mSourceRect
, aRect
.TopLeft());
1680 IntPoint endIndex
= TileIndex(mSourceRect
, aRect
.BottomRight());
1681 for (int32_t ix
= startIndex
.x
; ix
<= endIndex
.x
; ix
++) {
1682 for (int32_t iy
= startIndex
.y
; iy
<= endIndex
.y
; iy
++) {
1683 IntPoint
sourceToDestOffset(ix
* mSourceRect
.Width(),
1684 iy
* mSourceRect
.Height());
1685 IntRect destRect
= aRect
.Intersect(mSourceRect
+ sourceToDestOffset
);
1686 IntRect srcRect
= destRect
- sourceToDestOffset
;
1687 if (srcRect
.IsEmpty()) {
1691 RefPtr
<DataSourceSurface
> input
;
1692 InputMap::iterator it
= inputs
.find(srcRect
);
1693 if (it
== inputs
.end()) {
1694 input
= GetInputDataSourceSurface(IN_TILE_IN
, srcRect
);
1695 inputs
[srcRect
] = input
;
1703 // We delay creating the target until now because we want to use the
1704 // same format as our input filter, and we do not actually know the
1705 // input format before we call GetInputDataSourceSurface.
1707 Factory::CreateDataSourceSurface(aRect
.Size(), input
->GetFormat());
1708 if (MOZ2D_WARN_IF(!target
)) {
1713 if (input
->GetFormat() != target
->GetFormat()) {
1714 // Different rectangles of the input can have different formats. If
1715 // that happens, just convert everything to B8G8R8A8.
1716 target
= FilterProcessing::ConvertToB8G8R8A8(target
);
1717 input
= FilterProcessing::ConvertToB8G8R8A8(input
);
1718 if (MOZ2D_WARN_IF(!target
) || MOZ2D_WARN_IF(!input
)) {
1723 CopyRect(input
, target
, srcRect
- srcRect
.TopLeft(),
1724 destRect
.TopLeft() - aRect
.TopLeft());
1728 return target
.forget();
1731 void FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect
& aRect
) {
1732 // Do not request anything.
1733 // Source rects for the tile filter can be discontinuous with large gaps
1734 // between them. Requesting those from our input filter might cause it to
1735 // render the whole bounding box of all of them, which would be wasteful.
1738 IntRect
FilterNodeTileSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
1742 FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware()
1743 : mDisableR(true), mDisableG(true), mDisableB(true), mDisableA(true) {}
1745 void FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex
,
1748 case ATT_TRANSFER_DISABLE_R
:
1749 mDisableR
= aDisable
;
1751 case ATT_TRANSFER_DISABLE_G
:
1752 mDisableG
= aDisable
;
1754 case ATT_TRANSFER_DISABLE_B
:
1755 mDisableB
= aDisable
;
1757 case ATT_TRANSFER_DISABLE_A
:
1758 mDisableA
= aDisable
;
1761 MOZ_CRASH("GFX: FilterNodeComponentTransferSoftware::SetAttribute");
1766 void FilterNodeComponentTransferSoftware::GenerateLookupTable(
1767 ptrdiff_t aComponent
, uint8_t aTables
[4][256], bool aDisabled
) {
1769 for (int32_t i
= 0; i
< 256; ++i
) {
1770 aTables
[aComponent
][i
] = i
;
1773 FillLookupTable(aComponent
, aTables
[aComponent
]);
1777 template <uint32_t BytesPerPixel
>
1778 static void TransferComponents(
1779 DataSourceSurface
* aInput
, DataSourceSurface
* aTarget
,
1780 const uint8_t aLookupTables
[BytesPerPixel
][256]) {
1781 MOZ_ASSERT(aInput
->GetFormat() == aTarget
->GetFormat(), "different formats");
1782 IntSize size
= aInput
->GetSize();
1784 DataSourceSurface::ScopedMap
sourceMap(aInput
, DataSourceSurface::READ
);
1785 DataSourceSurface::ScopedMap
targetMap(aTarget
, DataSourceSurface::WRITE
);
1786 if (MOZ2D_WARN_IF(!sourceMap
.IsMapped() || !targetMap
.IsMapped())) {
1790 uint8_t* sourceData
= sourceMap
.GetData();
1791 int32_t sourceStride
= sourceMap
.GetStride();
1792 uint8_t* targetData
= targetMap
.GetData();
1793 int32_t targetStride
= targetMap
.GetStride();
1795 MOZ_ASSERT(sourceStride
<= targetStride
, "target smaller than source");
1797 for (int32_t y
= 0; y
< size
.height
; y
++) {
1798 for (int32_t x
= 0; x
< size
.width
; x
++) {
1799 uint32_t sourceIndex
= y
* sourceStride
+ x
* BytesPerPixel
;
1800 uint32_t targetIndex
= y
* targetStride
+ x
* BytesPerPixel
;
1801 for (uint32_t i
= 0; i
< BytesPerPixel
; i
++) {
1802 targetData
[targetIndex
+ i
] =
1803 aLookupTables
[i
][sourceData
[sourceIndex
+ i
]];
1807 // Zero padding to keep valgrind happy.
1808 PodZero(&targetData
[y
* targetStride
+ size
.width
* BytesPerPixel
],
1809 targetStride
- size
.width
* BytesPerPixel
);
1813 static bool IsAllZero(const uint8_t aLookupTable
[256]) {
1814 for (int32_t i
= 0; i
< 256; i
++) {
1815 if (aLookupTable
[i
] != 0) {
1822 already_AddRefed
<DataSourceSurface
> FilterNodeComponentTransferSoftware::Render(
1823 const IntRect
& aRect
) {
1824 if (mDisableR
&& mDisableG
&& mDisableB
&& mDisableA
) {
1825 return GetInputDataSourceSurface(IN_TRANSFER_IN
, aRect
);
1828 uint8_t lookupTables
[4][256];
1829 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R
, lookupTables
, mDisableR
);
1830 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G
, lookupTables
, mDisableG
);
1831 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B
, lookupTables
, mDisableB
);
1832 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A
, lookupTables
, mDisableA
);
1834 bool needColorChannels
=
1835 lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
][0] != 0 ||
1836 lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
][0] != 0 ||
1837 lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
][0] != 0;
1839 FormatHint pref
= needColorChannels
? NEED_COLOR_CHANNELS
: CAN_HANDLE_A8
;
1841 RefPtr
<DataSourceSurface
> input
=
1842 GetInputDataSourceSurface(IN_TRANSFER_IN
, aRect
, pref
);
1847 if (input
->GetFormat() == SurfaceFormat::B8G8R8A8
&& !needColorChannels
) {
1848 bool colorChannelsBecomeBlack
=
1849 IsAllZero(lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
]) &&
1850 IsAllZero(lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
]) &&
1851 IsAllZero(lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
]);
1853 if (colorChannelsBecomeBlack
) {
1854 input
= FilterProcessing::ExtractAlpha(input
);
1858 SurfaceFormat format
= input
->GetFormat();
1859 if (format
== SurfaceFormat::A8
&& mDisableA
) {
1860 return input
.forget();
1863 RefPtr
<DataSourceSurface
> target
=
1864 Factory::CreateDataSourceSurface(aRect
.Size(), format
);
1865 if (MOZ2D_WARN_IF(!target
)) {
1869 if (format
== SurfaceFormat::A8
) {
1870 TransferComponents
<1>(input
, target
,
1871 &lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_A
]);
1873 TransferComponents
<4>(input
, target
, lookupTables
);
1876 return target
.forget();
1879 void FilterNodeComponentTransferSoftware::RequestFromInputsForRect(
1880 const IntRect
& aRect
) {
1881 RequestInputRect(IN_TRANSFER_IN
, aRect
);
1884 IntRect
FilterNodeComponentTransferSoftware::MapRectToSource(
1885 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
1886 return MapInputRectToSource(IN_TRANSFER_IN
, aRect
, aMax
, aSourceNode
);
1889 IntRect
FilterNodeComponentTransferSoftware::GetOutputRectInRect(
1890 const IntRect
& aRect
) {
1892 return GetInputRectInRect(IN_TRANSFER_IN
, aRect
);
1897 int32_t FilterNodeComponentTransferSoftware::InputIndex(
1898 uint32_t aInputEnumIndex
) {
1899 switch (aInputEnumIndex
) {
1900 case IN_TRANSFER_IN
:
1907 void FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex
,
1908 const Float
* aFloat
,
1910 std::vector
<Float
> table(aFloat
, aFloat
+ aSize
);
1912 case ATT_TABLE_TRANSFER_TABLE_R
:
1915 case ATT_TABLE_TRANSFER_TABLE_G
:
1918 case ATT_TABLE_TRANSFER_TABLE_B
:
1921 case ATT_TABLE_TRANSFER_TABLE_A
:
1925 MOZ_CRASH("GFX: FilterNodeTableTransferSoftware::SetAttribute");
1930 void FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent
,
1931 uint8_t aTable
[256]) {
1932 switch (aComponent
) {
1933 case B8G8R8A8_COMPONENT_BYTEOFFSET_R
:
1934 FillLookupTableImpl(mTableR
, aTable
);
1936 case B8G8R8A8_COMPONENT_BYTEOFFSET_G
:
1937 FillLookupTableImpl(mTableG
, aTable
);
1939 case B8G8R8A8_COMPONENT_BYTEOFFSET_B
:
1940 FillLookupTableImpl(mTableB
, aTable
);
1942 case B8G8R8A8_COMPONENT_BYTEOFFSET_A
:
1943 FillLookupTableImpl(mTableA
, aTable
);
1946 MOZ_ASSERT(false, "unknown component");
1951 void FilterNodeTableTransferSoftware::FillLookupTableImpl(
1952 std::vector
<Float
>& aTableValues
, uint8_t aTable
[256]) {
1953 uint32_t tvLength
= aTableValues
.size();
1958 for (size_t i
= 0; i
< 256; i
++) {
1959 uint32_t k
= (i
* (tvLength
- 1)) / 255;
1960 Float v1
= aTableValues
[k
];
1961 Float v2
= aTableValues
[std::min(k
+ 1, tvLength
- 1)];
1962 int32_t val
= int32_t(255 * (v1
+ (i
/ 255.0f
- k
/ float(tvLength
- 1)) *
1963 (tvLength
- 1) * (v2
- v1
)));
1964 val
= std::min(255, val
);
1965 val
= std::max(0, val
);
1970 void FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex
,
1971 const Float
* aFloat
,
1973 std::vector
<Float
> discrete(aFloat
, aFloat
+ aSize
);
1975 case ATT_DISCRETE_TRANSFER_TABLE_R
:
1978 case ATT_DISCRETE_TRANSFER_TABLE_G
:
1981 case ATT_DISCRETE_TRANSFER_TABLE_B
:
1984 case ATT_DISCRETE_TRANSFER_TABLE_A
:
1988 MOZ_CRASH("GFX: FilterNodeDiscreteTransferSoftware::SetAttribute");
1993 void FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent
,
1994 uint8_t aTable
[256]) {
1995 switch (aComponent
) {
1996 case B8G8R8A8_COMPONENT_BYTEOFFSET_R
:
1997 FillLookupTableImpl(mTableR
, aTable
);
1999 case B8G8R8A8_COMPONENT_BYTEOFFSET_G
:
2000 FillLookupTableImpl(mTableG
, aTable
);
2002 case B8G8R8A8_COMPONENT_BYTEOFFSET_B
:
2003 FillLookupTableImpl(mTableB
, aTable
);
2005 case B8G8R8A8_COMPONENT_BYTEOFFSET_A
:
2006 FillLookupTableImpl(mTableA
, aTable
);
2009 MOZ_ASSERT(false, "unknown component");
2014 void FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(
2015 std::vector
<Float
>& aTableValues
, uint8_t aTable
[256]) {
2016 uint32_t tvLength
= aTableValues
.size();
2021 for (size_t i
= 0; i
< 256; i
++) {
2022 uint32_t k
= (i
* tvLength
) / 255;
2023 k
= std::min(k
, tvLength
- 1);
2024 Float v
= aTableValues
[k
];
2025 int32_t val
= NS_lround(255 * v
);
2026 val
= std::min(255, val
);
2027 val
= std::max(0, val
);
2032 FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware()
2042 void FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex
,
2045 case ATT_LINEAR_TRANSFER_SLOPE_R
:
2048 case ATT_LINEAR_TRANSFER_INTERCEPT_R
:
2049 mInterceptR
= aValue
;
2051 case ATT_LINEAR_TRANSFER_SLOPE_G
:
2054 case ATT_LINEAR_TRANSFER_INTERCEPT_G
:
2055 mInterceptG
= aValue
;
2057 case ATT_LINEAR_TRANSFER_SLOPE_B
:
2060 case ATT_LINEAR_TRANSFER_INTERCEPT_B
:
2061 mInterceptB
= aValue
;
2063 case ATT_LINEAR_TRANSFER_SLOPE_A
:
2066 case ATT_LINEAR_TRANSFER_INTERCEPT_A
:
2067 mInterceptA
= aValue
;
2070 MOZ_CRASH("GFX: FilterNodeLinearTransferSoftware::SetAttribute");
2075 void FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent
,
2076 uint8_t aTable
[256]) {
2077 switch (aComponent
) {
2078 case B8G8R8A8_COMPONENT_BYTEOFFSET_R
:
2079 FillLookupTableImpl(mSlopeR
, mInterceptR
, aTable
);
2081 case B8G8R8A8_COMPONENT_BYTEOFFSET_G
:
2082 FillLookupTableImpl(mSlopeG
, mInterceptG
, aTable
);
2084 case B8G8R8A8_COMPONENT_BYTEOFFSET_B
:
2085 FillLookupTableImpl(mSlopeB
, mInterceptB
, aTable
);
2087 case B8G8R8A8_COMPONENT_BYTEOFFSET_A
:
2088 FillLookupTableImpl(mSlopeA
, mInterceptA
, aTable
);
2091 MOZ_ASSERT(false, "unknown component");
2096 void FilterNodeLinearTransferSoftware::FillLookupTableImpl(
2097 Float aSlope
, Float aIntercept
, uint8_t aTable
[256]) {
2098 for (size_t i
= 0; i
< 256; i
++) {
2099 int32_t val
= NS_lround(aSlope
* i
+ 255 * aIntercept
);
2100 val
= std::min(255, val
);
2101 val
= std::max(0, val
);
2106 FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware()
2120 void FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex
,
2123 case ATT_GAMMA_TRANSFER_AMPLITUDE_R
:
2124 mAmplitudeR
= aValue
;
2126 case ATT_GAMMA_TRANSFER_EXPONENT_R
:
2127 mExponentR
= aValue
;
2129 case ATT_GAMMA_TRANSFER_OFFSET_R
:
2132 case ATT_GAMMA_TRANSFER_AMPLITUDE_G
:
2133 mAmplitudeG
= aValue
;
2135 case ATT_GAMMA_TRANSFER_EXPONENT_G
:
2136 mExponentG
= aValue
;
2138 case ATT_GAMMA_TRANSFER_OFFSET_G
:
2141 case ATT_GAMMA_TRANSFER_AMPLITUDE_B
:
2142 mAmplitudeB
= aValue
;
2144 case ATT_GAMMA_TRANSFER_EXPONENT_B
:
2145 mExponentB
= aValue
;
2147 case ATT_GAMMA_TRANSFER_OFFSET_B
:
2150 case ATT_GAMMA_TRANSFER_AMPLITUDE_A
:
2151 mAmplitudeA
= aValue
;
2153 case ATT_GAMMA_TRANSFER_EXPONENT_A
:
2154 mExponentA
= aValue
;
2156 case ATT_GAMMA_TRANSFER_OFFSET_A
:
2160 MOZ_CRASH("GFX: FilterNodeGammaTransferSoftware::SetAttribute");
2165 void FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent
,
2166 uint8_t aTable
[256]) {
2167 switch (aComponent
) {
2168 case B8G8R8A8_COMPONENT_BYTEOFFSET_R
:
2169 FillLookupTableImpl(mAmplitudeR
, mExponentR
, mOffsetR
, aTable
);
2171 case B8G8R8A8_COMPONENT_BYTEOFFSET_G
:
2172 FillLookupTableImpl(mAmplitudeG
, mExponentG
, mOffsetG
, aTable
);
2174 case B8G8R8A8_COMPONENT_BYTEOFFSET_B
:
2175 FillLookupTableImpl(mAmplitudeB
, mExponentB
, mOffsetB
, aTable
);
2177 case B8G8R8A8_COMPONENT_BYTEOFFSET_A
:
2178 FillLookupTableImpl(mAmplitudeA
, mExponentA
, mOffsetA
, aTable
);
2181 MOZ_ASSERT(false, "unknown component");
2186 void FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude
,
2189 uint8_t aTable
[256]) {
2190 for (size_t i
= 0; i
< 256; i
++) {
2192 NS_lround(255 * (aAmplitude
* pow(i
/ 255.0f
, aExponent
) + aOffset
));
2193 val
= std::min(255, val
);
2194 val
= std::max(0, val
);
2199 FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware()
2202 mEdgeMode(EDGE_MODE_DUPLICATE
),
2203 mPreserveAlpha(false) {}
2205 int32_t FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex
) {
2206 switch (aInputEnumIndex
) {
2207 case IN_CONVOLVE_MATRIX_IN
:
2214 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2215 uint32_t aIndex
, const IntSize
& aKernelSize
) {
2216 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_KERNEL_SIZE
);
2217 mKernelSize
= aKernelSize
;
2221 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex
,
2222 const Float
* aMatrix
,
2224 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_KERNEL_MATRIX
);
2225 mKernelMatrix
= std::vector
<Float
>(aMatrix
, aMatrix
+ aSize
);
2229 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex
,
2232 case ATT_CONVOLVE_MATRIX_DIVISOR
:
2235 case ATT_CONVOLVE_MATRIX_BIAS
:
2239 MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
2244 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2245 uint32_t aIndex
, const Size
& aKernelUnitLength
) {
2247 case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH
:
2248 mKernelUnitLength
= aKernelUnitLength
;
2251 MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
2256 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex
,
2257 const IntPoint
& aTarget
) {
2258 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_TARGET
);
2263 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2264 uint32_t aIndex
, const IntRect
& aSourceRect
) {
2265 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_SOURCE_RECT
);
2266 mSourceRect
= aSourceRect
;
2270 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex
,
2271 uint32_t aEdgeMode
) {
2272 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_EDGE_MODE
);
2273 mEdgeMode
= static_cast<ConvolveMatrixEdgeMode
>(aEdgeMode
);
2277 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex
,
2278 bool aPreserveAlpha
) {
2279 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA
);
2280 mPreserveAlpha
= aPreserveAlpha
;
2285 static inline void DebugOnlyCheckColorSamplingAccess(
2286 const uint8_t* aSampleAddress
, const uint8_t* aBoundsBegin
,
2287 const uint8_t* aBoundsEnd
) {
2288 MOZ_ASSERT(aSampleAddress
>= aBoundsBegin
, "accessing before start");
2289 MOZ_ASSERT(aSampleAddress
< aBoundsEnd
, "accessing after end");
2292 # define DebugOnlyCheckColorSamplingAccess(address, boundsBegin, boundsEnd)
2295 static inline uint8_t ColorComponentAtPoint(const uint8_t* aData
,
2297 const uint8_t* aBoundsBegin
,
2298 const uint8_t* aBoundsEnd
,
2299 int32_t x
, int32_t y
, ptrdiff_t bpp
,
2301 DebugOnlyCheckColorSamplingAccess(&aData
[y
* aStride
+ bpp
* x
+ c
],
2302 aBoundsBegin
, aBoundsEnd
);
2303 return aData
[y
* aStride
+ bpp
* x
+ c
];
2306 static inline int32_t ColorAtPoint(const uint8_t* aData
, ptrdiff_t aStride
,
2307 const uint8_t* aBoundsBegin
,
2308 const uint8_t* aBoundsEnd
, int32_t x
,
2310 DebugOnlyCheckColorSamplingAccess(aData
+ y
* aStride
+ 4 * x
, aBoundsBegin
,
2312 return *(uint32_t*)(aData
+ y
* aStride
+ 4 * x
);
2315 // Accepts fractional x & y and does bilinear interpolation.
2316 // Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible.
2317 static inline uint8_t ColorComponentAtPoint(
2318 const uint8_t* aData
, ptrdiff_t aStride
, const uint8_t* aBoundsBegin
,
2319 const uint8_t* aBoundsEnd
, Float x
, Float y
, ptrdiff_t bpp
, ptrdiff_t c
) {
2320 const uint32_t f
= 256;
2321 const int32_t lx
= floor(x
);
2322 const int32_t ly
= floor(y
);
2323 const int32_t tux
= uint32_t((x
- lx
) * f
);
2324 const int32_t tlx
= f
- tux
;
2325 const int32_t tuy
= uint32_t((y
- ly
) * f
);
2326 const int32_t tly
= f
- tuy
;
2327 const uint8_t& cll
= ColorComponentAtPoint(aData
, aStride
, aBoundsBegin
,
2328 aBoundsEnd
, lx
, ly
, bpp
, c
);
2329 const uint8_t& cul
= ColorComponentAtPoint(aData
, aStride
, aBoundsBegin
,
2330 aBoundsEnd
, lx
+ 1, ly
, bpp
, c
);
2331 const uint8_t& clu
= ColorComponentAtPoint(aData
, aStride
, aBoundsBegin
,
2332 aBoundsEnd
, lx
, ly
+ 1, bpp
, c
);
2333 const uint8_t& cuu
= ColorComponentAtPoint(
2334 aData
, aStride
, aBoundsBegin
, aBoundsEnd
, lx
+ 1, ly
+ 1, bpp
, c
);
2335 return ((cll
* tlx
+ cul
* tux
) * tly
+ (clu
* tlx
+ cuu
* tux
) * tuy
+
2340 static int32_t ClampToNonZero(int32_t a
) { return a
* (a
>= 0); }
2342 template <typename CoordType
>
2343 static void ConvolvePixel(const uint8_t* aSourceData
, uint8_t* aTargetData
,
2344 int32_t aWidth
, int32_t aHeight
,
2345 int32_t aSourceStride
, int32_t aTargetStride
,
2346 const uint8_t* aSourceBegin
,
2347 const uint8_t* aSourceEnd
, int32_t aX
, int32_t aY
,
2348 const int32_t* aKernel
, int32_t aBias
, int32_t shiftL
,
2349 int32_t shiftR
, bool aPreserveAlpha
, int32_t aOrderX
,
2350 int32_t aOrderY
, int32_t aTargetX
, int32_t aTargetY
,
2351 CoordType aKernelUnitLengthX
,
2352 CoordType aKernelUnitLengthY
) {
2353 int32_t sum
[4] = {0, 0, 0, 0};
2354 int32_t offsets
[4] = {
2355 B8G8R8A8_COMPONENT_BYTEOFFSET_R
, B8G8R8A8_COMPONENT_BYTEOFFSET_G
,
2356 B8G8R8A8_COMPONENT_BYTEOFFSET_B
, B8G8R8A8_COMPONENT_BYTEOFFSET_A
};
2357 int32_t channels
= aPreserveAlpha
? 3 : 4;
2358 int32_t roundingAddition
= shiftL
== 0 ? 0 : 1 << (shiftL
- 1);
2360 for (int32_t y
= 0; y
< aOrderY
; y
++) {
2361 CoordType sampleY
= aY
+ (y
- aTargetY
) * aKernelUnitLengthY
;
2362 for (int32_t x
= 0; x
< aOrderX
; x
++) {
2363 CoordType sampleX
= aX
+ (x
- aTargetX
) * aKernelUnitLengthX
;
2364 for (int32_t i
= 0; i
< channels
; i
++) {
2366 aKernel
[aOrderX
* y
+ x
] *
2367 ColorComponentAtPoint(aSourceData
, aSourceStride
, aSourceBegin
,
2368 aSourceEnd
, sampleX
, sampleY
, 4, offsets
[i
]);
2372 for (int32_t i
= 0; i
< channels
; i
++) {
2374 umin(ClampToNonZero(sum
[i
] + aBias
), 255 << shiftL
>> shiftR
);
2375 aTargetData
[aY
* aTargetStride
+ 4 * aX
+ offsets
[i
]] =
2376 (clamped
+ roundingAddition
) << shiftR
>> shiftL
;
2378 if (aPreserveAlpha
) {
2379 aTargetData
[aY
* aTargetStride
+ 4 * aX
+ B8G8R8A8_COMPONENT_BYTEOFFSET_A
] =
2380 aSourceData
[aY
* aSourceStride
+ 4 * aX
+
2381 B8G8R8A8_COMPONENT_BYTEOFFSET_A
];
2385 already_AddRefed
<DataSourceSurface
> FilterNodeConvolveMatrixSoftware::Render(
2386 const IntRect
& aRect
) {
2387 if (mKernelUnitLength
.width
== floor(mKernelUnitLength
.width
) &&
2388 mKernelUnitLength
.height
== floor(mKernelUnitLength
.height
)) {
2389 return DoRender(aRect
, (int32_t)mKernelUnitLength
.width
,
2390 (int32_t)mKernelUnitLength
.height
);
2392 return DoRender(aRect
, mKernelUnitLength
.width
, mKernelUnitLength
.height
);
2395 static std::vector
<Float
> ReversedVector(const std::vector
<Float
>& aVector
) {
2396 size_t length
= aVector
.size();
2397 std::vector
<Float
> result(length
, 0);
2398 for (size_t i
= 0; i
< length
; i
++) {
2399 result
[length
- 1 - i
] = aVector
[i
];
2404 static std::vector
<Float
> ScaledVector(const std::vector
<Float
>& aVector
,
2406 size_t length
= aVector
.size();
2407 std::vector
<Float
> result(length
, 0);
2408 for (size_t i
= 0; i
< length
; i
++) {
2409 result
[i
] = aVector
[i
] / aDivisor
;
2414 static Float
MaxVectorSum(const std::vector
<Float
>& aVector
) {
2416 size_t length
= aVector
.size();
2417 for (size_t i
= 0; i
< length
; i
++) {
2418 if (aVector
[i
] > 0) {
2425 // Returns shiftL and shiftR in such a way that
2426 // a << shiftL >> shiftR is roughly a * aFloat.
2427 static void TranslateDoubleToShifts(double aDouble
, int32_t& aShiftL
,
2432 MOZ_CRASH("GFX: TranslateDoubleToShifts");
2435 while (1 << (aShiftR
+ 1) < 1 / aDouble
) {
2439 while (1 << (aShiftL
+ 1) < aDouble
) {
2445 template <typename CoordType
>
2446 already_AddRefed
<DataSourceSurface
> FilterNodeConvolveMatrixSoftware::DoRender(
2447 const IntRect
& aRect
, CoordType aKernelUnitLengthX
,
2448 CoordType aKernelUnitLengthY
) {
2449 if (mKernelSize
.width
<= 0 || mKernelSize
.height
<= 0 ||
2450 mKernelMatrix
.size() !=
2451 uint32_t(mKernelSize
.width
* mKernelSize
.height
) ||
2452 !IntRect(IntPoint(0, 0), mKernelSize
).Contains(mTarget
) ||
2454 return Factory::CreateDataSourceSurface(aRect
.Size(),
2455 SurfaceFormat::B8G8R8A8
, true);
2458 IntRect srcRect
= InflatedSourceRect(aRect
);
2460 // Inflate the source rect by another pixel because the bilinear filtering in
2461 // ColorComponentAtPoint may want to access the margins.
2464 RefPtr
<DataSourceSurface
> input
=
2465 GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN
, srcRect
,
2466 NEED_COLOR_CHANNELS
, mEdgeMode
, &mSourceRect
);
2472 RefPtr
<DataSourceSurface
> target
= Factory::CreateDataSourceSurface(
2473 aRect
.Size(), SurfaceFormat::B8G8R8A8
, true);
2474 if (MOZ2D_WARN_IF(!target
)) {
2478 IntPoint offset
= aRect
.TopLeft() - srcRect
.TopLeft();
2480 DataSourceSurface::ScopedMap
sourceMap(input
, DataSourceSurface::READ
);
2481 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
2482 if (MOZ2D_WARN_IF(!sourceMap
.IsMapped() || !targetMap
.IsMapped())) {
2486 uint8_t* sourceData
=
2487 DataAtOffset(input
, sourceMap
.GetMappedSurface(), offset
);
2488 int32_t sourceStride
= sourceMap
.GetStride();
2489 uint8_t* sourceBegin
= sourceMap
.GetData();
2490 uint8_t* sourceEnd
= sourceBegin
+ sourceStride
* input
->GetSize().height
;
2491 uint8_t* targetData
= targetMap
.GetData();
2492 int32_t targetStride
= targetMap
.GetStride();
2494 // Why exactly are we reversing the kernel?
2495 std::vector
<Float
> kernel
= ReversedVector(mKernelMatrix
);
2496 kernel
= ScaledVector(kernel
, mDivisor
);
2497 Float maxResultAbs
= std::max(MaxVectorSum(kernel
) + mBias
,
2498 MaxVectorSum(ScaledVector(kernel
, -1)) - mBias
);
2499 maxResultAbs
= std::max(maxResultAbs
, 1.0f
);
2501 double idealFactor
= INT32_MAX
/ 2.0 / maxResultAbs
/ 255.0 * 0.999;
2502 MOZ_ASSERT(255.0 * maxResultAbs
* idealFactor
<= INT32_MAX
/ 2.0,
2503 "badly chosen float-to-int scale");
2504 int32_t shiftL
, shiftR
;
2505 TranslateDoubleToShifts(idealFactor
, shiftL
, shiftR
);
2506 double factorFromShifts
= Float(1 << shiftL
) / Float(1 << shiftR
);
2507 MOZ_ASSERT(255.0 * maxResultAbs
* factorFromShifts
<= INT32_MAX
/ 2.0,
2508 "badly chosen float-to-int scale");
2510 int32_t* intKernel
= new int32_t[kernel
.size()];
2511 for (size_t i
= 0; i
< kernel
.size(); i
++) {
2512 intKernel
[i
] = NS_lround(kernel
[i
] * factorFromShifts
);
2514 int32_t bias
= NS_lround(mBias
* 255 * factorFromShifts
);
2516 for (int32_t y
= 0; y
< aRect
.Height(); y
++) {
2517 for (int32_t x
= 0; x
< aRect
.Width(); x
++) {
2518 ConvolvePixel(sourceData
, targetData
, aRect
.Width(), aRect
.Height(),
2519 sourceStride
, targetStride
, sourceBegin
, sourceEnd
, x
, y
,
2520 intKernel
, bias
, shiftL
, shiftR
, mPreserveAlpha
,
2521 mKernelSize
.width
, mKernelSize
.height
, mTarget
.x
, mTarget
.y
,
2522 aKernelUnitLengthX
, aKernelUnitLengthY
);
2527 return target
.forget();
2530 void FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(
2531 const IntRect
& aRect
) {
2532 RequestInputRect(IN_CONVOLVE_MATRIX_IN
, InflatedSourceRect(aRect
));
2535 IntRect
FilterNodeConvolveMatrixSoftware::MapRectToSource(
2536 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
2537 return MapInputRectToSource(IN_CONVOLVE_MATRIX_IN
, InflatedSourceRect(aRect
),
2541 IntRect
FilterNodeConvolveMatrixSoftware::InflatedSourceRect(
2542 const IntRect
& aDestRect
) {
2543 if (aDestRect
.IsEmpty()) {
2548 margin
.left
= static_cast<int32_t>(ceil(mTarget
.x
* mKernelUnitLength
.width
));
2549 margin
.top
= static_cast<int32_t>(ceil(mTarget
.y
* mKernelUnitLength
.height
));
2550 margin
.right
= static_cast<int32_t>(
2551 ceil((mKernelSize
.width
- mTarget
.x
- 1) * mKernelUnitLength
.width
));
2552 margin
.bottom
= static_cast<int32_t>(
2553 ceil((mKernelSize
.height
- mTarget
.y
- 1) * mKernelUnitLength
.height
));
2555 IntRect srcRect
= aDestRect
;
2556 srcRect
.Inflate(margin
);
2560 IntRect
FilterNodeConvolveMatrixSoftware::InflatedDestRect(
2561 const IntRect
& aSourceRect
) {
2562 if (aSourceRect
.IsEmpty()) {
2567 margin
.left
= static_cast<int32_t>(
2568 ceil((mKernelSize
.width
- mTarget
.x
- 1) * mKernelUnitLength
.width
));
2569 margin
.top
= static_cast<int32_t>(
2570 ceil((mKernelSize
.height
- mTarget
.y
- 1) * mKernelUnitLength
.height
));
2572 static_cast<int32_t>(ceil(mTarget
.x
* mKernelUnitLength
.width
));
2574 static_cast<int32_t>(ceil(mTarget
.y
* mKernelUnitLength
.height
));
2576 IntRect destRect
= aSourceRect
;
2577 destRect
.Inflate(margin
);
2581 IntRect
FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(
2582 const IntRect
& aRect
) {
2583 IntRect srcRequest
= InflatedSourceRect(aRect
);
2584 IntRect srcOutput
= GetInputRectInRect(IN_CONVOLVE_MATRIX_IN
, srcRequest
);
2585 return InflatedDestRect(srcOutput
).Intersect(aRect
);
2588 FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware()
2589 : mScale(0.0f
), mChannelX(COLOR_CHANNEL_R
), mChannelY(COLOR_CHANNEL_G
) {}
2591 int32_t FilterNodeDisplacementMapSoftware::InputIndex(
2592 uint32_t aInputEnumIndex
) {
2593 switch (aInputEnumIndex
) {
2594 case IN_DISPLACEMENT_MAP_IN
:
2596 case IN_DISPLACEMENT_MAP_IN2
:
2603 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex
,
2605 MOZ_ASSERT(aIndex
== ATT_DISPLACEMENT_MAP_SCALE
);
2610 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex
,
2613 case ATT_DISPLACEMENT_MAP_X_CHANNEL
:
2614 mChannelX
= static_cast<ColorChannel
>(aValue
);
2616 case ATT_DISPLACEMENT_MAP_Y_CHANNEL
:
2617 mChannelY
= static_cast<ColorChannel
>(aValue
);
2620 MOZ_CRASH("GFX: FilterNodeDisplacementMapSoftware::SetAttribute");
2625 already_AddRefed
<DataSourceSurface
> FilterNodeDisplacementMapSoftware::Render(
2626 const IntRect
& aRect
) {
2627 IntRect srcRect
= InflatedSourceOrDestRect(aRect
);
2628 RefPtr
<DataSourceSurface
> input
= GetInputDataSourceSurface(
2629 IN_DISPLACEMENT_MAP_IN
, srcRect
, NEED_COLOR_CHANNELS
);
2630 RefPtr
<DataSourceSurface
> map
= GetInputDataSourceSurface(
2631 IN_DISPLACEMENT_MAP_IN2
, aRect
, NEED_COLOR_CHANNELS
);
2632 RefPtr
<DataSourceSurface
> target
=
2633 Factory::CreateDataSourceSurface(aRect
.Size(), SurfaceFormat::B8G8R8A8
);
2634 if (MOZ2D_WARN_IF(!(input
&& map
&& target
))) {
2638 IntPoint offset
= aRect
.TopLeft() - srcRect
.TopLeft();
2640 DataSourceSurface::ScopedMap
inputMap(input
, DataSourceSurface::READ
);
2641 DataSourceSurface::ScopedMap
mapMap(map
, DataSourceSurface::READ
);
2642 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
2643 if (MOZ2D_WARN_IF(!(inputMap
.IsMapped() && mapMap
.IsMapped() &&
2644 targetMap
.IsMapped()))) {
2648 uint8_t* sourceData
=
2649 DataAtOffset(input
, inputMap
.GetMappedSurface(), offset
);
2650 int32_t sourceStride
= inputMap
.GetStride();
2651 uint8_t* sourceBegin
= inputMap
.GetData();
2652 uint8_t* sourceEnd
= sourceBegin
+ sourceStride
* input
->GetSize().height
;
2653 uint8_t* mapData
= mapMap
.GetData();
2654 int32_t mapStride
= mapMap
.GetStride();
2655 uint8_t* targetData
= targetMap
.GetData();
2656 int32_t targetStride
= targetMap
.GetStride();
2658 static const ptrdiff_t channelMap
[4] = {
2659 B8G8R8A8_COMPONENT_BYTEOFFSET_R
, B8G8R8A8_COMPONENT_BYTEOFFSET_G
,
2660 B8G8R8A8_COMPONENT_BYTEOFFSET_B
, B8G8R8A8_COMPONENT_BYTEOFFSET_A
};
2661 uint16_t xChannel
= channelMap
[mChannelX
];
2662 uint16_t yChannel
= channelMap
[mChannelY
];
2664 float scaleOver255
= mScale
/ 255.0f
;
2665 float scaleAdjustment
= -0.5f
* mScale
;
2667 for (int32_t y
= 0; y
< aRect
.Height(); y
++) {
2668 for (int32_t x
= 0; x
< aRect
.Width(); x
++) {
2669 uint32_t mapIndex
= y
* mapStride
+ 4 * x
;
2670 uint32_t targIndex
= y
* targetStride
+ 4 * x
;
2672 x
+ scaleOver255
* mapData
[mapIndex
+ xChannel
] + scaleAdjustment
;
2674 y
+ scaleOver255
* mapData
[mapIndex
+ yChannel
] + scaleAdjustment
;
2675 *(uint32_t*)(targetData
+ targIndex
) = ColorAtPoint(
2676 sourceData
, sourceStride
, sourceBegin
, sourceEnd
, sourceX
, sourceY
);
2679 // Keep valgrind happy.
2680 PodZero(&targetData
[y
* targetStride
+ 4 * aRect
.Width()],
2681 targetStride
- 4 * aRect
.Width());
2684 return target
.forget();
2687 void FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(
2688 const IntRect
& aRect
) {
2689 RequestInputRect(IN_DISPLACEMENT_MAP_IN
, InflatedSourceOrDestRect(aRect
));
2690 RequestInputRect(IN_DISPLACEMENT_MAP_IN2
, aRect
);
2693 IntRect
FilterNodeDisplacementMapSoftware::MapRectToSource(
2694 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
2696 MapInputRectToSource(IN_DISPLACEMENT_MAP_IN
,
2697 InflatedSourceOrDestRect(aRect
), aMax
, aSourceNode
);
2699 MapInputRectToSource(IN_DISPLACEMENT_MAP_IN2
, aRect
, aMax
, aSourceNode
));
2703 IntRect
FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(
2704 const IntRect
& aDestOrSourceRect
) {
2705 IntRect sourceOrDestRect
= aDestOrSourceRect
;
2706 sourceOrDestRect
.Inflate(ceil(fabs(mScale
) / 2));
2707 return sourceOrDestRect
;
2710 IntRect
FilterNodeDisplacementMapSoftware::GetOutputRectInRect(
2711 const IntRect
& aRect
) {
2712 IntRect srcRequest
= InflatedSourceOrDestRect(aRect
);
2713 IntRect srcOutput
= GetInputRectInRect(IN_DISPLACEMENT_MAP_IN
, srcRequest
);
2714 return InflatedSourceOrDestRect(srcOutput
).Intersect(aRect
);
2717 FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware()
2721 mType(TURBULENCE_TYPE_TURBULENCE
) {}
2723 int32_t FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex
) {
2727 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex
,
2728 const Size
& aBaseFrequency
) {
2730 case ATT_TURBULENCE_BASE_FREQUENCY
:
2731 mBaseFrequency
= aBaseFrequency
;
2734 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2740 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex
,
2741 const IntRect
& aRect
) {
2743 case ATT_TURBULENCE_RECT
:
2744 mRenderRect
= aRect
;
2747 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2753 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex
,
2755 MOZ_ASSERT(aIndex
== ATT_TURBULENCE_STITCHABLE
);
2756 mStitchable
= aStitchable
;
2760 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex
,
2763 case ATT_TURBULENCE_NUM_OCTAVES
:
2764 mNumOctaves
= aValue
;
2766 case ATT_TURBULENCE_SEED
:
2769 case ATT_TURBULENCE_TYPE
:
2770 mType
= static_cast<TurbulenceType
>(aValue
);
2773 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2779 already_AddRefed
<DataSourceSurface
> FilterNodeTurbulenceSoftware::Render(
2780 const IntRect
& aRect
) {
2781 return FilterProcessing::RenderTurbulence(
2782 aRect
.Size(), aRect
.TopLeft(), mBaseFrequency
, mSeed
, mNumOctaves
, mType
,
2783 mStitchable
, Rect(mRenderRect
));
2786 IntRect
FilterNodeTurbulenceSoftware::GetOutputRectInRect(
2787 const IntRect
& aRect
) {
2788 return aRect
.Intersect(mRenderRect
);
2791 IntRect
FilterNodeTurbulenceSoftware::MapRectToSource(const IntRect
& aRect
,
2792 const IntRect
& aMax
,
2793 FilterNode
* aSourceNode
) {
2797 FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware()
2798 : mK1(0), mK2(0), mK3(0), mK4(0) {}
2800 int32_t FilterNodeArithmeticCombineSoftware::InputIndex(
2801 uint32_t aInputEnumIndex
) {
2802 switch (aInputEnumIndex
) {
2803 case IN_ARITHMETIC_COMBINE_IN
:
2805 case IN_ARITHMETIC_COMBINE_IN2
:
2812 void FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex
,
2813 const Float
* aFloat
,
2815 MOZ_ASSERT(aIndex
== ATT_ARITHMETIC_COMBINE_COEFFICIENTS
);
2816 MOZ_RELEASE_ASSERT(aSize
== 4);
2826 already_AddRefed
<DataSourceSurface
> FilterNodeArithmeticCombineSoftware::Render(
2827 const IntRect
& aRect
) {
2828 RefPtr
<DataSourceSurface
> input1
= GetInputDataSourceSurface(
2829 IN_ARITHMETIC_COMBINE_IN
, aRect
, NEED_COLOR_CHANNELS
);
2830 RefPtr
<DataSourceSurface
> input2
= GetInputDataSourceSurface(
2831 IN_ARITHMETIC_COMBINE_IN2
, aRect
, NEED_COLOR_CHANNELS
);
2832 if (!input1
&& !input2
) {
2836 // If one input is null, treat it as transparent by adjusting the factors.
2837 Float k1
= mK1
, k2
= mK2
, k3
= mK3
, k4
= mK4
;
2850 return FilterProcessing::ApplyArithmeticCombine(input1
, input2
, k1
, k2
, k3
,
2854 void FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(
2855 const IntRect
& aRect
) {
2856 RequestInputRect(IN_ARITHMETIC_COMBINE_IN
, aRect
);
2857 RequestInputRect(IN_ARITHMETIC_COMBINE_IN2
, aRect
);
2860 IntRect
FilterNodeArithmeticCombineSoftware::MapRectToSource(
2861 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
2863 MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN
, aRect
, aMax
, aSourceNode
);
2864 result
.OrWith(MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN2
, aRect
, aMax
,
2869 IntRect
FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(
2870 const IntRect
& aRect
) {
2875 GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN
, aRect
).Intersect(aRect
);
2877 GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2
, aRect
).Intersect(aRect
);
2880 result
= rectFrom1
.Intersect(rectFrom2
);
2883 result
= result
.Union(rectFrom1
);
2886 result
= result
.Union(rectFrom2
);
2891 FilterNodeCompositeSoftware::FilterNodeCompositeSoftware()
2892 : mOperator(COMPOSITE_OPERATOR_OVER
) {}
2894 int32_t FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex
) {
2895 return aInputEnumIndex
- IN_COMPOSITE_IN_START
;
2898 void FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex
,
2899 uint32_t aCompositeOperator
) {
2900 MOZ_ASSERT(aIndex
== ATT_COMPOSITE_OPERATOR
);
2901 mOperator
= static_cast<CompositeOperator
>(aCompositeOperator
);
2905 already_AddRefed
<DataSourceSurface
> FilterNodeCompositeSoftware::Render(
2906 const IntRect
& aRect
) {
2907 RefPtr
<DataSourceSurface
> start
= GetInputDataSourceSurface(
2908 IN_COMPOSITE_IN_START
, aRect
, NEED_COLOR_CHANNELS
);
2909 RefPtr
<DataSourceSurface
> dest
= Factory::CreateDataSourceSurface(
2910 aRect
.Size(), SurfaceFormat::B8G8R8A8
, true);
2911 if (MOZ2D_WARN_IF(!dest
)) {
2916 CopyRect(start
, dest
, aRect
- aRect
.TopLeft(), IntPoint());
2919 for (size_t inputIndex
= 1; inputIndex
< NumberOfSetInputs(); inputIndex
++) {
2920 RefPtr
<DataSourceSurface
> input
= GetInputDataSourceSurface(
2921 IN_COMPOSITE_IN_START
+ inputIndex
, aRect
, NEED_COLOR_CHANNELS
);
2923 FilterProcessing::ApplyComposition(input
, dest
, mOperator
);
2925 // We need to treat input as transparent. Depending on the composite
2926 // operator, different things happen to dest.
2927 switch (mOperator
) {
2928 case COMPOSITE_OPERATOR_OVER
:
2929 case COMPOSITE_OPERATOR_ATOP
:
2930 case COMPOSITE_OPERATOR_XOR
:
2931 case COMPOSITE_OPERATOR_LIGHTER
:
2932 // dest is unchanged.
2934 case COMPOSITE_OPERATOR_OUT
:
2935 // dest is now transparent, but it can become non-transparent again
2936 // when compositing additional inputs.
2937 ClearDataSourceSurface(dest
);
2939 case COMPOSITE_OPERATOR_IN
:
2940 // Transparency always wins. We're completely transparent now and
2941 // no additional input can get rid of that transparency.
2946 return dest
.forget();
2949 void FilterNodeCompositeSoftware::RequestFromInputsForRect(
2950 const IntRect
& aRect
) {
2951 for (size_t inputIndex
= 0; inputIndex
< NumberOfSetInputs(); inputIndex
++) {
2952 RequestInputRect(IN_COMPOSITE_IN_START
+ inputIndex
, aRect
);
2956 IntRect
FilterNodeCompositeSoftware::MapRectToSource(const IntRect
& aRect
,
2957 const IntRect
& aMax
,
2958 FilterNode
* aSourceNode
) {
2960 for (size_t inputIndex
= 0; inputIndex
< NumberOfSetInputs(); inputIndex
++) {
2961 result
.OrWith(MapInputRectToSource(IN_COMPOSITE_IN_START
+ inputIndex
,
2962 aRect
, aMax
, aSourceNode
));
2967 IntRect
FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
2969 for (size_t inputIndex
= 0; inputIndex
< NumberOfSetInputs(); inputIndex
++) {
2971 GetInputRectInRect(IN_COMPOSITE_IN_START
+ inputIndex
, aRect
);
2972 if (mOperator
== COMPOSITE_OPERATOR_IN
&& inputIndex
> 0) {
2973 rect
= rect
.Intersect(inputRect
);
2975 rect
= rect
.Union(inputRect
);
2981 int32_t FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex
) {
2982 switch (aInputEnumIndex
) {
2983 case IN_GAUSSIAN_BLUR_IN
:
2990 already_AddRefed
<DataSourceSurface
> FilterNodeBlurXYSoftware::Render(
2991 const IntRect
& aRect
) {
2992 Size sigmaXY
= StdDeviationXY();
2994 AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY
.width
, sigmaXY
.height
));
2996 if (d
.width
== 0 && d
.height
== 0) {
2997 return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN
, aRect
);
3000 IntRect srcRect
= InflatedSourceOrDestRect(aRect
);
3001 RefPtr
<DataSourceSurface
> input
=
3002 GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN
, srcRect
);
3007 RefPtr
<DataSourceSurface
> target
;
3008 Rect
r(0, 0, srcRect
.Width(), srcRect
.Height());
3010 if (input
->GetFormat() == SurfaceFormat::A8
) {
3012 Factory::CreateDataSourceSurface(srcRect
.Size(), SurfaceFormat::A8
);
3013 if (MOZ2D_WARN_IF(!target
)) {
3016 CopyRect(input
, target
, IntRect(IntPoint(), input
->GetSize()), IntPoint());
3018 DataSourceSurface::ScopedMap
targetMap(target
,
3019 DataSourceSurface::READ_WRITE
);
3020 if (MOZ2D_WARN_IF(!targetMap
.IsMapped())) {
3023 AlphaBoxBlur
blur(r
, targetMap
.GetStride(), sigmaXY
.width
, sigmaXY
.height
);
3024 blur
.Blur(targetMap
.GetData());
3026 RefPtr
<DataSourceSurface
> channel0
, channel1
, channel2
, channel3
;
3027 FilterProcessing::SeparateColorChannels(input
, channel0
, channel1
, channel2
,
3029 if (MOZ2D_WARN_IF(!(channel0
&& channel1
&& channel2
&& channel3
))) {
3033 DataSourceSurface::ScopedMap
channel0Map(channel0
,
3034 DataSourceSurface::READ_WRITE
);
3035 DataSourceSurface::ScopedMap
channel1Map(channel1
,
3036 DataSourceSurface::READ_WRITE
);
3037 DataSourceSurface::ScopedMap
channel2Map(channel2
,
3038 DataSourceSurface::READ_WRITE
);
3039 DataSourceSurface::ScopedMap
channel3Map(channel3
,
3040 DataSourceSurface::READ_WRITE
);
3041 if (MOZ2D_WARN_IF(!(channel0Map
.IsMapped() && channel1Map
.IsMapped() &&
3042 channel2Map
.IsMapped() && channel3Map
.IsMapped()))) {
3046 AlphaBoxBlur
blur(r
, channel0Map
.GetStride(), sigmaXY
.width
,
3048 blur
.Blur(channel0Map
.GetData());
3049 blur
.Blur(channel1Map
.GetData());
3050 blur
.Blur(channel2Map
.GetData());
3051 blur
.Blur(channel3Map
.GetData());
3053 target
= FilterProcessing::CombineColorChannels(channel0
, channel1
,
3054 channel2
, channel3
);
3057 return GetDataSurfaceInRect(target
, srcRect
, aRect
, EDGE_MODE_NONE
);
3060 void FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect
& aRect
) {
3061 RequestInputRect(IN_GAUSSIAN_BLUR_IN
, InflatedSourceOrDestRect(aRect
));
3064 IntRect
FilterNodeBlurXYSoftware::MapRectToSource(const IntRect
& aRect
,
3065 const IntRect
& aMax
,
3066 FilterNode
* aSourceNode
) {
3067 return MapInputRectToSource(
3068 IN_GAUSSIAN_BLUR_IN
, InflatedSourceOrDestRect(aRect
), aMax
, aSourceNode
);
3071 IntRect
FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(
3072 const IntRect
& aDestRect
) {
3073 Size sigmaXY
= StdDeviationXY();
3075 AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY
.width
, sigmaXY
.height
));
3076 IntRect srcRect
= aDestRect
;
3081 IntRect
FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
3082 IntRect srcRequest
= InflatedSourceOrDestRect(aRect
);
3083 IntRect srcOutput
= GetInputRectInRect(IN_GAUSSIAN_BLUR_IN
, srcRequest
);
3084 return InflatedSourceOrDestRect(srcOutput
).Intersect(aRect
);
3087 FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware()
3088 : mStdDeviation(0) {}
3090 static float ClampStdDeviation(float aStdDeviation
) {
3091 // Cap software blur radius for performance reasons.
3092 return std::clamp(aStdDeviation
, 0.f
, 100.f
);
3095 void FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex
,
3096 float aStdDeviation
) {
3098 case ATT_GAUSSIAN_BLUR_STD_DEVIATION
:
3099 mStdDeviation
= ClampStdDeviation(aStdDeviation
);
3102 MOZ_CRASH("GFX: FilterNodeGaussianBlurSoftware::SetAttribute");
3107 Size
FilterNodeGaussianBlurSoftware::StdDeviationXY() {
3108 return Size(mStdDeviation
, mStdDeviation
);
3111 FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware()
3112 : mStdDeviation(0.0), mBlurDirection(BLUR_DIRECTION_X
) {}
3114 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex
,
3115 Float aStdDeviation
) {
3117 case ATT_DIRECTIONAL_BLUR_STD_DEVIATION
:
3118 mStdDeviation
= ClampStdDeviation(aStdDeviation
);
3121 MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
3126 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex
,
3127 uint32_t aBlurDirection
) {
3129 case ATT_DIRECTIONAL_BLUR_DIRECTION
:
3130 mBlurDirection
= (BlurDirection
)aBlurDirection
;
3133 MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
3138 Size
FilterNodeDirectionalBlurSoftware::StdDeviationXY() {
3139 float sigmaX
= mBlurDirection
== BLUR_DIRECTION_X
? mStdDeviation
: 0;
3140 float sigmaY
= mBlurDirection
== BLUR_DIRECTION_Y
? mStdDeviation
: 0;
3141 return Size(sigmaX
, sigmaY
);
3144 int32_t FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex
) {
3145 switch (aInputEnumIndex
) {
3153 void FilterNodeCropSoftware::SetAttribute(uint32_t aIndex
,
3154 const Rect
& aSourceRect
) {
3155 MOZ_ASSERT(aIndex
== ATT_CROP_RECT
);
3156 Rect srcRect
= aSourceRect
;
3158 if (!srcRect
.ToIntRect(&mCropRect
)) {
3159 mCropRect
= IntRect();
3164 already_AddRefed
<DataSourceSurface
> FilterNodeCropSoftware::Render(
3165 const IntRect
& aRect
) {
3166 return GetInputDataSourceSurface(IN_CROP_IN
, aRect
.Intersect(mCropRect
));
3169 void FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect
& aRect
) {
3170 RequestInputRect(IN_CROP_IN
, aRect
.Intersect(mCropRect
));
3173 IntRect
FilterNodeCropSoftware::MapRectToSource(const IntRect
& aRect
,
3174 const IntRect
& aMax
,
3175 FilterNode
* aSourceNode
) {
3176 return MapInputRectToSource(IN_CROP_IN
, aRect
.Intersect(mCropRect
), aMax
,
3180 IntRect
FilterNodeCropSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
3181 return GetInputRectInRect(IN_CROP_IN
, aRect
).Intersect(mCropRect
);
3184 int32_t FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex
) {
3185 switch (aInputEnumIndex
) {
3186 case IN_PREMULTIPLY_IN
:
3193 already_AddRefed
<DataSourceSurface
> FilterNodePremultiplySoftware::Render(
3194 const IntRect
& aRect
) {
3195 RefPtr
<DataSourceSurface
> input
=
3196 GetInputDataSourceSurface(IN_PREMULTIPLY_IN
, aRect
);
3197 return input
? Premultiply(input
) : nullptr;
3200 void FilterNodePremultiplySoftware::RequestFromInputsForRect(
3201 const IntRect
& aRect
) {
3202 RequestInputRect(IN_PREMULTIPLY_IN
, aRect
);
3205 IntRect
FilterNodePremultiplySoftware::MapRectToSource(
3206 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
3207 return MapInputRectToSource(IN_PREMULTIPLY_IN
, aRect
, aMax
, aSourceNode
);
3210 IntRect
FilterNodePremultiplySoftware::GetOutputRectInRect(
3211 const IntRect
& aRect
) {
3212 return GetInputRectInRect(IN_PREMULTIPLY_IN
, aRect
);
3215 int32_t FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex
) {
3216 switch (aInputEnumIndex
) {
3217 case IN_UNPREMULTIPLY_IN
:
3224 already_AddRefed
<DataSourceSurface
> FilterNodeUnpremultiplySoftware::Render(
3225 const IntRect
& aRect
) {
3226 RefPtr
<DataSourceSurface
> input
=
3227 GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN
, aRect
);
3228 return input
? Unpremultiply(input
) : nullptr;
3231 void FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(
3232 const IntRect
& aRect
) {
3233 RequestInputRect(IN_UNPREMULTIPLY_IN
, aRect
);
3236 IntRect
FilterNodeUnpremultiplySoftware::MapRectToSource(
3237 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
3238 return MapInputRectToSource(IN_UNPREMULTIPLY_IN
, aRect
, aMax
, aSourceNode
);
3241 IntRect
FilterNodeUnpremultiplySoftware::GetOutputRectInRect(
3242 const IntRect
& aRect
) {
3243 return GetInputRectInRect(IN_UNPREMULTIPLY_IN
, aRect
);
3246 void FilterNodeOpacitySoftware::SetAttribute(uint32_t aIndex
, Float aValue
) {
3247 MOZ_ASSERT(aIndex
== ATT_OPACITY_VALUE
);
3252 int32_t FilterNodeOpacitySoftware::InputIndex(uint32_t aInputEnumIndex
) {
3253 switch (aInputEnumIndex
) {
3261 already_AddRefed
<DataSourceSurface
> FilterNodeOpacitySoftware::Render(
3262 const IntRect
& aRect
) {
3263 RefPtr
<DataSourceSurface
> input
=
3264 GetInputDataSourceSurface(IN_OPACITY_IN
, aRect
);
3265 return input
? Opacity(input
, mValue
) : nullptr;
3268 void FilterNodeOpacitySoftware::RequestFromInputsForRect(const IntRect
& aRect
) {
3269 RequestInputRect(IN_OPACITY_IN
, aRect
);
3272 IntRect
FilterNodeOpacitySoftware::MapRectToSource(const IntRect
& aRect
,
3273 const IntRect
& aMax
,
3274 FilterNode
* aSourceNode
) {
3275 return MapInputRectToSource(IN_OPACITY_IN
, aRect
, aMax
, aSourceNode
);
3278 IntRect
FilterNodeOpacitySoftware::GetOutputRectInRect(const IntRect
& aRect
) {
3279 return GetInputRectInRect(IN_OPACITY_IN
, aRect
);
3282 bool PointLightSoftware::SetAttribute(uint32_t aIndex
, const Point3D
& aPoint
) {
3284 case ATT_POINT_LIGHT_POSITION
:
3293 SpotLightSoftware::SpotLightSoftware()
3294 : mSpecularFocus(0), mLimitingConeAngle(0), mLimitingConeCos(1) {}
3296 bool SpotLightSoftware::SetAttribute(uint32_t aIndex
, const Point3D
& aPoint
) {
3298 case ATT_SPOT_LIGHT_POSITION
:
3301 case ATT_SPOT_LIGHT_POINTS_AT
:
3310 bool SpotLightSoftware::SetAttribute(uint32_t aIndex
, Float aValue
) {
3312 case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE
:
3313 mLimitingConeAngle
= aValue
;
3315 case ATT_SPOT_LIGHT_FOCUS
:
3316 mSpecularFocus
= aValue
;
3324 DistantLightSoftware::DistantLightSoftware() : mAzimuth(0), mElevation(0) {}
3326 bool DistantLightSoftware::SetAttribute(uint32_t aIndex
, Float aValue
) {
3328 case ATT_DISTANT_LIGHT_AZIMUTH
:
3331 case ATT_DISTANT_LIGHT_ELEVATION
:
3332 mElevation
= aValue
;
3340 static inline Point3D
Normalized(const Point3D
& vec
) {
3346 template <typename LightType
, typename LightingType
>
3347 FilterNodeLightingSoftware
<LightType
, LightingType
>::FilterNodeLightingSoftware(
3348 const char* aTypeName
)
3350 #if defined(MOZILLA_INTERNAL_API) && defined(NS_BUILD_REFCNT_LOGGING)
3352 mTypeName(aTypeName
)
3357 template <typename LightType
, typename LightingType
>
3358 int32_t FilterNodeLightingSoftware
<LightType
, LightingType
>::InputIndex(
3359 uint32_t aInputEnumIndex
) {
3360 switch (aInputEnumIndex
) {
3361 case IN_LIGHTING_IN
:
3368 template <typename LightType
, typename LightingType
>
3369 void FilterNodeLightingSoftware
<LightType
, LightingType
>::SetAttribute(
3370 uint32_t aIndex
, const Point3D
& aPoint
) {
3371 if (mLight
.SetAttribute(aIndex
, aPoint
)) {
3375 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute point");
3378 template <typename LightType
, typename LightingType
>
3379 void FilterNodeLightingSoftware
<LightType
, LightingType
>::SetAttribute(
3380 uint32_t aIndex
, Float aValue
) {
3381 if (mLight
.SetAttribute(aIndex
, aValue
) ||
3382 mLighting
.SetAttribute(aIndex
, aValue
)) {
3387 case ATT_LIGHTING_SURFACE_SCALE
:
3388 mSurfaceScale
= std::fpclassify(aValue
) == FP_SUBNORMAL
? 0.0 : aValue
;
3391 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float");
3396 template <typename LightType
, typename LightingType
>
3397 void FilterNodeLightingSoftware
<LightType
, LightingType
>::SetAttribute(
3398 uint32_t aIndex
, const Size
& aKernelUnitLength
) {
3400 case ATT_LIGHTING_KERNEL_UNIT_LENGTH
:
3401 mKernelUnitLength
= aKernelUnitLength
;
3404 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size");
3409 template <typename LightType
, typename LightingType
>
3410 void FilterNodeLightingSoftware
<LightType
, LightingType
>::SetAttribute(
3411 uint32_t aIndex
, const DeviceColor
& aColor
) {
3412 MOZ_ASSERT(aIndex
== ATT_LIGHTING_COLOR
);
3417 template <typename LightType
, typename LightingType
>
3419 FilterNodeLightingSoftware
<LightType
, LightingType
>::GetOutputRectInRect(
3420 const IntRect
& aRect
) {
3424 Point3D
PointLightSoftware::GetVectorToLight(const Point3D
& aTargetPoint
) {
3425 return Normalized(mPosition
- aTargetPoint
);
3428 uint32_t PointLightSoftware::GetColor(uint32_t aLightColor
,
3429 const Point3D
& aVectorToLight
) {
3433 void SpotLightSoftware::Prepare() {
3434 mVectorFromFocusPointToLight
= Normalized(mPointsAt
- mPosition
);
3436 std::max
<double>(cos(mLimitingConeAngle
* M_PI
/ 180.0), 0.0);
3437 mPowCache
.CacheForExponent(mSpecularFocus
);
3440 Point3D
SpotLightSoftware::GetVectorToLight(const Point3D
& aTargetPoint
) {
3441 return Normalized(mPosition
- aTargetPoint
);
3444 uint32_t SpotLightSoftware::GetColor(uint32_t aLightColor
,
3445 const Point3D
& aVectorToLight
) {
3451 Float dot
= -aVectorToLight
.DotProduct(mVectorFromFocusPointToLight
);
3452 if (!mPowCache
.HasPowerTable()) {
3453 dot
*= (dot
>= mLimitingConeCos
);
3454 color
= aLightColor
;
3455 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] *= dot
;
3456 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] *= dot
;
3457 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] *= dot
;
3459 color
= aLightColor
;
3460 uint16_t doti
= dot
* (dot
>= 0) * (1 << PowCache::sInputIntPrecisionBits
);
3461 uint32_t tmp
= mPowCache
.Pow(doti
) * (dot
>= mLimitingConeCos
);
3462 MOZ_ASSERT(tmp
<= (1 << PowCache::sOutputIntPrecisionBits
),
3463 "pow() result must not exceed 1.0");
3464 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] =
3465 uint8_t((colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] * tmp
) >>
3466 PowCache::sOutputIntPrecisionBits
);
3467 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] =
3468 uint8_t((colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] * tmp
) >>
3469 PowCache::sOutputIntPrecisionBits
);
3470 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] =
3471 uint8_t((colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] * tmp
) >>
3472 PowCache::sOutputIntPrecisionBits
);
3474 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_A
] = 255;
3478 void DistantLightSoftware::Prepare() {
3479 const double radPerDeg
= M_PI
/ 180.0;
3480 mVectorToLight
.x
= cos(mAzimuth
* radPerDeg
) * cos(mElevation
* radPerDeg
);
3481 mVectorToLight
.y
= sin(mAzimuth
* radPerDeg
) * cos(mElevation
* radPerDeg
);
3482 mVectorToLight
.z
= sin(mElevation
* radPerDeg
);
3485 Point3D
DistantLightSoftware::GetVectorToLight(const Point3D
& aTargetPoint
) {
3486 return mVectorToLight
;
3489 uint32_t DistantLightSoftware::GetColor(uint32_t aLightColor
,
3490 const Point3D
& aVectorToLight
) {
3494 template <typename CoordType
>
3495 static Point3D
GenerateNormal(const uint8_t* data
, int32_t stride
,
3496 uint8_t* boundsBegin
, uint8_t* boundsEnd
,
3497 int32_t x
, int32_t y
, float surfaceScale
,
3498 CoordType dx
, CoordType dy
) {
3499 const uint8_t* index
= data
+ y
* stride
+ x
;
3503 // See this for source of constants:
3504 // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
3505 int16_t normalX
= -1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3506 boundsEnd
, -dx
, -dy
, 1, 0) +
3507 1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3508 boundsEnd
, dx
, -dy
, 1, 0) +
3509 -2 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3510 boundsEnd
, -dx
, zero
, 1, 0) +
3511 2 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3512 boundsEnd
, dx
, zero
, 1, 0) +
3513 -1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3514 boundsEnd
, -dx
, dy
, 1, 0) +
3515 1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3516 boundsEnd
, dx
, dy
, 1, 0);
3518 int16_t normalY
= -1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3519 boundsEnd
, -dx
, -dy
, 1, 0) +
3520 -2 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3521 boundsEnd
, zero
, -dy
, 1, 0) +
3522 -1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3523 boundsEnd
, dx
, -dy
, 1, 0) +
3524 1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3525 boundsEnd
, -dx
, dy
, 1, 0) +
3526 2 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3527 boundsEnd
, zero
, dy
, 1, 0) +
3528 1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3529 boundsEnd
, dx
, dy
, 1, 0);
3532 normal
.x
= -surfaceScale
* normalX
/ 4.0f
;
3533 normal
.y
= -surfaceScale
* normalY
/ 4.0f
;
3535 return Normalized(normal
);
3538 template <typename LightType
, typename LightingType
>
3539 already_AddRefed
<DataSourceSurface
>
3540 FilterNodeLightingSoftware
<LightType
, LightingType
>::Render(
3541 const IntRect
& aRect
) {
3542 if (mKernelUnitLength
.width
== floor(mKernelUnitLength
.width
) &&
3543 mKernelUnitLength
.height
== floor(mKernelUnitLength
.height
)) {
3544 return DoRender(aRect
, (int32_t)mKernelUnitLength
.width
,
3545 (int32_t)mKernelUnitLength
.height
);
3547 return DoRender(aRect
, mKernelUnitLength
.width
, mKernelUnitLength
.height
);
3550 template <typename LightType
, typename LightingType
>
3551 void FilterNodeLightingSoftware
<
3552 LightType
, LightingType
>::RequestFromInputsForRect(const IntRect
& aRect
) {
3553 IntRect srcRect
= aRect
;
3554 srcRect
.Inflate(ceil(mKernelUnitLength
.width
),
3555 ceil(mKernelUnitLength
.height
));
3556 RequestInputRect(IN_LIGHTING_IN
, srcRect
);
3559 template <typename LightType
, typename LightingType
>
3560 IntRect FilterNodeLightingSoftware
<LightType
, LightingType
>::MapRectToSource(
3561 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
3562 IntRect srcRect
= aRect
;
3563 srcRect
.Inflate(ceil(mKernelUnitLength
.width
),
3564 ceil(mKernelUnitLength
.height
));
3565 return MapInputRectToSource(IN_LIGHTING_IN
, srcRect
, aMax
, aSourceNode
);
3568 template <typename LightType
, typename LightingType
>
3569 template <typename CoordType
>
3570 already_AddRefed
<DataSourceSurface
>
3571 FilterNodeLightingSoftware
<LightType
, LightingType
>::DoRender(
3572 const IntRect
& aRect
, CoordType aKernelUnitLengthX
,
3573 CoordType aKernelUnitLengthY
) {
3574 MOZ_ASSERT(aKernelUnitLengthX
> 0,
3575 "aKernelUnitLengthX can be a negative or zero value");
3576 MOZ_ASSERT(aKernelUnitLengthY
> 0,
3577 "aKernelUnitLengthY can be a negative or zero value");
3579 IntRect srcRect
= aRect
;
3580 IntSize size
= aRect
.Size();
3581 srcRect
.Inflate(ceil(float(aKernelUnitLengthX
)),
3582 ceil(float(aKernelUnitLengthY
)));
3584 // Inflate the source rect by another pixel because the bilinear filtering in
3585 // ColorComponentAtPoint may want to access the margins.
3588 RefPtr
<DataSourceSurface
> input
= GetInputDataSourceSurface(
3589 IN_LIGHTING_IN
, srcRect
, CAN_HANDLE_A8
, EDGE_MODE_NONE
);
3595 if (input
->GetFormat() != SurfaceFormat::A8
) {
3596 input
= FilterProcessing::ExtractAlpha(input
);
3599 RefPtr
<DataSourceSurface
> target
=
3600 Factory::CreateDataSourceSurface(size
, SurfaceFormat::B8G8R8A8
);
3601 if (MOZ2D_WARN_IF(!target
)) {
3605 IntPoint offset
= aRect
.TopLeft() - srcRect
.TopLeft();
3607 DataSourceSurface::ScopedMap
sourceMap(input
, DataSourceSurface::READ
);
3608 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
3609 if (MOZ2D_WARN_IF(!(sourceMap
.IsMapped() && targetMap
.IsMapped()))) {
3613 uint8_t* sourceData
=
3614 DataAtOffset(input
, sourceMap
.GetMappedSurface(), offset
);
3615 int32_t sourceStride
= sourceMap
.GetStride();
3616 uint8_t* sourceBegin
= sourceMap
.GetData();
3617 uint8_t* sourceEnd
= sourceBegin
+ sourceStride
* input
->GetSize().height
;
3618 uint8_t* targetData
= targetMap
.GetData();
3619 int32_t targetStride
= targetMap
.GetStride();
3621 uint32_t lightColor
= ColorToBGRA(mColor
);
3623 mLighting
.Prepare();
3625 for (int32_t y
= 0; y
< size
.height
; y
++) {
3626 for (int32_t x
= 0; x
< size
.width
; x
++) {
3627 int32_t sourceIndex
= y
* sourceStride
+ x
;
3628 int32_t targetIndex
= y
* targetStride
+ 4 * x
;
3631 GenerateNormal(sourceData
, sourceStride
, sourceBegin
, sourceEnd
, x
, y
,
3632 mSurfaceScale
, aKernelUnitLengthX
, aKernelUnitLengthY
);
3634 IntPoint
pointInFilterSpace(aRect
.X() + x
, aRect
.Y() + y
);
3635 Float Z
= mSurfaceScale
* sourceData
[sourceIndex
] / 255.0f
;
3636 Point3D
pt(pointInFilterSpace
.x
, pointInFilterSpace
.y
, Z
);
3637 Point3D rayDir
= mLight
.GetVectorToLight(pt
);
3638 uint32_t color
= mLight
.GetColor(lightColor
, rayDir
);
3640 *(uint32_t*)(targetData
+ targetIndex
) =
3641 mLighting
.LightPixel(normal
, rayDir
, color
);
3644 // Zero padding to keep valgrind happy.
3645 PodZero(&targetData
[y
* targetStride
+ 4 * size
.width
],
3646 targetStride
- 4 * size
.width
);
3649 return target
.forget();
3652 DiffuseLightingSoftware::DiffuseLightingSoftware() : mDiffuseConstant(0) {}
3654 bool DiffuseLightingSoftware::SetAttribute(uint32_t aIndex
, Float aValue
) {
3656 case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT
:
3657 mDiffuseConstant
= aValue
;
3665 uint32_t DiffuseLightingSoftware::LightPixel(const Point3D
& aNormal
,
3666 const Point3D
& aVectorToLight
,
3668 Float dotNL
= std::max(0.0f
, aNormal
.DotProduct(aVectorToLight
));
3669 Float diffuseNL
= mDiffuseConstant
* dotNL
;
3673 uint8_t components
[4];
3675 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] = umin(
3676 uint32_t(diffuseNL
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
]),
3678 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] = umin(
3679 uint32_t(diffuseNL
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
]),
3681 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] = umin(
3682 uint32_t(diffuseNL
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
]),
3684 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_A
] = 255;
3688 SpecularLightingSoftware::SpecularLightingSoftware()
3689 : mSpecularConstant(0), mSpecularExponent(0), mSpecularConstantInt(0) {}
3691 bool SpecularLightingSoftware::SetAttribute(uint32_t aIndex
, Float aValue
) {
3693 case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT
:
3694 mSpecularConstant
= std::clamp(aValue
, 0.0f
, 255.0f
);
3696 case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT
:
3697 mSpecularExponent
= std::clamp(aValue
, 1.0f
, 128.0f
);
3705 void SpecularLightingSoftware::Prepare() {
3706 mPowCache
.CacheForExponent(mSpecularExponent
);
3707 mSpecularConstantInt
= uint32_t(mSpecularConstant
* (1 << 8));
3710 uint32_t SpecularLightingSoftware::LightPixel(const Point3D
& aNormal
,
3711 const Point3D
& aVectorToLight
,
3713 Point3D
vectorToEye(0, 0, 1);
3714 Point3D halfwayVector
= aVectorToLight
+ vectorToEye
;
3715 Float halfwayLength
= halfwayVector
.Length();
3716 if (halfwayLength
> 0) {
3717 halfwayVector
/= halfwayLength
;
3719 Float dotNH
= aNormal
.DotProduct(halfwayVector
);
3721 uint16_t(dotNH
* (dotNH
>= 0) * (1 << PowCache::sInputIntPrecisionBits
));
3722 // The exponent for specular is in [1,128] range, so we don't need to check
3723 // and optimize for the "default power table" scenario here.
3724 MOZ_ASSERT(mPowCache
.HasPowerTable());
3725 uint32_t specularNHi
=
3726 uint32_t(mSpecularConstantInt
) * mPowCache
.Pow(dotNHi
) >> 8;
3730 uint8_t components
[4];
3732 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] =
3733 umin((specularNHi
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
]) >>
3734 PowCache::sOutputIntPrecisionBits
,
3736 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] =
3737 umin((specularNHi
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
]) >>
3738 PowCache::sOutputIntPrecisionBits
,
3740 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] =
3741 umin((specularNHi
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
]) >>
3742 PowCache::sOutputIntPrecisionBits
,
3745 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_A
] =
3746 umax(color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
],
3747 umax(color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
],
3748 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
]));
3753 } // namespace mozilla