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");
868 return filter
->GetOutputRectInRect(aInRect
);
871 size_t FilterNodeSoftware::NumberOfSetInputs() {
872 return std::max(mInputSurfaces
.size(), mInputFilters
.size());
875 void FilterNodeSoftware::AddInvalidationListener(
876 FilterInvalidationListener
* aListener
) {
877 MOZ_ASSERT(aListener
, "null listener");
878 mInvalidationListeners
.push_back(aListener
);
881 void FilterNodeSoftware::RemoveInvalidationListener(
882 FilterInvalidationListener
* aListener
) {
883 MOZ_ASSERT(aListener
, "null listener");
884 std::vector
<FilterInvalidationListener
*>::iterator it
= std::find(
885 mInvalidationListeners
.begin(), mInvalidationListeners
.end(), aListener
);
886 mInvalidationListeners
.erase(it
);
889 void FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware
* aFilter
) {
893 void FilterNodeSoftware::Invalidate() {
894 mCachedOutput
= nullptr;
895 mCachedRect
= IntRect();
896 for (std::vector
<FilterInvalidationListener
*>::iterator it
=
897 mInvalidationListeners
.begin();
898 it
!= mInvalidationListeners
.end(); it
++) {
899 (*it
)->FilterInvalidated(this);
903 FilterNodeSoftware::FilterNodeSoftware() {}
905 FilterNodeSoftware::~FilterNodeSoftware() {
907 mInvalidationListeners
.empty(),
908 "All invalidation listeners should have unsubscribed themselves by now!");
910 for (std::vector
<RefPtr
<FilterNodeSoftware
> >::iterator it
=
911 mInputFilters
.begin();
912 it
!= mInputFilters
.end(); it
++) {
914 (*it
)->RemoveInvalidationListener(this);
919 void FilterNodeSoftware::SetInput(uint32_t aIndex
, FilterNode
* aFilter
) {
920 if (aFilter
&& aFilter
->GetBackendType() != FILTER_BACKEND_SOFTWARE
) {
921 MOZ_ASSERT(false, "can only take software filters as inputs");
924 SetInput(aIndex
, nullptr, static_cast<FilterNodeSoftware
*>(aFilter
));
927 void FilterNodeSoftware::SetInput(uint32_t aIndex
, SourceSurface
* aSurface
) {
928 SetInput(aIndex
, aSurface
, nullptr);
931 void FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex
,
932 SourceSurface
* aSurface
,
933 FilterNodeSoftware
* aFilter
) {
934 int32_t inputIndex
= InputIndex(aInputEnumIndex
);
935 if (inputIndex
< 0) {
936 gfxDevCrash(LogReason::FilterInputSet
) << "Invalid set " << inputIndex
;
939 if ((uint32_t)inputIndex
>= NumberOfSetInputs()) {
940 mInputSurfaces
.resize(inputIndex
+ 1);
941 mInputFilters
.resize(inputIndex
+ 1);
943 mInputSurfaces
[inputIndex
] = aSurface
;
944 if (mInputFilters
[inputIndex
]) {
945 mInputFilters
[inputIndex
]->RemoveInvalidationListener(this);
948 aFilter
->AddInvalidationListener(this);
950 mInputFilters
[inputIndex
] = aFilter
;
951 if (!aSurface
&& !aFilter
&& (size_t)inputIndex
== NumberOfSetInputs()) {
952 mInputSurfaces
.resize(inputIndex
);
953 mInputFilters
.resize(inputIndex
);
958 FilterNodeBlendSoftware::FilterNodeBlendSoftware()
959 : mBlendMode(BLEND_MODE_MULTIPLY
) {}
961 int32_t FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex
) {
962 switch (aInputEnumIndex
) {
972 void FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex
,
973 uint32_t aBlendMode
) {
974 MOZ_ASSERT(aIndex
== ATT_BLEND_BLENDMODE
);
975 mBlendMode
= static_cast<BlendMode
>(aBlendMode
);
979 static CompositionOp
ToBlendOp(BlendMode aOp
) {
981 case BLEND_MODE_MULTIPLY
:
982 return CompositionOp::OP_MULTIPLY
;
983 case BLEND_MODE_SCREEN
:
984 return CompositionOp::OP_SCREEN
;
985 case BLEND_MODE_OVERLAY
:
986 return CompositionOp::OP_OVERLAY
;
987 case BLEND_MODE_DARKEN
:
988 return CompositionOp::OP_DARKEN
;
989 case BLEND_MODE_LIGHTEN
:
990 return CompositionOp::OP_LIGHTEN
;
991 case BLEND_MODE_COLOR_DODGE
:
992 return CompositionOp::OP_COLOR_DODGE
;
993 case BLEND_MODE_COLOR_BURN
:
994 return CompositionOp::OP_COLOR_BURN
;
995 case BLEND_MODE_HARD_LIGHT
:
996 return CompositionOp::OP_HARD_LIGHT
;
997 case BLEND_MODE_SOFT_LIGHT
:
998 return CompositionOp::OP_SOFT_LIGHT
;
999 case BLEND_MODE_DIFFERENCE
:
1000 return CompositionOp::OP_DIFFERENCE
;
1001 case BLEND_MODE_EXCLUSION
:
1002 return CompositionOp::OP_EXCLUSION
;
1003 case BLEND_MODE_HUE
:
1004 return CompositionOp::OP_HUE
;
1005 case BLEND_MODE_SATURATION
:
1006 return CompositionOp::OP_SATURATION
;
1007 case BLEND_MODE_COLOR
:
1008 return CompositionOp::OP_COLOR
;
1009 case BLEND_MODE_LUMINOSITY
:
1010 return CompositionOp::OP_LUMINOSITY
;
1013 MOZ_ASSERT_UNREACHABLE("Unexpected BlendMode");
1014 return CompositionOp::OP_OVER
;
1017 already_AddRefed
<DataSourceSurface
> FilterNodeBlendSoftware::Render(
1018 const IntRect
& aRect
) {
1019 RefPtr
<DataSourceSurface
> input1
=
1020 GetInputDataSourceSurface(IN_BLEND_IN
, aRect
, NEED_COLOR_CHANNELS
);
1021 RefPtr
<DataSourceSurface
> input2
=
1022 GetInputDataSourceSurface(IN_BLEND_IN2
, aRect
, NEED_COLOR_CHANNELS
);
1024 // Null inputs need to be treated as transparent.
1026 // First case: both are transparent.
1027 if (!input1
&& !input2
) {
1028 // Then the result is transparent, too.
1032 // Second case: one of them is transparent. Return the non-transparent one.
1033 if (!input1
|| !input2
) {
1034 return input1
? input1
.forget() : input2
.forget();
1037 // Third case: both are non-transparent.
1038 // Apply normal filtering.
1039 RefPtr
<DataSourceSurface
> target
=
1040 FilterProcessing::ApplyBlending(input1
, input2
, mBlendMode
);
1041 if (target
!= nullptr) {
1042 return target
.forget();
1045 IntSize size
= input1
->GetSize();
1046 target
= Factory::CreateDataSourceSurface(size
, SurfaceFormat::B8G8R8A8
);
1047 if (MOZ2D_WARN_IF(!target
)) {
1051 CopyRect(input1
, target
, IntRect(IntPoint(), size
), IntPoint());
1053 // This needs to stay in scope until the draw target has been flushed.
1054 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::READ_WRITE
);
1055 if (MOZ2D_WARN_IF(!targetMap
.IsMapped())) {
1059 RefPtr
<DrawTarget
> dt
= Factory::CreateDrawTargetForData(
1060 BackendType::SKIA
, targetMap
.GetData(), target
->GetSize(),
1061 targetMap
.GetStride(), target
->GetFormat());
1065 << "FilterNodeBlendSoftware::Render failed in CreateDrawTargetForData";
1069 Rect
r(0, 0, size
.width
, size
.height
);
1070 dt
->DrawSurface(input2
, r
, r
, DrawSurfaceOptions(),
1071 DrawOptions(1.0f
, ToBlendOp(mBlendMode
)));
1073 return target
.forget();
1076 void FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect
& aRect
) {
1077 RequestInputRect(IN_BLEND_IN
, aRect
);
1078 RequestInputRect(IN_BLEND_IN2
, aRect
);
1081 IntRect
FilterNodeBlendSoftware::MapRectToSource(const IntRect
& aRect
,
1082 const IntRect
& aMax
,
1083 FilterNode
* aSourceNode
) {
1084 IntRect result
= MapInputRectToSource(IN_BLEND_IN
, aRect
, aMax
, aSourceNode
);
1085 result
.OrWith(MapInputRectToSource(IN_BLEND_IN2
, aRect
, aMax
, aSourceNode
));
1089 IntRect
FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
1090 return GetInputRectInRect(IN_BLEND_IN
, aRect
)
1091 .Union(GetInputRectInRect(IN_BLEND_IN2
, aRect
))
1095 FilterNodeTransformSoftware::FilterNodeTransformSoftware()
1096 : mSamplingFilter(SamplingFilter::GOOD
) {}
1098 int32_t FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex
) {
1099 switch (aInputEnumIndex
) {
1100 case IN_TRANSFORM_IN
:
1107 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex
,
1109 MOZ_ASSERT(aIndex
== ATT_TRANSFORM_FILTER
);
1110 mSamplingFilter
= static_cast<SamplingFilter
>(aFilter
);
1114 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex
,
1115 const Matrix
& aMatrix
) {
1116 MOZ_ASSERT(aIndex
== ATT_TRANSFORM_MATRIX
);
1121 IntRect
FilterNodeTransformSoftware::SourceRectForOutputRect(
1122 const IntRect
& aRect
) {
1123 if (aRect
.IsEmpty()) {
1127 Matrix
inverted(mMatrix
);
1128 if (!inverted
.Invert()) {
1132 Rect neededRect
= inverted
.TransformBounds(Rect(aRect
));
1133 neededRect
.RoundOut();
1134 IntRect neededIntRect
;
1135 if (!neededRect
.ToIntRect(&neededIntRect
)) {
1138 return GetInputRectInRect(IN_TRANSFORM_IN
, neededIntRect
);
1141 IntRect
FilterNodeTransformSoftware::MapRectToSource(const IntRect
& aRect
,
1142 const IntRect
& aMax
,
1143 FilterNode
* aSourceNode
) {
1144 if (aRect
.IsEmpty()) {
1148 Matrix
inverted(mMatrix
);
1149 if (!inverted
.Invert()) {
1153 Rect neededRect
= inverted
.TransformBounds(Rect(aRect
));
1154 neededRect
.RoundOut();
1155 IntRect neededIntRect
;
1156 if (!neededRect
.ToIntRect(&neededIntRect
)) {
1159 return MapInputRectToSource(IN_TRANSFORM_IN
, neededIntRect
, aMax
,
1163 already_AddRefed
<DataSourceSurface
> FilterNodeTransformSoftware::Render(
1164 const IntRect
& aRect
) {
1165 IntRect srcRect
= SourceRectForOutputRect(aRect
);
1167 RefPtr
<DataSourceSurface
> input
=
1168 GetInputDataSourceSurface(IN_TRANSFORM_IN
, srcRect
);
1174 Matrix transform
= Matrix::Translation(srcRect
.X(), srcRect
.Y()) * mMatrix
*
1175 Matrix::Translation(-aRect
.X(), -aRect
.Y());
1176 if (transform
.IsIdentity() && srcRect
.Size() == aRect
.Size()) {
1177 return input
.forget();
1180 RefPtr
<DataSourceSurface
> surf
=
1181 Factory::CreateDataSourceSurface(aRect
.Size(), input
->GetFormat(), true);
1187 DataSourceSurface::MappedSurface mapping
;
1188 if (!surf
->Map(DataSourceSurface::MapType::WRITE
, &mapping
)) {
1190 << "FilterNodeTransformSoftware::Render failed to map surface";
1194 RefPtr
<DrawTarget
> dt
= Factory::CreateDrawTargetForData(
1195 BackendType::SKIA
, mapping
.mData
, surf
->GetSize(), mapping
.mStride
,
1198 gfxWarning() << "FilterNodeTransformSoftware::Render failed in "
1199 "CreateDrawTargetForData";
1203 Rect
r(0, 0, srcRect
.Width(), srcRect
.Height());
1204 dt
->SetTransform(transform
);
1205 dt
->DrawSurface(input
, r
, r
, DrawSurfaceOptions(mSamplingFilter
));
1209 return surf
.forget();
1212 void FilterNodeTransformSoftware::RequestFromInputsForRect(
1213 const IntRect
& aRect
) {
1214 RequestInputRect(IN_TRANSFORM_IN
, SourceRectForOutputRect(aRect
));
1217 IntRect
FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
1218 IntRect srcRect
= SourceRectForOutputRect(aRect
);
1219 if (srcRect
.IsEmpty()) {
1223 Rect outRect
= mMatrix
.TransformBounds(Rect(srcRect
));
1226 if (!outRect
.ToIntRect(&outIntRect
)) {
1229 return outIntRect
.Intersect(aRect
);
1232 FilterNodeMorphologySoftware::FilterNodeMorphologySoftware()
1233 : mOperator(MORPHOLOGY_OPERATOR_ERODE
) {}
1235 int32_t FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex
) {
1236 switch (aInputEnumIndex
) {
1237 case IN_MORPHOLOGY_IN
:
1244 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex
,
1245 const IntSize
& aRadii
) {
1246 MOZ_ASSERT(aIndex
== ATT_MORPHOLOGY_RADII
);
1247 mRadii
.width
= std::min(std::max(aRadii
.width
, 0), 100000);
1248 mRadii
.height
= std::min(std::max(aRadii
.height
, 0), 100000);
1252 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex
,
1253 uint32_t aOperator
) {
1254 MOZ_ASSERT(aIndex
== ATT_MORPHOLOGY_OPERATOR
);
1255 mOperator
= static_cast<MorphologyOperator
>(aOperator
);
1259 static already_AddRefed
<DataSourceSurface
> ApplyMorphology(
1260 const IntRect
& aSourceRect
, DataSourceSurface
* aInput
,
1261 const IntRect
& aDestRect
, int32_t rx
, int32_t ry
,
1262 MorphologyOperator aOperator
) {
1263 IntRect srcRect
= aSourceRect
- aDestRect
.TopLeft();
1264 IntRect destRect
= aDestRect
- aDestRect
.TopLeft();
1265 IntRect
tmpRect(destRect
.X(), srcRect
.Y(), destRect
.Width(),
1268 IntMargin margin
= srcRect
- destRect
;
1269 MOZ_ASSERT(margin
.top
>= ry
&& margin
.right
>= rx
&& margin
.bottom
>= ry
&&
1271 "insufficient margin");
1274 RefPtr
<DataSourceSurface
> tmp
;
1278 tmp
= Factory::CreateDataSourceSurface(tmpRect
.Size(),
1279 SurfaceFormat::B8G8R8A8
);
1280 if (MOZ2D_WARN_IF(!tmp
)) {
1284 DataSourceSurface::ScopedMap
sourceMap(aInput
, DataSourceSurface::READ
);
1285 DataSourceSurface::ScopedMap
tmpMap(tmp
, DataSourceSurface::WRITE
);
1286 if (MOZ2D_WARN_IF(!sourceMap
.IsMapped() || !tmpMap
.IsMapped())) {
1289 uint8_t* sourceData
= DataAtOffset(aInput
, sourceMap
.GetMappedSurface(),
1290 destRect
.TopLeft() - srcRect
.TopLeft());
1291 uint8_t* tmpData
= DataAtOffset(tmp
, tmpMap
.GetMappedSurface(),
1292 destRect
.TopLeft() - tmpRect
.TopLeft());
1294 FilterProcessing::ApplyMorphologyHorizontal(
1295 sourceData
, sourceMap
.GetStride(), tmpData
, tmpMap
.GetStride(), tmpRect
,
1299 RefPtr
<DataSourceSurface
> dest
;
1303 dest
= Factory::CreateDataSourceSurface(destRect
.Size(),
1304 SurfaceFormat::B8G8R8A8
);
1305 if (MOZ2D_WARN_IF(!dest
)) {
1309 DataSourceSurface::ScopedMap
tmpMap(tmp
, DataSourceSurface::READ
);
1310 DataSourceSurface::ScopedMap
destMap(dest
, DataSourceSurface::WRITE
);
1311 if (MOZ2D_WARN_IF(!tmpMap
.IsMapped() || !destMap
.IsMapped())) {
1314 int32_t tmpStride
= tmpMap
.GetStride();
1315 uint8_t* tmpData
= DataAtOffset(tmp
, tmpMap
.GetMappedSurface(),
1316 destRect
.TopLeft() - tmpRect
.TopLeft());
1318 int32_t destStride
= destMap
.GetStride();
1319 uint8_t* destData
= destMap
.GetData();
1321 FilterProcessing::ApplyMorphologyVertical(
1322 tmpData
, tmpStride
, destData
, destStride
, destRect
, ry
, aOperator
);
1325 return dest
.forget();
1328 already_AddRefed
<DataSourceSurface
> FilterNodeMorphologySoftware::Render(
1329 const IntRect
& aRect
) {
1330 IntRect srcRect
= aRect
;
1331 srcRect
.Inflate(mRadii
);
1333 RefPtr
<DataSourceSurface
> input
=
1334 GetInputDataSourceSurface(IN_MORPHOLOGY_IN
, srcRect
, NEED_COLOR_CHANNELS
);
1339 int32_t rx
= mRadii
.width
;
1340 int32_t ry
= mRadii
.height
;
1342 if (rx
== 0 && ry
== 0) {
1343 return input
.forget();
1346 return ApplyMorphology(srcRect
, input
, aRect
, rx
, ry
, mOperator
);
1349 void FilterNodeMorphologySoftware::RequestFromInputsForRect(
1350 const IntRect
& aRect
) {
1351 IntRect srcRect
= aRect
;
1352 srcRect
.Inflate(mRadii
);
1353 RequestInputRect(IN_MORPHOLOGY_IN
, srcRect
);
1356 IntRect
FilterNodeMorphologySoftware::GetOutputRectInRect(
1357 const IntRect
& aRect
) {
1358 IntRect inflatedSourceRect
= aRect
;
1359 inflatedSourceRect
.Inflate(mRadii
);
1360 IntRect inputRect
= GetInputRectInRect(IN_MORPHOLOGY_IN
, inflatedSourceRect
);
1361 if (mOperator
== MORPHOLOGY_OPERATOR_ERODE
) {
1362 inputRect
.Deflate(mRadii
);
1364 inputRect
.Inflate(mRadii
);
1366 return inputRect
.Intersect(aRect
);
1369 int32_t FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex
) {
1370 switch (aInputEnumIndex
) {
1371 case IN_COLOR_MATRIX_IN
:
1378 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex
,
1379 const Matrix5x4
& aMatrix
) {
1380 MOZ_ASSERT(aIndex
== ATT_COLOR_MATRIX_MATRIX
);
1385 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex
,
1386 uint32_t aAlphaMode
) {
1387 MOZ_ASSERT(aIndex
== ATT_COLOR_MATRIX_ALPHA_MODE
);
1388 mAlphaMode
= (AlphaMode
)aAlphaMode
;
1392 static already_AddRefed
<DataSourceSurface
> Premultiply(
1393 DataSourceSurface
* aSurface
) {
1394 if (aSurface
->GetFormat() == SurfaceFormat::A8
) {
1395 RefPtr
<DataSourceSurface
> surface(aSurface
);
1396 return surface
.forget();
1399 IntSize size
= aSurface
->GetSize();
1400 RefPtr
<DataSourceSurface
> target
=
1401 Factory::CreateDataSourceSurface(size
, SurfaceFormat::B8G8R8A8
);
1402 if (MOZ2D_WARN_IF(!target
)) {
1406 DataSourceSurface::ScopedMap
inputMap(aSurface
, DataSourceSurface::READ
);
1407 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
1408 if (MOZ2D_WARN_IF(!inputMap
.IsMapped() || !targetMap
.IsMapped())) {
1412 uint8_t* inputData
= inputMap
.GetData();
1413 int32_t inputStride
= inputMap
.GetStride();
1414 uint8_t* targetData
= targetMap
.GetData();
1415 int32_t targetStride
= targetMap
.GetStride();
1417 FilterProcessing::DoPremultiplicationCalculation(
1418 size
, targetData
, targetStride
, inputData
, inputStride
);
1420 return target
.forget();
1423 static already_AddRefed
<DataSourceSurface
> Unpremultiply(
1424 DataSourceSurface
* aSurface
) {
1425 if (aSurface
->GetFormat() == SurfaceFormat::A8
) {
1426 RefPtr
<DataSourceSurface
> surface(aSurface
);
1427 return surface
.forget();
1430 IntSize size
= aSurface
->GetSize();
1431 RefPtr
<DataSourceSurface
> target
=
1432 Factory::CreateDataSourceSurface(size
, SurfaceFormat::B8G8R8A8
);
1433 if (MOZ2D_WARN_IF(!target
)) {
1437 DataSourceSurface::ScopedMap
inputMap(aSurface
, DataSourceSurface::READ
);
1438 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
1439 if (MOZ2D_WARN_IF(!inputMap
.IsMapped() || !targetMap
.IsMapped())) {
1443 uint8_t* inputData
= inputMap
.GetData();
1444 int32_t inputStride
= inputMap
.GetStride();
1445 uint8_t* targetData
= targetMap
.GetData();
1446 int32_t targetStride
= targetMap
.GetStride();
1448 FilterProcessing::DoUnpremultiplicationCalculation(
1449 size
, targetData
, targetStride
, inputData
, inputStride
);
1451 return target
.forget();
1454 static already_AddRefed
<DataSourceSurface
> Opacity(DataSourceSurface
* aSurface
,
1456 if (aValue
== 1.0f
) {
1457 RefPtr
<DataSourceSurface
> surface(aSurface
);
1458 return surface
.forget();
1461 IntSize size
= aSurface
->GetSize();
1462 RefPtr
<DataSourceSurface
> target
=
1463 Factory::CreateDataSourceSurface(size
, aSurface
->GetFormat());
1464 if (MOZ2D_WARN_IF(!target
)) {
1468 DataSourceSurface::ScopedMap
inputMap(aSurface
, DataSourceSurface::READ
);
1469 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
1470 if (MOZ2D_WARN_IF(!inputMap
.IsMapped() || !targetMap
.IsMapped())) {
1474 uint8_t* inputData
= inputMap
.GetData();
1475 int32_t inputStride
= inputMap
.GetStride();
1476 uint8_t* targetData
= targetMap
.GetData();
1477 int32_t targetStride
= targetMap
.GetStride();
1479 if (aSurface
->GetFormat() == SurfaceFormat::A8
) {
1480 FilterProcessing::DoOpacityCalculationA8(size
, targetData
, targetStride
,
1481 inputData
, inputStride
, aValue
);
1483 MOZ_ASSERT(aSurface
->GetFormat() == SurfaceFormat::B8G8R8A8
);
1484 FilterProcessing::DoOpacityCalculation(size
, targetData
, targetStride
,
1485 inputData
, inputStride
, aValue
);
1488 return target
.forget();
1491 already_AddRefed
<DataSourceSurface
> FilterNodeColorMatrixSoftware::Render(
1492 const IntRect
& aRect
) {
1493 RefPtr
<DataSourceSurface
> input
=
1494 GetInputDataSourceSurface(IN_COLOR_MATRIX_IN
, aRect
, NEED_COLOR_CHANNELS
);
1499 if (mAlphaMode
== ALPHA_MODE_PREMULTIPLIED
) {
1500 input
= Unpremultiply(input
);
1503 RefPtr
<DataSourceSurface
> result
=
1504 FilterProcessing::ApplyColorMatrix(input
, mMatrix
);
1506 if (mAlphaMode
== ALPHA_MODE_PREMULTIPLIED
) {
1507 result
= Premultiply(result
);
1510 return result
.forget();
1513 void FilterNodeColorMatrixSoftware::RequestFromInputsForRect(
1514 const IntRect
& aRect
) {
1515 RequestInputRect(IN_COLOR_MATRIX_IN
, aRect
);
1518 IntRect
FilterNodeColorMatrixSoftware::MapRectToSource(
1519 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
1520 return MapInputRectToSource(IN_COLOR_MATRIX_IN
, aRect
, aMax
, aSourceNode
);
1523 IntRect
FilterNodeColorMatrixSoftware::GetOutputRectInRect(
1524 const IntRect
& aRect
) {
1525 if (mMatrix
._54
> 0.0f
) {
1528 return GetInputRectInRect(IN_COLOR_MATRIX_IN
, aRect
);
1531 void FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex
,
1532 const DeviceColor
& aColor
) {
1533 MOZ_ASSERT(aIndex
== ATT_FLOOD_COLOR
);
1538 static uint32_t ColorToBGRA(const DeviceColor
& aColor
) {
1541 uint8_t components
[4];
1543 components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] =
1544 NS_lround(aColor
.r
* aColor
.a
* 255.0f
);
1545 components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] =
1546 NS_lround(aColor
.g
* aColor
.a
* 255.0f
);
1547 components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] =
1548 NS_lround(aColor
.b
* aColor
.a
* 255.0f
);
1549 components
[B8G8R8A8_COMPONENT_BYTEOFFSET_A
] = NS_lround(aColor
.a
* 255.0f
);
1553 static SurfaceFormat
FormatForColor(DeviceColor aColor
) {
1554 if (aColor
.r
== 0 && aColor
.g
== 0 && aColor
.b
== 0) {
1555 return SurfaceFormat::A8
;
1557 return SurfaceFormat::B8G8R8A8
;
1560 already_AddRefed
<DataSourceSurface
> FilterNodeFloodSoftware::Render(
1561 const IntRect
& aRect
) {
1562 SurfaceFormat format
= FormatForColor(mColor
);
1563 RefPtr
<DataSourceSurface
> target
=
1564 Factory::CreateDataSourceSurface(aRect
.Size(), format
);
1565 if (MOZ2D_WARN_IF(!target
)) {
1569 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
1570 if (MOZ2D_WARN_IF(!targetMap
.IsMapped())) {
1574 uint8_t* targetData
= targetMap
.GetData();
1575 int32_t stride
= targetMap
.GetStride();
1577 if (format
== SurfaceFormat::B8G8R8A8
) {
1578 uint32_t color
= ColorToBGRA(mColor
);
1579 for (int32_t y
= 0; y
< aRect
.Height(); y
++) {
1580 for (int32_t x
= 0; x
< aRect
.Width(); x
++) {
1581 *((uint32_t*)targetData
+ x
) = color
;
1583 PodZero(&targetData
[aRect
.Width() * 4], stride
- aRect
.Width() * 4);
1584 targetData
+= stride
;
1586 } else if (format
== SurfaceFormat::A8
) {
1587 uint8_t alpha
= NS_lround(mColor
.a
* 255.0f
);
1588 for (int32_t y
= 0; y
< aRect
.Height(); y
++) {
1589 for (int32_t x
= 0; x
< aRect
.Width(); x
++) {
1590 targetData
[x
] = alpha
;
1592 PodZero(&targetData
[aRect
.Width()], stride
- aRect
.Width());
1593 targetData
+= stride
;
1596 gfxDevCrash(LogReason::FilterInputFormat
)
1597 << "Bad format in flood render " << (int)format
;
1601 return target
.forget();
1604 // Override GetOutput to get around caching. Rendering simple floods is
1605 // comparatively fast.
1606 already_AddRefed
<DataSourceSurface
> FilterNodeFloodSoftware::GetOutput(
1607 const IntRect
& aRect
) {
1608 return Render(aRect
);
1611 IntRect
FilterNodeFloodSoftware::MapRectToSource(const IntRect
& aRect
,
1612 const IntRect
& aMax
,
1613 FilterNode
* aSourceNode
) {
1617 IntRect
FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
1618 if (mColor
.a
== 0.0f
) {
1624 int32_t FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex
) {
1625 switch (aInputEnumIndex
) {
1633 void FilterNodeTileSoftware::SetAttribute(uint32_t aIndex
,
1634 const IntRect
& aSourceRect
) {
1635 MOZ_ASSERT(aIndex
== ATT_TILE_SOURCE_RECT
);
1636 mSourceRect
.SetRect(int32_t(aSourceRect
.X()), int32_t(aSourceRect
.Y()),
1637 int32_t(aSourceRect
.Width()),
1638 int32_t(aSourceRect
.Height()));
1643 struct CompareIntRects
{
1644 bool operator()(const IntRect
& a
, const IntRect
& b
) const {
1645 if (a
.X() != b
.X()) {
1646 return a
.X() < b
.X();
1648 if (a
.Y() != b
.Y()) {
1649 return a
.Y() < b
.Y();
1651 if (a
.Width() != b
.Width()) {
1652 return a
.Width() < b
.Width();
1654 return a
.Height() < b
.Height();
1660 already_AddRefed
<DataSourceSurface
> FilterNodeTileSoftware::Render(
1661 const IntRect
& aRect
) {
1662 if (mSourceRect
.IsEmpty()) {
1666 if (mSourceRect
.Contains(aRect
)) {
1667 return GetInputDataSourceSurface(IN_TILE_IN
, aRect
);
1670 RefPtr
<DataSourceSurface
> target
;
1672 typedef std::map
<IntRect
, RefPtr
<DataSourceSurface
>, CompareIntRects
>
1676 IntPoint startIndex
= TileIndex(mSourceRect
, aRect
.TopLeft());
1677 IntPoint endIndex
= TileIndex(mSourceRect
, aRect
.BottomRight());
1678 for (int32_t ix
= startIndex
.x
; ix
<= endIndex
.x
; ix
++) {
1679 for (int32_t iy
= startIndex
.y
; iy
<= endIndex
.y
; iy
++) {
1680 IntPoint
sourceToDestOffset(ix
* mSourceRect
.Width(),
1681 iy
* mSourceRect
.Height());
1682 IntRect destRect
= aRect
.Intersect(mSourceRect
+ sourceToDestOffset
);
1683 IntRect srcRect
= destRect
- sourceToDestOffset
;
1684 if (srcRect
.IsEmpty()) {
1688 RefPtr
<DataSourceSurface
> input
;
1689 InputMap::iterator it
= inputs
.find(srcRect
);
1690 if (it
== inputs
.end()) {
1691 input
= GetInputDataSourceSurface(IN_TILE_IN
, srcRect
);
1692 inputs
[srcRect
] = input
;
1700 // We delay creating the target until now because we want to use the
1701 // same format as our input filter, and we do not actually know the
1702 // input format before we call GetInputDataSourceSurface.
1704 Factory::CreateDataSourceSurface(aRect
.Size(), input
->GetFormat());
1705 if (MOZ2D_WARN_IF(!target
)) {
1710 if (input
->GetFormat() != target
->GetFormat()) {
1711 // Different rectangles of the input can have different formats. If
1712 // that happens, just convert everything to B8G8R8A8.
1713 target
= FilterProcessing::ConvertToB8G8R8A8(target
);
1714 input
= FilterProcessing::ConvertToB8G8R8A8(input
);
1715 if (MOZ2D_WARN_IF(!target
) || MOZ2D_WARN_IF(!input
)) {
1720 CopyRect(input
, target
, srcRect
- srcRect
.TopLeft(),
1721 destRect
.TopLeft() - aRect
.TopLeft());
1725 return target
.forget();
1728 void FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect
& aRect
) {
1729 // Do not request anything.
1730 // Source rects for the tile filter can be discontinuous with large gaps
1731 // between them. Requesting those from our input filter might cause it to
1732 // render the whole bounding box of all of them, which would be wasteful.
1735 IntRect
FilterNodeTileSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
1739 FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware()
1740 : mDisableR(true), mDisableG(true), mDisableB(true), mDisableA(true) {}
1742 void FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex
,
1745 case ATT_TRANSFER_DISABLE_R
:
1746 mDisableR
= aDisable
;
1748 case ATT_TRANSFER_DISABLE_G
:
1749 mDisableG
= aDisable
;
1751 case ATT_TRANSFER_DISABLE_B
:
1752 mDisableB
= aDisable
;
1754 case ATT_TRANSFER_DISABLE_A
:
1755 mDisableA
= aDisable
;
1758 MOZ_CRASH("GFX: FilterNodeComponentTransferSoftware::SetAttribute");
1763 void FilterNodeComponentTransferSoftware::GenerateLookupTable(
1764 ptrdiff_t aComponent
, uint8_t aTables
[4][256], bool aDisabled
) {
1766 for (int32_t i
= 0; i
< 256; ++i
) {
1767 aTables
[aComponent
][i
] = i
;
1770 FillLookupTable(aComponent
, aTables
[aComponent
]);
1774 template <uint32_t BytesPerPixel
>
1775 static void TransferComponents(
1776 DataSourceSurface
* aInput
, DataSourceSurface
* aTarget
,
1777 const uint8_t aLookupTables
[BytesPerPixel
][256]) {
1778 MOZ_ASSERT(aInput
->GetFormat() == aTarget
->GetFormat(), "different formats");
1779 IntSize size
= aInput
->GetSize();
1781 DataSourceSurface::ScopedMap
sourceMap(aInput
, DataSourceSurface::READ
);
1782 DataSourceSurface::ScopedMap
targetMap(aTarget
, DataSourceSurface::WRITE
);
1783 if (MOZ2D_WARN_IF(!sourceMap
.IsMapped() || !targetMap
.IsMapped())) {
1787 uint8_t* sourceData
= sourceMap
.GetData();
1788 int32_t sourceStride
= sourceMap
.GetStride();
1789 uint8_t* targetData
= targetMap
.GetData();
1790 int32_t targetStride
= targetMap
.GetStride();
1792 MOZ_ASSERT(sourceStride
<= targetStride
, "target smaller than source");
1794 for (int32_t y
= 0; y
< size
.height
; y
++) {
1795 for (int32_t x
= 0; x
< size
.width
; x
++) {
1796 uint32_t sourceIndex
= y
* sourceStride
+ x
* BytesPerPixel
;
1797 uint32_t targetIndex
= y
* targetStride
+ x
* BytesPerPixel
;
1798 for (uint32_t i
= 0; i
< BytesPerPixel
; i
++) {
1799 targetData
[targetIndex
+ i
] =
1800 aLookupTables
[i
][sourceData
[sourceIndex
+ i
]];
1804 // Zero padding to keep valgrind happy.
1805 PodZero(&targetData
[y
* targetStride
+ size
.width
* BytesPerPixel
],
1806 targetStride
- size
.width
* BytesPerPixel
);
1810 static bool IsAllZero(const uint8_t aLookupTable
[256]) {
1811 for (int32_t i
= 0; i
< 256; i
++) {
1812 if (aLookupTable
[i
] != 0) {
1819 already_AddRefed
<DataSourceSurface
> FilterNodeComponentTransferSoftware::Render(
1820 const IntRect
& aRect
) {
1821 if (mDisableR
&& mDisableG
&& mDisableB
&& mDisableA
) {
1822 return GetInputDataSourceSurface(IN_TRANSFER_IN
, aRect
);
1825 uint8_t lookupTables
[4][256];
1826 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R
, lookupTables
, mDisableR
);
1827 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G
, lookupTables
, mDisableG
);
1828 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B
, lookupTables
, mDisableB
);
1829 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A
, lookupTables
, mDisableA
);
1831 bool needColorChannels
=
1832 lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
][0] != 0 ||
1833 lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
][0] != 0 ||
1834 lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
][0] != 0;
1836 FormatHint pref
= needColorChannels
? NEED_COLOR_CHANNELS
: CAN_HANDLE_A8
;
1838 RefPtr
<DataSourceSurface
> input
=
1839 GetInputDataSourceSurface(IN_TRANSFER_IN
, aRect
, pref
);
1844 if (input
->GetFormat() == SurfaceFormat::B8G8R8A8
&& !needColorChannels
) {
1845 bool colorChannelsBecomeBlack
=
1846 IsAllZero(lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
]) &&
1847 IsAllZero(lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
]) &&
1848 IsAllZero(lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
]);
1850 if (colorChannelsBecomeBlack
) {
1851 input
= FilterProcessing::ExtractAlpha(input
);
1855 SurfaceFormat format
= input
->GetFormat();
1856 if (format
== SurfaceFormat::A8
&& mDisableA
) {
1857 return input
.forget();
1860 RefPtr
<DataSourceSurface
> target
=
1861 Factory::CreateDataSourceSurface(aRect
.Size(), format
);
1862 if (MOZ2D_WARN_IF(!target
)) {
1866 if (format
== SurfaceFormat::A8
) {
1867 TransferComponents
<1>(input
, target
,
1868 &lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_A
]);
1870 TransferComponents
<4>(input
, target
, lookupTables
);
1873 return target
.forget();
1876 void FilterNodeComponentTransferSoftware::RequestFromInputsForRect(
1877 const IntRect
& aRect
) {
1878 RequestInputRect(IN_TRANSFER_IN
, aRect
);
1881 IntRect
FilterNodeComponentTransferSoftware::MapRectToSource(
1882 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
1883 return MapInputRectToSource(IN_TRANSFER_IN
, aRect
, aMax
, aSourceNode
);
1886 IntRect
FilterNodeComponentTransferSoftware::GetOutputRectInRect(
1887 const IntRect
& aRect
) {
1889 return GetInputRectInRect(IN_TRANSFER_IN
, aRect
);
1894 int32_t FilterNodeComponentTransferSoftware::InputIndex(
1895 uint32_t aInputEnumIndex
) {
1896 switch (aInputEnumIndex
) {
1897 case IN_TRANSFER_IN
:
1904 void FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex
,
1905 const Float
* aFloat
,
1907 std::vector
<Float
> table(aFloat
, aFloat
+ aSize
);
1909 case ATT_TABLE_TRANSFER_TABLE_R
:
1912 case ATT_TABLE_TRANSFER_TABLE_G
:
1915 case ATT_TABLE_TRANSFER_TABLE_B
:
1918 case ATT_TABLE_TRANSFER_TABLE_A
:
1922 MOZ_CRASH("GFX: FilterNodeTableTransferSoftware::SetAttribute");
1927 void FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent
,
1928 uint8_t aTable
[256]) {
1929 switch (aComponent
) {
1930 case B8G8R8A8_COMPONENT_BYTEOFFSET_R
:
1931 FillLookupTableImpl(mTableR
, aTable
);
1933 case B8G8R8A8_COMPONENT_BYTEOFFSET_G
:
1934 FillLookupTableImpl(mTableG
, aTable
);
1936 case B8G8R8A8_COMPONENT_BYTEOFFSET_B
:
1937 FillLookupTableImpl(mTableB
, aTable
);
1939 case B8G8R8A8_COMPONENT_BYTEOFFSET_A
:
1940 FillLookupTableImpl(mTableA
, aTable
);
1943 MOZ_ASSERT(false, "unknown component");
1948 void FilterNodeTableTransferSoftware::FillLookupTableImpl(
1949 std::vector
<Float
>& aTableValues
, uint8_t aTable
[256]) {
1950 uint32_t tvLength
= aTableValues
.size();
1955 for (size_t i
= 0; i
< 256; i
++) {
1956 uint32_t k
= (i
* (tvLength
- 1)) / 255;
1957 Float v1
= aTableValues
[k
];
1958 Float v2
= aTableValues
[std::min(k
+ 1, tvLength
- 1)];
1959 int32_t val
= int32_t(255 * (v1
+ (i
/ 255.0f
- k
/ float(tvLength
- 1)) *
1960 (tvLength
- 1) * (v2
- v1
)));
1961 val
= std::min(255, val
);
1962 val
= std::max(0, val
);
1967 void FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex
,
1968 const Float
* aFloat
,
1970 std::vector
<Float
> discrete(aFloat
, aFloat
+ aSize
);
1972 case ATT_DISCRETE_TRANSFER_TABLE_R
:
1975 case ATT_DISCRETE_TRANSFER_TABLE_G
:
1978 case ATT_DISCRETE_TRANSFER_TABLE_B
:
1981 case ATT_DISCRETE_TRANSFER_TABLE_A
:
1985 MOZ_CRASH("GFX: FilterNodeDiscreteTransferSoftware::SetAttribute");
1990 void FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent
,
1991 uint8_t aTable
[256]) {
1992 switch (aComponent
) {
1993 case B8G8R8A8_COMPONENT_BYTEOFFSET_R
:
1994 FillLookupTableImpl(mTableR
, aTable
);
1996 case B8G8R8A8_COMPONENT_BYTEOFFSET_G
:
1997 FillLookupTableImpl(mTableG
, aTable
);
1999 case B8G8R8A8_COMPONENT_BYTEOFFSET_B
:
2000 FillLookupTableImpl(mTableB
, aTable
);
2002 case B8G8R8A8_COMPONENT_BYTEOFFSET_A
:
2003 FillLookupTableImpl(mTableA
, aTable
);
2006 MOZ_ASSERT(false, "unknown component");
2011 void FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(
2012 std::vector
<Float
>& aTableValues
, uint8_t aTable
[256]) {
2013 uint32_t tvLength
= aTableValues
.size();
2018 for (size_t i
= 0; i
< 256; i
++) {
2019 uint32_t k
= (i
* tvLength
) / 255;
2020 k
= std::min(k
, tvLength
- 1);
2021 Float v
= aTableValues
[k
];
2022 int32_t val
= NS_lround(255 * v
);
2023 val
= std::min(255, val
);
2024 val
= std::max(0, val
);
2029 FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware()
2039 void FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex
,
2042 case ATT_LINEAR_TRANSFER_SLOPE_R
:
2045 case ATT_LINEAR_TRANSFER_INTERCEPT_R
:
2046 mInterceptR
= aValue
;
2048 case ATT_LINEAR_TRANSFER_SLOPE_G
:
2051 case ATT_LINEAR_TRANSFER_INTERCEPT_G
:
2052 mInterceptG
= aValue
;
2054 case ATT_LINEAR_TRANSFER_SLOPE_B
:
2057 case ATT_LINEAR_TRANSFER_INTERCEPT_B
:
2058 mInterceptB
= aValue
;
2060 case ATT_LINEAR_TRANSFER_SLOPE_A
:
2063 case ATT_LINEAR_TRANSFER_INTERCEPT_A
:
2064 mInterceptA
= aValue
;
2067 MOZ_CRASH("GFX: FilterNodeLinearTransferSoftware::SetAttribute");
2072 void FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent
,
2073 uint8_t aTable
[256]) {
2074 switch (aComponent
) {
2075 case B8G8R8A8_COMPONENT_BYTEOFFSET_R
:
2076 FillLookupTableImpl(mSlopeR
, mInterceptR
, aTable
);
2078 case B8G8R8A8_COMPONENT_BYTEOFFSET_G
:
2079 FillLookupTableImpl(mSlopeG
, mInterceptG
, aTable
);
2081 case B8G8R8A8_COMPONENT_BYTEOFFSET_B
:
2082 FillLookupTableImpl(mSlopeB
, mInterceptB
, aTable
);
2084 case B8G8R8A8_COMPONENT_BYTEOFFSET_A
:
2085 FillLookupTableImpl(mSlopeA
, mInterceptA
, aTable
);
2088 MOZ_ASSERT(false, "unknown component");
2093 void FilterNodeLinearTransferSoftware::FillLookupTableImpl(
2094 Float aSlope
, Float aIntercept
, uint8_t aTable
[256]) {
2095 for (size_t i
= 0; i
< 256; i
++) {
2096 int32_t val
= NS_lround(aSlope
* i
+ 255 * aIntercept
);
2097 val
= std::min(255, val
);
2098 val
= std::max(0, val
);
2103 FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware()
2117 void FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex
,
2120 case ATT_GAMMA_TRANSFER_AMPLITUDE_R
:
2121 mAmplitudeR
= aValue
;
2123 case ATT_GAMMA_TRANSFER_EXPONENT_R
:
2124 mExponentR
= aValue
;
2126 case ATT_GAMMA_TRANSFER_OFFSET_R
:
2129 case ATT_GAMMA_TRANSFER_AMPLITUDE_G
:
2130 mAmplitudeG
= aValue
;
2132 case ATT_GAMMA_TRANSFER_EXPONENT_G
:
2133 mExponentG
= aValue
;
2135 case ATT_GAMMA_TRANSFER_OFFSET_G
:
2138 case ATT_GAMMA_TRANSFER_AMPLITUDE_B
:
2139 mAmplitudeB
= aValue
;
2141 case ATT_GAMMA_TRANSFER_EXPONENT_B
:
2142 mExponentB
= aValue
;
2144 case ATT_GAMMA_TRANSFER_OFFSET_B
:
2147 case ATT_GAMMA_TRANSFER_AMPLITUDE_A
:
2148 mAmplitudeA
= aValue
;
2150 case ATT_GAMMA_TRANSFER_EXPONENT_A
:
2151 mExponentA
= aValue
;
2153 case ATT_GAMMA_TRANSFER_OFFSET_A
:
2157 MOZ_CRASH("GFX: FilterNodeGammaTransferSoftware::SetAttribute");
2162 void FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent
,
2163 uint8_t aTable
[256]) {
2164 switch (aComponent
) {
2165 case B8G8R8A8_COMPONENT_BYTEOFFSET_R
:
2166 FillLookupTableImpl(mAmplitudeR
, mExponentR
, mOffsetR
, aTable
);
2168 case B8G8R8A8_COMPONENT_BYTEOFFSET_G
:
2169 FillLookupTableImpl(mAmplitudeG
, mExponentG
, mOffsetG
, aTable
);
2171 case B8G8R8A8_COMPONENT_BYTEOFFSET_B
:
2172 FillLookupTableImpl(mAmplitudeB
, mExponentB
, mOffsetB
, aTable
);
2174 case B8G8R8A8_COMPONENT_BYTEOFFSET_A
:
2175 FillLookupTableImpl(mAmplitudeA
, mExponentA
, mOffsetA
, aTable
);
2178 MOZ_ASSERT(false, "unknown component");
2183 void FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude
,
2186 uint8_t aTable
[256]) {
2187 for (size_t i
= 0; i
< 256; i
++) {
2189 NS_lround(255 * (aAmplitude
* pow(i
/ 255.0f
, aExponent
) + aOffset
));
2190 val
= std::min(255, val
);
2191 val
= std::max(0, val
);
2196 FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware()
2199 mEdgeMode(EDGE_MODE_DUPLICATE
),
2200 mPreserveAlpha(false) {}
2202 int32_t FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex
) {
2203 switch (aInputEnumIndex
) {
2204 case IN_CONVOLVE_MATRIX_IN
:
2211 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2212 uint32_t aIndex
, const IntSize
& aKernelSize
) {
2213 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_KERNEL_SIZE
);
2214 mKernelSize
= aKernelSize
;
2218 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex
,
2219 const Float
* aMatrix
,
2221 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_KERNEL_MATRIX
);
2222 mKernelMatrix
= std::vector
<Float
>(aMatrix
, aMatrix
+ aSize
);
2226 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex
,
2229 case ATT_CONVOLVE_MATRIX_DIVISOR
:
2232 case ATT_CONVOLVE_MATRIX_BIAS
:
2236 MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
2241 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2242 uint32_t aIndex
, const Size
& aKernelUnitLength
) {
2244 case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH
:
2245 mKernelUnitLength
= aKernelUnitLength
;
2248 MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
2253 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex
,
2254 const IntPoint
& aTarget
) {
2255 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_TARGET
);
2260 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2261 uint32_t aIndex
, const IntRect
& aSourceRect
) {
2262 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_SOURCE_RECT
);
2263 mSourceRect
= aSourceRect
;
2267 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex
,
2268 uint32_t aEdgeMode
) {
2269 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_EDGE_MODE
);
2270 mEdgeMode
= static_cast<ConvolveMatrixEdgeMode
>(aEdgeMode
);
2274 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex
,
2275 bool aPreserveAlpha
) {
2276 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA
);
2277 mPreserveAlpha
= aPreserveAlpha
;
2282 static inline void DebugOnlyCheckColorSamplingAccess(
2283 const uint8_t* aSampleAddress
, const uint8_t* aBoundsBegin
,
2284 const uint8_t* aBoundsEnd
) {
2285 MOZ_ASSERT(aSampleAddress
>= aBoundsBegin
, "accessing before start");
2286 MOZ_ASSERT(aSampleAddress
< aBoundsEnd
, "accessing after end");
2289 # define DebugOnlyCheckColorSamplingAccess(address, boundsBegin, boundsEnd)
2292 static inline uint8_t ColorComponentAtPoint(const uint8_t* aData
,
2294 const uint8_t* aBoundsBegin
,
2295 const uint8_t* aBoundsEnd
,
2296 int32_t x
, int32_t y
, ptrdiff_t bpp
,
2298 DebugOnlyCheckColorSamplingAccess(&aData
[y
* aStride
+ bpp
* x
+ c
],
2299 aBoundsBegin
, aBoundsEnd
);
2300 return aData
[y
* aStride
+ bpp
* x
+ c
];
2303 static inline int32_t ColorAtPoint(const uint8_t* aData
, ptrdiff_t aStride
,
2304 const uint8_t* aBoundsBegin
,
2305 const uint8_t* aBoundsEnd
, int32_t x
,
2307 DebugOnlyCheckColorSamplingAccess(aData
+ y
* aStride
+ 4 * x
, aBoundsBegin
,
2309 return *(uint32_t*)(aData
+ y
* aStride
+ 4 * x
);
2312 // Accepts fractional x & y and does bilinear interpolation.
2313 // Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible.
2314 static inline uint8_t ColorComponentAtPoint(
2315 const uint8_t* aData
, ptrdiff_t aStride
, const uint8_t* aBoundsBegin
,
2316 const uint8_t* aBoundsEnd
, Float x
, Float y
, ptrdiff_t bpp
, ptrdiff_t c
) {
2317 const uint32_t f
= 256;
2318 const int32_t lx
= floor(x
);
2319 const int32_t ly
= floor(y
);
2320 const int32_t tux
= uint32_t((x
- lx
) * f
);
2321 const int32_t tlx
= f
- tux
;
2322 const int32_t tuy
= uint32_t((y
- ly
) * f
);
2323 const int32_t tly
= f
- tuy
;
2324 const uint8_t& cll
= ColorComponentAtPoint(aData
, aStride
, aBoundsBegin
,
2325 aBoundsEnd
, lx
, ly
, bpp
, c
);
2326 const uint8_t& cul
= ColorComponentAtPoint(aData
, aStride
, aBoundsBegin
,
2327 aBoundsEnd
, lx
+ 1, ly
, bpp
, c
);
2328 const uint8_t& clu
= ColorComponentAtPoint(aData
, aStride
, aBoundsBegin
,
2329 aBoundsEnd
, lx
, ly
+ 1, bpp
, c
);
2330 const uint8_t& cuu
= ColorComponentAtPoint(
2331 aData
, aStride
, aBoundsBegin
, aBoundsEnd
, lx
+ 1, ly
+ 1, bpp
, c
);
2332 return ((cll
* tlx
+ cul
* tux
) * tly
+ (clu
* tlx
+ cuu
* tux
) * tuy
+
2337 static int32_t ClampToNonZero(int32_t a
) { return a
* (a
>= 0); }
2339 template <typename CoordType
>
2340 static void ConvolvePixel(const uint8_t* aSourceData
, uint8_t* aTargetData
,
2341 int32_t aWidth
, int32_t aHeight
,
2342 int32_t aSourceStride
, int32_t aTargetStride
,
2343 const uint8_t* aSourceBegin
,
2344 const uint8_t* aSourceEnd
, int32_t aX
, int32_t aY
,
2345 const int32_t* aKernel
, int32_t aBias
, int32_t shiftL
,
2346 int32_t shiftR
, bool aPreserveAlpha
, int32_t aOrderX
,
2347 int32_t aOrderY
, int32_t aTargetX
, int32_t aTargetY
,
2348 CoordType aKernelUnitLengthX
,
2349 CoordType aKernelUnitLengthY
) {
2350 int32_t sum
[4] = {0, 0, 0, 0};
2351 int32_t offsets
[4] = {
2352 B8G8R8A8_COMPONENT_BYTEOFFSET_R
, B8G8R8A8_COMPONENT_BYTEOFFSET_G
,
2353 B8G8R8A8_COMPONENT_BYTEOFFSET_B
, B8G8R8A8_COMPONENT_BYTEOFFSET_A
};
2354 int32_t channels
= aPreserveAlpha
? 3 : 4;
2355 int32_t roundingAddition
= shiftL
== 0 ? 0 : 1 << (shiftL
- 1);
2357 for (int32_t y
= 0; y
< aOrderY
; y
++) {
2358 CoordType sampleY
= aY
+ (y
- aTargetY
) * aKernelUnitLengthY
;
2359 for (int32_t x
= 0; x
< aOrderX
; x
++) {
2360 CoordType sampleX
= aX
+ (x
- aTargetX
) * aKernelUnitLengthX
;
2361 for (int32_t i
= 0; i
< channels
; i
++) {
2363 aKernel
[aOrderX
* y
+ x
] *
2364 ColorComponentAtPoint(aSourceData
, aSourceStride
, aSourceBegin
,
2365 aSourceEnd
, sampleX
, sampleY
, 4, offsets
[i
]);
2369 for (int32_t i
= 0; i
< channels
; i
++) {
2371 umin(ClampToNonZero(sum
[i
] + aBias
), 255 << shiftL
>> shiftR
);
2372 aTargetData
[aY
* aTargetStride
+ 4 * aX
+ offsets
[i
]] =
2373 (clamped
+ roundingAddition
) << shiftR
>> shiftL
;
2375 if (aPreserveAlpha
) {
2376 aTargetData
[aY
* aTargetStride
+ 4 * aX
+ B8G8R8A8_COMPONENT_BYTEOFFSET_A
] =
2377 aSourceData
[aY
* aSourceStride
+ 4 * aX
+
2378 B8G8R8A8_COMPONENT_BYTEOFFSET_A
];
2382 already_AddRefed
<DataSourceSurface
> FilterNodeConvolveMatrixSoftware::Render(
2383 const IntRect
& aRect
) {
2384 if (mKernelUnitLength
.width
== floor(mKernelUnitLength
.width
) &&
2385 mKernelUnitLength
.height
== floor(mKernelUnitLength
.height
)) {
2386 return DoRender(aRect
, (int32_t)mKernelUnitLength
.width
,
2387 (int32_t)mKernelUnitLength
.height
);
2389 return DoRender(aRect
, mKernelUnitLength
.width
, mKernelUnitLength
.height
);
2392 static std::vector
<Float
> ReversedVector(const std::vector
<Float
>& aVector
) {
2393 size_t length
= aVector
.size();
2394 std::vector
<Float
> result(length
, 0);
2395 for (size_t i
= 0; i
< length
; i
++) {
2396 result
[length
- 1 - i
] = aVector
[i
];
2401 static std::vector
<Float
> ScaledVector(const std::vector
<Float
>& aVector
,
2403 size_t length
= aVector
.size();
2404 std::vector
<Float
> result(length
, 0);
2405 for (size_t i
= 0; i
< length
; i
++) {
2406 result
[i
] = aVector
[i
] / aDivisor
;
2411 static Float
MaxVectorSum(const std::vector
<Float
>& aVector
) {
2413 size_t length
= aVector
.size();
2414 for (size_t i
= 0; i
< length
; i
++) {
2415 if (aVector
[i
] > 0) {
2422 // Returns shiftL and shiftR in such a way that
2423 // a << shiftL >> shiftR is roughly a * aFloat.
2424 static void TranslateDoubleToShifts(double aDouble
, int32_t& aShiftL
,
2429 MOZ_CRASH("GFX: TranslateDoubleToShifts");
2432 while (1 << (aShiftR
+ 1) < 1 / aDouble
) {
2436 while (1 << (aShiftL
+ 1) < aDouble
) {
2442 template <typename CoordType
>
2443 already_AddRefed
<DataSourceSurface
> FilterNodeConvolveMatrixSoftware::DoRender(
2444 const IntRect
& aRect
, CoordType aKernelUnitLengthX
,
2445 CoordType aKernelUnitLengthY
) {
2446 if (mKernelSize
.width
<= 0 || mKernelSize
.height
<= 0 ||
2447 mKernelMatrix
.size() !=
2448 uint32_t(mKernelSize
.width
* mKernelSize
.height
) ||
2449 !IntRect(IntPoint(0, 0), mKernelSize
).Contains(mTarget
) ||
2451 return Factory::CreateDataSourceSurface(aRect
.Size(),
2452 SurfaceFormat::B8G8R8A8
, true);
2455 IntRect srcRect
= InflatedSourceRect(aRect
);
2457 // Inflate the source rect by another pixel because the bilinear filtering in
2458 // ColorComponentAtPoint may want to access the margins.
2461 RefPtr
<DataSourceSurface
> input
=
2462 GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN
, srcRect
,
2463 NEED_COLOR_CHANNELS
, mEdgeMode
, &mSourceRect
);
2469 RefPtr
<DataSourceSurface
> target
= Factory::CreateDataSourceSurface(
2470 aRect
.Size(), SurfaceFormat::B8G8R8A8
, true);
2471 if (MOZ2D_WARN_IF(!target
)) {
2475 IntPoint offset
= aRect
.TopLeft() - srcRect
.TopLeft();
2477 DataSourceSurface::ScopedMap
sourceMap(input
, DataSourceSurface::READ
);
2478 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
2479 if (MOZ2D_WARN_IF(!sourceMap
.IsMapped() || !targetMap
.IsMapped())) {
2483 uint8_t* sourceData
=
2484 DataAtOffset(input
, sourceMap
.GetMappedSurface(), offset
);
2485 int32_t sourceStride
= sourceMap
.GetStride();
2486 uint8_t* sourceBegin
= sourceMap
.GetData();
2487 uint8_t* sourceEnd
= sourceBegin
+ sourceStride
* input
->GetSize().height
;
2488 uint8_t* targetData
= targetMap
.GetData();
2489 int32_t targetStride
= targetMap
.GetStride();
2491 // Why exactly are we reversing the kernel?
2492 std::vector
<Float
> kernel
= ReversedVector(mKernelMatrix
);
2493 kernel
= ScaledVector(kernel
, mDivisor
);
2494 Float maxResultAbs
= std::max(MaxVectorSum(kernel
) + mBias
,
2495 MaxVectorSum(ScaledVector(kernel
, -1)) - mBias
);
2496 maxResultAbs
= std::max(maxResultAbs
, 1.0f
);
2498 double idealFactor
= INT32_MAX
/ 2.0 / maxResultAbs
/ 255.0 * 0.999;
2499 MOZ_ASSERT(255.0 * maxResultAbs
* idealFactor
<= INT32_MAX
/ 2.0,
2500 "badly chosen float-to-int scale");
2501 int32_t shiftL
, shiftR
;
2502 TranslateDoubleToShifts(idealFactor
, shiftL
, shiftR
);
2503 double factorFromShifts
= Float(1 << shiftL
) / Float(1 << shiftR
);
2504 MOZ_ASSERT(255.0 * maxResultAbs
* factorFromShifts
<= INT32_MAX
/ 2.0,
2505 "badly chosen float-to-int scale");
2507 int32_t* intKernel
= new int32_t[kernel
.size()];
2508 for (size_t i
= 0; i
< kernel
.size(); i
++) {
2509 intKernel
[i
] = NS_lround(kernel
[i
] * factorFromShifts
);
2511 int32_t bias
= NS_lround(mBias
* 255 * factorFromShifts
);
2513 for (int32_t y
= 0; y
< aRect
.Height(); y
++) {
2514 for (int32_t x
= 0; x
< aRect
.Width(); x
++) {
2515 ConvolvePixel(sourceData
, targetData
, aRect
.Width(), aRect
.Height(),
2516 sourceStride
, targetStride
, sourceBegin
, sourceEnd
, x
, y
,
2517 intKernel
, bias
, shiftL
, shiftR
, mPreserveAlpha
,
2518 mKernelSize
.width
, mKernelSize
.height
, mTarget
.x
, mTarget
.y
,
2519 aKernelUnitLengthX
, aKernelUnitLengthY
);
2524 return target
.forget();
2527 void FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(
2528 const IntRect
& aRect
) {
2529 RequestInputRect(IN_CONVOLVE_MATRIX_IN
, InflatedSourceRect(aRect
));
2532 IntRect
FilterNodeConvolveMatrixSoftware::MapRectToSource(
2533 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
2534 return MapInputRectToSource(IN_CONVOLVE_MATRIX_IN
, InflatedSourceRect(aRect
),
2538 IntRect
FilterNodeConvolveMatrixSoftware::InflatedSourceRect(
2539 const IntRect
& aDestRect
) {
2540 if (aDestRect
.IsEmpty()) {
2545 margin
.left
= static_cast<int32_t>(ceil(mTarget
.x
* mKernelUnitLength
.width
));
2546 margin
.top
= static_cast<int32_t>(ceil(mTarget
.y
* mKernelUnitLength
.height
));
2547 margin
.right
= static_cast<int32_t>(
2548 ceil((mKernelSize
.width
- mTarget
.x
- 1) * mKernelUnitLength
.width
));
2549 margin
.bottom
= static_cast<int32_t>(
2550 ceil((mKernelSize
.height
- mTarget
.y
- 1) * mKernelUnitLength
.height
));
2552 IntRect srcRect
= aDestRect
;
2553 srcRect
.Inflate(margin
);
2557 IntRect
FilterNodeConvolveMatrixSoftware::InflatedDestRect(
2558 const IntRect
& aSourceRect
) {
2559 if (aSourceRect
.IsEmpty()) {
2564 margin
.left
= static_cast<int32_t>(
2565 ceil((mKernelSize
.width
- mTarget
.x
- 1) * mKernelUnitLength
.width
));
2566 margin
.top
= static_cast<int32_t>(
2567 ceil((mKernelSize
.height
- mTarget
.y
- 1) * mKernelUnitLength
.height
));
2569 static_cast<int32_t>(ceil(mTarget
.x
* mKernelUnitLength
.width
));
2571 static_cast<int32_t>(ceil(mTarget
.y
* mKernelUnitLength
.height
));
2573 IntRect destRect
= aSourceRect
;
2574 destRect
.Inflate(margin
);
2578 IntRect
FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(
2579 const IntRect
& aRect
) {
2580 IntRect srcRequest
= InflatedSourceRect(aRect
);
2581 IntRect srcOutput
= GetInputRectInRect(IN_CONVOLVE_MATRIX_IN
, srcRequest
);
2582 return InflatedDestRect(srcOutput
).Intersect(aRect
);
2585 FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware()
2586 : mScale(0.0f
), mChannelX(COLOR_CHANNEL_R
), mChannelY(COLOR_CHANNEL_G
) {}
2588 int32_t FilterNodeDisplacementMapSoftware::InputIndex(
2589 uint32_t aInputEnumIndex
) {
2590 switch (aInputEnumIndex
) {
2591 case IN_DISPLACEMENT_MAP_IN
:
2593 case IN_DISPLACEMENT_MAP_IN2
:
2600 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex
,
2602 MOZ_ASSERT(aIndex
== ATT_DISPLACEMENT_MAP_SCALE
);
2607 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex
,
2610 case ATT_DISPLACEMENT_MAP_X_CHANNEL
:
2611 mChannelX
= static_cast<ColorChannel
>(aValue
);
2613 case ATT_DISPLACEMENT_MAP_Y_CHANNEL
:
2614 mChannelY
= static_cast<ColorChannel
>(aValue
);
2617 MOZ_CRASH("GFX: FilterNodeDisplacementMapSoftware::SetAttribute");
2622 already_AddRefed
<DataSourceSurface
> FilterNodeDisplacementMapSoftware::Render(
2623 const IntRect
& aRect
) {
2624 IntRect srcRect
= InflatedSourceOrDestRect(aRect
);
2625 RefPtr
<DataSourceSurface
> input
= GetInputDataSourceSurface(
2626 IN_DISPLACEMENT_MAP_IN
, srcRect
, NEED_COLOR_CHANNELS
);
2627 RefPtr
<DataSourceSurface
> map
= GetInputDataSourceSurface(
2628 IN_DISPLACEMENT_MAP_IN2
, aRect
, NEED_COLOR_CHANNELS
);
2629 RefPtr
<DataSourceSurface
> target
=
2630 Factory::CreateDataSourceSurface(aRect
.Size(), SurfaceFormat::B8G8R8A8
);
2631 if (MOZ2D_WARN_IF(!(input
&& map
&& target
))) {
2635 IntPoint offset
= aRect
.TopLeft() - srcRect
.TopLeft();
2637 DataSourceSurface::ScopedMap
inputMap(input
, DataSourceSurface::READ
);
2638 DataSourceSurface::ScopedMap
mapMap(map
, DataSourceSurface::READ
);
2639 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
2640 if (MOZ2D_WARN_IF(!(inputMap
.IsMapped() && mapMap
.IsMapped() &&
2641 targetMap
.IsMapped()))) {
2645 uint8_t* sourceData
=
2646 DataAtOffset(input
, inputMap
.GetMappedSurface(), offset
);
2647 int32_t sourceStride
= inputMap
.GetStride();
2648 uint8_t* sourceBegin
= inputMap
.GetData();
2649 uint8_t* sourceEnd
= sourceBegin
+ sourceStride
* input
->GetSize().height
;
2650 uint8_t* mapData
= mapMap
.GetData();
2651 int32_t mapStride
= mapMap
.GetStride();
2652 uint8_t* targetData
= targetMap
.GetData();
2653 int32_t targetStride
= targetMap
.GetStride();
2655 static const ptrdiff_t channelMap
[4] = {
2656 B8G8R8A8_COMPONENT_BYTEOFFSET_R
, B8G8R8A8_COMPONENT_BYTEOFFSET_G
,
2657 B8G8R8A8_COMPONENT_BYTEOFFSET_B
, B8G8R8A8_COMPONENT_BYTEOFFSET_A
};
2658 uint16_t xChannel
= channelMap
[mChannelX
];
2659 uint16_t yChannel
= channelMap
[mChannelY
];
2661 float scaleOver255
= mScale
/ 255.0f
;
2662 float scaleAdjustment
= -0.5f
* mScale
;
2664 for (int32_t y
= 0; y
< aRect
.Height(); y
++) {
2665 for (int32_t x
= 0; x
< aRect
.Width(); x
++) {
2666 uint32_t mapIndex
= y
* mapStride
+ 4 * x
;
2667 uint32_t targIndex
= y
* targetStride
+ 4 * x
;
2669 x
+ scaleOver255
* mapData
[mapIndex
+ xChannel
] + scaleAdjustment
;
2671 y
+ scaleOver255
* mapData
[mapIndex
+ yChannel
] + scaleAdjustment
;
2672 *(uint32_t*)(targetData
+ targIndex
) = ColorAtPoint(
2673 sourceData
, sourceStride
, sourceBegin
, sourceEnd
, sourceX
, sourceY
);
2676 // Keep valgrind happy.
2677 PodZero(&targetData
[y
* targetStride
+ 4 * aRect
.Width()],
2678 targetStride
- 4 * aRect
.Width());
2681 return target
.forget();
2684 void FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(
2685 const IntRect
& aRect
) {
2686 RequestInputRect(IN_DISPLACEMENT_MAP_IN
, InflatedSourceOrDestRect(aRect
));
2687 RequestInputRect(IN_DISPLACEMENT_MAP_IN2
, aRect
);
2690 IntRect
FilterNodeDisplacementMapSoftware::MapRectToSource(
2691 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
2693 MapInputRectToSource(IN_DISPLACEMENT_MAP_IN
,
2694 InflatedSourceOrDestRect(aRect
), aMax
, aSourceNode
);
2696 MapInputRectToSource(IN_DISPLACEMENT_MAP_IN2
, aRect
, aMax
, aSourceNode
));
2700 IntRect
FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(
2701 const IntRect
& aDestOrSourceRect
) {
2702 IntRect sourceOrDestRect
= aDestOrSourceRect
;
2703 sourceOrDestRect
.Inflate(ceil(fabs(mScale
) / 2));
2704 return sourceOrDestRect
;
2707 IntRect
FilterNodeDisplacementMapSoftware::GetOutputRectInRect(
2708 const IntRect
& aRect
) {
2709 IntRect srcRequest
= InflatedSourceOrDestRect(aRect
);
2710 IntRect srcOutput
= GetInputRectInRect(IN_DISPLACEMENT_MAP_IN
, srcRequest
);
2711 return InflatedSourceOrDestRect(srcOutput
).Intersect(aRect
);
2714 FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware()
2718 mType(TURBULENCE_TYPE_TURBULENCE
) {}
2720 int32_t FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex
) {
2724 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex
,
2725 const Size
& aBaseFrequency
) {
2727 case ATT_TURBULENCE_BASE_FREQUENCY
:
2728 mBaseFrequency
= aBaseFrequency
;
2731 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2737 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex
,
2738 const IntRect
& aRect
) {
2740 case ATT_TURBULENCE_RECT
:
2741 mRenderRect
= aRect
;
2744 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2750 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex
,
2752 MOZ_ASSERT(aIndex
== ATT_TURBULENCE_STITCHABLE
);
2753 mStitchable
= aStitchable
;
2757 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex
,
2760 case ATT_TURBULENCE_NUM_OCTAVES
:
2761 mNumOctaves
= aValue
;
2763 case ATT_TURBULENCE_SEED
:
2766 case ATT_TURBULENCE_TYPE
:
2767 mType
= static_cast<TurbulenceType
>(aValue
);
2770 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2776 already_AddRefed
<DataSourceSurface
> FilterNodeTurbulenceSoftware::Render(
2777 const IntRect
& aRect
) {
2778 return FilterProcessing::RenderTurbulence(
2779 aRect
.Size(), aRect
.TopLeft(), mBaseFrequency
, mSeed
, mNumOctaves
, mType
,
2780 mStitchable
, Rect(mRenderRect
));
2783 IntRect
FilterNodeTurbulenceSoftware::GetOutputRectInRect(
2784 const IntRect
& aRect
) {
2785 return aRect
.Intersect(mRenderRect
);
2788 IntRect
FilterNodeTurbulenceSoftware::MapRectToSource(const IntRect
& aRect
,
2789 const IntRect
& aMax
,
2790 FilterNode
* aSourceNode
) {
2794 FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware()
2795 : mK1(0), mK2(0), mK3(0), mK4(0) {}
2797 int32_t FilterNodeArithmeticCombineSoftware::InputIndex(
2798 uint32_t aInputEnumIndex
) {
2799 switch (aInputEnumIndex
) {
2800 case IN_ARITHMETIC_COMBINE_IN
:
2802 case IN_ARITHMETIC_COMBINE_IN2
:
2809 void FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex
,
2810 const Float
* aFloat
,
2812 MOZ_ASSERT(aIndex
== ATT_ARITHMETIC_COMBINE_COEFFICIENTS
);
2813 MOZ_RELEASE_ASSERT(aSize
== 4);
2823 already_AddRefed
<DataSourceSurface
> FilterNodeArithmeticCombineSoftware::Render(
2824 const IntRect
& aRect
) {
2825 RefPtr
<DataSourceSurface
> input1
= GetInputDataSourceSurface(
2826 IN_ARITHMETIC_COMBINE_IN
, aRect
, NEED_COLOR_CHANNELS
);
2827 RefPtr
<DataSourceSurface
> input2
= GetInputDataSourceSurface(
2828 IN_ARITHMETIC_COMBINE_IN2
, aRect
, NEED_COLOR_CHANNELS
);
2829 if (!input1
&& !input2
) {
2833 // If one input is null, treat it as transparent by adjusting the factors.
2834 Float k1
= mK1
, k2
= mK2
, k3
= mK3
, k4
= mK4
;
2847 return FilterProcessing::ApplyArithmeticCombine(input1
, input2
, k1
, k2
, k3
,
2851 void FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(
2852 const IntRect
& aRect
) {
2853 RequestInputRect(IN_ARITHMETIC_COMBINE_IN
, aRect
);
2854 RequestInputRect(IN_ARITHMETIC_COMBINE_IN2
, aRect
);
2857 IntRect
FilterNodeArithmeticCombineSoftware::MapRectToSource(
2858 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
2860 MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN
, aRect
, aMax
, aSourceNode
);
2861 result
.OrWith(MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN2
, aRect
, aMax
,
2866 IntRect
FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(
2867 const IntRect
& aRect
) {
2872 GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN
, aRect
).Intersect(aRect
);
2874 GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2
, aRect
).Intersect(aRect
);
2877 result
= rectFrom1
.Intersect(rectFrom2
);
2880 result
= result
.Union(rectFrom1
);
2883 result
= result
.Union(rectFrom2
);
2888 FilterNodeCompositeSoftware::FilterNodeCompositeSoftware()
2889 : mOperator(COMPOSITE_OPERATOR_OVER
) {}
2891 int32_t FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex
) {
2892 return aInputEnumIndex
- IN_COMPOSITE_IN_START
;
2895 void FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex
,
2896 uint32_t aCompositeOperator
) {
2897 MOZ_ASSERT(aIndex
== ATT_COMPOSITE_OPERATOR
);
2898 mOperator
= static_cast<CompositeOperator
>(aCompositeOperator
);
2902 already_AddRefed
<DataSourceSurface
> FilterNodeCompositeSoftware::Render(
2903 const IntRect
& aRect
) {
2904 RefPtr
<DataSourceSurface
> start
= GetInputDataSourceSurface(
2905 IN_COMPOSITE_IN_START
, aRect
, NEED_COLOR_CHANNELS
);
2906 RefPtr
<DataSourceSurface
> dest
= Factory::CreateDataSourceSurface(
2907 aRect
.Size(), SurfaceFormat::B8G8R8A8
, true);
2908 if (MOZ2D_WARN_IF(!dest
)) {
2913 CopyRect(start
, dest
, aRect
- aRect
.TopLeft(), IntPoint());
2916 for (size_t inputIndex
= 1; inputIndex
< NumberOfSetInputs(); inputIndex
++) {
2917 RefPtr
<DataSourceSurface
> input
= GetInputDataSourceSurface(
2918 IN_COMPOSITE_IN_START
+ inputIndex
, aRect
, NEED_COLOR_CHANNELS
);
2920 FilterProcessing::ApplyComposition(input
, dest
, mOperator
);
2922 // We need to treat input as transparent. Depending on the composite
2923 // operator, different things happen to dest.
2924 switch (mOperator
) {
2925 case COMPOSITE_OPERATOR_OVER
:
2926 case COMPOSITE_OPERATOR_ATOP
:
2927 case COMPOSITE_OPERATOR_XOR
:
2928 case COMPOSITE_OPERATOR_LIGHTER
:
2929 // dest is unchanged.
2931 case COMPOSITE_OPERATOR_OUT
:
2932 // dest is now transparent, but it can become non-transparent again
2933 // when compositing additional inputs.
2934 ClearDataSourceSurface(dest
);
2936 case COMPOSITE_OPERATOR_IN
:
2937 // Transparency always wins. We're completely transparent now and
2938 // no additional input can get rid of that transparency.
2943 return dest
.forget();
2946 void FilterNodeCompositeSoftware::RequestFromInputsForRect(
2947 const IntRect
& aRect
) {
2948 for (size_t inputIndex
= 0; inputIndex
< NumberOfSetInputs(); inputIndex
++) {
2949 RequestInputRect(IN_COMPOSITE_IN_START
+ inputIndex
, aRect
);
2953 IntRect
FilterNodeCompositeSoftware::MapRectToSource(const IntRect
& aRect
,
2954 const IntRect
& aMax
,
2955 FilterNode
* aSourceNode
) {
2957 for (size_t inputIndex
= 0; inputIndex
< NumberOfSetInputs(); inputIndex
++) {
2958 result
.OrWith(MapInputRectToSource(IN_COMPOSITE_IN_START
+ inputIndex
,
2959 aRect
, aMax
, aSourceNode
));
2964 IntRect
FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
2966 for (size_t inputIndex
= 0; inputIndex
< NumberOfSetInputs(); inputIndex
++) {
2968 GetInputRectInRect(IN_COMPOSITE_IN_START
+ inputIndex
, aRect
);
2969 if (mOperator
== COMPOSITE_OPERATOR_IN
&& inputIndex
> 0) {
2970 rect
= rect
.Intersect(inputRect
);
2972 rect
= rect
.Union(inputRect
);
2978 int32_t FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex
) {
2979 switch (aInputEnumIndex
) {
2980 case IN_GAUSSIAN_BLUR_IN
:
2987 already_AddRefed
<DataSourceSurface
> FilterNodeBlurXYSoftware::Render(
2988 const IntRect
& aRect
) {
2989 Size sigmaXY
= StdDeviationXY();
2991 AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY
.width
, sigmaXY
.height
));
2993 if (d
.width
== 0 && d
.height
== 0) {
2994 return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN
, aRect
);
2997 IntRect srcRect
= InflatedSourceOrDestRect(aRect
);
2998 RefPtr
<DataSourceSurface
> input
=
2999 GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN
, srcRect
);
3004 RefPtr
<DataSourceSurface
> target
;
3005 Rect
r(0, 0, srcRect
.Width(), srcRect
.Height());
3007 if (input
->GetFormat() == SurfaceFormat::A8
) {
3009 Factory::CreateDataSourceSurface(srcRect
.Size(), SurfaceFormat::A8
);
3010 if (MOZ2D_WARN_IF(!target
)) {
3013 CopyRect(input
, target
, IntRect(IntPoint(), input
->GetSize()), IntPoint());
3015 DataSourceSurface::ScopedMap
targetMap(target
,
3016 DataSourceSurface::READ_WRITE
);
3017 if (MOZ2D_WARN_IF(!targetMap
.IsMapped())) {
3020 AlphaBoxBlur
blur(r
, targetMap
.GetStride(), sigmaXY
.width
, sigmaXY
.height
);
3021 blur
.Blur(targetMap
.GetData());
3023 RefPtr
<DataSourceSurface
> channel0
, channel1
, channel2
, channel3
;
3024 FilterProcessing::SeparateColorChannels(input
, channel0
, channel1
, channel2
,
3026 if (MOZ2D_WARN_IF(!(channel0
&& channel1
&& channel2
&& channel3
))) {
3030 DataSourceSurface::ScopedMap
channel0Map(channel0
,
3031 DataSourceSurface::READ_WRITE
);
3032 DataSourceSurface::ScopedMap
channel1Map(channel1
,
3033 DataSourceSurface::READ_WRITE
);
3034 DataSourceSurface::ScopedMap
channel2Map(channel2
,
3035 DataSourceSurface::READ_WRITE
);
3036 DataSourceSurface::ScopedMap
channel3Map(channel3
,
3037 DataSourceSurface::READ_WRITE
);
3038 if (MOZ2D_WARN_IF(!(channel0Map
.IsMapped() && channel1Map
.IsMapped() &&
3039 channel2Map
.IsMapped() && channel3Map
.IsMapped()))) {
3043 AlphaBoxBlur
blur(r
, channel0Map
.GetStride(), sigmaXY
.width
,
3045 blur
.Blur(channel0Map
.GetData());
3046 blur
.Blur(channel1Map
.GetData());
3047 blur
.Blur(channel2Map
.GetData());
3048 blur
.Blur(channel3Map
.GetData());
3050 target
= FilterProcessing::CombineColorChannels(channel0
, channel1
,
3051 channel2
, channel3
);
3054 return GetDataSurfaceInRect(target
, srcRect
, aRect
, EDGE_MODE_NONE
);
3057 void FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect
& aRect
) {
3058 RequestInputRect(IN_GAUSSIAN_BLUR_IN
, InflatedSourceOrDestRect(aRect
));
3061 IntRect
FilterNodeBlurXYSoftware::MapRectToSource(const IntRect
& aRect
,
3062 const IntRect
& aMax
,
3063 FilterNode
* aSourceNode
) {
3064 return MapInputRectToSource(
3065 IN_GAUSSIAN_BLUR_IN
, InflatedSourceOrDestRect(aRect
), aMax
, aSourceNode
);
3068 IntRect
FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(
3069 const IntRect
& aDestRect
) {
3070 Size sigmaXY
= StdDeviationXY();
3072 AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY
.width
, sigmaXY
.height
));
3073 IntRect srcRect
= aDestRect
;
3078 IntRect
FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
3079 IntRect srcRequest
= InflatedSourceOrDestRect(aRect
);
3080 IntRect srcOutput
= GetInputRectInRect(IN_GAUSSIAN_BLUR_IN
, srcRequest
);
3081 return InflatedSourceOrDestRect(srcOutput
).Intersect(aRect
);
3084 FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware()
3085 : mStdDeviation(0) {}
3087 static float ClampStdDeviation(float aStdDeviation
) {
3088 // Cap software blur radius for performance reasons.
3089 return std::min(std::max(0.0f
, aStdDeviation
), 100.0f
);
3092 void FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex
,
3093 float aStdDeviation
) {
3095 case ATT_GAUSSIAN_BLUR_STD_DEVIATION
:
3096 mStdDeviation
= ClampStdDeviation(aStdDeviation
);
3099 MOZ_CRASH("GFX: FilterNodeGaussianBlurSoftware::SetAttribute");
3104 Size
FilterNodeGaussianBlurSoftware::StdDeviationXY() {
3105 return Size(mStdDeviation
, mStdDeviation
);
3108 FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware()
3109 : mStdDeviation(0.0), mBlurDirection(BLUR_DIRECTION_X
) {}
3111 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex
,
3112 Float aStdDeviation
) {
3114 case ATT_DIRECTIONAL_BLUR_STD_DEVIATION
:
3115 mStdDeviation
= ClampStdDeviation(aStdDeviation
);
3118 MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
3123 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex
,
3124 uint32_t aBlurDirection
) {
3126 case ATT_DIRECTIONAL_BLUR_DIRECTION
:
3127 mBlurDirection
= (BlurDirection
)aBlurDirection
;
3130 MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
3135 Size
FilterNodeDirectionalBlurSoftware::StdDeviationXY() {
3136 float sigmaX
= mBlurDirection
== BLUR_DIRECTION_X
? mStdDeviation
: 0;
3137 float sigmaY
= mBlurDirection
== BLUR_DIRECTION_Y
? mStdDeviation
: 0;
3138 return Size(sigmaX
, sigmaY
);
3141 int32_t FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex
) {
3142 switch (aInputEnumIndex
) {
3150 void FilterNodeCropSoftware::SetAttribute(uint32_t aIndex
,
3151 const Rect
& aSourceRect
) {
3152 MOZ_ASSERT(aIndex
== ATT_CROP_RECT
);
3153 Rect srcRect
= aSourceRect
;
3155 if (!srcRect
.ToIntRect(&mCropRect
)) {
3156 mCropRect
= IntRect();
3161 already_AddRefed
<DataSourceSurface
> FilterNodeCropSoftware::Render(
3162 const IntRect
& aRect
) {
3163 return GetInputDataSourceSurface(IN_CROP_IN
, aRect
.Intersect(mCropRect
));
3166 void FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect
& aRect
) {
3167 RequestInputRect(IN_CROP_IN
, aRect
.Intersect(mCropRect
));
3170 IntRect
FilterNodeCropSoftware::MapRectToSource(const IntRect
& aRect
,
3171 const IntRect
& aMax
,
3172 FilterNode
* aSourceNode
) {
3173 return MapInputRectToSource(IN_CROP_IN
, aRect
.Intersect(mCropRect
), aMax
,
3177 IntRect
FilterNodeCropSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
3178 return GetInputRectInRect(IN_CROP_IN
, aRect
).Intersect(mCropRect
);
3181 int32_t FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex
) {
3182 switch (aInputEnumIndex
) {
3183 case IN_PREMULTIPLY_IN
:
3190 already_AddRefed
<DataSourceSurface
> FilterNodePremultiplySoftware::Render(
3191 const IntRect
& aRect
) {
3192 RefPtr
<DataSourceSurface
> input
=
3193 GetInputDataSourceSurface(IN_PREMULTIPLY_IN
, aRect
);
3194 return input
? Premultiply(input
) : nullptr;
3197 void FilterNodePremultiplySoftware::RequestFromInputsForRect(
3198 const IntRect
& aRect
) {
3199 RequestInputRect(IN_PREMULTIPLY_IN
, aRect
);
3202 IntRect
FilterNodePremultiplySoftware::MapRectToSource(
3203 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
3204 return MapInputRectToSource(IN_PREMULTIPLY_IN
, aRect
, aMax
, aSourceNode
);
3207 IntRect
FilterNodePremultiplySoftware::GetOutputRectInRect(
3208 const IntRect
& aRect
) {
3209 return GetInputRectInRect(IN_PREMULTIPLY_IN
, aRect
);
3212 int32_t FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex
) {
3213 switch (aInputEnumIndex
) {
3214 case IN_UNPREMULTIPLY_IN
:
3221 already_AddRefed
<DataSourceSurface
> FilterNodeUnpremultiplySoftware::Render(
3222 const IntRect
& aRect
) {
3223 RefPtr
<DataSourceSurface
> input
=
3224 GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN
, aRect
);
3225 return input
? Unpremultiply(input
) : nullptr;
3228 void FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(
3229 const IntRect
& aRect
) {
3230 RequestInputRect(IN_UNPREMULTIPLY_IN
, aRect
);
3233 IntRect
FilterNodeUnpremultiplySoftware::MapRectToSource(
3234 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
3235 return MapInputRectToSource(IN_UNPREMULTIPLY_IN
, aRect
, aMax
, aSourceNode
);
3238 IntRect
FilterNodeUnpremultiplySoftware::GetOutputRectInRect(
3239 const IntRect
& aRect
) {
3240 return GetInputRectInRect(IN_UNPREMULTIPLY_IN
, aRect
);
3243 void FilterNodeOpacitySoftware::SetAttribute(uint32_t aIndex
, Float aValue
) {
3244 MOZ_ASSERT(aIndex
== ATT_OPACITY_VALUE
);
3249 int32_t FilterNodeOpacitySoftware::InputIndex(uint32_t aInputEnumIndex
) {
3250 switch (aInputEnumIndex
) {
3258 already_AddRefed
<DataSourceSurface
> FilterNodeOpacitySoftware::Render(
3259 const IntRect
& aRect
) {
3260 RefPtr
<DataSourceSurface
> input
=
3261 GetInputDataSourceSurface(IN_OPACITY_IN
, aRect
);
3262 return input
? Opacity(input
, mValue
) : nullptr;
3265 void FilterNodeOpacitySoftware::RequestFromInputsForRect(const IntRect
& aRect
) {
3266 RequestInputRect(IN_OPACITY_IN
, aRect
);
3269 IntRect
FilterNodeOpacitySoftware::MapRectToSource(const IntRect
& aRect
,
3270 const IntRect
& aMax
,
3271 FilterNode
* aSourceNode
) {
3272 return MapInputRectToSource(IN_OPACITY_IN
, aRect
, aMax
, aSourceNode
);
3275 IntRect
FilterNodeOpacitySoftware::GetOutputRectInRect(const IntRect
& aRect
) {
3276 return GetInputRectInRect(IN_OPACITY_IN
, aRect
);
3279 bool PointLightSoftware::SetAttribute(uint32_t aIndex
, const Point3D
& aPoint
) {
3281 case ATT_POINT_LIGHT_POSITION
:
3290 SpotLightSoftware::SpotLightSoftware()
3291 : mSpecularFocus(0), mLimitingConeAngle(0), mLimitingConeCos(1) {}
3293 bool SpotLightSoftware::SetAttribute(uint32_t aIndex
, const Point3D
& aPoint
) {
3295 case ATT_SPOT_LIGHT_POSITION
:
3298 case ATT_SPOT_LIGHT_POINTS_AT
:
3307 bool SpotLightSoftware::SetAttribute(uint32_t aIndex
, Float aValue
) {
3309 case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE
:
3310 mLimitingConeAngle
= aValue
;
3312 case ATT_SPOT_LIGHT_FOCUS
:
3313 mSpecularFocus
= aValue
;
3321 DistantLightSoftware::DistantLightSoftware() : mAzimuth(0), mElevation(0) {}
3323 bool DistantLightSoftware::SetAttribute(uint32_t aIndex
, Float aValue
) {
3325 case ATT_DISTANT_LIGHT_AZIMUTH
:
3328 case ATT_DISTANT_LIGHT_ELEVATION
:
3329 mElevation
= aValue
;
3337 static inline Point3D
Normalized(const Point3D
& vec
) {
3343 template <typename LightType
, typename LightingType
>
3344 FilterNodeLightingSoftware
<LightType
, LightingType
>::FilterNodeLightingSoftware(
3345 const char* aTypeName
)
3347 #if defined(MOZILLA_INTERNAL_API) && defined(NS_BUILD_REFCNT_LOGGING)
3349 mTypeName(aTypeName
)
3354 template <typename LightType
, typename LightingType
>
3355 int32_t FilterNodeLightingSoftware
<LightType
, LightingType
>::InputIndex(
3356 uint32_t aInputEnumIndex
) {
3357 switch (aInputEnumIndex
) {
3358 case IN_LIGHTING_IN
:
3365 template <typename LightType
, typename LightingType
>
3366 void FilterNodeLightingSoftware
<LightType
, LightingType
>::SetAttribute(
3367 uint32_t aIndex
, const Point3D
& aPoint
) {
3368 if (mLight
.SetAttribute(aIndex
, aPoint
)) {
3372 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute point");
3375 template <typename LightType
, typename LightingType
>
3376 void FilterNodeLightingSoftware
<LightType
, LightingType
>::SetAttribute(
3377 uint32_t aIndex
, Float aValue
) {
3378 if (mLight
.SetAttribute(aIndex
, aValue
) ||
3379 mLighting
.SetAttribute(aIndex
, aValue
)) {
3384 case ATT_LIGHTING_SURFACE_SCALE
:
3385 mSurfaceScale
= std::fpclassify(aValue
) == FP_SUBNORMAL
? 0.0 : aValue
;
3388 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float");
3393 template <typename LightType
, typename LightingType
>
3394 void FilterNodeLightingSoftware
<LightType
, LightingType
>::SetAttribute(
3395 uint32_t aIndex
, const Size
& aKernelUnitLength
) {
3397 case ATT_LIGHTING_KERNEL_UNIT_LENGTH
:
3398 mKernelUnitLength
= aKernelUnitLength
;
3401 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size");
3406 template <typename LightType
, typename LightingType
>
3407 void FilterNodeLightingSoftware
<LightType
, LightingType
>::SetAttribute(
3408 uint32_t aIndex
, const DeviceColor
& aColor
) {
3409 MOZ_ASSERT(aIndex
== ATT_LIGHTING_COLOR
);
3414 template <typename LightType
, typename LightingType
>
3416 FilterNodeLightingSoftware
<LightType
, LightingType
>::GetOutputRectInRect(
3417 const IntRect
& aRect
) {
3421 Point3D
PointLightSoftware::GetVectorToLight(const Point3D
& aTargetPoint
) {
3422 return Normalized(mPosition
- aTargetPoint
);
3425 uint32_t PointLightSoftware::GetColor(uint32_t aLightColor
,
3426 const Point3D
& aVectorToLight
) {
3430 void SpotLightSoftware::Prepare() {
3431 mVectorFromFocusPointToLight
= Normalized(mPointsAt
- mPosition
);
3433 std::max
<double>(cos(mLimitingConeAngle
* M_PI
/ 180.0), 0.0);
3434 mPowCache
.CacheForExponent(mSpecularFocus
);
3437 Point3D
SpotLightSoftware::GetVectorToLight(const Point3D
& aTargetPoint
) {
3438 return Normalized(mPosition
- aTargetPoint
);
3441 uint32_t SpotLightSoftware::GetColor(uint32_t aLightColor
,
3442 const Point3D
& aVectorToLight
) {
3448 Float dot
= -aVectorToLight
.DotProduct(mVectorFromFocusPointToLight
);
3449 if (!mPowCache
.HasPowerTable()) {
3450 dot
*= (dot
>= mLimitingConeCos
);
3451 color
= aLightColor
;
3452 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] *= dot
;
3453 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] *= dot
;
3454 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] *= dot
;
3456 color
= aLightColor
;
3457 uint16_t doti
= dot
* (dot
>= 0) * (1 << PowCache::sInputIntPrecisionBits
);
3458 uint32_t tmp
= mPowCache
.Pow(doti
) * (dot
>= mLimitingConeCos
);
3459 MOZ_ASSERT(tmp
<= (1 << PowCache::sOutputIntPrecisionBits
),
3460 "pow() result must not exceed 1.0");
3461 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] =
3462 uint8_t((colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] * tmp
) >>
3463 PowCache::sOutputIntPrecisionBits
);
3464 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] =
3465 uint8_t((colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] * tmp
) >>
3466 PowCache::sOutputIntPrecisionBits
);
3467 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] =
3468 uint8_t((colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] * tmp
) >>
3469 PowCache::sOutputIntPrecisionBits
);
3471 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_A
] = 255;
3475 void DistantLightSoftware::Prepare() {
3476 const double radPerDeg
= M_PI
/ 180.0;
3477 mVectorToLight
.x
= cos(mAzimuth
* radPerDeg
) * cos(mElevation
* radPerDeg
);
3478 mVectorToLight
.y
= sin(mAzimuth
* radPerDeg
) * cos(mElevation
* radPerDeg
);
3479 mVectorToLight
.z
= sin(mElevation
* radPerDeg
);
3482 Point3D
DistantLightSoftware::GetVectorToLight(const Point3D
& aTargetPoint
) {
3483 return mVectorToLight
;
3486 uint32_t DistantLightSoftware::GetColor(uint32_t aLightColor
,
3487 const Point3D
& aVectorToLight
) {
3491 template <typename CoordType
>
3492 static Point3D
GenerateNormal(const uint8_t* data
, int32_t stride
,
3493 uint8_t* boundsBegin
, uint8_t* boundsEnd
,
3494 int32_t x
, int32_t y
, float surfaceScale
,
3495 CoordType dx
, CoordType dy
) {
3496 const uint8_t* index
= data
+ y
* stride
+ x
;
3500 // See this for source of constants:
3501 // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
3502 int16_t normalX
= -1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3503 boundsEnd
, -dx
, -dy
, 1, 0) +
3504 1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3505 boundsEnd
, dx
, -dy
, 1, 0) +
3506 -2 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3507 boundsEnd
, -dx
, zero
, 1, 0) +
3508 2 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3509 boundsEnd
, dx
, zero
, 1, 0) +
3510 -1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3511 boundsEnd
, -dx
, dy
, 1, 0) +
3512 1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3513 boundsEnd
, dx
, dy
, 1, 0);
3515 int16_t normalY
= -1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3516 boundsEnd
, -dx
, -dy
, 1, 0) +
3517 -2 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3518 boundsEnd
, zero
, -dy
, 1, 0) +
3519 -1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3520 boundsEnd
, dx
, -dy
, 1, 0) +
3521 1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3522 boundsEnd
, -dx
, dy
, 1, 0) +
3523 2 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3524 boundsEnd
, zero
, dy
, 1, 0) +
3525 1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3526 boundsEnd
, dx
, dy
, 1, 0);
3529 normal
.x
= -surfaceScale
* normalX
/ 4.0f
;
3530 normal
.y
= -surfaceScale
* normalY
/ 4.0f
;
3532 return Normalized(normal
);
3535 template <typename LightType
, typename LightingType
>
3536 already_AddRefed
<DataSourceSurface
>
3537 FilterNodeLightingSoftware
<LightType
, LightingType
>::Render(
3538 const IntRect
& aRect
) {
3539 if (mKernelUnitLength
.width
== floor(mKernelUnitLength
.width
) &&
3540 mKernelUnitLength
.height
== floor(mKernelUnitLength
.height
)) {
3541 return DoRender(aRect
, (int32_t)mKernelUnitLength
.width
,
3542 (int32_t)mKernelUnitLength
.height
);
3544 return DoRender(aRect
, mKernelUnitLength
.width
, mKernelUnitLength
.height
);
3547 template <typename LightType
, typename LightingType
>
3548 void FilterNodeLightingSoftware
<
3549 LightType
, LightingType
>::RequestFromInputsForRect(const IntRect
& aRect
) {
3550 IntRect srcRect
= aRect
;
3551 srcRect
.Inflate(ceil(mKernelUnitLength
.width
),
3552 ceil(mKernelUnitLength
.height
));
3553 RequestInputRect(IN_LIGHTING_IN
, srcRect
);
3556 template <typename LightType
, typename LightingType
>
3557 IntRect FilterNodeLightingSoftware
<LightType
, LightingType
>::MapRectToSource(
3558 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
3559 IntRect srcRect
= aRect
;
3560 srcRect
.Inflate(ceil(mKernelUnitLength
.width
),
3561 ceil(mKernelUnitLength
.height
));
3562 return MapInputRectToSource(IN_LIGHTING_IN
, srcRect
, aMax
, aSourceNode
);
3565 template <typename LightType
, typename LightingType
>
3566 template <typename CoordType
>
3567 already_AddRefed
<DataSourceSurface
>
3568 FilterNodeLightingSoftware
<LightType
, LightingType
>::DoRender(
3569 const IntRect
& aRect
, CoordType aKernelUnitLengthX
,
3570 CoordType aKernelUnitLengthY
) {
3571 MOZ_ASSERT(aKernelUnitLengthX
> 0,
3572 "aKernelUnitLengthX can be a negative or zero value");
3573 MOZ_ASSERT(aKernelUnitLengthY
> 0,
3574 "aKernelUnitLengthY can be a negative or zero value");
3576 IntRect srcRect
= aRect
;
3577 IntSize size
= aRect
.Size();
3578 srcRect
.Inflate(ceil(float(aKernelUnitLengthX
)),
3579 ceil(float(aKernelUnitLengthY
)));
3581 // Inflate the source rect by another pixel because the bilinear filtering in
3582 // ColorComponentAtPoint may want to access the margins.
3585 RefPtr
<DataSourceSurface
> input
= GetInputDataSourceSurface(
3586 IN_LIGHTING_IN
, srcRect
, CAN_HANDLE_A8
, EDGE_MODE_NONE
);
3592 if (input
->GetFormat() != SurfaceFormat::A8
) {
3593 input
= FilterProcessing::ExtractAlpha(input
);
3596 RefPtr
<DataSourceSurface
> target
=
3597 Factory::CreateDataSourceSurface(size
, SurfaceFormat::B8G8R8A8
);
3598 if (MOZ2D_WARN_IF(!target
)) {
3602 IntPoint offset
= aRect
.TopLeft() - srcRect
.TopLeft();
3604 DataSourceSurface::ScopedMap
sourceMap(input
, DataSourceSurface::READ
);
3605 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
3606 if (MOZ2D_WARN_IF(!(sourceMap
.IsMapped() && targetMap
.IsMapped()))) {
3610 uint8_t* sourceData
=
3611 DataAtOffset(input
, sourceMap
.GetMappedSurface(), offset
);
3612 int32_t sourceStride
= sourceMap
.GetStride();
3613 uint8_t* sourceBegin
= sourceMap
.GetData();
3614 uint8_t* sourceEnd
= sourceBegin
+ sourceStride
* input
->GetSize().height
;
3615 uint8_t* targetData
= targetMap
.GetData();
3616 int32_t targetStride
= targetMap
.GetStride();
3618 uint32_t lightColor
= ColorToBGRA(mColor
);
3620 mLighting
.Prepare();
3622 for (int32_t y
= 0; y
< size
.height
; y
++) {
3623 for (int32_t x
= 0; x
< size
.width
; x
++) {
3624 int32_t sourceIndex
= y
* sourceStride
+ x
;
3625 int32_t targetIndex
= y
* targetStride
+ 4 * x
;
3628 GenerateNormal(sourceData
, sourceStride
, sourceBegin
, sourceEnd
, x
, y
,
3629 mSurfaceScale
, aKernelUnitLengthX
, aKernelUnitLengthY
);
3631 IntPoint
pointInFilterSpace(aRect
.X() + x
, aRect
.Y() + y
);
3632 Float Z
= mSurfaceScale
* sourceData
[sourceIndex
] / 255.0f
;
3633 Point3D
pt(pointInFilterSpace
.x
, pointInFilterSpace
.y
, Z
);
3634 Point3D rayDir
= mLight
.GetVectorToLight(pt
);
3635 uint32_t color
= mLight
.GetColor(lightColor
, rayDir
);
3637 *(uint32_t*)(targetData
+ targetIndex
) =
3638 mLighting
.LightPixel(normal
, rayDir
, color
);
3641 // Zero padding to keep valgrind happy.
3642 PodZero(&targetData
[y
* targetStride
+ 4 * size
.width
],
3643 targetStride
- 4 * size
.width
);
3646 return target
.forget();
3649 DiffuseLightingSoftware::DiffuseLightingSoftware() : mDiffuseConstant(0) {}
3651 bool DiffuseLightingSoftware::SetAttribute(uint32_t aIndex
, Float aValue
) {
3653 case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT
:
3654 mDiffuseConstant
= aValue
;
3662 uint32_t DiffuseLightingSoftware::LightPixel(const Point3D
& aNormal
,
3663 const Point3D
& aVectorToLight
,
3665 Float dotNL
= std::max(0.0f
, aNormal
.DotProduct(aVectorToLight
));
3666 Float diffuseNL
= mDiffuseConstant
* dotNL
;
3670 uint8_t components
[4];
3672 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] = umin(
3673 uint32_t(diffuseNL
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
]),
3675 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] = umin(
3676 uint32_t(diffuseNL
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
]),
3678 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] = umin(
3679 uint32_t(diffuseNL
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
]),
3681 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_A
] = 255;
3685 SpecularLightingSoftware::SpecularLightingSoftware()
3686 : mSpecularConstant(0), mSpecularExponent(0), mSpecularConstantInt(0) {}
3688 bool SpecularLightingSoftware::SetAttribute(uint32_t aIndex
, Float aValue
) {
3690 case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT
:
3691 mSpecularConstant
= std::min(std::max(aValue
, 0.0f
), 255.0f
);
3693 case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT
:
3694 mSpecularExponent
= std::min(std::max(aValue
, 1.0f
), 128.0f
);
3702 void SpecularLightingSoftware::Prepare() {
3703 mPowCache
.CacheForExponent(mSpecularExponent
);
3704 mSpecularConstantInt
= uint32_t(mSpecularConstant
* (1 << 8));
3707 uint32_t SpecularLightingSoftware::LightPixel(const Point3D
& aNormal
,
3708 const Point3D
& aVectorToLight
,
3710 Point3D
vectorToEye(0, 0, 1);
3711 Point3D halfwayVector
= aVectorToLight
+ vectorToEye
;
3712 Float halfwayLength
= halfwayVector
.Length();
3713 if (halfwayLength
> 0) {
3714 halfwayVector
/= halfwayLength
;
3716 Float dotNH
= aNormal
.DotProduct(halfwayVector
);
3718 uint16_t(dotNH
* (dotNH
>= 0) * (1 << PowCache::sInputIntPrecisionBits
));
3719 // The exponent for specular is in [1,128] range, so we don't need to check
3720 // and optimize for the "default power table" scenario here.
3721 MOZ_ASSERT(mPowCache
.HasPowerTable());
3722 uint32_t specularNHi
=
3723 uint32_t(mSpecularConstantInt
) * mPowCache
.Pow(dotNHi
) >> 8;
3727 uint8_t components
[4];
3729 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] =
3730 umin((specularNHi
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
]) >>
3731 PowCache::sOutputIntPrecisionBits
,
3733 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] =
3734 umin((specularNHi
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
]) >>
3735 PowCache::sOutputIntPrecisionBits
,
3737 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] =
3738 umin((specularNHi
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
]) >>
3739 PowCache::sOutputIntPrecisionBits
,
3742 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_A
] =
3743 umax(color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
],
3744 umax(color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
],
3745 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
]));
3750 } // namespace mozilla