File indexing completed on 2024-04-14 14:22:05

0001 /*
0002     This file is a part of the KDE project
0003 
0004     Copyright © 2006 Zack Rusin <zack@kde.org>
0005     Copyright © 2006-2007, 2008 Fredrik Höglund <fredrik@kde.org>
0006 
0007     The stack blur algorithm was invented by Mario Klingemann <mario@quasimondo.com>
0008 
0009     This implementation is based on the version in Anti-Grain Geometry Version 2.4,
0010     Copyright © 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
0011 
0012     Redistribution and use in source and binary forms, with or without
0013     modification, are permitted provided that the following conditions
0014     are met:
0015 
0016     1. Redistributions of source code must retain the above copyright
0017        notice, this list of conditions and the following disclaimer.
0018     2. Redistributions in binary form must reproduce the above copyright
0019        notice, this list of conditions and the following disclaimer in the
0020        documentation and/or other materials provided with the distribution.
0021 
0022     THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0023     IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0024     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0025     IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0026     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0027     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0028     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0029     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0030     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0031     THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0032 */
0033 
0034 #include <QPainter>
0035 #include <QImage>
0036 #include <QColor>
0037 #include "khtml_debug.h"
0038 
0039 #include <cmath>
0040 #include <string.h>
0041 
0042 #include "imagefilter.h"
0043 
0044 using namespace khtml;
0045 
0046 static const quint32 stack_blur8_mul[255] = {
0047     512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512,
0048     454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512,
0049     482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456,
0050     437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512,
0051     497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328,
0052     320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456,
0053     446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335,
0054     329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512,
0055     505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405,
0056     399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328,
0057     324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271,
0058     268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456,
0059     451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388,
0060     385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335,
0061     332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
0062     289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259
0063 };
0064 
0065 static const quint32 stack_blur8_shr[255] = {
0066     9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
0067     17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
0068     19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
0069     20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
0070     21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
0071     21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
0072     22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
0073     22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
0074     23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
0075     23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
0076     23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
0077     23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
0078     24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
0079     24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
0080     24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
0081     24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
0082 };
0083 
0084 inline static void blurHorizontal(QImage &image, unsigned int *stack, int div, int radius)
0085 {
0086     int stackindex;
0087     int stackstart;
0088 
0089     quint32 *const pixels = reinterpret_cast<quint32 *>(image.bits());
0090     quint32 pixel;
0091 
0092     int w = image.width();
0093     int h = image.height();
0094     int wm = w - 1;
0095 
0096     unsigned int mul_sum = stack_blur8_mul[radius];
0097     unsigned int shr_sum = stack_blur8_shr[radius];
0098 
0099     unsigned int sum, sum_in, sum_out;
0100 
0101     for (int y = 0; y < h; y++) {
0102         sum     = 0;
0103         sum_in  = 0;
0104         sum_out = 0;
0105 
0106         const int yw = y * w;
0107         pixel = pixels[yw];
0108         for (int i = 0; i <= radius; i++) {
0109             stack[i] = qAlpha(pixel);
0110 
0111             sum += stack[i] * (i + 1);
0112             sum_out += stack[i];
0113         }
0114 
0115         for (int i = 1; i <= radius; i++) {
0116             pixel = pixels[yw + qMin(i, wm)];
0117 
0118             unsigned int *stackpix = &stack[i + radius];
0119             *stackpix = qAlpha(pixel);
0120 
0121             sum    += *stackpix * (radius + 1 - i);
0122             sum_in += *stackpix;
0123         }
0124 
0125         stackindex = radius;
0126         for (int x = 0, i = yw; x < w; x++) {
0127             pixels[i++] = (((sum * mul_sum) >> shr_sum) << 24) & 0xff000000;
0128 
0129             sum -= sum_out;
0130 
0131             stackstart = stackindex + div - radius;
0132             if (stackstart >= div) {
0133                 stackstart -= div;
0134             }
0135 
0136             unsigned int *stackpix = &stack[stackstart];
0137 
0138             sum_out -= *stackpix;
0139 
0140             pixel = pixels[yw + qMin(x + radius + 1, wm)];
0141 
0142             *stackpix = qAlpha(pixel);
0143 
0144             sum_in += *stackpix;
0145             sum    += sum_in;
0146 
0147             if (++stackindex >= div) {
0148                 stackindex = 0;
0149             }
0150 
0151             stackpix = &stack[stackindex];
0152 
0153             sum_out += *stackpix;
0154             sum_in  -= *stackpix;
0155         } // for (x = 0, ...)
0156     } // for (y = 0, ...)
0157 }
0158 
0159 inline static void blurVertical(QImage &image, unsigned int *stack, int div, int radius)
0160 {
0161     int stackindex;
0162     int stackstart;
0163 
0164     quint32 *const pixels = reinterpret_cast<quint32 *>(image.bits());
0165     quint32 pixel;
0166 
0167     int w = image.width();
0168     int h = image.height();
0169     int hm = h - 1;
0170 
0171     int mul_sum = stack_blur8_mul[radius];
0172     int shr_sum = stack_blur8_shr[radius];
0173 
0174     unsigned int sum, sum_in, sum_out;
0175 
0176     for (int x = 0; x < w; x++) {
0177         sum     = 0;
0178         sum_in  = 0;
0179         sum_out = 0;
0180 
0181         pixel = pixels[x];
0182         for (int i = 0; i <= radius; i++) {
0183             stack[i] = qAlpha(pixel);
0184 
0185             sum += stack[i] * (i + 1);
0186             sum_out += stack[i];
0187         }
0188 
0189         for (int i = 1; i <= radius; i++) {
0190             pixel = pixels[qMin(i, hm) * w + x];
0191 
0192             unsigned int *stackpix = &stack[i + radius];
0193             *stackpix = qAlpha(pixel);
0194 
0195             sum    += *stackpix * (radius + 1 - i);
0196             sum_in += *stackpix;
0197         }
0198 
0199         stackindex = radius;
0200         for (int y = 0, i = x; y < h; y++, i += w) {
0201             pixels[i] = (((sum * mul_sum) >> shr_sum) << 24) & 0xff000000;
0202 
0203             sum -= sum_out;
0204 
0205             stackstart = stackindex + div - radius;
0206             if (stackstart >= div) {
0207                 stackstart -= div;
0208             }
0209 
0210             unsigned int *stackpix = &stack[stackstart];
0211 
0212             sum_out -= *stackpix;
0213 
0214             pixel = pixels[qMin(y + radius + 1, hm) * w + x];
0215 
0216             *stackpix = qAlpha(pixel);
0217 
0218             sum_in += *stackpix;
0219             sum    += sum_in;
0220 
0221             if (++stackindex >= div) {
0222                 stackindex = 0;
0223             }
0224 
0225             stackpix = &stack[stackindex];
0226 
0227             sum_out += *stackpix;
0228             sum_in  -= *stackpix;
0229         } // for (y = 0, ...)
0230     } // for (x = 0, ...)
0231 }
0232 
0233 static void stackBlur(QImage &image, float radius)
0234 {
0235     radius = qRound(radius);
0236 
0237     int div = int(radius * 2) + 1;
0238     unsigned int *stack  = new unsigned int[div];
0239 
0240     blurHorizontal(image, stack, div, radius);
0241     blurVertical(image, stack, div, radius);
0242 
0243     delete [] stack;
0244 }
0245 
0246 void ImageFilter::shadowBlur(QImage &image, float radius, const QColor &color)
0247 {
0248     if (radius < 0) {
0249         return;
0250     }
0251 
0252     if (radius > 0) {
0253         stackBlur(image, radius);
0254     }
0255 
0256     // Correct the color and opacity of the shadow
0257     QPainter p(&image);
0258     p.setCompositionMode(QPainter::CompositionMode_SourceIn);
0259     p.fillRect(image.rect(), color);
0260 }
0261