]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/private/kpixmapmodifier.cpp
Merge branch 'master' into frameworks
[dolphin.git] / src / kitemviews / private / kpixmapmodifier.cpp
1 // krazy:excludeall=copyright (email of Maxim is missing)
2 /*
3 This file is a part of the KDE project
4
5 Copyright © 2006 Zack Rusin <zack@kde.org>
6 Copyright © 2006-2007, 2008 Fredrik Höglund <fredrik@kde.org>
7
8 The stack blur algorithm was invented by Mario Klingemann <mario@quasimondo.com>
9
10 This implementation is based on the version in Anti-Grain Geometry Version 2.4,
11 Copyright © 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
12
13 Redistribution and use in source and binary forms, with or without
14 modification, are permitted provided that the following conditions
15 are met:
16
17 1. Redistributions of source code must retain the above copyright
18 notice, this list of conditions and the following disclaimer.
19 2. Redistributions in binary form must reproduce the above copyright
20 notice, this list of conditions and the following disclaimer in the
21 documentation and/or other materials provided with the distribution.
22
23 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include "kpixmapmodifier.h"
36
37 #include <QImage>
38 #include <QPainter>
39 #include <QPixmap>
40 #include <QSize>
41
42
43 #include <config-X11.h> // for HAVE_XRENDER
44 #if defined(Q_WS_X11) && defined(HAVE_XRENDER)
45 # include <QX11Info>
46 # include <X11/Xlib.h>
47 # include <X11/extensions/Xrender.h>
48 #endif
49
50 static const quint32 stackBlur8Mul[255] =
51 {
52 512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,
53 454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,
54 482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,
55 437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,
56 497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,
57 320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,
58 446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,
59 329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,
60 505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,
61 399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,
62 324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,
63 268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,
64 451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,
65 385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,
66 332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,
67 289,287,285,282,280,278,275,273,271,269,267,265,263,261,259
68 };
69
70 static const quint32 stackBlur8Shr[255] =
71 {
72 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
73 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
74 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
75 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
76 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
77 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
78 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
79 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
80 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
81 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
82 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
83 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
84 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
85 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
86 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
87 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
88 };
89
90 static void blurHorizontal(QImage& image, unsigned int* stack, int div, int radius)
91 {
92 int stackindex;
93 int stackstart;
94
95 quint32 * const pixels = reinterpret_cast<quint32 *>(image.bits());
96 quint32 pixel;
97
98 int w = image.width();
99 int h = image.height();
100 int wm = w - 1;
101
102 unsigned int mulSum = stackBlur8Mul[radius];
103 unsigned int shrSum = stackBlur8Shr[radius];
104
105 unsigned int sum, sumIn, sumOut;
106
107 for (int y = 0; y < h; y++) {
108 sum = 0;
109 sumIn = 0;
110 sumOut = 0;
111
112 const int yw = y * w;
113 pixel = pixels[yw];
114 for (int i = 0; i <= radius; i++) {
115 stack[i] = qAlpha(pixel);
116
117 sum += stack[i] * (i + 1);
118 sumOut += stack[i];
119 }
120
121 for (int i = 1; i <= radius; i++) {
122 pixel = pixels[yw + qMin(i, wm)];
123
124 unsigned int* stackpix = &stack[i + radius];
125 *stackpix = qAlpha(pixel);
126
127 sum += *stackpix * (radius + 1 - i);
128 sumIn += *stackpix;
129 }
130
131 stackindex = radius;
132 for (int x = 0, i = yw; x < w; x++) {
133 pixels[i++] = (((sum * mulSum) >> shrSum) << 24) & 0xff000000;
134
135 sum -= sumOut;
136
137 stackstart = stackindex + div - radius;
138 if (stackstart >= div) {
139 stackstart -= div;
140 }
141
142 unsigned int* stackpix = &stack[stackstart];
143
144 sumOut -= *stackpix;
145
146 pixel = pixels[yw + qMin(x + radius + 1, wm)];
147
148 *stackpix = qAlpha(pixel);
149
150 sumIn += *stackpix;
151 sum += sumIn;
152
153 if (++stackindex >= div) {
154 stackindex = 0;
155 }
156
157 stackpix = &stack[stackindex];
158
159 sumOut += *stackpix;
160 sumIn -= *stackpix;
161 }
162 }
163 }
164
165 static void blurVertical(QImage& image, unsigned int* stack, int div, int radius)
166 {
167 int stackindex;
168 int stackstart;
169
170 quint32 * const pixels = reinterpret_cast<quint32 *>(image.bits());
171 quint32 pixel;
172
173 int w = image.width();
174 int h = image.height();
175 int hm = h - 1;
176
177 int mul_sum = stackBlur8Mul[radius];
178 int shr_sum = stackBlur8Shr[radius];
179
180 unsigned int sum, sumIn, sumOut;
181
182 for (int x = 0; x < w; x++) {
183 sum = 0;
184 sumIn = 0;
185 sumOut = 0;
186
187 pixel = pixels[x];
188 for (int i = 0; i <= radius; i++) {
189 stack[i] = qAlpha(pixel);
190
191 sum += stack[i] * (i + 1);
192 sumOut += stack[i];
193 }
194
195 for (int i = 1; i <= radius; i++) {
196 pixel = pixels[qMin(i, hm) * w + x];
197
198 unsigned int* stackpix = &stack[i + radius];
199 *stackpix = qAlpha(pixel);
200
201 sum += *stackpix * (radius + 1 - i);
202 sumIn += *stackpix;
203 }
204
205 stackindex = radius;
206 for (int y = 0, i = x; y < h; y++, i += w) {
207 pixels[i] = (((sum * mul_sum) >> shr_sum) << 24) & 0xff000000;
208
209 sum -= sumOut;
210
211 stackstart = stackindex + div - radius;
212 if (stackstart >= div)
213 stackstart -= div;
214
215 unsigned int* stackpix = &stack[stackstart];
216
217 sumOut -= *stackpix;
218
219 pixel = pixels[qMin(y + radius + 1, hm) * w + x];
220
221 *stackpix = qAlpha(pixel);
222
223 sumIn += *stackpix;
224 sum += sumIn;
225
226 if (++stackindex >= div) {
227 stackindex = 0;
228 }
229
230 stackpix = &stack[stackindex];
231
232 sumOut += *stackpix;
233 sumIn -= *stackpix;
234 }
235 }
236 }
237
238 static void stackBlur(QImage& image, float radius)
239 {
240 radius = qRound(radius);
241
242 int div = int(radius * 2) + 1;
243 unsigned int* stack = new unsigned int[div];
244
245 blurHorizontal(image, stack, div, radius);
246 blurVertical(image, stack, div, radius);
247
248 delete [] stack;
249 }
250
251 static void shadowBlur(QImage& image, float radius, const QColor& color)
252 {
253 if (radius < 0) {
254 return;
255 }
256
257 if (radius > 0) {
258 stackBlur(image, radius);
259 }
260
261 // Correct the color and opacity of the shadow
262 QPainter p(&image);
263 p.setCompositionMode(QPainter::CompositionMode_SourceIn);
264 p.fillRect(image.rect(), color);
265 }
266
267 namespace {
268 /** Helper class for drawing frames for KPixmapModifier::applyFrame(). */
269 class TileSet
270 {
271 public:
272 enum { LeftMargin = 3, TopMargin = 2, RightMargin = 3, BottomMargin = 4 };
273
274 enum Tile { TopLeftCorner = 0, TopSide, TopRightCorner, LeftSide,
275 RightSide, BottomLeftCorner, BottomSide, BottomRightCorner,
276 NumTiles };
277
278 TileSet()
279 {
280 QImage image(8 * 3, 8 * 3, QImage::Format_ARGB32_Premultiplied);
281
282 QPainter p(&image);
283 p.setCompositionMode(QPainter::CompositionMode_Source);
284 p.fillRect(image.rect(), Qt::transparent);
285 p.fillRect(image.rect().adjusted(3, 3, -3, -3), Qt::black);
286 p.end();
287
288 shadowBlur(image, 3, Qt::black);
289
290 QPixmap pixmap = QPixmap::fromImage(image);
291 m_tiles[TopLeftCorner] = pixmap.copy(0, 0, 8, 8);
292 m_tiles[TopSide] = pixmap.copy(8, 0, 8, 8);
293 m_tiles[TopRightCorner] = pixmap.copy(16, 0, 8, 8);
294 m_tiles[LeftSide] = pixmap.copy(0, 8, 8, 8);
295 m_tiles[RightSide] = pixmap.copy(16, 8, 8, 8);
296 m_tiles[BottomLeftCorner] = pixmap.copy(0, 16, 8, 8);
297 m_tiles[BottomSide] = pixmap.copy(8, 16, 8, 8);
298 m_tiles[BottomRightCorner] = pixmap.copy(16, 16, 8, 8);
299 }
300
301 void paint(QPainter* p, const QRect& r)
302 {
303 p->drawPixmap(r.topLeft(), m_tiles[TopLeftCorner]);
304 if (r.width() - 16 > 0) {
305 p->drawTiledPixmap(r.x() + 8, r.y(), r.width() - 16, 8, m_tiles[TopSide]);
306 }
307 p->drawPixmap(r.right() - 8 + 1, r.y(), m_tiles[TopRightCorner]);
308 if (r.height() - 16 > 0) {
309 p->drawTiledPixmap(r.x(), r.y() + 8, 8, r.height() - 16, m_tiles[LeftSide]);
310 p->drawTiledPixmap(r.right() - 8 + 1, r.y() + 8, 8, r.height() - 16, m_tiles[RightSide]);
311 }
312 p->drawPixmap(r.x(), r.bottom() - 8 + 1, m_tiles[BottomLeftCorner]);
313 if (r.width() - 16 > 0) {
314 p->drawTiledPixmap(r.x() + 8, r.bottom() - 8 + 1, r.width() - 16, 8, m_tiles[BottomSide]);
315 }
316 p->drawPixmap(r.right() - 8 + 1, r.bottom() - 8 + 1, m_tiles[BottomRightCorner]);
317
318 const QRect contentRect = r.adjusted(LeftMargin + 1, TopMargin + 1,
319 -(RightMargin + 1), -(BottomMargin + 1));
320 p->fillRect(contentRect, Qt::transparent);
321 }
322
323 QPixmap m_tiles[NumTiles];
324 };
325 }
326
327 void KPixmapModifier::scale(QPixmap& pixmap, const QSize& scaledSize)
328 {
329 if (scaledSize.isEmpty()) {
330 pixmap = QPixmap();
331 return;
332 }
333
334 #if defined(Q_WS_X11) && defined(HAVE_XRENDER)
335 // Assume that the texture size limit is 2048x2048
336 if ((pixmap.width() <= 2048) && (pixmap.height() <= 2048) && pixmap.x11PictureHandle()) {
337 const QPixmap unscaledPixmap = pixmap.copy(); // Make a deep copy for XRender
338 QSize scaledPixmapSize = pixmap.size();
339 scaledPixmapSize.scale(scaledSize, Qt::KeepAspectRatio);
340
341 const qreal factor = scaledPixmapSize.width() / qreal(unscaledPixmap.width());
342
343 XTransform xform = {{
344 { XDoubleToFixed(1 / factor), 0, 0 },
345 { 0, XDoubleToFixed(1 / factor), 0 },
346 { 0, 0, XDoubleToFixed(1) }
347 }};
348
349 QPixmap scaledPixmap(scaledPixmapSize);
350 scaledPixmap.fill(Qt::transparent);
351
352 Display* dpy = QX11Info::display();
353
354 XRenderPictureAttributes attr;
355 attr.repeat = RepeatPad;
356 XRenderChangePicture(dpy, unscaledPixmap.x11PictureHandle(), CPRepeat, &attr);
357
358 XRenderSetPictureFilter(dpy, unscaledPixmap.x11PictureHandle(), FilterBilinear, 0, 0);
359 XRenderSetPictureTransform(dpy, unscaledPixmap.x11PictureHandle(), &xform);
360 XRenderComposite(dpy, PictOpOver, unscaledPixmap.x11PictureHandle(), None, scaledPixmap.x11PictureHandle(),
361 0, 0, 0, 0, 0, 0, scaledPixmap.width(), scaledPixmap.height());
362 pixmap = scaledPixmap;
363 } else {
364 pixmap = pixmap.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
365 }
366 #else
367 pixmap = pixmap.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
368 #endif
369 }
370
371 void KPixmapModifier::applyFrame(QPixmap& icon, const QSize& scaledSize)
372 {
373 static TileSet tileSet;
374
375 // Resize the icon to the maximum size minus the space required for the frame
376 const QSize size(scaledSize.width() - TileSet::LeftMargin - TileSet::RightMargin,
377 scaledSize.height() - TileSet::TopMargin - TileSet::BottomMargin);
378 scale(icon, size);
379
380 QPixmap framedIcon(icon.size().width() + TileSet::LeftMargin + TileSet::RightMargin,
381 icon.size().height() + TileSet::TopMargin + TileSet::BottomMargin);
382 framedIcon.fill(Qt::transparent);
383
384 QPainter painter;
385 painter.begin(&framedIcon);
386 painter.setCompositionMode(QPainter::CompositionMode_Source);
387 tileSet.paint(&painter, framedIcon.rect());
388 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
389 painter.drawPixmap(TileSet::LeftMargin, TileSet::TopMargin, icon);
390
391 icon = framedIcon;
392 }
393
394 QSize KPixmapModifier::sizeInsideFrame(const QSize& frameSize)
395 {
396 return QSize(frameSize.width() - TileSet::LeftMargin - TileSet::RightMargin,
397 frameSize.height() - TileSet::TopMargin - TileSet::BottomMargin);
398 }
399