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
}