File indexing completed on 2024-04-21 11:33:27

0001 // krazy:excludeall=copyright (email of Maxim is missing)
0002 /*
0003     This file is a part of the KDE project
0004 
0005     SPDX-FileCopyrightText: 2006 Zack Rusin <zack@kde.org>
0006     SPDX-FileCopyrightText: 2006-2007, 2008 Fredrik Höglund <fredrik@kde.org>
0007 
0008     The stack blur algorithm was invented by Mario Klingemann <mario@quasimondo.com>
0009 
0010     This implementation is based on the version in Anti-Grain Geometry Version 2.4,
0011     SPDX-FileCopyrightText: 2002-2005 Maxim Shemanarev <http://www.antigrain.com>
0012 
0013     SPDX-License-Identifier: BSD-2-Clause
0014 */
0015 
0016 #include "imagefilter_p.h"
0017 
0018 #include <QColor>
0019 #include <QImage>
0020 #include <QPainter>
0021 
0022 #include <cmath>
0023 #include <string.h>
0024 
0025 using namespace KIO;
0026 
0027 static const quint32 stack_blur8_mul[255] = {
0028     512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312,
0029     292, 273, 512, 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456, 437, 420, 404, 388, 374, 360, 347, 335, 323, 312,
0030     302, 292, 282, 273, 265, 512, 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, 320, 312, 305, 298, 291, 284, 278,
0031     271, 265, 259, 507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312,
0032     307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405, 399,
0033     394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278,
0034     274, 271, 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456, 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408,
0035     404, 400, 396, 392, 388, 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335, 332, 329, 326, 323, 320, 318, 315, 312,
0036     310, 307, 304, 302, 299, 297, 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259};
0037 
0038 static const quint32 stack_blur8_shr[255] = {
0039     9,  11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19,
0040     19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
0041     21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
0042     22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
0043     23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24,
0044     24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
0045     24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24};
0046 
0047 inline static void blurHorizontal(QImage &image, unsigned int *stack, int div, int radius)
0048 {
0049     quint32 *const pixels = reinterpret_cast<quint32 *>(image.bits());
0050     quint32 pixel = 0;
0051 
0052     const int width = image.width();
0053     const int height = image.height();
0054     const int wm = width - 1;
0055 
0056     const unsigned int mul_sum = stack_blur8_mul[radius];
0057     const unsigned int shr_sum = stack_blur8_shr[radius];
0058 
0059     for (int y = 0; y < height; y++) {
0060         unsigned int sum = 0;
0061         unsigned int sum_in = 0;
0062         unsigned int sum_out = 0;
0063 
0064         const int yw = y * width;
0065         pixel = pixels[yw];
0066         for (int i = 0; i <= radius; i++) {
0067             stack[i] = qAlpha(pixel);
0068 
0069             sum += stack[i] * (i + 1);
0070             sum_out += stack[i];
0071         }
0072 
0073         for (int i = 1; i <= radius; i++) {
0074             pixel = pixels[yw + qMin(i, wm)];
0075 
0076             unsigned int *stackpix = &stack[i + radius];
0077             *stackpix = qAlpha(pixel);
0078 
0079             sum += *stackpix * (radius + 1 - i);
0080             sum_in += *stackpix;
0081         }
0082 
0083         int stackindex = radius;
0084         for (int x = 0, i = yw; x < width; x++) {
0085             pixels[i++] = (((sum * mul_sum) >> shr_sum) << 24) & 0xff000000;
0086 
0087             sum -= sum_out;
0088 
0089             int stackstart = stackindex + div - radius;
0090             if (stackstart >= div) {
0091                 stackstart -= div;
0092             }
0093 
0094             unsigned int *stackpix = &stack[stackstart];
0095 
0096             sum_out -= *stackpix;
0097 
0098             pixel = pixels[yw + qMin(x + radius + 1, wm)];
0099 
0100             *stackpix = qAlpha(pixel);
0101 
0102             sum_in += *stackpix;
0103             sum += sum_in;
0104 
0105             if (++stackindex >= div) {
0106                 stackindex = 0;
0107             }
0108 
0109             stackpix = &stack[stackindex];
0110 
0111             sum_out += *stackpix;
0112             sum_in -= *stackpix;
0113         } // for (x = 0, ...)
0114     } // for (y = 0, ...)
0115 }
0116 
0117 inline static void blurVertical(QImage &image, unsigned int *stack, int div, int radius)
0118 {
0119     int stackindex;
0120     int stackstart;
0121 
0122     quint32 *const pixels = reinterpret_cast<quint32 *>(image.bits());
0123     quint32 pixel;
0124 
0125     int w = image.width();
0126     int h = image.height();
0127     int hm = h - 1;
0128 
0129     int mul_sum = stack_blur8_mul[radius];
0130     int shr_sum = stack_blur8_shr[radius];
0131 
0132     unsigned int sum;
0133     unsigned int sum_in;
0134     unsigned int sum_out;
0135 
0136     for (int x = 0; x < w; x++) {
0137         sum = 0;
0138         sum_in = 0;
0139         sum_out = 0;
0140 
0141         pixel = pixels[x];
0142         for (int i = 0; i <= radius; i++) {
0143             stack[i] = qAlpha(pixel);
0144 
0145             sum += stack[i] * (i + 1);
0146             sum_out += stack[i];
0147         }
0148 
0149         for (int i = 1; i <= radius; i++) {
0150             pixel = pixels[qMin(i, hm) * w + x];
0151 
0152             unsigned int *stackpix = &stack[i + radius];
0153             *stackpix = qAlpha(pixel);
0154 
0155             sum += *stackpix * (radius + 1 - i);
0156             sum_in += *stackpix;
0157         }
0158 
0159         stackindex = radius;
0160         for (int y = 0, i = x; y < h; y++, i += w) {
0161             pixels[i] = (((sum * mul_sum) >> shr_sum) << 24) & 0xff000000;
0162 
0163             sum -= sum_out;
0164 
0165             stackstart = stackindex + div - radius;
0166             if (stackstart >= div) {
0167                 stackstart -= div;
0168             }
0169 
0170             unsigned int *stackpix = &stack[stackstart];
0171 
0172             sum_out -= *stackpix;
0173 
0174             pixel = pixels[qMin(y + radius + 1, hm) * w + x];
0175 
0176             *stackpix = qAlpha(pixel);
0177 
0178             sum_in += *stackpix;
0179             sum += sum_in;
0180 
0181             if (++stackindex >= div) {
0182                 stackindex = 0;
0183             }
0184 
0185             stackpix = &stack[stackindex];
0186 
0187             sum_out += *stackpix;
0188             sum_in -= *stackpix;
0189         } // for (y = 0, ...)
0190     } // for (x = 0, ...)
0191 }
0192 
0193 static void stackBlur(QImage &image, float radius)
0194 {
0195     radius = qRound(radius);
0196 
0197     int div = int(radius * 2) + 1;
0198     unsigned int *stack = new unsigned int[div];
0199 
0200     blurHorizontal(image, stack, div, radius);
0201     blurVertical(image, stack, div, radius);
0202 
0203     delete[] stack;
0204 }
0205 
0206 void ImageFilter::shadowBlur(QImage &image, float radius, const QColor &color)
0207 {
0208     if (radius < 0) {
0209         return;
0210     }
0211 
0212     if (radius > 0) {
0213         stackBlur(image, radius);
0214     }
0215 
0216     // Correct the color and opacity of the shadow
0217     QPainter p(&image);
0218     p.setCompositionMode(QPainter::CompositionMode_SourceIn);
0219     p.fillRect(image.rect(), color);
0220 }