gfx/2d/Blur.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial