| 1 |
/* |
| 2 |
This file is part of the Ofi Labs X2 project. |
| 3 |
|
| 4 |
Copyright (C) 2010 Ariya Hidayat <ariya.hidayat@gmail.com> |
| 5 |
|
| 6 |
Redistribution and use in source and binary forms, with or without |
| 7 |
modification, are permitted provided that the following conditions are met: |
| 8 |
|
| 9 |
* Redistributions of source code must retain the above copyright |
| 10 |
notice, this list of conditions and the following disclaimer. |
| 11 |
* Redistributions in binary form must reproduce the above copyright |
| 12 |
notice, this list of conditions and the following disclaimer in the |
| 13 |
documentation and/or other materials provided with the distribution. |
| 14 |
* Neither the name of the <organization> nor the |
| 15 |
names of its contributors may be used to endorse or promote products |
| 16 |
derived from this software without specific prior written permission. |
| 17 |
|
| 18 |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 19 |
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 20 |
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 21 |
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY |
| 22 |
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 23 |
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 24 |
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 25 |
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 26 |
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 27 |
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 |
*/ |
| 29 |
|
| 30 |
#include "shadowblur.h" |
| 31 |
|
| 32 |
#include <QImage> |
| 33 |
#include <QPainter> |
| 34 |
|
| 35 |
static const int BlurSumShift = 15; |
| 36 |
|
| 37 |
// Check http://www.w3.org/TR/SVG/filters.html#feGaussianBlur. |
| 38 |
// As noted in the SVG filter specification, running box blur 3x |
| 39 |
// approximates a real gaussian blur nicely. |
| 40 |
|
| 41 |
void shadowBlur(QImage& image, int radius, const QColor& shadowColor) |
| 42 |
{ |
| 43 |
// See comments in http://webkit.org/b/40793, it seems sensible |
| 44 |
// to follow Skia's limit of 128 pixels for the blur radius. |
| 45 |
if (radius > 128) |
| 46 |
radius = 128; |
| 47 |
|
| 48 |
int channels[4] = { 3, 0, 1, 3 }; |
| 49 |
int dmax = radius >> 1; |
| 50 |
int dmin = dmax - 1 + (radius & 1); |
| 51 |
if (dmin < 0) |
| 52 |
dmin = 0; |
| 53 |
|
| 54 |
// Two stages: horizontal and vertical |
| 55 |
for (int k = 0; k < 2; ++k) { |
| 56 |
|
| 57 |
unsigned char* pixels = image.bits(); |
| 58 |
int stride = (k == 0) ? 4 : image.bytesPerLine(); |
| 59 |
int delta = (k == 0) ? image.bytesPerLine() : 4; |
| 60 |
int jfinal = (k == 0) ? image.height() : image.width(); |
| 61 |
int dim = (k == 0) ? image.width() : image.height(); |
| 62 |
|
| 63 |
for (int j = 0; j < jfinal; ++j, pixels += delta) { |
| 64 |
|
| 65 |
// For each step, we blur the alpha in a channel and store the result |
| 66 |
// in another channel for the subsequent step. |
| 67 |
// We use sliding window algorithm to accumulate the alpha values. |
| 68 |
// This is much more efficient than computing the sum of each pixels |
| 69 |
// covered by the box kernel size for each x. |
| 70 |
|
| 71 |
for (int step = 0; step < 3; ++step) { |
| 72 |
int side1 = (step == 0) ? dmin : dmax; |
| 73 |
int side2 = (step == 1) ? dmin : dmax; |
| 74 |
int pixelCount = side1 + 1 + side2; |
| 75 |
int invCount = ((1 << BlurSumShift) + pixelCount - 1) / pixelCount; |
| 76 |
int ofs = 1 + side2; |
| 77 |
int alpha1 = pixels[channels[step]]; |
| 78 |
int alpha2 = pixels[(dim - 1) * stride + channels[step]]; |
| 79 |
unsigned char* ptr = pixels + channels[step + 1]; |
| 80 |
unsigned char* prev = pixels + stride + channels[step]; |
| 81 |
unsigned char* next = pixels + ofs * stride + channels[step]; |
| 82 |
|
| 83 |
int i; |
| 84 |
int sum = side1 * alpha1 + alpha1; |
| 85 |
int limit = (dim < side2 + 1) ? dim : side2 + 1; |
| 86 |
for (i = 1; i < limit; ++i, prev += stride) |
| 87 |
sum += *prev; |
| 88 |
if (limit <= side2) |
| 89 |
sum += (side2 - limit + 1) * alpha2; |
| 90 |
|
| 91 |
limit = (side1 < dim) ? side1 : dim; |
| 92 |
for (i = 0; i < limit; ptr += stride, next += stride, ++i, ++ofs) { |
| 93 |
*ptr = (sum * invCount) >> BlurSumShift; |
| 94 |
sum += ((ofs < dim) ? *next : alpha2) - alpha1; |
| 95 |
} |
| 96 |
prev = pixels + channels[step]; |
| 97 |
for (; ofs < dim; ptr += stride, prev += stride, next += stride, ++i, ++ofs) { |
| 98 |
*ptr = (sum * invCount) >> BlurSumShift; |
| 99 |
sum += (*next) - (*prev); |
| 100 |
} |
| 101 |
for (; i < dim; ptr += stride, prev += stride, ++i) { |
| 102 |
*ptr = (sum * invCount) >> BlurSumShift; |
| 103 |
sum += alpha2 - (*prev); |
| 104 |
} |
| 105 |
} |
| 106 |
} |
| 107 |
} |
| 108 |
|
| 109 |
// "Colorize" with the right shadow color. |
| 110 |
QPainter p(&image); |
| 111 |
p.setCompositionMode(QPainter::CompositionMode_SourceIn); |
| 112 |
p.fillRect(image.rect(), shadowColor); |
| 113 |
p.end(); |
| 114 |
} |