]> cloud.milkyroute.net Git - dolphin.git/blob - src/kitemviews/private/kpixmapmodifier.cpp
3e0edf02c48eef2a5126ed29a88ee91807b82f96
[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.setDevicePixelRatio(pixmap.devicePixelRatio());
351 scaledPixmap.fill(Qt::transparent);
352
353 Display* dpy = QX11Info::display();
354
355 XRenderPictureAttributes attr;
356 attr.repeat = RepeatPad;
357 XRenderChangePicture(dpy, unscaledPixmap.x11PictureHandle(), CPRepeat, &attr);
358
359 XRenderSetPictureFilter(dpy, unscaledPixmap.x11PictureHandle(), FilterBilinear, 0, 0);
360 XRenderSetPictureTransform(dpy, unscaledPixmap.x11PictureHandle(), &xform);
361 XRenderComposite(dpy, PictOpOver, unscaledPixmap.x11PictureHandle(), None, scaledPixmap.x11PictureHandle(),
362 0, 0, 0, 0, 0, 0, scaledPixmap.width(), scaledPixmap.height());
363 pixmap = scaledPixmap;
364 } else {
365 pixmap = pixmap.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
366 }
367 #else
368 qreal dpr = pixmap.devicePixelRatio();
369 pixmap = pixmap.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
370 pixmap.setDevicePixelRatio(dpr);
371 #endif
372 }
373
374 void KPixmapModifier::applyFrame(QPixmap& icon, const QSize& scaledSize)
375 {
376 static TileSet tileSet;
377 qreal dpr = icon.devicePixelRatio();
378
379 // Resize the icon to the maximum size minus the space required for the frame
380 const QSize size(scaledSize.width() - (TileSet::LeftMargin + TileSet::RightMargin) * dpr,
381 scaledSize.height() - (TileSet::TopMargin + TileSet::BottomMargin) * dpr);
382 scale(icon, size);
383
384 QPixmap framedIcon(icon.size().width() + (TileSet::LeftMargin + TileSet::RightMargin) * dpr,
385 icon.size().height() + (TileSet::TopMargin + TileSet::BottomMargin * dpr) );
386 framedIcon.setDevicePixelRatio(dpr);
387 framedIcon.fill(Qt::transparent);
388
389 QPainter painter;
390 painter.begin(&framedIcon);
391 painter.setCompositionMode(QPainter::CompositionMode_Source);
392 tileSet.paint(&painter, framedIcon.rect());
393 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
394 painter.drawPixmap(TileSet::LeftMargin, TileSet::TopMargin, icon);
395
396 icon = framedIcon;
397 }
398
399 QSize KPixmapModifier::sizeInsideFrame(const QSize& frameSize)
400 {
401 return QSize(frameSize.width() - TileSet::LeftMargin - TileSet::RightMargin,
402 frameSize.height() - TileSet::TopMargin - TileSet::BottomMargin);
403 }
404