Bumping manifests a=b2g-bump
[gecko.git] / gfx / 2d / Blur.cpp
blobedc558adda4329718988247b7ba22dce1c5b205d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "Blur.h"
9 #include <algorithm>
10 #include <math.h>
11 #include <string.h>
13 #include "mozilla/CheckedInt.h"
14 #include "mozilla/Constants.h"
16 #include "2D.h"
17 #include "DataSurfaceHelpers.h"
18 #include "Tools.h"
20 using namespace std;
22 namespace mozilla {
23 namespace gfx {
25 /**
26 * Box blur involves looking at one pixel, and setting its value to the average
27 * of its neighbouring pixels.
28 * @param aInput The input buffer.
29 * @param aOutput The output buffer.
30 * @param aLeftLobe The number of pixels to blend on the left.
31 * @param aRightLobe The number of pixels to blend on the right.
32 * @param aWidth The number of columns in the buffers.
33 * @param aRows The number of rows in the buffers.
34 * @param aSkipRect An area to skip blurring in.
35 * XXX shouldn't we pass stride in separately here?
37 static void
38 BoxBlurHorizontal(unsigned char* aInput,
39 unsigned char* aOutput,
40 int32_t aLeftLobe,
41 int32_t aRightLobe,
42 int32_t aWidth,
43 int32_t aRows,
44 const IntRect& aSkipRect)
46 MOZ_ASSERT(aWidth > 0);
48 int32_t boxSize = aLeftLobe + aRightLobe + 1;
49 bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
50 aWidth <= aSkipRect.XMost();
51 if (boxSize == 1) {
52 memcpy(aOutput, aInput, aWidth*aRows);
53 return;
55 uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
57 for (int32_t y = 0; y < aRows; y++) {
58 // Check whether the skip rect intersects this row. If the skip
59 // rect covers the whole surface in this row, we can avoid
60 // this row entirely (and any others along the skip rect).
61 bool inSkipRectY = y >= aSkipRect.y &&
62 y < aSkipRect.YMost();
63 if (inSkipRectY && skipRectCoversWholeRow) {
64 y = aSkipRect.YMost() - 1;
65 continue;
68 uint32_t alphaSum = 0;
69 for (int32_t i = 0; i < boxSize; i++) {
70 int32_t pos = i - aLeftLobe;
71 // See assertion above; if aWidth is zero, then we would have no
72 // valid position to clamp to.
73 pos = max(pos, 0);
74 pos = min(pos, aWidth - 1);
75 alphaSum += aInput[aWidth * y + pos];
77 for (int32_t x = 0; x < aWidth; x++) {
78 // Check whether we are within the skip rect. If so, go
79 // to the next point outside the skip rect.
80 if (inSkipRectY && x >= aSkipRect.x &&
81 x < aSkipRect.XMost()) {
82 x = aSkipRect.XMost();
83 if (x >= aWidth)
84 break;
86 // Recalculate the neighbouring alpha values for
87 // our new point on the surface.
88 alphaSum = 0;
89 for (int32_t i = 0; i < boxSize; i++) {
90 int32_t pos = x + i - aLeftLobe;
91 // See assertion above; if aWidth is zero, then we would have no
92 // valid position to clamp to.
93 pos = max(pos, 0);
94 pos = min(pos, aWidth - 1);
95 alphaSum += aInput[aWidth * y + pos];
98 int32_t tmp = x - aLeftLobe;
99 int32_t last = max(tmp, 0);
100 int32_t next = min(tmp + boxSize, aWidth - 1);
102 aOutput[aWidth * y + x] = (uint64_t(alphaSum) * reciprocal) >> 32;
104 alphaSum += aInput[aWidth * y + next] -
105 aInput[aWidth * y + last];
111 * Identical to BoxBlurHorizontal, except it blurs top and bottom instead of
112 * left and right.
113 * XXX shouldn't we pass stride in separately here?
115 static void
116 BoxBlurVertical(unsigned char* aInput,
117 unsigned char* aOutput,
118 int32_t aTopLobe,
119 int32_t aBottomLobe,
120 int32_t aWidth,
121 int32_t aRows,
122 const IntRect& aSkipRect)
124 MOZ_ASSERT(aRows > 0);
126 int32_t boxSize = aTopLobe + aBottomLobe + 1;
127 bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
128 aRows <= aSkipRect.YMost();
129 if (boxSize == 1) {
130 memcpy(aOutput, aInput, aWidth*aRows);
131 return;
133 uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
135 for (int32_t x = 0; x < aWidth; x++) {
136 bool inSkipRectX = x >= aSkipRect.x &&
137 x < aSkipRect.XMost();
138 if (inSkipRectX && skipRectCoversWholeColumn) {
139 x = aSkipRect.XMost() - 1;
140 continue;
143 uint32_t alphaSum = 0;
144 for (int32_t i = 0; i < boxSize; i++) {
145 int32_t pos = i - aTopLobe;
146 // See assertion above; if aRows is zero, then we would have no
147 // valid position to clamp to.
148 pos = max(pos, 0);
149 pos = min(pos, aRows - 1);
150 alphaSum += aInput[aWidth * pos + x];
152 for (int32_t y = 0; y < aRows; y++) {
153 if (inSkipRectX && y >= aSkipRect.y &&
154 y < aSkipRect.YMost()) {
155 y = aSkipRect.YMost();
156 if (y >= aRows)
157 break;
159 alphaSum = 0;
160 for (int32_t i = 0; i < boxSize; i++) {
161 int32_t pos = y + i - aTopLobe;
162 // See assertion above; if aRows is zero, then we would have no
163 // valid position to clamp to.
164 pos = max(pos, 0);
165 pos = min(pos, aRows - 1);
166 alphaSum += aInput[aWidth * pos + x];
169 int32_t tmp = y - aTopLobe;
170 int32_t last = max(tmp, 0);
171 int32_t next = min(tmp + boxSize, aRows - 1);
173 aOutput[aWidth * y + x] = (uint64_t(alphaSum) * reciprocal) >> 32;
175 alphaSum += aInput[aWidth * next + x] -
176 aInput[aWidth * last + x];
181 static void ComputeLobes(int32_t aRadius, int32_t aLobes[3][2])
183 int32_t major, minor, final;
185 /* See http://www.w3.org/TR/SVG/filters.html#feGaussianBlur for
186 * some notes about approximating the Gaussian blur with box-blurs.
187 * The comments below are in the terminology of that page.
189 int32_t z = aRadius / 3;
190 switch (aRadius % 3) {
191 case 0:
192 // aRadius = z*3; choose d = 2*z + 1
193 major = minor = final = z;
194 break;
195 case 1:
196 // aRadius = z*3 + 1
197 // This is a tricky case since there is no value of d which will
198 // yield a radius of exactly aRadius. If d is odd, i.e. d=2*k + 1
199 // for some integer k, then the radius will be 3*k. If d is even,
200 // i.e. d=2*k, then the radius will be 3*k - 1.
201 // So we have to choose values that don't match the standard
202 // algorithm.
203 major = z + 1;
204 minor = final = z;
205 break;
206 case 2:
207 // aRadius = z*3 + 2; choose d = 2*z + 2
208 major = final = z + 1;
209 minor = z;
210 break;
211 default:
212 // Mathematical impossibility!
213 MOZ_ASSERT(false);
214 major = minor = final = 0;
216 MOZ_ASSERT(major + minor + final == aRadius);
218 aLobes[0][0] = major;
219 aLobes[0][1] = minor;
220 aLobes[1][0] = minor;
221 aLobes[1][1] = major;
222 aLobes[2][0] = final;
223 aLobes[2][1] = final;
226 static void
227 SpreadHorizontal(unsigned char* aInput,
228 unsigned char* aOutput,
229 int32_t aRadius,
230 int32_t aWidth,
231 int32_t aRows,
232 int32_t aStride,
233 const IntRect& aSkipRect)
235 if (aRadius == 0) {
236 memcpy(aOutput, aInput, aStride * aRows);
237 return;
240 bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
241 aWidth <= aSkipRect.XMost();
242 for (int32_t y = 0; y < aRows; y++) {
243 // Check whether the skip rect intersects this row. If the skip
244 // rect covers the whole surface in this row, we can avoid
245 // this row entirely (and any others along the skip rect).
246 bool inSkipRectY = y >= aSkipRect.y &&
247 y < aSkipRect.YMost();
248 if (inSkipRectY && skipRectCoversWholeRow) {
249 y = aSkipRect.YMost() - 1;
250 continue;
253 for (int32_t x = 0; x < aWidth; x++) {
254 // Check whether we are within the skip rect. If so, go
255 // to the next point outside the skip rect.
256 if (inSkipRectY && x >= aSkipRect.x &&
257 x < aSkipRect.XMost()) {
258 x = aSkipRect.XMost();
259 if (x >= aWidth)
260 break;
263 int32_t sMin = max(x - aRadius, 0);
264 int32_t sMax = min(x + aRadius, aWidth - 1);
265 int32_t v = 0;
266 for (int32_t s = sMin; s <= sMax; ++s) {
267 v = max<int32_t>(v, aInput[aStride * y + s]);
269 aOutput[aStride * y + x] = v;
274 static void
275 SpreadVertical(unsigned char* aInput,
276 unsigned char* aOutput,
277 int32_t aRadius,
278 int32_t aWidth,
279 int32_t aRows,
280 int32_t aStride,
281 const IntRect& aSkipRect)
283 if (aRadius == 0) {
284 memcpy(aOutput, aInput, aStride * aRows);
285 return;
288 bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
289 aRows <= aSkipRect.YMost();
290 for (int32_t x = 0; x < aWidth; x++) {
291 bool inSkipRectX = x >= aSkipRect.x &&
292 x < aSkipRect.XMost();
293 if (inSkipRectX && skipRectCoversWholeColumn) {
294 x = aSkipRect.XMost() - 1;
295 continue;
298 for (int32_t y = 0; y < aRows; y++) {
299 // Check whether we are within the skip rect. If so, go
300 // to the next point outside the skip rect.
301 if (inSkipRectX && y >= aSkipRect.y &&
302 y < aSkipRect.YMost()) {
303 y = aSkipRect.YMost();
304 if (y >= aRows)
305 break;
308 int32_t sMin = max(y - aRadius, 0);
309 int32_t sMax = min(y + aRadius, aRows - 1);
310 int32_t v = 0;
311 for (int32_t s = sMin; s <= sMax; ++s) {
312 v = max<int32_t>(v, aInput[aStride * s + x]);
314 aOutput[aStride * y + x] = v;
319 CheckedInt<int32_t>
320 AlphaBoxBlur::RoundUpToMultipleOf4(int32_t aVal)
322 CheckedInt<int32_t> val(aVal);
324 val += 3;
325 val /= 4;
326 val *= 4;
328 return val;
331 AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
332 const IntSize& aSpreadRadius,
333 const IntSize& aBlurRadius,
334 const Rect* aDirtyRect,
335 const Rect* aSkipRect)
336 : mSpreadRadius(aSpreadRadius),
337 mBlurRadius(aBlurRadius),
338 mSurfaceAllocationSize(0)
340 Rect rect(aRect);
341 rect.Inflate(Size(aBlurRadius + aSpreadRadius));
342 rect.RoundOut();
344 if (aDirtyRect) {
345 // If we get passed a dirty rect from layout, we can minimize the
346 // shadow size and make painting faster.
347 mHasDirtyRect = true;
348 mDirtyRect = *aDirtyRect;
349 Rect requiredBlurArea = mDirtyRect.Intersect(rect);
350 requiredBlurArea.Inflate(Size(aBlurRadius + aSpreadRadius));
351 rect = requiredBlurArea.Intersect(rect);
352 } else {
353 mHasDirtyRect = false;
356 mRect = IntRect(int32_t(rect.x), int32_t(rect.y),
357 int32_t(rect.width), int32_t(rect.height));
358 if (mRect.IsEmpty()) {
359 return;
362 if (aSkipRect) {
363 // If we get passed a skip rect, we can lower the amount of
364 // blurring/spreading we need to do. We convert it to IntRect to avoid
365 // expensive int<->float conversions if we were to use Rect instead.
366 Rect skipRect = *aSkipRect;
367 skipRect.RoundIn();
368 skipRect.Deflate(Size(aBlurRadius + aSpreadRadius));
369 mSkipRect = IntRect(int32_t(skipRect.x), int32_t(skipRect.y),
370 int32_t(skipRect.width), int32_t(skipRect.height));
372 mSkipRect = mSkipRect.Intersect(mRect);
373 if (mSkipRect.IsEqualInterior(mRect))
374 return;
376 mSkipRect -= mRect.TopLeft();
377 } else {
378 mSkipRect = IntRect(0, 0, 0, 0);
381 CheckedInt<int32_t> stride = RoundUpToMultipleOf4(mRect.width);
382 if (stride.isValid()) {
383 mStride = stride.value();
385 // We need to leave room for an additional 3 bytes for a potential overrun
386 // in our blurring code.
387 size_t size = BufferSizeFromStrideAndHeight(mStride, mRect.height, 3);
388 if (size != 0) {
389 mSurfaceAllocationSize = size;
394 AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
395 int32_t aStride,
396 float aSigmaX,
397 float aSigmaY)
398 : mRect(int32_t(aRect.x), int32_t(aRect.y),
399 int32_t(aRect.width), int32_t(aRect.height)),
400 mSpreadRadius(),
401 mBlurRadius(CalculateBlurRadius(Point(aSigmaX, aSigmaY))),
402 mStride(aStride),
403 mSurfaceAllocationSize(0)
405 IntRect intRect;
406 if (aRect.ToIntRect(&intRect)) {
407 size_t minDataSize = BufferSizeFromStrideAndHeight(intRect.width, intRect.height);
408 if (minDataSize != 0) {
409 mSurfaceAllocationSize = minDataSize;
415 AlphaBoxBlur::~AlphaBoxBlur()
419 IntSize
420 AlphaBoxBlur::GetSize()
422 IntSize size(mRect.width, mRect.height);
423 return size;
426 int32_t
427 AlphaBoxBlur::GetStride()
429 return mStride;
432 IntRect
433 AlphaBoxBlur::GetRect()
435 return mRect;
438 Rect*
439 AlphaBoxBlur::GetDirtyRect()
441 if (mHasDirtyRect) {
442 return &mDirtyRect;
445 return nullptr;
448 size_t
449 AlphaBoxBlur::GetSurfaceAllocationSize() const
451 return mSurfaceAllocationSize;
454 void
455 AlphaBoxBlur::Blur(uint8_t* aData)
457 if (!aData) {
458 return;
461 // no need to do all this if not blurring or spreading
462 if (mBlurRadius != IntSize(0,0) || mSpreadRadius != IntSize(0,0)) {
463 int32_t stride = GetStride();
465 IntSize size = GetSize();
467 if (mSpreadRadius.width > 0 || mSpreadRadius.height > 0) {
468 // No need to use CheckedInt here - we have validated it in the constructor.
469 size_t szB = stride * size.height;
470 unsigned char* tmpData = new (std::nothrow) uint8_t[szB];
472 if (!tmpData) {
473 return;
476 memset(tmpData, 0, szB);
478 SpreadHorizontal(aData, tmpData, mSpreadRadius.width, GetSize().width, GetSize().height, stride, mSkipRect);
479 SpreadVertical(tmpData, aData, mSpreadRadius.height, GetSize().width, GetSize().height, stride, mSkipRect);
481 delete [] tmpData;
484 int32_t horizontalLobes[3][2];
485 ComputeLobes(mBlurRadius.width, horizontalLobes);
486 int32_t verticalLobes[3][2];
487 ComputeLobes(mBlurRadius.height, verticalLobes);
489 // We want to allow for some extra space on the left for alignment reasons.
490 int32_t maxLeftLobe = RoundUpToMultipleOf4(horizontalLobes[0][0] + 1).value();
492 IntSize integralImageSize(size.width + maxLeftLobe + horizontalLobes[1][1],
493 size.height + verticalLobes[0][0] + verticalLobes[1][1] + 1);
495 if ((integralImageSize.width * integralImageSize.height) > (1 << 24)) {
496 // Fallback to old blurring code when the surface is so large it may
497 // overflow our integral image!
499 // No need to use CheckedInt here - we have validated it in the constructor.
500 size_t szB = stride * size.height;
501 uint8_t* tmpData = new (std::nothrow) uint8_t[szB];
502 if (!tmpData) {
503 return;
506 memset(tmpData, 0, szB);
508 uint8_t* a = aData;
509 uint8_t* b = tmpData;
510 if (mBlurRadius.width > 0) {
511 BoxBlurHorizontal(a, b, horizontalLobes[0][0], horizontalLobes[0][1], stride, GetSize().height, mSkipRect);
512 BoxBlurHorizontal(b, a, horizontalLobes[1][0], horizontalLobes[1][1], stride, GetSize().height, mSkipRect);
513 BoxBlurHorizontal(a, b, horizontalLobes[2][0], horizontalLobes[2][1], stride, GetSize().height, mSkipRect);
514 } else {
515 a = tmpData;
516 b = aData;
518 // The result is in 'b' here.
519 if (mBlurRadius.height > 0) {
520 BoxBlurVertical(b, a, verticalLobes[0][0], verticalLobes[0][1], stride, GetSize().height, mSkipRect);
521 BoxBlurVertical(a, b, verticalLobes[1][0], verticalLobes[1][1], stride, GetSize().height, mSkipRect);
522 BoxBlurVertical(b, a, verticalLobes[2][0], verticalLobes[2][1], stride, GetSize().height, mSkipRect);
523 } else {
524 a = b;
526 // The result is in 'a' here.
527 if (a == tmpData) {
528 memcpy(aData, tmpData, szB);
530 delete [] tmpData;
531 } else {
532 size_t integralImageStride = GetAlignedStride<16>(integralImageSize.width * 4);
534 // We need to leave room for an additional 12 bytes for a maximum overrun
535 // of 3 pixels in the blurring code.
536 size_t bufLen = BufferSizeFromStrideAndHeight(integralImageStride, integralImageSize.height, 12);
537 if (bufLen == 0) {
538 return;
540 // bufLen is a byte count, but here we want a multiple of 32-bit ints, so
541 // we divide by 4.
542 AlignedArray<uint32_t> integralImage((bufLen / 4) + ((bufLen % 4) ? 1 : 0));
544 if (!integralImage) {
545 return;
547 #ifdef USE_SSE2
548 if (Factory::HasSSE2()) {
549 BoxBlur_SSE2(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
550 verticalLobes[0][1], integralImage, integralImageStride);
551 BoxBlur_SSE2(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
552 verticalLobes[1][1], integralImage, integralImageStride);
553 BoxBlur_SSE2(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
554 verticalLobes[2][1], integralImage, integralImageStride);
555 } else
556 #endif
558 BoxBlur_C(aData, horizontalLobes[0][0], horizontalLobes[0][1], verticalLobes[0][0],
559 verticalLobes[0][1], integralImage, integralImageStride);
560 BoxBlur_C(aData, horizontalLobes[1][0], horizontalLobes[1][1], verticalLobes[1][0],
561 verticalLobes[1][1], integralImage, integralImageStride);
562 BoxBlur_C(aData, horizontalLobes[2][0], horizontalLobes[2][1], verticalLobes[2][0],
563 verticalLobes[2][1], integralImage, integralImageStride);
569 MOZ_ALWAYS_INLINE void
570 GenerateIntegralRow(uint32_t *aDest, const uint8_t *aSource, uint32_t *aPreviousRow,
571 const uint32_t &aSourceWidth, const uint32_t &aLeftInflation, const uint32_t &aRightInflation)
573 uint32_t currentRowSum = 0;
574 uint32_t pixel = aSource[0];
575 for (uint32_t x = 0; x < aLeftInflation; x++) {
576 currentRowSum += pixel;
577 *aDest++ = currentRowSum + *aPreviousRow++;
579 for (uint32_t x = aLeftInflation; x < (aSourceWidth + aLeftInflation); x += 4) {
580 uint32_t alphaValues = *(uint32_t*)(aSource + (x - aLeftInflation));
581 #if defined WORDS_BIGENDIAN || defined IS_BIG_ENDIAN || defined __BIG_ENDIAN__
582 currentRowSum += (alphaValues >> 24) & 0xff;
583 *aDest++ = *aPreviousRow++ + currentRowSum;
584 currentRowSum += (alphaValues >> 16) & 0xff;
585 *aDest++ = *aPreviousRow++ + currentRowSum;
586 currentRowSum += (alphaValues >> 8) & 0xff;
587 *aDest++ = *aPreviousRow++ + currentRowSum;
588 currentRowSum += alphaValues & 0xff;
589 *aDest++ = *aPreviousRow++ + currentRowSum;
590 #else
591 currentRowSum += alphaValues & 0xff;
592 *aDest++ = *aPreviousRow++ + currentRowSum;
593 alphaValues >>= 8;
594 currentRowSum += alphaValues & 0xff;
595 *aDest++ = *aPreviousRow++ + currentRowSum;
596 alphaValues >>= 8;
597 currentRowSum += alphaValues & 0xff;
598 *aDest++ = *aPreviousRow++ + currentRowSum;
599 alphaValues >>= 8;
600 currentRowSum += alphaValues & 0xff;
601 *aDest++ = *aPreviousRow++ + currentRowSum;
602 #endif
604 pixel = aSource[aSourceWidth - 1];
605 for (uint32_t x = (aSourceWidth + aLeftInflation); x < (aSourceWidth + aLeftInflation + aRightInflation); x++) {
606 currentRowSum += pixel;
607 *aDest++ = currentRowSum + *aPreviousRow++;
611 MOZ_ALWAYS_INLINE void
612 GenerateIntegralImage_C(int32_t aLeftInflation, int32_t aRightInflation,
613 int32_t aTopInflation, int32_t aBottomInflation,
614 uint32_t *aIntegralImage, size_t aIntegralImageStride,
615 uint8_t *aSource, int32_t aSourceStride, const IntSize &aSize)
617 uint32_t stride32bit = aIntegralImageStride / 4;
619 IntSize integralImageSize(aSize.width + aLeftInflation + aRightInflation,
620 aSize.height + aTopInflation + aBottomInflation);
622 memset(aIntegralImage, 0, aIntegralImageStride);
624 GenerateIntegralRow(aIntegralImage, aSource, aIntegralImage,
625 aSize.width, aLeftInflation, aRightInflation);
626 for (int y = 1; y < aTopInflation + 1; y++) {
627 GenerateIntegralRow(aIntegralImage + (y * stride32bit), aSource, aIntegralImage + (y - 1) * stride32bit,
628 aSize.width, aLeftInflation, aRightInflation);
631 for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) {
632 GenerateIntegralRow(aIntegralImage + (y * stride32bit), aSource + aSourceStride * (y - aTopInflation),
633 aIntegralImage + (y - 1) * stride32bit, aSize.width, aLeftInflation, aRightInflation);
636 if (aBottomInflation) {
637 for (int y = (aSize.height + aTopInflation); y < integralImageSize.height; y++) {
638 GenerateIntegralRow(aIntegralImage + (y * stride32bit), aSource + ((aSize.height - 1) * aSourceStride),
639 aIntegralImage + (y - 1) * stride32bit,
640 aSize.width, aLeftInflation, aRightInflation);
646 * Attempt to do an in-place box blur using an integral image.
648 void
649 AlphaBoxBlur::BoxBlur_C(uint8_t* aData,
650 int32_t aLeftLobe,
651 int32_t aRightLobe,
652 int32_t aTopLobe,
653 int32_t aBottomLobe,
654 uint32_t *aIntegralImage,
655 size_t aIntegralImageStride)
657 IntSize size = GetSize();
659 MOZ_ASSERT(size.width > 0);
661 // Our 'left' or 'top' lobe will include the current pixel. i.e. when
662 // looking at an integral image the value of a pixel at 'x,y' is calculated
663 // using the value of the integral image values above/below that.
664 aLeftLobe++;
665 aTopLobe++;
666 int32_t boxSize = (aLeftLobe + aRightLobe) * (aTopLobe + aBottomLobe);
668 MOZ_ASSERT(boxSize > 0);
670 if (boxSize == 1) {
671 return;
674 int32_t stride32bit = aIntegralImageStride / 4;
676 int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value();
678 GenerateIntegralImage_C(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
679 aIntegralImage, aIntegralImageStride, aData,
680 mStride, size);
682 uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
684 uint32_t *innerIntegral = aIntegralImage + (aTopLobe * stride32bit) + leftInflation;
686 // Storing these locally makes this about 30% faster! Presumably the compiler
687 // can't be sure we're not altering the member variables in this loop.
688 IntRect skipRect = mSkipRect;
689 uint8_t *data = aData;
690 int32_t stride = mStride;
691 for (int32_t y = 0; y < size.height; y++) {
692 bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();
694 uint32_t *topLeftBase = innerIntegral + ((y - aTopLobe) * stride32bit - aLeftLobe);
695 uint32_t *topRightBase = innerIntegral + ((y - aTopLobe) * stride32bit + aRightLobe);
696 uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * stride32bit + aRightLobe);
697 uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * stride32bit - aLeftLobe);
699 for (int32_t x = 0; x < size.width; x++) {
700 if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
701 x = skipRect.XMost() - 1;
702 // Trigger early jump on coming loop iterations, this will be reset
703 // next line anyway.
704 inSkipRectY = false;
705 continue;
707 int32_t topLeft = topLeftBase[x];
708 int32_t topRight = topRightBase[x];
709 int32_t bottomRight = bottomRightBase[x];
710 int32_t bottomLeft = bottomLeftBase[x];
712 uint32_t value = bottomRight - topRight - bottomLeft;
713 value += topLeft;
715 data[stride * y + x] = (uint64_t(reciprocal) * value + (uint64_t(1) << 31)) >> 32;
721 * Compute the box blur size (which we're calling the blur radius) from
722 * the standard deviation.
724 * Much of this, the 3 * sqrt(2 * pi) / 4, is the known value for
725 * approximating a Gaussian using box blurs. This yields quite a good
726 * approximation for a Gaussian. Then we multiply this by 1.5 since our
727 * code wants the radius of the entire triple-box-blur kernel instead of
728 * the diameter of an individual box blur. For more details, see:
729 * http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
730 * https://bugzilla.mozilla.org/show_bug.cgi?id=590039#c19
732 static const Float GAUSSIAN_SCALE_FACTOR = Float((3 * sqrt(2 * M_PI) / 4) * 1.5);
734 IntSize
735 AlphaBoxBlur::CalculateBlurRadius(const Point& aStd)
737 IntSize size(static_cast<int32_t>(floor(aStd.x * GAUSSIAN_SCALE_FACTOR + 0.5f)),
738 static_cast<int32_t>(floor(aStd.y * GAUSSIAN_SCALE_FACTOR + 0.5f)));
740 return size;