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 // Lock the cache and retrieve a cached surface if we have one and it can
622 // satisfy this request, or else request a rect we will compute and cache
624 MutexAutoLock
lock(mCacheMutex
);
626 if (!mCachedRect
.Contains(aRect
)) {
628 requestedRect
= mRequestedRect
;
630 MOZ_ASSERT(mCachedOutput
, "cached rect but no cached output?");
631 cachedRect
= mCachedRect
;
632 cachedOutput
= mCachedOutput
;
637 // Compute the output
638 cachedOutput
= Render(requestedRect
);
640 // Update the cache for future requests
641 MutexAutoLock
lock(mCacheMutex
);
643 mCachedOutput
= cachedOutput
;
644 if (!mCachedOutput
) {
645 mCachedRect
= IntRect();
646 mRequestedRect
= IntRect();
649 mCachedRect
= requestedRect
;
650 mRequestedRect
= IntRect();
652 cachedRect
= mCachedRect
;
655 return GetDataSurfaceInRect(cachedOutput
, cachedRect
, aRect
, EDGE_MODE_NONE
);
658 void FilterNodeSoftware::RequestRect(const IntRect
& aRect
) {
659 if (mRequestedRect
.Contains(aRect
)) {
660 // Bail out now. Otherwise pathological filters can spend time exponential
661 // in the number of primitives, e.g. if each primitive takes the
662 // previous primitive as its two inputs.
665 mRequestedRect
= mRequestedRect
.Union(aRect
);
666 RequestFromInputsForRect(aRect
);
669 IntRect
FilterNodeSoftware::MapInputRectToSource(uint32_t aInputEnumIndex
,
670 const IntRect
& aRect
,
672 FilterNode
* aSourceNode
) {
673 int32_t inputIndex
= InputIndex(aInputEnumIndex
);
674 if (inputIndex
< 0) {
675 gfxDevCrash(LogReason::FilterInputError
)
676 << "Invalid input " << inputIndex
<< " vs. " << NumberOfSetInputs();
679 if ((uint32_t)inputIndex
< NumberOfSetInputs()) {
680 RefPtr
<FilterNodeSoftware
> filter
= mInputFilters
[inputIndex
];
681 // If we have any input filters call into them to do the mapping,
682 // otherwise we can assume an input surface will be used
683 // and just return aRect.
685 return filter
->MapRectToSource(aRect
, aMax
, aSourceNode
);
688 // We have an input surface instead of a filter
689 // so check if we're the target node.
690 if (this == aSourceNode
) {
696 void FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex
,
697 const IntRect
& aRect
) {
698 if (aRect
.Overflows()) {
702 int32_t inputIndex
= InputIndex(aInputEnumIndex
);
703 if (inputIndex
< 0 || (uint32_t)inputIndex
>= NumberOfSetInputs()) {
704 gfxDevCrash(LogReason::FilterInputError
)
705 << "Invalid input " << inputIndex
<< " vs. " << NumberOfSetInputs();
708 if (mInputSurfaces
[inputIndex
]) {
711 RefPtr
<FilterNodeSoftware
> filter
= mInputFilters
[inputIndex
];
712 MOZ_ASSERT(filter
, "missing input");
713 filter
->RequestRect(filter
->GetOutputRectInRect(aRect
));
716 SurfaceFormat
FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat
,
717 FormatHint aFormatHint
) {
718 if (aCurrentFormat
== SurfaceFormat::A8
&& aFormatHint
== CAN_HANDLE_A8
) {
719 return SurfaceFormat::A8
;
721 return SurfaceFormat::B8G8R8A8
;
724 already_AddRefed
<DataSourceSurface
>
725 FilterNodeSoftware::GetInputDataSourceSurface(
726 uint32_t aInputEnumIndex
, const IntRect
& aRect
, FormatHint aFormatHint
,
727 ConvolveMatrixEdgeMode aEdgeMode
,
728 const IntRect
* aTransparencyPaddedSourceRect
) {
729 if (aRect
.Overflows()) {
733 #ifdef DEBUG_DUMP_SURFACES
735 "<section><h1>GetInputDataSourceSurface with aRect: %d, %d, %d, "
737 aRect
.x
, aRect
.y
, aRect
.Width(), aRect
.Height());
739 int32_t inputIndex
= InputIndex(aInputEnumIndex
);
740 if (inputIndex
< 0 || (uint32_t)inputIndex
>= NumberOfSetInputs()) {
741 gfxDevCrash(LogReason::FilterInputData
)
742 << "Invalid data " << inputIndex
<< " vs. " << NumberOfSetInputs();
746 if (aRect
.IsEmpty()) {
750 RefPtr
<SourceSurface
> surface
;
753 if (mInputSurfaces
[inputIndex
]) {
754 // Input from input surface
755 surface
= mInputSurfaces
[inputIndex
];
756 #ifdef DEBUG_DUMP_SURFACES
757 printf("input from input surface:\n");
759 surfaceRect
= surface
->GetRect();
761 // Input from input filter
762 #ifdef DEBUG_DUMP_SURFACES
763 printf("getting input from input filter %s...\n",
764 mInputFilters
[inputIndex
]->GetName());
766 RefPtr
<FilterNodeSoftware
> filter
= mInputFilters
[inputIndex
];
767 MOZ_ASSERT(filter
, "missing input");
768 IntRect inputFilterOutput
= filter
->GetOutputRectInRect(aRect
);
769 if (!inputFilterOutput
.IsEmpty()) {
770 surface
= filter
->GetOutput(inputFilterOutput
);
772 #ifdef DEBUG_DUMP_SURFACES
773 printf("input from input filter %s:\n",
774 mInputFilters
[inputIndex
]->GetName());
776 surfaceRect
= inputFilterOutput
;
777 MOZ_ASSERT(!surface
|| surfaceRect
.Size() == surface
->GetSize());
780 if (surface
&& surface
->GetFormat() == SurfaceFormat::UNKNOWN
) {
781 #ifdef DEBUG_DUMP_SURFACES
782 printf("wrong input format</section>\n\n");
787 if (!surfaceRect
.IsEmpty() && !surface
) {
788 #ifdef DEBUG_DUMP_SURFACES
789 printf(" -- no input --</section>\n\n");
794 if (aTransparencyPaddedSourceRect
&&
795 !aTransparencyPaddedSourceRect
->IsEmpty()) {
796 IntRect srcRect
= aTransparencyPaddedSourceRect
->Intersect(aRect
);
798 GetDataSurfaceInRect(surface
, surfaceRect
, srcRect
, EDGE_MODE_NONE
);
799 surfaceRect
= srcRect
;
802 RefPtr
<DataSourceSurface
> result
=
803 GetDataSurfaceInRect(surface
, surfaceRect
, aRect
, aEdgeMode
);
806 // TODO: This isn't safe since we don't have a guarantee
807 // that future Maps will have the same stride
808 DataSourceSurface::MappedSurface map
;
809 if (result
->Map(DataSourceSurface::READ
, &map
)) {
810 // Unmap immediately since CloneAligned hasn't been updated
811 // to use the Map API yet. We can still read the stride/data
812 // values as long as we don't try to dereference them.
814 if (map
.mStride
!= GetAlignedStride
<16>(map
.mStride
, 1) ||
815 reinterpret_cast<uintptr_t>(map
.mData
) % 16 != 0) {
816 // Align unaligned surface.
817 result
= CloneAligned(result
);
825 #ifdef DEBUG_DUMP_SURFACES
826 printf(" -- no input --</section>\n\n");
831 SurfaceFormat currentFormat
= result
->GetFormat();
832 if (DesiredFormat(currentFormat
, aFormatHint
) == SurfaceFormat::B8G8R8A8
&&
833 currentFormat
!= SurfaceFormat::B8G8R8A8
) {
834 result
= FilterProcessing::ConvertToB8G8R8A8(result
);
837 #ifdef DEBUG_DUMP_SURFACES
838 printf("<img src='");
839 gfxUtils::DumpAsDataURL(result
);
840 printf("'></section>");
843 MOZ_ASSERT(!result
|| result
->GetSize() == aRect
.Size(),
844 "wrong surface size");
846 return result
.forget();
849 IntRect
FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex
,
850 const IntRect
& aInRect
) {
851 if (aInRect
.Overflows()) {
855 int32_t inputIndex
= InputIndex(aInputEnumIndex
);
856 if (inputIndex
< 0 || (uint32_t)inputIndex
>= NumberOfSetInputs()) {
857 gfxDevCrash(LogReason::FilterInputRect
)
858 << "Invalid rect " << inputIndex
<< " vs. " << NumberOfSetInputs();
861 if (mInputSurfaces
[inputIndex
]) {
862 return aInRect
.Intersect(mInputSurfaces
[inputIndex
]->GetRect());
864 RefPtr
<FilterNodeSoftware
> filter
= mInputFilters
[inputIndex
];
865 MOZ_ASSERT(filter
, "missing input");
866 return filter
->GetOutputRectInRect(aInRect
);
869 size_t FilterNodeSoftware::NumberOfSetInputs() {
870 return std::max(mInputSurfaces
.size(), mInputFilters
.size());
873 void FilterNodeSoftware::AddInvalidationListener(
874 FilterInvalidationListener
* aListener
) {
875 MOZ_ASSERT(aListener
, "null listener");
876 mInvalidationListeners
.push_back(aListener
);
879 void FilterNodeSoftware::RemoveInvalidationListener(
880 FilterInvalidationListener
* aListener
) {
881 MOZ_ASSERT(aListener
, "null listener");
882 std::vector
<FilterInvalidationListener
*>::iterator it
= std::find(
883 mInvalidationListeners
.begin(), mInvalidationListeners
.end(), aListener
);
884 mInvalidationListeners
.erase(it
);
887 void FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware
* aFilter
) {
891 void FilterNodeSoftware::Invalidate() {
892 MutexAutoLock
lock(mCacheMutex
);
893 mCachedOutput
= nullptr;
894 mCachedRect
= IntRect();
895 for (std::vector
<FilterInvalidationListener
*>::iterator it
=
896 mInvalidationListeners
.begin();
897 it
!= mInvalidationListeners
.end(); it
++) {
898 (*it
)->FilterInvalidated(this);
902 FilterNodeSoftware::FilterNodeSoftware()
903 : mCacheMutex("FilterNodeSoftware::mCacheMutex") {}
905 FilterNodeSoftware::~FilterNodeSoftware() {
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
;
1012 return CompositionOp::OP_OVER
;
1015 return CompositionOp::OP_OVER
;
1018 already_AddRefed
<DataSourceSurface
> FilterNodeBlendSoftware::Render(
1019 const IntRect
& aRect
) {
1020 RefPtr
<DataSourceSurface
> input1
=
1021 GetInputDataSourceSurface(IN_BLEND_IN
, aRect
, NEED_COLOR_CHANNELS
);
1022 RefPtr
<DataSourceSurface
> input2
=
1023 GetInputDataSourceSurface(IN_BLEND_IN2
, aRect
, NEED_COLOR_CHANNELS
);
1025 // Null inputs need to be treated as transparent.
1027 // First case: both are transparent.
1028 if (!input1
&& !input2
) {
1029 // Then the result is transparent, too.
1033 // Second case: one of them is transparent. Return the non-transparent one.
1034 if (!input1
|| !input2
) {
1035 return input1
? input1
.forget() : input2
.forget();
1038 // Third case: both are non-transparent.
1039 // Apply normal filtering.
1040 RefPtr
<DataSourceSurface
> target
=
1041 FilterProcessing::ApplyBlending(input1
, input2
, mBlendMode
);
1042 if (target
!= nullptr) {
1043 return target
.forget();
1046 IntSize size
= input1
->GetSize();
1047 target
= Factory::CreateDataSourceSurface(size
, SurfaceFormat::B8G8R8A8
);
1048 if (MOZ2D_WARN_IF(!target
)) {
1052 CopyRect(input1
, target
, IntRect(IntPoint(), size
), IntPoint());
1054 // This needs to stay in scope until the draw target has been flushed.
1055 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::READ_WRITE
);
1056 if (MOZ2D_WARN_IF(!targetMap
.IsMapped())) {
1060 RefPtr
<DrawTarget
> dt
= Factory::CreateDrawTargetForData(
1061 BackendType::CAIRO
, targetMap
.GetData(), target
->GetSize(),
1062 targetMap
.GetStride(), target
->GetFormat());
1066 << "FilterNodeBlendSoftware::Render failed in CreateDrawTargetForData";
1070 Rect
r(0, 0, size
.width
, size
.height
);
1071 dt
->DrawSurface(input2
, r
, r
, DrawSurfaceOptions(),
1072 DrawOptions(1.0f
, ToBlendOp(mBlendMode
)));
1074 return target
.forget();
1077 void FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect
& aRect
) {
1078 RequestInputRect(IN_BLEND_IN
, aRect
);
1079 RequestInputRect(IN_BLEND_IN2
, aRect
);
1082 IntRect
FilterNodeBlendSoftware::MapRectToSource(const IntRect
& aRect
,
1083 const IntRect
& aMax
,
1084 FilterNode
* aSourceNode
) {
1085 IntRect result
= MapInputRectToSource(IN_BLEND_IN
, aRect
, aMax
, aSourceNode
);
1086 result
.OrWith(MapInputRectToSource(IN_BLEND_IN2
, aRect
, aMax
, aSourceNode
));
1090 IntRect
FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
1091 return GetInputRectInRect(IN_BLEND_IN
, aRect
)
1092 .Union(GetInputRectInRect(IN_BLEND_IN2
, aRect
))
1096 FilterNodeTransformSoftware::FilterNodeTransformSoftware()
1097 : mSamplingFilter(SamplingFilter::GOOD
) {}
1099 int32_t FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex
) {
1100 switch (aInputEnumIndex
) {
1101 case IN_TRANSFORM_IN
:
1108 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex
,
1110 MOZ_ASSERT(aIndex
== ATT_TRANSFORM_FILTER
);
1111 mSamplingFilter
= static_cast<SamplingFilter
>(aFilter
);
1115 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex
,
1116 const Matrix
& aMatrix
) {
1117 MOZ_ASSERT(aIndex
== ATT_TRANSFORM_MATRIX
);
1122 IntRect
FilterNodeTransformSoftware::SourceRectForOutputRect(
1123 const IntRect
& aRect
) {
1124 if (aRect
.IsEmpty()) {
1128 Matrix
inverted(mMatrix
);
1129 if (!inverted
.Invert()) {
1133 Rect neededRect
= inverted
.TransformBounds(Rect(aRect
));
1134 neededRect
.RoundOut();
1135 IntRect neededIntRect
;
1136 if (!neededRect
.ToIntRect(&neededIntRect
)) {
1139 return GetInputRectInRect(IN_TRANSFORM_IN
, neededIntRect
);
1142 IntRect
FilterNodeTransformSoftware::MapRectToSource(const IntRect
& aRect
,
1143 const IntRect
& aMax
,
1144 FilterNode
* aSourceNode
) {
1145 if (aRect
.IsEmpty()) {
1149 Matrix
inverted(mMatrix
);
1150 if (!inverted
.Invert()) {
1154 Rect neededRect
= inverted
.TransformBounds(Rect(aRect
));
1155 neededRect
.RoundOut();
1156 IntRect neededIntRect
;
1157 if (!neededRect
.ToIntRect(&neededIntRect
)) {
1160 return MapInputRectToSource(IN_TRANSFORM_IN
, neededIntRect
, aMax
,
1164 already_AddRefed
<DataSourceSurface
> FilterNodeTransformSoftware::Render(
1165 const IntRect
& aRect
) {
1166 IntRect srcRect
= SourceRectForOutputRect(aRect
);
1168 RefPtr
<DataSourceSurface
> input
=
1169 GetInputDataSourceSurface(IN_TRANSFORM_IN
, srcRect
);
1175 Matrix transform
= Matrix::Translation(srcRect
.X(), srcRect
.Y()) * mMatrix
*
1176 Matrix::Translation(-aRect
.X(), -aRect
.Y());
1177 if (transform
.IsIdentity() && srcRect
.Size() == aRect
.Size()) {
1178 return input
.forget();
1181 RefPtr
<DataSourceSurface
> surf
=
1182 Factory::CreateDataSourceSurface(aRect
.Size(), input
->GetFormat(), true);
1188 DataSourceSurface::MappedSurface mapping
;
1189 if (!surf
->Map(DataSourceSurface::MapType::WRITE
, &mapping
)) {
1191 << "FilterNodeTransformSoftware::Render failed to map surface";
1195 RefPtr
<DrawTarget
> dt
= Factory::CreateDrawTargetForData(
1196 BackendType::CAIRO
, mapping
.mData
, surf
->GetSize(), mapping
.mStride
,
1199 gfxWarning() << "FilterNodeTransformSoftware::Render failed in "
1200 "CreateDrawTargetForData";
1204 Rect
r(0, 0, srcRect
.Width(), srcRect
.Height());
1205 dt
->SetTransform(transform
);
1206 dt
->DrawSurface(input
, r
, r
, DrawSurfaceOptions(mSamplingFilter
));
1210 return surf
.forget();
1213 void FilterNodeTransformSoftware::RequestFromInputsForRect(
1214 const IntRect
& aRect
) {
1215 RequestInputRect(IN_TRANSFORM_IN
, SourceRectForOutputRect(aRect
));
1218 IntRect
FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
1219 IntRect srcRect
= SourceRectForOutputRect(aRect
);
1220 if (srcRect
.IsEmpty()) {
1224 Rect outRect
= mMatrix
.TransformBounds(Rect(srcRect
));
1227 if (!outRect
.ToIntRect(&outIntRect
)) {
1230 return outIntRect
.Intersect(aRect
);
1233 FilterNodeMorphologySoftware::FilterNodeMorphologySoftware()
1234 : mOperator(MORPHOLOGY_OPERATOR_ERODE
) {}
1236 int32_t FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex
) {
1237 switch (aInputEnumIndex
) {
1238 case IN_MORPHOLOGY_IN
:
1245 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex
,
1246 const IntSize
& aRadii
) {
1247 MOZ_ASSERT(aIndex
== ATT_MORPHOLOGY_RADII
);
1248 mRadii
.width
= std::min(std::max(aRadii
.width
, 0), 100000);
1249 mRadii
.height
= std::min(std::max(aRadii
.height
, 0), 100000);
1253 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex
,
1254 uint32_t aOperator
) {
1255 MOZ_ASSERT(aIndex
== ATT_MORPHOLOGY_OPERATOR
);
1256 mOperator
= static_cast<MorphologyOperator
>(aOperator
);
1260 static already_AddRefed
<DataSourceSurface
> ApplyMorphology(
1261 const IntRect
& aSourceRect
, DataSourceSurface
* aInput
,
1262 const IntRect
& aDestRect
, int32_t rx
, int32_t ry
,
1263 MorphologyOperator aOperator
) {
1264 IntRect srcRect
= aSourceRect
- aDestRect
.TopLeft();
1265 IntRect destRect
= aDestRect
- aDestRect
.TopLeft();
1266 IntRect
tmpRect(destRect
.X(), srcRect
.Y(), destRect
.Width(),
1269 IntMargin margin
= srcRect
- destRect
;
1270 MOZ_ASSERT(margin
.top
>= ry
&& margin
.right
>= rx
&& margin
.bottom
>= ry
&&
1272 "insufficient margin");
1275 RefPtr
<DataSourceSurface
> tmp
;
1279 tmp
= Factory::CreateDataSourceSurface(tmpRect
.Size(),
1280 SurfaceFormat::B8G8R8A8
);
1281 if (MOZ2D_WARN_IF(!tmp
)) {
1285 DataSourceSurface::ScopedMap
sourceMap(aInput
, DataSourceSurface::READ
);
1286 DataSourceSurface::ScopedMap
tmpMap(tmp
, DataSourceSurface::WRITE
);
1287 if (MOZ2D_WARN_IF(!sourceMap
.IsMapped() || !tmpMap
.IsMapped())) {
1290 uint8_t* sourceData
= DataAtOffset(aInput
, sourceMap
.GetMappedSurface(),
1291 destRect
.TopLeft() - srcRect
.TopLeft());
1292 uint8_t* tmpData
= DataAtOffset(tmp
, tmpMap
.GetMappedSurface(),
1293 destRect
.TopLeft() - tmpRect
.TopLeft());
1295 FilterProcessing::ApplyMorphologyHorizontal(
1296 sourceData
, sourceMap
.GetStride(), tmpData
, tmpMap
.GetStride(), tmpRect
,
1300 RefPtr
<DataSourceSurface
> dest
;
1304 dest
= Factory::CreateDataSourceSurface(destRect
.Size(),
1305 SurfaceFormat::B8G8R8A8
);
1306 if (MOZ2D_WARN_IF(!dest
)) {
1310 DataSourceSurface::ScopedMap
tmpMap(tmp
, DataSourceSurface::READ
);
1311 DataSourceSurface::ScopedMap
destMap(dest
, DataSourceSurface::WRITE
);
1312 if (MOZ2D_WARN_IF(!tmpMap
.IsMapped() || !destMap
.IsMapped())) {
1315 int32_t tmpStride
= tmpMap
.GetStride();
1316 uint8_t* tmpData
= DataAtOffset(tmp
, tmpMap
.GetMappedSurface(),
1317 destRect
.TopLeft() - tmpRect
.TopLeft());
1319 int32_t destStride
= destMap
.GetStride();
1320 uint8_t* destData
= destMap
.GetData();
1322 FilterProcessing::ApplyMorphologyVertical(
1323 tmpData
, tmpStride
, destData
, destStride
, destRect
, ry
, aOperator
);
1326 return dest
.forget();
1329 already_AddRefed
<DataSourceSurface
> FilterNodeMorphologySoftware::Render(
1330 const IntRect
& aRect
) {
1331 IntRect srcRect
= aRect
;
1332 srcRect
.Inflate(mRadii
);
1334 RefPtr
<DataSourceSurface
> input
=
1335 GetInputDataSourceSurface(IN_MORPHOLOGY_IN
, srcRect
, NEED_COLOR_CHANNELS
);
1340 int32_t rx
= mRadii
.width
;
1341 int32_t ry
= mRadii
.height
;
1343 if (rx
== 0 && ry
== 0) {
1344 return input
.forget();
1347 return ApplyMorphology(srcRect
, input
, aRect
, rx
, ry
, mOperator
);
1350 void FilterNodeMorphologySoftware::RequestFromInputsForRect(
1351 const IntRect
& aRect
) {
1352 IntRect srcRect
= aRect
;
1353 srcRect
.Inflate(mRadii
);
1354 RequestInputRect(IN_MORPHOLOGY_IN
, srcRect
);
1357 IntRect
FilterNodeMorphologySoftware::GetOutputRectInRect(
1358 const IntRect
& aRect
) {
1359 IntRect inflatedSourceRect
= aRect
;
1360 inflatedSourceRect
.Inflate(mRadii
);
1361 IntRect inputRect
= GetInputRectInRect(IN_MORPHOLOGY_IN
, inflatedSourceRect
);
1362 if (mOperator
== MORPHOLOGY_OPERATOR_ERODE
) {
1363 inputRect
.Deflate(mRadii
);
1365 inputRect
.Inflate(mRadii
);
1367 return inputRect
.Intersect(aRect
);
1370 int32_t FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex
) {
1371 switch (aInputEnumIndex
) {
1372 case IN_COLOR_MATRIX_IN
:
1379 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex
,
1380 const Matrix5x4
& aMatrix
) {
1381 MOZ_ASSERT(aIndex
== ATT_COLOR_MATRIX_MATRIX
);
1386 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex
,
1387 uint32_t aAlphaMode
) {
1388 MOZ_ASSERT(aIndex
== ATT_COLOR_MATRIX_ALPHA_MODE
);
1389 mAlphaMode
= (AlphaMode
)aAlphaMode
;
1393 static already_AddRefed
<DataSourceSurface
> Premultiply(
1394 DataSourceSurface
* aSurface
) {
1395 if (aSurface
->GetFormat() == SurfaceFormat::A8
) {
1396 RefPtr
<DataSourceSurface
> surface(aSurface
);
1397 return surface
.forget();
1400 IntSize size
= aSurface
->GetSize();
1401 RefPtr
<DataSourceSurface
> target
=
1402 Factory::CreateDataSourceSurface(size
, SurfaceFormat::B8G8R8A8
);
1403 if (MOZ2D_WARN_IF(!target
)) {
1407 DataSourceSurface::ScopedMap
inputMap(aSurface
, DataSourceSurface::READ
);
1408 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
1409 if (MOZ2D_WARN_IF(!inputMap
.IsMapped() || !targetMap
.IsMapped())) {
1413 uint8_t* inputData
= inputMap
.GetData();
1414 int32_t inputStride
= inputMap
.GetStride();
1415 uint8_t* targetData
= targetMap
.GetData();
1416 int32_t targetStride
= targetMap
.GetStride();
1418 FilterProcessing::DoPremultiplicationCalculation(
1419 size
, targetData
, targetStride
, inputData
, inputStride
);
1421 return target
.forget();
1424 static already_AddRefed
<DataSourceSurface
> Unpremultiply(
1425 DataSourceSurface
* aSurface
) {
1426 if (aSurface
->GetFormat() == SurfaceFormat::A8
) {
1427 RefPtr
<DataSourceSurface
> surface(aSurface
);
1428 return surface
.forget();
1431 IntSize size
= aSurface
->GetSize();
1432 RefPtr
<DataSourceSurface
> target
=
1433 Factory::CreateDataSourceSurface(size
, SurfaceFormat::B8G8R8A8
);
1434 if (MOZ2D_WARN_IF(!target
)) {
1438 DataSourceSurface::ScopedMap
inputMap(aSurface
, DataSourceSurface::READ
);
1439 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
1440 if (MOZ2D_WARN_IF(!inputMap
.IsMapped() || !targetMap
.IsMapped())) {
1444 uint8_t* inputData
= inputMap
.GetData();
1445 int32_t inputStride
= inputMap
.GetStride();
1446 uint8_t* targetData
= targetMap
.GetData();
1447 int32_t targetStride
= targetMap
.GetStride();
1449 FilterProcessing::DoUnpremultiplicationCalculation(
1450 size
, targetData
, targetStride
, inputData
, inputStride
);
1452 return target
.forget();
1455 static already_AddRefed
<DataSourceSurface
> Opacity(DataSourceSurface
* aSurface
,
1457 if (aValue
== 1.0f
) {
1458 RefPtr
<DataSourceSurface
> surface(aSurface
);
1459 return surface
.forget();
1462 IntSize size
= aSurface
->GetSize();
1463 RefPtr
<DataSourceSurface
> target
=
1464 Factory::CreateDataSourceSurface(size
, aSurface
->GetFormat());
1465 if (MOZ2D_WARN_IF(!target
)) {
1469 DataSourceSurface::ScopedMap
inputMap(aSurface
, DataSourceSurface::READ
);
1470 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
1471 if (MOZ2D_WARN_IF(!inputMap
.IsMapped() || !targetMap
.IsMapped())) {
1475 uint8_t* inputData
= inputMap
.GetData();
1476 int32_t inputStride
= inputMap
.GetStride();
1477 uint8_t* targetData
= targetMap
.GetData();
1478 int32_t targetStride
= targetMap
.GetStride();
1480 if (aSurface
->GetFormat() == SurfaceFormat::A8
) {
1481 FilterProcessing::DoOpacityCalculationA8(size
, targetData
, targetStride
,
1482 inputData
, inputStride
, aValue
);
1484 MOZ_ASSERT(aSurface
->GetFormat() == SurfaceFormat::B8G8R8A8
);
1485 FilterProcessing::DoOpacityCalculation(size
, targetData
, targetStride
,
1486 inputData
, inputStride
, aValue
);
1489 return target
.forget();
1492 already_AddRefed
<DataSourceSurface
> FilterNodeColorMatrixSoftware::Render(
1493 const IntRect
& aRect
) {
1494 RefPtr
<DataSourceSurface
> input
=
1495 GetInputDataSourceSurface(IN_COLOR_MATRIX_IN
, aRect
, NEED_COLOR_CHANNELS
);
1500 if (mAlphaMode
== ALPHA_MODE_PREMULTIPLIED
) {
1501 input
= Unpremultiply(input
);
1504 RefPtr
<DataSourceSurface
> result
=
1505 FilterProcessing::ApplyColorMatrix(input
, mMatrix
);
1507 if (mAlphaMode
== ALPHA_MODE_PREMULTIPLIED
) {
1508 result
= Premultiply(result
);
1511 return result
.forget();
1514 void FilterNodeColorMatrixSoftware::RequestFromInputsForRect(
1515 const IntRect
& aRect
) {
1516 RequestInputRect(IN_COLOR_MATRIX_IN
, aRect
);
1519 IntRect
FilterNodeColorMatrixSoftware::MapRectToSource(
1520 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
1521 return MapInputRectToSource(IN_COLOR_MATRIX_IN
, aRect
, aMax
, aSourceNode
);
1524 IntRect
FilterNodeColorMatrixSoftware::GetOutputRectInRect(
1525 const IntRect
& aRect
) {
1526 if (mMatrix
._54
> 0.0f
) {
1529 return GetInputRectInRect(IN_COLOR_MATRIX_IN
, aRect
);
1532 void FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex
,
1533 const DeviceColor
& aColor
) {
1534 MOZ_ASSERT(aIndex
== ATT_FLOOD_COLOR
);
1539 static uint32_t ColorToBGRA(const DeviceColor
& aColor
) {
1542 uint8_t components
[4];
1544 components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] =
1545 NS_lround(aColor
.r
* aColor
.a
* 255.0f
);
1546 components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] =
1547 NS_lround(aColor
.g
* aColor
.a
* 255.0f
);
1548 components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] =
1549 NS_lround(aColor
.b
* aColor
.a
* 255.0f
);
1550 components
[B8G8R8A8_COMPONENT_BYTEOFFSET_A
] = NS_lround(aColor
.a
* 255.0f
);
1554 static SurfaceFormat
FormatForColor(DeviceColor aColor
) {
1555 if (aColor
.r
== 0 && aColor
.g
== 0 && aColor
.b
== 0) {
1556 return SurfaceFormat::A8
;
1558 return SurfaceFormat::B8G8R8A8
;
1561 already_AddRefed
<DataSourceSurface
> FilterNodeFloodSoftware::Render(
1562 const IntRect
& aRect
) {
1563 SurfaceFormat format
= FormatForColor(mColor
);
1564 RefPtr
<DataSourceSurface
> target
=
1565 Factory::CreateDataSourceSurface(aRect
.Size(), format
);
1566 if (MOZ2D_WARN_IF(!target
)) {
1570 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
1571 if (MOZ2D_WARN_IF(!targetMap
.IsMapped())) {
1575 uint8_t* targetData
= targetMap
.GetData();
1576 int32_t stride
= targetMap
.GetStride();
1578 if (format
== SurfaceFormat::B8G8R8A8
) {
1579 uint32_t color
= ColorToBGRA(mColor
);
1580 for (int32_t y
= 0; y
< aRect
.Height(); y
++) {
1581 for (int32_t x
= 0; x
< aRect
.Width(); x
++) {
1582 *((uint32_t*)targetData
+ x
) = color
;
1584 PodZero(&targetData
[aRect
.Width() * 4], stride
- aRect
.Width() * 4);
1585 targetData
+= stride
;
1587 } else if (format
== SurfaceFormat::A8
) {
1588 uint8_t alpha
= NS_lround(mColor
.a
* 255.0f
);
1589 for (int32_t y
= 0; y
< aRect
.Height(); y
++) {
1590 for (int32_t x
= 0; x
< aRect
.Width(); x
++) {
1591 targetData
[x
] = alpha
;
1593 PodZero(&targetData
[aRect
.Width()], stride
- aRect
.Width());
1594 targetData
+= stride
;
1597 gfxDevCrash(LogReason::FilterInputFormat
)
1598 << "Bad format in flood render " << (int)format
;
1602 return target
.forget();
1605 // Override GetOutput to get around caching. Rendering simple floods is
1606 // comparatively fast.
1607 already_AddRefed
<DataSourceSurface
> FilterNodeFloodSoftware::GetOutput(
1608 const IntRect
& aRect
) {
1609 return Render(aRect
);
1612 IntRect
FilterNodeFloodSoftware::MapRectToSource(const IntRect
& aRect
,
1613 const IntRect
& aMax
,
1614 FilterNode
* aSourceNode
) {
1618 IntRect
FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
1619 if (mColor
.a
== 0.0f
) {
1625 int32_t FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex
) {
1626 switch (aInputEnumIndex
) {
1634 void FilterNodeTileSoftware::SetAttribute(uint32_t aIndex
,
1635 const IntRect
& aSourceRect
) {
1636 MOZ_ASSERT(aIndex
== ATT_TILE_SOURCE_RECT
);
1637 mSourceRect
.SetRect(int32_t(aSourceRect
.X()), int32_t(aSourceRect
.Y()),
1638 int32_t(aSourceRect
.Width()),
1639 int32_t(aSourceRect
.Height()));
1644 struct CompareIntRects
{
1645 bool operator()(const IntRect
& a
, const IntRect
& b
) const {
1646 if (a
.X() != b
.X()) {
1647 return a
.X() < b
.X();
1649 if (a
.Y() != b
.Y()) {
1650 return a
.Y() < b
.Y();
1652 if (a
.Width() != b
.Width()) {
1653 return a
.Width() < b
.Width();
1655 return a
.Height() < b
.Height();
1661 already_AddRefed
<DataSourceSurface
> FilterNodeTileSoftware::Render(
1662 const IntRect
& aRect
) {
1663 if (mSourceRect
.IsEmpty()) {
1667 if (mSourceRect
.Contains(aRect
)) {
1668 return GetInputDataSourceSurface(IN_TILE_IN
, aRect
);
1671 RefPtr
<DataSourceSurface
> target
;
1673 typedef std::map
<IntRect
, RefPtr
<DataSourceSurface
>, CompareIntRects
>
1677 IntPoint startIndex
= TileIndex(mSourceRect
, aRect
.TopLeft());
1678 IntPoint endIndex
= TileIndex(mSourceRect
, aRect
.BottomRight());
1679 for (int32_t ix
= startIndex
.x
; ix
<= endIndex
.x
; ix
++) {
1680 for (int32_t iy
= startIndex
.y
; iy
<= endIndex
.y
; iy
++) {
1681 IntPoint
sourceToDestOffset(ix
* mSourceRect
.Width(),
1682 iy
* mSourceRect
.Height());
1683 IntRect destRect
= aRect
.Intersect(mSourceRect
+ sourceToDestOffset
);
1684 IntRect srcRect
= destRect
- sourceToDestOffset
;
1685 if (srcRect
.IsEmpty()) {
1689 RefPtr
<DataSourceSurface
> input
;
1690 InputMap::iterator it
= inputs
.find(srcRect
);
1691 if (it
== inputs
.end()) {
1692 input
= GetInputDataSourceSurface(IN_TILE_IN
, srcRect
);
1693 inputs
[srcRect
] = input
;
1701 // We delay creating the target until now because we want to use the
1702 // same format as our input filter, and we do not actually know the
1703 // input format before we call GetInputDataSourceSurface.
1705 Factory::CreateDataSourceSurface(aRect
.Size(), input
->GetFormat());
1706 if (MOZ2D_WARN_IF(!target
)) {
1711 if (input
->GetFormat() != target
->GetFormat()) {
1712 // Different rectangles of the input can have different formats. If
1713 // that happens, just convert everything to B8G8R8A8.
1714 target
= FilterProcessing::ConvertToB8G8R8A8(target
);
1715 input
= FilterProcessing::ConvertToB8G8R8A8(input
);
1716 if (MOZ2D_WARN_IF(!target
) || MOZ2D_WARN_IF(!input
)) {
1721 CopyRect(input
, target
, srcRect
- srcRect
.TopLeft(),
1722 destRect
.TopLeft() - aRect
.TopLeft());
1726 return target
.forget();
1729 void FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect
& aRect
) {
1730 // Do not request anything.
1731 // Source rects for the tile filter can be discontinuous with large gaps
1732 // between them. Requesting those from our input filter might cause it to
1733 // render the whole bounding box of all of them, which would be wasteful.
1736 IntRect
FilterNodeTileSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
1740 FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware()
1741 : mDisableR(true), mDisableG(true), mDisableB(true), mDisableA(true) {}
1743 void FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex
,
1746 case ATT_TRANSFER_DISABLE_R
:
1747 mDisableR
= aDisable
;
1749 case ATT_TRANSFER_DISABLE_G
:
1750 mDisableG
= aDisable
;
1752 case ATT_TRANSFER_DISABLE_B
:
1753 mDisableB
= aDisable
;
1755 case ATT_TRANSFER_DISABLE_A
:
1756 mDisableA
= aDisable
;
1759 MOZ_CRASH("GFX: FilterNodeComponentTransferSoftware::SetAttribute");
1764 void FilterNodeComponentTransferSoftware::GenerateLookupTable(
1765 ptrdiff_t aComponent
, uint8_t aTables
[4][256], bool aDisabled
) {
1767 static uint8_t sIdentityLookupTable
[256];
1768 static bool sInitializedIdentityLookupTable
= false;
1769 if (!sInitializedIdentityLookupTable
) {
1770 for (int32_t i
= 0; i
< 256; i
++) {
1771 sIdentityLookupTable
[i
] = i
;
1773 sInitializedIdentityLookupTable
= true;
1775 memcpy(aTables
[aComponent
], sIdentityLookupTable
, 256);
1777 FillLookupTable(aComponent
, aTables
[aComponent
]);
1781 template <uint32_t BytesPerPixel
>
1782 static void TransferComponents(
1783 DataSourceSurface
* aInput
, DataSourceSurface
* aTarget
,
1784 const uint8_t aLookupTables
[BytesPerPixel
][256]) {
1785 MOZ_ASSERT(aInput
->GetFormat() == aTarget
->GetFormat(), "different formats");
1786 IntSize size
= aInput
->GetSize();
1788 DataSourceSurface::ScopedMap
sourceMap(aInput
, DataSourceSurface::READ
);
1789 DataSourceSurface::ScopedMap
targetMap(aTarget
, DataSourceSurface::WRITE
);
1790 if (MOZ2D_WARN_IF(!sourceMap
.IsMapped() || !targetMap
.IsMapped())) {
1794 uint8_t* sourceData
= sourceMap
.GetData();
1795 int32_t sourceStride
= sourceMap
.GetStride();
1796 uint8_t* targetData
= targetMap
.GetData();
1797 int32_t targetStride
= targetMap
.GetStride();
1799 MOZ_ASSERT(sourceStride
<= targetStride
, "target smaller than source");
1801 for (int32_t y
= 0; y
< size
.height
; y
++) {
1802 for (int32_t x
= 0; x
< size
.width
; x
++) {
1803 uint32_t sourceIndex
= y
* sourceStride
+ x
* BytesPerPixel
;
1804 uint32_t targetIndex
= y
* targetStride
+ x
* BytesPerPixel
;
1805 for (uint32_t i
= 0; i
< BytesPerPixel
; i
++) {
1806 targetData
[targetIndex
+ i
] =
1807 aLookupTables
[i
][sourceData
[sourceIndex
+ i
]];
1811 // Zero padding to keep valgrind happy.
1812 PodZero(&targetData
[y
* targetStride
+ size
.width
* BytesPerPixel
],
1813 targetStride
- size
.width
* BytesPerPixel
);
1817 static bool IsAllZero(const uint8_t aLookupTable
[256]) {
1818 for (int32_t i
= 0; i
< 256; i
++) {
1819 if (aLookupTable
[i
] != 0) {
1826 already_AddRefed
<DataSourceSurface
> FilterNodeComponentTransferSoftware::Render(
1827 const IntRect
& aRect
) {
1828 if (mDisableR
&& mDisableG
&& mDisableB
&& mDisableA
) {
1829 return GetInputDataSourceSurface(IN_TRANSFER_IN
, aRect
);
1832 uint8_t lookupTables
[4][256];
1833 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R
, lookupTables
, mDisableR
);
1834 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G
, lookupTables
, mDisableG
);
1835 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B
, lookupTables
, mDisableB
);
1836 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A
, lookupTables
, mDisableA
);
1838 bool needColorChannels
=
1839 lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
][0] != 0 ||
1840 lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
][0] != 0 ||
1841 lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
][0] != 0;
1843 FormatHint pref
= needColorChannels
? NEED_COLOR_CHANNELS
: CAN_HANDLE_A8
;
1845 RefPtr
<DataSourceSurface
> input
=
1846 GetInputDataSourceSurface(IN_TRANSFER_IN
, aRect
, pref
);
1851 if (input
->GetFormat() == SurfaceFormat::B8G8R8A8
&& !needColorChannels
) {
1852 bool colorChannelsBecomeBlack
=
1853 IsAllZero(lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
]) &&
1854 IsAllZero(lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
]) &&
1855 IsAllZero(lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
]);
1857 if (colorChannelsBecomeBlack
) {
1858 input
= FilterProcessing::ExtractAlpha(input
);
1862 SurfaceFormat format
= input
->GetFormat();
1863 if (format
== SurfaceFormat::A8
&& mDisableA
) {
1864 return input
.forget();
1867 RefPtr
<DataSourceSurface
> target
=
1868 Factory::CreateDataSourceSurface(aRect
.Size(), format
);
1869 if (MOZ2D_WARN_IF(!target
)) {
1873 if (format
== SurfaceFormat::A8
) {
1874 TransferComponents
<1>(input
, target
,
1875 &lookupTables
[B8G8R8A8_COMPONENT_BYTEOFFSET_A
]);
1877 TransferComponents
<4>(input
, target
, lookupTables
);
1880 return target
.forget();
1883 void FilterNodeComponentTransferSoftware::RequestFromInputsForRect(
1884 const IntRect
& aRect
) {
1885 RequestInputRect(IN_TRANSFER_IN
, aRect
);
1888 IntRect
FilterNodeComponentTransferSoftware::MapRectToSource(
1889 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
1890 return MapInputRectToSource(IN_TRANSFER_IN
, aRect
, aMax
, aSourceNode
);
1893 IntRect
FilterNodeComponentTransferSoftware::GetOutputRectInRect(
1894 const IntRect
& aRect
) {
1896 return GetInputRectInRect(IN_TRANSFER_IN
, aRect
);
1901 int32_t FilterNodeComponentTransferSoftware::InputIndex(
1902 uint32_t aInputEnumIndex
) {
1903 switch (aInputEnumIndex
) {
1904 case IN_TRANSFER_IN
:
1911 void FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex
,
1912 const Float
* aFloat
,
1914 std::vector
<Float
> table(aFloat
, aFloat
+ aSize
);
1916 case ATT_TABLE_TRANSFER_TABLE_R
:
1919 case ATT_TABLE_TRANSFER_TABLE_G
:
1922 case ATT_TABLE_TRANSFER_TABLE_B
:
1925 case ATT_TABLE_TRANSFER_TABLE_A
:
1929 MOZ_CRASH("GFX: FilterNodeTableTransferSoftware::SetAttribute");
1934 void FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent
,
1935 uint8_t aTable
[256]) {
1936 switch (aComponent
) {
1937 case B8G8R8A8_COMPONENT_BYTEOFFSET_R
:
1938 FillLookupTableImpl(mTableR
, aTable
);
1940 case B8G8R8A8_COMPONENT_BYTEOFFSET_G
:
1941 FillLookupTableImpl(mTableG
, aTable
);
1943 case B8G8R8A8_COMPONENT_BYTEOFFSET_B
:
1944 FillLookupTableImpl(mTableB
, aTable
);
1946 case B8G8R8A8_COMPONENT_BYTEOFFSET_A
:
1947 FillLookupTableImpl(mTableA
, aTable
);
1950 MOZ_ASSERT(false, "unknown component");
1955 void FilterNodeTableTransferSoftware::FillLookupTableImpl(
1956 std::vector
<Float
>& aTableValues
, uint8_t aTable
[256]) {
1957 uint32_t tvLength
= aTableValues
.size();
1962 for (size_t i
= 0; i
< 256; i
++) {
1963 uint32_t k
= (i
* (tvLength
- 1)) / 255;
1964 Float v1
= aTableValues
[k
];
1965 Float v2
= aTableValues
[std::min(k
+ 1, tvLength
- 1)];
1966 int32_t val
= int32_t(255 * (v1
+ (i
/ 255.0f
- k
/ float(tvLength
- 1)) *
1967 (tvLength
- 1) * (v2
- v1
)));
1968 val
= std::min(255, val
);
1969 val
= std::max(0, val
);
1974 void FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex
,
1975 const Float
* aFloat
,
1977 std::vector
<Float
> discrete(aFloat
, aFloat
+ aSize
);
1979 case ATT_DISCRETE_TRANSFER_TABLE_R
:
1982 case ATT_DISCRETE_TRANSFER_TABLE_G
:
1985 case ATT_DISCRETE_TRANSFER_TABLE_B
:
1988 case ATT_DISCRETE_TRANSFER_TABLE_A
:
1992 MOZ_CRASH("GFX: FilterNodeDiscreteTransferSoftware::SetAttribute");
1997 void FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent
,
1998 uint8_t aTable
[256]) {
1999 switch (aComponent
) {
2000 case B8G8R8A8_COMPONENT_BYTEOFFSET_R
:
2001 FillLookupTableImpl(mTableR
, aTable
);
2003 case B8G8R8A8_COMPONENT_BYTEOFFSET_G
:
2004 FillLookupTableImpl(mTableG
, aTable
);
2006 case B8G8R8A8_COMPONENT_BYTEOFFSET_B
:
2007 FillLookupTableImpl(mTableB
, aTable
);
2009 case B8G8R8A8_COMPONENT_BYTEOFFSET_A
:
2010 FillLookupTableImpl(mTableA
, aTable
);
2013 MOZ_ASSERT(false, "unknown component");
2018 void FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(
2019 std::vector
<Float
>& aTableValues
, uint8_t aTable
[256]) {
2020 uint32_t tvLength
= aTableValues
.size();
2025 for (size_t i
= 0; i
< 256; i
++) {
2026 uint32_t k
= (i
* tvLength
) / 255;
2027 k
= std::min(k
, tvLength
- 1);
2028 Float v
= aTableValues
[k
];
2029 int32_t val
= NS_lround(255 * v
);
2030 val
= std::min(255, val
);
2031 val
= std::max(0, val
);
2036 FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware()
2046 void FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex
,
2049 case ATT_LINEAR_TRANSFER_SLOPE_R
:
2052 case ATT_LINEAR_TRANSFER_INTERCEPT_R
:
2053 mInterceptR
= aValue
;
2055 case ATT_LINEAR_TRANSFER_SLOPE_G
:
2058 case ATT_LINEAR_TRANSFER_INTERCEPT_G
:
2059 mInterceptG
= aValue
;
2061 case ATT_LINEAR_TRANSFER_SLOPE_B
:
2064 case ATT_LINEAR_TRANSFER_INTERCEPT_B
:
2065 mInterceptB
= aValue
;
2067 case ATT_LINEAR_TRANSFER_SLOPE_A
:
2070 case ATT_LINEAR_TRANSFER_INTERCEPT_A
:
2071 mInterceptA
= aValue
;
2074 MOZ_CRASH("GFX: FilterNodeLinearTransferSoftware::SetAttribute");
2079 void FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent
,
2080 uint8_t aTable
[256]) {
2081 switch (aComponent
) {
2082 case B8G8R8A8_COMPONENT_BYTEOFFSET_R
:
2083 FillLookupTableImpl(mSlopeR
, mInterceptR
, aTable
);
2085 case B8G8R8A8_COMPONENT_BYTEOFFSET_G
:
2086 FillLookupTableImpl(mSlopeG
, mInterceptG
, aTable
);
2088 case B8G8R8A8_COMPONENT_BYTEOFFSET_B
:
2089 FillLookupTableImpl(mSlopeB
, mInterceptB
, aTable
);
2091 case B8G8R8A8_COMPONENT_BYTEOFFSET_A
:
2092 FillLookupTableImpl(mSlopeA
, mInterceptA
, aTable
);
2095 MOZ_ASSERT(false, "unknown component");
2100 void FilterNodeLinearTransferSoftware::FillLookupTableImpl(
2101 Float aSlope
, Float aIntercept
, uint8_t aTable
[256]) {
2102 for (size_t i
= 0; i
< 256; i
++) {
2103 int32_t val
= NS_lround(aSlope
* i
+ 255 * aIntercept
);
2104 val
= std::min(255, val
);
2105 val
= std::max(0, val
);
2110 FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware()
2124 void FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex
,
2127 case ATT_GAMMA_TRANSFER_AMPLITUDE_R
:
2128 mAmplitudeR
= aValue
;
2130 case ATT_GAMMA_TRANSFER_EXPONENT_R
:
2131 mExponentR
= aValue
;
2133 case ATT_GAMMA_TRANSFER_OFFSET_R
:
2136 case ATT_GAMMA_TRANSFER_AMPLITUDE_G
:
2137 mAmplitudeG
= aValue
;
2139 case ATT_GAMMA_TRANSFER_EXPONENT_G
:
2140 mExponentG
= aValue
;
2142 case ATT_GAMMA_TRANSFER_OFFSET_G
:
2145 case ATT_GAMMA_TRANSFER_AMPLITUDE_B
:
2146 mAmplitudeB
= aValue
;
2148 case ATT_GAMMA_TRANSFER_EXPONENT_B
:
2149 mExponentB
= aValue
;
2151 case ATT_GAMMA_TRANSFER_OFFSET_B
:
2154 case ATT_GAMMA_TRANSFER_AMPLITUDE_A
:
2155 mAmplitudeA
= aValue
;
2157 case ATT_GAMMA_TRANSFER_EXPONENT_A
:
2158 mExponentA
= aValue
;
2160 case ATT_GAMMA_TRANSFER_OFFSET_A
:
2164 MOZ_CRASH("GFX: FilterNodeGammaTransferSoftware::SetAttribute");
2169 void FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent
,
2170 uint8_t aTable
[256]) {
2171 switch (aComponent
) {
2172 case B8G8R8A8_COMPONENT_BYTEOFFSET_R
:
2173 FillLookupTableImpl(mAmplitudeR
, mExponentR
, mOffsetR
, aTable
);
2175 case B8G8R8A8_COMPONENT_BYTEOFFSET_G
:
2176 FillLookupTableImpl(mAmplitudeG
, mExponentG
, mOffsetG
, aTable
);
2178 case B8G8R8A8_COMPONENT_BYTEOFFSET_B
:
2179 FillLookupTableImpl(mAmplitudeB
, mExponentB
, mOffsetB
, aTable
);
2181 case B8G8R8A8_COMPONENT_BYTEOFFSET_A
:
2182 FillLookupTableImpl(mAmplitudeA
, mExponentA
, mOffsetA
, aTable
);
2185 MOZ_ASSERT(false, "unknown component");
2190 void FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude
,
2193 uint8_t aTable
[256]) {
2194 for (size_t i
= 0; i
< 256; i
++) {
2196 NS_lround(255 * (aAmplitude
* pow(i
/ 255.0f
, aExponent
) + aOffset
));
2197 val
= std::min(255, val
);
2198 val
= std::max(0, val
);
2203 FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware()
2206 mEdgeMode(EDGE_MODE_DUPLICATE
),
2207 mPreserveAlpha(false) {}
2209 int32_t FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex
) {
2210 switch (aInputEnumIndex
) {
2211 case IN_CONVOLVE_MATRIX_IN
:
2218 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2219 uint32_t aIndex
, const IntSize
& aKernelSize
) {
2220 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_KERNEL_SIZE
);
2221 mKernelSize
= aKernelSize
;
2225 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex
,
2226 const Float
* aMatrix
,
2228 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_KERNEL_MATRIX
);
2229 mKernelMatrix
= std::vector
<Float
>(aMatrix
, aMatrix
+ aSize
);
2233 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex
,
2236 case ATT_CONVOLVE_MATRIX_DIVISOR
:
2239 case ATT_CONVOLVE_MATRIX_BIAS
:
2243 MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
2248 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2249 uint32_t aIndex
, const Size
& aKernelUnitLength
) {
2251 case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH
:
2252 mKernelUnitLength
= aKernelUnitLength
;
2255 MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
2260 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex
,
2261 const IntPoint
& aTarget
) {
2262 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_TARGET
);
2267 void FilterNodeConvolveMatrixSoftware::SetAttribute(
2268 uint32_t aIndex
, const IntRect
& aSourceRect
) {
2269 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_SOURCE_RECT
);
2270 mSourceRect
= aSourceRect
;
2274 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex
,
2275 uint32_t aEdgeMode
) {
2276 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_EDGE_MODE
);
2277 mEdgeMode
= static_cast<ConvolveMatrixEdgeMode
>(aEdgeMode
);
2281 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex
,
2282 bool aPreserveAlpha
) {
2283 MOZ_ASSERT(aIndex
== ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA
);
2284 mPreserveAlpha
= aPreserveAlpha
;
2289 static inline void DebugOnlyCheckColorSamplingAccess(
2290 const uint8_t* aSampleAddress
, const uint8_t* aBoundsBegin
,
2291 const uint8_t* aBoundsEnd
) {
2292 MOZ_ASSERT(aSampleAddress
>= aBoundsBegin
, "accessing before start");
2293 MOZ_ASSERT(aSampleAddress
< aBoundsEnd
, "accessing after end");
2296 # define DebugOnlyCheckColorSamplingAccess(address, boundsBegin, boundsEnd)
2299 static inline uint8_t ColorComponentAtPoint(const uint8_t* aData
,
2301 const uint8_t* aBoundsBegin
,
2302 const uint8_t* aBoundsEnd
,
2303 int32_t x
, int32_t y
, ptrdiff_t bpp
,
2305 DebugOnlyCheckColorSamplingAccess(&aData
[y
* aStride
+ bpp
* x
+ c
],
2306 aBoundsBegin
, aBoundsEnd
);
2307 return aData
[y
* aStride
+ bpp
* x
+ c
];
2310 static inline int32_t ColorAtPoint(const uint8_t* aData
, ptrdiff_t aStride
,
2311 const uint8_t* aBoundsBegin
,
2312 const uint8_t* aBoundsEnd
, int32_t x
,
2314 DebugOnlyCheckColorSamplingAccess(aData
+ y
* aStride
+ 4 * x
, aBoundsBegin
,
2316 return *(uint32_t*)(aData
+ y
* aStride
+ 4 * x
);
2319 // Accepts fractional x & y and does bilinear interpolation.
2320 // Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible.
2321 static inline uint8_t ColorComponentAtPoint(
2322 const uint8_t* aData
, ptrdiff_t aStride
, const uint8_t* aBoundsBegin
,
2323 const uint8_t* aBoundsEnd
, Float x
, Float y
, ptrdiff_t bpp
, ptrdiff_t c
) {
2324 const uint32_t f
= 256;
2325 const int32_t lx
= floor(x
);
2326 const int32_t ly
= floor(y
);
2327 const int32_t tux
= uint32_t((x
- lx
) * f
);
2328 const int32_t tlx
= f
- tux
;
2329 const int32_t tuy
= uint32_t((y
- ly
) * f
);
2330 const int32_t tly
= f
- tuy
;
2331 const uint8_t& cll
= ColorComponentAtPoint(aData
, aStride
, aBoundsBegin
,
2332 aBoundsEnd
, lx
, ly
, bpp
, c
);
2333 const uint8_t& cul
= ColorComponentAtPoint(aData
, aStride
, aBoundsBegin
,
2334 aBoundsEnd
, lx
+ 1, ly
, bpp
, c
);
2335 const uint8_t& clu
= ColorComponentAtPoint(aData
, aStride
, aBoundsBegin
,
2336 aBoundsEnd
, lx
, ly
+ 1, bpp
, c
);
2337 const uint8_t& cuu
= ColorComponentAtPoint(
2338 aData
, aStride
, aBoundsBegin
, aBoundsEnd
, lx
+ 1, ly
+ 1, bpp
, c
);
2339 return ((cll
* tlx
+ cul
* tux
) * tly
+ (clu
* tlx
+ cuu
* tux
) * tuy
+
2344 static int32_t ClampToNonZero(int32_t a
) { return a
* (a
>= 0); }
2346 template <typename CoordType
>
2347 static void ConvolvePixel(const uint8_t* aSourceData
, uint8_t* aTargetData
,
2348 int32_t aWidth
, int32_t aHeight
,
2349 int32_t aSourceStride
, int32_t aTargetStride
,
2350 const uint8_t* aSourceBegin
,
2351 const uint8_t* aSourceEnd
, int32_t aX
, int32_t aY
,
2352 const int32_t* aKernel
, int32_t aBias
, int32_t shiftL
,
2353 int32_t shiftR
, bool aPreserveAlpha
, int32_t aOrderX
,
2354 int32_t aOrderY
, int32_t aTargetX
, int32_t aTargetY
,
2355 CoordType aKernelUnitLengthX
,
2356 CoordType aKernelUnitLengthY
) {
2357 int32_t sum
[4] = {0, 0, 0, 0};
2358 int32_t offsets
[4] = {
2359 B8G8R8A8_COMPONENT_BYTEOFFSET_R
, B8G8R8A8_COMPONENT_BYTEOFFSET_G
,
2360 B8G8R8A8_COMPONENT_BYTEOFFSET_B
, B8G8R8A8_COMPONENT_BYTEOFFSET_A
};
2361 int32_t channels
= aPreserveAlpha
? 3 : 4;
2362 int32_t roundingAddition
= shiftL
== 0 ? 0 : 1 << (shiftL
- 1);
2364 for (int32_t y
= 0; y
< aOrderY
; y
++) {
2365 CoordType sampleY
= aY
+ (y
- aTargetY
) * aKernelUnitLengthY
;
2366 for (int32_t x
= 0; x
< aOrderX
; x
++) {
2367 CoordType sampleX
= aX
+ (x
- aTargetX
) * aKernelUnitLengthX
;
2368 for (int32_t i
= 0; i
< channels
; i
++) {
2370 aKernel
[aOrderX
* y
+ x
] *
2371 ColorComponentAtPoint(aSourceData
, aSourceStride
, aSourceBegin
,
2372 aSourceEnd
, sampleX
, sampleY
, 4, offsets
[i
]);
2376 for (int32_t i
= 0; i
< channels
; i
++) {
2378 umin(ClampToNonZero(sum
[i
] + aBias
), 255 << shiftL
>> shiftR
);
2379 aTargetData
[aY
* aTargetStride
+ 4 * aX
+ offsets
[i
]] =
2380 (clamped
+ roundingAddition
) << shiftR
>> shiftL
;
2382 if (aPreserveAlpha
) {
2383 aTargetData
[aY
* aTargetStride
+ 4 * aX
+ B8G8R8A8_COMPONENT_BYTEOFFSET_A
] =
2384 aSourceData
[aY
* aSourceStride
+ 4 * aX
+
2385 B8G8R8A8_COMPONENT_BYTEOFFSET_A
];
2389 already_AddRefed
<DataSourceSurface
> FilterNodeConvolveMatrixSoftware::Render(
2390 const IntRect
& aRect
) {
2391 if (mKernelUnitLength
.width
== floor(mKernelUnitLength
.width
) &&
2392 mKernelUnitLength
.height
== floor(mKernelUnitLength
.height
)) {
2393 return DoRender(aRect
, (int32_t)mKernelUnitLength
.width
,
2394 (int32_t)mKernelUnitLength
.height
);
2396 return DoRender(aRect
, mKernelUnitLength
.width
, mKernelUnitLength
.height
);
2399 static std::vector
<Float
> ReversedVector(const std::vector
<Float
>& aVector
) {
2400 size_t length
= aVector
.size();
2401 std::vector
<Float
> result(length
, 0);
2402 for (size_t i
= 0; i
< length
; i
++) {
2403 result
[length
- 1 - i
] = aVector
[i
];
2408 static std::vector
<Float
> ScaledVector(const std::vector
<Float
>& aVector
,
2410 size_t length
= aVector
.size();
2411 std::vector
<Float
> result(length
, 0);
2412 for (size_t i
= 0; i
< length
; i
++) {
2413 result
[i
] = aVector
[i
] / aDivisor
;
2418 static Float
MaxVectorSum(const std::vector
<Float
>& aVector
) {
2420 size_t length
= aVector
.size();
2421 for (size_t i
= 0; i
< length
; i
++) {
2422 if (aVector
[i
] > 0) {
2429 // Returns shiftL and shiftR in such a way that
2430 // a << shiftL >> shiftR is roughly a * aFloat.
2431 static void TranslateDoubleToShifts(double aDouble
, int32_t& aShiftL
,
2436 MOZ_CRASH("GFX: TranslateDoubleToShifts");
2439 while (1 << (aShiftR
+ 1) < 1 / aDouble
) {
2443 while (1 << (aShiftL
+ 1) < aDouble
) {
2449 template <typename CoordType
>
2450 already_AddRefed
<DataSourceSurface
> FilterNodeConvolveMatrixSoftware::DoRender(
2451 const IntRect
& aRect
, CoordType aKernelUnitLengthX
,
2452 CoordType aKernelUnitLengthY
) {
2453 if (mKernelSize
.width
<= 0 || mKernelSize
.height
<= 0 ||
2454 mKernelMatrix
.size() !=
2455 uint32_t(mKernelSize
.width
* mKernelSize
.height
) ||
2456 !IntRect(IntPoint(0, 0), mKernelSize
).Contains(mTarget
) ||
2458 return Factory::CreateDataSourceSurface(aRect
.Size(),
2459 SurfaceFormat::B8G8R8A8
, true);
2462 IntRect srcRect
= InflatedSourceRect(aRect
);
2464 // Inflate the source rect by another pixel because the bilinear filtering in
2465 // ColorComponentAtPoint may want to access the margins.
2468 RefPtr
<DataSourceSurface
> input
=
2469 GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN
, srcRect
,
2470 NEED_COLOR_CHANNELS
, mEdgeMode
, &mSourceRect
);
2476 RefPtr
<DataSourceSurface
> target
= Factory::CreateDataSourceSurface(
2477 aRect
.Size(), SurfaceFormat::B8G8R8A8
, true);
2478 if (MOZ2D_WARN_IF(!target
)) {
2482 IntPoint offset
= aRect
.TopLeft() - srcRect
.TopLeft();
2484 DataSourceSurface::ScopedMap
sourceMap(input
, DataSourceSurface::READ
);
2485 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
2486 if (MOZ2D_WARN_IF(!sourceMap
.IsMapped() || !targetMap
.IsMapped())) {
2490 uint8_t* sourceData
=
2491 DataAtOffset(input
, sourceMap
.GetMappedSurface(), offset
);
2492 int32_t sourceStride
= sourceMap
.GetStride();
2493 uint8_t* sourceBegin
= sourceMap
.GetData();
2494 uint8_t* sourceEnd
= sourceBegin
+ sourceStride
* input
->GetSize().height
;
2495 uint8_t* targetData
= targetMap
.GetData();
2496 int32_t targetStride
= targetMap
.GetStride();
2498 // Why exactly are we reversing the kernel?
2499 std::vector
<Float
> kernel
= ReversedVector(mKernelMatrix
);
2500 kernel
= ScaledVector(kernel
, mDivisor
);
2501 Float maxResultAbs
= std::max(MaxVectorSum(kernel
) + mBias
,
2502 MaxVectorSum(ScaledVector(kernel
, -1)) - mBias
);
2503 maxResultAbs
= std::max(maxResultAbs
, 1.0f
);
2505 double idealFactor
= INT32_MAX
/ 2.0 / maxResultAbs
/ 255.0 * 0.999;
2506 MOZ_ASSERT(255.0 * maxResultAbs
* idealFactor
<= INT32_MAX
/ 2.0,
2507 "badly chosen float-to-int scale");
2508 int32_t shiftL
, shiftR
;
2509 TranslateDoubleToShifts(idealFactor
, shiftL
, shiftR
);
2510 double factorFromShifts
= Float(1 << shiftL
) / Float(1 << shiftR
);
2511 MOZ_ASSERT(255.0 * maxResultAbs
* factorFromShifts
<= INT32_MAX
/ 2.0,
2512 "badly chosen float-to-int scale");
2514 int32_t* intKernel
= new int32_t[kernel
.size()];
2515 for (size_t i
= 0; i
< kernel
.size(); i
++) {
2516 intKernel
[i
] = NS_lround(kernel
[i
] * factorFromShifts
);
2518 int32_t bias
= NS_lround(mBias
* 255 * factorFromShifts
);
2520 for (int32_t y
= 0; y
< aRect
.Height(); y
++) {
2521 for (int32_t x
= 0; x
< aRect
.Width(); x
++) {
2522 ConvolvePixel(sourceData
, targetData
, aRect
.Width(), aRect
.Height(),
2523 sourceStride
, targetStride
, sourceBegin
, sourceEnd
, x
, y
,
2524 intKernel
, bias
, shiftL
, shiftR
, mPreserveAlpha
,
2525 mKernelSize
.width
, mKernelSize
.height
, mTarget
.x
, mTarget
.y
,
2526 aKernelUnitLengthX
, aKernelUnitLengthY
);
2531 return target
.forget();
2534 void FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(
2535 const IntRect
& aRect
) {
2536 RequestInputRect(IN_CONVOLVE_MATRIX_IN
, InflatedSourceRect(aRect
));
2539 IntRect
FilterNodeConvolveMatrixSoftware::MapRectToSource(
2540 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
2541 return MapInputRectToSource(IN_CONVOLVE_MATRIX_IN
, InflatedSourceRect(aRect
),
2545 IntRect
FilterNodeConvolveMatrixSoftware::InflatedSourceRect(
2546 const IntRect
& aDestRect
) {
2547 if (aDestRect
.IsEmpty()) {
2552 margin
.left
= ceil(mTarget
.x
* mKernelUnitLength
.width
);
2553 margin
.top
= ceil(mTarget
.y
* mKernelUnitLength
.height
);
2555 ceil((mKernelSize
.width
- mTarget
.x
- 1) * mKernelUnitLength
.width
);
2557 ceil((mKernelSize
.height
- mTarget
.y
- 1) * mKernelUnitLength
.height
);
2559 IntRect srcRect
= aDestRect
;
2560 srcRect
.Inflate(margin
);
2564 IntRect
FilterNodeConvolveMatrixSoftware::InflatedDestRect(
2565 const IntRect
& aSourceRect
) {
2566 if (aSourceRect
.IsEmpty()) {
2572 ceil((mKernelSize
.width
- mTarget
.x
- 1) * mKernelUnitLength
.width
);
2574 ceil((mKernelSize
.height
- mTarget
.y
- 1) * mKernelUnitLength
.height
);
2575 margin
.right
= ceil(mTarget
.x
* mKernelUnitLength
.width
);
2576 margin
.bottom
= ceil(mTarget
.y
* mKernelUnitLength
.height
);
2578 IntRect destRect
= aSourceRect
;
2579 destRect
.Inflate(margin
);
2583 IntRect
FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(
2584 const IntRect
& aRect
) {
2585 IntRect srcRequest
= InflatedSourceRect(aRect
);
2586 IntRect srcOutput
= GetInputRectInRect(IN_COLOR_MATRIX_IN
, srcRequest
);
2587 return InflatedDestRect(srcOutput
).Intersect(aRect
);
2590 FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware()
2591 : mScale(0.0f
), mChannelX(COLOR_CHANNEL_R
), mChannelY(COLOR_CHANNEL_G
) {}
2593 int32_t FilterNodeDisplacementMapSoftware::InputIndex(
2594 uint32_t aInputEnumIndex
) {
2595 switch (aInputEnumIndex
) {
2596 case IN_DISPLACEMENT_MAP_IN
:
2598 case IN_DISPLACEMENT_MAP_IN2
:
2605 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex
,
2607 MOZ_ASSERT(aIndex
== ATT_DISPLACEMENT_MAP_SCALE
);
2612 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex
,
2615 case ATT_DISPLACEMENT_MAP_X_CHANNEL
:
2616 mChannelX
= static_cast<ColorChannel
>(aValue
);
2618 case ATT_DISPLACEMENT_MAP_Y_CHANNEL
:
2619 mChannelY
= static_cast<ColorChannel
>(aValue
);
2622 MOZ_CRASH("GFX: FilterNodeDisplacementMapSoftware::SetAttribute");
2627 already_AddRefed
<DataSourceSurface
> FilterNodeDisplacementMapSoftware::Render(
2628 const IntRect
& aRect
) {
2629 IntRect srcRect
= InflatedSourceOrDestRect(aRect
);
2630 RefPtr
<DataSourceSurface
> input
= GetInputDataSourceSurface(
2631 IN_DISPLACEMENT_MAP_IN
, srcRect
, NEED_COLOR_CHANNELS
);
2632 RefPtr
<DataSourceSurface
> map
= GetInputDataSourceSurface(
2633 IN_DISPLACEMENT_MAP_IN2
, aRect
, NEED_COLOR_CHANNELS
);
2634 RefPtr
<DataSourceSurface
> target
=
2635 Factory::CreateDataSourceSurface(aRect
.Size(), SurfaceFormat::B8G8R8A8
);
2636 if (MOZ2D_WARN_IF(!(input
&& map
&& target
))) {
2640 IntPoint offset
= aRect
.TopLeft() - srcRect
.TopLeft();
2642 DataSourceSurface::ScopedMap
inputMap(input
, DataSourceSurface::READ
);
2643 DataSourceSurface::ScopedMap
mapMap(map
, DataSourceSurface::READ
);
2644 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
2645 if (MOZ2D_WARN_IF(!(inputMap
.IsMapped() && mapMap
.IsMapped() &&
2646 targetMap
.IsMapped()))) {
2650 uint8_t* sourceData
=
2651 DataAtOffset(input
, inputMap
.GetMappedSurface(), offset
);
2652 int32_t sourceStride
= inputMap
.GetStride();
2653 uint8_t* sourceBegin
= inputMap
.GetData();
2654 uint8_t* sourceEnd
= sourceBegin
+ sourceStride
* input
->GetSize().height
;
2655 uint8_t* mapData
= mapMap
.GetData();
2656 int32_t mapStride
= mapMap
.GetStride();
2657 uint8_t* targetData
= targetMap
.GetData();
2658 int32_t targetStride
= targetMap
.GetStride();
2660 static const ptrdiff_t channelMap
[4] = {
2661 B8G8R8A8_COMPONENT_BYTEOFFSET_R
, B8G8R8A8_COMPONENT_BYTEOFFSET_G
,
2662 B8G8R8A8_COMPONENT_BYTEOFFSET_B
, B8G8R8A8_COMPONENT_BYTEOFFSET_A
};
2663 uint16_t xChannel
= channelMap
[mChannelX
];
2664 uint16_t yChannel
= channelMap
[mChannelY
];
2666 float scaleOver255
= mScale
/ 255.0f
;
2667 float scaleAdjustment
= -0.5f
* mScale
;
2669 for (int32_t y
= 0; y
< aRect
.Height(); y
++) {
2670 for (int32_t x
= 0; x
< aRect
.Width(); x
++) {
2671 uint32_t mapIndex
= y
* mapStride
+ 4 * x
;
2672 uint32_t targIndex
= y
* targetStride
+ 4 * x
;
2674 x
+ scaleOver255
* mapData
[mapIndex
+ xChannel
] + scaleAdjustment
;
2676 y
+ scaleOver255
* mapData
[mapIndex
+ yChannel
] + scaleAdjustment
;
2677 *(uint32_t*)(targetData
+ targIndex
) = ColorAtPoint(
2678 sourceData
, sourceStride
, sourceBegin
, sourceEnd
, sourceX
, sourceY
);
2681 // Keep valgrind happy.
2682 PodZero(&targetData
[y
* targetStride
+ 4 * aRect
.Width()],
2683 targetStride
- 4 * aRect
.Width());
2686 return target
.forget();
2689 void FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(
2690 const IntRect
& aRect
) {
2691 RequestInputRect(IN_DISPLACEMENT_MAP_IN
, InflatedSourceOrDestRect(aRect
));
2692 RequestInputRect(IN_DISPLACEMENT_MAP_IN2
, aRect
);
2695 IntRect
FilterNodeDisplacementMapSoftware::MapRectToSource(
2696 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
2698 MapInputRectToSource(IN_DISPLACEMENT_MAP_IN
,
2699 InflatedSourceOrDestRect(aRect
), aMax
, aSourceNode
);
2701 MapInputRectToSource(IN_DISPLACEMENT_MAP_IN2
, aRect
, aMax
, aSourceNode
));
2705 IntRect
FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(
2706 const IntRect
& aDestOrSourceRect
) {
2707 IntRect sourceOrDestRect
= aDestOrSourceRect
;
2708 sourceOrDestRect
.Inflate(ceil(fabs(mScale
) / 2));
2709 return sourceOrDestRect
;
2712 IntRect
FilterNodeDisplacementMapSoftware::GetOutputRectInRect(
2713 const IntRect
& aRect
) {
2714 IntRect srcRequest
= InflatedSourceOrDestRect(aRect
);
2715 IntRect srcOutput
= GetInputRectInRect(IN_DISPLACEMENT_MAP_IN
, srcRequest
);
2716 return InflatedSourceOrDestRect(srcOutput
).Intersect(aRect
);
2719 FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware()
2723 mType(TURBULENCE_TYPE_TURBULENCE
) {}
2725 int32_t FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex
) {
2729 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex
,
2730 const Size
& aBaseFrequency
) {
2732 case ATT_TURBULENCE_BASE_FREQUENCY
:
2733 mBaseFrequency
= aBaseFrequency
;
2736 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2742 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex
,
2743 const IntRect
& aRect
) {
2745 case ATT_TURBULENCE_RECT
:
2746 mRenderRect
= aRect
;
2749 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2755 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex
,
2757 MOZ_ASSERT(aIndex
== ATT_TURBULENCE_STITCHABLE
);
2758 mStitchable
= aStitchable
;
2762 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex
,
2765 case ATT_TURBULENCE_NUM_OCTAVES
:
2766 mNumOctaves
= aValue
;
2768 case ATT_TURBULENCE_SEED
:
2771 case ATT_TURBULENCE_TYPE
:
2772 mType
= static_cast<TurbulenceType
>(aValue
);
2775 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2781 already_AddRefed
<DataSourceSurface
> FilterNodeTurbulenceSoftware::Render(
2782 const IntRect
& aRect
) {
2783 return FilterProcessing::RenderTurbulence(
2784 aRect
.Size(), aRect
.TopLeft(), mBaseFrequency
, mSeed
, mNumOctaves
, mType
,
2785 mStitchable
, Rect(mRenderRect
));
2788 IntRect
FilterNodeTurbulenceSoftware::GetOutputRectInRect(
2789 const IntRect
& aRect
) {
2790 return aRect
.Intersect(mRenderRect
);
2793 IntRect
FilterNodeTurbulenceSoftware::MapRectToSource(const IntRect
& aRect
,
2794 const IntRect
& aMax
,
2795 FilterNode
* aSourceNode
) {
2799 FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware()
2800 : mK1(0), mK2(0), mK3(0), mK4(0) {}
2802 int32_t FilterNodeArithmeticCombineSoftware::InputIndex(
2803 uint32_t aInputEnumIndex
) {
2804 switch (aInputEnumIndex
) {
2805 case IN_ARITHMETIC_COMBINE_IN
:
2807 case IN_ARITHMETIC_COMBINE_IN2
:
2814 void FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex
,
2815 const Float
* aFloat
,
2817 MOZ_ASSERT(aIndex
== ATT_ARITHMETIC_COMBINE_COEFFICIENTS
);
2818 MOZ_RELEASE_ASSERT(aSize
== 4);
2828 already_AddRefed
<DataSourceSurface
> FilterNodeArithmeticCombineSoftware::Render(
2829 const IntRect
& aRect
) {
2830 RefPtr
<DataSourceSurface
> input1
= GetInputDataSourceSurface(
2831 IN_ARITHMETIC_COMBINE_IN
, aRect
, NEED_COLOR_CHANNELS
);
2832 RefPtr
<DataSourceSurface
> input2
= GetInputDataSourceSurface(
2833 IN_ARITHMETIC_COMBINE_IN2
, aRect
, NEED_COLOR_CHANNELS
);
2834 if (!input1
&& !input2
) {
2838 // If one input is null, treat it as transparent by adjusting the factors.
2839 Float k1
= mK1
, k2
= mK2
, k3
= mK3
, k4
= mK4
;
2852 return FilterProcessing::ApplyArithmeticCombine(input1
, input2
, k1
, k2
, k3
,
2856 void FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(
2857 const IntRect
& aRect
) {
2858 RequestInputRect(IN_ARITHMETIC_COMBINE_IN
, aRect
);
2859 RequestInputRect(IN_ARITHMETIC_COMBINE_IN2
, aRect
);
2862 IntRect
FilterNodeArithmeticCombineSoftware::MapRectToSource(
2863 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
2865 MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN
, aRect
, aMax
, aSourceNode
);
2866 result
.OrWith(MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN2
, aRect
, aMax
,
2871 IntRect
FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(
2872 const IntRect
& aRect
) {
2877 GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN
, aRect
).Intersect(aRect
);
2879 GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2
, aRect
).Intersect(aRect
);
2882 result
= rectFrom1
.Intersect(rectFrom2
);
2885 result
= result
.Union(rectFrom1
);
2888 result
= result
.Union(rectFrom2
);
2893 FilterNodeCompositeSoftware::FilterNodeCompositeSoftware()
2894 : mOperator(COMPOSITE_OPERATOR_OVER
) {}
2896 int32_t FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex
) {
2897 return aInputEnumIndex
- IN_COMPOSITE_IN_START
;
2900 void FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex
,
2901 uint32_t aCompositeOperator
) {
2902 MOZ_ASSERT(aIndex
== ATT_COMPOSITE_OPERATOR
);
2903 mOperator
= static_cast<CompositeOperator
>(aCompositeOperator
);
2907 already_AddRefed
<DataSourceSurface
> FilterNodeCompositeSoftware::Render(
2908 const IntRect
& aRect
) {
2909 RefPtr
<DataSourceSurface
> start
= GetInputDataSourceSurface(
2910 IN_COMPOSITE_IN_START
, aRect
, NEED_COLOR_CHANNELS
);
2911 RefPtr
<DataSourceSurface
> dest
= Factory::CreateDataSourceSurface(
2912 aRect
.Size(), SurfaceFormat::B8G8R8A8
, true);
2913 if (MOZ2D_WARN_IF(!dest
)) {
2918 CopyRect(start
, dest
, aRect
- aRect
.TopLeft(), IntPoint());
2921 for (size_t inputIndex
= 1; inputIndex
< NumberOfSetInputs(); inputIndex
++) {
2922 RefPtr
<DataSourceSurface
> input
= GetInputDataSourceSurface(
2923 IN_COMPOSITE_IN_START
+ inputIndex
, aRect
, NEED_COLOR_CHANNELS
);
2925 FilterProcessing::ApplyComposition(input
, dest
, mOperator
);
2927 // We need to treat input as transparent. Depending on the composite
2928 // operator, different things happen to dest.
2929 switch (mOperator
) {
2930 case COMPOSITE_OPERATOR_OVER
:
2931 case COMPOSITE_OPERATOR_ATOP
:
2932 case COMPOSITE_OPERATOR_XOR
:
2933 // dest is unchanged.
2935 case COMPOSITE_OPERATOR_OUT
:
2936 // dest is now transparent, but it can become non-transparent again
2937 // when compositing additional inputs.
2938 ClearDataSourceSurface(dest
);
2940 case COMPOSITE_OPERATOR_IN
:
2941 // Transparency always wins. We're completely transparent now and
2942 // no additional input can get rid of that transparency.
2947 return dest
.forget();
2950 void FilterNodeCompositeSoftware::RequestFromInputsForRect(
2951 const IntRect
& aRect
) {
2952 for (size_t inputIndex
= 0; inputIndex
< NumberOfSetInputs(); inputIndex
++) {
2953 RequestInputRect(IN_COMPOSITE_IN_START
+ inputIndex
, aRect
);
2957 IntRect
FilterNodeCompositeSoftware::MapRectToSource(const IntRect
& aRect
,
2958 const IntRect
& aMax
,
2959 FilterNode
* aSourceNode
) {
2961 for (size_t inputIndex
= 0; inputIndex
< NumberOfSetInputs(); inputIndex
++) {
2962 result
.OrWith(MapInputRectToSource(IN_COMPOSITE_IN_START
+ inputIndex
,
2963 aRect
, aMax
, aSourceNode
));
2968 IntRect
FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
2970 for (size_t inputIndex
= 0; inputIndex
< NumberOfSetInputs(); inputIndex
++) {
2972 GetInputRectInRect(IN_COMPOSITE_IN_START
+ inputIndex
, aRect
);
2973 if (mOperator
== COMPOSITE_OPERATOR_IN
&& inputIndex
> 0) {
2974 rect
= rect
.Intersect(inputRect
);
2976 rect
= rect
.Union(inputRect
);
2982 int32_t FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex
) {
2983 switch (aInputEnumIndex
) {
2984 case IN_GAUSSIAN_BLUR_IN
:
2991 already_AddRefed
<DataSourceSurface
> FilterNodeBlurXYSoftware::Render(
2992 const IntRect
& aRect
) {
2993 Size sigmaXY
= StdDeviationXY();
2995 AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY
.width
, sigmaXY
.height
));
2997 if (d
.width
== 0 && d
.height
== 0) {
2998 return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN
, aRect
);
3001 IntRect srcRect
= InflatedSourceOrDestRect(aRect
);
3002 RefPtr
<DataSourceSurface
> input
=
3003 GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN
, srcRect
);
3008 RefPtr
<DataSourceSurface
> target
;
3009 Rect
r(0, 0, srcRect
.Width(), srcRect
.Height());
3011 if (input
->GetFormat() == SurfaceFormat::A8
) {
3013 Factory::CreateDataSourceSurface(srcRect
.Size(), SurfaceFormat::A8
);
3014 if (MOZ2D_WARN_IF(!target
)) {
3017 CopyRect(input
, target
, IntRect(IntPoint(), input
->GetSize()), IntPoint());
3019 DataSourceSurface::ScopedMap
targetMap(target
,
3020 DataSourceSurface::READ_WRITE
);
3021 if (MOZ2D_WARN_IF(!targetMap
.IsMapped())) {
3024 AlphaBoxBlur
blur(r
, targetMap
.GetStride(), sigmaXY
.width
, sigmaXY
.height
);
3025 blur
.Blur(targetMap
.GetData());
3027 RefPtr
<DataSourceSurface
> channel0
, channel1
, channel2
, channel3
;
3028 FilterProcessing::SeparateColorChannels(input
, channel0
, channel1
, channel2
,
3030 if (MOZ2D_WARN_IF(!(channel0
&& channel1
&& channel2
&& channel3
))) {
3034 DataSourceSurface::ScopedMap
channel0Map(channel0
,
3035 DataSourceSurface::READ_WRITE
);
3036 DataSourceSurface::ScopedMap
channel1Map(channel1
,
3037 DataSourceSurface::READ_WRITE
);
3038 DataSourceSurface::ScopedMap
channel2Map(channel2
,
3039 DataSourceSurface::READ_WRITE
);
3040 DataSourceSurface::ScopedMap
channel3Map(channel3
,
3041 DataSourceSurface::READ_WRITE
);
3042 if (MOZ2D_WARN_IF(!(channel0Map
.IsMapped() && channel1Map
.IsMapped() &&
3043 channel2Map
.IsMapped() && channel3Map
.IsMapped()))) {
3047 AlphaBoxBlur
blur(r
, channel0Map
.GetStride(), sigmaXY
.width
,
3049 blur
.Blur(channel0Map
.GetData());
3050 blur
.Blur(channel1Map
.GetData());
3051 blur
.Blur(channel2Map
.GetData());
3052 blur
.Blur(channel3Map
.GetData());
3054 target
= FilterProcessing::CombineColorChannels(channel0
, channel1
,
3055 channel2
, channel3
);
3058 return GetDataSurfaceInRect(target
, srcRect
, aRect
, EDGE_MODE_NONE
);
3061 void FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect
& aRect
) {
3062 RequestInputRect(IN_GAUSSIAN_BLUR_IN
, InflatedSourceOrDestRect(aRect
));
3065 IntRect
FilterNodeBlurXYSoftware::MapRectToSource(const IntRect
& aRect
,
3066 const IntRect
& aMax
,
3067 FilterNode
* aSourceNode
) {
3068 return MapInputRectToSource(
3069 IN_GAUSSIAN_BLUR_IN
, InflatedSourceOrDestRect(aRect
), aMax
, aSourceNode
);
3072 IntRect
FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(
3073 const IntRect
& aDestRect
) {
3074 Size sigmaXY
= StdDeviationXY();
3076 AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY
.width
, sigmaXY
.height
));
3077 IntRect srcRect
= aDestRect
;
3082 IntRect
FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
3083 IntRect srcRequest
= InflatedSourceOrDestRect(aRect
);
3084 IntRect srcOutput
= GetInputRectInRect(IN_GAUSSIAN_BLUR_IN
, srcRequest
);
3085 return InflatedSourceOrDestRect(srcOutput
).Intersect(aRect
);
3088 FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware()
3089 : mStdDeviation(0) {}
3091 static float ClampStdDeviation(float aStdDeviation
) {
3092 // Cap software blur radius for performance reasons.
3093 return std::min(std::max(0.0f
, aStdDeviation
), 100.0f
);
3096 void FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex
,
3097 float aStdDeviation
) {
3099 case ATT_GAUSSIAN_BLUR_STD_DEVIATION
:
3100 mStdDeviation
= ClampStdDeviation(aStdDeviation
);
3103 MOZ_CRASH("GFX: FilterNodeGaussianBlurSoftware::SetAttribute");
3108 Size
FilterNodeGaussianBlurSoftware::StdDeviationXY() {
3109 return Size(mStdDeviation
, mStdDeviation
);
3112 FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware()
3113 : mStdDeviation(0.0), mBlurDirection(BLUR_DIRECTION_X
) {}
3115 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex
,
3116 Float aStdDeviation
) {
3118 case ATT_DIRECTIONAL_BLUR_STD_DEVIATION
:
3119 mStdDeviation
= ClampStdDeviation(aStdDeviation
);
3122 MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
3127 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex
,
3128 uint32_t aBlurDirection
) {
3130 case ATT_DIRECTIONAL_BLUR_DIRECTION
:
3131 mBlurDirection
= (BlurDirection
)aBlurDirection
;
3134 MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
3139 Size
FilterNodeDirectionalBlurSoftware::StdDeviationXY() {
3140 float sigmaX
= mBlurDirection
== BLUR_DIRECTION_X
? mStdDeviation
: 0;
3141 float sigmaY
= mBlurDirection
== BLUR_DIRECTION_Y
? mStdDeviation
: 0;
3142 return Size(sigmaX
, sigmaY
);
3145 int32_t FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex
) {
3146 switch (aInputEnumIndex
) {
3154 void FilterNodeCropSoftware::SetAttribute(uint32_t aIndex
,
3155 const Rect
& aSourceRect
) {
3156 MOZ_ASSERT(aIndex
== ATT_CROP_RECT
);
3157 Rect srcRect
= aSourceRect
;
3159 if (!srcRect
.ToIntRect(&mCropRect
)) {
3160 mCropRect
= IntRect();
3165 already_AddRefed
<DataSourceSurface
> FilterNodeCropSoftware::Render(
3166 const IntRect
& aRect
) {
3167 return GetInputDataSourceSurface(IN_CROP_IN
, aRect
.Intersect(mCropRect
));
3170 void FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect
& aRect
) {
3171 RequestInputRect(IN_CROP_IN
, aRect
.Intersect(mCropRect
));
3174 IntRect
FilterNodeCropSoftware::MapRectToSource(const IntRect
& aRect
,
3175 const IntRect
& aMax
,
3176 FilterNode
* aSourceNode
) {
3177 return MapInputRectToSource(IN_CROP_IN
, aRect
.Intersect(mCropRect
), aMax
,
3181 IntRect
FilterNodeCropSoftware::GetOutputRectInRect(const IntRect
& aRect
) {
3182 return GetInputRectInRect(IN_CROP_IN
, aRect
).Intersect(mCropRect
);
3185 int32_t FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex
) {
3186 switch (aInputEnumIndex
) {
3187 case IN_PREMULTIPLY_IN
:
3194 already_AddRefed
<DataSourceSurface
> FilterNodePremultiplySoftware::Render(
3195 const IntRect
& aRect
) {
3196 RefPtr
<DataSourceSurface
> input
=
3197 GetInputDataSourceSurface(IN_PREMULTIPLY_IN
, aRect
);
3198 return input
? Premultiply(input
) : nullptr;
3201 void FilterNodePremultiplySoftware::RequestFromInputsForRect(
3202 const IntRect
& aRect
) {
3203 RequestInputRect(IN_PREMULTIPLY_IN
, aRect
);
3206 IntRect
FilterNodePremultiplySoftware::MapRectToSource(
3207 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
3208 return MapInputRectToSource(IN_PREMULTIPLY_IN
, aRect
, aMax
, aSourceNode
);
3211 IntRect
FilterNodePremultiplySoftware::GetOutputRectInRect(
3212 const IntRect
& aRect
) {
3213 return GetInputRectInRect(IN_PREMULTIPLY_IN
, aRect
);
3216 int32_t FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex
) {
3217 switch (aInputEnumIndex
) {
3218 case IN_UNPREMULTIPLY_IN
:
3225 already_AddRefed
<DataSourceSurface
> FilterNodeUnpremultiplySoftware::Render(
3226 const IntRect
& aRect
) {
3227 RefPtr
<DataSourceSurface
> input
=
3228 GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN
, aRect
);
3229 return input
? Unpremultiply(input
) : nullptr;
3232 void FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(
3233 const IntRect
& aRect
) {
3234 RequestInputRect(IN_UNPREMULTIPLY_IN
, aRect
);
3237 IntRect
FilterNodeUnpremultiplySoftware::MapRectToSource(
3238 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
3239 return MapInputRectToSource(IN_UNPREMULTIPLY_IN
, aRect
, aMax
, aSourceNode
);
3242 IntRect
FilterNodeUnpremultiplySoftware::GetOutputRectInRect(
3243 const IntRect
& aRect
) {
3244 return GetInputRectInRect(IN_UNPREMULTIPLY_IN
, aRect
);
3247 void FilterNodeOpacitySoftware::SetAttribute(uint32_t aIndex
, Float aValue
) {
3248 MOZ_ASSERT(aIndex
== ATT_OPACITY_VALUE
);
3253 int32_t FilterNodeOpacitySoftware::InputIndex(uint32_t aInputEnumIndex
) {
3254 switch (aInputEnumIndex
) {
3262 already_AddRefed
<DataSourceSurface
> FilterNodeOpacitySoftware::Render(
3263 const IntRect
& aRect
) {
3264 RefPtr
<DataSourceSurface
> input
=
3265 GetInputDataSourceSurface(IN_OPACITY_IN
, aRect
);
3266 return input
? Opacity(input
, mValue
) : nullptr;
3269 void FilterNodeOpacitySoftware::RequestFromInputsForRect(const IntRect
& aRect
) {
3270 RequestInputRect(IN_OPACITY_IN
, aRect
);
3273 IntRect
FilterNodeOpacitySoftware::MapRectToSource(const IntRect
& aRect
,
3274 const IntRect
& aMax
,
3275 FilterNode
* aSourceNode
) {
3276 return MapInputRectToSource(IN_OPACITY_IN
, aRect
, aMax
, aSourceNode
);
3279 IntRect
FilterNodeOpacitySoftware::GetOutputRectInRect(const IntRect
& aRect
) {
3280 return GetInputRectInRect(IN_OPACITY_IN
, aRect
);
3283 bool PointLightSoftware::SetAttribute(uint32_t aIndex
, const Point3D
& aPoint
) {
3285 case ATT_POINT_LIGHT_POSITION
:
3294 SpotLightSoftware::SpotLightSoftware()
3295 : mSpecularFocus(0), mLimitingConeAngle(0), mLimitingConeCos(1) {}
3297 bool SpotLightSoftware::SetAttribute(uint32_t aIndex
, const Point3D
& aPoint
) {
3299 case ATT_SPOT_LIGHT_POSITION
:
3302 case ATT_SPOT_LIGHT_POINTS_AT
:
3311 bool SpotLightSoftware::SetAttribute(uint32_t aIndex
, Float aValue
) {
3313 case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE
:
3314 mLimitingConeAngle
= aValue
;
3316 case ATT_SPOT_LIGHT_FOCUS
:
3317 mSpecularFocus
= aValue
;
3325 DistantLightSoftware::DistantLightSoftware() : mAzimuth(0), mElevation(0) {}
3327 bool DistantLightSoftware::SetAttribute(uint32_t aIndex
, Float aValue
) {
3329 case ATT_DISTANT_LIGHT_AZIMUTH
:
3332 case ATT_DISTANT_LIGHT_ELEVATION
:
3333 mElevation
= aValue
;
3341 static inline Point3D
Normalized(const Point3D
& vec
) {
3347 template <typename LightType
, typename LightingType
>
3348 FilterNodeLightingSoftware
<LightType
, LightingType
>::FilterNodeLightingSoftware(
3349 const char* aTypeName
)
3350 : mLock("FilterNodeLightingSoftware"),
3352 #if defined(MOZILLA_INTERNAL_API) && \
3353 (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
3355 mTypeName(aTypeName
)
3360 template <typename LightType
, typename LightingType
>
3361 int32_t FilterNodeLightingSoftware
<LightType
, LightingType
>::InputIndex(
3362 uint32_t aInputEnumIndex
) {
3363 switch (aInputEnumIndex
) {
3364 case IN_LIGHTING_IN
:
3371 template <typename LightType
, typename LightingType
>
3372 void FilterNodeLightingSoftware
<LightType
, LightingType
>::SetAttribute(
3373 uint32_t aIndex
, const Point3D
& aPoint
) {
3374 if (mLight
.SetAttribute(aIndex
, aPoint
)) {
3378 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute point");
3381 template <typename LightType
, typename LightingType
>
3382 void FilterNodeLightingSoftware
<LightType
, LightingType
>::SetAttribute(
3383 uint32_t aIndex
, Float aValue
) {
3384 if (mLight
.SetAttribute(aIndex
, aValue
) ||
3385 mLighting
.SetAttribute(aIndex
, aValue
)) {
3390 case ATT_LIGHTING_SURFACE_SCALE
:
3391 mSurfaceScale
= std::fpclassify(aValue
) == FP_SUBNORMAL
? 0.0 : aValue
;
3394 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float");
3399 template <typename LightType
, typename LightingType
>
3400 void FilterNodeLightingSoftware
<LightType
, LightingType
>::SetAttribute(
3401 uint32_t aIndex
, const Size
& aKernelUnitLength
) {
3403 case ATT_LIGHTING_KERNEL_UNIT_LENGTH
:
3404 mKernelUnitLength
= aKernelUnitLength
;
3407 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size");
3412 template <typename LightType
, typename LightingType
>
3413 void FilterNodeLightingSoftware
<LightType
, LightingType
>::SetAttribute(
3414 uint32_t aIndex
, const DeviceColor
& aColor
) {
3415 MOZ_ASSERT(aIndex
== ATT_LIGHTING_COLOR
);
3420 template <typename LightType
, typename LightingType
>
3422 FilterNodeLightingSoftware
<LightType
, LightingType
>::GetOutputRectInRect(
3423 const IntRect
& aRect
) {
3427 Point3D
PointLightSoftware::GetVectorToLight(const Point3D
& aTargetPoint
) {
3428 return Normalized(mPosition
- aTargetPoint
);
3431 uint32_t PointLightSoftware::GetColor(uint32_t aLightColor
,
3432 const Point3D
& aVectorToLight
) {
3436 void SpotLightSoftware::Prepare() {
3437 mVectorFromFocusPointToLight
= Normalized(mPointsAt
- mPosition
);
3439 std::max
<double>(cos(mLimitingConeAngle
* M_PI
/ 180.0), 0.0);
3440 mPowCache
.CacheForExponent(mSpecularFocus
);
3443 Point3D
SpotLightSoftware::GetVectorToLight(const Point3D
& aTargetPoint
) {
3444 return Normalized(mPosition
- aTargetPoint
);
3447 uint32_t SpotLightSoftware::GetColor(uint32_t aLightColor
,
3448 const Point3D
& aVectorToLight
) {
3454 Float dot
= -aVectorToLight
.DotProduct(mVectorFromFocusPointToLight
);
3455 if (!mPowCache
.HasPowerTable()) {
3456 dot
*= (dot
>= mLimitingConeCos
);
3457 color
= aLightColor
;
3458 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] *= dot
;
3459 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] *= dot
;
3460 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] *= dot
;
3462 color
= aLightColor
;
3463 uint16_t doti
= dot
* (dot
>= 0) * (1 << PowCache::sInputIntPrecisionBits
);
3464 uint32_t tmp
= mPowCache
.Pow(doti
) * (dot
>= mLimitingConeCos
);
3465 MOZ_ASSERT(tmp
<= (1 << PowCache::sOutputIntPrecisionBits
),
3466 "pow() result must not exceed 1.0");
3467 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] =
3468 uint8_t((colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] * tmp
) >>
3469 PowCache::sOutputIntPrecisionBits
);
3470 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] =
3471 uint8_t((colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] * tmp
) >>
3472 PowCache::sOutputIntPrecisionBits
);
3473 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] =
3474 uint8_t((colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] * tmp
) >>
3475 PowCache::sOutputIntPrecisionBits
);
3477 colorC
[B8G8R8A8_COMPONENT_BYTEOFFSET_A
] = 255;
3481 void DistantLightSoftware::Prepare() {
3482 const double radPerDeg
= M_PI
/ 180.0;
3483 mVectorToLight
.x
= cos(mAzimuth
* radPerDeg
) * cos(mElevation
* radPerDeg
);
3484 mVectorToLight
.y
= sin(mAzimuth
* radPerDeg
) * cos(mElevation
* radPerDeg
);
3485 mVectorToLight
.z
= sin(mElevation
* radPerDeg
);
3488 Point3D
DistantLightSoftware::GetVectorToLight(const Point3D
& aTargetPoint
) {
3489 return mVectorToLight
;
3492 uint32_t DistantLightSoftware::GetColor(uint32_t aLightColor
,
3493 const Point3D
& aVectorToLight
) {
3497 template <typename CoordType
>
3498 static Point3D
GenerateNormal(const uint8_t* data
, int32_t stride
,
3499 uint8_t* boundsBegin
, uint8_t* boundsEnd
,
3500 int32_t x
, int32_t y
, float surfaceScale
,
3501 CoordType dx
, CoordType dy
) {
3502 const uint8_t* index
= data
+ y
* stride
+ x
;
3506 // See this for source of constants:
3507 // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
3508 int16_t normalX
= -1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3509 boundsEnd
, -dx
, -dy
, 1, 0) +
3510 1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3511 boundsEnd
, dx
, -dy
, 1, 0) +
3512 -2 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3513 boundsEnd
, -dx
, zero
, 1, 0) +
3514 2 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3515 boundsEnd
, dx
, zero
, 1, 0) +
3516 -1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3517 boundsEnd
, -dx
, dy
, 1, 0) +
3518 1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3519 boundsEnd
, dx
, dy
, 1, 0);
3521 int16_t normalY
= -1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3522 boundsEnd
, -dx
, -dy
, 1, 0) +
3523 -2 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3524 boundsEnd
, zero
, -dy
, 1, 0) +
3525 -1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3526 boundsEnd
, dx
, -dy
, 1, 0) +
3527 1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3528 boundsEnd
, -dx
, dy
, 1, 0) +
3529 2 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3530 boundsEnd
, zero
, dy
, 1, 0) +
3531 1 * ColorComponentAtPoint(index
, stride
, boundsBegin
,
3532 boundsEnd
, dx
, dy
, 1, 0);
3535 normal
.x
= -surfaceScale
* normalX
/ 4.0f
;
3536 normal
.y
= -surfaceScale
* normalY
/ 4.0f
;
3538 return Normalized(normal
);
3541 template <typename LightType
, typename LightingType
>
3542 already_AddRefed
<DataSourceSurface
>
3543 FilterNodeLightingSoftware
<LightType
, LightingType
>::Render(
3544 const IntRect
& aRect
) {
3545 if (mKernelUnitLength
.width
== floor(mKernelUnitLength
.width
) &&
3546 mKernelUnitLength
.height
== floor(mKernelUnitLength
.height
)) {
3547 return DoRender(aRect
, (int32_t)mKernelUnitLength
.width
,
3548 (int32_t)mKernelUnitLength
.height
);
3550 return DoRender(aRect
, mKernelUnitLength
.width
, mKernelUnitLength
.height
);
3553 template <typename LightType
, typename LightingType
>
3554 void FilterNodeLightingSoftware
<
3555 LightType
, LightingType
>::RequestFromInputsForRect(const IntRect
& aRect
) {
3556 IntRect srcRect
= aRect
;
3557 srcRect
.Inflate(ceil(mKernelUnitLength
.width
),
3558 ceil(mKernelUnitLength
.height
));
3559 RequestInputRect(IN_LIGHTING_IN
, srcRect
);
3562 template <typename LightType
, typename LightingType
>
3563 IntRect FilterNodeLightingSoftware
<LightType
, LightingType
>::MapRectToSource(
3564 const IntRect
& aRect
, const IntRect
& aMax
, FilterNode
* aSourceNode
) {
3565 IntRect srcRect
= aRect
;
3566 srcRect
.Inflate(ceil(mKernelUnitLength
.width
),
3567 ceil(mKernelUnitLength
.height
));
3568 return MapInputRectToSource(IN_LIGHTING_IN
, srcRect
, aMax
, aSourceNode
);
3571 template <typename LightType
, typename LightingType
>
3572 template <typename CoordType
>
3573 already_AddRefed
<DataSourceSurface
>
3574 FilterNodeLightingSoftware
<LightType
, LightingType
>::DoRender(
3575 const IntRect
& aRect
, CoordType aKernelUnitLengthX
,
3576 CoordType aKernelUnitLengthY
) {
3577 MOZ_ASSERT(aKernelUnitLengthX
> 0,
3578 "aKernelUnitLengthX can be a negative or zero value");
3579 MOZ_ASSERT(aKernelUnitLengthY
> 0,
3580 "aKernelUnitLengthY can be a negative or zero value");
3582 IntRect srcRect
= aRect
;
3583 IntSize size
= aRect
.Size();
3584 srcRect
.Inflate(ceil(float(aKernelUnitLengthX
)),
3585 ceil(float(aKernelUnitLengthY
)));
3587 // Inflate the source rect by another pixel because the bilinear filtering in
3588 // ColorComponentAtPoint may want to access the margins.
3591 RefPtr
<DataSourceSurface
> input
= GetInputDataSourceSurface(
3592 IN_LIGHTING_IN
, srcRect
, CAN_HANDLE_A8
, EDGE_MODE_NONE
);
3598 if (input
->GetFormat() != SurfaceFormat::A8
) {
3599 input
= FilterProcessing::ExtractAlpha(input
);
3602 RefPtr
<DataSourceSurface
> target
=
3603 Factory::CreateDataSourceSurface(size
, SurfaceFormat::B8G8R8A8
);
3604 if (MOZ2D_WARN_IF(!target
)) {
3608 IntPoint offset
= aRect
.TopLeft() - srcRect
.TopLeft();
3610 DataSourceSurface::ScopedMap
sourceMap(input
, DataSourceSurface::READ
);
3611 DataSourceSurface::ScopedMap
targetMap(target
, DataSourceSurface::WRITE
);
3612 if (MOZ2D_WARN_IF(!(sourceMap
.IsMapped() && targetMap
.IsMapped()))) {
3616 uint8_t* sourceData
=
3617 DataAtOffset(input
, sourceMap
.GetMappedSurface(), offset
);
3618 int32_t sourceStride
= sourceMap
.GetStride();
3619 uint8_t* sourceBegin
= sourceMap
.GetData();
3620 uint8_t* sourceEnd
= sourceBegin
+ sourceStride
* input
->GetSize().height
;
3621 uint8_t* targetData
= targetMap
.GetData();
3622 int32_t targetStride
= targetMap
.GetStride();
3624 MutexAutoLock
lock(mLock
);
3626 uint32_t lightColor
= ColorToBGRA(mColor
);
3628 mLighting
.Prepare();
3630 for (int32_t y
= 0; y
< size
.height
; y
++) {
3631 for (int32_t x
= 0; x
< size
.width
; x
++) {
3632 int32_t sourceIndex
= y
* sourceStride
+ x
;
3633 int32_t targetIndex
= y
* targetStride
+ 4 * x
;
3636 GenerateNormal(sourceData
, sourceStride
, sourceBegin
, sourceEnd
, x
, y
,
3637 mSurfaceScale
, aKernelUnitLengthX
, aKernelUnitLengthY
);
3639 IntPoint
pointInFilterSpace(aRect
.X() + x
, aRect
.Y() + y
);
3640 Float Z
= mSurfaceScale
* sourceData
[sourceIndex
] / 255.0f
;
3641 Point3D
pt(pointInFilterSpace
.x
, pointInFilterSpace
.y
, Z
);
3642 Point3D rayDir
= mLight
.GetVectorToLight(pt
);
3643 uint32_t color
= mLight
.GetColor(lightColor
, rayDir
);
3645 *(uint32_t*)(targetData
+ targetIndex
) =
3646 mLighting
.LightPixel(normal
, rayDir
, color
);
3649 // Zero padding to keep valgrind happy.
3650 PodZero(&targetData
[y
* targetStride
+ 4 * size
.width
],
3651 targetStride
- 4 * size
.width
);
3654 return target
.forget();
3657 DiffuseLightingSoftware::DiffuseLightingSoftware() : mDiffuseConstant(0) {}
3659 bool DiffuseLightingSoftware::SetAttribute(uint32_t aIndex
, Float aValue
) {
3661 case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT
:
3662 mDiffuseConstant
= aValue
;
3670 uint32_t DiffuseLightingSoftware::LightPixel(const Point3D
& aNormal
,
3671 const Point3D
& aVectorToLight
,
3673 Float dotNL
= std::max(0.0f
, aNormal
.DotProduct(aVectorToLight
));
3674 Float diffuseNL
= mDiffuseConstant
* dotNL
;
3678 uint8_t components
[4];
3680 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] = umin(
3681 uint32_t(diffuseNL
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
]),
3683 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] = umin(
3684 uint32_t(diffuseNL
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
]),
3686 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] = umin(
3687 uint32_t(diffuseNL
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
]),
3689 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_A
] = 255;
3693 SpecularLightingSoftware::SpecularLightingSoftware()
3694 : mSpecularConstant(0), mSpecularExponent(0), mSpecularConstantInt(0) {}
3696 bool SpecularLightingSoftware::SetAttribute(uint32_t aIndex
, Float aValue
) {
3698 case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT
:
3699 mSpecularConstant
= std::min(std::max(aValue
, 0.0f
), 255.0f
);
3701 case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT
:
3702 mSpecularExponent
= std::min(std::max(aValue
, 1.0f
), 128.0f
);
3710 void SpecularLightingSoftware::Prepare() {
3711 mPowCache
.CacheForExponent(mSpecularExponent
);
3712 mSpecularConstantInt
= uint32_t(mSpecularConstant
* (1 << 8));
3715 uint32_t SpecularLightingSoftware::LightPixel(const Point3D
& aNormal
,
3716 const Point3D
& aVectorToLight
,
3718 Point3D
vectorToEye(0, 0, 1);
3719 Point3D halfwayVector
= Normalized(aVectorToLight
+ vectorToEye
);
3720 Float dotNH
= aNormal
.DotProduct(halfwayVector
);
3722 uint16_t(dotNH
* (dotNH
>= 0) * (1 << PowCache::sInputIntPrecisionBits
));
3723 // The exponent for specular is in [1,128] range, so we don't need to check
3724 // and optimize for the "default power table" scenario here.
3725 MOZ_ASSERT(mPowCache
.HasPowerTable());
3726 uint32_t specularNHi
=
3727 uint32_t(mSpecularConstantInt
) * mPowCache
.Pow(dotNHi
) >> 8;
3731 uint8_t components
[4];
3733 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
] =
3734 umin((specularNHi
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
]) >>
3735 PowCache::sOutputIntPrecisionBits
,
3737 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
] =
3738 umin((specularNHi
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
]) >>
3739 PowCache::sOutputIntPrecisionBits
,
3741 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
] =
3742 umin((specularNHi
* color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
]) >>
3743 PowCache::sOutputIntPrecisionBits
,
3746 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_A
] =
3747 umax(color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_B
],
3748 umax(color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_G
],
3749 color
.components
[B8G8R8A8_COMPONENT_BYTEOFFSET_R
]));
3754 } // namespace mozilla