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 }