1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is Mozilla gfx.
15 : *
16 : * The Initial Developer of the Original Code is Mozilla Foundation.
17 : * Portions created by the Initial Developer are Copyright (C) 2011
18 : * the Initial Developer. All Rights Reserved.
19 : *
20 : * Contributor(s):
21 : *
22 : * Alternatively, the contents of this file may be used under the terms of
23 : * either the GNU General Public License Version 2 or later (the "GPL"), or
24 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25 : * in which case the provisions of the GPL or the LGPL are applicable instead
26 : * of those above. If you wish to allow use of your version of this file only
27 : * under the terms of either the GPL or the LGPL, and not to allow others to
28 : * use your version of this file under the terms of the MPL, indicate your
29 : * decision by deleting the provisions above and replace them with the notice
30 : * and other provisions required by the GPL or the LGPL. If you do not delete
31 : * the provisions above, a recipient may use your version of this file under
32 : * the terms of any one of the MPL, the GPL or the LGPL.
33 : *
34 : * ***** END LICENSE BLOCK ***** */
35 :
36 : #include "mozilla/gfx/Blur.h"
37 :
38 : #include <algorithm>
39 : #include <math.h>
40 : #include <string.h>
41 :
42 : #include "CheckedInt.h"
43 : #include "mozilla/Util.h"
44 :
45 : #ifndef M_PI
46 : #define M_PI 3.14159265358979323846
47 : #endif
48 :
49 : using namespace std;
50 :
51 : namespace mozilla {
52 : namespace gfx {
53 :
54 : /**
55 : * Box blur involves looking at one pixel, and setting its value to the average
56 : * of its neighbouring pixels.
57 : * @param aInput The input buffer.
58 : * @param aOutput The output buffer.
59 : * @param aLeftLobe The number of pixels to blend on the left.
60 : * @param aRightLobe The number of pixels to blend on the right.
61 : * @param aWidth The number of columns in the buffers.
62 : * @param aRows The number of rows in the buffers.
63 : * @param aSkipRect An area to skip blurring in.
64 : * XXX shouldn't we pass stride in separately here?
65 : */
66 : static void
67 0 : BoxBlurHorizontal(unsigned char* aInput,
68 : unsigned char* aOutput,
69 : int32_t aLeftLobe,
70 : int32_t aRightLobe,
71 : int32_t aWidth,
72 : int32_t aRows,
73 : const IntRect& aSkipRect)
74 : {
75 0 : MOZ_ASSERT(aWidth > 0);
76 :
77 0 : int32_t boxSize = aLeftLobe + aRightLobe + 1;
78 : bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
79 0 : aWidth <= aSkipRect.XMost();
80 :
81 0 : for (int32_t y = 0; y < aRows; y++) {
82 : // Check whether the skip rect intersects this row. If the skip
83 : // rect covers the whole surface in this row, we can avoid
84 : // this row entirely (and any others along the skip rect).
85 : bool inSkipRectY = y >= aSkipRect.y &&
86 0 : y < aSkipRect.YMost();
87 0 : if (inSkipRectY && skipRectCoversWholeRow) {
88 0 : y = aSkipRect.YMost() - 1;
89 0 : continue;
90 : }
91 :
92 0 : int32_t alphaSum = 0;
93 0 : for (int32_t i = 0; i < boxSize; i++) {
94 0 : int32_t pos = i - aLeftLobe;
95 : // See assertion above; if aWidth is zero, then we would have no
96 : // valid position to clamp to.
97 0 : pos = max(pos, 0);
98 0 : pos = min(pos, aWidth - 1);
99 0 : alphaSum += aInput[aWidth * y + pos];
100 : }
101 0 : for (int32_t x = 0; x < aWidth; x++) {
102 : // Check whether we are within the skip rect. If so, go
103 : // to the next point outside the skip rect.
104 0 : if (inSkipRectY && x >= aSkipRect.x &&
105 0 : x < aSkipRect.XMost()) {
106 0 : x = aSkipRect.XMost();
107 0 : if (x >= aWidth)
108 0 : break;
109 :
110 : // Recalculate the neighbouring alpha values for
111 : // our new point on the surface.
112 0 : alphaSum = 0;
113 0 : for (int32_t i = 0; i < boxSize; i++) {
114 0 : int32_t pos = x + i - aLeftLobe;
115 : // See assertion above; if aWidth is zero, then we would have no
116 : // valid position to clamp to.
117 0 : pos = max(pos, 0);
118 0 : pos = min(pos, aWidth - 1);
119 0 : alphaSum += aInput[aWidth * y + pos];
120 : }
121 : }
122 0 : int32_t tmp = x - aLeftLobe;
123 0 : int32_t last = max(tmp, 0);
124 0 : int32_t next = min(tmp + boxSize, aWidth - 1);
125 :
126 0 : aOutput[aWidth * y + x] = alphaSum / boxSize;
127 :
128 0 : alphaSum += aInput[aWidth * y + next] -
129 0 : aInput[aWidth * y + last];
130 : }
131 : }
132 0 : }
133 :
134 : /**
135 : * Identical to BoxBlurHorizontal, except it blurs top and bottom instead of
136 : * left and right.
137 : * XXX shouldn't we pass stride in separately here?
138 : */
139 : static void
140 0 : BoxBlurVertical(unsigned char* aInput,
141 : unsigned char* aOutput,
142 : int32_t aTopLobe,
143 : int32_t aBottomLobe,
144 : int32_t aWidth,
145 : int32_t aRows,
146 : const IntRect& aSkipRect)
147 : {
148 0 : MOZ_ASSERT(aRows > 0);
149 :
150 0 : int32_t boxSize = aTopLobe + aBottomLobe + 1;
151 : bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
152 0 : aRows <= aSkipRect.YMost();
153 :
154 0 : for (int32_t x = 0; x < aWidth; x++) {
155 : bool inSkipRectX = x >= aSkipRect.x &&
156 0 : x < aSkipRect.XMost();
157 0 : if (inSkipRectX && skipRectCoversWholeColumn) {
158 0 : x = aSkipRect.XMost() - 1;
159 0 : continue;
160 : }
161 :
162 0 : int32_t alphaSum = 0;
163 0 : for (int32_t i = 0; i < boxSize; i++) {
164 0 : int32_t pos = i - aTopLobe;
165 : // See assertion above; if aRows is zero, then we would have no
166 : // valid position to clamp to.
167 0 : pos = max(pos, 0);
168 0 : pos = min(pos, aRows - 1);
169 0 : alphaSum += aInput[aWidth * pos + x];
170 : }
171 0 : for (int32_t y = 0; y < aRows; y++) {
172 0 : if (inSkipRectX && y >= aSkipRect.y &&
173 0 : y < aSkipRect.YMost()) {
174 0 : y = aSkipRect.YMost();
175 0 : if (y >= aRows)
176 0 : break;
177 :
178 0 : alphaSum = 0;
179 0 : for (int32_t i = 0; i < boxSize; i++) {
180 0 : int32_t pos = y + i - aTopLobe;
181 : // See assertion above; if aRows is zero, then we would have no
182 : // valid position to clamp to.
183 0 : pos = max(pos, 0);
184 0 : pos = min(pos, aRows - 1);
185 0 : alphaSum += aInput[aWidth * pos + x];
186 : }
187 : }
188 0 : int32_t tmp = y - aTopLobe;
189 0 : int32_t last = max(tmp, 0);
190 0 : int32_t next = min(tmp + boxSize, aRows - 1);
191 :
192 0 : aOutput[aWidth * y + x] = alphaSum/boxSize;
193 :
194 0 : alphaSum += aInput[aWidth * next + x] -
195 0 : aInput[aWidth * last + x];
196 : }
197 : }
198 0 : }
199 :
200 0 : static void ComputeLobes(int32_t aRadius, int32_t aLobes[3][2])
201 : {
202 : int32_t major, minor, final;
203 :
204 : /* See http://www.w3.org/TR/SVG/filters.html#feGaussianBlur for
205 : * some notes about approximating the Gaussian blur with box-blurs.
206 : * The comments below are in the terminology of that page.
207 : */
208 0 : int32_t z = aRadius / 3;
209 0 : switch (aRadius % 3) {
210 : case 0:
211 : // aRadius = z*3; choose d = 2*z + 1
212 0 : major = minor = final = z;
213 0 : break;
214 : case 1:
215 : // aRadius = z*3 + 1
216 : // This is a tricky case since there is no value of d which will
217 : // yield a radius of exactly aRadius. If d is odd, i.e. d=2*k + 1
218 : // for some integer k, then the radius will be 3*k. If d is even,
219 : // i.e. d=2*k, then the radius will be 3*k - 1.
220 : // So we have to choose values that don't match the standard
221 : // algorithm.
222 0 : major = z + 1;
223 0 : minor = final = z;
224 0 : break;
225 : case 2:
226 : // aRadius = z*3 + 2; choose d = 2*z + 2
227 0 : major = final = z + 1;
228 0 : minor = z;
229 0 : break;
230 : default:
231 : // Mathematical impossibility!
232 0 : MOZ_ASSERT(false);
233 0 : major = minor = final = 0;
234 : }
235 0 : MOZ_ASSERT(major + minor + final == aRadius);
236 :
237 0 : aLobes[0][0] = major;
238 0 : aLobes[0][1] = minor;
239 0 : aLobes[1][0] = minor;
240 0 : aLobes[1][1] = major;
241 0 : aLobes[2][0] = final;
242 0 : aLobes[2][1] = final;
243 0 : }
244 :
245 : static void
246 0 : SpreadHorizontal(unsigned char* aInput,
247 : unsigned char* aOutput,
248 : int32_t aRadius,
249 : int32_t aWidth,
250 : int32_t aRows,
251 : int32_t aStride,
252 : const IntRect& aSkipRect)
253 : {
254 0 : if (aRadius == 0) {
255 0 : memcpy(aOutput, aInput, aStride * aRows);
256 0 : return;
257 : }
258 :
259 : bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
260 0 : aWidth <= aSkipRect.XMost();
261 0 : for (int32_t y = 0; y < aRows; y++) {
262 : // Check whether the skip rect intersects this row. If the skip
263 : // rect covers the whole surface in this row, we can avoid
264 : // this row entirely (and any others along the skip rect).
265 : bool inSkipRectY = y >= aSkipRect.y &&
266 0 : y < aSkipRect.YMost();
267 0 : if (inSkipRectY && skipRectCoversWholeRow) {
268 0 : y = aSkipRect.YMost() - 1;
269 0 : continue;
270 : }
271 :
272 0 : for (int32_t x = 0; x < aWidth; x++) {
273 : // Check whether we are within the skip rect. If so, go
274 : // to the next point outside the skip rect.
275 0 : if (inSkipRectY && x >= aSkipRect.x &&
276 0 : x < aSkipRect.XMost()) {
277 0 : x = aSkipRect.XMost();
278 0 : if (x >= aWidth)
279 0 : break;
280 : }
281 :
282 0 : int32_t sMin = max(x - aRadius, 0);
283 0 : int32_t sMax = min(x + aRadius, aWidth - 1);
284 0 : int32_t v = 0;
285 0 : for (int32_t s = sMin; s <= sMax; ++s) {
286 0 : v = max<int32_t>(v, aInput[aStride * y + s]);
287 : }
288 0 : aOutput[aStride * y + x] = v;
289 : }
290 : }
291 : }
292 :
293 : static void
294 0 : SpreadVertical(unsigned char* aInput,
295 : unsigned char* aOutput,
296 : int32_t aRadius,
297 : int32_t aWidth,
298 : int32_t aRows,
299 : int32_t aStride,
300 : const IntRect& aSkipRect)
301 : {
302 0 : if (aRadius == 0) {
303 0 : memcpy(aOutput, aInput, aStride * aRows);
304 0 : return;
305 : }
306 :
307 : bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
308 0 : aRows <= aSkipRect.YMost();
309 0 : for (int32_t x = 0; x < aWidth; x++) {
310 : bool inSkipRectX = x >= aSkipRect.x &&
311 0 : x < aSkipRect.XMost();
312 0 : if (inSkipRectX && skipRectCoversWholeColumn) {
313 0 : x = aSkipRect.XMost() - 1;
314 0 : continue;
315 : }
316 :
317 0 : for (int32_t y = 0; y < aRows; y++) {
318 : // Check whether we are within the skip rect. If so, go
319 : // to the next point outside the skip rect.
320 0 : if (inSkipRectX && y >= aSkipRect.y &&
321 0 : y < aSkipRect.YMost()) {
322 0 : y = aSkipRect.YMost();
323 0 : if (y >= aRows)
324 0 : break;
325 : }
326 :
327 0 : int32_t sMin = max(y - aRadius, 0);
328 0 : int32_t sMax = min(y + aRadius, aRows - 1);
329 0 : int32_t v = 0;
330 0 : for (int32_t s = sMin; s <= sMax; ++s) {
331 0 : v = max<int32_t>(v, aInput[aStride * s + x]);
332 : }
333 0 : aOutput[aStride * y + x] = v;
334 : }
335 : }
336 : }
337 :
338 : static CheckedInt<int32_t>
339 0 : RoundUpToMultipleOf4(int32_t aVal)
340 : {
341 0 : CheckedInt<int32_t> val(aVal);
342 :
343 0 : val += 3;
344 0 : val /= 4;
345 0 : val *= 4;
346 :
347 : return val;
348 : }
349 :
350 0 : AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
351 : const IntSize& aSpreadRadius,
352 : const IntSize& aBlurRadius,
353 : const Rect* aDirtyRect,
354 : const Rect* aSkipRect)
355 : : mSpreadRadius(aSpreadRadius),
356 : mBlurRadius(aBlurRadius),
357 0 : mData(NULL)
358 : {
359 0 : Rect rect(aRect);
360 0 : rect.Inflate(Size(aBlurRadius + aSpreadRadius));
361 0 : rect.RoundOut();
362 :
363 0 : if (aDirtyRect) {
364 : // If we get passed a dirty rect from layout, we can minimize the
365 : // shadow size and make painting faster.
366 0 : mHasDirtyRect = true;
367 0 : mDirtyRect = *aDirtyRect;
368 0 : Rect requiredBlurArea = mDirtyRect.Intersect(rect);
369 0 : requiredBlurArea.Inflate(Size(aBlurRadius + aSpreadRadius));
370 0 : rect = requiredBlurArea.Intersect(rect);
371 : } else {
372 0 : mHasDirtyRect = false;
373 : }
374 :
375 0 : if (rect.IsEmpty()) {
376 0 : return;
377 : }
378 :
379 0 : if (aSkipRect) {
380 : // If we get passed a skip rect, we can lower the amount of
381 : // blurring/spreading we need to do. We convert it to IntRect to avoid
382 : // expensive int<->float conversions if we were to use Rect instead.
383 0 : Rect skipRect = *aSkipRect;
384 0 : skipRect.RoundIn();
385 0 : skipRect.Deflate(Size(aBlurRadius + aSpreadRadius));
386 0 : mSkipRect = IntRect(skipRect.x, skipRect.y, skipRect.width, skipRect.height);
387 :
388 0 : IntRect shadowIntRect(rect.x, rect.y, rect.width, rect.height);
389 0 : mSkipRect.IntersectRect(mSkipRect, shadowIntRect);
390 :
391 0 : if (mSkipRect.IsEqualInterior(shadowIntRect))
392 0 : return;
393 :
394 0 : mSkipRect -= shadowIntRect.TopLeft();
395 : } else {
396 0 : mSkipRect = IntRect(0, 0, 0, 0);
397 : }
398 :
399 0 : mRect = IntRect(rect.x, rect.y, rect.width, rect.height);
400 :
401 0 : CheckedInt<int32_t> stride = RoundUpToMultipleOf4(mRect.width);
402 0 : if (stride.valid()) {
403 0 : mStride = stride.value();
404 :
405 0 : CheckedInt<int32_t> size = CheckedInt<int32_t>(mStride) * mRect.height *
406 0 : sizeof(unsigned char);
407 0 : if (size.valid()) {
408 0 : mData = static_cast<unsigned char*>(malloc(size.value()));
409 0 : memset(mData, 0, size.value());
410 : }
411 : }
412 : }
413 :
414 0 : AlphaBoxBlur::~AlphaBoxBlur()
415 : {
416 0 : free(mData);
417 0 : }
418 :
419 : unsigned char*
420 0 : AlphaBoxBlur::GetData()
421 : {
422 0 : return mData;
423 : }
424 :
425 : IntSize
426 0 : AlphaBoxBlur::GetSize()
427 : {
428 0 : IntSize size(mRect.width, mRect.height);
429 : return size;
430 : }
431 :
432 : int32_t
433 0 : AlphaBoxBlur::GetStride()
434 : {
435 0 : return mStride;
436 : }
437 :
438 : IntRect
439 0 : AlphaBoxBlur::GetRect()
440 : {
441 0 : return mRect;
442 : }
443 :
444 : Rect*
445 0 : AlphaBoxBlur::GetDirtyRect()
446 : {
447 0 : if (mHasDirtyRect) {
448 0 : return &mDirtyRect;
449 : }
450 :
451 0 : return NULL;
452 : }
453 :
454 : void
455 0 : AlphaBoxBlur::Blur()
456 : {
457 0 : if (!mData) {
458 0 : return;
459 : }
460 :
461 : // no need to do all this if not blurring or spreading
462 0 : if (mBlurRadius != IntSize(0,0) || mSpreadRadius != IntSize(0,0)) {
463 0 : int32_t stride = GetStride();
464 :
465 : // No need to use CheckedInt here - we have validated it in the constructor.
466 0 : size_t szB = stride * GetSize().height * sizeof(unsigned char);
467 0 : unsigned char* tmpData = static_cast<unsigned char*>(malloc(szB));
468 0 : if (!tmpData)
469 0 : return; // OOM
470 :
471 0 : memset(tmpData, 0, szB);
472 :
473 0 : if (mSpreadRadius.width > 0 || mSpreadRadius.height > 0) {
474 0 : SpreadHorizontal(mData, tmpData, mSpreadRadius.width, GetSize().width, GetSize().height, stride, mSkipRect);
475 0 : SpreadVertical(tmpData, mData, mSpreadRadius.height, GetSize().width, GetSize().height, stride, mSkipRect);
476 : }
477 :
478 0 : if (mBlurRadius.width > 0) {
479 : int32_t lobes[3][2];
480 0 : ComputeLobes(mBlurRadius.width, lobes);
481 0 : BoxBlurHorizontal(mData, tmpData, lobes[0][0], lobes[0][1], stride, GetSize().height, mSkipRect);
482 0 : BoxBlurHorizontal(tmpData, mData, lobes[1][0], lobes[1][1], stride, GetSize().height, mSkipRect);
483 0 : BoxBlurHorizontal(mData, tmpData, lobes[2][0], lobes[2][1], stride, GetSize().height, mSkipRect);
484 : } else {
485 0 : memcpy(tmpData, mData, stride * GetSize().height);
486 : }
487 :
488 0 : if (mBlurRadius.height > 0) {
489 : int32_t lobes[3][2];
490 0 : ComputeLobes(mBlurRadius.height, lobes);
491 0 : BoxBlurVertical(tmpData, mData, lobes[0][0], lobes[0][1], stride, GetSize().height, mSkipRect);
492 0 : BoxBlurVertical(mData, tmpData, lobes[1][0], lobes[1][1], stride, GetSize().height, mSkipRect);
493 0 : BoxBlurVertical(tmpData, mData, lobes[2][0], lobes[2][1], stride, GetSize().height, mSkipRect);
494 : } else {
495 0 : memcpy(mData, tmpData, stride * GetSize().height);
496 : }
497 :
498 0 : free(tmpData);
499 : }
500 :
501 : }
502 :
503 : /**
504 : * Compute the box blur size (which we're calling the blur radius) from
505 : * the standard deviation.
506 : *
507 : * Much of this, the 3 * sqrt(2 * pi) / 4, is the known value for
508 : * approximating a Gaussian using box blurs. This yields quite a good
509 : * approximation for a Gaussian. Then we multiply this by 1.5 since our
510 : * code wants the radius of the entire triple-box-blur kernel instead of
511 : * the diameter of an individual box blur. For more details, see:
512 : * http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
513 : * https://bugzilla.mozilla.org/show_bug.cgi?id=590039#c19
514 : */
515 : static const Float GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5;
516 :
517 : IntSize
518 0 : AlphaBoxBlur::CalculateBlurRadius(const Point& aStd)
519 : {
520 0 : IntSize size(static_cast<int32_t>(floor(aStd.x * GAUSSIAN_SCALE_FACTOR + 0.5)),
521 0 : static_cast<int32_t>(floor(aStd.y * GAUSSIAN_SCALE_FACTOR + 0.5)));
522 :
523 : return size;
524 : }
525 :
526 : }
527 : }
|