1 // krazy:excludeall=copyright (email of Maxim is missing)
3 This file is a part of the KDE project
5 Copyright © 2006 Zack Rusin <zack@kde.org>
6 Copyright © 2006-2007, 2008 Fredrik Höglund <fredrik@kde.org>
8 The stack blur algorithm was invented by Mario Klingemann <mario@quasimondo.com>
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)
13 Redistribution and use in source and binary forms, with or without
14 modification, are permitted provided that the following conditions
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.
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.
35 #include "kpixmapmodifier.h"
43 #include <config-X11.h> // for HAVE_XRENDER
44 #if defined(Q_WS_X11) && defined(HAVE_XRENDER)
46 # include <X11/Xlib.h>
47 # include <X11/extensions/Xrender.h>
50 static const quint32 stackBlur8Mul
[255] =
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
70 static const quint32 stackBlur8Shr
[255] =
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
90 static void blurHorizontal(QImage
& image
, unsigned int* stack
, int div
, int radius
)
95 quint32
* const pixels
= reinterpret_cast<quint32
*>(image
.bits());
98 int w
= image
.width();
99 int h
= image
.height();
102 unsigned int mulSum
= stackBlur8Mul
[radius
];
103 unsigned int shrSum
= stackBlur8Shr
[radius
];
105 unsigned int sum
, sumIn
, sumOut
;
107 for (int y
= 0; y
< h
; y
++) {
112 const int yw
= y
* w
;
114 for (int i
= 0; i
<= radius
; i
++) {
115 stack
[i
] = qAlpha(pixel
);
117 sum
+= stack
[i
] * (i
+ 1);
121 for (int i
= 1; i
<= radius
; i
++) {
122 pixel
= pixels
[yw
+ qMin(i
, wm
)];
124 unsigned int* stackpix
= &stack
[i
+ radius
];
125 *stackpix
= qAlpha(pixel
);
127 sum
+= *stackpix
* (radius
+ 1 - i
);
132 for (int x
= 0, i
= yw
; x
< w
; x
++) {
133 pixels
[i
++] = (((sum
* mulSum
) >> shrSum
) << 24) & 0xff000000;
137 stackstart
= stackindex
+ div
- radius
;
138 if (stackstart
>= div
) {
142 unsigned int* stackpix
= &stack
[stackstart
];
146 pixel
= pixels
[yw
+ qMin(x
+ radius
+ 1, wm
)];
148 *stackpix
= qAlpha(pixel
);
153 if (++stackindex
>= div
) {
157 stackpix
= &stack
[stackindex
];
165 static void blurVertical(QImage
& image
, unsigned int* stack
, int div
, int radius
)
170 quint32
* const pixels
= reinterpret_cast<quint32
*>(image
.bits());
173 int w
= image
.width();
174 int h
= image
.height();
177 int mul_sum
= stackBlur8Mul
[radius
];
178 int shr_sum
= stackBlur8Shr
[radius
];
180 unsigned int sum
, sumIn
, sumOut
;
182 for (int x
= 0; x
< w
; x
++) {
188 for (int i
= 0; i
<= radius
; i
++) {
189 stack
[i
] = qAlpha(pixel
);
191 sum
+= stack
[i
] * (i
+ 1);
195 for (int i
= 1; i
<= radius
; i
++) {
196 pixel
= pixels
[qMin(i
, hm
) * w
+ x
];
198 unsigned int* stackpix
= &stack
[i
+ radius
];
199 *stackpix
= qAlpha(pixel
);
201 sum
+= *stackpix
* (radius
+ 1 - i
);
206 for (int y
= 0, i
= x
; y
< h
; y
++, i
+= w
) {
207 pixels
[i
] = (((sum
* mul_sum
) >> shr_sum
) << 24) & 0xff000000;
211 stackstart
= stackindex
+ div
- radius
;
212 if (stackstart
>= div
)
215 unsigned int* stackpix
= &stack
[stackstart
];
219 pixel
= pixels
[qMin(y
+ radius
+ 1, hm
) * w
+ x
];
221 *stackpix
= qAlpha(pixel
);
226 if (++stackindex
>= div
) {
230 stackpix
= &stack
[stackindex
];
238 static void stackBlur(QImage
& image
, float radius
)
240 radius
= qRound(radius
);
242 int div
= int(radius
* 2) + 1;
243 unsigned int* stack
= new unsigned int[div
];
245 blurHorizontal(image
, stack
, div
, radius
);
246 blurVertical(image
, stack
, div
, radius
);
251 static void shadowBlur(QImage
& image
, float radius
, const QColor
& color
)
258 stackBlur(image
, radius
);
261 // Correct the color and opacity of the shadow
263 p
.setCompositionMode(QPainter::CompositionMode_SourceIn
);
264 p
.fillRect(image
.rect(), color
);
268 /** Helper class for drawing frames for KPixmapModifier::applyFrame(). */
272 enum { LeftMargin
= 3, TopMargin
= 2, RightMargin
= 3, BottomMargin
= 4 };
274 enum Tile
{ TopLeftCorner
= 0, TopSide
, TopRightCorner
, LeftSide
,
275 RightSide
, BottomLeftCorner
, BottomSide
, BottomRightCorner
,
280 QImage
image(8 * 3, 8 * 3, QImage::Format_ARGB32_Premultiplied
);
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
);
288 shadowBlur(image
, 3, Qt::black
);
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);
301 void paint(QPainter
* p
, const QRect
& r
)
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
]);
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
]);
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
]);
316 p
->drawPixmap(r
.right() - 8 + 1, r
.bottom() - 8 + 1, m_tiles
[BottomRightCorner
]);
318 const QRect contentRect
= r
.adjusted(LeftMargin
+ 1, TopMargin
+ 1,
319 -(RightMargin
+ 1), -(BottomMargin
+ 1));
320 p
->fillRect(contentRect
, Qt::transparent
);
323 QPixmap m_tiles
[NumTiles
];
327 void KPixmapModifier::scale(QPixmap
& pixmap
, const QSize
& scaledSize
)
329 if (scaledSize
.isEmpty()) {
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
);
341 const qreal factor
= scaledPixmapSize
.width() / qreal(unscaledPixmap
.width());
343 XTransform xform
= {{
344 { XDoubleToFixed(1 / factor
), 0, 0 },
345 { 0, XDoubleToFixed(1 / factor
), 0 },
346 { 0, 0, XDoubleToFixed(1) }
349 QPixmap
scaledPixmap(scaledPixmapSize
);
350 scaledPixmap
.setDevicePixelRatio(pixmap
.devicePixelRatio());
351 scaledPixmap
.fill(Qt::transparent
);
353 Display
* dpy
= QX11Info::display();
355 XRenderPictureAttributes attr
;
356 attr
.repeat
= RepeatPad
;
357 XRenderChangePicture(dpy
, unscaledPixmap
.x11PictureHandle(), CPRepeat
, &attr
);
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
;
365 pixmap
= pixmap
.scaled(scaledSize
, Qt::KeepAspectRatio
, Qt::SmoothTransformation
);
368 qreal dpr
= pixmap
.devicePixelRatio();
369 pixmap
= pixmap
.scaled(scaledSize
, Qt::KeepAspectRatio
, Qt::SmoothTransformation
);
370 pixmap
.setDevicePixelRatio(dpr
);
374 void KPixmapModifier::applyFrame(QPixmap
& icon
, const QSize
& scaledSize
)
376 static TileSet tileSet
;
377 qreal dpr
= icon
.devicePixelRatio();
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
);
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
);
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
);
399 QSize
KPixmapModifier::sizeInsideFrame(const QSize
& frameSize
)
401 return QSize(frameSize
.width() - TileSet::LeftMargin
- TileSet::RightMargin
,
402 frameSize
.height() - TileSet::TopMargin
- TileSet::BottomMargin
);