Allow building as monolithic app (no plugins) for easier developement.
[tagua/yd.git] / src / imageeffects.cpp
blob0887cb3344b8068d6d0cd3c78c93982f3ce42cc1
1 /*
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.
14 #include <cmath>
15 #include <QPainter>
16 #include <blitzcpu.h>
17 #include "imageeffects.h"
19 template<int aprec, int zprec>
20 static inline void blurinner(unsigned char *bptr, int &zR,
21 int &zG, int &zB, int &zA, int alpha);
23 template<int aprec,int zprec>
24 static inline void blurrow(QImage & im, int line, int alpha);
26 template<int aprec, int zprec>
27 static inline void blurcol(QImage & im, int col, int alpha);
30 * expblur(QImage &img, int radius)
32 * In-place blur of image 'img' with kernel
33 * of approximate radius 'radius'.
35 * Blurs with two sided exponential impulse
36 * response.
38 * aprec = precision of alpha parameter
39 * in fixed-point format 0.aprec
41 * zprec = precision of state parameters
42 * zR,zG,zB and zA in fp format 8.zprec
44 template<int aprec,int zprec>
45 static void expblur(QImage &img, int radius )
47 if (radius < 1)
48 return;
50 /* Calculate the alpha such that 90% of
51 the kernel is within the radius.
52 (Kernel extends to infinity)
54 int alpha = (int)((1<<aprec)*(1.0f-expf(-2.3f/(radius+1.f))));
56 for(int row=0;row<img.height();row++) {
57 blurrow<aprec,zprec>(img,row,alpha);
60 for(int col=0;col<img.width();col++) {
61 blurcol<aprec,zprec>(img,col,alpha);
63 return;
66 template<int aprec, int zprec>
67 static inline void blurinner(unsigned char *bptr,
68 int &zR, int &zG, int &zB, int &zA, int alpha)
70 int R,G,B,A;
71 R = *bptr;
72 G = *(bptr+1);
73 B = *(bptr+2);
74 A = *(bptr+3);
76 zR += (alpha * ((R<<zprec)-zR))>>aprec;
77 zG += (alpha * ((G<<zprec)-zG))>>aprec;
78 zB += (alpha * ((B<<zprec)-zB))>>aprec;
79 zA += (alpha * ((A<<zprec)-zA))>>aprec;
81 *bptr = zR>>zprec;
82 *(bptr+1) = zG>>zprec;
83 *(bptr+2) = zB>>zprec;
84 *(bptr+3) = zA>>zprec;
87 template<int aprec,int zprec>
88 static inline void blurrow( QImage & im, int line, int alpha)
90 int zR,zG,zB,zA;
92 QRgb *ptr = (QRgb *)im.scanLine(line);
94 zR = *((unsigned char *)ptr )<<zprec;
95 zG = *((unsigned char *)ptr + 1)<<zprec;
96 zB = *((unsigned char *)ptr + 2)<<zprec;
97 zA = *((unsigned char *)ptr + 3)<<zprec;
99 for(int index=1; index<im.width(); index++) {
100 blurinner<aprec,zprec>((unsigned char *)&ptr[index],
101 zR, zG, zB, zA, alpha);
103 for(int index=im.width()-2; index>=0; index--) {
104 blurinner<aprec,zprec>((unsigned char *)&ptr[index],
105 zR, zG, zB, zA, alpha);
111 template<int aprec, int zprec>
112 static inline void blurcol(QImage & im, int col, int alpha)
114 int zR,zG,zB,zA;
116 QRgb *ptr = (QRgb *)im.bits();
117 ptr+=col;
119 zR = *((unsigned char *)ptr )<<zprec;
120 zG = *((unsigned char *)ptr + 1)<<zprec;
121 zB = *((unsigned char *)ptr + 2)<<zprec;
122 zA = *((unsigned char *)ptr + 3)<<zprec;
124 for(int index=im.width(); index<(im.height()-1)*im.width();
125 index+=im.width()) {
126 blurinner<aprec,zprec>((unsigned char *)&ptr[index],
127 zR, zG, zB, zA, alpha);
130 for(int index=(im.height()-2)*im.width(); index>=0;
131 index-=im.width()) {
132 blurinner<aprec,zprec>((unsigned char *)&ptr[index],
133 zR, zG, zB, zA, alpha);
138 namespace ImageEffects {
141 #ifdef HAVE_X86_SSE2
142 void expblur_sse( QImage &img, int radius );
143 #endif //HAVE_X86_SSE2
146 #ifdef HAVE_X86_MMX
147 void expblur_mmx( QImage &img, int radius );
148 #endif //HAVE_X86_MMX
152 void expBlur(QImage& img, int radius) {
154 //KCPUInfo:: returns false on x86_64, and x86_64 always have sse/mmx
155 #ifdef HAVE_X86_SSE2
156 #ifndef __x86_64__
157 if(BlitzCPUInfo::haveExtension( BlitzCPUInfo::SSE2 ) )
158 #endif //__x86_64__
159 return expblur_sse(img, radius);
160 #endif //HAVE_X86_SSE2
162 #ifdef HAVE_X86_MMX
163 #ifndef __x86_64__
164 if(BlitzCPUInfo::haveExtension( BlitzCPUInfo::MMX ) )
165 #endif //__x86_64__
166 return expblur_mmx(img, radius);
167 #endif //HAVE_X86_MMX
169 return expblur<15,7>(img, radius);
172 QImage addShadow(const QImage& image, int r, QColor color,
173 int offx, int offy, int growx, int growy) {
174 QPainter p;
175 QImage retv(image.width()+growx, image.height()+growy, QImage::Format_ARGB32_Premultiplied);
176 int dx = (growx-offx)/2, dy = (growy-offy)/2;
178 p.begin(&retv);
179 p.setCompositionMode(QPainter::CompositionMode_Source);
180 p.fillRect(0,0,retv.width(), retv.height(), QColor(0,0,0,0));
181 p.fillRect(dx+offx, dy+offy, image.width(), image.height(), color);
182 p.setCompositionMode(QPainter::CompositionMode_DestinationAtop );
183 p.drawImage(dx+offx, dy+offy, image);
184 p.end();
186 expBlur(retv, r);
188 p.begin(&retv);
189 p.drawImage(dx, dy, image);
190 p.end();
192 return retv;
195 QImage growBorder(const QImage& image) {
196 int w = image.width();
197 int h = image.height();
198 QPainter p;
199 QImage retv(w+2, h+2, QImage::Format_ARGB32_Premultiplied);
201 p.begin(&retv);
202 p.setCompositionMode(QPainter::CompositionMode_Source);
203 p.drawImage(0, 0, image, 0, 0, 1, 1);
204 p.drawImage(w+1, 0, image, w-1, 0, 1, 1);
205 p.drawImage(0, h+1, image, 0, h-1, 1, 1);
206 p.drawImage(w+1, h+1, image, w-1, h-1, 1, 1);
207 p.drawImage(1, 0, image, 0, 0, w, 1);
208 p.drawImage(1, h+1, image, 0, h-1, w, 1);
209 p.drawImage(0, 1, image, 0, 0, 1, h);
210 p.drawImage(w+1, 1, image, w-1, 0, 1, h);
211 p.drawImage(1, 1, image);
212 p.end();
214 return retv;
217 struct Line {
218 int y, x1, x2;
219 Line(){}
220 Line(int _y, int _x1, int _x2)
221 : y(_y), x1(_x1), x2(_x2) {}
224 void floodFill(QImage& image, QPoint point, QColor color,
225 bool invade_border, std::vector<QPoint>* border) {
227 int* ptr = (int*)image.bits();
228 int h = image.height();
229 int w = image.width();
230 int newcol = color.rgba();
231 int oldcol = ptr[point.x()+point.y()*w];
232 std::vector<Line> lines;
235 Line l(point.y(), point.x(), point.x()+1);
236 int *scanline = ptr+point.y()*w;
237 scanline[l.x1] = newcol;
238 while(l.x1 > 0 && scanline[l.x1-1] == oldcol)
239 scanline[--l.x1] = newcol;
240 while(l.x2 < w && scanline[l.x2] == oldcol)
241 scanline[l.x2++] = newcol;
242 lines.push_back(l);
245 while(!lines.empty()) {
246 Line ll = lines.back();
247 lines.pop_back();
249 if(ll.x1>0) {
250 if(invade_border)
251 ptr[ll.y*w + ll.x1 - 1] = newcol;
252 if(border)
253 border->push_back(QPoint(ll.x1-1, ll.y));
255 if(ll.x2<w) {
256 if(invade_border)
257 ptr[ll.y*w + ll.x2] = newcol;
258 if(border)
259 border->push_back(QPoint(ll.x2, ll.y));
262 for(int d=-1; d<=1; d+=2)
263 if( (d == -1) ? (ll.y > 0) : (ll.y < h-1) ) {
264 int *scanline = ptr + (ll.y+d)*w;
266 for(int i=ll.x1;i<ll.x2;i++){
267 if(scanline[i]==oldcol) {
268 Line l(ll.y+d, i, i+1);
270 scanline[l.x1] = newcol;
271 while(l.x1 > 0 && scanline[l.x1-1] == oldcol)
272 scanline[--l.x1] = newcol;
273 while(l.x2 < w && scanline[l.x2] == oldcol)
274 scanline[l.x2++] = newcol;
275 lines.push_back(l);
276 i = l.x2;
278 if(i<ll.x2 && scanline[i]!=newcol) {
279 if(invade_border)
280 scanline[i]=newcol;
281 if(border)
282 border->push_back(QPoint(i, ll.y+d));
289 void floodFillBlueThreshold(QImage& image, QPoint point, QColor color, unsigned int thresh,
290 bool invade_border, std::vector<QPoint>* border) {
292 unsigned int* ptr = (unsigned int*)image.bits();
293 int h = image.height();
294 int w = image.width();
295 unsigned int newcol = color.rgba();
296 std::vector<Line> lines;
298 #define TEST(x) ((((x) & 0xff) < thresh) && ((x) != newcol))
300 Line l(point.y(), point.x(), point.x()+1);
301 unsigned int *scanline = ptr+point.y()*w;
302 scanline[l.x1] = newcol;
303 while(l.x1 > 0 && TEST(scanline[l.x1-1]))
304 scanline[--l.x1] = newcol;
305 while(l.x2 < w && TEST(scanline[l.x2]))
306 scanline[l.x2++] = newcol;
307 lines.push_back(l);
310 while(!lines.empty()) {
311 Line ll = lines.back();
312 lines.pop_back();
314 if(ll.x1>0) {
315 if(invade_border)
316 ptr[ll.y*w + ll.x1 - 1] = newcol;
317 if(border)
318 border->push_back(QPoint(ll.x1-1, ll.y));
320 if(ll.x2<w) {
321 if(invade_border)
322 ptr[ll.y*w + ll.x2] = newcol;
323 if(border)
324 border->push_back(QPoint(ll.x2, ll.y));
327 for(int d=-1; d<=1; d+=2)
328 if( (d == -1) ? (ll.y > 0) : (ll.y < h-1) ) {
329 unsigned int *scanline = ptr + (ll.y+d)*w;
331 for(int i=ll.x1;i<ll.x2;i++){
332 if(TEST(scanline[i])) {
333 Line l(ll.y+d, i, i+1);
335 scanline[l.x1] = newcol;
336 while(l.x1 > 0 && TEST(scanline[l.x1-1]))
337 scanline[--l.x1] = newcol;
338 while(l.x2 < w && TEST(scanline[l.x2]))
339 scanline[l.x2++] = newcol;
340 lines.push_back(l);
341 i = l.x2;
343 if(i<ll.x2 && scanline[i]!=newcol) {
344 if(invade_border)
345 scanline[i]=newcol;
346 if(border)
347 border->push_back(QPoint(i, ll.y+d));