2 Copyright (c) 2006 Maurizio Monge <maurizio.monge@kdemail.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 Part of the code been contributed by Jani Huhtanen.
10 Copyright (c) 2006 Jani Huhtanen.
16 #include "imageeffects.h"
24 #if defined(__x86_64__) || defined(HAVE_X86_SSE)
26 #include <xmmintrin.h>
28 #endif //defined(__x86_64__) || defined(HAVE_X86_SSE)
33 template<int aprec
, int zprec
>
34 static inline void blurinner(unsigned char *bptr
, int &zR
,
35 int &zG
, int &zB
, int &zA
, int alpha
);
37 template<int aprec
,int zprec
>
38 static inline void blurrow(QImage
& im
, int line
, int alpha
);
40 template<int aprec
, int zprec
>
41 static inline void blurcol(QImage
& im
, int col
, int alpha
);
44 * expblur(QImage &img, int radius)
46 * In-place blur of image 'img' with kernel
47 * of approximate radius 'radius'.
49 * Blurs with two sided exponential impulse
52 * aprec = precision of alpha parameter
53 * in fixed-point format 0.aprec
55 * zprec = precision of state parameters
56 * zR,zG,zB and zA in fp format 8.zprec
58 template<int aprec
,int zprec
>
59 static void expblur(QImage
&img
, int radius
)
64 /* Calculate the alpha such that 90% of
65 the kernel is within the radius.
66 (Kernel extends to infinity)
68 int alpha
= (int)((1<<aprec
)*(1.0f
-expf(-2.3f
/(radius
+1.f
))));
70 for(int row
=0;row
<img
.height();row
++) {
71 blurrow
<aprec
,zprec
>(img
,row
,alpha
);
74 for(int col
=0;col
<img
.width();col
++) {
75 blurcol
<aprec
,zprec
>(img
,col
,alpha
);
80 template<int aprec
, int zprec
>
81 static inline void blurinner(unsigned char *bptr
,
82 int &zR
, int &zG
, int &zB
, int &zA
, int alpha
)
90 zR
+= (alpha
* ((R
<<zprec
)-zR
))>>aprec
;
91 zG
+= (alpha
* ((G
<<zprec
)-zG
))>>aprec
;
92 zB
+= (alpha
* ((B
<<zprec
)-zB
))>>aprec
;
93 zA
+= (alpha
* ((A
<<zprec
)-zA
))>>aprec
;
96 *(bptr
+1) = zG
>>zprec
;
97 *(bptr
+2) = zB
>>zprec
;
98 *(bptr
+3) = zA
>>zprec
;
101 template<int aprec
,int zprec
>
102 static inline void blurrow( QImage
& im
, int line
, int alpha
)
106 QRgb
*ptr
= (QRgb
*)im
.scanLine(line
);
108 zR
= *((unsigned char *)ptr
)<<zprec
;
109 zG
= *((unsigned char *)ptr
+ 1)<<zprec
;
110 zB
= *((unsigned char *)ptr
+ 2)<<zprec
;
111 zA
= *((unsigned char *)ptr
+ 3)<<zprec
;
113 for(int index
=1; index
<im
.width(); index
++) {
114 blurinner
<aprec
,zprec
>((unsigned char *)&ptr
[index
],
115 zR
, zG
, zB
, zA
, alpha
);
117 for(int index
=im
.width()-2; index
>=0; index
--) {
118 blurinner
<aprec
,zprec
>((unsigned char *)&ptr
[index
],
119 zR
, zG
, zB
, zA
, alpha
);
125 template<int aprec
, int zprec
>
126 static inline void blurcol(QImage
& im
, int col
, int alpha
)
130 QRgb
*ptr
= (QRgb
*)im
.bits();
133 zR
= *((unsigned char *)ptr
)<<zprec
;
134 zG
= *((unsigned char *)ptr
+ 1)<<zprec
;
135 zB
= *((unsigned char *)ptr
+ 2)<<zprec
;
136 zA
= *((unsigned char *)ptr
+ 3)<<zprec
;
138 for(int index
=im
.width(); index
<(im
.height()-1)*im
.width();
140 blurinner
<aprec
,zprec
>((unsigned char *)&ptr
[index
],
141 zR
, zG
, zB
, zA
, alpha
);
144 for(int index
=(im
.height()-2)*im
.width(); index
>=0;
146 blurinner
<aprec
,zprec
>((unsigned char *)&ptr
[index
],
147 zR
, zG
, zB
, zA
, alpha
);
156 #if defined(HAVE_X86_SSE) || defined(__x86_64__)
164 static inline void blur_sse_near(void* pixels
, __m128i
& state
, __m128i alpha
)
171 "movq %[ppix], %[pixels]\n"
172 "punpcklbw %[pixels], %[aux1]\n" // unpack two pixels setting their bytes
173 // as the most significant in the corr. word
174 "psrlw $1, %[aux1]\n" // shift right by 1, i.e. shift the colour
176 "psubw %[state], %[aux1]\n" // - state
177 "pmulhw %[alpha], %[aux1]\n" // * alpha, and take the 16 most significant bits
178 "psllw $1, %[aux1]\n" // shift left (we trade 1 bit for performance, here)
179 "paddw %[aux1], %[state]\n" // sum result to state
180 "movdqa %[state], %[aux2]\n" // copy state to the aux2 register
181 "psrlw $7, %[aux2]\n" // shift right by 7: this is the new pixel value
182 "packuswb %[aux2], %[aux2]\n" // pack pixels as 8 bits
183 "movq %[aux2], %[ppix]\n"
184 : [state
] "+x"(state
)
185 , [ppix
] "+m"(*(uint64_t*)pixels
)
193 static inline void blur_sse_sep(void* pixel1
, void* pixel2
, __m128i
& state
, __m128i alpha
)
201 "movd %[ppix1], %[pixels]\n" // load the first pixel
202 "movd %[ppix2], %[tmp]\n" // load the second pixel in [tmp]
203 "pslldq $4, %[tmp]\n" // shift left the second pixel
204 "paddd %[tmp], %[pixels]\n" // now both pixel are packed in [pixels]
206 "punpcklbw %[pixels], %[aux1]\n" // unpack two pixels setting their bytes
207 // as the most significant in the corr. word
208 "psrlw $1, %[aux1]\n" // shift right by 1, i.e. shift the colour
210 "psubw %[state], %[aux1]\n" // - state
211 "pmulhw %[alpha], %[aux1]\n" // * alpha, and take the 16 most significant bits
212 "psllw $1, %[aux1]\n" // shift left (we trade 1 bit for performance, here)
213 "paddw %[aux1], %[state]\n" // sum result to state
214 "movdqa %[state], %[aux2]\n" // copy state to the aux2 register
215 "psrlw $7, %[aux2]\n" // shift right by 7: this is the new pixel value
216 "packuswb %[aux2], %[aux2]\n" // pack pixels as 8 bits
218 "movd %[aux2], %[ppix1]\n"
219 "psrldq $4, %[aux2]\n"
220 "movd %[aux2], %[ppix2]\n"
221 : [state
] "+x"(state
)
222 , [ppix1
] "+m"(*(uint32_t*)pixel1
)
223 , [ppix2
] "+m"(*(uint32_t*)pixel2
)
232 static void expblur_sse( QImage
&img
, int radius
)
237 /* Calculate the alpha such that 90% of
238 the kernel is within the radius.
239 (Kernel extends to infinity)
241 uint16_t alpha
= (uint16_t)((1<<15)*(1.0f
-expf(-2.3f
/(radius
+1.f
))));
244 QRgb
*ptr
= (QRgb
*)img
.bits();
245 int h
= img
.height();
247 int hw
= (img
.height()-1)*img
.width();
251 for(int row
=0;row
<h
-1;row
+=2)
254 uint8_t *cptr
= (uint8_t*)(ptr
+row
*w
);
258 z
.i
[4+i
] = cptr
[w
*4+i
]<<7;
260 for(int index
=1; index
<w
; index
++)
261 blur_sse_sep(&cptr
[index
*4], &cptr
[(index
+w
)*4], z
.v
, a
.v
);
263 for(int index
=w
-2; index
>=0; index
--)
264 blur_sse_sep(&cptr
[index
*4], &cptr
[(index
+w
)*4] , z
.v
, a
.v
);
271 uint8_t *cptr
= (uint8_t*)(ptr
+(h
-1)*w
);
275 for(int index
=1; index
<w
; index
++)
276 blur_sse_sep(&cptr
[index
*4], &dummy
, z
.v
, a
.v
);
278 for(int index
=w
-2; index
>=0; index
--)
279 blur_sse_sep(&cptr
[index
*4], &dummy
, z
.v
, a
.v
);
282 for(int col
=0;col
<w
-1;col
+=2)
285 uint8_t *cptr
= (uint8_t*)(ptr
+col
);
290 for(int index
=w
; index
<hw
; index
+=w
)
291 blur_sse_near(&cptr
[index
*4], z
.v
, a
.v
);
293 for(int index
=hw
-2*w
; index
>=0; index
-=w
)
294 blur_sse_near(&cptr
[index
*4], z
.v
, a
.v
);
301 uint8_t *cptr
= (uint8_t*)(ptr
+w
-1);
306 for(int index
=w
; index
<hw
; index
+=w
)
307 blur_sse_sep(&cptr
[index
*4], &dummy
, z
.v
, a
.v
);
309 for(int index
=hw
-w
; index
>=0; index
-=w
)
310 blur_sse_sep(&cptr
[index
*4], &dummy
, z
.v
, a
.v
);
315 #endif //defined(HAVE_X86_SSE) || defined(__x86_64__)
327 static inline void blur_mmx(void *px
, __m64
& v
, __m64
& alpha
)
332 "movd %[pixel], %[t1]\n"
333 "punpcklbw %[t1], %[t2]\n"
335 "psubw %[accum], %[t2]\n"
336 "pmulhw %[alpha], %[t2]\n"
338 "paddw %[t2], %[accum]\n"
339 "movq %[accum], %[t1]\n"
341 // "pand %[mask], %[t1]\n"
342 "packuswb %[t1], %[t1]\n"
343 "movd %[t1], %[pixel]\n"
344 : [pixel
] "+m"(*(uint32_t*)px
)
349 // , [mask] "y"(0x00ff00ff00ff00ffULL)
353 static void expblur_mmx( QImage
&img
, int radius
)
358 /* Calculate the alpha such that 90% of
359 the kernel is within the radius.
360 (Kernel extends to infinity)
362 uint16_t alpha
= (uint16_t)((1<<15)*(1.0f
-expf(-2.3f
/(radius
+1.f
))));
365 QRgb
*ptr
= (QRgb
*)img
.bits();
366 int h
= img
.height();
368 int hw
= (img
.height()-1)*img
.width();
372 for(int row
=0;row
<h
;row
++)
375 uint8_t *cptr
= (uint8_t*)(ptr
+row
*w
);
379 for(int index
=1; index
<w
; index
++)
380 blur_mmx(&cptr
[index
*4], z
.v
, a
.v
);
382 for(int index
=w
-2; index
>=0; index
--)
383 blur_mmx(&cptr
[index
*4], z
.v
, a
.v
);
386 for(int col
=0;col
<w
;col
++)
389 uint8_t *cptr
= (uint8_t*)(ptr
+col
);
394 for(int index
=w
; index
<hw
; index
+=w
)
395 blur_mmx(&cptr
[index
*4], z
.v
, a
.v
);
397 for(int index
=hw
-w
; index
>=0; index
-=w
)
398 blur_mmx(&cptr
[index
*4], z
.v
, a
.v
);
404 #endif //HAVE_X86_MMX
409 namespace ImageEffects
{
411 void expBlur(QImage
& img
, int radius
) {
413 return expblur_sse(img
, radius
);
416 if(KCPUInfo::haveExtension( KCPUInfo::IntelSSE
) )
417 return expblur_sse(img
, radius
);
418 #endif //HAVE_X86_SSE
420 if(KCPUInfo::haveExtension( KCPUInfo::IntelMMX
) )
421 return expblur_mmx(img
, radius
);
422 #endif //HAVE_X86_MMX
423 return expblur
<15,7>(img
, radius
);
427 QImage
addShadow(const QImage
& image
, int r
, QColor color
,
428 int offx
, int offy
, int growx
, int growy
) {
430 QImage
retv(image
.width()+growx
, image
.height()+growy
, QImage::Format_ARGB32_Premultiplied
);
431 int dx
= (growx
-offx
)/2, dy
= (growy
-offy
)/2;
434 p
.setCompositionMode(QPainter::CompositionMode_Source
);
435 p
.fillRect(0,0,retv
.width(), retv
.height(), QColor(0,0,0,0));
436 p
.fillRect(dx
+offx
, dy
+offy
, image
.width(), image
.height(), color
);
437 p
.setCompositionMode(QPainter::CompositionMode_DestinationAtop
);
438 p
.drawImage(dx
+offx
, dy
+offy
, image
);
444 p
.drawImage(dx
, dy
, image
);
450 QImage
growBorder(const QImage
& image
) {
451 int w
= image
.width();
452 int h
= image
.height();
454 QImage
retv(w
+2, h
+2, QImage::Format_ARGB32_Premultiplied
);
457 p
.setCompositionMode(QPainter::CompositionMode_Source
);
458 p
.drawImage(0, 0, image
, 0, 0, 1, 1);
459 p
.drawImage(w
+1, 0, image
, w
-1, 0, 1, 1);
460 p
.drawImage(0, h
+1, image
, 0, h
-1, 1, 1);
461 p
.drawImage(w
+1, h
+1, image
, w
-1, h
-1, 1, 1);
462 p
.drawImage(1, 0, image
, 0, 0, w
, 1);
463 p
.drawImage(1, h
+1, image
, 0, h
-1, w
, 1);
464 p
.drawImage(0, 1, image
, 0, 0, 1, h
);
465 p
.drawImage(w
+1, 1, image
, w
-1, 0, 1, h
);
466 p
.drawImage(1, 1, image
);
475 Line(int _y
, int _x1
, int _x2
)
476 : y(_y
), x1(_x1
), x2(_x2
) {}
479 void floodFill(QImage
& image
, QPoint point
, QColor color
,
480 bool invade_border
, std::vector
<QPoint
>* border
) {
482 int* ptr
= (int*)image
.bits();
483 int h
= image
.height();
484 int w
= image
.width();
485 int newcol
= color
.rgba();
486 int oldcol
= ptr
[point
.x()+point
.y()*w
];
487 std::vector
<Line
> lines
;
490 Line
l(point
.y(), point
.x(), point
.x()+1);
491 int *scanline
= ptr
+point
.y()*w
;
492 scanline
[l
.x1
] = newcol
;
493 while(l
.x1
> 0 && scanline
[l
.x1
-1] == oldcol
)
494 scanline
[--l
.x1
] = newcol
;
495 while(l
.x2
< w
&& scanline
[l
.x2
] == oldcol
)
496 scanline
[l
.x2
++] = newcol
;
500 while(!lines
.empty()) {
501 Line ll
= lines
[lines
.size()-1];
506 ptr
[ll
.y
*w
+ ll
.x1
- 1] = newcol
;
508 border
->push_back(QPoint(ll
.x1
-1, ll
.y
));
512 ptr
[ll
.y
*w
+ ll
.x2
] = newcol
;
514 border
->push_back(QPoint(ll
.x2
, ll
.y
));
517 for(int d
=-1; d
<=1; d
+=2)
518 if( (d
== -1) ? (ll
.y
> 0) : (ll
.y
< h
-1) ) {
519 int *scanline
= ptr
+ (ll
.y
+d
)*w
;
521 for(int i
=ll
.x1
;i
<ll
.x2
;i
++){
522 if(scanline
[i
]==oldcol
) {
523 Line
l(ll
.y
+d
, i
, i
+1);
525 scanline
[l
.x1
] = newcol
;
526 while(l
.x1
> 0 && scanline
[l
.x1
-1] == oldcol
)
527 scanline
[--l
.x1
] = newcol
;
528 while(l
.x2
< w
&& scanline
[l
.x2
] == oldcol
)
529 scanline
[l
.x2
++] = newcol
;
533 if(i
<ll
.x2
&& scanline
[i
]!=newcol
) {
537 border
->push_back(QPoint(i
, ll
.y
+d
));
544 void floodFillBlueThreshold(QImage
& image
, QPoint point
, QColor color
, unsigned int thresh
,
545 bool invade_border
, std::vector
<QPoint
>* border
) {
547 unsigned int* ptr
= (unsigned int*)image
.bits();
548 int h
= image
.height();
549 int w
= image
.width();
550 unsigned int newcol
= color
.rgba();
551 std::vector
<Line
> lines
;
553 #define TEST(x) ((((x) & 0xff) < thresh) && ((x) != newcol))
555 Line
l(point
.y(), point
.x(), point
.x()+1);
556 unsigned int *scanline
= ptr
+point
.y()*w
;
557 scanline
[l
.x1
] = newcol
;
558 while(l
.x1
> 0 && TEST(scanline
[l
.x1
-1]))
559 scanline
[--l
.x1
] = newcol
;
560 while(l
.x2
< w
&& TEST(scanline
[l
.x2
]))
561 scanline
[l
.x2
++] = newcol
;
565 while(!lines
.empty()) {
566 Line ll
= lines
[lines
.size()-1];
571 ptr
[ll
.y
*w
+ ll
.x1
- 1] = newcol
;
573 border
->push_back(QPoint(ll
.x1
-1, ll
.y
));
577 ptr
[ll
.y
*w
+ ll
.x2
] = newcol
;
579 border
->push_back(QPoint(ll
.x2
, ll
.y
));
582 for(int d
=-1; d
<=1; d
+=2)
583 if( (d
== -1) ? (ll
.y
> 0) : (ll
.y
< h
-1) ) {
584 unsigned int *scanline
= ptr
+ (ll
.y
+d
)*w
;
586 for(int i
=ll
.x1
;i
<ll
.x2
;i
++){
587 if(TEST(scanline
[i
])) {
588 Line
l(ll
.y
+d
, i
, i
+1);
590 scanline
[l
.x1
] = newcol
;
591 while(l
.x1
> 0 && TEST(scanline
[l
.x1
-1]))
592 scanline
[--l
.x1
] = newcol
;
593 while(l
.x2
< w
&& TEST(scanline
[l
.x2
]))
594 scanline
[l
.x2
++] = newcol
;
598 if(i
<ll
.x2
&& scanline
[i
]!=newcol
) {
602 border
->push_back(QPoint(i
, ll
.y
+d
));