File indexing completed on 2024-05-12 16:34:27

0001 /* This file is part of the KDE project
0002  * Copyright (c) 2009 Jan Hambrecht <jaham@gmx.net>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Library General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Lesser General Public License
0015  * along with this library; see the file COPYING.LIB.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018  */
0019 
0020 #include "BlurEffect.h"
0021 #include "KoFilterEffectRenderContext.h"
0022 #include "KoFilterEffectLoadingContext.h"
0023 #include "KoViewConverter.h"
0024 #include "KoXmlWriter.h"
0025 #include "KoXmlReader.h"
0026 #include <klocalizedstring.h>
0027 #include <QColor>
0028 #include <QImage>
0029 
0030 // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
0031 // fixed to handle alpha channel correctly by Zack Rusin
0032 void fastbluralpha(QImage &img, int radius)
0033 {
0034     if (radius < 1) {
0035         return;
0036     }
0037 
0038     QRgb *pix = (QRgb*)img.bits();
0039     int w   = img.width();
0040     int h   = img.height();
0041     int wm  = w - 1;
0042     int hm  = h - 1;
0043     int wh  = w * h;
0044     int div = radius + radius + 1;
0045 
0046     int *r = new int[wh];
0047     int *g = new int[wh];
0048     int *b = new int[wh];
0049     int *a = new int[wh];
0050     int rsum, gsum, bsum, asum, x, y, i, yp, yi, yw;
0051     QRgb p;
0052     int *vmin = new int[qMax(w, h)];
0053 
0054     int divsum = (div + 1) >> 1;
0055     divsum *= divsum;
0056     int *dv = new int[256*divsum];
0057     for (i = 0; i < 256*divsum; ++i) {
0058         dv[i] = (i / divsum);
0059     }
0060 
0061     yw = yi = 0;
0062 
0063     int **stack = new int*[div];
0064     for (int i = 0; i < div; ++i) {
0065         stack[i] = new int[4];
0066     }
0067 
0068 
0069     int stackpointer;
0070     int stackstart;
0071     int *sir;
0072     int rbs;
0073     int r1 = radius + 1;
0074     int routsum, goutsum, boutsum, aoutsum;
0075     int rinsum, ginsum, binsum, ainsum;
0076 
0077     for (y = 0; y < h; ++y) {
0078         rinsum = ginsum = binsum = ainsum
0079                                    = routsum = goutsum = boutsum = aoutsum
0080                                                                    = rsum = gsum = bsum = asum = 0;
0081         for (i = - radius; i <= radius; ++i) {
0082             p = pix[yi+qMin(wm, qMax(i, 0))];
0083             sir = stack[i+radius];
0084             sir[0] = qRed(p);
0085             sir[1] = qGreen(p);
0086             sir[2] = qBlue(p);
0087             sir[3] = qAlpha(p);
0088 
0089             rbs = r1 - abs(i);
0090             rsum += sir[0] * rbs;
0091             gsum += sir[1] * rbs;
0092             bsum += sir[2] * rbs;
0093             asum += sir[3] * rbs;
0094 
0095             if (i > 0) {
0096                 rinsum += sir[0];
0097                 ginsum += sir[1];
0098                 binsum += sir[2];
0099                 ainsum += sir[3];
0100             } else {
0101                 routsum += sir[0];
0102                 goutsum += sir[1];
0103                 boutsum += sir[2];
0104                 aoutsum += sir[3];
0105             }
0106         }
0107         stackpointer = radius;
0108 
0109         for (x = 0; x < w; ++x) {
0110 
0111             r[yi] = dv[rsum];
0112             g[yi] = dv[gsum];
0113             b[yi] = dv[bsum];
0114             a[yi] = dv[asum];
0115 
0116             rsum -= routsum;
0117             gsum -= goutsum;
0118             bsum -= boutsum;
0119             asum -= aoutsum;
0120 
0121             stackstart = stackpointer - radius + div;
0122             sir = stack[stackstart%div];
0123 
0124             routsum -= sir[0];
0125             goutsum -= sir[1];
0126             boutsum -= sir[2];
0127             aoutsum -= sir[3];
0128 
0129             if (y == 0) {
0130                 vmin[x] = qMin(x + radius + 1, wm);
0131             }
0132             p = pix[yw+vmin[x]];
0133 
0134             sir[0] = qRed(p);
0135             sir[1] = qGreen(p);
0136             sir[2] = qBlue(p);
0137             sir[3] = qAlpha(p);
0138 
0139             rinsum += sir[0];
0140             ginsum += sir[1];
0141             binsum += sir[2];
0142             ainsum += sir[3];
0143 
0144             rsum += rinsum;
0145             gsum += ginsum;
0146             bsum += binsum;
0147             asum += ainsum;
0148 
0149             stackpointer = (stackpointer + 1) % div;
0150             sir = stack[(stackpointer)%div];
0151 
0152             routsum += sir[0];
0153             goutsum += sir[1];
0154             boutsum += sir[2];
0155             aoutsum += sir[3];
0156 
0157             rinsum -= sir[0];
0158             ginsum -= sir[1];
0159             binsum -= sir[2];
0160             ainsum -= sir[3];
0161 
0162             ++yi;
0163         }
0164         yw += w;
0165     }
0166     for (x = 0; x < w; ++x) {
0167         rinsum = ginsum = binsum = ainsum
0168                                    = routsum = goutsum = boutsum = aoutsum
0169                                                                    = rsum = gsum = bsum = asum = 0;
0170 
0171         yp = - radius * w;
0172 
0173         for (i = -radius; i <= radius; ++i) {
0174             yi = qMax(0, yp) + x;
0175 
0176             sir = stack[i+radius];
0177 
0178             sir[0] = r[yi];
0179             sir[1] = g[yi];
0180             sir[2] = b[yi];
0181             sir[3] = a[yi];
0182 
0183             rbs = r1 - abs(i);
0184 
0185             rsum += r[yi] * rbs;
0186             gsum += g[yi] * rbs;
0187             bsum += b[yi] * rbs;
0188             asum += a[yi] * rbs;
0189 
0190             if (i > 0) {
0191                 rinsum += sir[0];
0192                 ginsum += sir[1];
0193                 binsum += sir[2];
0194                 ainsum += sir[3];
0195             } else {
0196                 routsum += sir[0];
0197                 goutsum += sir[1];
0198                 boutsum += sir[2];
0199                 aoutsum += sir[3];
0200             }
0201 
0202             if (i < hm) {
0203                 yp += w;
0204             }
0205         }
0206 
0207         yi = x;
0208         stackpointer = radius;
0209 
0210         for (y = 0; y < h; ++y) {
0211             pix[yi] = qRgba(dv[rsum], dv[gsum], dv[bsum], dv[asum]);
0212 
0213             rsum -= routsum;
0214             gsum -= goutsum;
0215             bsum -= boutsum;
0216             asum -= aoutsum;
0217 
0218             stackstart = stackpointer - radius + div;
0219             sir = stack[stackstart%div];
0220 
0221             routsum -= sir[0];
0222             goutsum -= sir[1];
0223             boutsum -= sir[2];
0224             aoutsum -= sir[3];
0225 
0226             if (x == 0) {
0227                 vmin[y] = qMin(y + r1, hm) * w;
0228             }
0229             p = x + vmin[y];
0230 
0231             sir[0] = r[p];
0232             sir[1] = g[p];
0233             sir[2] = b[p];
0234             sir[3] = a[p];
0235 
0236             rinsum += sir[0];
0237             ginsum += sir[1];
0238             binsum += sir[2];
0239             ainsum += sir[3];
0240 
0241             rsum += rinsum;
0242             gsum += ginsum;
0243             bsum += binsum;
0244             asum += ainsum;
0245 
0246             stackpointer = (stackpointer + 1) % div;
0247             sir = stack[stackpointer];
0248 
0249             routsum += sir[0];
0250             goutsum += sir[1];
0251             boutsum += sir[2];
0252             aoutsum += sir[3];
0253 
0254             rinsum -= sir[0];
0255             ginsum -= sir[1];
0256             binsum -= sir[2];
0257             ainsum -= sir[3];
0258 
0259             yi += w;
0260         }
0261     }
0262     delete [] r;
0263     delete [] g;
0264     delete [] b;
0265     delete [] a;
0266     delete [] vmin;
0267     delete [] dv;
0268 
0269     for (int i = 0; i < div; ++i) {
0270         delete [] stack[i];
0271     }
0272     delete [] stack;
0273 }
0274 
0275 BlurEffect::BlurEffect()
0276         : KoFilterEffect(BlurEffectId, i18n("Gaussian blur"))
0277         , m_deviation(0, 0)
0278 {
0279 }
0280 
0281 QPointF BlurEffect::deviation() const
0282 {
0283     return m_deviation;
0284 }
0285 
0286 void BlurEffect::setDeviation(const QPointF &deviation)
0287 {
0288     m_deviation.setX(qMax(qreal(0.0), deviation.x()));
0289     m_deviation.setY(qMax(qreal(0.0), deviation.y()));
0290 }
0291 
0292 QImage BlurEffect::processImage(const QImage &image, const KoFilterEffectRenderContext &context) const
0293 {
0294     if (m_deviation.x() == 0.0 || m_deviation.y() == 0.0)
0295         return image;
0296 
0297     // TODO: take filter region into account
0298     // TODO: blur with different kernels in x and y
0299     // convert from bounding box coordinates
0300     QPointF dev = context.toUserSpace(m_deviation);
0301     // transform to view coordinates
0302     dev = context.viewConverter()->documentToView(dev);
0303 
0304     QImage result = image;
0305     fastbluralpha(result, dev.x());
0306 
0307     return result;
0308 }
0309 
0310 bool BlurEffect::load(const KoXmlElement &element, const KoFilterEffectLoadingContext &context)
0311 {
0312     if (element.tagName() != id())
0313         return false;
0314 
0315     QString deviationStr = element.attribute("stdDeviation");
0316     QStringList params = deviationStr.replace(',', ' ').simplified().split(' ');
0317 
0318     switch (params.count()) {
0319     case 1:
0320         m_deviation.rx() = params[0].toDouble();
0321         m_deviation.ry() = m_deviation.x();
0322         break;
0323     case 2:
0324         m_deviation.rx() = params[0].toDouble();
0325         m_deviation.ry() = params[1].toDouble();
0326         break;
0327     default:
0328         return false;
0329     }
0330 
0331     m_deviation = context.convertFilterPrimitiveUnits(m_deviation);
0332 
0333     return true;
0334 }
0335 
0336 void BlurEffect::save(KoXmlWriter &writer)
0337 {
0338     writer.startElement(BlurEffectId);
0339 
0340     saveCommonAttributes(writer);
0341 
0342     if (m_deviation.x() != m_deviation.y()) {
0343         writer.addAttribute("stdDeviation", QString("%1, %2").arg(m_deviation.x()).arg(m_deviation.y()));
0344     } else {
0345         writer.addAttribute("stdDeviation", m_deviation.x());
0346     }
0347 
0348     writer.endElement();
0349 }