Line data Source code
1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include <cmath>
7 : #include "DataSurfaceHelpers.h"
8 : #include "FilterNodeSoftware.h"
9 : #include "2D.h"
10 : #include "Tools.h"
11 : #include "Blur.h"
12 : #include <map>
13 : #include "FilterProcessing.h"
14 : #include "Logging.h"
15 : #include "mozilla/PodOperations.h"
16 : #include "mozilla/DebugOnly.h"
17 :
18 : // #define DEBUG_DUMP_SURFACES
19 :
20 : #ifdef DEBUG_DUMP_SURFACES
21 : #include "gfxUtils.h" // not part of Moz2D
22 : #endif
23 :
24 : namespace mozilla {
25 : namespace gfx {
26 :
27 : namespace {
28 :
29 : /**
30 : * This class provides a way to get a pow() results in constant-time. It works
31 : * by caching 129 ((1 << sCacheIndexPrecisionBits) + 1) values for bases between
32 : * 0 and 1 and a fixed exponent.
33 : **/
34 : class PowCache
35 : {
36 : public:
37 0 : PowCache()
38 0 : : mNumPowTablePreSquares(-1)
39 : {
40 0 : }
41 :
42 0 : void CacheForExponent(Float aExponent)
43 : {
44 : // Since we are in the world where we only care about
45 : // input and results in [0,1], there is no point in
46 : // dealing with non-positive exponents.
47 0 : if (aExponent <= 0) {
48 0 : mNumPowTablePreSquares = -1;
49 0 : return;
50 : }
51 0 : int numPreSquares = 0;
52 0 : while (numPreSquares < 5 && aExponent > (1 << (numPreSquares + 2))) {
53 0 : numPreSquares++;
54 : }
55 0 : mNumPowTablePreSquares = numPreSquares;
56 0 : for (size_t i = 0; i < sCacheSize; i++) {
57 : // sCacheSize is chosen in such a way that a takes values
58 : // from 0.0 to 1.0 inclusive.
59 0 : Float a = i / Float(1 << sCacheIndexPrecisionBits);
60 0 : MOZ_ASSERT(0.0f <= a && a <= 1.0f, "We only want to cache for bases between 0 and 1.");
61 :
62 0 : for (int j = 0; j < mNumPowTablePreSquares; j++) {
63 0 : a = sqrt(a);
64 : }
65 0 : uint32_t cachedInt = pow(a, aExponent) * (1 << sOutputIntPrecisionBits);
66 0 : MOZ_ASSERT(cachedInt < (1 << (sizeof(mPowTable[i]) * 8)), "mPowCache integer type too small");
67 :
68 0 : mPowTable[i] = cachedInt;
69 : }
70 : }
71 :
72 : // Only call Pow() if HasPowerTable() would return true, to avoid complicating
73 : // this code and having it just return (1 << sOutputIntPrecisionBits))
74 0 : uint16_t Pow(uint16_t aBase)
75 : {
76 0 : MOZ_ASSERT(HasPowerTable());
77 : // Results should be similar to what the following code would produce:
78 : // Float x = Float(aBase) / (1 << sInputIntPrecisionBits);
79 : // return uint16_t(pow(x, aExponent) * (1 << sOutputIntPrecisionBits));
80 :
81 0 : MOZ_ASSERT(aBase <= (1 << sInputIntPrecisionBits), "aBase needs to be between 0 and 1!");
82 :
83 0 : uint32_t a = aBase;
84 0 : for (int j = 0; j < mNumPowTablePreSquares; j++) {
85 0 : a = a * a >> sInputIntPrecisionBits;
86 : }
87 0 : uint32_t i = a >> (sInputIntPrecisionBits - sCacheIndexPrecisionBits);
88 0 : MOZ_ASSERT(i < sCacheSize, "out-of-bounds mPowTable access");
89 0 : return mPowTable[i];
90 : }
91 :
92 : static const int sInputIntPrecisionBits = 15;
93 : static const int sOutputIntPrecisionBits = 15;
94 : static const int sCacheIndexPrecisionBits = 7;
95 :
96 0 : inline bool HasPowerTable() const
97 : {
98 0 : return mNumPowTablePreSquares >= 0;
99 : }
100 :
101 : private:
102 : static const size_t sCacheSize = (1 << sCacheIndexPrecisionBits) + 1;
103 :
104 : int mNumPowTablePreSquares;
105 : uint16_t mPowTable[sCacheSize];
106 : };
107 :
108 0 : class PointLightSoftware
109 : {
110 : public:
111 0 : bool SetAttribute(uint32_t aIndex, Float) { return false; }
112 : bool SetAttribute(uint32_t aIndex, const Point3D &);
113 0 : void Prepare() {}
114 : Point3D GetVectorToLight(const Point3D &aTargetPoint);
115 : uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
116 :
117 : private:
118 : Point3D mPosition;
119 : };
120 :
121 : class SpotLightSoftware
122 : {
123 : public:
124 : SpotLightSoftware();
125 : bool SetAttribute(uint32_t aIndex, Float);
126 : bool SetAttribute(uint32_t aIndex, const Point3D &);
127 : void Prepare();
128 : Point3D GetVectorToLight(const Point3D &aTargetPoint);
129 : uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
130 :
131 : private:
132 : Point3D mPosition;
133 : Point3D mPointsAt;
134 : Point3D mVectorFromFocusPointToLight;
135 : Float mSpecularFocus;
136 : Float mLimitingConeAngle;
137 : Float mLimitingConeCos;
138 : PowCache mPowCache;
139 : };
140 :
141 : class DistantLightSoftware
142 : {
143 : public:
144 : DistantLightSoftware();
145 : bool SetAttribute(uint32_t aIndex, Float);
146 0 : bool SetAttribute(uint32_t aIndex, const Point3D &) { return false; }
147 : void Prepare();
148 : Point3D GetVectorToLight(const Point3D &aTargetPoint);
149 : uint32_t GetColor(uint32_t aLightColor, const Point3D &aVectorToLight);
150 :
151 : private:
152 : Float mAzimuth;
153 : Float mElevation;
154 : Point3D mVectorToLight;
155 : };
156 :
157 : class DiffuseLightingSoftware
158 : {
159 : public:
160 : DiffuseLightingSoftware();
161 : bool SetAttribute(uint32_t aIndex, Float);
162 0 : void Prepare() {}
163 : uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight,
164 : uint32_t aColor);
165 :
166 : private:
167 : Float mDiffuseConstant;
168 : };
169 :
170 : class SpecularLightingSoftware
171 : {
172 : public:
173 : SpecularLightingSoftware();
174 : bool SetAttribute(uint32_t aIndex, Float);
175 : void Prepare();
176 : uint32_t LightPixel(const Point3D &aNormal, const Point3D &aVectorToLight,
177 : uint32_t aColor);
178 :
179 : private:
180 : Float mSpecularConstant;
181 : Float mSpecularExponent;
182 : uint32_t mSpecularConstantInt;
183 : PowCache mPowCache;
184 : };
185 :
186 : } // unnamed namespace
187 :
188 : // from xpcom/ds/nsMathUtils.h
189 : static int32_t
190 0 : NS_lround(double x)
191 : {
192 0 : return x >= 0.0 ? int32_t(x + 0.5) : int32_t(x - 0.5);
193 : }
194 :
195 : already_AddRefed<DataSourceSurface>
196 0 : CloneAligned(DataSourceSurface* aSource)
197 : {
198 0 : return CreateDataSourceSurfaceByCloning(aSource);
199 : }
200 :
201 : static void
202 0 : FillRectWithPixel(DataSourceSurface *aSurface, const IntRect &aFillRect, IntPoint aPixelPos)
203 : {
204 0 : MOZ_ASSERT(!aFillRect.Overflows());
205 0 : MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
206 : "aFillRect needs to be completely inside the surface");
207 0 : MOZ_ASSERT(SurfaceContainsPoint(aSurface, aPixelPos),
208 : "aPixelPos needs to be inside the surface");
209 :
210 0 : DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
211 0 : if(MOZ2D_WARN_IF(!surfMap.IsMapped())) {
212 0 : return;
213 : }
214 0 : uint8_t* sourcePixelData = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aPixelPos);
215 0 : uint8_t* data = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
216 0 : int bpp = BytesPerPixel(aSurface->GetFormat());
217 :
218 : // Fill the first row by hand.
219 0 : if (bpp == 4) {
220 0 : uint32_t sourcePixel = *(uint32_t*)sourcePixelData;
221 0 : for (int32_t x = 0; x < aFillRect.width; x++) {
222 0 : *((uint32_t*)data + x) = sourcePixel;
223 : }
224 0 : } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
225 0 : uint8_t sourcePixel = *sourcePixelData;
226 0 : memset(data, sourcePixel, aFillRect.width);
227 : }
228 :
229 : // Copy the first row into the other rows.
230 0 : for (int32_t y = 1; y < aFillRect.height; y++) {
231 0 : PodCopy(data + y * surfMap.GetStride(), data, aFillRect.width * bpp);
232 : }
233 : }
234 :
235 : static void
236 0 : FillRectWithVerticallyRepeatingHorizontalStrip(DataSourceSurface *aSurface,
237 : const IntRect &aFillRect,
238 : const IntRect &aSampleRect)
239 : {
240 0 : MOZ_ASSERT(!aFillRect.Overflows());
241 0 : MOZ_ASSERT(!aSampleRect.Overflows());
242 0 : MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
243 : "aFillRect needs to be completely inside the surface");
244 0 : MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
245 : "aSampleRect needs to be completely inside the surface");
246 :
247 0 : DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
248 0 : if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
249 0 : return;
250 : }
251 :
252 0 : uint8_t* sampleData = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft());
253 0 : uint8_t* data = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
254 0 : if (BytesPerPixel(aSurface->GetFormat()) == 4) {
255 0 : for (int32_t y = 0; y < aFillRect.height; y++) {
256 0 : PodCopy((uint32_t*)data, (uint32_t*)sampleData, aFillRect.width);
257 0 : data += surfMap.GetStride();
258 : }
259 0 : } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
260 0 : for (int32_t y = 0; y < aFillRect.height; y++) {
261 0 : PodCopy(data, sampleData, aFillRect.width);
262 0 : data += surfMap.GetStride();
263 : }
264 : }
265 : }
266 :
267 : static void
268 0 : FillRectWithHorizontallyRepeatingVerticalStrip(DataSourceSurface *aSurface,
269 : const IntRect &aFillRect,
270 : const IntRect &aSampleRect)
271 : {
272 0 : MOZ_ASSERT(!aFillRect.Overflows());
273 0 : MOZ_ASSERT(!aSampleRect.Overflows());
274 0 : MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect),
275 : "aFillRect needs to be completely inside the surface");
276 0 : MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect),
277 : "aSampleRect needs to be completely inside the surface");
278 :
279 0 : DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE);
280 0 : if (MOZ2D_WARN_IF(!surfMap.IsMapped())) {
281 0 : return;
282 : }
283 :
284 0 : uint8_t* sampleData = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft());
285 0 : uint8_t* data = DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft());
286 0 : if (BytesPerPixel(aSurface->GetFormat()) == 4) {
287 0 : for (int32_t y = 0; y < aFillRect.height; y++) {
288 0 : int32_t sampleColor = *((uint32_t*)sampleData);
289 0 : for (int32_t x = 0; x < aFillRect.width; x++) {
290 0 : *((uint32_t*)data + x) = sampleColor;
291 : }
292 0 : data += surfMap.GetStride();
293 0 : sampleData += surfMap.GetStride();
294 : }
295 0 : } else if (BytesPerPixel(aSurface->GetFormat()) == 1) {
296 0 : for (int32_t y = 0; y < aFillRect.height; y++) {
297 0 : uint8_t sampleColor = *sampleData;
298 0 : memset(data, sampleColor, aFillRect.width);
299 0 : data += surfMap.GetStride();
300 0 : sampleData += surfMap.GetStride();
301 : }
302 : }
303 : }
304 :
305 : static void
306 0 : DuplicateEdges(DataSourceSurface* aSurface, const IntRect &aFromRect)
307 : {
308 0 : MOZ_ASSERT(!aFromRect.Overflows());
309 0 : MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFromRect),
310 : "aFromRect needs to be completely inside the surface");
311 :
312 0 : IntSize size = aSurface->GetSize();
313 0 : IntRect fill;
314 0 : IntRect sampleRect;
315 0 : for (int32_t ix = 0; ix < 3; ix++) {
316 0 : switch (ix) {
317 : case 0:
318 0 : fill.x = 0;
319 0 : fill.width = aFromRect.x;
320 0 : sampleRect.x = fill.XMost();
321 0 : sampleRect.width = 1;
322 0 : break;
323 : case 1:
324 0 : fill.x = aFromRect.x;
325 0 : fill.width = aFromRect.width;
326 0 : sampleRect.x = fill.x;
327 0 : sampleRect.width = fill.width;
328 0 : break;
329 : case 2:
330 0 : fill.x = aFromRect.XMost();
331 0 : fill.width = size.width - fill.x;
332 0 : sampleRect.x = fill.x - 1;
333 0 : sampleRect.width = 1;
334 0 : break;
335 : }
336 0 : if (fill.width <= 0) {
337 0 : continue;
338 : }
339 0 : bool xIsMiddle = (ix == 1);
340 0 : for (int32_t iy = 0; iy < 3; iy++) {
341 0 : switch (iy) {
342 : case 0:
343 0 : fill.y = 0;
344 0 : fill.height = aFromRect.y;
345 0 : sampleRect.y = fill.YMost();
346 0 : sampleRect.height = 1;
347 0 : break;
348 : case 1:
349 0 : fill.y = aFromRect.y;
350 0 : fill.height = aFromRect.height;
351 0 : sampleRect.y = fill.y;
352 0 : sampleRect.height = fill.height;
353 0 : break;
354 : case 2:
355 0 : fill.y = aFromRect.YMost();
356 0 : fill.height = size.height - fill.y;
357 0 : sampleRect.y = fill.y - 1;
358 0 : sampleRect.height = 1;
359 0 : break;
360 : }
361 0 : if (fill.height <= 0) {
362 0 : continue;
363 : }
364 0 : bool yIsMiddle = (iy == 1);
365 0 : if (!xIsMiddle && !yIsMiddle) {
366 : // Corner
367 0 : FillRectWithPixel(aSurface, fill, sampleRect.TopLeft());
368 : }
369 0 : if (xIsMiddle && !yIsMiddle) {
370 : // Top middle or bottom middle
371 0 : FillRectWithVerticallyRepeatingHorizontalStrip(aSurface, fill, sampleRect);
372 : }
373 0 : if (!xIsMiddle && yIsMiddle) {
374 : // Left middle or right middle
375 0 : FillRectWithHorizontallyRepeatingVerticalStrip(aSurface, fill, sampleRect);
376 : }
377 : }
378 : }
379 0 : }
380 :
381 : static IntPoint
382 0 : TileIndex(const IntRect &aFirstTileRect, const IntPoint &aPoint)
383 : {
384 0 : return IntPoint(int32_t(floor(double(aPoint.x - aFirstTileRect.x) / aFirstTileRect.width)),
385 0 : int32_t(floor(double(aPoint.y - aFirstTileRect.y) / aFirstTileRect.height)));
386 : }
387 :
388 : static void
389 0 : TileSurface(DataSourceSurface* aSource, DataSourceSurface* aTarget, const IntPoint &aOffset)
390 : {
391 0 : IntRect sourceRect(aOffset, aSource->GetSize());
392 0 : IntRect targetRect(IntPoint(0, 0), aTarget->GetSize());
393 0 : IntPoint startIndex = TileIndex(sourceRect, targetRect.TopLeft());
394 0 : IntPoint endIndex = TileIndex(sourceRect, targetRect.BottomRight());
395 :
396 0 : for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
397 0 : for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
398 0 : IntPoint destPoint(sourceRect.x + ix * sourceRect.width,
399 0 : sourceRect.y + iy * sourceRect.height);
400 0 : IntRect destRect(destPoint, sourceRect.Size());
401 0 : destRect = destRect.Intersect(targetRect);
402 0 : IntRect srcRect = destRect - destPoint;
403 0 : CopyRect(aSource, aTarget, srcRect, destRect.TopLeft());
404 : }
405 : }
406 0 : }
407 :
408 : static already_AddRefed<DataSourceSurface>
409 0 : GetDataSurfaceInRect(SourceSurface *aSurface,
410 : const IntRect &aSurfaceRect,
411 : const IntRect &aDestRect,
412 : ConvolveMatrixEdgeMode aEdgeMode)
413 : {
414 0 : MOZ_ASSERT(aSurface ? aSurfaceRect.Size() == aSurface->GetSize() : aSurfaceRect.IsEmpty());
415 :
416 0 : if (aSurfaceRect.Overflows() || aDestRect.Overflows()) {
417 : // We can't rely on the intersection calculations below to make sense when
418 : // XMost() or YMost() overflow. Bail out.
419 0 : return nullptr;
420 : }
421 :
422 0 : IntRect sourceRect = aSurfaceRect;
423 :
424 0 : if (sourceRect.IsEqualEdges(aDestRect)) {
425 0 : return aSurface ? aSurface->GetDataSurface() : nullptr;
426 : }
427 :
428 0 : IntRect intersect = sourceRect.Intersect(aDestRect);
429 0 : IntRect intersectInSourceSpace = intersect - sourceRect.TopLeft();
430 0 : IntRect intersectInDestSpace = intersect - aDestRect.TopLeft();
431 0 : SurfaceFormat format = aSurface ? aSurface->GetFormat() : SurfaceFormat(SurfaceFormat::B8G8R8A8);
432 :
433 : RefPtr<DataSourceSurface> target =
434 0 : Factory::CreateDataSourceSurface(aDestRect.Size(), format, true);
435 0 : if (MOZ2D_WARN_IF(!target)) {
436 0 : return nullptr;
437 : }
438 :
439 0 : if (!aSurface) {
440 0 : return target.forget();
441 : }
442 :
443 0 : RefPtr<DataSourceSurface> dataSource = aSurface->GetDataSurface();
444 0 : MOZ_ASSERT(dataSource);
445 :
446 0 : if (aEdgeMode == EDGE_MODE_WRAP) {
447 0 : TileSurface(dataSource, target, intersectInDestSpace.TopLeft());
448 0 : return target.forget();
449 : }
450 :
451 0 : CopyRect(dataSource, target, intersectInSourceSpace,
452 0 : intersectInDestSpace.TopLeft());
453 :
454 0 : if (aEdgeMode == EDGE_MODE_DUPLICATE) {
455 0 : DuplicateEdges(target, intersectInDestSpace);
456 : }
457 :
458 0 : return target.forget();
459 : }
460 :
461 : /* static */ already_AddRefed<FilterNode>
462 0 : FilterNodeSoftware::Create(FilterType aType)
463 : {
464 0 : RefPtr<FilterNodeSoftware> filter;
465 0 : switch (aType) {
466 : case FilterType::BLEND:
467 0 : filter = new FilterNodeBlendSoftware();
468 0 : break;
469 : case FilterType::TRANSFORM:
470 0 : filter = new FilterNodeTransformSoftware();
471 0 : break;
472 : case FilterType::MORPHOLOGY:
473 0 : filter = new FilterNodeMorphologySoftware();
474 0 : break;
475 : case FilterType::COLOR_MATRIX:
476 0 : filter = new FilterNodeColorMatrixSoftware();
477 0 : break;
478 : case FilterType::FLOOD:
479 0 : filter = new FilterNodeFloodSoftware();
480 0 : break;
481 : case FilterType::TILE:
482 0 : filter = new FilterNodeTileSoftware();
483 0 : break;
484 : case FilterType::TABLE_TRANSFER:
485 0 : filter = new FilterNodeTableTransferSoftware();
486 0 : break;
487 : case FilterType::DISCRETE_TRANSFER:
488 0 : filter = new FilterNodeDiscreteTransferSoftware();
489 0 : break;
490 : case FilterType::LINEAR_TRANSFER:
491 0 : filter = new FilterNodeLinearTransferSoftware();
492 0 : break;
493 : case FilterType::GAMMA_TRANSFER:
494 0 : filter = new FilterNodeGammaTransferSoftware();
495 0 : break;
496 : case FilterType::CONVOLVE_MATRIX:
497 0 : filter = new FilterNodeConvolveMatrixSoftware();
498 0 : break;
499 : case FilterType::DISPLACEMENT_MAP:
500 0 : filter = new FilterNodeDisplacementMapSoftware();
501 0 : break;
502 : case FilterType::TURBULENCE:
503 0 : filter = new FilterNodeTurbulenceSoftware();
504 0 : break;
505 : case FilterType::ARITHMETIC_COMBINE:
506 0 : filter = new FilterNodeArithmeticCombineSoftware();
507 0 : break;
508 : case FilterType::COMPOSITE:
509 0 : filter = new FilterNodeCompositeSoftware();
510 0 : break;
511 : case FilterType::GAUSSIAN_BLUR:
512 0 : filter = new FilterNodeGaussianBlurSoftware();
513 0 : break;
514 : case FilterType::DIRECTIONAL_BLUR:
515 0 : filter = new FilterNodeDirectionalBlurSoftware();
516 0 : break;
517 : case FilterType::CROP:
518 0 : filter = new FilterNodeCropSoftware();
519 0 : break;
520 : case FilterType::PREMULTIPLY:
521 0 : filter = new FilterNodePremultiplySoftware();
522 0 : break;
523 : case FilterType::UNPREMULTIPLY:
524 0 : filter = new FilterNodeUnpremultiplySoftware();
525 0 : break;
526 : case FilterType::POINT_DIFFUSE:
527 0 : filter = new FilterNodeLightingSoftware<PointLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<PointLight, DiffuseLighting>");
528 0 : break;
529 : case FilterType::POINT_SPECULAR:
530 0 : filter = new FilterNodeLightingSoftware<PointLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<PointLight, SpecularLighting>");
531 0 : break;
532 : case FilterType::SPOT_DIFFUSE:
533 0 : filter = new FilterNodeLightingSoftware<SpotLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<SpotLight, DiffuseLighting>");
534 0 : break;
535 : case FilterType::SPOT_SPECULAR:
536 0 : filter = new FilterNodeLightingSoftware<SpotLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<SpotLight, SpecularLighting>");
537 0 : break;
538 : case FilterType::DISTANT_DIFFUSE:
539 0 : filter = new FilterNodeLightingSoftware<DistantLightSoftware, DiffuseLightingSoftware>("FilterNodeLightingSoftware<DistantLight, DiffuseLighting>");
540 0 : break;
541 : case FilterType::DISTANT_SPECULAR:
542 0 : filter = new FilterNodeLightingSoftware<DistantLightSoftware, SpecularLightingSoftware>("FilterNodeLightingSoftware<DistantLight, SpecularLighting>");
543 0 : break;
544 : }
545 0 : return filter.forget();
546 : }
547 :
548 : void
549 0 : FilterNodeSoftware::Draw(DrawTarget* aDrawTarget,
550 : const Rect &aSourceRect,
551 : const Point &aDestPoint,
552 : const DrawOptions &aOptions)
553 : {
554 : #ifdef DEBUG_DUMP_SURFACES
555 : printf("<style>section{margin:10px;}</style><pre>\nRendering filter %s...\n", GetName());
556 : #endif
557 :
558 0 : Rect renderRect = aSourceRect;
559 0 : renderRect.RoundOut();
560 0 : IntRect renderIntRect;
561 0 : if (!renderRect.ToIntRect(&renderIntRect)) {
562 : #ifdef DEBUG_DUMP_SURFACES
563 : printf("render rect overflowed, not painting anything\n");
564 : printf("</pre>\n");
565 : #endif
566 0 : return;
567 : }
568 :
569 0 : IntRect outputRect = GetOutputRectInRect(renderIntRect);
570 0 : if (outputRect.Overflows()) {
571 : #ifdef DEBUG_DUMP_SURFACES
572 : printf("output rect overflowed, not painting anything\n");
573 : printf("</pre>\n");
574 : #endif
575 0 : return;
576 : }
577 :
578 0 : RefPtr<DataSourceSurface> result;
579 0 : if (!outputRect.IsEmpty()) {
580 0 : result = GetOutput(outputRect);
581 : }
582 :
583 0 : if (!result) {
584 : // Null results are allowed and treated as transparent. Don't draw anything.
585 : #ifdef DEBUG_DUMP_SURFACES
586 : printf("output returned null\n");
587 : printf("</pre>\n");
588 : #endif
589 0 : return;
590 : }
591 :
592 : #ifdef DEBUG_DUMP_SURFACES
593 : printf("output from %s:\n", GetName());
594 : printf("<img src='"); gfxUtils::DumpAsDataURL(result); printf("'>\n");
595 : printf("</pre>\n");
596 : #endif
597 :
598 0 : Point sourceToDestOffset = aDestPoint - aSourceRect.TopLeft();
599 0 : Rect renderedSourceRect = Rect(outputRect).Intersect(aSourceRect);
600 0 : Rect renderedDestRect = renderedSourceRect + sourceToDestOffset;
601 0 : if (result->GetFormat() == SurfaceFormat::A8) {
602 : // Interpret the result as having implicitly black color channels.
603 0 : aDrawTarget->PushClipRect(renderedDestRect);
604 0 : aDrawTarget->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)),
605 : result,
606 0 : Point(outputRect.TopLeft()) + sourceToDestOffset,
607 0 : aOptions);
608 0 : aDrawTarget->PopClip();
609 : } else {
610 : aDrawTarget->DrawSurface(result, renderedDestRect,
611 0 : renderedSourceRect - Point(outputRect.TopLeft()),
612 0 : DrawSurfaceOptions(), aOptions);
613 : }
614 : }
615 :
616 : already_AddRefed<DataSourceSurface>
617 0 : FilterNodeSoftware::GetOutput(const IntRect &aRect)
618 : {
619 0 : MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect));
620 :
621 0 : if (aRect.Overflows()) {
622 0 : return nullptr;
623 : }
624 :
625 0 : if (!mCachedRect.Contains(aRect)) {
626 0 : RequestRect(aRect);
627 0 : mCachedOutput = Render(mRequestedRect);
628 0 : if (!mCachedOutput) {
629 0 : mCachedRect = IntRect();
630 0 : mRequestedRect = IntRect();
631 0 : return nullptr;
632 : }
633 0 : mCachedRect = mRequestedRect;
634 0 : mRequestedRect = IntRect();
635 : } else {
636 0 : MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?");
637 : }
638 0 : return GetDataSurfaceInRect(mCachedOutput, mCachedRect, aRect, EDGE_MODE_NONE);
639 : }
640 :
641 : void
642 0 : FilterNodeSoftware::RequestRect(const IntRect &aRect)
643 : {
644 0 : if (mRequestedRect.Contains(aRect)) {
645 : // Bail out now. Otherwise pathological filters can spend time exponential
646 : // in the number of primitives, e.g. if each primitive takes the
647 : // previous primitive as its two inputs.
648 0 : return;
649 : }
650 0 : mRequestedRect = mRequestedRect.Union(aRect);
651 0 : RequestFromInputsForRect(aRect);
652 : }
653 :
654 : void
655 0 : FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex, const IntRect &aRect)
656 : {
657 0 : if (aRect.Overflows()) {
658 0 : return;
659 : }
660 :
661 0 : int32_t inputIndex = InputIndex(aInputEnumIndex);
662 0 : if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
663 0 : gfxDevCrash(LogReason::FilterInputError) << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
664 0 : return;
665 : }
666 0 : if (mInputSurfaces[inputIndex]) {
667 0 : return;
668 : }
669 0 : RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
670 0 : MOZ_ASSERT(filter, "missing input");
671 0 : filter->RequestRect(filter->GetOutputRectInRect(aRect));
672 : }
673 :
674 : SurfaceFormat
675 0 : FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat,
676 : FormatHint aFormatHint)
677 : {
678 0 : if (aCurrentFormat == SurfaceFormat::A8 && aFormatHint == CAN_HANDLE_A8) {
679 0 : return SurfaceFormat::A8;
680 : }
681 0 : return SurfaceFormat::B8G8R8A8;
682 : }
683 :
684 : already_AddRefed<DataSourceSurface>
685 0 : FilterNodeSoftware::GetInputDataSourceSurface(uint32_t aInputEnumIndex,
686 : const IntRect& aRect,
687 : FormatHint aFormatHint,
688 : ConvolveMatrixEdgeMode aEdgeMode,
689 : const IntRect *aTransparencyPaddedSourceRect)
690 : {
691 0 : if (aRect.Overflows()) {
692 0 : return nullptr;
693 : }
694 :
695 : #ifdef DEBUG_DUMP_SURFACES
696 : printf("<section><h1>GetInputDataSourceSurface with aRect: %d, %d, %d, %d</h1>\n",
697 : aRect.x, aRect.y, aRect.width, aRect.height);
698 : #endif
699 0 : int32_t inputIndex = InputIndex(aInputEnumIndex);
700 0 : if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
701 0 : gfxDevCrash(LogReason::FilterInputData) << "Invalid data " << inputIndex << " vs. " << NumberOfSetInputs();
702 0 : return nullptr;
703 : }
704 :
705 0 : if (aRect.IsEmpty()) {
706 0 : return nullptr;
707 : }
708 :
709 0 : RefPtr<SourceSurface> surface;
710 0 : IntRect surfaceRect;
711 :
712 0 : if (mInputSurfaces[inputIndex]) {
713 : // Input from input surface
714 0 : surface = mInputSurfaces[inputIndex];
715 : #ifdef DEBUG_DUMP_SURFACES
716 : printf("input from input surface:\n");
717 : #endif
718 0 : surfaceRect = IntRect(IntPoint(0, 0), surface->GetSize());
719 : } else {
720 : // Input from input filter
721 : #ifdef DEBUG_DUMP_SURFACES
722 : printf("getting input from input filter %s...\n", mInputFilters[inputIndex]->GetName());
723 : #endif
724 0 : RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
725 0 : MOZ_ASSERT(filter, "missing input");
726 0 : IntRect inputFilterOutput = filter->GetOutputRectInRect(aRect);
727 0 : if (!inputFilterOutput.IsEmpty()) {
728 0 : surface = filter->GetOutput(inputFilterOutput);
729 : }
730 : #ifdef DEBUG_DUMP_SURFACES
731 : printf("input from input filter %s:\n", mInputFilters[inputIndex]->GetName());
732 : #endif
733 0 : surfaceRect = inputFilterOutput;
734 0 : MOZ_ASSERT(!surface || surfaceRect.Size() == surface->GetSize());
735 : }
736 :
737 0 : if (surface && surface->GetFormat() == SurfaceFormat::UNKNOWN) {
738 : #ifdef DEBUG_DUMP_SURFACES
739 : printf("wrong input format</section>\n\n");
740 : #endif
741 0 : return nullptr;
742 : }
743 :
744 0 : if (!surfaceRect.IsEmpty() && !surface) {
745 : #ifdef DEBUG_DUMP_SURFACES
746 : printf(" -- no input --</section>\n\n");
747 : #endif
748 0 : return nullptr;
749 : }
750 :
751 0 : if (aTransparencyPaddedSourceRect && !aTransparencyPaddedSourceRect->IsEmpty()) {
752 0 : IntRect srcRect = aTransparencyPaddedSourceRect->Intersect(aRect);
753 0 : surface = GetDataSurfaceInRect(surface, surfaceRect, srcRect, EDGE_MODE_NONE);
754 0 : surfaceRect = srcRect;
755 : }
756 :
757 : RefPtr<DataSourceSurface> result =
758 0 : GetDataSurfaceInRect(surface, surfaceRect, aRect, aEdgeMode);
759 :
760 0 : if (result) {
761 : // TODO: This isn't safe since we don't have a guarantee
762 : // that future Maps will have the same stride
763 : DataSourceSurface::MappedSurface map;
764 0 : if (result->Map(DataSourceSurface::READ, &map)) {
765 : // Unmap immediately since CloneAligned hasn't been updated
766 : // to use the Map API yet. We can still read the stride/data
767 : // values as long as we don't try to dereference them.
768 0 : result->Unmap();
769 0 : if (map.mStride != GetAlignedStride<16>(map.mStride, 1) ||
770 0 : reinterpret_cast<uintptr_t>(map.mData) % 16 != 0) {
771 : // Align unaligned surface.
772 0 : result = CloneAligned(result);
773 : }
774 : } else {
775 0 : result = nullptr;
776 : }
777 : }
778 :
779 :
780 0 : if (!result) {
781 : #ifdef DEBUG_DUMP_SURFACES
782 : printf(" -- no input --</section>\n\n");
783 : #endif
784 0 : return nullptr;
785 : }
786 :
787 0 : SurfaceFormat currentFormat = result->GetFormat();
788 0 : if (DesiredFormat(currentFormat, aFormatHint) == SurfaceFormat::B8G8R8A8 &&
789 : currentFormat != SurfaceFormat::B8G8R8A8) {
790 0 : result = FilterProcessing::ConvertToB8G8R8A8(result);
791 : }
792 :
793 : #ifdef DEBUG_DUMP_SURFACES
794 : printf("<img src='"); gfxUtils::DumpAsDataURL(result); printf("'></section>");
795 : #endif
796 :
797 0 : MOZ_ASSERT(!result || result->GetSize() == aRect.Size(), "wrong surface size");
798 :
799 0 : return result.forget();
800 : }
801 :
802 : IntRect
803 0 : FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex,
804 : const IntRect &aInRect)
805 : {
806 0 : if (aInRect.Overflows()) {
807 0 : return IntRect();
808 : }
809 :
810 0 : int32_t inputIndex = InputIndex(aInputEnumIndex);
811 0 : if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
812 0 : gfxDevCrash(LogReason::FilterInputRect) << "Invalid rect " << inputIndex << " vs. " << NumberOfSetInputs();
813 0 : return IntRect();
814 : }
815 0 : if (mInputSurfaces[inputIndex]) {
816 0 : return aInRect.Intersect(IntRect(IntPoint(0, 0),
817 0 : mInputSurfaces[inputIndex]->GetSize()));
818 : }
819 0 : RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
820 0 : MOZ_ASSERT(filter, "missing input");
821 0 : return filter->GetOutputRectInRect(aInRect);
822 : }
823 :
824 : size_t
825 0 : FilterNodeSoftware::NumberOfSetInputs()
826 : {
827 0 : return std::max(mInputSurfaces.size(), mInputFilters.size());
828 : }
829 :
830 : void
831 0 : FilterNodeSoftware::AddInvalidationListener(FilterInvalidationListener* aListener)
832 : {
833 0 : MOZ_ASSERT(aListener, "null listener");
834 0 : mInvalidationListeners.push_back(aListener);
835 0 : }
836 :
837 : void
838 0 : FilterNodeSoftware::RemoveInvalidationListener(FilterInvalidationListener* aListener)
839 : {
840 0 : MOZ_ASSERT(aListener, "null listener");
841 : std::vector<FilterInvalidationListener*>::iterator it =
842 0 : std::find(mInvalidationListeners.begin(), mInvalidationListeners.end(), aListener);
843 0 : mInvalidationListeners.erase(it);
844 0 : }
845 :
846 : void
847 0 : FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware* aFilter)
848 : {
849 0 : Invalidate();
850 0 : }
851 :
852 : void
853 0 : FilterNodeSoftware::Invalidate()
854 : {
855 0 : mCachedOutput = nullptr;
856 0 : mCachedRect = IntRect();
857 0 : for (std::vector<FilterInvalidationListener*>::iterator it = mInvalidationListeners.begin();
858 0 : it != mInvalidationListeners.end(); it++) {
859 0 : (*it)->FilterInvalidated(this);
860 : }
861 0 : }
862 :
863 0 : FilterNodeSoftware::~FilterNodeSoftware()
864 : {
865 0 : MOZ_ASSERT(!mInvalidationListeners.size(),
866 : "All invalidation listeners should have unsubscribed themselves by now!");
867 :
868 0 : for (std::vector<RefPtr<FilterNodeSoftware> >::iterator it = mInputFilters.begin();
869 0 : it != mInputFilters.end(); it++) {
870 0 : if (*it) {
871 0 : (*it)->RemoveInvalidationListener(this);
872 : }
873 : }
874 0 : }
875 :
876 : void
877 0 : FilterNodeSoftware::SetInput(uint32_t aIndex, FilterNode *aFilter)
878 : {
879 0 : if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
880 0 : MOZ_ASSERT(false, "can only take software filters as inputs");
881 : return;
882 : }
883 0 : SetInput(aIndex, nullptr, static_cast<FilterNodeSoftware*>(aFilter));
884 0 : }
885 :
886 : void
887 0 : FilterNodeSoftware::SetInput(uint32_t aIndex, SourceSurface *aSurface)
888 : {
889 0 : SetInput(aIndex, aSurface, nullptr);
890 0 : }
891 :
892 : void
893 0 : FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex,
894 : SourceSurface *aSurface,
895 : FilterNodeSoftware *aFilter)
896 : {
897 0 : int32_t inputIndex = InputIndex(aInputEnumIndex);
898 0 : if (inputIndex < 0) {
899 0 : gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex;
900 0 : return;
901 : }
902 0 : if ((uint32_t)inputIndex >= NumberOfSetInputs()) {
903 0 : mInputSurfaces.resize(inputIndex + 1);
904 0 : mInputFilters.resize(inputIndex + 1);
905 : }
906 0 : mInputSurfaces[inputIndex] = aSurface;
907 0 : if (mInputFilters[inputIndex]) {
908 0 : mInputFilters[inputIndex]->RemoveInvalidationListener(this);
909 : }
910 0 : if (aFilter) {
911 0 : aFilter->AddInvalidationListener(this);
912 : }
913 0 : mInputFilters[inputIndex] = aFilter;
914 0 : if (!aSurface && !aFilter && (size_t)inputIndex == NumberOfSetInputs()) {
915 0 : mInputSurfaces.resize(inputIndex);
916 0 : mInputFilters.resize(inputIndex);
917 : }
918 0 : Invalidate();
919 : }
920 :
921 0 : FilterNodeBlendSoftware::FilterNodeBlendSoftware()
922 0 : : mBlendMode(BLEND_MODE_MULTIPLY)
923 0 : {}
924 :
925 : int32_t
926 0 : FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex)
927 : {
928 0 : switch (aInputEnumIndex) {
929 0 : case IN_BLEND_IN: return 0;
930 0 : case IN_BLEND_IN2: return 1;
931 0 : default: return -1;
932 : }
933 : }
934 :
935 : void
936 0 : FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex, uint32_t aBlendMode)
937 : {
938 0 : MOZ_ASSERT(aIndex == ATT_BLEND_BLENDMODE);
939 0 : mBlendMode = static_cast<BlendMode>(aBlendMode);
940 0 : Invalidate();
941 0 : }
942 :
943 0 : static CompositionOp ToBlendOp(BlendMode aOp)
944 : {
945 0 : switch (aOp) {
946 : case BLEND_MODE_MULTIPLY:
947 0 : return CompositionOp::OP_MULTIPLY;
948 : case BLEND_MODE_SCREEN:
949 0 : return CompositionOp::OP_SCREEN;
950 : case BLEND_MODE_OVERLAY:
951 0 : return CompositionOp::OP_OVERLAY;
952 : case BLEND_MODE_DARKEN:
953 0 : return CompositionOp::OP_DARKEN;
954 : case BLEND_MODE_LIGHTEN:
955 0 : return CompositionOp::OP_LIGHTEN;
956 : case BLEND_MODE_COLOR_DODGE:
957 0 : return CompositionOp::OP_COLOR_DODGE;
958 : case BLEND_MODE_COLOR_BURN:
959 0 : return CompositionOp::OP_COLOR_BURN;
960 : case BLEND_MODE_HARD_LIGHT:
961 0 : return CompositionOp::OP_HARD_LIGHT;
962 : case BLEND_MODE_SOFT_LIGHT:
963 0 : return CompositionOp::OP_SOFT_LIGHT;
964 : case BLEND_MODE_DIFFERENCE:
965 0 : return CompositionOp::OP_DIFFERENCE;
966 : case BLEND_MODE_EXCLUSION:
967 0 : return CompositionOp::OP_EXCLUSION;
968 : case BLEND_MODE_HUE:
969 0 : return CompositionOp::OP_HUE;
970 : case BLEND_MODE_SATURATION:
971 0 : return CompositionOp::OP_SATURATION;
972 : case BLEND_MODE_COLOR:
973 0 : return CompositionOp::OP_COLOR;
974 : case BLEND_MODE_LUMINOSITY:
975 0 : return CompositionOp::OP_LUMINOSITY;
976 : default:
977 0 : return CompositionOp::OP_OVER;
978 : }
979 :
980 : return CompositionOp::OP_OVER;
981 : }
982 :
983 : already_AddRefed<DataSourceSurface>
984 0 : FilterNodeBlendSoftware::Render(const IntRect& aRect)
985 : {
986 : RefPtr<DataSourceSurface> input1 =
987 0 : GetInputDataSourceSurface(IN_BLEND_IN, aRect, NEED_COLOR_CHANNELS);
988 : RefPtr<DataSourceSurface> input2 =
989 0 : GetInputDataSourceSurface(IN_BLEND_IN2, aRect, NEED_COLOR_CHANNELS);
990 :
991 : // Null inputs need to be treated as transparent.
992 :
993 : // First case: both are transparent.
994 0 : if (!input1 && !input2) {
995 : // Then the result is transparent, too.
996 0 : return nullptr;
997 : }
998 :
999 : // Second case: one of them is transparent. Return the non-transparent one.
1000 0 : if (!input1 || !input2) {
1001 0 : return input1 ? input1.forget() : input2.forget();
1002 : }
1003 :
1004 : // Third case: both are non-transparent.
1005 : // Apply normal filtering.
1006 0 : RefPtr<DataSourceSurface> target = FilterProcessing::ApplyBlending(input1, input2, mBlendMode);
1007 0 : if (target != nullptr) {
1008 0 : return target.forget();
1009 : }
1010 :
1011 0 : IntSize size = input1->GetSize();
1012 : target =
1013 0 : Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1014 0 : if (MOZ2D_WARN_IF(!target)) {
1015 0 : return nullptr;
1016 : }
1017 :
1018 0 : CopyRect(input1, target, IntRect(IntPoint(), size), IntPoint());
1019 :
1020 : // This needs to stay in scope until the draw target has been flushed.
1021 0 : DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::READ_WRITE);
1022 0 : if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
1023 0 : return nullptr;
1024 : }
1025 :
1026 : RefPtr<DrawTarget> dt =
1027 0 : Factory::CreateDrawTargetForData(BackendType::CAIRO,
1028 : targetMap.GetData(),
1029 0 : target->GetSize(),
1030 : targetMap.GetStride(),
1031 0 : target->GetFormat());
1032 :
1033 0 : if (!dt) {
1034 0 : gfxWarning() << "FilterNodeBlendSoftware::Render failed in CreateDrawTargetForData";
1035 0 : return nullptr;
1036 : }
1037 :
1038 0 : Rect r(0, 0, size.width, size.height);
1039 0 : dt->DrawSurface(input2, r, r, DrawSurfaceOptions(), DrawOptions(1.0f, ToBlendOp(mBlendMode)));
1040 0 : dt->Flush();
1041 0 : return target.forget();
1042 : }
1043 :
1044 : void
1045 0 : FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect &aRect)
1046 : {
1047 0 : RequestInputRect(IN_BLEND_IN, aRect);
1048 0 : RequestInputRect(IN_BLEND_IN2, aRect);
1049 0 : }
1050 :
1051 : IntRect
1052 0 : FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect& aRect)
1053 : {
1054 0 : return GetInputRectInRect(IN_BLEND_IN, aRect).Union(
1055 0 : GetInputRectInRect(IN_BLEND_IN2, aRect)).Intersect(aRect);
1056 : }
1057 :
1058 0 : FilterNodeTransformSoftware::FilterNodeTransformSoftware()
1059 0 : : mSamplingFilter(SamplingFilter::GOOD)
1060 0 : {}
1061 :
1062 : int32_t
1063 0 : FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex)
1064 : {
1065 0 : switch (aInputEnumIndex) {
1066 0 : case IN_TRANSFORM_IN: return 0;
1067 0 : default: return -1;
1068 : }
1069 : }
1070 :
1071 : void
1072 0 : FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, uint32_t aFilter)
1073 : {
1074 0 : MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER);
1075 0 : mSamplingFilter = static_cast<SamplingFilter>(aFilter);
1076 0 : Invalidate();
1077 0 : }
1078 :
1079 : void
1080 0 : FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, const Matrix &aMatrix)
1081 : {
1082 0 : MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX);
1083 0 : mMatrix = aMatrix;
1084 0 : Invalidate();
1085 0 : }
1086 :
1087 : IntRect
1088 0 : FilterNodeTransformSoftware::SourceRectForOutputRect(const IntRect &aRect)
1089 : {
1090 0 : if (aRect.IsEmpty()) {
1091 0 : return IntRect();
1092 : }
1093 :
1094 0 : Matrix inverted(mMatrix);
1095 0 : if (!inverted.Invert()) {
1096 0 : return IntRect();
1097 : }
1098 :
1099 0 : Rect neededRect = inverted.TransformBounds(Rect(aRect));
1100 0 : neededRect.RoundOut();
1101 0 : IntRect neededIntRect;
1102 0 : if (!neededRect.ToIntRect(&neededIntRect)) {
1103 0 : return IntRect();
1104 : }
1105 0 : return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect);
1106 : }
1107 :
1108 : already_AddRefed<DataSourceSurface>
1109 0 : FilterNodeTransformSoftware::Render(const IntRect& aRect)
1110 : {
1111 0 : IntRect srcRect = SourceRectForOutputRect(aRect);
1112 :
1113 : RefPtr<DataSourceSurface> input =
1114 0 : GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect);
1115 :
1116 0 : if (!input) {
1117 0 : return nullptr;
1118 : }
1119 :
1120 0 : Matrix transform = Matrix::Translation(srcRect.x, srcRect.y) * mMatrix *
1121 0 : Matrix::Translation(-aRect.x, -aRect.y);
1122 0 : if (transform.IsIdentity() && srcRect.Size() == aRect.Size()) {
1123 0 : return input.forget();
1124 : }
1125 :
1126 : RefPtr<DataSourceSurface> surf =
1127 0 : Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat(), true);
1128 :
1129 0 : if (!surf) {
1130 0 : return nullptr;
1131 : }
1132 :
1133 : DataSourceSurface::MappedSurface mapping;
1134 0 : if (!surf->Map(DataSourceSurface::MapType::WRITE, &mapping)) {
1135 0 : gfxCriticalError() << "FilterNodeTransformSoftware::Render failed to map surface";
1136 0 : return nullptr;
1137 : }
1138 :
1139 : RefPtr<DrawTarget> dt =
1140 0 : Factory::CreateDrawTargetForData(BackendType::CAIRO,
1141 : mapping.mData,
1142 0 : surf->GetSize(),
1143 : mapping.mStride,
1144 0 : surf->GetFormat());
1145 0 : if (!dt) {
1146 0 : gfxWarning() << "FilterNodeTransformSoftware::Render failed in CreateDrawTargetForData";
1147 0 : return nullptr;
1148 : }
1149 :
1150 0 : Rect r(0, 0, srcRect.width, srcRect.height);
1151 0 : dt->SetTransform(transform);
1152 0 : dt->DrawSurface(input, r, r, DrawSurfaceOptions(mSamplingFilter));
1153 :
1154 0 : dt->Flush();
1155 0 : surf->Unmap();
1156 0 : return surf.forget();
1157 : }
1158 :
1159 : void
1160 0 : FilterNodeTransformSoftware::RequestFromInputsForRect(const IntRect &aRect)
1161 : {
1162 0 : RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect));
1163 0 : }
1164 :
1165 : IntRect
1166 0 : FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect)
1167 : {
1168 0 : IntRect srcRect = SourceRectForOutputRect(aRect);
1169 0 : if (srcRect.IsEmpty()) {
1170 0 : return IntRect();
1171 : }
1172 :
1173 0 : Rect outRect = mMatrix.TransformBounds(Rect(srcRect));
1174 0 : outRect.RoundOut();
1175 0 : IntRect outIntRect;
1176 0 : if (!outRect.ToIntRect(&outIntRect)) {
1177 0 : return IntRect();
1178 : }
1179 0 : return outIntRect.Intersect(aRect);
1180 : }
1181 :
1182 0 : FilterNodeMorphologySoftware::FilterNodeMorphologySoftware()
1183 0 : : mOperator(MORPHOLOGY_OPERATOR_ERODE)
1184 0 : {}
1185 :
1186 : int32_t
1187 0 : FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex)
1188 : {
1189 0 : switch (aInputEnumIndex) {
1190 0 : case IN_MORPHOLOGY_IN: return 0;
1191 0 : default: return -1;
1192 : }
1193 : }
1194 :
1195 : void
1196 0 : FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
1197 : const IntSize &aRadii)
1198 : {
1199 0 : MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_RADII);
1200 0 : mRadii.width = std::min(std::max(aRadii.width, 0), 100000);
1201 0 : mRadii.height = std::min(std::max(aRadii.height, 0), 100000);
1202 0 : Invalidate();
1203 0 : }
1204 :
1205 : void
1206 0 : FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
1207 : uint32_t aOperator)
1208 : {
1209 0 : MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_OPERATOR);
1210 0 : mOperator = static_cast<MorphologyOperator>(aOperator);
1211 0 : Invalidate();
1212 0 : }
1213 :
1214 : static already_AddRefed<DataSourceSurface>
1215 0 : ApplyMorphology(const IntRect& aSourceRect, DataSourceSurface* aInput,
1216 : const IntRect& aDestRect, int32_t rx, int32_t ry,
1217 : MorphologyOperator aOperator)
1218 : {
1219 0 : IntRect srcRect = aSourceRect - aDestRect.TopLeft();
1220 0 : IntRect destRect = aDestRect - aDestRect.TopLeft();
1221 0 : IntRect tmpRect(destRect.x, srcRect.y, destRect.width, srcRect.height);
1222 : #ifdef DEBUG
1223 0 : IntMargin margin = srcRect - destRect;
1224 0 : MOZ_ASSERT(margin.top >= ry && margin.right >= rx &&
1225 : margin.bottom >= ry && margin.left >= rx, "insufficient margin");
1226 : #endif
1227 :
1228 0 : RefPtr<DataSourceSurface> tmp;
1229 0 : if (rx == 0) {
1230 0 : tmp = aInput;
1231 : } else {
1232 0 : tmp = Factory::CreateDataSourceSurface(tmpRect.Size(), SurfaceFormat::B8G8R8A8);
1233 0 : if (MOZ2D_WARN_IF(!tmp)) {
1234 0 : return nullptr;
1235 : }
1236 :
1237 0 : DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
1238 0 : DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::WRITE);
1239 0 : if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !tmpMap.IsMapped())) {
1240 0 : return nullptr;
1241 : }
1242 0 : uint8_t* sourceData = DataAtOffset(aInput, sourceMap.GetMappedSurface(),
1243 0 : destRect.TopLeft() - srcRect.TopLeft());
1244 0 : uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(),
1245 0 : destRect.TopLeft() - tmpRect.TopLeft());
1246 :
1247 0 : FilterProcessing::ApplyMorphologyHorizontal(
1248 0 : sourceData, sourceMap.GetStride(), tmpData, tmpMap.GetStride(), tmpRect, rx, aOperator);
1249 : }
1250 :
1251 0 : RefPtr<DataSourceSurface> dest;
1252 0 : if (ry == 0) {
1253 0 : dest = tmp;
1254 : } else {
1255 0 : dest = Factory::CreateDataSourceSurface(destRect.Size(), SurfaceFormat::B8G8R8A8);
1256 0 : if (MOZ2D_WARN_IF(!dest)) {
1257 0 : return nullptr;
1258 : }
1259 :
1260 0 : DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::READ);
1261 0 : DataSourceSurface::ScopedMap destMap(dest, DataSourceSurface::WRITE);
1262 0 : if (MOZ2D_WARN_IF(!tmpMap.IsMapped() || !destMap.IsMapped())) {
1263 0 : return nullptr;
1264 : }
1265 0 : int32_t tmpStride = tmpMap.GetStride();
1266 0 : uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(), destRect.TopLeft() - tmpRect.TopLeft());
1267 :
1268 0 : int32_t destStride = destMap.GetStride();
1269 0 : uint8_t* destData = destMap.GetData();
1270 :
1271 : FilterProcessing::ApplyMorphologyVertical(
1272 0 : tmpData, tmpStride, destData, destStride, destRect, ry, aOperator);
1273 : }
1274 :
1275 0 : return dest.forget();
1276 : }
1277 :
1278 : already_AddRefed<DataSourceSurface>
1279 0 : FilterNodeMorphologySoftware::Render(const IntRect& aRect)
1280 : {
1281 0 : IntRect srcRect = aRect;
1282 0 : srcRect.Inflate(mRadii);
1283 :
1284 : RefPtr<DataSourceSurface> input =
1285 0 : GetInputDataSourceSurface(IN_MORPHOLOGY_IN, srcRect, NEED_COLOR_CHANNELS);
1286 0 : if (!input) {
1287 0 : return nullptr;
1288 : }
1289 :
1290 0 : int32_t rx = mRadii.width;
1291 0 : int32_t ry = mRadii.height;
1292 :
1293 0 : if (rx == 0 && ry == 0) {
1294 0 : return input.forget();
1295 : }
1296 :
1297 0 : return ApplyMorphology(srcRect, input, aRect, rx, ry, mOperator);
1298 : }
1299 :
1300 : void
1301 0 : FilterNodeMorphologySoftware::RequestFromInputsForRect(const IntRect &aRect)
1302 : {
1303 0 : IntRect srcRect = aRect;
1304 0 : srcRect.Inflate(mRadii);
1305 0 : RequestInputRect(IN_MORPHOLOGY_IN, srcRect);
1306 0 : }
1307 :
1308 : IntRect
1309 0 : FilterNodeMorphologySoftware::GetOutputRectInRect(const IntRect& aRect)
1310 : {
1311 0 : IntRect inflatedSourceRect = aRect;
1312 0 : inflatedSourceRect.Inflate(mRadii);
1313 0 : IntRect inputRect = GetInputRectInRect(IN_MORPHOLOGY_IN, inflatedSourceRect);
1314 0 : if (mOperator == MORPHOLOGY_OPERATOR_ERODE) {
1315 0 : inputRect.Deflate(mRadii);
1316 : } else {
1317 0 : inputRect.Inflate(mRadii);
1318 : }
1319 0 : return inputRect.Intersect(aRect);
1320 : }
1321 :
1322 : int32_t
1323 0 : FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex)
1324 : {
1325 0 : switch (aInputEnumIndex) {
1326 0 : case IN_COLOR_MATRIX_IN: return 0;
1327 0 : default: return -1;
1328 : }
1329 : }
1330 :
1331 : void
1332 0 : FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
1333 : const Matrix5x4 &aMatrix)
1334 : {
1335 0 : MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX);
1336 0 : mMatrix = aMatrix;
1337 0 : Invalidate();
1338 0 : }
1339 :
1340 : void
1341 0 : FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
1342 : uint32_t aAlphaMode)
1343 : {
1344 0 : MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE);
1345 0 : mAlphaMode = (AlphaMode)aAlphaMode;
1346 0 : Invalidate();
1347 0 : }
1348 :
1349 : static already_AddRefed<DataSourceSurface>
1350 0 : Premultiply(DataSourceSurface* aSurface)
1351 : {
1352 0 : if (aSurface->GetFormat() == SurfaceFormat::A8) {
1353 0 : RefPtr<DataSourceSurface> surface(aSurface);
1354 0 : return surface.forget();
1355 : }
1356 :
1357 0 : IntSize size = aSurface->GetSize();
1358 : RefPtr<DataSourceSurface> target =
1359 0 : Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1360 0 : if (MOZ2D_WARN_IF(!target)) {
1361 0 : return nullptr;
1362 : }
1363 :
1364 0 : DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
1365 0 : DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
1366 0 : if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
1367 0 : return nullptr;
1368 : }
1369 :
1370 0 : uint8_t* inputData = inputMap.GetData();
1371 0 : int32_t inputStride = inputMap.GetStride();
1372 0 : uint8_t* targetData = targetMap.GetData();
1373 0 : int32_t targetStride = targetMap.GetStride();
1374 :
1375 : FilterProcessing::DoPremultiplicationCalculation(
1376 0 : size, targetData, targetStride, inputData, inputStride);
1377 :
1378 0 : return target.forget();
1379 : }
1380 :
1381 : static already_AddRefed<DataSourceSurface>
1382 0 : Unpremultiply(DataSourceSurface* aSurface)
1383 : {
1384 0 : if (aSurface->GetFormat() == SurfaceFormat::A8) {
1385 0 : RefPtr<DataSourceSurface> surface(aSurface);
1386 0 : return surface.forget();
1387 : }
1388 :
1389 0 : IntSize size = aSurface->GetSize();
1390 : RefPtr<DataSourceSurface> target =
1391 0 : Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
1392 0 : if (MOZ2D_WARN_IF(!target)) {
1393 0 : return nullptr;
1394 : }
1395 :
1396 0 : DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ);
1397 0 : DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
1398 0 : if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) {
1399 0 : return nullptr;
1400 : }
1401 :
1402 0 : uint8_t* inputData = inputMap.GetData();
1403 0 : int32_t inputStride = inputMap.GetStride();
1404 0 : uint8_t* targetData = targetMap.GetData();
1405 0 : int32_t targetStride = targetMap.GetStride();
1406 :
1407 : FilterProcessing::DoUnpremultiplicationCalculation(
1408 0 : size, targetData, targetStride, inputData, inputStride);
1409 :
1410 0 : return target.forget();
1411 : }
1412 :
1413 : already_AddRefed<DataSourceSurface>
1414 0 : FilterNodeColorMatrixSoftware::Render(const IntRect& aRect)
1415 : {
1416 : RefPtr<DataSourceSurface> input =
1417 0 : GetInputDataSourceSurface(IN_COLOR_MATRIX_IN, aRect, NEED_COLOR_CHANNELS);
1418 0 : if (!input) {
1419 0 : return nullptr;
1420 : }
1421 :
1422 0 : if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
1423 0 : input = Unpremultiply(input);
1424 : }
1425 :
1426 : RefPtr<DataSourceSurface> result =
1427 0 : FilterProcessing::ApplyColorMatrix(input, mMatrix);
1428 :
1429 0 : if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
1430 0 : result = Premultiply(result);
1431 : }
1432 :
1433 0 : return result.forget();
1434 : }
1435 :
1436 : void
1437 0 : FilterNodeColorMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
1438 : {
1439 0 : RequestInputRect(IN_COLOR_MATRIX_IN, aRect);
1440 0 : }
1441 :
1442 : IntRect
1443 0 : FilterNodeColorMatrixSoftware::GetOutputRectInRect(const IntRect& aRect)
1444 : {
1445 0 : if (mMatrix._54 > 0.0f) {
1446 0 : return aRect;
1447 : }
1448 0 : return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect);
1449 : }
1450 :
1451 : void
1452 0 : FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex, const Color &aColor)
1453 : {
1454 0 : MOZ_ASSERT(aIndex == ATT_FLOOD_COLOR);
1455 0 : mColor = aColor;
1456 0 : Invalidate();
1457 0 : }
1458 :
1459 : static uint32_t
1460 0 : ColorToBGRA(const Color& aColor)
1461 : {
1462 : union {
1463 : uint32_t color;
1464 : uint8_t components[4];
1465 : };
1466 0 : components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = NS_lround(aColor.r * aColor.a * 255.0f);
1467 0 : components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = NS_lround(aColor.g * aColor.a * 255.0f);
1468 0 : components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = NS_lround(aColor.b * aColor.a * 255.0f);
1469 0 : components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = NS_lround(aColor.a * 255.0f);
1470 0 : return color;
1471 : }
1472 :
1473 : static SurfaceFormat
1474 0 : FormatForColor(Color aColor)
1475 : {
1476 0 : if (aColor.r == 0 && aColor.g == 0 && aColor.b == 0) {
1477 0 : return SurfaceFormat::A8;
1478 : }
1479 0 : return SurfaceFormat::B8G8R8A8;
1480 : }
1481 :
1482 : already_AddRefed<DataSourceSurface>
1483 0 : FilterNodeFloodSoftware::Render(const IntRect& aRect)
1484 : {
1485 0 : SurfaceFormat format = FormatForColor(mColor);
1486 : RefPtr<DataSourceSurface> target =
1487 0 : Factory::CreateDataSourceSurface(aRect.Size(), format);
1488 0 : if (MOZ2D_WARN_IF(!target)) {
1489 0 : return nullptr;
1490 : }
1491 :
1492 0 : DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
1493 0 : if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
1494 0 : return nullptr;
1495 : }
1496 :
1497 0 : uint8_t* targetData = targetMap.GetData();
1498 0 : int32_t stride = targetMap.GetStride();
1499 :
1500 0 : if (format == SurfaceFormat::B8G8R8A8) {
1501 0 : uint32_t color = ColorToBGRA(mColor);
1502 0 : for (int32_t y = 0; y < aRect.height; y++) {
1503 0 : for (int32_t x = 0; x < aRect.width; x++) {
1504 0 : *((uint32_t*)targetData + x) = color;
1505 : }
1506 0 : PodZero(&targetData[aRect.width * 4], stride - aRect.width * 4);
1507 0 : targetData += stride;
1508 : }
1509 0 : } else if (format == SurfaceFormat::A8) {
1510 0 : uint8_t alpha = NS_lround(mColor.a * 255.0f);
1511 0 : for (int32_t y = 0; y < aRect.height; y++) {
1512 0 : for (int32_t x = 0; x < aRect.width; x++) {
1513 0 : targetData[x] = alpha;
1514 : }
1515 0 : PodZero(&targetData[aRect.width], stride - aRect.width);
1516 0 : targetData += stride;
1517 : }
1518 : } else {
1519 0 : gfxDevCrash(LogReason::FilterInputFormat) << "Bad format in flood render " << (int)format;
1520 0 : return nullptr;
1521 : }
1522 :
1523 0 : return target.forget();
1524 : }
1525 :
1526 : // Override GetOutput to get around caching. Rendering simple floods is
1527 : // comparatively fast.
1528 : already_AddRefed<DataSourceSurface>
1529 0 : FilterNodeFloodSoftware::GetOutput(const IntRect& aRect)
1530 : {
1531 0 : return Render(aRect);
1532 : }
1533 :
1534 : IntRect
1535 0 : FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect& aRect)
1536 : {
1537 0 : if (mColor.a == 0.0f) {
1538 0 : return IntRect();
1539 : }
1540 0 : return aRect;
1541 : }
1542 :
1543 : int32_t
1544 0 : FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex)
1545 : {
1546 0 : switch (aInputEnumIndex) {
1547 0 : case IN_TILE_IN: return 0;
1548 0 : default: return -1;
1549 : }
1550 : }
1551 :
1552 : void
1553 0 : FilterNodeTileSoftware::SetAttribute(uint32_t aIndex,
1554 : const IntRect &aSourceRect)
1555 : {
1556 0 : MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT);
1557 0 : mSourceRect = IntRect(int32_t(aSourceRect.x), int32_t(aSourceRect.y),
1558 0 : int32_t(aSourceRect.width), int32_t(aSourceRect.height));
1559 0 : Invalidate();
1560 0 : }
1561 :
1562 : namespace {
1563 : struct CompareIntRects
1564 : {
1565 0 : bool operator()(const IntRect& a, const IntRect& b) const
1566 : {
1567 0 : if (a.x != b.x) {
1568 0 : return a.x < b.x;
1569 : }
1570 0 : if (a.y != b.y) {
1571 0 : return a.y < b.y;
1572 : }
1573 0 : if (a.width != b.width) {
1574 0 : return a.width < b.width;
1575 : }
1576 0 : return a.height < b.height;
1577 : }
1578 : };
1579 :
1580 : } // namespace
1581 :
1582 : already_AddRefed<DataSourceSurface>
1583 0 : FilterNodeTileSoftware::Render(const IntRect& aRect)
1584 : {
1585 0 : if (mSourceRect.IsEmpty()) {
1586 0 : return nullptr;
1587 : }
1588 :
1589 0 : if (mSourceRect.Contains(aRect)) {
1590 0 : return GetInputDataSourceSurface(IN_TILE_IN, aRect);
1591 : }
1592 :
1593 0 : RefPtr<DataSourceSurface> target;
1594 :
1595 : typedef std::map<IntRect, RefPtr<DataSourceSurface>, CompareIntRects> InputMap;
1596 0 : InputMap inputs;
1597 :
1598 0 : IntPoint startIndex = TileIndex(mSourceRect, aRect.TopLeft());
1599 0 : IntPoint endIndex = TileIndex(mSourceRect, aRect.BottomRight());
1600 0 : for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) {
1601 0 : for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) {
1602 0 : IntPoint sourceToDestOffset(ix * mSourceRect.width,
1603 0 : iy * mSourceRect.height);
1604 0 : IntRect destRect = aRect.Intersect(mSourceRect + sourceToDestOffset);
1605 0 : IntRect srcRect = destRect - sourceToDestOffset;
1606 0 : if (srcRect.IsEmpty()) {
1607 0 : continue;
1608 : }
1609 :
1610 0 : RefPtr<DataSourceSurface> input;
1611 0 : InputMap::iterator it = inputs.find(srcRect);
1612 0 : if (it == inputs.end()) {
1613 0 : input = GetInputDataSourceSurface(IN_TILE_IN, srcRect);
1614 0 : inputs[srcRect] = input;
1615 : } else {
1616 0 : input = it->second;
1617 : }
1618 0 : if (!input) {
1619 0 : return nullptr;
1620 : }
1621 0 : if (!target) {
1622 : // We delay creating the target until now because we want to use the
1623 : // same format as our input filter, and we do not actually know the
1624 : // input format before we call GetInputDataSourceSurface.
1625 0 : target = Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat());
1626 0 : if (MOZ2D_WARN_IF(!target)) {
1627 0 : return nullptr;
1628 : }
1629 : }
1630 :
1631 0 : if (input->GetFormat() != target->GetFormat()) {
1632 : // Different rectangles of the input can have different formats. If
1633 : // that happens, just convert everything to B8G8R8A8.
1634 0 : target = FilterProcessing::ConvertToB8G8R8A8(target);
1635 0 : input = FilterProcessing::ConvertToB8G8R8A8(input);
1636 0 : if (MOZ2D_WARN_IF(!target) || MOZ2D_WARN_IF(!input)) {
1637 0 : return nullptr;
1638 : }
1639 : }
1640 :
1641 0 : CopyRect(input, target, srcRect - srcRect.TopLeft(), destRect.TopLeft() - aRect.TopLeft());
1642 : }
1643 : }
1644 :
1645 0 : return target.forget();
1646 : }
1647 :
1648 : void
1649 0 : FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect &aRect)
1650 : {
1651 : // Do not request anything.
1652 : // Source rects for the tile filter can be discontinuous with large gaps
1653 : // between them. Requesting those from our input filter might cause it to
1654 : // render the whole bounding box of all of them, which would be wasteful.
1655 0 : }
1656 :
1657 : IntRect
1658 0 : FilterNodeTileSoftware::GetOutputRectInRect(const IntRect& aRect)
1659 : {
1660 0 : return aRect;
1661 : }
1662 :
1663 0 : FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware()
1664 : : mDisableR(true)
1665 : , mDisableG(true)
1666 : , mDisableB(true)
1667 0 : , mDisableA(true)
1668 0 : {}
1669 :
1670 : void
1671 0 : FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex,
1672 : bool aDisable)
1673 : {
1674 0 : switch (aIndex) {
1675 : case ATT_TRANSFER_DISABLE_R:
1676 0 : mDisableR = aDisable;
1677 0 : break;
1678 : case ATT_TRANSFER_DISABLE_G:
1679 0 : mDisableG = aDisable;
1680 0 : break;
1681 : case ATT_TRANSFER_DISABLE_B:
1682 0 : mDisableB = aDisable;
1683 0 : break;
1684 : case ATT_TRANSFER_DISABLE_A:
1685 0 : mDisableA = aDisable;
1686 0 : break;
1687 : default:
1688 0 : MOZ_CRASH("GFX: FilterNodeComponentTransferSoftware::SetAttribute");
1689 : }
1690 0 : Invalidate();
1691 0 : }
1692 :
1693 : void
1694 0 : FilterNodeComponentTransferSoftware::GenerateLookupTable(ptrdiff_t aComponent,
1695 : uint8_t aTables[4][256],
1696 : bool aDisabled)
1697 : {
1698 0 : if (aDisabled) {
1699 : static uint8_t sIdentityLookupTable[256];
1700 : static bool sInitializedIdentityLookupTable = false;
1701 0 : if (!sInitializedIdentityLookupTable) {
1702 0 : for (int32_t i = 0; i < 256; i++) {
1703 0 : sIdentityLookupTable[i] = i;
1704 : }
1705 0 : sInitializedIdentityLookupTable = true;
1706 : }
1707 0 : memcpy(aTables[aComponent], sIdentityLookupTable, 256);
1708 : } else {
1709 0 : FillLookupTable(aComponent, aTables[aComponent]);
1710 : }
1711 0 : }
1712 :
1713 : template<uint32_t BytesPerPixel>
1714 0 : static void TransferComponents(DataSourceSurface* aInput,
1715 : DataSourceSurface* aTarget,
1716 : const uint8_t aLookupTables[BytesPerPixel][256])
1717 : {
1718 0 : MOZ_ASSERT(aInput->GetFormat() == aTarget->GetFormat(), "different formats");
1719 0 : IntSize size = aInput->GetSize();
1720 :
1721 0 : DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ);
1722 0 : DataSourceSurface::ScopedMap targetMap(aTarget, DataSourceSurface::WRITE);
1723 0 : if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
1724 0 : return;
1725 : }
1726 :
1727 0 : uint8_t* sourceData = sourceMap.GetData();
1728 0 : int32_t sourceStride = sourceMap.GetStride();
1729 0 : uint8_t* targetData = targetMap.GetData();
1730 0 : int32_t targetStride = targetMap.GetStride();
1731 :
1732 0 : MOZ_ASSERT(sourceStride <= targetStride, "target smaller than source");
1733 :
1734 0 : for (int32_t y = 0; y < size.height; y++) {
1735 0 : for (int32_t x = 0; x < size.width; x++) {
1736 0 : uint32_t sourceIndex = y * sourceStride + x * BytesPerPixel;
1737 0 : uint32_t targetIndex = y * targetStride + x * BytesPerPixel;
1738 0 : for (uint32_t i = 0; i < BytesPerPixel; i++) {
1739 0 : targetData[targetIndex + i] = aLookupTables[i][sourceData[sourceIndex + i]];
1740 : }
1741 : }
1742 :
1743 : // Zero padding to keep valgrind happy.
1744 0 : PodZero(&targetData[y * targetStride + size.width * BytesPerPixel],
1745 0 : targetStride - size.width * BytesPerPixel);
1746 : }
1747 : }
1748 :
1749 : bool
1750 0 : IsAllZero(uint8_t aLookupTable[256])
1751 : {
1752 0 : for (int32_t i = 0; i < 256; i++) {
1753 0 : if (aLookupTable[i] != 0) {
1754 0 : return false;
1755 : }
1756 : }
1757 0 : return true;
1758 : }
1759 :
1760 : already_AddRefed<DataSourceSurface>
1761 0 : FilterNodeComponentTransferSoftware::Render(const IntRect& aRect)
1762 : {
1763 0 : if (mDisableR && mDisableG && mDisableB && mDisableA) {
1764 0 : return GetInputDataSourceSurface(IN_TRANSFER_IN, aRect);
1765 : }
1766 :
1767 : uint8_t lookupTables[4][256];
1768 0 : GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R, lookupTables, mDisableR);
1769 0 : GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G, lookupTables, mDisableG);
1770 0 : GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B, lookupTables, mDisableB);
1771 0 : GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A, lookupTables, mDisableA);
1772 :
1773 : bool needColorChannels =
1774 0 : lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R][0] != 0 ||
1775 0 : lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G][0] != 0 ||
1776 0 : lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B][0] != 0;
1777 :
1778 0 : FormatHint pref = needColorChannels ? NEED_COLOR_CHANNELS : CAN_HANDLE_A8;
1779 :
1780 : RefPtr<DataSourceSurface> input =
1781 0 : GetInputDataSourceSurface(IN_TRANSFER_IN, aRect, pref);
1782 0 : if (!input) {
1783 0 : return nullptr;
1784 : }
1785 :
1786 0 : if (input->GetFormat() == SurfaceFormat::B8G8R8A8 && !needColorChannels) {
1787 : bool colorChannelsBecomeBlack =
1788 0 : IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) &&
1789 0 : IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) &&
1790 0 : IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B]);
1791 :
1792 0 : if (colorChannelsBecomeBlack) {
1793 0 : input = FilterProcessing::ExtractAlpha(input);
1794 : }
1795 : }
1796 :
1797 0 : SurfaceFormat format = input->GetFormat();
1798 0 : if (format == SurfaceFormat::A8 && mDisableA) {
1799 0 : return input.forget();
1800 : }
1801 :
1802 : RefPtr<DataSourceSurface> target =
1803 0 : Factory::CreateDataSourceSurface(aRect.Size(), format);
1804 0 : if (MOZ2D_WARN_IF(!target)) {
1805 0 : return nullptr;
1806 : }
1807 :
1808 0 : if (format == SurfaceFormat::A8) {
1809 0 : TransferComponents<1>(input, target, &lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_A]);
1810 : } else {
1811 0 : TransferComponents<4>(input, target, lookupTables);
1812 : }
1813 :
1814 0 : return target.forget();
1815 : }
1816 :
1817 : void
1818 0 : FilterNodeComponentTransferSoftware::RequestFromInputsForRect(const IntRect &aRect)
1819 : {
1820 0 : RequestInputRect(IN_TRANSFER_IN, aRect);
1821 0 : }
1822 :
1823 : IntRect
1824 0 : FilterNodeComponentTransferSoftware::GetOutputRectInRect(const IntRect& aRect)
1825 : {
1826 0 : if (mDisableA) {
1827 0 : return GetInputRectInRect(IN_TRANSFER_IN, aRect);
1828 : }
1829 0 : return aRect;
1830 : }
1831 :
1832 : int32_t
1833 0 : FilterNodeComponentTransferSoftware::InputIndex(uint32_t aInputEnumIndex)
1834 : {
1835 0 : switch (aInputEnumIndex) {
1836 0 : case IN_TRANSFER_IN: return 0;
1837 0 : default: return -1;
1838 : }
1839 : }
1840 :
1841 : void
1842 0 : FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex,
1843 : const Float* aFloat,
1844 : uint32_t aSize)
1845 : {
1846 0 : std::vector<Float> table(aFloat, aFloat + aSize);
1847 0 : switch (aIndex) {
1848 : case ATT_TABLE_TRANSFER_TABLE_R:
1849 0 : mTableR = table;
1850 0 : break;
1851 : case ATT_TABLE_TRANSFER_TABLE_G:
1852 0 : mTableG = table;
1853 0 : break;
1854 : case ATT_TABLE_TRANSFER_TABLE_B:
1855 0 : mTableB = table;
1856 0 : break;
1857 : case ATT_TABLE_TRANSFER_TABLE_A:
1858 0 : mTableA = table;
1859 0 : break;
1860 : default:
1861 0 : MOZ_CRASH("GFX: FilterNodeTableTransferSoftware::SetAttribute");
1862 : }
1863 0 : Invalidate();
1864 0 : }
1865 :
1866 : void
1867 0 : FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
1868 : uint8_t aTable[256])
1869 : {
1870 0 : switch (aComponent) {
1871 : case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
1872 0 : FillLookupTableImpl(mTableR, aTable);
1873 0 : break;
1874 : case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
1875 0 : FillLookupTableImpl(mTableG, aTable);
1876 0 : break;
1877 : case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
1878 0 : FillLookupTableImpl(mTableB, aTable);
1879 0 : break;
1880 : case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
1881 0 : FillLookupTableImpl(mTableA, aTable);
1882 0 : break;
1883 : default:
1884 0 : MOZ_ASSERT(false, "unknown component");
1885 : break;
1886 : }
1887 0 : }
1888 :
1889 : void
1890 0 : FilterNodeTableTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues,
1891 : uint8_t aTable[256])
1892 : {
1893 0 : uint32_t tvLength = aTableValues.size();
1894 0 : if (tvLength < 2) {
1895 0 : return;
1896 : }
1897 :
1898 0 : for (size_t i = 0; i < 256; i++) {
1899 0 : uint32_t k = (i * (tvLength - 1)) / 255;
1900 0 : Float v1 = aTableValues[k];
1901 0 : Float v2 = aTableValues[std::min(k + 1, tvLength - 1)];
1902 : int32_t val =
1903 0 : int32_t(255 * (v1 + (i/255.0f - k/float(tvLength-1))*(tvLength - 1)*(v2 - v1)));
1904 0 : val = std::min(255, val);
1905 0 : val = std::max(0, val);
1906 0 : aTable[i] = val;
1907 : }
1908 : }
1909 :
1910 : void
1911 0 : FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex,
1912 : const Float* aFloat,
1913 : uint32_t aSize)
1914 : {
1915 0 : std::vector<Float> discrete(aFloat, aFloat + aSize);
1916 0 : switch (aIndex) {
1917 : case ATT_DISCRETE_TRANSFER_TABLE_R:
1918 0 : mTableR = discrete;
1919 0 : break;
1920 : case ATT_DISCRETE_TRANSFER_TABLE_G:
1921 0 : mTableG = discrete;
1922 0 : break;
1923 : case ATT_DISCRETE_TRANSFER_TABLE_B:
1924 0 : mTableB = discrete;
1925 0 : break;
1926 : case ATT_DISCRETE_TRANSFER_TABLE_A:
1927 0 : mTableA = discrete;
1928 0 : break;
1929 : default:
1930 0 : MOZ_CRASH("GFX: FilterNodeDiscreteTransferSoftware::SetAttribute");
1931 : }
1932 0 : Invalidate();
1933 0 : }
1934 :
1935 : void
1936 0 : FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
1937 : uint8_t aTable[256])
1938 : {
1939 0 : switch (aComponent) {
1940 : case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
1941 0 : FillLookupTableImpl(mTableR, aTable);
1942 0 : break;
1943 : case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
1944 0 : FillLookupTableImpl(mTableG, aTable);
1945 0 : break;
1946 : case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
1947 0 : FillLookupTableImpl(mTableB, aTable);
1948 0 : break;
1949 : case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
1950 0 : FillLookupTableImpl(mTableA, aTable);
1951 0 : break;
1952 : default:
1953 0 : MOZ_ASSERT(false, "unknown component");
1954 : break;
1955 : }
1956 0 : }
1957 :
1958 : void
1959 0 : FilterNodeDiscreteTransferSoftware::FillLookupTableImpl(std::vector<Float>& aTableValues,
1960 : uint8_t aTable[256])
1961 : {
1962 0 : uint32_t tvLength = aTableValues.size();
1963 0 : if (tvLength < 1) {
1964 0 : return;
1965 : }
1966 :
1967 0 : for (size_t i = 0; i < 256; i++) {
1968 0 : uint32_t k = (i * tvLength) / 255;
1969 0 : k = std::min(k, tvLength - 1);
1970 0 : Float v = aTableValues[k];
1971 0 : int32_t val = NS_lround(255 * v);
1972 0 : val = std::min(255, val);
1973 0 : val = std::max(0, val);
1974 0 : aTable[i] = val;
1975 : }
1976 : }
1977 :
1978 0 : FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware()
1979 : : mSlopeR(0)
1980 : , mSlopeG(0)
1981 : , mSlopeB(0)
1982 : , mSlopeA(0)
1983 : , mInterceptR(0)
1984 : , mInterceptG(0)
1985 : , mInterceptB(0)
1986 0 : , mInterceptA(0)
1987 0 : {}
1988 :
1989 : void
1990 0 : FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex,
1991 : Float aValue)
1992 : {
1993 0 : switch (aIndex) {
1994 : case ATT_LINEAR_TRANSFER_SLOPE_R:
1995 0 : mSlopeR = aValue;
1996 0 : break;
1997 : case ATT_LINEAR_TRANSFER_INTERCEPT_R:
1998 0 : mInterceptR = aValue;
1999 0 : break;
2000 : case ATT_LINEAR_TRANSFER_SLOPE_G:
2001 0 : mSlopeG = aValue;
2002 0 : break;
2003 : case ATT_LINEAR_TRANSFER_INTERCEPT_G:
2004 0 : mInterceptG = aValue;
2005 0 : break;
2006 : case ATT_LINEAR_TRANSFER_SLOPE_B:
2007 0 : mSlopeB = aValue;
2008 0 : break;
2009 : case ATT_LINEAR_TRANSFER_INTERCEPT_B:
2010 0 : mInterceptB = aValue;
2011 0 : break;
2012 : case ATT_LINEAR_TRANSFER_SLOPE_A:
2013 0 : mSlopeA = aValue;
2014 0 : break;
2015 : case ATT_LINEAR_TRANSFER_INTERCEPT_A:
2016 0 : mInterceptA = aValue;
2017 0 : break;
2018 : default:
2019 0 : MOZ_CRASH("GFX: FilterNodeLinearTransferSoftware::SetAttribute");
2020 : }
2021 0 : Invalidate();
2022 0 : }
2023 :
2024 : void
2025 0 : FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
2026 : uint8_t aTable[256])
2027 : {
2028 0 : switch (aComponent) {
2029 : case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
2030 0 : FillLookupTableImpl(mSlopeR, mInterceptR, aTable);
2031 0 : break;
2032 : case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
2033 0 : FillLookupTableImpl(mSlopeG, mInterceptG, aTable);
2034 0 : break;
2035 : case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
2036 0 : FillLookupTableImpl(mSlopeB, mInterceptB, aTable);
2037 0 : break;
2038 : case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
2039 0 : FillLookupTableImpl(mSlopeA, mInterceptA, aTable);
2040 0 : break;
2041 : default:
2042 0 : MOZ_ASSERT(false, "unknown component");
2043 : break;
2044 : }
2045 0 : }
2046 :
2047 : void
2048 0 : FilterNodeLinearTransferSoftware::FillLookupTableImpl(Float aSlope,
2049 : Float aIntercept,
2050 : uint8_t aTable[256])
2051 : {
2052 0 : for (size_t i = 0; i < 256; i++) {
2053 0 : int32_t val = NS_lround(aSlope * i + 255 * aIntercept);
2054 0 : val = std::min(255, val);
2055 0 : val = std::max(0, val);
2056 0 : aTable[i] = val;
2057 : }
2058 0 : }
2059 :
2060 0 : FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware()
2061 : : mAmplitudeR(0)
2062 : , mAmplitudeG(0)
2063 : , mAmplitudeB(0)
2064 : , mAmplitudeA(0)
2065 : , mExponentR(0)
2066 : , mExponentG(0)
2067 : , mExponentB(0)
2068 0 : , mExponentA(0)
2069 0 : {}
2070 :
2071 : void
2072 0 : FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex,
2073 : Float aValue)
2074 : {
2075 0 : switch (aIndex) {
2076 : case ATT_GAMMA_TRANSFER_AMPLITUDE_R:
2077 0 : mAmplitudeR = aValue;
2078 0 : break;
2079 : case ATT_GAMMA_TRANSFER_EXPONENT_R:
2080 0 : mExponentR = aValue;
2081 0 : break;
2082 : case ATT_GAMMA_TRANSFER_OFFSET_R:
2083 0 : mOffsetR = aValue;
2084 0 : break;
2085 : case ATT_GAMMA_TRANSFER_AMPLITUDE_G:
2086 0 : mAmplitudeG = aValue;
2087 0 : break;
2088 : case ATT_GAMMA_TRANSFER_EXPONENT_G:
2089 0 : mExponentG = aValue;
2090 0 : break;
2091 : case ATT_GAMMA_TRANSFER_OFFSET_G:
2092 0 : mOffsetG = aValue;
2093 0 : break;
2094 : case ATT_GAMMA_TRANSFER_AMPLITUDE_B:
2095 0 : mAmplitudeB = aValue;
2096 0 : break;
2097 : case ATT_GAMMA_TRANSFER_EXPONENT_B:
2098 0 : mExponentB = aValue;
2099 0 : break;
2100 : case ATT_GAMMA_TRANSFER_OFFSET_B:
2101 0 : mOffsetB = aValue;
2102 0 : break;
2103 : case ATT_GAMMA_TRANSFER_AMPLITUDE_A:
2104 0 : mAmplitudeA = aValue;
2105 0 : break;
2106 : case ATT_GAMMA_TRANSFER_EXPONENT_A:
2107 0 : mExponentA = aValue;
2108 0 : break;
2109 : case ATT_GAMMA_TRANSFER_OFFSET_A:
2110 0 : mOffsetA = aValue;
2111 0 : break;
2112 : default:
2113 0 : MOZ_CRASH("GFX: FilterNodeGammaTransferSoftware::SetAttribute");
2114 : }
2115 0 : Invalidate();
2116 0 : }
2117 :
2118 : void
2119 0 : FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
2120 : uint8_t aTable[256])
2121 : {
2122 0 : switch (aComponent) {
2123 : case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
2124 0 : FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable);
2125 0 : break;
2126 : case B8G8R8A8_COMPONENT_BYTEOFFSET_G:
2127 0 : FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable);
2128 0 : break;
2129 : case B8G8R8A8_COMPONENT_BYTEOFFSET_B:
2130 0 : FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable);
2131 0 : break;
2132 : case B8G8R8A8_COMPONENT_BYTEOFFSET_A:
2133 0 : FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable);
2134 0 : break;
2135 : default:
2136 0 : MOZ_ASSERT(false, "unknown component");
2137 : break;
2138 : }
2139 0 : }
2140 :
2141 : void
2142 0 : FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude,
2143 : Float aExponent,
2144 : Float aOffset,
2145 : uint8_t aTable[256])
2146 : {
2147 0 : for (size_t i = 0; i < 256; i++) {
2148 0 : int32_t val = NS_lround(255 * (aAmplitude * pow(i / 255.0f, aExponent) + aOffset));
2149 0 : val = std::min(255, val);
2150 0 : val = std::max(0, val);
2151 0 : aTable[i] = val;
2152 : }
2153 0 : }
2154 :
2155 0 : FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware()
2156 : : mDivisor(0)
2157 : , mBias(0)
2158 : , mEdgeMode(EDGE_MODE_DUPLICATE)
2159 0 : , mPreserveAlpha(false)
2160 0 : {}
2161 :
2162 : int32_t
2163 0 : FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex)
2164 : {
2165 0 : switch (aInputEnumIndex) {
2166 0 : case IN_CONVOLVE_MATRIX_IN: return 0;
2167 0 : default: return -1;
2168 : }
2169 : }
2170 :
2171 : void
2172 0 : FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2173 : const IntSize &aKernelSize)
2174 : {
2175 0 : MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_SIZE);
2176 0 : mKernelSize = aKernelSize;
2177 0 : Invalidate();
2178 0 : }
2179 :
2180 : void
2181 0 : FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2182 : const Float *aMatrix,
2183 : uint32_t aSize)
2184 : {
2185 0 : MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_MATRIX);
2186 0 : mKernelMatrix = std::vector<Float>(aMatrix, aMatrix + aSize);
2187 0 : Invalidate();
2188 0 : }
2189 :
2190 : void
2191 0 : FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, Float aValue)
2192 : {
2193 0 : switch (aIndex) {
2194 : case ATT_CONVOLVE_MATRIX_DIVISOR:
2195 0 : mDivisor = aValue;
2196 0 : break;
2197 : case ATT_CONVOLVE_MATRIX_BIAS:
2198 0 : mBias = aValue;
2199 0 : break;
2200 : default:
2201 0 : MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
2202 : }
2203 0 : Invalidate();
2204 0 : }
2205 :
2206 : void
2207 0 : FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
2208 : {
2209 0 : switch (aIndex) {
2210 : case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH:
2211 0 : mKernelUnitLength = aKernelUnitLength;
2212 0 : break;
2213 : default:
2214 0 : MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
2215 : }
2216 0 : Invalidate();
2217 0 : }
2218 :
2219 : void
2220 0 : FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2221 : const IntPoint &aTarget)
2222 : {
2223 0 : MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_TARGET);
2224 0 : mTarget = aTarget;
2225 0 : Invalidate();
2226 0 : }
2227 :
2228 : void
2229 0 : FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2230 : const IntRect &aSourceRect)
2231 : {
2232 0 : MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_SOURCE_RECT);
2233 0 : mSourceRect = aSourceRect;
2234 0 : Invalidate();
2235 0 : }
2236 :
2237 : void
2238 0 : FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2239 : uint32_t aEdgeMode)
2240 : {
2241 0 : MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_EDGE_MODE);
2242 0 : mEdgeMode = static_cast<ConvolveMatrixEdgeMode>(aEdgeMode);
2243 0 : Invalidate();
2244 0 : }
2245 :
2246 : void
2247 0 : FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
2248 : bool aPreserveAlpha)
2249 : {
2250 0 : MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA);
2251 0 : mPreserveAlpha = aPreserveAlpha;
2252 0 : Invalidate();
2253 0 : }
2254 :
2255 : #ifdef DEBUG
2256 : static bool sColorSamplingAccessControlEnabled = false;
2257 : static uint8_t* sColorSamplingAccessControlStart = nullptr;
2258 : static uint8_t* sColorSamplingAccessControlEnd = nullptr;
2259 :
2260 : struct DebugOnlyAutoColorSamplingAccessControl
2261 : {
2262 0 : explicit DebugOnlyAutoColorSamplingAccessControl(DataSourceSurface* aSurface)
2263 : {
2264 0 : sColorSamplingAccessControlStart = aSurface->GetData();
2265 0 : sColorSamplingAccessControlEnd = sColorSamplingAccessControlStart +
2266 0 : aSurface->Stride() * aSurface->GetSize().height;
2267 0 : sColorSamplingAccessControlEnabled = true;
2268 0 : }
2269 :
2270 0 : ~DebugOnlyAutoColorSamplingAccessControl()
2271 : {
2272 0 : sColorSamplingAccessControlEnabled = false;
2273 0 : }
2274 : };
2275 :
2276 : static inline void
2277 0 : DebugOnlyCheckColorSamplingAccess(const uint8_t* aSampleAddress)
2278 : {
2279 0 : if (sColorSamplingAccessControlEnabled) {
2280 0 : MOZ_ASSERT(aSampleAddress >= sColorSamplingAccessControlStart, "accessing before start");
2281 0 : MOZ_ASSERT(aSampleAddress < sColorSamplingAccessControlEnd, "accessing after end");
2282 : }
2283 0 : }
2284 : #else
2285 : typedef DebugOnly<DataSourceSurface*> DebugOnlyAutoColorSamplingAccessControl;
2286 : #define DebugOnlyCheckColorSamplingAccess(address)
2287 : #endif
2288 :
2289 : static inline uint8_t
2290 0 : ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y, size_t bpp, ptrdiff_t c)
2291 : {
2292 0 : DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c]);
2293 0 : return aData[y * aStride + bpp * x + c];
2294 : }
2295 :
2296 : static inline int32_t
2297 0 : ColorAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y)
2298 : {
2299 0 : DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x);
2300 0 : return *(uint32_t*)(aData + y * aStride + 4 * x);
2301 : }
2302 :
2303 : // Accepts fractional x & y and does bilinear interpolation.
2304 : // Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible.
2305 : static inline uint8_t
2306 0 : ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, Float x, Float y, size_t bpp, ptrdiff_t c)
2307 : {
2308 0 : const uint32_t f = 256;
2309 0 : const int32_t lx = floor(x);
2310 0 : const int32_t ly = floor(y);
2311 0 : const int32_t tux = uint32_t((x - lx) * f);
2312 0 : const int32_t tlx = f - tux;
2313 0 : const int32_t tuy = uint32_t((y - ly) * f);
2314 0 : const int32_t tly = f - tuy;
2315 0 : const uint8_t &cll = ColorComponentAtPoint(aData, aStride, lx, ly, bpp, c);
2316 0 : const uint8_t &cul = ColorComponentAtPoint(aData, aStride, lx + 1, ly, bpp, c);
2317 0 : const uint8_t &clu = ColorComponentAtPoint(aData, aStride, lx, ly + 1, bpp, c);
2318 0 : const uint8_t &cuu = ColorComponentAtPoint(aData, aStride, lx + 1, ly + 1, bpp, c);
2319 0 : return ((cll * tlx + cul * tux) * tly +
2320 0 : (clu * tlx + cuu * tux) * tuy + f * f / 2) / (f * f);
2321 : }
2322 :
2323 : static int32_t
2324 0 : ClampToNonZero(int32_t a)
2325 : {
2326 0 : return a * (a >= 0);
2327 : }
2328 :
2329 : template<typename CoordType>
2330 : static void
2331 0 : ConvolvePixel(const uint8_t *aSourceData,
2332 : uint8_t *aTargetData,
2333 : int32_t aWidth, int32_t aHeight,
2334 : int32_t aSourceStride, int32_t aTargetStride,
2335 : int32_t aX, int32_t aY,
2336 : const int32_t *aKernel,
2337 : int32_t aBias, int32_t shiftL, int32_t shiftR,
2338 : bool aPreserveAlpha,
2339 : int32_t aOrderX, int32_t aOrderY,
2340 : int32_t aTargetX, int32_t aTargetY,
2341 : CoordType aKernelUnitLengthX,
2342 : CoordType aKernelUnitLengthY)
2343 : {
2344 0 : int32_t sum[4] = {0, 0, 0, 0};
2345 : int32_t offsets[4] = { B8G8R8A8_COMPONENT_BYTEOFFSET_R,
2346 : B8G8R8A8_COMPONENT_BYTEOFFSET_G,
2347 : B8G8R8A8_COMPONENT_BYTEOFFSET_B,
2348 0 : B8G8R8A8_COMPONENT_BYTEOFFSET_A };
2349 0 : int32_t channels = aPreserveAlpha ? 3 : 4;
2350 0 : int32_t roundingAddition = shiftL == 0 ? 0 : 1 << (shiftL - 1);
2351 :
2352 0 : for (int32_t y = 0; y < aOrderY; y++) {
2353 0 : CoordType sampleY = aY + (y - aTargetY) * aKernelUnitLengthY;
2354 0 : for (int32_t x = 0; x < aOrderX; x++) {
2355 0 : CoordType sampleX = aX + (x - aTargetX) * aKernelUnitLengthX;
2356 0 : for (int32_t i = 0; i < channels; i++) {
2357 0 : sum[i] += aKernel[aOrderX * y + x] *
2358 0 : ColorComponentAtPoint(aSourceData, aSourceStride,
2359 : sampleX, sampleY, 4, offsets[i]);
2360 : }
2361 : }
2362 : }
2363 0 : for (int32_t i = 0; i < channels; i++) {
2364 0 : int32_t clamped = umin(ClampToNonZero(sum[i] + aBias), 255 << shiftL >> shiftR);
2365 0 : aTargetData[aY * aTargetStride + 4 * aX + offsets[i]] =
2366 0 : (clamped + roundingAddition) << shiftR >> shiftL;
2367 : }
2368 0 : if (aPreserveAlpha) {
2369 0 : aTargetData[aY * aTargetStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
2370 0 : aSourceData[aY * aSourceStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A];
2371 : }
2372 0 : }
2373 :
2374 : already_AddRefed<DataSourceSurface>
2375 0 : FilterNodeConvolveMatrixSoftware::Render(const IntRect& aRect)
2376 : {
2377 0 : if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
2378 0 : mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
2379 0 : return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height);
2380 : }
2381 0 : return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
2382 : }
2383 :
2384 : static std::vector<Float>
2385 0 : ReversedVector(const std::vector<Float> &aVector)
2386 : {
2387 0 : size_t length = aVector.size();
2388 0 : std::vector<Float> result(length, 0);
2389 0 : for (size_t i = 0; i < length; i++) {
2390 0 : result[length - 1 - i] = aVector[i];
2391 : }
2392 0 : return result;
2393 : }
2394 :
2395 : static std::vector<Float>
2396 0 : ScaledVector(const std::vector<Float> &aVector, Float aDivisor)
2397 : {
2398 0 : size_t length = aVector.size();
2399 0 : std::vector<Float> result(length, 0);
2400 0 : for (size_t i = 0; i < length; i++) {
2401 0 : result[i] = aVector[i] / aDivisor;
2402 : }
2403 0 : return result;
2404 : }
2405 :
2406 : static Float
2407 0 : MaxVectorSum(const std::vector<Float> &aVector)
2408 : {
2409 0 : Float sum = 0;
2410 0 : size_t length = aVector.size();
2411 0 : for (size_t i = 0; i < length; i++) {
2412 0 : if (aVector[i] > 0) {
2413 0 : sum += aVector[i];
2414 : }
2415 : }
2416 0 : return sum;
2417 : }
2418 :
2419 : // Returns shiftL and shiftR in such a way that
2420 : // a << shiftL >> shiftR is roughly a * aFloat.
2421 : static void
2422 0 : TranslateDoubleToShifts(double aDouble, int32_t &aShiftL, int32_t &aShiftR)
2423 : {
2424 0 : aShiftL = 0;
2425 0 : aShiftR = 0;
2426 0 : if (aDouble <= 0) {
2427 0 : MOZ_CRASH("GFX: TranslateDoubleToShifts");
2428 : }
2429 0 : if (aDouble < 1) {
2430 0 : while (1 << (aShiftR + 1) < 1 / aDouble) {
2431 0 : aShiftR++;
2432 : }
2433 : } else {
2434 0 : while (1 << (aShiftL + 1) < aDouble) {
2435 0 : aShiftL++;
2436 : }
2437 : }
2438 0 : }
2439 :
2440 : template<typename CoordType>
2441 : already_AddRefed<DataSourceSurface>
2442 0 : FilterNodeConvolveMatrixSoftware::DoRender(const IntRect& aRect,
2443 : CoordType aKernelUnitLengthX,
2444 : CoordType aKernelUnitLengthY)
2445 : {
2446 0 : if (mKernelSize.width <= 0 || mKernelSize.height <= 0 ||
2447 0 : mKernelMatrix.size() != uint32_t(mKernelSize.width * mKernelSize.height) ||
2448 0 : !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) ||
2449 0 : mDivisor == 0) {
2450 0 : return Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true);
2451 : }
2452 :
2453 0 : IntRect srcRect = InflatedSourceRect(aRect);
2454 :
2455 : // Inflate the source rect by another pixel because the bilinear filtering in
2456 : // ColorComponentAtPoint may want to access the margins.
2457 0 : srcRect.Inflate(1);
2458 :
2459 : RefPtr<DataSourceSurface> input =
2460 0 : GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect, NEED_COLOR_CHANNELS, mEdgeMode, &mSourceRect);
2461 :
2462 0 : if (!input) {
2463 0 : return nullptr;
2464 : }
2465 :
2466 0 : DebugOnlyAutoColorSamplingAccessControl accessControl(input);
2467 :
2468 : RefPtr<DataSourceSurface> target =
2469 0 : Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true);
2470 0 : if (MOZ2D_WARN_IF(!target)) {
2471 0 : return nullptr;
2472 : }
2473 :
2474 0 : IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
2475 :
2476 0 : DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
2477 0 : DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
2478 0 : if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
2479 0 : return nullptr;
2480 : }
2481 :
2482 0 : uint8_t* sourceData = DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
2483 0 : int32_t sourceStride = sourceMap.GetStride();
2484 0 : uint8_t* targetData = targetMap.GetData();
2485 0 : int32_t targetStride = targetMap.GetStride();
2486 :
2487 : // Why exactly are we reversing the kernel?
2488 0 : std::vector<Float> kernel = ReversedVector(mKernelMatrix);
2489 0 : kernel = ScaledVector(kernel, mDivisor);
2490 0 : Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias,
2491 0 : MaxVectorSum(ScaledVector(kernel, -1)) - mBias);
2492 0 : maxResultAbs = std::max(maxResultAbs, 1.0f);
2493 :
2494 0 : double idealFactor = INT32_MAX / 2.0 / maxResultAbs / 255.0 * 0.999;
2495 0 : MOZ_ASSERT(255.0 * maxResultAbs * idealFactor <= INT32_MAX / 2.0, "badly chosen float-to-int scale");
2496 : int32_t shiftL, shiftR;
2497 0 : TranslateDoubleToShifts(idealFactor, shiftL, shiftR);
2498 0 : double factorFromShifts = Float(1 << shiftL) / Float(1 << shiftR);
2499 0 : MOZ_ASSERT(255.0 * maxResultAbs * factorFromShifts <= INT32_MAX / 2.0, "badly chosen float-to-int scale");
2500 :
2501 0 : int32_t* intKernel = new int32_t[kernel.size()];
2502 0 : for (size_t i = 0; i < kernel.size(); i++) {
2503 0 : intKernel[i] = NS_lround(kernel[i] * factorFromShifts);
2504 : }
2505 0 : int32_t bias = NS_lround(mBias * 255 * factorFromShifts);
2506 :
2507 0 : for (int32_t y = 0; y < aRect.height; y++) {
2508 0 : for (int32_t x = 0; x < aRect.width; x++) {
2509 0 : ConvolvePixel(sourceData, targetData,
2510 0 : aRect.width, aRect.height, sourceStride, targetStride,
2511 0 : x, y, intKernel, bias, shiftL, shiftR, mPreserveAlpha,
2512 : mKernelSize.width, mKernelSize.height, mTarget.x, mTarget.y,
2513 : aKernelUnitLengthX, aKernelUnitLengthY);
2514 : }
2515 : }
2516 0 : delete[] intKernel;
2517 :
2518 0 : return target.forget();
2519 : }
2520 :
2521 : void
2522 0 : FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
2523 : {
2524 0 : RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect));
2525 0 : }
2526 :
2527 : IntRect
2528 0 : FilterNodeConvolveMatrixSoftware::InflatedSourceRect(const IntRect &aDestRect)
2529 : {
2530 0 : if (aDestRect.IsEmpty()) {
2531 0 : return IntRect();
2532 : }
2533 :
2534 0 : IntMargin margin;
2535 0 : margin.left = ceil(mTarget.x * mKernelUnitLength.width);
2536 0 : margin.top = ceil(mTarget.y * mKernelUnitLength.height);
2537 0 : margin.right = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
2538 0 : margin.bottom = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
2539 :
2540 0 : IntRect srcRect = aDestRect;
2541 0 : srcRect.Inflate(margin);
2542 0 : return srcRect;
2543 : }
2544 :
2545 : IntRect
2546 0 : FilterNodeConvolveMatrixSoftware::InflatedDestRect(const IntRect &aSourceRect)
2547 : {
2548 0 : if (aSourceRect.IsEmpty()) {
2549 0 : return IntRect();
2550 : }
2551 :
2552 0 : IntMargin margin;
2553 0 : margin.left = ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width);
2554 0 : margin.top = ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height);
2555 0 : margin.right = ceil(mTarget.x * mKernelUnitLength.width);
2556 0 : margin.bottom = ceil(mTarget.y * mKernelUnitLength.height);
2557 :
2558 0 : IntRect destRect = aSourceRect;
2559 0 : destRect.Inflate(margin);
2560 0 : return destRect;
2561 : }
2562 :
2563 : IntRect
2564 0 : FilterNodeConvolveMatrixSoftware::GetOutputRectInRect(const IntRect& aRect)
2565 : {
2566 0 : IntRect srcRequest = InflatedSourceRect(aRect);
2567 0 : IntRect srcOutput = GetInputRectInRect(IN_COLOR_MATRIX_IN, srcRequest);
2568 0 : return InflatedDestRect(srcOutput).Intersect(aRect);
2569 : }
2570 :
2571 0 : FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware()
2572 : : mScale(0.0f)
2573 : , mChannelX(COLOR_CHANNEL_R)
2574 0 : , mChannelY(COLOR_CHANNEL_G)
2575 0 : {}
2576 :
2577 : int32_t
2578 0 : FilterNodeDisplacementMapSoftware::InputIndex(uint32_t aInputEnumIndex)
2579 : {
2580 0 : switch (aInputEnumIndex) {
2581 0 : case IN_DISPLACEMENT_MAP_IN: return 0;
2582 0 : case IN_DISPLACEMENT_MAP_IN2: return 1;
2583 0 : default: return -1;
2584 : }
2585 : }
2586 :
2587 : void
2588 0 : FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
2589 : Float aScale)
2590 : {
2591 0 : MOZ_ASSERT(aIndex == ATT_DISPLACEMENT_MAP_SCALE);
2592 0 : mScale = aScale;
2593 0 : Invalidate();
2594 0 : }
2595 :
2596 : void
2597 0 : FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue)
2598 : {
2599 0 : switch (aIndex) {
2600 : case ATT_DISPLACEMENT_MAP_X_CHANNEL:
2601 0 : mChannelX = static_cast<ColorChannel>(aValue);
2602 0 : break;
2603 : case ATT_DISPLACEMENT_MAP_Y_CHANNEL:
2604 0 : mChannelY = static_cast<ColorChannel>(aValue);
2605 0 : break;
2606 : default:
2607 0 : MOZ_CRASH("GFX: FilterNodeDisplacementMapSoftware::SetAttribute");
2608 : }
2609 0 : Invalidate();
2610 0 : }
2611 :
2612 : already_AddRefed<DataSourceSurface>
2613 0 : FilterNodeDisplacementMapSoftware::Render(const IntRect& aRect)
2614 : {
2615 0 : IntRect srcRect = InflatedSourceOrDestRect(aRect);
2616 : RefPtr<DataSourceSurface> input =
2617 0 : GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN, srcRect, NEED_COLOR_CHANNELS);
2618 : RefPtr<DataSourceSurface> map =
2619 0 : GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN2, aRect, NEED_COLOR_CHANNELS);
2620 : RefPtr<DataSourceSurface> target =
2621 0 : Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8);
2622 0 : if (MOZ2D_WARN_IF(!(input && map && target))) {
2623 0 : return nullptr;
2624 : }
2625 :
2626 0 : IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
2627 :
2628 0 : DataSourceSurface::ScopedMap inputMap(input, DataSourceSurface::READ);
2629 0 : DataSourceSurface::ScopedMap mapMap(map, DataSourceSurface::READ);
2630 0 : DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
2631 0 : if (MOZ2D_WARN_IF(!(inputMap.IsMapped() && mapMap.IsMapped() && targetMap.IsMapped()))) {
2632 0 : return nullptr;
2633 : }
2634 :
2635 0 : uint8_t* sourceData = DataAtOffset(input, inputMap.GetMappedSurface(), offset);
2636 0 : int32_t sourceStride = inputMap.GetStride();
2637 0 : uint8_t* mapData = mapMap.GetData();
2638 0 : int32_t mapStride = mapMap.GetStride();
2639 0 : uint8_t* targetData = targetMap.GetData();
2640 0 : int32_t targetStride = targetMap.GetStride();
2641 :
2642 : static const ptrdiff_t channelMap[4] = {
2643 : B8G8R8A8_COMPONENT_BYTEOFFSET_R,
2644 : B8G8R8A8_COMPONENT_BYTEOFFSET_G,
2645 : B8G8R8A8_COMPONENT_BYTEOFFSET_B,
2646 : B8G8R8A8_COMPONENT_BYTEOFFSET_A };
2647 0 : uint16_t xChannel = channelMap[mChannelX];
2648 0 : uint16_t yChannel = channelMap[mChannelY];
2649 :
2650 0 : float scaleOver255 = mScale / 255.0f;
2651 0 : float scaleAdjustment = -0.5f * mScale;
2652 :
2653 0 : for (int32_t y = 0; y < aRect.height; y++) {
2654 0 : for (int32_t x = 0; x < aRect.width; x++) {
2655 0 : uint32_t mapIndex = y * mapStride + 4 * x;
2656 0 : uint32_t targIndex = y * targetStride + 4 * x;
2657 0 : int32_t sourceX = x +
2658 0 : scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment;
2659 0 : int32_t sourceY = y +
2660 0 : scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment;
2661 0 : *(uint32_t*)(targetData + targIndex) =
2662 0 : ColorAtPoint(sourceData, sourceStride, sourceX, sourceY);
2663 : }
2664 :
2665 : // Keep valgrind happy.
2666 0 : PodZero(&targetData[y * targetStride + 4 * aRect.width], targetStride - 4 * aRect.width);
2667 : }
2668 :
2669 0 : return target.forget();
2670 : }
2671 :
2672 : void
2673 0 : FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(const IntRect &aRect)
2674 : {
2675 0 : RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect));
2676 0 : RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect);
2677 0 : }
2678 :
2679 : IntRect
2680 0 : FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect)
2681 : {
2682 0 : IntRect sourceOrDestRect = aDestOrSourceRect;
2683 0 : sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2));
2684 0 : return sourceOrDestRect;
2685 : }
2686 :
2687 : IntRect
2688 0 : FilterNodeDisplacementMapSoftware::GetOutputRectInRect(const IntRect& aRect)
2689 : {
2690 0 : IntRect srcRequest = InflatedSourceOrDestRect(aRect);
2691 0 : IntRect srcOutput = GetInputRectInRect(IN_DISPLACEMENT_MAP_IN, srcRequest);
2692 0 : return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
2693 : }
2694 :
2695 0 : FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware()
2696 : : mNumOctaves(0)
2697 : , mSeed(0)
2698 : , mStitchable(false)
2699 0 : , mType(TURBULENCE_TYPE_TURBULENCE)
2700 0 : {}
2701 :
2702 : int32_t
2703 0 : FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex)
2704 : {
2705 0 : return -1;
2706 : }
2707 :
2708 : void
2709 0 : FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const Size &aBaseFrequency)
2710 : {
2711 0 : switch (aIndex) {
2712 : case ATT_TURBULENCE_BASE_FREQUENCY:
2713 0 : mBaseFrequency = aBaseFrequency;
2714 0 : break;
2715 : default:
2716 0 : MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2717 : break;
2718 : }
2719 0 : Invalidate();
2720 0 : }
2721 :
2722 : void
2723 0 : FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const IntRect &aRect)
2724 : {
2725 0 : switch (aIndex) {
2726 : case ATT_TURBULENCE_RECT:
2727 0 : mRenderRect = aRect;
2728 0 : break;
2729 : default:
2730 0 : MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2731 : break;
2732 : }
2733 0 : Invalidate();
2734 0 : }
2735 :
2736 : void
2737 0 : FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, bool aStitchable)
2738 : {
2739 0 : MOZ_ASSERT(aIndex == ATT_TURBULENCE_STITCHABLE);
2740 0 : mStitchable = aStitchable;
2741 0 : Invalidate();
2742 0 : }
2743 :
2744 : void
2745 0 : FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue)
2746 : {
2747 0 : switch (aIndex) {
2748 : case ATT_TURBULENCE_NUM_OCTAVES:
2749 0 : mNumOctaves = aValue;
2750 0 : break;
2751 : case ATT_TURBULENCE_SEED:
2752 0 : mSeed = aValue;
2753 0 : break;
2754 : case ATT_TURBULENCE_TYPE:
2755 0 : mType = static_cast<TurbulenceType>(aValue);
2756 0 : break;
2757 : default:
2758 0 : MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
2759 : break;
2760 : }
2761 0 : Invalidate();
2762 0 : }
2763 :
2764 : already_AddRefed<DataSourceSurface>
2765 0 : FilterNodeTurbulenceSoftware::Render(const IntRect& aRect)
2766 : {
2767 : return FilterProcessing::RenderTurbulence(
2768 0 : aRect.Size(), aRect.TopLeft(), mBaseFrequency,
2769 0 : mSeed, mNumOctaves, mType, mStitchable, Rect(mRenderRect));
2770 : }
2771 :
2772 : IntRect
2773 0 : FilterNodeTurbulenceSoftware::GetOutputRectInRect(const IntRect& aRect)
2774 : {
2775 0 : return aRect.Intersect(mRenderRect);
2776 : }
2777 :
2778 0 : FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware()
2779 0 : : mK1(0), mK2(0), mK3(0), mK4(0)
2780 : {
2781 0 : }
2782 :
2783 : int32_t
2784 0 : FilterNodeArithmeticCombineSoftware::InputIndex(uint32_t aInputEnumIndex)
2785 : {
2786 0 : switch (aInputEnumIndex) {
2787 0 : case IN_ARITHMETIC_COMBINE_IN: return 0;
2788 0 : case IN_ARITHMETIC_COMBINE_IN2: return 1;
2789 0 : default: return -1;
2790 : }
2791 : }
2792 :
2793 : void
2794 0 : FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex,
2795 : const Float* aFloat,
2796 : uint32_t aSize)
2797 : {
2798 0 : MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS);
2799 0 : MOZ_ASSERT(aSize == 4);
2800 :
2801 0 : mK1 = aFloat[0];
2802 0 : mK2 = aFloat[1];
2803 0 : mK3 = aFloat[2];
2804 0 : mK4 = aFloat[3];
2805 :
2806 0 : Invalidate();
2807 0 : }
2808 :
2809 : already_AddRefed<DataSourceSurface>
2810 0 : FilterNodeArithmeticCombineSoftware::Render(const IntRect& aRect)
2811 : {
2812 : RefPtr<DataSourceSurface> input1 =
2813 0 : GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS);
2814 : RefPtr<DataSourceSurface> input2 =
2815 0 : GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN2, aRect, NEED_COLOR_CHANNELS);
2816 0 : if (!input1 && !input2) {
2817 0 : return nullptr;
2818 : }
2819 :
2820 : // If one input is null, treat it as transparent by adjusting the factors.
2821 0 : Float k1 = mK1, k2 = mK2, k3 = mK3, k4 = mK4;
2822 0 : if (!input1) {
2823 0 : k1 = 0.0f;
2824 0 : k2 = 0.0f;
2825 0 : input1 = input2;
2826 : }
2827 :
2828 0 : if (!input2) {
2829 0 : k1 = 0.0f;
2830 0 : k3 = 0.0f;
2831 0 : input2 = input1;
2832 : }
2833 :
2834 0 : return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3, k4);
2835 : }
2836 :
2837 : void
2838 0 : FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(const IntRect &aRect)
2839 : {
2840 0 : RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect);
2841 0 : RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect);
2842 0 : }
2843 :
2844 : IntRect
2845 0 : FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(const IntRect& aRect)
2846 : {
2847 0 : if (mK4 > 0.0f) {
2848 0 : return aRect;
2849 : }
2850 0 : IntRect rectFrom1 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect);
2851 0 : IntRect rectFrom2 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2, aRect).Intersect(aRect);
2852 0 : IntRect result;
2853 0 : if (mK1 > 0.0f) {
2854 0 : result = rectFrom1.Intersect(rectFrom2);
2855 : }
2856 0 : if (mK2 > 0.0f) {
2857 0 : result = result.Union(rectFrom1);
2858 : }
2859 0 : if (mK3 > 0.0f) {
2860 0 : result = result.Union(rectFrom2);
2861 : }
2862 0 : return result;
2863 : }
2864 :
2865 0 : FilterNodeCompositeSoftware::FilterNodeCompositeSoftware()
2866 0 : : mOperator(COMPOSITE_OPERATOR_OVER)
2867 0 : {}
2868 :
2869 : int32_t
2870 0 : FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex)
2871 : {
2872 0 : return aInputEnumIndex - IN_COMPOSITE_IN_START;
2873 : }
2874 :
2875 : void
2876 0 : FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex, uint32_t aCompositeOperator)
2877 : {
2878 0 : MOZ_ASSERT(aIndex == ATT_COMPOSITE_OPERATOR);
2879 0 : mOperator = static_cast<CompositeOperator>(aCompositeOperator);
2880 0 : Invalidate();
2881 0 : }
2882 :
2883 : already_AddRefed<DataSourceSurface>
2884 0 : FilterNodeCompositeSoftware::Render(const IntRect& aRect)
2885 : {
2886 : RefPtr<DataSourceSurface> start =
2887 0 : GetInputDataSourceSurface(IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS);
2888 : RefPtr<DataSourceSurface> dest =
2889 0 : Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true);
2890 0 : if (MOZ2D_WARN_IF(!dest)) {
2891 0 : return nullptr;
2892 : }
2893 :
2894 0 : if (start) {
2895 0 : CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint());
2896 : }
2897 :
2898 0 : for (size_t inputIndex = 1; inputIndex < NumberOfSetInputs(); inputIndex++) {
2899 : RefPtr<DataSourceSurface> input =
2900 0 : GetInputDataSourceSurface(IN_COMPOSITE_IN_START + inputIndex, aRect, NEED_COLOR_CHANNELS);
2901 0 : if (input) {
2902 0 : FilterProcessing::ApplyComposition(input, dest, mOperator);
2903 : } else {
2904 : // We need to treat input as transparent. Depending on the composite
2905 : // operator, different things happen to dest.
2906 0 : switch (mOperator) {
2907 : case COMPOSITE_OPERATOR_OVER:
2908 : case COMPOSITE_OPERATOR_ATOP:
2909 : case COMPOSITE_OPERATOR_XOR:
2910 : // dest is unchanged.
2911 0 : break;
2912 : case COMPOSITE_OPERATOR_OUT:
2913 : // dest is now transparent, but it can become non-transparent again
2914 : // when compositing additional inputs.
2915 0 : ClearDataSourceSurface(dest);
2916 0 : break;
2917 : case COMPOSITE_OPERATOR_IN:
2918 : // Transparency always wins. We're completely transparent now and
2919 : // no additional input can get rid of that transparency.
2920 0 : return nullptr;
2921 : }
2922 : }
2923 : }
2924 0 : return dest.forget();
2925 : }
2926 :
2927 : void
2928 0 : FilterNodeCompositeSoftware::RequestFromInputsForRect(const IntRect &aRect)
2929 : {
2930 0 : for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
2931 0 : RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
2932 : }
2933 0 : }
2934 :
2935 : IntRect
2936 0 : FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect& aRect)
2937 : {
2938 0 : IntRect rect;
2939 0 : for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
2940 0 : IntRect inputRect = GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
2941 0 : if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) {
2942 0 : rect = rect.Intersect(inputRect);
2943 : } else {
2944 0 : rect = rect.Union(inputRect);
2945 : }
2946 : }
2947 0 : return rect;
2948 : }
2949 :
2950 : int32_t
2951 0 : FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex)
2952 : {
2953 0 : switch (aInputEnumIndex) {
2954 0 : case IN_GAUSSIAN_BLUR_IN: return 0;
2955 0 : default: return -1;
2956 : }
2957 : }
2958 :
2959 : already_AddRefed<DataSourceSurface>
2960 0 : FilterNodeBlurXYSoftware::Render(const IntRect& aRect)
2961 : {
2962 0 : Size sigmaXY = StdDeviationXY();
2963 0 : IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
2964 :
2965 0 : if (d.width == 0 && d.height == 0) {
2966 0 : return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, aRect);
2967 : }
2968 :
2969 0 : IntRect srcRect = InflatedSourceOrDestRect(aRect);
2970 : RefPtr<DataSourceSurface> input =
2971 0 : GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, srcRect);
2972 0 : if (!input) {
2973 0 : return nullptr;
2974 : }
2975 :
2976 0 : RefPtr<DataSourceSurface> target;
2977 0 : Rect r(0, 0, srcRect.width, srcRect.height);
2978 :
2979 0 : if (input->GetFormat() == SurfaceFormat::A8) {
2980 0 : target = Factory::CreateDataSourceSurface(srcRect.Size(), SurfaceFormat::A8);
2981 0 : if (MOZ2D_WARN_IF(!target)) {
2982 0 : return nullptr;
2983 : }
2984 0 : CopyRect(input, target, IntRect(IntPoint(), input->GetSize()), IntPoint());
2985 :
2986 0 : DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::READ_WRITE);
2987 0 : if (MOZ2D_WARN_IF(!targetMap.IsMapped())) {
2988 0 : return nullptr;
2989 : }
2990 0 : AlphaBoxBlur blur(r, targetMap.GetStride(), sigmaXY.width, sigmaXY.height);
2991 0 : blur.Blur(targetMap.GetData());
2992 : } else {
2993 0 : RefPtr<DataSourceSurface> channel0, channel1, channel2, channel3;
2994 0 : FilterProcessing::SeparateColorChannels(input, channel0, channel1, channel2, channel3);
2995 0 : if (MOZ2D_WARN_IF(!(channel0 && channel1 && channel2 && channel3))) {
2996 0 : return nullptr;
2997 : }
2998 : {
2999 0 : DataSourceSurface::ScopedMap channel0Map(channel0, DataSourceSurface::READ_WRITE);
3000 0 : DataSourceSurface::ScopedMap channel1Map(channel1, DataSourceSurface::READ_WRITE);
3001 0 : DataSourceSurface::ScopedMap channel2Map(channel2, DataSourceSurface::READ_WRITE);
3002 0 : DataSourceSurface::ScopedMap channel3Map(channel3, DataSourceSurface::READ_WRITE);
3003 0 : if (MOZ2D_WARN_IF(!(channel0Map.IsMapped() && channel1Map.IsMapped() &&
3004 : channel2Map.IsMapped() && channel3Map.IsMapped()))) {
3005 0 : return nullptr;
3006 : }
3007 :
3008 0 : AlphaBoxBlur blur(r, channel0Map.GetStride(), sigmaXY.width, sigmaXY.height);
3009 0 : blur.Blur(channel0Map.GetData());
3010 0 : blur.Blur(channel1Map.GetData());
3011 0 : blur.Blur(channel2Map.GetData());
3012 0 : blur.Blur(channel3Map.GetData());
3013 : }
3014 0 : target = FilterProcessing::CombineColorChannels(channel0, channel1, channel2, channel3);
3015 : }
3016 :
3017 0 : return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE);
3018 : }
3019 :
3020 : void
3021 0 : FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect &aRect)
3022 : {
3023 0 : RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect));
3024 0 : }
3025 :
3026 : IntRect
3027 0 : FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(const IntRect &aDestRect)
3028 : {
3029 0 : Size sigmaXY = StdDeviationXY();
3030 0 : IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
3031 0 : IntRect srcRect = aDestRect;
3032 0 : srcRect.Inflate(d);
3033 0 : return srcRect;
3034 : }
3035 :
3036 : IntRect
3037 0 : FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect& aRect)
3038 : {
3039 0 : IntRect srcRequest = InflatedSourceOrDestRect(aRect);
3040 0 : IntRect srcOutput = GetInputRectInRect(IN_GAUSSIAN_BLUR_IN, srcRequest);
3041 0 : return InflatedSourceOrDestRect(srcOutput).Intersect(aRect);
3042 : }
3043 :
3044 0 : FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware()
3045 0 : : mStdDeviation(0)
3046 0 : {}
3047 :
3048 : static float
3049 0 : ClampStdDeviation(float aStdDeviation)
3050 : {
3051 : // Cap software blur radius for performance reasons.
3052 0 : return std::min(std::max(0.0f, aStdDeviation), 100.0f);
3053 : }
3054 :
3055 : void
3056 0 : FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex,
3057 : float aStdDeviation)
3058 : {
3059 0 : switch (aIndex) {
3060 : case ATT_GAUSSIAN_BLUR_STD_DEVIATION:
3061 0 : mStdDeviation = ClampStdDeviation(aStdDeviation);
3062 0 : break;
3063 : default:
3064 0 : MOZ_CRASH("GFX: FilterNodeGaussianBlurSoftware::SetAttribute");
3065 : }
3066 0 : Invalidate();
3067 0 : }
3068 :
3069 : Size
3070 0 : FilterNodeGaussianBlurSoftware::StdDeviationXY()
3071 : {
3072 0 : return Size(mStdDeviation, mStdDeviation);
3073 : }
3074 :
3075 0 : FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware()
3076 0 : : mBlurDirection(BLUR_DIRECTION_X)
3077 0 : {}
3078 :
3079 : void
3080 0 : FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
3081 : Float aStdDeviation)
3082 : {
3083 0 : switch (aIndex) {
3084 : case ATT_DIRECTIONAL_BLUR_STD_DEVIATION:
3085 0 : mStdDeviation = ClampStdDeviation(aStdDeviation);
3086 0 : break;
3087 : default:
3088 0 : MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
3089 : }
3090 0 : Invalidate();
3091 0 : }
3092 :
3093 : void
3094 0 : FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
3095 : uint32_t aBlurDirection)
3096 : {
3097 0 : switch (aIndex) {
3098 : case ATT_DIRECTIONAL_BLUR_DIRECTION:
3099 0 : mBlurDirection = (BlurDirection)aBlurDirection;
3100 0 : break;
3101 : default:
3102 0 : MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
3103 : }
3104 0 : Invalidate();
3105 0 : }
3106 :
3107 : Size
3108 0 : FilterNodeDirectionalBlurSoftware::StdDeviationXY()
3109 : {
3110 0 : float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0;
3111 0 : float sigmaY = mBlurDirection == BLUR_DIRECTION_Y ? mStdDeviation : 0;
3112 0 : return Size(sigmaX, sigmaY);
3113 : }
3114 :
3115 : int32_t
3116 0 : FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex)
3117 : {
3118 0 : switch (aInputEnumIndex) {
3119 0 : case IN_CROP_IN: return 0;
3120 0 : default: return -1;
3121 : }
3122 : }
3123 :
3124 : void
3125 0 : FilterNodeCropSoftware::SetAttribute(uint32_t aIndex,
3126 : const Rect &aSourceRect)
3127 : {
3128 0 : MOZ_ASSERT(aIndex == ATT_CROP_RECT);
3129 0 : Rect srcRect = aSourceRect;
3130 0 : srcRect.Round();
3131 0 : if (!srcRect.ToIntRect(&mCropRect)) {
3132 0 : mCropRect = IntRect();
3133 : }
3134 0 : Invalidate();
3135 0 : }
3136 :
3137 : already_AddRefed<DataSourceSurface>
3138 0 : FilterNodeCropSoftware::Render(const IntRect& aRect)
3139 : {
3140 0 : return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect));
3141 : }
3142 :
3143 : void
3144 0 : FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect &aRect)
3145 : {
3146 0 : RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect));
3147 0 : }
3148 :
3149 : IntRect
3150 0 : FilterNodeCropSoftware::GetOutputRectInRect(const IntRect& aRect)
3151 : {
3152 0 : return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect);
3153 : }
3154 :
3155 : int32_t
3156 0 : FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex)
3157 : {
3158 0 : switch (aInputEnumIndex) {
3159 0 : case IN_PREMULTIPLY_IN: return 0;
3160 0 : default: return -1;
3161 : }
3162 : }
3163 :
3164 : already_AddRefed<DataSourceSurface>
3165 0 : FilterNodePremultiplySoftware::Render(const IntRect& aRect)
3166 : {
3167 : RefPtr<DataSourceSurface> input =
3168 0 : GetInputDataSourceSurface(IN_PREMULTIPLY_IN, aRect);
3169 0 : return input ? Premultiply(input) : nullptr;
3170 : }
3171 :
3172 : void
3173 0 : FilterNodePremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect)
3174 : {
3175 0 : RequestInputRect(IN_PREMULTIPLY_IN, aRect);
3176 0 : }
3177 :
3178 : IntRect
3179 0 : FilterNodePremultiplySoftware::GetOutputRectInRect(const IntRect& aRect)
3180 : {
3181 0 : return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect);
3182 : }
3183 :
3184 : int32_t
3185 0 : FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex)
3186 : {
3187 0 : switch (aInputEnumIndex) {
3188 0 : case IN_UNPREMULTIPLY_IN: return 0;
3189 0 : default: return -1;
3190 : }
3191 : }
3192 :
3193 : already_AddRefed<DataSourceSurface>
3194 0 : FilterNodeUnpremultiplySoftware::Render(const IntRect& aRect)
3195 : {
3196 : RefPtr<DataSourceSurface> input =
3197 0 : GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN, aRect);
3198 0 : return input ? Unpremultiply(input) : nullptr;
3199 : }
3200 :
3201 : void
3202 0 : FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect)
3203 : {
3204 0 : RequestInputRect(IN_UNPREMULTIPLY_IN, aRect);
3205 0 : }
3206 :
3207 : IntRect
3208 0 : FilterNodeUnpremultiplySoftware::GetOutputRectInRect(const IntRect& aRect)
3209 : {
3210 0 : return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect);
3211 : }
3212 :
3213 : bool
3214 0 : PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
3215 : {
3216 0 : switch (aIndex) {
3217 : case ATT_POINT_LIGHT_POSITION:
3218 0 : mPosition = aPoint;
3219 0 : break;
3220 : default:
3221 0 : return false;
3222 : }
3223 0 : return true;
3224 : }
3225 :
3226 0 : SpotLightSoftware::SpotLightSoftware()
3227 : : mSpecularFocus(0)
3228 : , mLimitingConeAngle(0)
3229 0 : , mLimitingConeCos(1)
3230 : {
3231 0 : }
3232 :
3233 : bool
3234 0 : SpotLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
3235 : {
3236 0 : switch (aIndex) {
3237 : case ATT_SPOT_LIGHT_POSITION:
3238 0 : mPosition = aPoint;
3239 0 : break;
3240 : case ATT_SPOT_LIGHT_POINTS_AT:
3241 0 : mPointsAt = aPoint;
3242 0 : break;
3243 : default:
3244 0 : return false;
3245 : }
3246 0 : return true;
3247 : }
3248 :
3249 : bool
3250 0 : SpotLightSoftware::SetAttribute(uint32_t aIndex, Float aValue)
3251 : {
3252 0 : switch (aIndex) {
3253 : case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE:
3254 0 : mLimitingConeAngle = aValue;
3255 0 : break;
3256 : case ATT_SPOT_LIGHT_FOCUS:
3257 0 : mSpecularFocus = aValue;
3258 0 : break;
3259 : default:
3260 0 : return false;
3261 : }
3262 0 : return true;
3263 : }
3264 :
3265 0 : DistantLightSoftware::DistantLightSoftware()
3266 : : mAzimuth(0)
3267 0 : , mElevation(0)
3268 : {
3269 0 : }
3270 :
3271 : bool
3272 0 : DistantLightSoftware::SetAttribute(uint32_t aIndex, Float aValue)
3273 : {
3274 0 : switch (aIndex) {
3275 : case ATT_DISTANT_LIGHT_AZIMUTH:
3276 0 : mAzimuth = aValue;
3277 0 : break;
3278 : case ATT_DISTANT_LIGHT_ELEVATION:
3279 0 : mElevation = aValue;
3280 0 : break;
3281 : default:
3282 0 : return false;
3283 : }
3284 0 : return true;
3285 : }
3286 :
3287 0 : static inline Point3D Normalized(const Point3D &vec) {
3288 0 : Point3D copy(vec);
3289 0 : copy.Normalize();
3290 0 : return copy;
3291 : }
3292 :
3293 : template<typename LightType, typename LightingType>
3294 0 : FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware(const char* aTypeName)
3295 : : mSurfaceScale(0)
3296 : #if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
3297 0 : , mTypeName(aTypeName)
3298 : #endif
3299 0 : {}
3300 :
3301 : template<typename LightType, typename LightingType>
3302 : int32_t
3303 0 : FilterNodeLightingSoftware<LightType, LightingType>::InputIndex(uint32_t aInputEnumIndex)
3304 : {
3305 0 : switch (aInputEnumIndex) {
3306 0 : case IN_LIGHTING_IN: return 0;
3307 0 : default: return -1;
3308 : }
3309 : }
3310 :
3311 : template<typename LightType, typename LightingType>
3312 : void
3313 0 : FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
3314 : {
3315 0 : if (mLight.SetAttribute(aIndex, aPoint)) {
3316 0 : Invalidate();
3317 0 : return;
3318 : }
3319 0 : MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute point");
3320 : }
3321 :
3322 : template<typename LightType, typename LightingType>
3323 : void
3324 0 : FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, Float aValue)
3325 : {
3326 0 : if (mLight.SetAttribute(aIndex, aValue) ||
3327 : mLighting.SetAttribute(aIndex, aValue)) {
3328 0 : Invalidate();
3329 0 : return;
3330 : }
3331 0 : switch (aIndex) {
3332 : case ATT_LIGHTING_SURFACE_SCALE:
3333 0 : mSurfaceScale = std::fpclassify(aValue) == FP_SUBNORMAL ? 0.0 : aValue;
3334 0 : break;
3335 : default:
3336 0 : MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float");
3337 : }
3338 0 : Invalidate();
3339 : }
3340 :
3341 : template<typename LightType, typename LightingType>
3342 : void
3343 0 : FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
3344 : {
3345 0 : switch (aIndex) {
3346 : case ATT_LIGHTING_KERNEL_UNIT_LENGTH:
3347 0 : mKernelUnitLength = aKernelUnitLength;
3348 0 : break;
3349 : default:
3350 0 : MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size");
3351 : }
3352 0 : Invalidate();
3353 0 : }
3354 :
3355 : template<typename LightType, typename LightingType>
3356 : void
3357 0 : FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Color &aColor)
3358 : {
3359 0 : MOZ_ASSERT(aIndex == ATT_LIGHTING_COLOR);
3360 0 : mColor = aColor;
3361 0 : Invalidate();
3362 0 : }
3363 :
3364 : template<typename LightType, typename LightingType>
3365 : IntRect
3366 0 : FilterNodeLightingSoftware<LightType, LightingType>::GetOutputRectInRect(const IntRect& aRect)
3367 : {
3368 0 : return aRect;
3369 : }
3370 :
3371 : Point3D
3372 0 : PointLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
3373 : {
3374 0 : return Normalized(mPosition - aTargetPoint);
3375 : }
3376 :
3377 : uint32_t
3378 0 : PointLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
3379 : {
3380 0 : return aLightColor;
3381 : }
3382 :
3383 : void
3384 0 : SpotLightSoftware::Prepare()
3385 : {
3386 0 : mVectorFromFocusPointToLight = Normalized(mPointsAt - mPosition);
3387 0 : mLimitingConeCos = std::max<double>(cos(mLimitingConeAngle * M_PI/180.0), 0.0);
3388 0 : mPowCache.CacheForExponent(mSpecularFocus);
3389 0 : }
3390 :
3391 : Point3D
3392 0 : SpotLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
3393 : {
3394 0 : return Normalized(mPosition - aTargetPoint);
3395 : }
3396 :
3397 : uint32_t
3398 0 : SpotLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
3399 : {
3400 : union {
3401 : uint32_t color;
3402 : uint8_t colorC[4];
3403 : };
3404 :
3405 0 : Float dot = -aVectorToLight.DotProduct(mVectorFromFocusPointToLight);
3406 0 : if (!mPowCache.HasPowerTable()) {
3407 0 : dot *= (dot >= mLimitingConeCos);
3408 0 : color = aLightColor;
3409 0 : colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] *= dot;
3410 0 : colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] *= dot;
3411 0 : colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] *= dot;
3412 : } else {
3413 0 : color = aLightColor;
3414 0 : uint16_t doti = dot * (dot >= 0) * (1 << PowCache::sInputIntPrecisionBits);
3415 0 : uint32_t tmp = mPowCache.Pow(doti) * (dot >= mLimitingConeCos);
3416 0 : MOZ_ASSERT(tmp <= (1 << PowCache::sOutputIntPrecisionBits), "pow() result must not exceed 1.0");
3417 0 : colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] * tmp) >> PowCache::sOutputIntPrecisionBits);
3418 0 : colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] * tmp) >> PowCache::sOutputIntPrecisionBits);
3419 0 : colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] * tmp) >> PowCache::sOutputIntPrecisionBits);
3420 : }
3421 0 : colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
3422 0 : return color;
3423 : }
3424 :
3425 : void
3426 0 : DistantLightSoftware::Prepare()
3427 : {
3428 0 : const double radPerDeg = M_PI / 180.0;
3429 0 : mVectorToLight.x = cos(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
3430 0 : mVectorToLight.y = sin(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg);
3431 0 : mVectorToLight.z = sin(mElevation * radPerDeg);
3432 0 : }
3433 :
3434 : Point3D
3435 0 : DistantLightSoftware::GetVectorToLight(const Point3D &aTargetPoint)
3436 : {
3437 0 : return mVectorToLight;
3438 : }
3439 :
3440 : uint32_t
3441 0 : DistantLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
3442 : {
3443 0 : return aLightColor;
3444 : }
3445 :
3446 : template<typename CoordType>
3447 : static Point3D
3448 0 : GenerateNormal(const uint8_t *data, int32_t stride,
3449 : int32_t x, int32_t y, float surfaceScale,
3450 : CoordType dx, CoordType dy)
3451 : {
3452 0 : const uint8_t *index = data + y * stride + x;
3453 :
3454 0 : CoordType zero = 0;
3455 :
3456 : // See this for source of constants:
3457 : // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
3458 : int16_t normalX =
3459 0 : -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) +
3460 0 : 1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) +
3461 0 : -2 * ColorComponentAtPoint(index, stride, -dx, zero, 1, 0) +
3462 0 : 2 * ColorComponentAtPoint(index, stride, dx, zero, 1, 0) +
3463 0 : -1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) +
3464 0 : 1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0);
3465 :
3466 : int16_t normalY =
3467 0 : -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) +
3468 0 : -2 * ColorComponentAtPoint(index, stride, zero, -dy, 1, 0) +
3469 0 : -1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) +
3470 0 : 1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) +
3471 0 : 2 * ColorComponentAtPoint(index, stride, zero, dy, 1, 0) +
3472 0 : 1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0);
3473 :
3474 0 : Point3D normal;
3475 0 : normal.x = -surfaceScale * normalX / 4.0f;
3476 0 : normal.y = -surfaceScale * normalY / 4.0f;
3477 0 : normal.z = 255;
3478 0 : return Normalized(normal);
3479 : }
3480 :
3481 : template<typename LightType, typename LightingType>
3482 : already_AddRefed<DataSourceSurface>
3483 0 : FilterNodeLightingSoftware<LightType, LightingType>::Render(const IntRect& aRect)
3484 : {
3485 0 : if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
3486 0 : mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
3487 0 : return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height);
3488 : }
3489 0 : return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
3490 : }
3491 :
3492 : template<typename LightType, typename LightingType>
3493 : void
3494 0 : FilterNodeLightingSoftware<LightType, LightingType>::RequestFromInputsForRect(const IntRect &aRect)
3495 : {
3496 0 : IntRect srcRect = aRect;
3497 0 : srcRect.Inflate(ceil(mKernelUnitLength.width),
3498 0 : ceil(mKernelUnitLength.height));
3499 0 : RequestInputRect(IN_LIGHTING_IN, srcRect);
3500 0 : }
3501 :
3502 : template<typename LightType, typename LightingType> template<typename CoordType>
3503 : already_AddRefed<DataSourceSurface>
3504 0 : FilterNodeLightingSoftware<LightType, LightingType>::DoRender(const IntRect& aRect,
3505 : CoordType aKernelUnitLengthX,
3506 : CoordType aKernelUnitLengthY)
3507 : {
3508 0 : MOZ_ASSERT(aKernelUnitLengthX > 0, "aKernelUnitLengthX can be a negative or zero value");
3509 0 : MOZ_ASSERT(aKernelUnitLengthY > 0, "aKernelUnitLengthY can be a negative or zero value");
3510 :
3511 0 : IntRect srcRect = aRect;
3512 0 : IntSize size = aRect.Size();
3513 0 : srcRect.Inflate(ceil(float(aKernelUnitLengthX)),
3514 0 : ceil(float(aKernelUnitLengthY)));
3515 :
3516 : // Inflate the source rect by another pixel because the bilinear filtering in
3517 : // ColorComponentAtPoint may want to access the margins.
3518 0 : srcRect.Inflate(1);
3519 :
3520 : RefPtr<DataSourceSurface> input =
3521 : GetInputDataSourceSurface(IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8,
3522 0 : EDGE_MODE_NONE);
3523 :
3524 0 : if (!input) {
3525 0 : return nullptr;
3526 : }
3527 :
3528 0 : if (input->GetFormat() != SurfaceFormat::A8) {
3529 0 : input = FilterProcessing::ExtractAlpha(input);
3530 : }
3531 :
3532 0 : DebugOnlyAutoColorSamplingAccessControl accessControl(input);
3533 :
3534 : RefPtr<DataSourceSurface> target =
3535 0 : Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
3536 0 : if (MOZ2D_WARN_IF(!target)) {
3537 0 : return nullptr;
3538 : }
3539 :
3540 0 : IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
3541 :
3542 :
3543 0 : DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
3544 0 : DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
3545 0 : if (MOZ2D_WARN_IF(!(sourceMap.IsMapped() && targetMap.IsMapped()))) {
3546 0 : return nullptr;
3547 : }
3548 :
3549 0 : uint8_t* sourceData = DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
3550 0 : int32_t sourceStride = sourceMap.GetStride();
3551 0 : uint8_t* targetData = targetMap.GetData();
3552 0 : int32_t targetStride = targetMap.GetStride();
3553 :
3554 0 : uint32_t lightColor = ColorToBGRA(mColor);
3555 0 : mLight.Prepare();
3556 0 : mLighting.Prepare();
3557 :
3558 0 : for (int32_t y = 0; y < size.height; y++) {
3559 0 : for (int32_t x = 0; x < size.width; x++) {
3560 0 : int32_t sourceIndex = y * sourceStride + x;
3561 0 : int32_t targetIndex = y * targetStride + 4 * x;
3562 :
3563 0 : Point3D normal = GenerateNormal(sourceData, sourceStride,
3564 : x, y, mSurfaceScale,
3565 0 : aKernelUnitLengthX, aKernelUnitLengthY);
3566 :
3567 0 : IntPoint pointInFilterSpace(aRect.x + x, aRect.y + y);
3568 0 : Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f;
3569 0 : Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z);
3570 0 : Point3D rayDir = mLight.GetVectorToLight(pt);
3571 0 : uint32_t color = mLight.GetColor(lightColor, rayDir);
3572 :
3573 0 : *(uint32_t*)(targetData + targetIndex) = mLighting.LightPixel(normal, rayDir, color);
3574 : }
3575 :
3576 : // Zero padding to keep valgrind happy.
3577 0 : PodZero(&targetData[y * targetStride + 4 * size.width], targetStride - 4 * size.width);
3578 : }
3579 :
3580 0 : return target.forget();
3581 : }
3582 :
3583 0 : DiffuseLightingSoftware::DiffuseLightingSoftware()
3584 0 : : mDiffuseConstant(0)
3585 : {
3586 0 : }
3587 :
3588 : bool
3589 0 : DiffuseLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue)
3590 : {
3591 0 : switch (aIndex) {
3592 : case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT:
3593 0 : mDiffuseConstant = aValue;
3594 0 : break;
3595 : default:
3596 0 : return false;
3597 : }
3598 0 : return true;
3599 : }
3600 :
3601 : uint32_t
3602 0 : DiffuseLightingSoftware::LightPixel(const Point3D &aNormal,
3603 : const Point3D &aVectorToLight,
3604 : uint32_t aColor)
3605 : {
3606 0 : Float dotNL = std::max(0.0f, aNormal.DotProduct(aVectorToLight));
3607 0 : Float diffuseNL = mDiffuseConstant * dotNL;
3608 :
3609 : union {
3610 : uint32_t bgra;
3611 : uint8_t components[4];
3612 0 : } color = { aColor };
3613 0 : color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
3614 0 : umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]), 255U);
3615 0 : color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
3616 0 : umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]), 255U);
3617 0 : color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
3618 0 : umin(uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]), 255U);
3619 0 : color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255;
3620 0 : return color.bgra;
3621 : }
3622 :
3623 0 : SpecularLightingSoftware::SpecularLightingSoftware()
3624 : : mSpecularConstant(0)
3625 : , mSpecularExponent(0)
3626 0 : , mSpecularConstantInt(0)
3627 : {
3628 0 : }
3629 :
3630 : bool
3631 0 : SpecularLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue)
3632 : {
3633 0 : switch (aIndex) {
3634 : case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT:
3635 0 : mSpecularConstant = std::min(std::max(aValue, 0.0f), 255.0f);
3636 0 : break;
3637 : case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT:
3638 0 : mSpecularExponent = std::min(std::max(aValue, 1.0f), 128.0f);
3639 0 : break;
3640 : default:
3641 0 : return false;
3642 : }
3643 0 : return true;
3644 : }
3645 :
3646 : void
3647 0 : SpecularLightingSoftware::Prepare()
3648 : {
3649 0 : mPowCache.CacheForExponent(mSpecularExponent);
3650 0 : mSpecularConstantInt = uint32_t(mSpecularConstant * (1 << 8));
3651 0 : }
3652 :
3653 : uint32_t
3654 0 : SpecularLightingSoftware::LightPixel(const Point3D &aNormal,
3655 : const Point3D &aVectorToLight,
3656 : uint32_t aColor)
3657 : {
3658 0 : Point3D vectorToEye(0, 0, 1);
3659 0 : Point3D halfwayVector = Normalized(aVectorToLight + vectorToEye);
3660 0 : Float dotNH = aNormal.DotProduct(halfwayVector);
3661 0 : uint16_t dotNHi = uint16_t(dotNH * (dotNH >= 0) * (1 << PowCache::sInputIntPrecisionBits));
3662 : // The exponent for specular is in [1,128] range, so we don't need to check and
3663 : // optimize for the "default power table" scenario here.
3664 0 : MOZ_ASSERT(mPowCache.HasPowerTable());
3665 0 : uint32_t specularNHi = uint32_t(mSpecularConstantInt) * mPowCache.Pow(dotNHi) >> 8;
3666 :
3667 : union {
3668 : uint32_t bgra;
3669 : uint8_t components[4];
3670 0 : } color = { aColor };
3671 0 : color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] =
3672 0 : umin(
3673 0 : (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]) >> PowCache::sOutputIntPrecisionBits, 255U);
3674 0 : color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] =
3675 0 : umin(
3676 0 : (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) >> PowCache::sOutputIntPrecisionBits, 255U);
3677 0 : color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] =
3678 0 : umin(
3679 0 : (specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) >> PowCache::sOutputIntPrecisionBits, 255U);
3680 :
3681 0 : color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] =
3682 0 : umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B],
3683 0 : umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G],
3684 0 : color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]));
3685 0 : return color.bgra;
3686 : }
3687 :
3688 : } // namespace gfx
3689 : } // namespace mozilla
|