]> Creatis software - clitk.git/blob - utilities/CxImage/ximadsp.cpp
Ensure compilation when CLITK_USE_PACS_CONNECTION is OFF
[clitk.git] / utilities / CxImage / ximadsp.cpp
1 // xImaDsp.cpp : DSP functions\r
2 /* 07/08/2001 v1.00 - Davide Pizzolato - www.xdp.it\r
3  * CxImage version 6.0.0 02/Feb/2008\r
4  */\r
5 \r
6 #include "ximage.h"\r
7 \r
8 #include "ximaiter.h"\r
9 \r
10 #if CXIMAGE_SUPPORT_DSP\r
11 \r
12 ////////////////////////////////////////////////////////////////////////////////\r
13 /**\r
14  * Converts the image to B&W.\r
15  * The OptimalThreshold() function can be used for calculating the optimal threshold.\r
16  * \param level: the lightness threshold.\r
17  * \return true if everything is ok\r
18  */\r
19 bool CxImage::Threshold(BYTE level)\r
20 {\r
21         if (!pDib) return false;\r
22         if (head.biBitCount == 1) return true;\r
23 \r
24         GrayScale();\r
25 \r
26         CxImage tmp(head.biWidth,head.biHeight,1);\r
27         if (!tmp.IsValid()){\r
28                 strcpy(info.szLastError,tmp.GetLastError());\r
29                 return false;\r
30         }\r
31 \r
32         for (long y=0;y<head.biHeight;y++){\r
33                 info.nProgress = (long)(100*y/head.biHeight);\r
34                 if (info.nEscape) break;\r
35                 for (long x=0;x<head.biWidth;x++){\r
36                         if (BlindGetPixelIndex(x,y)>level)\r
37                                 tmp.BlindSetPixelIndex(x,y,1);\r
38                         else\r
39                                 tmp.BlindSetPixelIndex(x,y,0);\r
40                 }\r
41         }\r
42         tmp.SetPaletteColor(0,0,0,0);\r
43         tmp.SetPaletteColor(1,255,255,255);\r
44         Transfer(tmp);\r
45         return true;\r
46 }\r
47 ////////////////////////////////////////////////////////////////////////////////\r
48 /**\r
49  * Converts the image to B&W, using a threshold mask\r
50  * \param pThresholdMask: the lightness threshold mask.\r
51  * the pThresholdMask image must be grayscale with same with and height of the current image\r
52  * \return true if everything is ok\r
53  */\r
54 bool CxImage::Threshold(CxImage* pThresholdMask)\r
55 {\r
56         if (!pDib) return false;\r
57         if (head.biBitCount == 1) return true;\r
58 \r
59         if (!pThresholdMask) return false;\r
60         \r
61         if (!pThresholdMask->IsValid() ||\r
62                 !pThresholdMask->IsGrayScale() ||\r
63                 pThresholdMask->GetWidth() != GetWidth() ||\r
64                 pThresholdMask->GetHeight() != GetHeight()){\r
65                 strcpy(info.szLastError,"invalid ThresholdMask");\r
66                 return false;\r
67         }\r
68 \r
69         GrayScale();\r
70 \r
71         CxImage tmp(head.biWidth,head.biHeight,1);\r
72         if (!tmp.IsValid()){\r
73                 strcpy(info.szLastError,tmp.GetLastError());\r
74                 return false;\r
75         }\r
76 \r
77         for (long y=0;y<head.biHeight;y++){\r
78                 info.nProgress = (long)(100*y/head.biHeight);\r
79                 if (info.nEscape) break;\r
80                 for (long x=0;x<head.biWidth;x++){\r
81                         if (BlindGetPixelIndex(x,y)>pThresholdMask->BlindGetPixelIndex(x,y))\r
82                                 tmp.BlindSetPixelIndex(x,y,1);\r
83                         else\r
84                                 tmp.BlindSetPixelIndex(x,y,0);\r
85                 }\r
86         }\r
87         tmp.SetPaletteColor(0,0,0,0);\r
88         tmp.SetPaletteColor(1,255,255,255);\r
89         Transfer(tmp);\r
90         return true;\r
91 }\r
92 ////////////////////////////////////////////////////////////////////////////////\r
93 /**\r
94  * Filters only the pixels with a lightness less (or more) than the threshold level,\r
95  * and preserves the colors for the unfiltered pixels.\r
96  * \param level = the lightness threshold.\r
97  * \param bDirection = false: filter dark pixels, true: filter light pixels\r
98  * \param nBkgndColor =  filtered pixels are set to nBkgndColor color\r
99  * \param bSetAlpha = if true, sets also the alpha component for the filtered pixels, with nBkgndColor.rgbReserved\r
100  * \return true if everything is ok\r
101  * \author [DP], [wangsongtao]\r
102  */\r
103 ////////////////////////////////////////////////////////////////////////////////\r
104 bool CxImage::Threshold2(BYTE level, bool bDirection, RGBQUAD nBkgndColor, bool bSetAlpha)\r
105 {\r
106         if (!pDib) return false;\r
107         if (head.biBitCount == 1) return true;\r
108 \r
109         CxImage tmp(*this, true, false, false);\r
110         if (!tmp.IsValid()){\r
111                 strcpy(info.szLastError,tmp.GetLastError());\r
112                 return false;\r
113         }\r
114 \r
115         tmp.GrayScale();\r
116 \r
117         long xmin,xmax,ymin,ymax;\r
118         if (pSelection){\r
119                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
120                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
121         } else {\r
122                 xmin = ymin = 0;\r
123                 xmax = head.biWidth; ymax=head.biHeight;\r
124         }\r
125 \r
126         for(long y=ymin; y<ymax; y++){\r
127                 info.nProgress = (long)(100*y/head.biHeight);\r
128                 if (info.nEscape) break;\r
129                 for(long x=xmin; x<xmax; x++){\r
130 #if CXIMAGE_SUPPORT_SELECTION\r
131                         if (BlindSelectionIsInside(x,y))\r
132 #endif //CXIMAGE_SUPPORT_SELECTION\r
133                         {\r
134                                 BYTE i = tmp.BlindGetPixelIndex(x,y);\r
135                                 if (!bDirection && i<level) BlindSetPixelColor(x,y,nBkgndColor,bSetAlpha);\r
136                                 if (bDirection && i>=level) BlindSetPixelColor(x,y,nBkgndColor,bSetAlpha);\r
137                         }\r
138                 }\r
139         }\r
140 \r
141         return true;\r
142 }\r
143 ////////////////////////////////////////////////////////////////////////////////\r
144 /**\r
145  * Extract RGB channels from the image. Each channel is an 8 bit grayscale image. \r
146  * \param r,g,b: pointers to CxImage objects, to store the splited channels\r
147  * \return true if everything is ok\r
148  */\r
149 bool CxImage::SplitRGB(CxImage* r,CxImage* g,CxImage* b)\r
150 {\r
151         if (!pDib) return false;\r
152         if (r==NULL && g==NULL && b==NULL) return false;\r
153 \r
154         CxImage tmpr(head.biWidth,head.biHeight,8);\r
155         CxImage tmpg(head.biWidth,head.biHeight,8);\r
156         CxImage tmpb(head.biWidth,head.biHeight,8);\r
157 \r
158         RGBQUAD color;\r
159         for(long y=0; y<head.biHeight; y++){\r
160                 for(long x=0; x<head.biWidth; x++){\r
161                         color = BlindGetPixelColor(x,y);\r
162                         if (r) tmpr.BlindSetPixelIndex(x,y,color.rgbRed);\r
163                         if (g) tmpg.BlindSetPixelIndex(x,y,color.rgbGreen);\r
164                         if (b) tmpb.BlindSetPixelIndex(x,y,color.rgbBlue);\r
165                 }\r
166         }\r
167 \r
168         if (r) tmpr.SetGrayPalette();\r
169         if (g) tmpg.SetGrayPalette();\r
170         if (b) tmpb.SetGrayPalette();\r
171 \r
172         /*for(long j=0; j<256; j++){\r
173                 BYTE i=(BYTE)j;\r
174                 if (r) tmpr.SetPaletteColor(i,i,0,0);\r
175                 if (g) tmpg.SetPaletteColor(i,0,i,0);\r
176                 if (b) tmpb.SetPaletteColor(i,0,0,i);\r
177         }*/\r
178 \r
179         if (r) r->Transfer(tmpr);\r
180         if (g) g->Transfer(tmpg);\r
181         if (b) b->Transfer(tmpb);\r
182 \r
183         return true;\r
184 }\r
185 ////////////////////////////////////////////////////////////////////////////////\r
186 /**\r
187  * Extract CMYK channels from the image. Each channel is an 8 bit grayscale image. \r
188  * \param c,m,y,k: pointers to CxImage objects, to store the splited channels\r
189  * \return true if everything is ok\r
190  */\r
191 bool CxImage::SplitCMYK(CxImage* c,CxImage* m,CxImage* y,CxImage* k)\r
192 {\r
193         if (!pDib) return false;\r
194         if (c==NULL && m==NULL && y==NULL && k==NULL) return false;\r
195 \r
196         CxImage tmpc(head.biWidth,head.biHeight,8);\r
197         CxImage tmpm(head.biWidth,head.biHeight,8);\r
198         CxImage tmpy(head.biWidth,head.biHeight,8);\r
199         CxImage tmpk(head.biWidth,head.biHeight,8);\r
200 \r
201         RGBQUAD color;\r
202         for(long yy=0; yy<head.biHeight; yy++){\r
203                 for(long xx=0; xx<head.biWidth; xx++){\r
204                         color = BlindGetPixelColor(xx,yy);\r
205                         if (c) tmpc.BlindSetPixelIndex(xx,yy,(BYTE)(255-color.rgbRed));\r
206                         if (m) tmpm.BlindSetPixelIndex(xx,yy,(BYTE)(255-color.rgbGreen));\r
207                         if (y) tmpy.BlindSetPixelIndex(xx,yy,(BYTE)(255-color.rgbBlue));\r
208                         if (k) tmpk.BlindSetPixelIndex(xx,yy,(BYTE)RGB2GRAY(color.rgbRed,color.rgbGreen,color.rgbBlue));\r
209                 }\r
210         }\r
211 \r
212         if (c) tmpc.SetGrayPalette();\r
213         if (m) tmpm.SetGrayPalette();\r
214         if (y) tmpy.SetGrayPalette();\r
215         if (k) tmpk.SetGrayPalette();\r
216 \r
217         if (c) c->Transfer(tmpc);\r
218         if (m) m->Transfer(tmpm);\r
219         if (y) y->Transfer(tmpy);\r
220         if (k) k->Transfer(tmpk);\r
221 \r
222         return true;\r
223 }\r
224 ////////////////////////////////////////////////////////////////////////////////\r
225 /**\r
226  * Extract YUV channels from the image. Each channel is an 8 bit grayscale image. \r
227  * \param y,u,v: pointers to CxImage objects, to store the splited channels\r
228  * \return true if everything is ok\r
229  */\r
230 bool CxImage::SplitYUV(CxImage* y,CxImage* u,CxImage* v)\r
231 {\r
232         if (!pDib) return false;\r
233         if (y==NULL && u==NULL && v==NULL) return false;\r
234 \r
235         CxImage tmpy(head.biWidth,head.biHeight,8);\r
236         CxImage tmpu(head.biWidth,head.biHeight,8);\r
237         CxImage tmpv(head.biWidth,head.biHeight,8);\r
238 \r
239         RGBQUAD color;\r
240         for(long yy=0; yy<head.biHeight; yy++){\r
241                 for(long x=0; x<head.biWidth; x++){\r
242                         color = RGBtoYUV(BlindGetPixelColor(x,yy));\r
243                         if (y) tmpy.BlindSetPixelIndex(x,yy,color.rgbRed);\r
244                         if (u) tmpu.BlindSetPixelIndex(x,yy,color.rgbGreen);\r
245                         if (v) tmpv.BlindSetPixelIndex(x,yy,color.rgbBlue);\r
246                 }\r
247         }\r
248 \r
249         if (y) tmpy.SetGrayPalette();\r
250         if (u) tmpu.SetGrayPalette();\r
251         if (v) tmpv.SetGrayPalette();\r
252 \r
253         if (y) y->Transfer(tmpy);\r
254         if (u) u->Transfer(tmpu);\r
255         if (v) v->Transfer(tmpv);\r
256 \r
257         return true;\r
258 }\r
259 ////////////////////////////////////////////////////////////////////////////////\r
260 /**\r
261  * Extract YIQ channels from the image. Each channel is an 8 bit grayscale image. \r
262  * \param y,i,q: pointers to CxImage objects, to store the splited channels\r
263  * \return true if everything is ok\r
264  */\r
265 bool CxImage::SplitYIQ(CxImage* y,CxImage* i,CxImage* q)\r
266 {\r
267         if (!pDib) return false;\r
268         if (y==NULL && i==NULL && q==NULL) return false;\r
269 \r
270         CxImage tmpy(head.biWidth,head.biHeight,8);\r
271         CxImage tmpi(head.biWidth,head.biHeight,8);\r
272         CxImage tmpq(head.biWidth,head.biHeight,8);\r
273 \r
274         RGBQUAD color;\r
275         for(long yy=0; yy<head.biHeight; yy++){\r
276                 for(long x=0; x<head.biWidth; x++){\r
277                         color = RGBtoYIQ(BlindGetPixelColor(x,yy));\r
278                         if (y) tmpy.BlindSetPixelIndex(x,yy,color.rgbRed);\r
279                         if (i) tmpi.BlindSetPixelIndex(x,yy,color.rgbGreen);\r
280                         if (q) tmpq.BlindSetPixelIndex(x,yy,color.rgbBlue);\r
281                 }\r
282         }\r
283 \r
284         if (y) tmpy.SetGrayPalette();\r
285         if (i) tmpi.SetGrayPalette();\r
286         if (q) tmpq.SetGrayPalette();\r
287 \r
288         if (y) y->Transfer(tmpy);\r
289         if (i) i->Transfer(tmpi);\r
290         if (q) q->Transfer(tmpq);\r
291 \r
292         return true;\r
293 }\r
294 ////////////////////////////////////////////////////////////////////////////////\r
295 /**\r
296  * Extract XYZ channels from the image. Each channel is an 8 bit grayscale image. \r
297  * \param x,y,z: pointers to CxImage objects, to store the splited channels\r
298  * \return true if everything is ok\r
299  */\r
300 bool CxImage::SplitXYZ(CxImage* x,CxImage* y,CxImage* z)\r
301 {\r
302         if (!pDib) return false;\r
303         if (x==NULL && y==NULL && z==NULL) return false;\r
304 \r
305         CxImage tmpx(head.biWidth,head.biHeight,8);\r
306         CxImage tmpy(head.biWidth,head.biHeight,8);\r
307         CxImage tmpz(head.biWidth,head.biHeight,8);\r
308 \r
309         RGBQUAD color;\r
310         for(long yy=0; yy<head.biHeight; yy++){\r
311                 for(long xx=0; xx<head.biWidth; xx++){\r
312                         color = RGBtoXYZ(BlindGetPixelColor(xx,yy));\r
313                         if (x) tmpx.BlindSetPixelIndex(xx,yy,color.rgbRed);\r
314                         if (y) tmpy.BlindSetPixelIndex(xx,yy,color.rgbGreen);\r
315                         if (z) tmpz.BlindSetPixelIndex(xx,yy,color.rgbBlue);\r
316                 }\r
317         }\r
318 \r
319         if (x) tmpx.SetGrayPalette();\r
320         if (y) tmpy.SetGrayPalette();\r
321         if (z) tmpz.SetGrayPalette();\r
322 \r
323         if (x) x->Transfer(tmpx);\r
324         if (y) y->Transfer(tmpy);\r
325         if (z) z->Transfer(tmpz);\r
326 \r
327         return true;\r
328 }\r
329 ////////////////////////////////////////////////////////////////////////////////\r
330 /**\r
331  * Extract HSL channels from the image. Each channel is an 8 bit grayscale image. \r
332  * \param h,s,l: pointers to CxImage objects, to store the splited channels\r
333  * \return true if everything is ok\r
334  */\r
335 bool CxImage::SplitHSL(CxImage* h,CxImage* s,CxImage* l)\r
336 {\r
337         if (!pDib) return false;\r
338         if (h==NULL && s==NULL && l==NULL) return false;\r
339 \r
340         CxImage tmph(head.biWidth,head.biHeight,8);\r
341         CxImage tmps(head.biWidth,head.biHeight,8);\r
342         CxImage tmpl(head.biWidth,head.biHeight,8);\r
343 \r
344         RGBQUAD color;\r
345         for(long y=0; y<head.biHeight; y++){\r
346                 for(long x=0; x<head.biWidth; x++){\r
347                         color = RGBtoHSL(BlindGetPixelColor(x,y));\r
348                         if (h) tmph.BlindSetPixelIndex(x,y,color.rgbRed);\r
349                         if (s) tmps.BlindSetPixelIndex(x,y,color.rgbGreen);\r
350                         if (l) tmpl.BlindSetPixelIndex(x,y,color.rgbBlue);\r
351                 }\r
352         }\r
353 \r
354         if (h) tmph.SetGrayPalette();\r
355         if (s) tmps.SetGrayPalette();\r
356         if (l) tmpl.SetGrayPalette();\r
357 \r
358         /* pseudo-color generator for hue channel (visual debug)\r
359         if (h) for(long j=0; j<256; j++){\r
360                 BYTE i=(BYTE)j;\r
361                 RGBQUAD hsl={120,240,i,0};\r
362                 tmph.SetPaletteColor(i,HSLtoRGB(hsl));\r
363         }*/\r
364 \r
365         if (h) h->Transfer(tmph);\r
366         if (s) s->Transfer(tmps);\r
367         if (l) l->Transfer(tmpl);\r
368 \r
369         return true;\r
370 }\r
371 ////////////////////////////////////////////////////////////////////////////////\r
372 #define  HSLMAX   255   /* H,L, and S vary over 0-HSLMAX */\r
373 #define  RGBMAX   255   /* R,G, and B vary over 0-RGBMAX */\r
374                         /* HSLMAX BEST IF DIVISIBLE BY 6 */\r
375                         /* RGBMAX, HSLMAX must each fit in a BYTE. */\r
376 /* Hue is undefined if Saturation is 0 (grey-scale) */\r
377 /* This value determines where the Hue scrollbar is */\r
378 /* initially set for achromatic colors */\r
379 #define HSLUNDEFINED (HSLMAX*2/3)\r
380 ////////////////////////////////////////////////////////////////////////////////\r
381 RGBQUAD CxImage::RGBtoHSL(RGBQUAD lRGBColor)\r
382 {\r
383         BYTE R,G,B;                                     /* input RGB values */\r
384         BYTE H,L,S;                                     /* output HSL values */\r
385         BYTE cMax,cMin;                         /* max and min RGB values */\r
386         WORD Rdelta,Gdelta,Bdelta;      /* intermediate value: % of spread from max*/\r
387 \r
388         R = lRGBColor.rgbRed;   /* get R, G, and B out of DWORD */\r
389         G = lRGBColor.rgbGreen;\r
390         B = lRGBColor.rgbBlue;\r
391 \r
392         cMax = max( max(R,G), B);       /* calculate lightness */\r
393         cMin = min( min(R,G), B);\r
394         L = (BYTE)((((cMax+cMin)*HSLMAX)+RGBMAX)/(2*RGBMAX));\r
395 \r
396         if (cMax==cMin){                        /* r=g=b --> achromatic case */\r
397                 S = 0;                                  /* saturation */\r
398                 H = HSLUNDEFINED;               /* hue */\r
399         } else {                                        /* chromatic case */\r
400                 if (L <= (HSLMAX/2))    /* saturation */\r
401                         S = (BYTE)((((cMax-cMin)*HSLMAX)+((cMax+cMin)/2))/(cMax+cMin));\r
402                 else\r
403                         S = (BYTE)((((cMax-cMin)*HSLMAX)+((2*RGBMAX-cMax-cMin)/2))/(2*RGBMAX-cMax-cMin));\r
404                 /* hue */\r
405                 Rdelta = (WORD)((((cMax-R)*(HSLMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin));\r
406                 Gdelta = (WORD)((((cMax-G)*(HSLMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin));\r
407                 Bdelta = (WORD)((((cMax-B)*(HSLMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin));\r
408 \r
409                 if (R == cMax)\r
410                         H = (BYTE)(Bdelta - Gdelta);\r
411                 else if (G == cMax)\r
412                         H = (BYTE)((HSLMAX/3) + Rdelta - Bdelta);\r
413                 else /* B == cMax */\r
414                         H = (BYTE)(((2*HSLMAX)/3) + Gdelta - Rdelta);\r
415 \r
416 //              if (H < 0) H += HSLMAX;     //always false\r
417                 if (H > HSLMAX) H -= HSLMAX;\r
418         }\r
419         RGBQUAD hsl={L,S,H,0};\r
420         return hsl;\r
421 }\r
422 ////////////////////////////////////////////////////////////////////////////////\r
423 float CxImage::HueToRGB(float n1,float n2, float hue)\r
424 {\r
425         //<F. Livraghi> fixed implementation for HSL2RGB routine\r
426         float rValue;\r
427 \r
428         if (hue > 360)\r
429                 hue = hue - 360;\r
430         else if (hue < 0)\r
431                 hue = hue + 360;\r
432 \r
433         if (hue < 60)\r
434                 rValue = n1 + (n2-n1)*hue/60.0f;\r
435         else if (hue < 180)\r
436                 rValue = n2;\r
437         else if (hue < 240)\r
438                 rValue = n1+(n2-n1)*(240-hue)/60;\r
439         else\r
440                 rValue = n1;\r
441 \r
442         return rValue;\r
443 }\r
444 ////////////////////////////////////////////////////////////////////////////////\r
445 RGBQUAD CxImage::HSLtoRGB(COLORREF cHSLColor)\r
446 {\r
447         return HSLtoRGB(RGBtoRGBQUAD(cHSLColor));\r
448 }\r
449 ////////////////////////////////////////////////////////////////////////////////\r
450 RGBQUAD CxImage::HSLtoRGB(RGBQUAD lHSLColor)\r
451\r
452         //<F. Livraghi> fixed implementation for HSL2RGB routine\r
453         float h,s,l;\r
454         float m1,m2;\r
455         BYTE r,g,b;\r
456 \r
457         h = (float)lHSLColor.rgbRed * 360.0f/255.0f;\r
458         s = (float)lHSLColor.rgbGreen/255.0f;\r
459         l = (float)lHSLColor.rgbBlue/255.0f;\r
460 \r
461         if (l <= 0.5)   m2 = l * (1+s);\r
462         else                    m2 = l + s - l*s;\r
463 \r
464         m1 = 2 * l - m2;\r
465 \r
466         if (s == 0) {\r
467                 r=g=b=(BYTE)(l*255.0f);\r
468         } else {\r
469                 r = (BYTE)(HueToRGB(m1,m2,h+120) * 255.0f);\r
470                 g = (BYTE)(HueToRGB(m1,m2,h) * 255.0f);\r
471                 b = (BYTE)(HueToRGB(m1,m2,h-120) * 255.0f);\r
472         }\r
473 \r
474         RGBQUAD rgb = {b,g,r,0};\r
475         return rgb;\r
476 }\r
477 ////////////////////////////////////////////////////////////////////////////////\r
478 RGBQUAD CxImage::YUVtoRGB(RGBQUAD lYUVColor)\r
479 {\r
480         int U,V,R,G,B;\r
481         float Y = lYUVColor.rgbRed;\r
482         U = lYUVColor.rgbGreen - 128;\r
483         V = lYUVColor.rgbBlue - 128;\r
484 \r
485 //      R = (int)(1.164 * Y + 2.018 * U);\r
486 //      G = (int)(1.164 * Y - 0.813 * V - 0.391 * U);\r
487 //      B = (int)(1.164 * Y + 1.596 * V);\r
488         R = (int)( Y + 1.403f * V);\r
489         G = (int)( Y - 0.344f * U - 0.714f * V);\r
490         B = (int)( Y + 1.770f * U);\r
491 \r
492         R= min(255,max(0,R));\r
493         G= min(255,max(0,G));\r
494         B= min(255,max(0,B));\r
495         RGBQUAD rgb={(BYTE)B,(BYTE)G,(BYTE)R,0};\r
496         return rgb;\r
497 }\r
498 ////////////////////////////////////////////////////////////////////////////////\r
499 RGBQUAD CxImage::RGBtoYUV(RGBQUAD lRGBColor)\r
500 {\r
501         int Y,U,V,R,G,B;\r
502         R = lRGBColor.rgbRed;\r
503         G = lRGBColor.rgbGreen;\r
504         B = lRGBColor.rgbBlue;\r
505 \r
506 //      Y = (int)( 0.257 * R + 0.504 * G + 0.098 * B);\r
507 //      U = (int)( 0.439 * R - 0.368 * G - 0.071 * B + 128);\r
508 //      V = (int)(-0.148 * R - 0.291 * G + 0.439 * B + 128);\r
509         Y = (int)(0.299f * R + 0.587f * G + 0.114f * B);\r
510         U = (int)((B-Y) * 0.565f + 128);\r
511         V = (int)((R-Y) * 0.713f + 128);\r
512 \r
513         Y= min(255,max(0,Y));\r
514         U= min(255,max(0,U));\r
515         V= min(255,max(0,V));\r
516         RGBQUAD yuv={(BYTE)V,(BYTE)U,(BYTE)Y,0};\r
517         return yuv;\r
518 }\r
519 ////////////////////////////////////////////////////////////////////////////////\r
520 RGBQUAD CxImage::YIQtoRGB(RGBQUAD lYIQColor)\r
521 {\r
522         int I,Q,R,G,B;\r
523         float Y = lYIQColor.rgbRed;\r
524         I = lYIQColor.rgbGreen - 128;\r
525         Q = lYIQColor.rgbBlue - 128;\r
526 \r
527         R = (int)( Y + 0.956f * I + 0.621f * Q);\r
528         G = (int)( Y - 0.273f * I - 0.647f * Q);\r
529         B = (int)( Y - 1.104f * I + 1.701f * Q);\r
530 \r
531         R= min(255,max(0,R));\r
532         G= min(255,max(0,G));\r
533         B= min(255,max(0,B));\r
534         RGBQUAD rgb={(BYTE)B,(BYTE)G,(BYTE)R,0};\r
535         return rgb;\r
536 }\r
537 ////////////////////////////////////////////////////////////////////////////////\r
538 RGBQUAD CxImage::RGBtoYIQ(RGBQUAD lRGBColor)\r
539 {\r
540         int Y,I,Q,R,G,B;\r
541         R = lRGBColor.rgbRed;\r
542         G = lRGBColor.rgbGreen;\r
543         B = lRGBColor.rgbBlue;\r
544 \r
545         Y = (int)( 0.2992f * R + 0.5868f * G + 0.1140f * B);\r
546         I = (int)( 0.5960f * R - 0.2742f * G - 0.3219f * B + 128);\r
547         Q = (int)( 0.2109f * R - 0.5229f * G + 0.3120f * B + 128);\r
548 \r
549         Y= min(255,max(0,Y));\r
550         I= min(255,max(0,I));\r
551         Q= min(255,max(0,Q));\r
552         RGBQUAD yiq={(BYTE)Q,(BYTE)I,(BYTE)Y,0};\r
553         return yiq;\r
554 }\r
555 ////////////////////////////////////////////////////////////////////////////////\r
556 RGBQUAD CxImage::XYZtoRGB(RGBQUAD lXYZColor)\r
557 {\r
558         int X,Y,Z,R,G,B;\r
559         X = lXYZColor.rgbRed;\r
560         Y = lXYZColor.rgbGreen;\r
561         Z = lXYZColor.rgbBlue;\r
562         double k=1.088751;\r
563 \r
564         R = (int)(  3.240479f * X - 1.537150f * Y - 0.498535f * Z * k);\r
565         G = (int)( -0.969256f * X + 1.875992f * Y + 0.041556f * Z * k);\r
566         B = (int)(  0.055648f * X - 0.204043f * Y + 1.057311f * Z * k);\r
567 \r
568         R= min(255,max(0,R));\r
569         G= min(255,max(0,G));\r
570         B= min(255,max(0,B));\r
571         RGBQUAD rgb={(BYTE)B,(BYTE)G,(BYTE)R,0};\r
572         return rgb;\r
573 }\r
574 ////////////////////////////////////////////////////////////////////////////////\r
575 RGBQUAD CxImage::RGBtoXYZ(RGBQUAD lRGBColor)\r
576 {\r
577         int X,Y,Z,R,G,B;\r
578         R = lRGBColor.rgbRed;\r
579         G = lRGBColor.rgbGreen;\r
580         B = lRGBColor.rgbBlue;\r
581 \r
582         X = (int)( 0.412453f * R + 0.357580f * G + 0.180423f * B);\r
583         Y = (int)( 0.212671f * R + 0.715160f * G + 0.072169f * B);\r
584         Z = (int)((0.019334f * R + 0.119193f * G + 0.950227f * B)*0.918483657f);\r
585 \r
586         //X= min(255,max(0,X));\r
587         //Y= min(255,max(0,Y));\r
588         //Z= min(255,max(0,Z));\r
589         RGBQUAD xyz={(BYTE)Z,(BYTE)Y,(BYTE)X,0};\r
590         return xyz;\r
591 }\r
592 ////////////////////////////////////////////////////////////////////////////////\r
593 /**\r
594  * Generates a "rainbow" palette with saturated colors\r
595  * \param correction: 1 generates a single hue spectrum. 0.75 is nice for scientific applications.\r
596  */\r
597 void CxImage::HuePalette(float correction)\r
598 {\r
599         if (head.biClrUsed==0) return;\r
600 \r
601         for(DWORD j=0; j<head.biClrUsed; j++){\r
602                 BYTE i=(BYTE)(j*correction*(255/(head.biClrUsed-1)));\r
603                 RGBQUAD hsl={120,240,i,0};\r
604                 SetPaletteColor((BYTE)j,HSLtoRGB(hsl));\r
605         }\r
606 }\r
607 ////////////////////////////////////////////////////////////////////////////////\r
608 /**\r
609  * Replaces the original hue and saturation values.\r
610  * \param hue: hue\r
611  * \param sat: saturation\r
612  * \param blend: can be from 0 (no effect) to 1 (full effect)\r
613  * \return true if everything is ok\r
614  */\r
615 bool CxImage::Colorize(BYTE hue, BYTE sat, float blend)\r
616 {\r
617         if (!pDib) return false;\r
618 \r
619         if (blend < 0.0f) blend = 0.0f;\r
620         if (blend > 1.0f) blend = 1.0f;\r
621         int a0 = (int)(256*blend);\r
622         int a1 = 256 - a0;\r
623 \r
624         bool bFullBlend = false;\r
625         if (blend > 0.999f)     bFullBlend = true;\r
626 \r
627         RGBQUAD color,hsl;\r
628         if (head.biClrUsed==0){\r
629 \r
630                 long xmin,xmax,ymin,ymax;\r
631                 if (pSelection){\r
632                         xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
633                         ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
634                 } else {\r
635                         xmin = ymin = 0;\r
636                         xmax = head.biWidth; ymax=head.biHeight;\r
637                 }\r
638 \r
639                 for(long y=ymin; y<ymax; y++){\r
640                         info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));\r
641                         if (info.nEscape) break;\r
642                         for(long x=xmin; x<xmax; x++){\r
643 #if CXIMAGE_SUPPORT_SELECTION\r
644                                 if (BlindSelectionIsInside(x,y))\r
645 #endif //CXIMAGE_SUPPORT_SELECTION\r
646                                 {\r
647                                         if (bFullBlend){\r
648                                                 color = RGBtoHSL(BlindGetPixelColor(x,y));\r
649                                                 color.rgbRed=hue;\r
650                                                 color.rgbGreen=sat;\r
651                                                 BlindSetPixelColor(x,y,HSLtoRGB(color));\r
652                                         } else {\r
653                                                 color = BlindGetPixelColor(x,y);\r
654                                                 hsl.rgbRed=hue;\r
655                                                 hsl.rgbGreen=sat;\r
656                                                 hsl.rgbBlue = (BYTE)RGB2GRAY(color.rgbRed,color.rgbGreen,color.rgbBlue);\r
657                                                 hsl = HSLtoRGB(hsl);\r
658                                                 //BlendPixelColor(x,y,hsl,blend);\r
659                                                 //color.rgbRed = (BYTE)(hsl.rgbRed * blend + color.rgbRed * (1.0f - blend));\r
660                                                 //color.rgbBlue = (BYTE)(hsl.rgbBlue * blend + color.rgbBlue * (1.0f - blend));\r
661                                                 //color.rgbGreen = (BYTE)(hsl.rgbGreen * blend + color.rgbGreen * (1.0f - blend));\r
662                                                 color.rgbRed = (BYTE)((hsl.rgbRed * a0 + color.rgbRed * a1)>>8);\r
663                                                 color.rgbBlue = (BYTE)((hsl.rgbBlue * a0 + color.rgbBlue * a1)>>8);\r
664                                                 color.rgbGreen = (BYTE)((hsl.rgbGreen * a0 + color.rgbGreen * a1)>>8);\r
665                                                 BlindSetPixelColor(x,y,color);\r
666                                         }\r
667                                 }\r
668                         }\r
669                 }\r
670         } else {\r
671                 for(DWORD j=0; j<head.biClrUsed; j++){\r
672                         if (bFullBlend){\r
673                                 color = RGBtoHSL(GetPaletteColor((BYTE)j));\r
674                                 color.rgbRed=hue;\r
675                                 color.rgbGreen=sat;\r
676                                 SetPaletteColor((BYTE)j,HSLtoRGB(color));\r
677                         } else {\r
678                                 color = GetPaletteColor((BYTE)j);\r
679                                 hsl.rgbRed=hue;\r
680                                 hsl.rgbGreen=sat;\r
681                                 hsl.rgbBlue = (BYTE)RGB2GRAY(color.rgbRed,color.rgbGreen,color.rgbBlue);\r
682                                 hsl = HSLtoRGB(hsl);\r
683                                 color.rgbRed = (BYTE)(hsl.rgbRed * blend + color.rgbRed * (1.0f - blend));\r
684                                 color.rgbBlue = (BYTE)(hsl.rgbBlue * blend + color.rgbBlue * (1.0f - blend));\r
685                                 color.rgbGreen = (BYTE)(hsl.rgbGreen * blend + color.rgbGreen * (1.0f - blend));\r
686                                 SetPaletteColor((BYTE)j,color);\r
687                         }\r
688                 }\r
689         }\r
690 \r
691         return true;\r
692 }\r
693 ////////////////////////////////////////////////////////////////////////////////\r
694 /**\r
695  * Changes the brightness and the contrast of the image. \r
696  * \param brightness: can be from -255 to 255, if brightness is negative, the image becomes dark.\r
697  * \param contrast: can be from -100 to 100, the neutral value is 0.\r
698  * \return true if everything is ok\r
699  */\r
700 bool CxImage::Light(long brightness, long contrast)\r
701 {\r
702         if (!pDib) return false;\r
703         float c=(100 + contrast)/100.0f;\r
704         brightness+=128;\r
705 \r
706         BYTE cTable[256]; //<nipper>\r
707         for (int i=0;i<256;i++) {\r
708                 cTable[i] = (BYTE)max(0,min(255,(int)((i-128)*c + brightness + 0.5f)));\r
709         }\r
710 \r
711         return Lut(cTable);\r
712 }\r
713 ////////////////////////////////////////////////////////////////////////////////\r
714 /**\r
715  * \return mean lightness of the image. Useful with Threshold() and Light()\r
716  */\r
717 float CxImage::Mean()\r
718 {\r
719         if (!pDib) return 0;\r
720 \r
721         CxImage tmp(*this,true);\r
722         if (!tmp.IsValid()){\r
723                 strcpy(info.szLastError,tmp.GetLastError());\r
724                 return false;\r
725         }\r
726 \r
727         tmp.GrayScale();\r
728         float sum=0;\r
729 \r
730         long xmin,xmax,ymin,ymax;\r
731         if (pSelection){\r
732                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
733                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
734         } else {\r
735                 xmin = ymin = 0;\r
736                 xmax = head.biWidth; ymax=head.biHeight;\r
737         }\r
738         if (xmin==xmax || ymin==ymax) return (float)0.0;\r
739 \r
740         BYTE *iSrc=tmp.info.pImage;\r
741         iSrc += tmp.info.dwEffWidth*ymin; // necessary for selections <Admir Hodzic>\r
742 \r
743         for(long y=ymin; y<ymax; y++){\r
744                 info.nProgress = (long)(100*(y-ymin)/(ymax-ymin)); //<zhanghk><Anatoly Ivasyuk>\r
745                 for(long x=xmin; x<xmax; x++){\r
746                         sum+=iSrc[x];\r
747                 }\r
748                 iSrc+=tmp.info.dwEffWidth;\r
749         }\r
750         return sum/(xmax-xmin)/(ymax-ymin);\r
751 }\r
752 ////////////////////////////////////////////////////////////////////////////////\r
753 /**\r
754  * 2D linear filter\r
755  * \param kernel: convolving matrix, in row format.\r
756  * \param Ksize: size of the kernel.\r
757  * \param Kfactor: normalization constant.\r
758  * \param Koffset: bias.\r
759  * \verbatim Example: the "soften" filter uses this kernel:\r
760         1 1 1\r
761         1 8 1\r
762         1 1 1\r
763  the function needs: kernel={1,1,1,1,8,1,1,1,1}; Ksize=3; Kfactor=16; Koffset=0; \endverbatim\r
764  * \return true if everything is ok\r
765  */\r
766 bool CxImage::Filter(long* kernel, long Ksize, long Kfactor, long Koffset)\r
767 {\r
768         if (!pDib) return false;\r
769 \r
770         long k2 = Ksize/2;\r
771         long kmax= Ksize-k2;\r
772         long r,g,b,i;\r
773         long ksumcur,ksumtot;\r
774         RGBQUAD c;\r
775 \r
776         CxImage tmp(*this);\r
777         if (!tmp.IsValid()){\r
778                 strcpy(info.szLastError,tmp.GetLastError());\r
779                 return false;\r
780         }\r
781 \r
782         long xmin,xmax,ymin,ymax;\r
783         if (pSelection){\r
784                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
785                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
786         } else {\r
787                 xmin = ymin = 0;\r
788                 xmax = head.biWidth; ymax=head.biHeight;\r
789         }\r
790 \r
791         ksumtot = 0;\r
792         for(long j=-k2;j<kmax;j++){\r
793                 for(long k=-k2;k<kmax;k++){\r
794                         ksumtot += kernel[(j+k2)+Ksize*(k+k2)];\r
795                 }\r
796         }\r
797 \r
798         if ((head.biBitCount==8) && IsGrayScale())\r
799         {\r
800                 unsigned char* cPtr;\r
801                 unsigned char* cPtr2;      \r
802                 int iCount;\r
803                 int iY, iY2, iY1;\r
804                 cPtr = info.pImage;\r
805                 cPtr2 = (unsigned char *)tmp.info.pImage;\r
806                 for(long y=ymin; y<ymax; y++){\r
807                         info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));\r
808                         if (info.nEscape) break;\r
809                         iY1 = y*info.dwEffWidth+xmin;\r
810                         for(long x=xmin; x<xmax; x++, iY1++){\r
811 #if CXIMAGE_SUPPORT_SELECTION\r
812                                 if (BlindSelectionIsInside(x,y))\r
813 #endif //CXIMAGE_SUPPORT_SELECTION\r
814                                 {\r
815                                         b=ksumcur=0;\r
816                                         iCount = 0;\r
817                                         iY2 = ((y-k2)*info.dwEffWidth);\r
818                                         for(long j=-k2;j<kmax;j++, iY2+=info.dwEffWidth)\r
819                                         {\r
820                                                 if (0>(y+j) || (y+j)>=head.biHeight) continue;\r
821                                                 iY = iY2+x;\r
822                                                 for(long k=-k2;k<kmax;k++, iCount++)\r
823                                                 {\r
824                                                         if (0>(x+k) || (x+k)>=head.biWidth) continue;\r
825                                                         i=kernel[iCount];\r
826                                                         b += cPtr[iY+k] * i;\r
827                                                         ksumcur += i;\r
828                                                 }\r
829                                         }\r
830                                         if (Kfactor==0 || ksumcur==0){\r
831                                                 cPtr2[iY1] = (BYTE)min(255, max(0,(int)(b + Koffset)));\r
832                                         } else if (ksumtot == ksumcur) {\r
833                                                 cPtr2[iY1] = (BYTE)min(255, max(0,(int)(b/Kfactor + Koffset)));\r
834                                         } else {\r
835                                                 cPtr2[iY1] = (BYTE)min(255, max(0,(int)((b*ksumtot)/(ksumcur*Kfactor) + Koffset)));\r
836                                         }\r
837                                 }\r
838                         }\r
839                 }\r
840         }\r
841         else\r
842         {\r
843                 for(long y=ymin; y<ymax; y++){\r
844                         info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));\r
845                         if (info.nEscape) break;\r
846                         for(long x=xmin; x<xmax; x++){\r
847         #if CXIMAGE_SUPPORT_SELECTION\r
848                                 if (BlindSelectionIsInside(x,y))\r
849         #endif //CXIMAGE_SUPPORT_SELECTION\r
850                                         {\r
851                                         r=b=g=ksumcur=0;\r
852                                         for(long j=-k2;j<kmax;j++){\r
853                                                 for(long k=-k2;k<kmax;k++){\r
854                                                         if (!IsInside(x+j,y+k)) continue;\r
855                                                         c = BlindGetPixelColor(x+j,y+k);\r
856                                                         i = kernel[(j+k2)+Ksize*(k+k2)];\r
857                                                         r += c.rgbRed * i;\r
858                                                         g += c.rgbGreen * i;\r
859                                                         b += c.rgbBlue * i;\r
860                                                         ksumcur += i;\r
861                                                 }\r
862                                         }\r
863                                         if (Kfactor==0 || ksumcur==0){\r
864                                                 c.rgbRed   = (BYTE)min(255, max(0,(int)(r + Koffset)));\r
865                                                 c.rgbGreen = (BYTE)min(255, max(0,(int)(g + Koffset)));\r
866                                                 c.rgbBlue  = (BYTE)min(255, max(0,(int)(b + Koffset)));\r
867                                         } else if (ksumtot == ksumcur) {\r
868                                                 c.rgbRed   = (BYTE)min(255, max(0,(int)(r/Kfactor + Koffset)));\r
869                                                 c.rgbGreen = (BYTE)min(255, max(0,(int)(g/Kfactor + Koffset)));\r
870                                                 c.rgbBlue  = (BYTE)min(255, max(0,(int)(b/Kfactor + Koffset)));\r
871                                         } else {\r
872                                                 c.rgbRed   = (BYTE)min(255, max(0,(int)((r*ksumtot)/(ksumcur*Kfactor) + Koffset)));\r
873                                                 c.rgbGreen = (BYTE)min(255, max(0,(int)((g*ksumtot)/(ksumcur*Kfactor) + Koffset)));\r
874                                                 c.rgbBlue  = (BYTE)min(255, max(0,(int)((b*ksumtot)/(ksumcur*Kfactor) + Koffset)));\r
875                                         }\r
876                                         tmp.BlindSetPixelColor(x,y,c);\r
877                                 }\r
878                         }\r
879                 }\r
880         }\r
881         Transfer(tmp);\r
882         return true;\r
883 }\r
884 ////////////////////////////////////////////////////////////////////////////////\r
885 /**\r
886  * Enhance the dark areas of the image\r
887  * \param Ksize: size of the kernel.\r
888  * \return true if everything is ok\r
889  */\r
890 bool CxImage::Erode(long Ksize)\r
891 {\r
892         if (!pDib) return false;\r
893 \r
894         long k2 = Ksize/2;\r
895         long kmax= Ksize-k2;\r
896         BYTE r,g,b;\r
897         RGBQUAD c;\r
898 \r
899         CxImage tmp(*this);\r
900         if (!tmp.IsValid()){\r
901                 strcpy(info.szLastError,tmp.GetLastError());\r
902                 return false;\r
903         }\r
904 \r
905         long xmin,xmax,ymin,ymax;\r
906         if (pSelection){\r
907                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
908                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
909         } else {\r
910                 xmin = ymin = 0;\r
911                 xmax = head.biWidth; ymax=head.biHeight;\r
912         }\r
913 \r
914         for(long y=ymin; y<ymax; y++){\r
915                 info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));\r
916                 if (info.nEscape) break;\r
917                 for(long x=xmin; x<xmax; x++){\r
918 #if CXIMAGE_SUPPORT_SELECTION\r
919                         if (BlindSelectionIsInside(x,y))\r
920 #endif //CXIMAGE_SUPPORT_SELECTION\r
921                         {\r
922                                 r=b=g=255;\r
923                                 for(long j=-k2;j<kmax;j++){\r
924                                         for(long k=-k2;k<kmax;k++){\r
925                                                 if (!IsInside(x+j,y+k)) continue;\r
926                                                 c = BlindGetPixelColor(x+j,y+k);\r
927                                                 if (c.rgbRed < r) r=c.rgbRed;\r
928                                                 if (c.rgbGreen < g) g=c.rgbGreen;\r
929                                                 if (c.rgbBlue < b) b=c.rgbBlue;\r
930                                         }\r
931                                 }\r
932                                 c.rgbRed   = r;\r
933                                 c.rgbGreen = g;\r
934                                 c.rgbBlue  = b;\r
935                                 tmp.BlindSetPixelColor(x,y,c);\r
936                         }\r
937                 }\r
938         }\r
939         Transfer(tmp);\r
940         return true;\r
941 }\r
942 ////////////////////////////////////////////////////////////////////////////////\r
943 /**\r
944  * Enhance the light areas of the image\r
945  * \param Ksize: size of the kernel.\r
946  * \return true if everything is ok\r
947  */\r
948 bool CxImage::Dilate(long Ksize)\r
949 {\r
950         if (!pDib) return false;\r
951 \r
952         long k2 = Ksize/2;\r
953         long kmax= Ksize-k2;\r
954         BYTE r,g,b;\r
955         RGBQUAD c;\r
956 \r
957         CxImage tmp(*this);\r
958         if (!tmp.IsValid()){\r
959                 strcpy(info.szLastError,tmp.GetLastError());\r
960                 return false;\r
961         }\r
962 \r
963         long xmin,xmax,ymin,ymax;\r
964         if (pSelection){\r
965                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
966                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
967         } else {\r
968                 xmin = ymin = 0;\r
969                 xmax = head.biWidth; ymax=head.biHeight;\r
970         }\r
971 \r
972         for(long y=ymin; y<ymax; y++){\r
973                 info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));\r
974                 if (info.nEscape) break;\r
975                 for(long x=xmin; x<xmax; x++){\r
976 #if CXIMAGE_SUPPORT_SELECTION\r
977                         if (BlindSelectionIsInside(x,y))\r
978 #endif //CXIMAGE_SUPPORT_SELECTION\r
979                         {\r
980                                 r=b=g=0;\r
981                                 for(long j=-k2;j<kmax;j++){\r
982                                         for(long k=-k2;k<kmax;k++){\r
983                                                 if (!IsInside(x+j,y+k)) continue;\r
984                                                 c = BlindGetPixelColor(x+j,y+k);\r
985                                                 if (c.rgbRed > r) r=c.rgbRed;\r
986                                                 if (c.rgbGreen > g) g=c.rgbGreen;\r
987                                                 if (c.rgbBlue > b) b=c.rgbBlue;\r
988                                         }\r
989                                 }\r
990                                 c.rgbRed   = r;\r
991                                 c.rgbGreen = g;\r
992                                 c.rgbBlue  = b;\r
993                                 tmp.BlindSetPixelColor(x,y,c);\r
994                         }\r
995                 }\r
996         }\r
997         Transfer(tmp);\r
998         return true;\r
999 }\r
1000 ////////////////////////////////////////////////////////////////////////////////\r
1001 /**\r
1002  * Enhance the variations between adjacent pixels.\r
1003  * Similar results can be achieved using Filter(),\r
1004  * but the algorithms are different both in Edge() and in Contour().\r
1005  * \param Ksize: size of the kernel.\r
1006  * \return true if everything is ok\r
1007  */\r
1008 bool CxImage::Edge(long Ksize)\r
1009 {\r
1010         if (!pDib) return false;\r
1011 \r
1012         long k2 = Ksize/2;\r
1013         long kmax= Ksize-k2;\r
1014         BYTE r,g,b,rr,gg,bb;\r
1015         RGBQUAD c;\r
1016 \r
1017         CxImage tmp(*this);\r
1018         if (!tmp.IsValid()){\r
1019                 strcpy(info.szLastError,tmp.GetLastError());\r
1020                 return false;\r
1021         }\r
1022 \r
1023         long xmin,xmax,ymin,ymax;\r
1024         if (pSelection){\r
1025                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
1026                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
1027         } else {\r
1028                 xmin = ymin = 0;\r
1029                 xmax = head.biWidth; ymax=head.biHeight;\r
1030         }\r
1031 \r
1032         for(long y=ymin; y<ymax; y++){\r
1033                 info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));\r
1034                 if (info.nEscape) break;\r
1035                 for(long x=xmin; x<xmax; x++){\r
1036 #if CXIMAGE_SUPPORT_SELECTION\r
1037                         if (BlindSelectionIsInside(x,y))\r
1038 #endif //CXIMAGE_SUPPORT_SELECTION\r
1039                         {\r
1040                                 r=b=g=0;\r
1041                                 rr=bb=gg=255;\r
1042                                 for(long j=-k2;j<kmax;j++){\r
1043                                         for(long k=-k2;k<kmax;k++){\r
1044                                                 if (!IsInside(x+j,y+k)) continue;\r
1045                                                 c = BlindGetPixelColor(x+j,y+k);\r
1046                                                 if (c.rgbRed > r) r=c.rgbRed;\r
1047                                                 if (c.rgbGreen > g) g=c.rgbGreen;\r
1048                                                 if (c.rgbBlue > b) b=c.rgbBlue;\r
1049 \r
1050                                                 if (c.rgbRed < rr) rr=c.rgbRed;\r
1051                                                 if (c.rgbGreen < gg) gg=c.rgbGreen;\r
1052                                                 if (c.rgbBlue < bb) bb=c.rgbBlue;\r
1053                                         }\r
1054                                 }\r
1055                                 c.rgbRed   = (BYTE)(255-abs(r-rr));\r
1056                                 c.rgbGreen = (BYTE)(255-abs(g-gg));\r
1057                                 c.rgbBlue  = (BYTE)(255-abs(b-bb));\r
1058                                 tmp.BlindSetPixelColor(x,y,c);\r
1059                         }\r
1060                 }\r
1061         }\r
1062         Transfer(tmp);\r
1063         return true;\r
1064 }\r
1065 ////////////////////////////////////////////////////////////////////////////////\r
1066 /**\r
1067  * Blends two images\r
1068  * \param imgsrc2: image to be mixed with this\r
1069  * \param op: blending method; see ImageOpType\r
1070  * \param lXOffset, lYOffset: image displacement\r
1071  * \param bMixAlpha: if true and imgsrc2 has a valid alpha layer, it will be mixed in the destination image.\r
1072  * \return true if everything is ok\r
1073  *\r
1074  * thanks to Mwolski\r
1075  */\r
1076 // \r
1077 void CxImage::Mix(CxImage & imgsrc2, ImageOpType op, long lXOffset, long lYOffset, bool bMixAlpha)\r
1078 {\r
1079     long lWide = min(GetWidth(),imgsrc2.GetWidth()-lXOffset);\r
1080     long lHeight = min(GetHeight(),imgsrc2.GetHeight()-lYOffset);\r
1081 \r
1082         bool bEditAlpha = imgsrc2.AlphaIsValid() & bMixAlpha;\r
1083 \r
1084         if (bEditAlpha && AlphaIsValid()==false){\r
1085                 AlphaCreate();\r
1086         }\r
1087 \r
1088     RGBQUAD rgbBackgrnd1 = GetTransColor();\r
1089     RGBQUAD rgb1, rgb2, rgbDest;\r
1090 \r
1091     for(long lY=0;lY<lHeight;lY++)\r
1092     {\r
1093                 info.nProgress = (long)(100*lY/head.biHeight);\r
1094                 if (info.nEscape) break;\r
1095 \r
1096         for(long lX=0;lX<lWide;lX++)\r
1097         {\r
1098 #if CXIMAGE_SUPPORT_SELECTION\r
1099                         if (SelectionIsInside(lX,lY) && imgsrc2.SelectionIsInside(lX+lXOffset,lY+lYOffset))\r
1100 #endif //CXIMAGE_SUPPORT_SELECTION\r
1101                         {\r
1102                                 rgb1 = GetPixelColor(lX,lY);\r
1103                                 rgb2 = imgsrc2.GetPixelColor(lX+lXOffset,lY+lYOffset);\r
1104                                 switch(op)\r
1105                                 {\r
1106                                         case OpAvg:\r
1107                                                 rgbDest.rgbBlue =  (BYTE)((rgb1.rgbBlue+rgb2.rgbBlue)/2);\r
1108                                                 rgbDest.rgbGreen = (BYTE)((rgb1.rgbGreen+rgb2.rgbGreen)/2);\r
1109                                                 rgbDest.rgbRed =   (BYTE)((rgb1.rgbRed+rgb2.rgbRed)/2);\r
1110                                                 if (bEditAlpha) rgbDest.rgbReserved = (BYTE)((rgb1.rgbReserved+rgb2.rgbReserved)/2);\r
1111                                         break;\r
1112                                         case OpAdd:\r
1113                                                 rgbDest.rgbBlue = (BYTE)max(0,min(255,rgb1.rgbBlue+rgb2.rgbBlue));\r
1114                                                 rgbDest.rgbGreen = (BYTE)max(0,min(255,rgb1.rgbGreen+rgb2.rgbGreen));\r
1115                                                 rgbDest.rgbRed = (BYTE)max(0,min(255,rgb1.rgbRed+rgb2.rgbRed));\r
1116                                                 if (bEditAlpha) rgbDest.rgbReserved = (BYTE)max(0,min(255,rgb1.rgbReserved+rgb2.rgbReserved));\r
1117                                         break;\r
1118                                         case OpSub:\r
1119                                                 rgbDest.rgbBlue = (BYTE)max(0,min(255,rgb1.rgbBlue-rgb2.rgbBlue));\r
1120                                                 rgbDest.rgbGreen = (BYTE)max(0,min(255,rgb1.rgbGreen-rgb2.rgbGreen));\r
1121                                                 rgbDest.rgbRed = (BYTE)max(0,min(255,rgb1.rgbRed-rgb2.rgbRed));\r
1122                                                 if (bEditAlpha) rgbDest.rgbReserved = (BYTE)max(0,min(255,rgb1.rgbReserved-rgb2.rgbReserved));\r
1123                                         break;\r
1124                                         case OpAnd:\r
1125                                                 rgbDest.rgbBlue = (BYTE)(rgb1.rgbBlue&rgb2.rgbBlue);\r
1126                                                 rgbDest.rgbGreen = (BYTE)(rgb1.rgbGreen&rgb2.rgbGreen);\r
1127                                                 rgbDest.rgbRed = (BYTE)(rgb1.rgbRed&rgb2.rgbRed);\r
1128                                                 if (bEditAlpha) rgbDest.rgbReserved = (BYTE)(rgb1.rgbReserved&rgb2.rgbReserved);\r
1129                                         break;\r
1130                                         case OpXor:\r
1131                                                 rgbDest.rgbBlue = (BYTE)(rgb1.rgbBlue^rgb2.rgbBlue);\r
1132                                                 rgbDest.rgbGreen = (BYTE)(rgb1.rgbGreen^rgb2.rgbGreen);\r
1133                                                 rgbDest.rgbRed = (BYTE)(rgb1.rgbRed^rgb2.rgbRed);\r
1134                                                 if (bEditAlpha) rgbDest.rgbReserved = (BYTE)(rgb1.rgbReserved^rgb2.rgbReserved);\r
1135                                         break;\r
1136                                         case OpOr:\r
1137                                                 rgbDest.rgbBlue = (BYTE)(rgb1.rgbBlue|rgb2.rgbBlue);\r
1138                                                 rgbDest.rgbGreen = (BYTE)(rgb1.rgbGreen|rgb2.rgbGreen);\r
1139                                                 rgbDest.rgbRed = (BYTE)(rgb1.rgbRed|rgb2.rgbRed);\r
1140                                                 if (bEditAlpha) rgbDest.rgbReserved = (BYTE)(rgb1.rgbReserved|rgb2.rgbReserved);\r
1141                                         break;\r
1142                                         case OpMask:\r
1143                                                 if(rgb2.rgbBlue==0 && rgb2.rgbGreen==0 && rgb2.rgbRed==0)\r
1144                                                         rgbDest = rgbBackgrnd1;\r
1145                                                 else\r
1146                                                         rgbDest = rgb1;\r
1147                                                 break;\r
1148                                         case OpSrcCopy:\r
1149                                                 if(IsTransparent(lX,lY))\r
1150                                                         rgbDest = rgb2;\r
1151                                                 else // copy straight over\r
1152                                                         rgbDest = rgb1;\r
1153                                                 break;\r
1154                                         case OpDstCopy:\r
1155                                                 if(imgsrc2.IsTransparent(lX+lXOffset,lY+lYOffset))\r
1156                                                         rgbDest = rgb1;\r
1157                                                 else // copy straight over\r
1158                                                         rgbDest = rgb2;\r
1159                                                 break;\r
1160                                         case OpScreen:\r
1161                                                 { \r
1162                                                         BYTE a,a1; \r
1163                                                         \r
1164                                                         if (imgsrc2.IsTransparent(lX+lXOffset,lY+lYOffset)){\r
1165                                                                 a=0;\r
1166                                                         } else if (imgsrc2.AlphaIsValid()){\r
1167                                                                 a=imgsrc2.AlphaGet(lX+lXOffset,lY+lYOffset);\r
1168                                                                 a =(BYTE)((a*imgsrc2.info.nAlphaMax)/255);\r
1169                                                         } else {\r
1170                                                                 a=255;\r
1171                                                         }\r
1172 \r
1173                                                         if (a==0){ //transparent \r
1174                                                                 rgbDest = rgb1; \r
1175                                                         } else if (a==255){ //opaque \r
1176                                                                 rgbDest = rgb2; \r
1177                                                         } else { //blend \r
1178                                                                 a1 = (BYTE)~a; \r
1179                                                                 rgbDest.rgbBlue = (BYTE)((rgb1.rgbBlue*a1+rgb2.rgbBlue*a)/255); \r
1180                                                                 rgbDest.rgbGreen = (BYTE)((rgb1.rgbGreen*a1+rgb2.rgbGreen*a)/255); \r
1181                                                                 rgbDest.rgbRed = (BYTE)((rgb1.rgbRed*a1+rgb2.rgbRed*a)/255);  \r
1182                                                         }\r
1183 \r
1184                                                         if (bEditAlpha) rgbDest.rgbReserved = (BYTE)((rgb1.rgbReserved*a)/255);\r
1185                                                 } \r
1186                                                 break; \r
1187                                         case OpSrcBlend:\r
1188                                                 if(IsTransparent(lX,lY))\r
1189                                                         rgbDest = rgb2;\r
1190                                                 else\r
1191                                                 {\r
1192                                                         long lBDiff = abs(rgb1.rgbBlue - rgbBackgrnd1.rgbBlue);\r
1193                                                         long lGDiff = abs(rgb1.rgbGreen - rgbBackgrnd1.rgbGreen);\r
1194                                                         long lRDiff = abs(rgb1.rgbRed - rgbBackgrnd1.rgbRed);\r
1195 \r
1196                                                         double lAverage = (lBDiff+lGDiff+lRDiff)/3;\r
1197                                                         double lThresh = 16;\r
1198                                                         double dLarge = lAverage/lThresh;\r
1199                                                         double dSmall = (lThresh-lAverage)/lThresh;\r
1200                                                         double dSmallAmt = dSmall*((double)rgb2.rgbBlue);\r
1201 \r
1202                                                         if( lAverage < lThresh+1){\r
1203                                                                 rgbDest.rgbBlue = (BYTE)max(0,min(255,(int)(dLarge*((double)rgb1.rgbBlue) +\r
1204                                                                                                 dSmallAmt)));\r
1205                                                                 rgbDest.rgbGreen = (BYTE)max(0,min(255,(int)(dLarge*((double)rgb1.rgbGreen) +\r
1206                                                                                                 dSmallAmt)));\r
1207                                                                 rgbDest.rgbRed = (BYTE)max(0,min(255,(int)(dLarge*((double)rgb1.rgbRed) +\r
1208                                                                                                 dSmallAmt)));\r
1209                                                         }\r
1210                                                         else\r
1211                                                                 rgbDest = rgb1;\r
1212                                                 }\r
1213                                                 break;\r
1214                                                 default:\r
1215                                                 return;\r
1216                                 }\r
1217                                 SetPixelColor(lX,lY,rgbDest,bEditAlpha);\r
1218                         }\r
1219                 }\r
1220         }\r
1221 }\r
1222 ////////////////////////////////////////////////////////////////////////////////\r
1223 // thanks to Kenneth Ballard\r
1224 void CxImage::MixFrom(CxImage & imagesrc2, long lXOffset, long lYOffset)\r
1225 {\r
1226     long width = imagesrc2.GetWidth();\r
1227     long height = imagesrc2.GetHeight();\r
1228 \r
1229     int x, y;\r
1230 \r
1231         if (imagesrc2.IsTransparent()) {\r
1232                 for(x = 0; x < width; x++) {\r
1233                         for(y = 0; y < height; y++) {\r
1234                                 if(!imagesrc2.IsTransparent(x,y)){\r
1235                                         SetPixelColor(x + lXOffset, y + lYOffset, imagesrc2.BlindGetPixelColor(x, y));\r
1236                                 }\r
1237                         }\r
1238                 }\r
1239         } else { //no transparency so just set it <Matt>\r
1240                 for(x = 0; x < width; x++) {\r
1241                         for(y = 0; y < height; y++) {\r
1242                                 SetPixelColor(x + lXOffset, y + lYOffset, imagesrc2.BlindGetPixelColor(x, y)); \r
1243                         }\r
1244                 }\r
1245         }\r
1246 }\r
1247 ////////////////////////////////////////////////////////////////////////////////\r
1248 /**\r
1249  * Adjusts separately the red, green, and blue values in the image.\r
1250  * \param r, g, b: can be from -255 to +255.\r
1251  * \return true if everything is ok\r
1252  */\r
1253 bool CxImage::ShiftRGB(long r, long g, long b)\r
1254 {\r
1255         if (!pDib) return false;\r
1256         RGBQUAD color;\r
1257         if (head.biClrUsed==0){\r
1258 \r
1259                 long xmin,xmax,ymin,ymax;\r
1260                 if (pSelection){\r
1261                         xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
1262                         ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
1263                 } else {\r
1264                         xmin = ymin = 0;\r
1265                         xmax = head.biWidth; ymax=head.biHeight;\r
1266                 }\r
1267 \r
1268                 for(long y=ymin; y<ymax; y++){\r
1269                         for(long x=xmin; x<xmax; x++){\r
1270 #if CXIMAGE_SUPPORT_SELECTION\r
1271                                 if (BlindSelectionIsInside(x,y))\r
1272 #endif //CXIMAGE_SUPPORT_SELECTION\r
1273                                 {\r
1274                                         color = BlindGetPixelColor(x,y);\r
1275                                         color.rgbRed = (BYTE)max(0,min(255,(int)(color.rgbRed + r)));\r
1276                                         color.rgbGreen = (BYTE)max(0,min(255,(int)(color.rgbGreen + g)));\r
1277                                         color.rgbBlue = (BYTE)max(0,min(255,(int)(color.rgbBlue + b)));\r
1278                                         BlindSetPixelColor(x,y,color);\r
1279                                 }\r
1280                         }\r
1281                 }\r
1282         } else {\r
1283                 for(DWORD j=0; j<head.biClrUsed; j++){\r
1284                         color = GetPaletteColor((BYTE)j);\r
1285                         color.rgbRed = (BYTE)max(0,min(255,(int)(color.rgbRed + r)));\r
1286                         color.rgbGreen = (BYTE)max(0,min(255,(int)(color.rgbGreen + g)));\r
1287                         color.rgbBlue = (BYTE)max(0,min(255,(int)(color.rgbBlue + b)));\r
1288                         SetPaletteColor((BYTE)j,color);\r
1289                 }\r
1290         }\r
1291         return true;\r
1292 }\r
1293 ////////////////////////////////////////////////////////////////////////////////\r
1294 /**\r
1295  * Adjusts the color balance of the image\r
1296  * \param gamma can be from 0.1 to 5.\r
1297  * \return true if everything is ok\r
1298  * \sa GammaRGB\r
1299  */\r
1300 bool CxImage::Gamma(float gamma)\r
1301 {\r
1302         if (!pDib) return false;\r
1303 \r
1304         if (gamma <= 0.0f) return false;\r
1305 \r
1306         double dinvgamma = 1/gamma;\r
1307         double dMax = pow(255.0, dinvgamma) / 255.0;\r
1308 \r
1309         BYTE cTable[256]; //<nipper>\r
1310         for (int i=0;i<256;i++) {\r
1311                 cTable[i] = (BYTE)max(0,min(255,(int)( pow((double)i, dinvgamma) / dMax)));\r
1312         }\r
1313 \r
1314         return Lut(cTable);\r
1315 }\r
1316 ////////////////////////////////////////////////////////////////////////////////\r
1317 /**\r
1318  * Adjusts the color balance indipendent for each color channel\r
1319  * \param gammaR, gammaG, gammaB  can be from 0.1 to 5.\r
1320  * \return true if everything is ok\r
1321  * \sa Gamma\r
1322  */\r
1323 bool CxImage::GammaRGB(float gammaR, float gammaG, float gammaB)\r
1324 {\r
1325         if (!pDib) return false;\r
1326 \r
1327         if (gammaR <= 0.0f) return false;\r
1328         if (gammaG <= 0.0f) return false;\r
1329         if (gammaB <= 0.0f) return false;\r
1330 \r
1331         double dinvgamma, dMax;\r
1332         int i;\r
1333 \r
1334         dinvgamma = 1/gammaR;\r
1335         dMax = pow(255.0, dinvgamma) / 255.0;\r
1336         BYTE cTableR[256];\r
1337         for (i=0;i<256;i++)     {\r
1338                 cTableR[i] = (BYTE)max(0,min(255,(int)( pow((double)i, dinvgamma) / dMax)));\r
1339         }\r
1340 \r
1341         dinvgamma = 1/gammaG;\r
1342         dMax = pow(255.0, dinvgamma) / 255.0;\r
1343         BYTE cTableG[256];\r
1344         for (i=0;i<256;i++)     {\r
1345                 cTableG[i] = (BYTE)max(0,min(255,(int)( pow((double)i, dinvgamma) / dMax)));\r
1346         }\r
1347 \r
1348         dinvgamma = 1/gammaB;\r
1349         dMax = pow(255.0, dinvgamma) / 255.0;\r
1350         BYTE cTableB[256];\r
1351         for (i=0;i<256;i++)     {\r
1352                 cTableB[i] = (BYTE)max(0,min(255,(int)( pow((double)i, dinvgamma) / dMax)));\r
1353         }\r
1354 \r
1355         return Lut(cTableR, cTableG, cTableB);\r
1356 }\r
1357 ////////////////////////////////////////////////////////////////////////////////\r
1358 \r
1359 //#if !defined (_WIN32_WCE)\r
1360 /**\r
1361  * Adjusts the intensity of each pixel to the median intensity of its surrounding pixels.\r
1362  * \param Ksize: size of the kernel.\r
1363  * \return true if everything is ok\r
1364  */\r
1365 bool CxImage::Median(long Ksize)\r
1366 {\r
1367         if (!pDib) return false;\r
1368 \r
1369         long k2 = Ksize/2;\r
1370         long kmax= Ksize-k2;\r
1371         long i,j,k;\r
1372 \r
1373         RGBQUAD* kernel = (RGBQUAD*)malloc(Ksize*Ksize*sizeof(RGBQUAD));\r
1374 \r
1375         CxImage tmp(*this);\r
1376         if (!tmp.IsValid()){\r
1377                 strcpy(info.szLastError,tmp.GetLastError());\r
1378                 return false;\r
1379         }\r
1380 \r
1381         long xmin,xmax,ymin,ymax;\r
1382         if (pSelection){\r
1383                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
1384                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
1385         } else {\r
1386                 xmin = ymin = 0;\r
1387                 xmax = head.biWidth; ymax=head.biHeight;\r
1388         }\r
1389 \r
1390         for(long y=ymin; y<ymax; y++){\r
1391                 info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));\r
1392                 if (info.nEscape) break;\r
1393                 for(long x=xmin; x<xmax; x++){\r
1394 #if CXIMAGE_SUPPORT_SELECTION\r
1395                         if (BlindSelectionIsInside(x,y))\r
1396 #endif //CXIMAGE_SUPPORT_SELECTION\r
1397                                 {\r
1398                                 for(j=-k2, i=0;j<kmax;j++)\r
1399                                         for(k=-k2;k<kmax;k++)\r
1400                                                 if (IsInside(x+j,y+k))\r
1401                                                         kernel[i++]=BlindGetPixelColor(x+j,y+k);\r
1402 \r
1403                                 qsort(kernel, i, sizeof(RGBQUAD), CompareColors);\r
1404                                 tmp.SetPixelColor(x,y,kernel[i/2]);\r
1405                         }\r
1406                 }\r
1407         }\r
1408         free(kernel);\r
1409         Transfer(tmp);\r
1410         return true;\r
1411 }\r
1412 //#endif //_WIN32_WCE\r
1413 ////////////////////////////////////////////////////////////////////////////////\r
1414 /**\r
1415  * Adds an uniform noise to the image\r
1416  * \param level: can be from 0 (no noise) to 255 (lot of noise).\r
1417  * \return true if everything is ok\r
1418  */\r
1419 bool CxImage::Noise(long level)\r
1420 {\r
1421         if (!pDib) return false;\r
1422         RGBQUAD color;\r
1423 \r
1424         long xmin,xmax,ymin,ymax,n;\r
1425         if (pSelection){\r
1426                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
1427                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
1428         } else {\r
1429                 xmin = ymin = 0;\r
1430                 xmax = head.biWidth; ymax=head.biHeight;\r
1431         }\r
1432 \r
1433         for(long y=ymin; y<ymax; y++){\r
1434                 info.nProgress = (long)(100*(y-ymin)/(ymax-ymin)); //<zhanghk><Anatoly Ivasyuk>\r
1435                 for(long x=xmin; x<xmax; x++){\r
1436 #if CXIMAGE_SUPPORT_SELECTION\r
1437                         if (BlindSelectionIsInside(x,y))\r
1438 #endif //CXIMAGE_SUPPORT_SELECTION\r
1439                         {\r
1440                                 color = BlindGetPixelColor(x,y);\r
1441                                 n=(long)((rand()/(float)RAND_MAX - 0.5)*level);\r
1442                                 color.rgbRed = (BYTE)max(0,min(255,(int)(color.rgbRed + n)));\r
1443                                 n=(long)((rand()/(float)RAND_MAX - 0.5)*level);\r
1444                                 color.rgbGreen = (BYTE)max(0,min(255,(int)(color.rgbGreen + n)));\r
1445                                 n=(long)((rand()/(float)RAND_MAX - 0.5)*level);\r
1446                                 color.rgbBlue = (BYTE)max(0,min(255,(int)(color.rgbBlue + n)));\r
1447                                 BlindSetPixelColor(x,y,color);\r
1448                         }\r
1449                 }\r
1450         }\r
1451         return true;\r
1452 }\r
1453 ////////////////////////////////////////////////////////////////////////////////\r
1454 /**\r
1455  * Computes the bidimensional FFT or DFT of the image.\r
1456  * - The images are processed as grayscale\r
1457  * - If the dimensions of the image are a power of, 2 the FFT is performed automatically.\r
1458  * - If dstReal and/or dstImag are NULL, the resulting images replaces the original(s).\r
1459  * - Note: with 8 bits there is a HUGE loss in the dynamics. The function tries\r
1460  *   to keep an acceptable SNR, but 8bit = 48dB...\r
1461  *\r
1462  * \param srcReal, srcImag: source images: One can be NULL, but not both\r
1463  * \param dstReal, dstImag: destination images. Can be NULL.\r
1464  * \param direction: 1 = forward, -1 = inverse.\r
1465  * \param bForceFFT: if true, the images are resampled to make the dimensions a power of 2.\r
1466  * \param bMagnitude: if true, the real part returns the magnitude, the imaginary part returns the phase\r
1467  * \return true if everything is ok\r
1468  */\r
1469 bool CxImage::FFT2(CxImage* srcReal, CxImage* srcImag, CxImage* dstReal, CxImage* dstImag,\r
1470                                    long direction, bool bForceFFT, bool bMagnitude)\r
1471 {\r
1472         //check if there is something to convert\r
1473         if (srcReal==NULL && srcImag==NULL) return false;\r
1474 \r
1475         long w,h;\r
1476         //get width and height\r
1477         if (srcReal) {\r
1478                 w=srcReal->GetWidth();\r
1479                 h=srcReal->GetHeight();\r
1480         } else {\r
1481                 w=srcImag->GetWidth();\r
1482                 h=srcImag->GetHeight();\r
1483         }\r
1484 \r
1485         bool bXpow2 = IsPowerof2(w);\r
1486         bool bYpow2 = IsPowerof2(h);\r
1487         //if bForceFFT, width AND height must be powers of 2\r
1488         if (bForceFFT && !(bXpow2 && bYpow2)) {\r
1489                 long i;\r
1490                 \r
1491                 i=0;\r
1492                 while((1<<i)<w) i++;\r
1493                 w=1<<i;\r
1494                 bXpow2=true;\r
1495 \r
1496                 i=0;\r
1497                 while((1<<i)<h) i++;\r
1498                 h=1<<i;\r
1499                 bYpow2=true;\r
1500         }\r
1501 \r
1502         // I/O images for FFT\r
1503         CxImage *tmpReal,*tmpImag;\r
1504 \r
1505         // select output\r
1506         tmpReal = (dstReal) ? dstReal : srcReal;\r
1507         tmpImag = (dstImag) ? dstImag : srcImag;\r
1508 \r
1509         // src!=dst -> copy the image\r
1510         if (srcReal && dstReal) tmpReal->Copy(*srcReal,true,false,false);\r
1511         if (srcImag && dstImag) tmpImag->Copy(*srcImag,true,false,false);\r
1512 \r
1513         // dst&&src are empty -> create new one, else turn to GrayScale\r
1514         if (srcReal==0 && dstReal==0){\r
1515                 tmpReal = new CxImage(w,h,8);\r
1516                 tmpReal->Clear(0);\r
1517                 tmpReal->SetGrayPalette();\r
1518         } else {\r
1519                 if (!tmpReal->IsGrayScale()) tmpReal->GrayScale();\r
1520         }\r
1521         if (srcImag==0 && dstImag==0){\r
1522                 tmpImag = new CxImage(w,h,8);\r
1523                 tmpImag->Clear(0);\r
1524                 tmpImag->SetGrayPalette();\r
1525         } else {\r
1526                 if (!tmpImag->IsGrayScale()) tmpImag->GrayScale();\r
1527         }\r
1528 \r
1529         if (!(tmpReal->IsValid() && tmpImag->IsValid())){\r
1530                 if (srcReal==0 && dstReal==0) delete tmpReal;\r
1531                 if (srcImag==0 && dstImag==0) delete tmpImag;\r
1532                 return false;\r
1533         }\r
1534 \r
1535         //resample for FFT, if necessary \r
1536         tmpReal->Resample(w,h,0);\r
1537         tmpImag->Resample(w,h,0);\r
1538 \r
1539         //ok, here we have 2 (w x h), grayscale images ready for a FFT\r
1540 \r
1541         double* real;\r
1542         double* imag;\r
1543         long j,k,m;\r
1544 \r
1545         _complex **grid;\r
1546         //double mean = tmpReal->Mean();\r
1547         /* Allocate memory for the grid */\r
1548         grid = (_complex **)malloc(w * sizeof(_complex));\r
1549         for (k=0;k<w;k++) {\r
1550                 grid[k] = (_complex *)malloc(h * sizeof(_complex));\r
1551         }\r
1552         for (j=0;j<h;j++) {\r
1553                 for (k=0;k<w;k++) {\r
1554                         grid[k][j].x = tmpReal->GetPixelIndex(k,j)-128;\r
1555                         grid[k][j].y = tmpImag->GetPixelIndex(k,j)-128;\r
1556                 }\r
1557         }\r
1558 \r
1559         //DFT buffers\r
1560         double *real2,*imag2;\r
1561         real2 = (double*)malloc(max(w,h) * sizeof(double));\r
1562         imag2 = (double*)malloc(max(w,h) * sizeof(double));\r
1563 \r
1564         /* Transform the rows */\r
1565         real = (double *)malloc(w * sizeof(double));\r
1566         imag = (double *)malloc(w * sizeof(double));\r
1567 \r
1568         m=0;\r
1569         while((1<<m)<w) m++;\r
1570 \r
1571         for (j=0;j<h;j++) {\r
1572                 for (k=0;k<w;k++) {\r
1573                         real[k] = grid[k][j].x;\r
1574                         imag[k] = grid[k][j].y;\r
1575                 }\r
1576 \r
1577                 if (bXpow2) FFT(direction,m,real,imag);\r
1578                 else            DFT(direction,w,real,imag,real2,imag2);\r
1579 \r
1580                 for (k=0;k<w;k++) {\r
1581                         grid[k][j].x = real[k];\r
1582                         grid[k][j].y = imag[k];\r
1583                 }\r
1584         }\r
1585         free(real);\r
1586         free(imag);\r
1587 \r
1588         /* Transform the columns */\r
1589         real = (double *)malloc(h * sizeof(double));\r
1590         imag = (double *)malloc(h * sizeof(double));\r
1591 \r
1592         m=0;\r
1593         while((1<<m)<h) m++;\r
1594 \r
1595         for (k=0;k<w;k++) {\r
1596                 for (j=0;j<h;j++) {\r
1597                         real[j] = grid[k][j].x;\r
1598                         imag[j] = grid[k][j].y;\r
1599                 }\r
1600 \r
1601                 if (bYpow2) FFT(direction,m,real,imag);\r
1602                 else            DFT(direction,h,real,imag,real2,imag2);\r
1603 \r
1604                 for (j=0;j<h;j++) {\r
1605                         grid[k][j].x = real[j];\r
1606                         grid[k][j].y = imag[j];\r
1607                 }\r
1608         }\r
1609         free(real);\r
1610         free(imag);\r
1611 \r
1612         free(real2);\r
1613         free(imag2);\r
1614 \r
1615         /* converting from double to byte, there is a HUGE loss in the dynamics\r
1616           "nn" tries to keep an acceptable SNR, but 8bit=48dB: don't ask more */\r
1617         double nn=pow((double)2,(double)log((double)max(w,h))/(double)log((double)2)-4);\r
1618         //reversed gain for reversed transform\r
1619         if (direction==-1) nn=1/nn;\r
1620         //bMagnitude : just to see it on the screen\r
1621         if (bMagnitude) nn*=4;\r
1622 \r
1623         for (j=0;j<h;j++) {\r
1624                 for (k=0;k<w;k++) {\r
1625                         if (bMagnitude){\r
1626                                 tmpReal->SetPixelIndex(k,j,(BYTE)max(0,min(255,(nn*(3+log(_cabs(grid[k][j])))))));\r
1627                                 if (grid[k][j].x==0){\r
1628                                         tmpImag->SetPixelIndex(k,j,(BYTE)max(0,min(255,(128+(atan(grid[k][j].y/0.0000000001)*nn)))));\r
1629                                 } else {\r
1630                                         tmpImag->SetPixelIndex(k,j,(BYTE)max(0,min(255,(128+(atan(grid[k][j].y/grid[k][j].x)*nn)))));\r
1631                                 }\r
1632                         } else {\r
1633                                 tmpReal->SetPixelIndex(k,j,(BYTE)max(0,min(255,(128 + grid[k][j].x*nn))));\r
1634                                 tmpImag->SetPixelIndex(k,j,(BYTE)max(0,min(255,(128 + grid[k][j].y*nn))));\r
1635                         }\r
1636                 }\r
1637         }\r
1638 \r
1639         for (k=0;k<w;k++) free (grid[k]);\r
1640         free (grid);\r
1641 \r
1642         if (srcReal==0 && dstReal==0) delete tmpReal;\r
1643         if (srcImag==0 && dstImag==0) delete tmpImag;\r
1644 \r
1645         return true;\r
1646 }\r
1647 ////////////////////////////////////////////////////////////////////////////////\r
1648 bool CxImage::IsPowerof2(long x)\r
1649 {\r
1650         long i=0;\r
1651         while ((1<<i)<x) i++;\r
1652         if (x==(1<<i)) return true;\r
1653         return false;\r
1654 }\r
1655 ////////////////////////////////////////////////////////////////////////////////\r
1656 /**\r
1657    This computes an in-place complex-to-complex FFT \r
1658    x and y are the real and imaginary arrays of n=2^m points.\r
1659    o(n)=n*log2(n)\r
1660    dir =  1 gives forward transform\r
1661    dir = -1 gives reverse transform \r
1662    Written by Paul Bourke, July 1998\r
1663    FFT algorithm by Cooley and Tukey, 1965 \r
1664 */\r
1665 bool CxImage::FFT(int dir,int m,double *x,double *y)\r
1666 {\r
1667         long nn,i,i1,j,k,i2,l,l1,l2;\r
1668         double c1,c2,tx,ty,t1,t2,u1,u2,z;\r
1669 \r
1670         /* Calculate the number of points */\r
1671         nn = 1<<m;\r
1672 \r
1673         /* Do the bit reversal */\r
1674         i2 = nn >> 1;\r
1675         j = 0;\r
1676         for (i=0;i<nn-1;i++) {\r
1677                 if (i < j) {\r
1678                         tx = x[i];\r
1679                         ty = y[i];\r
1680                         x[i] = x[j];\r
1681                         y[i] = y[j];\r
1682                         x[j] = tx;\r
1683                         y[j] = ty;\r
1684                 }\r
1685                 k = i2;\r
1686                 while (k <= j) {\r
1687                         j -= k;\r
1688                         k >>= 1;\r
1689                 }\r
1690                 j += k;\r
1691         }\r
1692 \r
1693         /* Compute the FFT */\r
1694         c1 = -1.0;\r
1695         c2 = 0.0;\r
1696         l2 = 1;\r
1697         for (l=0;l<m;l++) {\r
1698                 l1 = l2;\r
1699                 l2 <<= 1;\r
1700                 u1 = 1.0;\r
1701                 u2 = 0.0;\r
1702                 for (j=0;j<l1;j++) {\r
1703                         for (i=j;i<nn;i+=l2) {\r
1704                                 i1 = i + l1;\r
1705                                 t1 = u1 * x[i1] - u2 * y[i1];\r
1706                                 t2 = u1 * y[i1] + u2 * x[i1];\r
1707                                 x[i1] = x[i] - t1;\r
1708                                 y[i1] = y[i] - t2;\r
1709                                 x[i] += t1;\r
1710                                 y[i] += t2;\r
1711                         }\r
1712                         z =  u1 * c1 - u2 * c2;\r
1713                         u2 = u1 * c2 + u2 * c1;\r
1714                         u1 = z;\r
1715                 }\r
1716                 c2 = sqrt((1.0 - c1) / 2.0);\r
1717                 if (dir == 1)\r
1718                         c2 = -c2;\r
1719                 c1 = sqrt((1.0 + c1) / 2.0);\r
1720         }\r
1721 \r
1722         /* Scaling for forward transform */\r
1723         if (dir == 1) {\r
1724                 for (i=0;i<nn;i++) {\r
1725                         x[i] /= (double)nn;\r
1726                         y[i] /= (double)nn;\r
1727                 }\r
1728         }\r
1729 \r
1730    return true;\r
1731 }\r
1732 ////////////////////////////////////////////////////////////////////////////////\r
1733 /**\r
1734    Direct fourier transform o(n)=n^2\r
1735    Written by Paul Bourke, July 1998 \r
1736 */\r
1737 bool CxImage::DFT(int dir,long m,double *x1,double *y1,double *x2,double *y2)\r
1738 {\r
1739    long i,k;\r
1740    double arg;\r
1741    double cosarg,sinarg;\r
1742    \r
1743    for (i=0;i<m;i++) {\r
1744       x2[i] = 0;\r
1745       y2[i] = 0;\r
1746       arg = - dir * 2.0 * PI * i / (double)m;\r
1747       for (k=0;k<m;k++) {\r
1748          cosarg = cos(k * arg);\r
1749          sinarg = sin(k * arg);\r
1750          x2[i] += (x1[k] * cosarg - y1[k] * sinarg);\r
1751          y2[i] += (x1[k] * sinarg + y1[k] * cosarg);\r
1752       }\r
1753    }\r
1754    \r
1755    /* Copy the data back */\r
1756    if (dir == 1) {\r
1757       for (i=0;i<m;i++) {\r
1758          x1[i] = x2[i] / m;\r
1759          y1[i] = y2[i] / m;\r
1760       }\r
1761    } else {\r
1762       for (i=0;i<m;i++) {\r
1763          x1[i] = x2[i];\r
1764          y1[i] = y2[i];\r
1765       }\r
1766    }\r
1767    \r
1768    return true;\r
1769 }\r
1770 ////////////////////////////////////////////////////////////////////////////////\r
1771 /**\r
1772  * Combines different color components into a single image\r
1773  * \param r,g,b: color channels\r
1774  * \param a: alpha layer, can be NULL\r
1775  * \param colorspace: 0 = RGB, 1 = HSL, 2 = YUV, 3 = YIQ, 4 = XYZ \r
1776  * \return true if everything is ok\r
1777  */\r
1778 bool CxImage::Combine(CxImage* r,CxImage* g,CxImage* b,CxImage* a, long colorspace)\r
1779 {\r
1780         if (r==0 || g==0 || b==0) return false;\r
1781 \r
1782         long w = r->GetWidth();\r
1783         long h = r->GetHeight();\r
1784 \r
1785         Create(w,h,24);\r
1786 \r
1787         g->Resample(w,h);\r
1788         b->Resample(w,h);\r
1789 \r
1790         if (a) {\r
1791                 a->Resample(w,h);\r
1792 #if CXIMAGE_SUPPORT_ALPHA\r
1793                 AlphaCreate();\r
1794 #endif //CXIMAGE_SUPPORT_ALPHA\r
1795         }\r
1796 \r
1797         RGBQUAD c;\r
1798         for (long y=0;y<h;y++){\r
1799                 info.nProgress = (long)(100*y/h); //<Anatoly Ivasyuk>\r
1800                 for (long x=0;x<w;x++){\r
1801                         c.rgbRed=r->GetPixelIndex(x,y);\r
1802                         c.rgbGreen=g->GetPixelIndex(x,y);\r
1803                         c.rgbBlue=b->GetPixelIndex(x,y);\r
1804                         switch (colorspace){\r
1805                         case 1:\r
1806                                 BlindSetPixelColor(x,y,HSLtoRGB(c));\r
1807                                 break;\r
1808                         case 2:\r
1809                                 BlindSetPixelColor(x,y,YUVtoRGB(c));\r
1810                                 break;\r
1811                         case 3:\r
1812                                 BlindSetPixelColor(x,y,YIQtoRGB(c));\r
1813                                 break;\r
1814                         case 4:\r
1815                                 BlindSetPixelColor(x,y,XYZtoRGB(c));\r
1816                                 break;\r
1817                         default:\r
1818                                 BlindSetPixelColor(x,y,c);\r
1819                         }\r
1820 #if CXIMAGE_SUPPORT_ALPHA\r
1821                         if (a) AlphaSet(x,y,a->GetPixelIndex(x,y));\r
1822 #endif //CXIMAGE_SUPPORT_ALPHA\r
1823                 }\r
1824         }\r
1825 \r
1826         return true;\r
1827 }\r
1828 ////////////////////////////////////////////////////////////////////////////////\r
1829 /**\r
1830  * Smart blurring to remove small defects, dithering or artifacts.\r
1831  * \param radius: normally between 0.01 and 0.5\r
1832  * \param niterations: should be trimmed with radius, to avoid blurring should be (radius*niterations)<1\r
1833  * \param colorspace: 0 = RGB, 1 = HSL, 2 = YUV, 3 = YIQ, 4 = XYZ \r
1834  * \return true if everything is ok\r
1835  */\r
1836 bool CxImage::Repair(float radius, long niterations, long colorspace)\r
1837 {\r
1838         if (!IsValid()) return false;\r
1839 \r
1840         long w = GetWidth();\r
1841         long h = GetHeight();\r
1842 \r
1843         CxImage r,g,b;\r
1844 \r
1845         r.Create(w,h,8);\r
1846         g.Create(w,h,8);\r
1847         b.Create(w,h,8);\r
1848 \r
1849         switch (colorspace){\r
1850         case 1:\r
1851                 SplitHSL(&r,&g,&b);\r
1852                 break;\r
1853         case 2:\r
1854                 SplitYUV(&r,&g,&b);\r
1855                 break;\r
1856         case 3:\r
1857                 SplitYIQ(&r,&g,&b);\r
1858                 break;\r
1859         case 4:\r
1860                 SplitXYZ(&r,&g,&b);\r
1861                 break;\r
1862         default:\r
1863                 SplitRGB(&r,&g,&b);\r
1864         }\r
1865         \r
1866         for (int i=0; i<niterations; i++){\r
1867                 RepairChannel(&r,radius);\r
1868                 RepairChannel(&g,radius);\r
1869                 RepairChannel(&b,radius);\r
1870         }\r
1871 \r
1872         CxImage* a=NULL;\r
1873 #if CXIMAGE_SUPPORT_ALPHA\r
1874         if (AlphaIsValid()){\r
1875                 a = new CxImage();\r
1876                 AlphaSplit(a);\r
1877         }\r
1878 #endif\r
1879 \r
1880         Combine(&r,&g,&b,a,colorspace);\r
1881 \r
1882         delete a;\r
1883 \r
1884         return true;\r
1885 }\r
1886 ////////////////////////////////////////////////////////////////////////////////\r
1887 bool CxImage::RepairChannel(CxImage *ch, float radius)\r
1888 {\r
1889         if (ch==NULL) return false;\r
1890 \r
1891         CxImage tmp(*ch);\r
1892         if (!tmp.IsValid()){\r
1893                 strcpy(info.szLastError,tmp.GetLastError());\r
1894                 return false;\r
1895         }\r
1896 \r
1897         long w = ch->GetWidth()-1;\r
1898         long h = ch->GetHeight()-1;\r
1899 \r
1900         double correction,ix,iy,ixx,ixy,iyy;\r
1901         int x,y,xy0,xp1,xm1,yp1,ym1;\r
1902 \r
1903         for(x=1; x<w; x++){\r
1904                 for(y=1; y<h; y++){\r
1905 \r
1906                         xy0 = ch->BlindGetPixelIndex(x,y);\r
1907                         xm1 = ch->BlindGetPixelIndex(x-1,y);\r
1908                         xp1 = ch->BlindGetPixelIndex(x+1,y);\r
1909                         ym1 = ch->BlindGetPixelIndex(x,y-1);\r
1910                         yp1 = ch->BlindGetPixelIndex(x,y+1);\r
1911 \r
1912                         ix= (xp1-xm1)/2.0;\r
1913                         iy= (yp1-ym1)/2.0;\r
1914                         ixx= xp1 - 2.0 * xy0 + xm1;\r
1915                         iyy= yp1 - 2.0 * xy0 + ym1;\r
1916                         ixy=(ch->BlindGetPixelIndex(x+1,y+1) + ch->BlindGetPixelIndex(x-1,y-1) -\r
1917                                  ch->BlindGetPixelIndex(x-1,y+1) - ch->BlindGetPixelIndex(x+1,y-1))/4.0;\r
1918 \r
1919                         correction = ((1.0+iy*iy)*ixx - ix*iy*ixy + (1.0+ix*ix)*iyy)/(1.0+ix*ix+iy*iy);\r
1920 \r
1921                         tmp.BlindSetPixelIndex(x,y,(BYTE)min(255,max(0,(xy0 + radius * correction + 0.5))));\r
1922                 }\r
1923         }\r
1924 \r
1925         for (x=0;x<=w;x++){\r
1926                 for(y=0; y<=h; y+=h){\r
1927                         xy0 = ch->BlindGetPixelIndex(x,y);\r
1928                         xm1 = ch->GetPixelIndex(x-1,y);\r
1929                         xp1 = ch->GetPixelIndex(x+1,y);\r
1930                         ym1 = ch->GetPixelIndex(x,y-1);\r
1931                         yp1 = ch->GetPixelIndex(x,y+1);\r
1932 \r
1933                         ix= (xp1-xm1)/2.0;\r
1934                         iy= (yp1-ym1)/2.0;\r
1935                         ixx= xp1 - 2.0 * xy0 + xm1;\r
1936                         iyy= yp1 - 2.0 * xy0 + ym1;\r
1937                         ixy=(ch->GetPixelIndex(x+1,y+1) + ch->GetPixelIndex(x-1,y-1) -\r
1938                                  ch->GetPixelIndex(x-1,y+1) - ch->GetPixelIndex(x+1,y-1))/4.0;\r
1939 \r
1940                         correction = ((1.0+iy*iy)*ixx - ix*iy*ixy + (1.0+ix*ix)*iyy)/(1.0+ix*ix+iy*iy);\r
1941 \r
1942                         tmp.BlindSetPixelIndex(x,y,(BYTE)min(255,max(0,(xy0 + radius * correction + 0.5))));\r
1943                 }\r
1944         }\r
1945         for (x=0;x<=w;x+=w){\r
1946                 for (y=0;y<=h;y++){\r
1947                         xy0 = ch->BlindGetPixelIndex(x,y);\r
1948                         xm1 = ch->GetPixelIndex(x-1,y);\r
1949                         xp1 = ch->GetPixelIndex(x+1,y);\r
1950                         ym1 = ch->GetPixelIndex(x,y-1);\r
1951                         yp1 = ch->GetPixelIndex(x,y+1);\r
1952 \r
1953                         ix= (xp1-xm1)/2.0;\r
1954                         iy= (yp1-ym1)/2.0;\r
1955                         ixx= xp1 - 2.0 * xy0 + xm1;\r
1956                         iyy= yp1 - 2.0 * xy0 + ym1;\r
1957                         ixy=(ch->GetPixelIndex(x+1,y+1) + ch->GetPixelIndex(x-1,y-1) -\r
1958                                  ch->GetPixelIndex(x-1,y+1) - ch->GetPixelIndex(x+1,y-1))/4.0;\r
1959 \r
1960                         correction = ((1.0+iy*iy)*ixx - ix*iy*ixy + (1.0+ix*ix)*iyy)/(1.0+ix*ix+iy*iy);\r
1961 \r
1962                         tmp.BlindSetPixelIndex(x,y,(BYTE)min(255,max(0,(xy0 + radius * correction + 0.5))));\r
1963                 }\r
1964         }\r
1965 \r
1966         ch->Transfer(tmp);\r
1967         return true;\r
1968 }\r
1969 ////////////////////////////////////////////////////////////////////////////////\r
1970 /**\r
1971  * Enhance the variations between adjacent pixels.\r
1972  * Similar results can be achieved using Filter(),\r
1973  * but the algorithms are different both in Edge() and in Contour().\r
1974  * \return true if everything is ok\r
1975  */\r
1976 bool CxImage::Contour()\r
1977 {\r
1978         if (!pDib) return false;\r
1979 \r
1980         long Ksize = 3;\r
1981         long k2 = Ksize/2;\r
1982         long kmax= Ksize-k2;\r
1983         long i,j,k;\r
1984         BYTE maxr,maxg,maxb;\r
1985         RGBQUAD pix1,pix2;\r
1986 \r
1987         CxImage tmp(*this);\r
1988         if (!tmp.IsValid()){\r
1989                 strcpy(info.szLastError,tmp.GetLastError());\r
1990                 return false;\r
1991         }\r
1992 \r
1993         long xmin,xmax,ymin,ymax;\r
1994         if (pSelection){\r
1995                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
1996                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
1997         } else {\r
1998                 xmin = ymin = 0;\r
1999                 xmax = head.biWidth; ymax=head.biHeight;\r
2000         }\r
2001 \r
2002         for(long y=ymin; y<ymax; y++){\r
2003                 info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));\r
2004                 if (info.nEscape) break;\r
2005                 for(long x=xmin; x<xmax; x++){\r
2006 #if CXIMAGE_SUPPORT_SELECTION\r
2007                         if (BlindSelectionIsInside(x,y))\r
2008 #endif //CXIMAGE_SUPPORT_SELECTION\r
2009                                 {\r
2010                                 pix1 = BlindGetPixelColor(x,y);\r
2011                                 maxr=maxg=maxb=0;\r
2012                                 for(j=-k2, i=0;j<kmax;j++){\r
2013                                         for(k=-k2;k<kmax;k++, i++){\r
2014                                                 if (!IsInside(x+j,y+k)) continue;\r
2015                                                 pix2 = BlindGetPixelColor(x+j,y+k);\r
2016                                                 if ((pix2.rgbBlue-pix1.rgbBlue)>maxb) maxb = pix2.rgbBlue;\r
2017                                                 if ((pix2.rgbGreen-pix1.rgbGreen)>maxg) maxg = pix2.rgbGreen;\r
2018                                                 if ((pix2.rgbRed-pix1.rgbRed)>maxr) maxr = pix2.rgbRed;\r
2019                                         }\r
2020                                 }\r
2021                                 pix1.rgbBlue=(BYTE)(255-maxb);\r
2022                                 pix1.rgbGreen=(BYTE)(255-maxg);\r
2023                                 pix1.rgbRed=(BYTE)(255-maxr);\r
2024                                 tmp.BlindSetPixelColor(x,y,pix1);\r
2025                         }\r
2026                 }\r
2027         }\r
2028         Transfer(tmp);\r
2029         return true;\r
2030 }\r
2031 ////////////////////////////////////////////////////////////////////////////////\r
2032 /**\r
2033  * Adds a random offset to each pixel in the image\r
2034  * \param radius: maximum pixel displacement\r
2035  * \return true if everything is ok\r
2036  */\r
2037 bool CxImage::Jitter(long radius)\r
2038 {\r
2039         if (!pDib) return false;\r
2040 \r
2041         long nx,ny;\r
2042 \r
2043         CxImage tmp(*this);\r
2044         if (!tmp.IsValid()){\r
2045                 strcpy(info.szLastError,tmp.GetLastError());\r
2046                 return false;\r
2047         }\r
2048 \r
2049         long xmin,xmax,ymin,ymax;\r
2050         if (pSelection){\r
2051                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
2052                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
2053         } else {\r
2054                 xmin = ymin = 0;\r
2055                 xmax = head.biWidth; ymax=head.biHeight;\r
2056         }\r
2057 \r
2058         for(long y=ymin; y<ymax; y++){\r
2059                 info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));\r
2060                 if (info.nEscape) break;\r
2061                 for(long x=xmin; x<xmax; x++){\r
2062 #if CXIMAGE_SUPPORT_SELECTION\r
2063                         if (BlindSelectionIsInside(x,y))\r
2064 #endif //CXIMAGE_SUPPORT_SELECTION\r
2065                         {\r
2066                                 nx=x+(long)((rand()/(float)RAND_MAX - 0.5)*(radius*2));\r
2067                                 ny=y+(long)((rand()/(float)RAND_MAX - 0.5)*(radius*2));\r
2068                                 if (!IsInside(nx,ny)) {\r
2069                                         nx=x;\r
2070                                         ny=y;\r
2071                                 }\r
2072                                 if (head.biClrUsed==0){\r
2073                                         tmp.BlindSetPixelColor(x,y,BlindGetPixelColor(nx,ny));\r
2074                                 } else {\r
2075                                         tmp.BlindSetPixelIndex(x,y,BlindGetPixelIndex(nx,ny));\r
2076                                 }\r
2077 #if CXIMAGE_SUPPORT_ALPHA\r
2078                                 tmp.AlphaSet(x,y,AlphaGet(nx,ny));\r
2079 #endif //CXIMAGE_SUPPORT_ALPHA\r
2080                         }\r
2081                 }\r
2082         }\r
2083         Transfer(tmp);\r
2084         return true;\r
2085 }\r
2086 ////////////////////////////////////////////////////////////////////////////////\r
2087 /** \r
2088  * generates a 1-D convolution matrix to be used for each pass of \r
2089  * a two-pass gaussian blur.  Returns the length of the matrix.\r
2090  * \author [nipper]\r
2091  */\r
2092 int CxImage::gen_convolve_matrix (float radius, float **cmatrix_p)\r
2093 {\r
2094         int matrix_length;\r
2095         int matrix_midpoint;\r
2096         float* cmatrix;\r
2097         int i,j;\r
2098         float std_dev;\r
2099         float sum;\r
2100         \r
2101         /* we want to generate a matrix that goes out a certain radius\r
2102         * from the center, so we have to go out ceil(rad-0.5) pixels,\r
2103         * inlcuding the center pixel.  Of course, that's only in one direction,\r
2104         * so we have to go the same amount in the other direction, but not count\r
2105         * the center pixel again.  So we double the previous result and subtract\r
2106         * one.\r
2107         * The radius parameter that is passed to this function is used as\r
2108         * the standard deviation, and the radius of effect is the\r
2109         * standard deviation * 2.  It's a little confusing.\r
2110         * <DP> modified scaling, so that matrix_lenght = 1+2*radius parameter\r
2111         */\r
2112         radius = (float)fabs(0.5*radius) + 0.25f;\r
2113         \r
2114         std_dev = radius;\r
2115         radius = std_dev * 2;\r
2116         \r
2117         /* go out 'radius' in each direction */\r
2118         matrix_length = int (2 * ceil(radius-0.5) + 1);\r
2119         if (matrix_length <= 0) matrix_length = 1;\r
2120         matrix_midpoint = matrix_length/2 + 1;\r
2121         *cmatrix_p = new float[matrix_length];\r
2122         cmatrix = *cmatrix_p;\r
2123         \r
2124         /*  Now we fill the matrix by doing a numeric integration approximation\r
2125         * from -2*std_dev to 2*std_dev, sampling 50 points per pixel.\r
2126         * We do the bottom half, mirror it to the top half, then compute the\r
2127         * center point.  Otherwise asymmetric quantization errors will occur.\r
2128         *  The formula to integrate is e^-(x^2/2s^2).\r
2129         */\r
2130         \r
2131         /* first we do the top (right) half of matrix */\r
2132         for (i = matrix_length/2 + 1; i < matrix_length; i++)\r
2133     {\r
2134                 float base_x = i - (float)floor((float)(matrix_length/2)) - 0.5f;\r
2135                 sum = 0;\r
2136                 for (j = 1; j <= 50; j++)\r
2137                 {\r
2138                         if ( base_x+0.02*j <= radius ) \r
2139                                 sum += (float)exp (-(base_x+0.02*j)*(base_x+0.02*j) / \r
2140                                 (2*std_dev*std_dev));\r
2141                 }\r
2142                 cmatrix[i] = sum/50;\r
2143     }\r
2144         \r
2145         /* mirror the thing to the bottom half */\r
2146         for (i=0; i<=matrix_length/2; i++) {\r
2147                 cmatrix[i] = cmatrix[matrix_length-1-i];\r
2148         }\r
2149         \r
2150         /* find center val -- calculate an odd number of quanta to make it symmetric,\r
2151         * even if the center point is weighted slightly higher than others. */\r
2152         sum = 0;\r
2153         for (j=0; j<=50; j++)\r
2154     {\r
2155                 sum += (float)exp (-(0.5+0.02*j)*(0.5+0.02*j) /\r
2156                         (2*std_dev*std_dev));\r
2157     }\r
2158         cmatrix[matrix_length/2] = sum/51;\r
2159         \r
2160         /* normalize the distribution by scaling the total sum to one */\r
2161         sum=0;\r
2162         for (i=0; i<matrix_length; i++) sum += cmatrix[i];\r
2163         for (i=0; i<matrix_length; i++) cmatrix[i] = cmatrix[i] / sum;\r
2164         \r
2165         return matrix_length;\r
2166 }\r
2167 ////////////////////////////////////////////////////////////////////////////////\r
2168 /**\r
2169  * generates a lookup table for every possible product of 0-255 and\r
2170  * each value in the convolution matrix.  The returned array is\r
2171  * indexed first by matrix position, then by input multiplicand (?)\r
2172  * value.\r
2173  * \author [nipper]\r
2174  */\r
2175 float* CxImage::gen_lookup_table (float *cmatrix, int cmatrix_length)\r
2176 {\r
2177         float* lookup_table = new float[cmatrix_length * 256];\r
2178         float* lookup_table_p = lookup_table;\r
2179         float* cmatrix_p      = cmatrix;\r
2180         \r
2181         for (int i=0; i<cmatrix_length; i++)\r
2182     {\r
2183                 for (int j=0; j<256; j++)\r
2184                 {\r
2185                         *(lookup_table_p++) = *cmatrix_p * (float)j;\r
2186                 }\r
2187                 cmatrix_p++;\r
2188     }\r
2189         \r
2190         return lookup_table;\r
2191 }\r
2192 ////////////////////////////////////////////////////////////////////////////////\r
2193 /**\r
2194  * this function is written as if it is blurring a column at a time,\r
2195  * even though it can operate on rows, too.  There is no difference\r
2196  * in the processing of the lines, at least to the blur_line function.\r
2197  * \author [nipper]\r
2198  */\r
2199 void CxImage::blur_line (float *ctable, float *cmatrix, int cmatrix_length, BYTE* cur_col, BYTE* dest_col, int y, long bytes)\r
2200 {\r
2201         float scale;\r
2202         float sum;\r
2203         int i=0, j=0;\r
2204         int row;\r
2205         int cmatrix_middle = cmatrix_length/2;\r
2206         \r
2207         float *cmatrix_p;\r
2208         BYTE  *cur_col_p;\r
2209         BYTE  *cur_col_p1;\r
2210         BYTE  *dest_col_p;\r
2211         float *ctable_p;\r
2212         \r
2213         /* this first block is the same as the non-optimized version --\r
2214         * it is only used for very small pictures, so speed isn't a\r
2215         * big concern.\r
2216         */\r
2217         if (cmatrix_length > y)\r
2218     {\r
2219                 for (row = 0; row < y ; row++)\r
2220                 {\r
2221                         scale=0;\r
2222                         /* find the scale factor */\r
2223                         for (j = 0; j < y ; j++)\r
2224                         {\r
2225                                 /* if the index is in bounds, add it to the scale counter */\r
2226                                 if ((j + cmatrix_middle - row >= 0) &&\r
2227                                         (j + cmatrix_middle - row < cmatrix_length))\r
2228                                         scale += cmatrix[j + cmatrix_middle - row];\r
2229                         }\r
2230                         for (i = 0; i<bytes; i++)\r
2231                         {\r
2232                                 sum = 0;\r
2233                                 for (j = 0; j < y; j++)\r
2234                                 {\r
2235                                         if ((j >= row - cmatrix_middle) &&\r
2236                                                 (j <= row + cmatrix_middle))\r
2237                                                 sum += cur_col[j*bytes + i] * cmatrix[j];\r
2238                                 }\r
2239                                 dest_col[row*bytes + i] = (BYTE)(0.5f + sum / scale);\r
2240                         }\r
2241                 }\r
2242     }\r
2243         else\r
2244     {\r
2245                 /* for the edge condition, we only use available info and scale to one */\r
2246                 for (row = 0; row < cmatrix_middle; row++)\r
2247                 {\r
2248                         /* find scale factor */\r
2249                         scale=0;\r
2250                         for (j = cmatrix_middle - row; j<cmatrix_length; j++)\r
2251                                 scale += cmatrix[j];\r
2252                         for (i = 0; i<bytes; i++)\r
2253                         {\r
2254                                 sum = 0;\r
2255                                 for (j = cmatrix_middle - row; j<cmatrix_length; j++)\r
2256                                 {\r
2257                                         sum += cur_col[(row + j-cmatrix_middle)*bytes + i] * cmatrix[j];\r
2258                                 }\r
2259                                 dest_col[row*bytes + i] = (BYTE)(0.5f + sum / scale);\r
2260                         }\r
2261                 }\r
2262                 /* go through each pixel in each col */\r
2263                 dest_col_p = dest_col + row*bytes;\r
2264                 for (; row < y-cmatrix_middle; row++)\r
2265                 {\r
2266                         cur_col_p = (row - cmatrix_middle) * bytes + cur_col;\r
2267                         for (i = 0; i<bytes; i++)\r
2268                         {\r
2269                                 sum = 0;\r
2270                                 cmatrix_p = cmatrix;\r
2271                                 cur_col_p1 = cur_col_p;\r
2272                                 ctable_p = ctable;\r
2273                                 for (j = cmatrix_length; j>0; j--)\r
2274                                 {\r
2275                                         sum += *(ctable_p + *cur_col_p1);\r
2276                                         cur_col_p1 += bytes;\r
2277                                         ctable_p += 256;\r
2278                                 }\r
2279                                 cur_col_p++;\r
2280                                 *(dest_col_p++) = (BYTE)(0.5f + sum);\r
2281                         }\r
2282                 }\r
2283                 \r
2284                 /* for the edge condition , we only use available info, and scale to one */\r
2285                 for (; row < y; row++)\r
2286                 {\r
2287                         /* find scale factor */\r
2288                         scale=0;\r
2289                         for (j = 0; j< y-row + cmatrix_middle; j++)\r
2290                                 scale += cmatrix[j];\r
2291                         for (i = 0; i<bytes; i++)\r
2292                         {\r
2293                                 sum = 0;\r
2294                                 for (j = 0; j<y-row + cmatrix_middle; j++)\r
2295                                 {\r
2296                                         sum += cur_col[(row + j-cmatrix_middle)*bytes + i] * cmatrix[j];\r
2297                                 }\r
2298                                 dest_col[row*bytes + i] = (BYTE) (0.5f + sum / scale);\r
2299                         }\r
2300                 }\r
2301     }\r
2302 }\r
2303 ////////////////////////////////////////////////////////////////////////////////\r
2304 /**\r
2305  * \author [DP]\r
2306  */\r
2307 void CxImage::blur_text (BYTE threshold, BYTE decay, BYTE max_depth, CxImage* iSrc, CxImage* iDst, BYTE bytes)\r
2308 {\r
2309         long x,y,z,m;\r
2310         BYTE *pSrc, *pSrc2, *pSrc3, *pDst;\r
2311         BYTE step,n;\r
2312         int pivot;\r
2313 \r
2314         if (max_depth<1) max_depth = 1;\r
2315 \r
2316         long nmin,nmax,xmin,xmax,ymin,ymax;\r
2317         xmin = ymin = 0;\r
2318         xmax = iSrc->head.biWidth;\r
2319         ymax = iSrc->head.biHeight;\r
2320 \r
2321         if (xmin==xmax || ymin==ymax) return;\r
2322 \r
2323         nmin = xmin * bytes;\r
2324         nmax = xmax * bytes;\r
2325 \r
2326         CImageIterator itSrc(iSrc);\r
2327         CImageIterator itTmp(iDst);\r
2328 \r
2329         double dbScaler = 100.0f/(ymax-ymin)/bytes;\r
2330 \r
2331         for (n=0; n<bytes; n++){\r
2332                 for (y=ymin+1;y<(ymax-1);y++)\r
2333                 {\r
2334                         if (info.nEscape) break;\r
2335                         info.nProgress = (long)((y-ymin)*dbScaler*(1+n));\r
2336 \r
2337                         pSrc  = itSrc.GetRow(y);\r
2338                         pSrc2 = itSrc.GetRow(y+1);\r
2339                         pSrc3 = itSrc.GetRow(y-1);\r
2340                         pDst  = itTmp.GetRow(y);\r
2341 \r
2342                         //scan left to right\r
2343                         for (x=n+nmin /*,i=xmin*/; x<(nmax-1); x+=bytes /*,i++*/)\r
2344                         {\r
2345                                 z=x+bytes;\r
2346                                 pivot = pSrc[z]-threshold;\r
2347                                 //find upper corner\r
2348                                 if (pSrc[x]<pivot && pSrc2[z]<pivot && pSrc3[x]>=pivot){\r
2349                                         while (z<nmax && pSrc2[z]<pSrc[x+bytes] && pSrc[x+bytes]<=pSrc[z]){\r
2350                                                 z+=bytes;\r
2351                                         }\r
2352                                         m = z-x;\r
2353                                         m = (decay>1) ? ((m/bytes)/decay+1) : m/bytes;\r
2354                                         if (m>max_depth) m = max_depth;\r
2355                                         step = (BYTE)((pSrc[x+bytes]-pSrc[x])/(m+1));\r
2356                                         while (m-->1){\r
2357                                                 pDst[x+m*bytes] = (BYTE)(pDst[x]+(step*(m+1)));\r
2358                                         }\r
2359                                 }\r
2360                                 //find lower corner\r
2361                                 z=x+bytes;\r
2362                                 if (pSrc[x]<pivot && pSrc3[z]<pivot && pSrc2[x]>=pivot){\r
2363                                         while (z<nmax && pSrc3[z]<pSrc[x+bytes] && pSrc[x+bytes]<=pSrc[z]){\r
2364                                                 z+=bytes;\r
2365                                         }\r
2366                                         m = z-x;\r
2367                                         m = (decay>1) ? ((m/bytes)/decay+1) : m/bytes;\r
2368                                         if (m>max_depth) m = max_depth;\r
2369                                         step = (BYTE)((pSrc[x+bytes]-pSrc[x])/(m+1));\r
2370                                         while (m-->1){\r
2371                                                 pDst[x+m*bytes] = (BYTE)(pDst[x]+(step*(m+1)));\r
2372                                         }\r
2373                                 }\r
2374                         }\r
2375                         //scan right to left\r
2376                         for (x=nmax-1-n /*,i=(xmax-1)*/; x>0; x-=bytes /*,i--*/)\r
2377                         {\r
2378                                 z=x-bytes;\r
2379                                 pivot = pSrc[z]-threshold;\r
2380                                 //find upper corner\r
2381                                 if (pSrc[x]<pivot && pSrc2[z]<pivot && pSrc3[x]>=pivot){\r
2382                                         while (z>n && pSrc2[z]<pSrc[x-bytes] && pSrc[x-bytes]<=pSrc[z]){\r
2383                                                 z-=bytes;\r
2384                                         }\r
2385                                         m = x-z;\r
2386                                         m = (decay>1) ? ((m/bytes)/decay+1) : m/bytes;\r
2387                                         if (m>max_depth) m = max_depth;\r
2388                                         step = (BYTE)((pSrc[x-bytes]-pSrc[x])/(m+1));\r
2389                                         while (m-->1){\r
2390                                                 pDst[x-m*bytes] = (BYTE)(pDst[x]+(step*(m+1)));\r
2391                                         }\r
2392                                 }\r
2393                                 //find lower corner\r
2394                                 z=x-bytes;\r
2395                                 if (pSrc[x]<pivot && pSrc3[z]<pivot && pSrc2[x]>=pivot){\r
2396                                         while (z>n && pSrc3[z]<pSrc[x-bytes] && pSrc[x-bytes]<=pSrc[z]){\r
2397                                                 z-=bytes;\r
2398                                         }\r
2399                                         m = x-z;\r
2400                                         m = (decay>1) ? ((m/bytes)/decay+1) : m/bytes;\r
2401                                         if (m>max_depth) m = max_depth;\r
2402                                         step = (BYTE)((pSrc[x-bytes]-pSrc[x])/(m+1));\r
2403                                         while (m-->1){\r
2404                                                 pDst[x-m*bytes] = (BYTE)(pDst[x]+(step*(m+1)));\r
2405                                         }\r
2406                                 }\r
2407                         }\r
2408                 }\r
2409         }\r
2410 }\r
2411 ////////////////////////////////////////////////////////////////////////////////\r
2412 /**\r
2413  * \author [DP]\r
2414  */\r
2415 bool CxImage::TextBlur(BYTE threshold, BYTE decay, BYTE max_depth, bool bBlurHorizontal, bool bBlurVertical, CxImage* iDst)\r
2416 {\r
2417         if (!pDib) return false;\r
2418 \r
2419         RGBQUAD* pPalette=NULL;\r
2420         WORD bpp = GetBpp();\r
2421 \r
2422         //the routine is optimized for RGB or GrayScale images\r
2423         if (!(head.biBitCount == 24 || IsGrayScale())){\r
2424                 pPalette = new RGBQUAD[head.biClrUsed];\r
2425                 memcpy(pPalette, GetPalette(),GetPaletteSize());\r
2426                 if (!IncreaseBpp(24))\r
2427                         return false;\r
2428         }\r
2429 \r
2430         CxImage tmp(*this);\r
2431         if (!tmp.IsValid()){\r
2432                 strcpy(info.szLastError,tmp.GetLastError());\r
2433                 return false;\r
2434         }\r
2435 \r
2436         if (bBlurHorizontal)\r
2437                 blur_text(threshold, decay, max_depth, this, &tmp, head.biBitCount>>3);\r
2438 \r
2439         if (bBlurVertical){\r
2440                 CxImage src2(*this);\r
2441                 src2.RotateLeft();\r
2442                 tmp.RotateLeft();\r
2443                 blur_text(threshold, decay, max_depth, &src2, &tmp, head.biBitCount>>3);\r
2444                 tmp.RotateRight();\r
2445         }\r
2446 \r
2447 #if CXIMAGE_SUPPORT_SELECTION\r
2448         //restore the non selected region\r
2449         if (pSelection){\r
2450                 for(long y=0; y<head.biHeight; y++){\r
2451                         for(long x=0; x<head.biWidth; x++){\r
2452                                 if (!BlindSelectionIsInside(x,y)){\r
2453                                         tmp.BlindSetPixelColor(x,y,BlindGetPixelColor(x,y));\r
2454                                 }\r
2455                         }\r
2456                 }\r
2457         }\r
2458 #endif //CXIMAGE_SUPPORT_SELECTION\r
2459 \r
2460         //if necessary, restore the original BPP and palette\r
2461         if (pPalette){\r
2462                 tmp.DecreaseBpp(bpp, true, pPalette);\r
2463                 delete [] pPalette;\r
2464         }\r
2465 \r
2466         if (iDst) iDst->Transfer(tmp);\r
2467         else Transfer(tmp);\r
2468 \r
2469         return true;\r
2470 }\r
2471 ////////////////////////////////////////////////////////////////////////////////\r
2472 /**\r
2473  * \author [nipper]; changes [DP]\r
2474  */\r
2475 bool CxImage::GaussianBlur(float radius /*= 1.0f*/, CxImage* iDst /*= 0*/)\r
2476 {\r
2477         if (!pDib) return false;\r
2478 \r
2479         RGBQUAD* pPalette=NULL;\r
2480         WORD bpp = GetBpp();\r
2481 \r
2482         //the routine is optimized for RGB or GrayScale images\r
2483         if (!(head.biBitCount == 24 || IsGrayScale())){\r
2484                 pPalette = new RGBQUAD[head.biClrUsed];\r
2485                 memcpy(pPalette, GetPalette(),GetPaletteSize());\r
2486                 if (!IncreaseBpp(24))\r
2487                         return false;\r
2488         }\r
2489 \r
2490         CxImage tmp_x(*this, false, true, true);\r
2491         if (!tmp_x.IsValid()){\r
2492                 strcpy(info.szLastError,tmp_x.GetLastError());\r
2493                 return false;\r
2494         }\r
2495 \r
2496         // generate convolution matrix and make sure it's smaller than each dimension\r
2497         float *cmatrix = NULL;\r
2498         int cmatrix_length = gen_convolve_matrix(radius, &cmatrix);\r
2499         // generate lookup table\r
2500         float *ctable = gen_lookup_table(cmatrix, cmatrix_length);\r
2501 \r
2502         long x,y;\r
2503         int bypp = head.biBitCount>>3;\r
2504 \r
2505         CImageIterator itSrc(this);\r
2506         CImageIterator itTmp(&tmp_x);\r
2507 \r
2508         double dbScaler = 50.0f/head.biHeight;\r
2509 \r
2510         // blur the rows\r
2511     for (y=0;y<head.biHeight;y++)\r
2512         {\r
2513                 if (info.nEscape) break;\r
2514                 info.nProgress = (long)(y*dbScaler);\r
2515 \r
2516                 blur_line(ctable, cmatrix, cmatrix_length, itSrc.GetRow(y), itTmp.GetRow(y), head.biWidth, bypp);\r
2517         }\r
2518 \r
2519         CxImage tmp_y(tmp_x, false, true, true);\r
2520         if (!tmp_y.IsValid()){\r
2521                 strcpy(info.szLastError,tmp_y.GetLastError());\r
2522                 return false;\r
2523         }\r
2524 \r
2525         CImageIterator itDst(&tmp_y);\r
2526 \r
2527         // blur the cols\r
2528     BYTE* cur_col = (BYTE*)malloc(bypp*head.biHeight);\r
2529     BYTE* dest_col = (BYTE*)malloc(bypp*head.biHeight);\r
2530 \r
2531         dbScaler = 50.0f/head.biWidth;\r
2532 \r
2533         for (x=0;x<head.biWidth;x++)\r
2534         {\r
2535                 if (info.nEscape) break;\r
2536                 info.nProgress = (long)(50.0f+x*dbScaler);\r
2537 \r
2538                 itTmp.GetCol(cur_col, x);\r
2539                 itDst.GetCol(dest_col, x);\r
2540                 blur_line(ctable, cmatrix, cmatrix_length, cur_col, dest_col, head.biHeight, bypp);\r
2541                 itDst.SetCol(dest_col, x);\r
2542         }\r
2543 \r
2544         free(cur_col);\r
2545         free(dest_col);\r
2546 \r
2547         delete [] cmatrix;\r
2548         delete [] ctable;\r
2549 \r
2550 #if CXIMAGE_SUPPORT_SELECTION\r
2551         //restore the non selected region\r
2552         if (pSelection){\r
2553                 for(y=0; y<head.biHeight; y++){\r
2554                         for(x=0; x<head.biWidth; x++){\r
2555                                 if (!BlindSelectionIsInside(x,y)){\r
2556                                         tmp_y.BlindSetPixelColor(x,y,BlindGetPixelColor(x,y));\r
2557                                 }\r
2558                         }\r
2559                 }\r
2560         }\r
2561 #endif //CXIMAGE_SUPPORT_SELECTION\r
2562 \r
2563         //if necessary, restore the original BPP and palette\r
2564         if (pPalette){\r
2565                 tmp_y.DecreaseBpp(bpp, false, pPalette);\r
2566                 if (iDst) DecreaseBpp(bpp, false, pPalette);\r
2567                 delete [] pPalette;\r
2568         }\r
2569 \r
2570         if (iDst) iDst->Transfer(tmp_y);\r
2571         else Transfer(tmp_y);\r
2572 \r
2573         return true;\r
2574 }\r
2575 ////////////////////////////////////////////////////////////////////////////////\r
2576 /**\r
2577  * \author [DP],[nipper]\r
2578  */\r
2579 bool CxImage::SelectiveBlur(float radius, BYTE threshold, CxImage* iDst)\r
2580 {\r
2581         if (!pDib) return false;\r
2582 \r
2583         RGBQUAD* pPalette=NULL;\r
2584         WORD bpp = GetBpp();\r
2585 \r
2586         CxImage Tmp(*this, true, true, true);\r
2587         if (!Tmp.IsValid()){\r
2588                 strcpy(info.szLastError,Tmp.GetLastError());\r
2589                 return false;\r
2590         }\r
2591 \r
2592         //the routine is optimized for RGB or GrayScale images\r
2593         if (!(head.biBitCount == 24 || IsGrayScale())){\r
2594                 pPalette = new RGBQUAD[head.biClrUsed];\r
2595                 memcpy(pPalette, GetPalette(),GetPaletteSize());\r
2596                 if (!Tmp.IncreaseBpp(24))\r
2597                         return false;\r
2598         }\r
2599 \r
2600         CxImage Dst(Tmp, true, true, true);\r
2601         if (!Dst.IsValid()){\r
2602                 strcpy(info.szLastError,Dst.GetLastError());\r
2603                 return false;\r
2604         }\r
2605 \r
2606         //build the difference mask\r
2607         BYTE thresh_dw = (BYTE)max( 0 ,(int)(128 - threshold));\r
2608         BYTE thresh_up = (BYTE)min(255,(int)(128 + threshold));\r
2609         long kernel[]={-100,-100,-100,-100,801,-100,-100,-100,-100};\r
2610         if (!Tmp.Filter(kernel,3,800,128)){\r
2611                 strcpy(info.szLastError,Tmp.GetLastError());\r
2612                 return false;\r
2613         }\r
2614 \r
2615         //if the image has no selection, build a selection for the whole image\r
2616         if (!Tmp.SelectionIsValid()){\r
2617                 Tmp.SelectionCreate();\r
2618                 Tmp.SelectionClear(255);\r
2619         }\r
2620 \r
2621         long xmin,xmax,ymin,ymax;\r
2622         xmin = Tmp.info.rSelectionBox.left;\r
2623         xmax = Tmp.info.rSelectionBox.right;\r
2624         ymin = Tmp.info.rSelectionBox.bottom;\r
2625         ymax = Tmp.info.rSelectionBox.top;\r
2626 \r
2627         //modify the selection where the difference mask is over the threshold\r
2628         for(long y=ymin; y<ymax; y++){\r
2629                 info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));\r
2630                 if (info.nEscape) break;\r
2631                 for(long x=xmin; x<xmax; x++){\r
2632                         if(Tmp.BlindSelectionIsInside(x,y)){\r
2633                                 RGBQUAD c = Tmp.BlindGetPixelColor(x,y);\r
2634                                 if ((c.rgbRed   < thresh_dw || c.rgbRed   > thresh_up) ||\r
2635                                         (c.rgbGreen < thresh_dw || c.rgbGreen > thresh_up) ||\r
2636                                         (c.rgbBlue  < thresh_dw || c.rgbBlue  > thresh_up))\r
2637                                 {\r
2638                                         Tmp.SelectionSet(x,y,0);\r
2639                                 }\r
2640                         }\r
2641                 }\r
2642         }\r
2643 \r
2644         //blur the image (only in the selected pixels)\r
2645         Dst.SelectionCopy(Tmp);\r
2646         if (!Dst.GaussianBlur(radius)){\r
2647                 strcpy(info.szLastError,Dst.GetLastError());\r
2648                 return false;\r
2649         }\r
2650 \r
2651         //restore the original selection\r
2652         Dst.SelectionCopy(*this);\r
2653 \r
2654         //if necessary, restore the original BPP and palette\r
2655         if (pPalette){\r
2656                 Dst.DecreaseBpp(bpp, false, pPalette);\r
2657                 delete [] pPalette;\r
2658         }\r
2659 \r
2660         if (iDst) iDst->Transfer(Dst);\r
2661         else Transfer(Dst);\r
2662 \r
2663         return true;\r
2664 }\r
2665 ////////////////////////////////////////////////////////////////////////////////\r
2666 /**\r
2667  * sharpen the image by subtracting a blurred copy from the original image.\r
2668  * \param radius: width in pixels of the blurring effect. Range: >0; default = 5.\r
2669  * \param amount: strength of the filter. Range: 0.0 (none) to 1.0 (max); default = 0.5\r
2670  * \param threshold: difference, between blurred and original pixel, to trigger the filter\r
2671  *                   Range: 0 (always triggered) to 255 (never triggered); default = 0.\r
2672  * \return true if everything is ok\r
2673  * \author [nipper]; changes [DP]\r
2674  */\r
2675 bool CxImage::UnsharpMask(float radius /*= 5.0*/, float amount /*= 0.5*/, int threshold /*= 0*/)\r
2676 {\r
2677         if (!pDib) return false;\r
2678 \r
2679         RGBQUAD* pPalette=NULL;\r
2680         WORD bpp = GetBpp();\r
2681 \r
2682         //the routine is optimized for RGB or GrayScale images\r
2683         if (!(head.biBitCount == 24 || IsGrayScale())){\r
2684                 pPalette = new RGBQUAD[head.biClrUsed];\r
2685                 memcpy(pPalette, GetPalette(),GetPaletteSize());\r
2686                 if (!IncreaseBpp(24))\r
2687                         return false;\r
2688         }\r
2689 \r
2690         CxImage iDst;\r
2691         if (!GaussianBlur(radius,&iDst))\r
2692                 return false;\r
2693 \r
2694         CImageIterator itSrc(this);\r
2695         CImageIterator itDst(&iDst);\r
2696 \r
2697         long xmin,xmax,ymin,ymax;\r
2698         if (pSelection){\r
2699                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
2700                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
2701         } else {\r
2702                 xmin = ymin = 0;\r
2703                 xmax = head.biWidth; ymax=head.biHeight;\r
2704         }\r
2705 \r
2706         if (xmin==xmax || ymin==ymax)\r
2707                 return false;\r
2708 \r
2709         double dbScaler = 100.0/(ymax-ymin);\r
2710         int bypp = head.biBitCount>>3;\r
2711         \r
2712         // merge the source and destination (which currently contains\r
2713         // the blurred version) images\r
2714     for (long y=ymin; y<ymax; y++)\r
2715         {\r
2716                 if (info.nEscape) break;\r
2717                 info.nProgress = (long)((y-ymin)*dbScaler);\r
2718 \r
2719                 // get source row\r
2720                 BYTE* cur_row = itSrc.GetRow(y);\r
2721                 // get dest row\r
2722                 BYTE* dest_row = itDst.GetRow(y);\r
2723                 // combine the two\r
2724                 for (long x=xmin; x<xmax; x++) {\r
2725 #if CXIMAGE_SUPPORT_SELECTION\r
2726                         if (BlindSelectionIsInside(x,y))\r
2727 #endif //CXIMAGE_SUPPORT_SELECTION\r
2728                         {\r
2729                                 for (long b=0, z=x*bypp; b<bypp; b++, z++){\r
2730                                         int diff = cur_row[z] - dest_row[z];\r
2731 \r
2732                                         // do tresholding\r
2733                                         if (abs(diff) < threshold){\r
2734                                                 dest_row[z] = cur_row[z];\r
2735                                         } else {\r
2736                                                 dest_row[z] = (BYTE)min(255, max(0,(int)(cur_row[z] + amount * diff)));\r
2737                                         }\r
2738                                 }\r
2739                         }\r
2740                 }\r
2741         }\r
2742 \r
2743         //if necessary, restore the original BPP and palette\r
2744         if (pPalette){\r
2745                 iDst.DecreaseBpp(bpp, false, pPalette);\r
2746                 delete [] pPalette;\r
2747         }\r
2748 \r
2749         Transfer(iDst);\r
2750 \r
2751         return true;\r
2752 }\r
2753 ////////////////////////////////////////////////////////////////////////////////\r
2754 /**\r
2755  * Apply a look up table to the image. \r
2756  * \param pLut: BYTE[256] look up table\r
2757  * \return true if everything is ok\r
2758  */\r
2759 bool CxImage::Lut(BYTE* pLut)\r
2760 {\r
2761         if (!pDib || !pLut) return false;\r
2762         RGBQUAD color;\r
2763 \r
2764         double dbScaler;\r
2765         if (head.biClrUsed==0){\r
2766 \r
2767                 long xmin,xmax,ymin,ymax;\r
2768                 if (pSelection){\r
2769                         xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
2770                         ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
2771                 } else {\r
2772                         // faster loop for full image\r
2773                         BYTE *iSrc=info.pImage;\r
2774                         for(unsigned long i=0; i < head.biSizeImage ; i++){\r
2775                                 *iSrc++ = pLut[*iSrc];\r
2776                         }\r
2777                         return true;\r
2778                 }\r
2779 \r
2780                 if (xmin==xmax || ymin==ymax)\r
2781                         return false;\r
2782 \r
2783                 dbScaler = 100.0/(ymax-ymin);\r
2784 \r
2785                 for(long y=ymin; y<ymax; y++){\r
2786                         info.nProgress = (long)((y-ymin)*dbScaler); //<Anatoly Ivasyuk>\r
2787                         for(long x=xmin; x<xmax; x++){\r
2788 #if CXIMAGE_SUPPORT_SELECTION\r
2789                                 if (BlindSelectionIsInside(x,y))\r
2790 #endif //CXIMAGE_SUPPORT_SELECTION\r
2791                                 {\r
2792                                         color = BlindGetPixelColor(x,y);\r
2793                                         color.rgbRed = pLut[color.rgbRed];\r
2794                                         color.rgbGreen = pLut[color.rgbGreen];\r
2795                                         color.rgbBlue = pLut[color.rgbBlue];\r
2796                                         BlindSetPixelColor(x,y,color);\r
2797                                 }\r
2798                         }\r
2799                 }\r
2800 #if CXIMAGE_SUPPORT_SELECTION\r
2801         } else if (pSelection && (head.biBitCount==8) && IsGrayScale()){\r
2802                 long xmin,xmax,ymin,ymax;\r
2803                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
2804                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
2805 \r
2806                 if (xmin==xmax || ymin==ymax)\r
2807                         return false;\r
2808 \r
2809                 dbScaler = 100.0/(ymax-ymin);\r
2810                 for(long y=ymin; y<ymax; y++){\r
2811                         info.nProgress = (long)((y-ymin)*dbScaler);\r
2812                         for(long x=xmin; x<xmax; x++){\r
2813                                 if (BlindSelectionIsInside(x,y))\r
2814                                 {\r
2815                                         BlindSetPixelIndex(x,y,pLut[BlindGetPixelIndex(x,y)]);\r
2816                                 }\r
2817                         }\r
2818                 }\r
2819 #endif //CXIMAGE_SUPPORT_SELECTION\r
2820         } else {\r
2821                 bool bIsGrayScale = IsGrayScale();\r
2822                 for(DWORD j=0; j<head.biClrUsed; j++){\r
2823                         color = GetPaletteColor((BYTE)j);\r
2824                         color.rgbRed = pLut[color.rgbRed];\r
2825                         color.rgbGreen = pLut[color.rgbGreen];\r
2826                         color.rgbBlue = pLut[color.rgbBlue];\r
2827                         SetPaletteColor((BYTE)j,color);\r
2828                 }\r
2829                 if (bIsGrayScale) GrayScale();\r
2830         }\r
2831         return true;\r
2832 \r
2833 }\r
2834 ////////////////////////////////////////////////////////////////////////////////\r
2835 /**\r
2836  * Apply an indipendent look up table for each channel\r
2837  * \param pLutR, pLutG, pLutB, pLutA: BYTE[256] look up tables\r
2838  * \return true if everything is ok\r
2839  */\r
2840 bool CxImage::Lut(BYTE* pLutR, BYTE* pLutG, BYTE* pLutB, BYTE* pLutA)\r
2841 {\r
2842         if (!pDib || !pLutR || !pLutG || !pLutB) return false;\r
2843         RGBQUAD color;\r
2844 \r
2845         double dbScaler;\r
2846         if (head.biClrUsed==0){\r
2847 \r
2848                 long xmin,xmax,ymin,ymax;\r
2849                 if (pSelection){\r
2850                         xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
2851                         ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
2852                 } else {\r
2853                         xmin = ymin = 0;\r
2854                         xmax = head.biWidth; ymax=head.biHeight;\r
2855                 }\r
2856 \r
2857                 if (xmin==xmax || ymin==ymax)\r
2858                         return false;\r
2859 \r
2860                 dbScaler = 100.0/(ymax-ymin);\r
2861 \r
2862                 for(long y=ymin; y<ymax; y++){\r
2863                         info.nProgress = (long)((y-ymin)*dbScaler);\r
2864                         for(long x=xmin; x<xmax; x++){\r
2865 #if CXIMAGE_SUPPORT_SELECTION\r
2866                                 if (BlindSelectionIsInside(x,y))\r
2867 #endif //CXIMAGE_SUPPORT_SELECTION\r
2868                                 {\r
2869                                         color = BlindGetPixelColor(x,y);\r
2870                                         color.rgbRed =   pLutR[color.rgbRed];\r
2871                                         color.rgbGreen = pLutG[color.rgbGreen];\r
2872                                         color.rgbBlue =  pLutB[color.rgbBlue];\r
2873                                         if (pLutA) color.rgbReserved=pLutA[color.rgbReserved];\r
2874                                         BlindSetPixelColor(x,y,color,true);\r
2875                                 }\r
2876                         }\r
2877                 }\r
2878         } else {\r
2879                 bool bIsGrayScale = IsGrayScale();\r
2880                 for(DWORD j=0; j<head.biClrUsed; j++){\r
2881                         color = GetPaletteColor((BYTE)j);\r
2882                         color.rgbRed =   pLutR[color.rgbRed];\r
2883                         color.rgbGreen = pLutG[color.rgbGreen];\r
2884                         color.rgbBlue =  pLutB[color.rgbBlue];\r
2885                         SetPaletteColor((BYTE)j,color);\r
2886                 }\r
2887                 if (bIsGrayScale) GrayScale();\r
2888         }\r
2889 \r
2890         return true;\r
2891 \r
2892 }\r
2893 ////////////////////////////////////////////////////////////////////////////////\r
2894 /**\r
2895  * Use the RedEyeRemove function to remove the red-eye effect that frequently\r
2896  * occurs in photographs of humans and animals. You must select the region \r
2897  * where the function will filter the red channel.\r
2898  * \param strength: range from 0.0f (no effect) to 1.0f (full effect). Default = 0.8\r
2899  * \return true if everything is ok\r
2900  */\r
2901 bool CxImage::RedEyeRemove(float strength)\r
2902 {\r
2903         if (!pDib) return false;\r
2904         RGBQUAD color;\r
2905 \r
2906         long xmin,xmax,ymin,ymax;\r
2907         if (pSelection){\r
2908                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
2909                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
2910         } else {\r
2911                 xmin = ymin = 0;\r
2912                 xmax = head.biWidth; ymax=head.biHeight;\r
2913         }\r
2914 \r
2915         if (xmin==xmax || ymin==ymax)\r
2916                 return false;\r
2917 \r
2918         if (strength<0.0f) strength = 0.0f;\r
2919         if (strength>1.0f) strength = 1.0f;\r
2920 \r
2921         for(long y=ymin; y<ymax; y++){\r
2922                 info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));\r
2923                 if (info.nEscape) break;\r
2924                 for(long x=xmin; x<xmax; x++){\r
2925 #if CXIMAGE_SUPPORT_SELECTION\r
2926                         if (BlindSelectionIsInside(x,y))\r
2927 #endif //CXIMAGE_SUPPORT_SELECTION\r
2928                         {\r
2929                                 float a = 1.0f-5.0f*((float)((x-0.5f*(xmax+xmin))*(x-0.5f*(xmax+xmin))+(y-0.5f*(ymax+ymin))*(y-0.5f*(ymax+ymin))))/((float)((xmax-xmin)*(ymax-ymin)));\r
2930                                 if (a<0) a=0;\r
2931                                 color = BlindGetPixelColor(x,y);\r
2932                                 color.rgbRed = (BYTE)(a*min(color.rgbGreen,color.rgbBlue)+(1.0f-a)*color.rgbRed);\r
2933                                 BlindSetPixelColor(x,y,color);\r
2934                         }\r
2935                 }\r
2936         }\r
2937         return true;\r
2938 }\r
2939 ////////////////////////////////////////////////////////////////////////////////\r
2940 /**\r
2941  * Changes the saturation of the image. \r
2942  * \param saturation: can be from -100 to 100, positive values increase the saturation.\r
2943  * \param colorspace: can be 1 (HSL) or 2 (YUV).\r
2944  * \return true if everything is ok\r
2945  */\r
2946 bool CxImage::Saturate(const long saturation, const long colorspace)\r
2947 {\r
2948         if (!pDib)\r
2949                 return false;\r
2950 \r
2951         long xmin,xmax,ymin,ymax;\r
2952         if (pSelection){\r
2953                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
2954                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
2955         } else {\r
2956                 xmin = ymin = 0;\r
2957                 xmax = head.biWidth; ymax=head.biHeight;\r
2958         }\r
2959 \r
2960         if (xmin==xmax || ymin==ymax)\r
2961                 return false;\r
2962 \r
2963         BYTE cTable[256];\r
2964 \r
2965         switch(colorspace)\r
2966         {\r
2967         case 1:\r
2968                 {\r
2969                         for (int i=0;i<256;i++) {\r
2970                                 cTable[i] = (BYTE)max(0,min(255,(int)(i + saturation)));\r
2971                         }\r
2972                         for(long y=ymin; y<ymax; y++){\r
2973                                 info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));\r
2974                                 if (info.nEscape) break;\r
2975                                 for(long x=xmin; x<xmax; x++){\r
2976 #if CXIMAGE_SUPPORT_SELECTION\r
2977                                         if (BlindSelectionIsInside(x,y))\r
2978 #endif //CXIMAGE_SUPPORT_SELECTION\r
2979                                         {\r
2980                                                 RGBQUAD c = RGBtoHSL(BlindGetPixelColor(x,y));\r
2981                                                 c.rgbGreen  = cTable[c.rgbGreen];\r
2982                                                 c = HSLtoRGB(c);\r
2983                                                 BlindSetPixelColor(x,y,c);\r
2984                                         }\r
2985                                 }\r
2986                         }\r
2987                 }\r
2988                 break;\r
2989         case 2:\r
2990                 {\r
2991                         for (int i=0;i<256;i++) {\r
2992                                 cTable[i] = (BYTE)max(0,min(255,(int)((i-128)*(100 + saturation)/100.0f + 128.5f)));\r
2993                         }\r
2994                         for(long y=ymin; y<ymax; y++){\r
2995                                 info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));\r
2996                                 if (info.nEscape) break;\r
2997                                 for(long x=xmin; x<xmax; x++){\r
2998 #if CXIMAGE_SUPPORT_SELECTION\r
2999                                         if (BlindSelectionIsInside(x,y))\r
3000 #endif //CXIMAGE_SUPPORT_SELECTION\r
3001                                         {\r
3002                                                 RGBQUAD c = RGBtoYUV(BlindGetPixelColor(x,y));\r
3003                                                 c.rgbGreen  = cTable[c.rgbGreen];\r
3004                                                 c.rgbBlue = cTable[c.rgbBlue];\r
3005                                                 c = YUVtoRGB(c);\r
3006                                                 BlindSetPixelColor(x,y,c);\r
3007                                         }\r
3008                                 }\r
3009                         }\r
3010                 }\r
3011                 break;\r
3012         default:\r
3013                 strcpy(info.szLastError,"Saturate: wrong colorspace");\r
3014                 return false;\r
3015         }\r
3016         return true;\r
3017 }\r
3018 \r
3019 ////////////////////////////////////////////////////////////////////////////////\r
3020 /**\r
3021  * Solarize: convert all colors above a given lightness level into their negative\r
3022  * \param  level : lightness threshold. Range = 0 to 255; default = 128.\r
3023  * \param  bLinkedChannels: true = compare with luminance, preserve colors (default)\r
3024  *                         false = compare with independent R,G,B levels\r
3025  * \return true if everything is ok\r
3026  * \author [Priyank Bolia] (priyank_bolia(at)yahoo(dot)com); changes [DP]\r
3027  */\r
3028 bool CxImage::Solarize(BYTE level, bool bLinkedChannels)\r
3029 {\r
3030         if (!pDib) return false;\r
3031 \r
3032         long xmin,xmax,ymin,ymax;\r
3033         if (pSelection){\r
3034                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;\r
3035                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;\r
3036         } else {\r
3037                 xmin = ymin = 0;\r
3038                 xmax = head.biWidth; ymax=head.biHeight;\r
3039         }\r
3040 \r
3041         if (head.biBitCount<=8){\r
3042                 if (IsGrayScale()){ //GRAYSCALE, selection\r
3043                         for(long y=ymin; y<ymax; y++){\r
3044                                 for(long x=xmin; x<xmax; x++){\r
3045 #if CXIMAGE_SUPPORT_SELECTION\r
3046                                         if (BlindSelectionIsInside(x,y))\r
3047 #endif //CXIMAGE_SUPPORT_SELECTION\r
3048                                         {\r
3049                                                 BYTE index = BlindGetPixelIndex(x,y);\r
3050                                                 RGBQUAD color = GetPaletteColor(index);\r
3051                                                 if ((BYTE)RGB2GRAY(color.rgbRed,color.rgbGreen,color.rgbBlue)>level){\r
3052                                                         BlindSetPixelIndex(x,y,255-index);\r
3053                                                 }\r
3054                                         }\r
3055                                 }\r
3056                         }\r
3057                 } else { //PALETTE, full image\r
3058                         RGBQUAD* ppal=GetPalette();\r
3059                         for(DWORD i=0;i<head.biClrUsed;i++){\r
3060                                 RGBQUAD color = GetPaletteColor((BYTE)i);\r
3061                                 if (bLinkedChannels){\r
3062                                         if ((BYTE)RGB2GRAY(color.rgbRed,color.rgbGreen,color.rgbBlue)>level){\r
3063                                                 ppal[i].rgbBlue =(BYTE)(255-ppal[i].rgbBlue);\r
3064                                                 ppal[i].rgbGreen =(BYTE)(255-ppal[i].rgbGreen);\r
3065                                                 ppal[i].rgbRed =(BYTE)(255-ppal[i].rgbRed);\r
3066                                         }\r
3067                                 } else {\r
3068                                         if (color.rgbBlue>level)        ppal[i].rgbBlue =(BYTE)(255-ppal[i].rgbBlue);\r
3069                                         if (color.rgbGreen>level)       ppal[i].rgbGreen =(BYTE)(255-ppal[i].rgbGreen);\r
3070                                         if (color.rgbRed>level)         ppal[i].rgbRed =(BYTE)(255-ppal[i].rgbRed);\r
3071                                 }\r
3072                         }\r
3073                 }\r
3074         } else { //RGB, selection\r
3075                 for(long y=ymin; y<ymax; y++){\r
3076                         for(long x=xmin; x<xmax; x++){\r
3077 #if CXIMAGE_SUPPORT_SELECTION\r
3078                                 if (BlindSelectionIsInside(x,y))\r
3079 #endif //CXIMAGE_SUPPORT_SELECTION\r
3080                                 {\r
3081                                         RGBQUAD color = BlindGetPixelColor(x,y);\r
3082                                         if (bLinkedChannels){\r
3083                                                 if ((BYTE)RGB2GRAY(color.rgbRed,color.rgbGreen,color.rgbBlue)>level){\r
3084                                                         color.rgbRed = (BYTE)(255-color.rgbRed);\r
3085                                                         color.rgbGreen = (BYTE)(255-color.rgbGreen);\r
3086                                                         color.rgbBlue = (BYTE)(255-color.rgbBlue);\r
3087                                                 }\r
3088                                         } else {\r
3089                                                 if (color.rgbBlue>level)        color.rgbBlue =(BYTE)(255-color.rgbBlue);\r
3090                                                 if (color.rgbGreen>level)       color.rgbGreen =(BYTE)(255-color.rgbGreen);\r
3091                                                 if (color.rgbRed>level)         color.rgbRed =(BYTE)(255-color.rgbRed);\r
3092                                         }\r
3093                                         BlindSetPixelColor(x,y,color);\r
3094                                 }\r
3095                         }\r
3096                 }\r
3097         }\r
3098 \r
3099         //invert transparent color only in case of full image processing\r
3100         if (pSelection==0 || (!IsGrayScale() && IsIndexed())){\r
3101                 if (bLinkedChannels){\r
3102                         if ((BYTE)RGB2GRAY(info.nBkgndColor.rgbRed,info.nBkgndColor.rgbGreen,info.nBkgndColor.rgbBlue)>level){\r
3103                                 info.nBkgndColor.rgbBlue = (BYTE)(255-info.nBkgndColor.rgbBlue);\r
3104                                 info.nBkgndColor.rgbGreen = (BYTE)(255-info.nBkgndColor.rgbGreen);\r
3105                                 info.nBkgndColor.rgbRed = (BYTE)(255-info.nBkgndColor.rgbRed);\r
3106                         } \r
3107                 } else {\r
3108                         if (info.nBkgndColor.rgbBlue>level)      info.nBkgndColor.rgbBlue = (BYTE)(255-info.nBkgndColor.rgbBlue);\r
3109                         if (info.nBkgndColor.rgbGreen>level) info.nBkgndColor.rgbGreen = (BYTE)(255-info.nBkgndColor.rgbGreen);\r
3110                         if (info.nBkgndColor.rgbRed>level)       info.nBkgndColor.rgbRed = (BYTE)(255-info.nBkgndColor.rgbRed);\r
3111                 }\r
3112         }\r
3113 \r
3114         return true;\r
3115 }\r
3116 \r
3117 ////////////////////////////////////////////////////////////////////////////////\r
3118 /**\r
3119  * Converts the RGB triplets to and from different colorspace\r
3120  * \param dstColorSpace: destination colorspace; 0 = RGB, 1 = HSL, 2 = YUV, 3 = YIQ, 4 = XYZ \r
3121  * \param srcColorSpace: source colorspace; 0 = RGB, 1 = HSL, 2 = YUV, 3 = YIQ, 4 = XYZ \r
3122  * \return true if everything is ok\r
3123  */\r
3124 bool CxImage::ConvertColorSpace(const long dstColorSpace, const long srcColorSpace)\r
3125 {\r
3126         if (!pDib)\r
3127                 return false;\r
3128 \r
3129         if (dstColorSpace == srcColorSpace)\r
3130                 return true;\r
3131 \r
3132         long w = GetWidth();\r
3133         long h = GetHeight();\r
3134 \r
3135         for (long y=0;y<h;y++){\r
3136                 info.nProgress = (long)(100*y/h);\r
3137                 if (info.nEscape) break;\r
3138                 for (long x=0;x<w;x++){\r
3139                         RGBQUAD c = BlindGetPixelColor(x,y);\r
3140                         switch (srcColorSpace){\r
3141                         case 0:\r
3142                                 break;\r
3143                         case 1:\r
3144                                 c = HSLtoRGB(c);\r
3145                                 break;\r
3146                         case 2:\r
3147                                 c = YUVtoRGB(c);\r
3148                                 break;\r
3149                         case 3:\r
3150                                 c = YIQtoRGB(c);\r
3151                                 break;\r
3152                         case 4:\r
3153                                 c = XYZtoRGB(c);\r
3154                                 break;\r
3155                         default:\r
3156                                 strcpy(info.szLastError,"ConvertColorSpace: unknown source colorspace");\r
3157                                 return false;\r
3158                         }\r
3159                         switch (dstColorSpace){\r
3160                         case 0:\r
3161                                 break;\r
3162                         case 1:\r
3163                                 c = RGBtoHSL(c);\r
3164                                 break;\r
3165                         case 2:\r
3166                                 c = RGBtoYUV(c);\r
3167                                 break;\r
3168                         case 3:\r
3169                                 c = RGBtoYIQ(c);\r
3170                                 break;\r
3171                         case 4:\r
3172                                 c = RGBtoXYZ(c);\r
3173                                 break;\r
3174                         default:\r
3175                                 strcpy(info.szLastError,"ConvertColorSpace: unknown destination colorspace");\r
3176                                 return false;\r
3177                         }\r
3178                         BlindSetPixelColor(x,y,c);\r
3179                 }\r
3180         }\r
3181         return true;\r
3182 }\r
3183 ////////////////////////////////////////////////////////////////////////////////\r
3184 /**\r
3185  * Finds the optimal (global or local) treshold for image binarization\r
3186  * \param method: 0 = average all methods (default); 1 = Otsu; 2 = Kittler & Illingworth; 3 = max entropy; 4 = potential difference;\r
3187  * \param pBox: region from where the threshold is computed; 0 = full image (default).\r
3188  * \param pContrastMask: limit the computation only in regions with contrasted (!=0) pixels; default = 0.\r
3189  * the pContrastMask image must be grayscale with same with and height of the current image,\r
3190  * can be obtained from the current image with a filter:\r
3191  *   CxImage iContrastMask(*image,true,false,false);\r
3192  *   iContrastMask.GrayScale();\r
3193  *   long edge[]={-1,-1,-1,-1,8,-1,-1,-1,-1};\r
3194  *   iContrastMask.Filter(edge,3,1,0);\r
3195  *   long blur[]={1,1,1,1,1,1,1,1,1};\r
3196  *   iContrastMask.Filter(blur,3,9,0);\r
3197  * \return optimal threshold; -1 = error.\r
3198  * \sa AdaptiveThreshold\r
3199  */\r
3200 int  CxImage::OptimalThreshold(long method, RECT * pBox, CxImage* pContrastMask)\r
3201 {\r
3202         if (!pDib)\r
3203                 return false;\r
3204 \r
3205         if (head.biBitCount!=8){\r
3206                 strcpy(info.szLastError,"OptimalThreshold works only on 8 bit images");\r
3207                 return -1;\r
3208         }\r
3209 \r
3210         if (pContrastMask){\r
3211                 if (!pContrastMask->IsValid() ||\r
3212                         !pContrastMask->IsGrayScale() ||\r
3213                         pContrastMask->GetWidth() != GetWidth() ||\r
3214                         pContrastMask->GetHeight() != GetHeight()){\r
3215                         strcpy(info.szLastError,"OptimalThreshold invalid ContrastMask");\r
3216                         return -1;\r
3217                 }\r
3218         }\r
3219 \r
3220         long xmin,xmax,ymin,ymax;\r
3221         if (pBox){\r
3222                 xmin = max(pBox->left,0);\r
3223                 xmax = min(pBox->right,head.biWidth);\r
3224                 ymin = max(pBox->bottom,0);\r
3225                 ymax = min(pBox->top,head.biHeight);\r
3226         } else {\r
3227                 xmin = ymin = 0;\r
3228                 xmax = head.biWidth; ymax=head.biHeight;\r
3229         }\r
3230         \r
3231         if (xmin>=xmax || ymin>=ymax)\r
3232                 return -1;\r
3233 \r
3234         double p[256];\r
3235         memset(p,  0, 256*sizeof(double));\r
3236         //build histogram\r
3237         for (long y = ymin; y<ymax; y++){\r
3238                 BYTE* pGray = GetBits(y) + xmin;\r
3239                 BYTE* pContr = 0;\r
3240                 if (pContrastMask) pContr = pContrastMask->GetBits(y) + xmin;\r
3241                 for (long x = xmin; x<xmax; x++){\r
3242                         BYTE n = *pGray++;\r
3243                         if (pContr){\r
3244                                 if (*pContr) p[n]++;\r
3245                                 pContr++;\r
3246                         } else {\r
3247                                 p[n]++;\r
3248                         }\r
3249                 }\r
3250         }\r
3251 \r
3252         //find histogram limits\r
3253         int gray_min = 0;\r
3254         while (gray_min<255 && p[gray_min]==0) gray_min++;\r
3255         int gray_max = 255;\r
3256         while (gray_max>0 && p[gray_max]==0) gray_max--;\r
3257         if (gray_min > gray_max)\r
3258                 return -1;\r
3259         if (gray_min == gray_max){\r
3260                 if (gray_min == 0)\r
3261                         return 0;\r
3262                 else\r
3263                         return gray_max-1;\r
3264         }\r
3265 \r
3266         //compute total moments 0th,1st,2nd order\r
3267         int i,k;\r
3268         double w_tot = 0;\r
3269         double m_tot = 0;\r
3270         double q_tot = 0;\r
3271         for (i = gray_min; i <= gray_max; i++){\r
3272                 w_tot += p[i];\r
3273                 m_tot += i*p[i];\r
3274                 q_tot += i*i*p[i];\r
3275         }\r
3276 \r
3277         double L, L1max, L2max, L3max, L4max; //objective functions\r
3278         int th1,th2,th3,th4; //optimal thresholds\r
3279         L1max = L2max = L3max = L4max = 0;\r
3280         th1 = th2 = th3 = th4 = -1;\r
3281 \r
3282         double w1, w2, m1, m2, q1, q2, s1, s2;\r
3283         w1 = m1 = q1 = 0;\r
3284         for (i = gray_min; i < gray_max; i++){\r
3285                 w1 += p[i];\r
3286                 w2 = w_tot - w1;\r
3287                 m1 += i*p[i];\r
3288                 m2 = m_tot - m1;\r
3289                 q1 += i*i*p[i];\r
3290                 q2 = q_tot - q1;\r
3291                 s1 = q1/w1-m1*m1/w1/w1; //s1 = q1/w1-pow(m1/w1,2);\r
3292                 s2 = q2/w2-m2*m2/w2/w2; //s2 = q2/w2-pow(m2/w2,2);\r
3293 \r
3294                 //Otsu\r
3295                 L = -(s1*w1 + s2*w2); //implemented as definition\r
3296                 //L = w1 * w2 * (m2/w2 - m1/w1)*(m2/w2 - m1/w1); //implementation that doesn't need s1 & s2\r
3297                 if (L1max < L || th1<0){\r
3298                         L1max = L;\r
3299                         th1 = i;\r
3300                 }\r
3301 \r
3302                 //Kittler and Illingworth\r
3303                 if (s1>0 && s2>0){\r
3304                         L = w1*log(w1/sqrt(s1))+w2*log(w2/sqrt(s2));\r
3305                         //L = w1*log(w1*w1/s1)+w2*log(w2*w2/s2);\r
3306                         if (L2max < L || th2<0){\r
3307                                 L2max = L;\r
3308                                 th2 = i;\r
3309                         }\r
3310                 }\r
3311 \r
3312                 //max entropy\r
3313                 L = 0;\r
3314                 for (k=gray_min;k<=i;k++) if (p[k] > 0) L -= p[k]*log(p[k]/w1)/w1;\r
3315                 for (k;k<=gray_max;k++) if (p[k] > 0)   L -= p[k]*log(p[k]/w2)/w2;\r
3316                 if (L3max < L || th3<0){\r
3317                         L3max = L;\r
3318                         th3 = i;\r
3319                 }\r
3320 \r
3321                 //potential difference (based on Electrostatic Binarization method by J. Acharya & G. Sreechakra)\r
3322                 // L=-fabs(vdiff/vsum); Ã¨ molto selettivo, sembra che L=-fabs(vdiff) o L=-(vsum)\r
3323                 // abbiano lo stesso valore di soglia... il che semplificherebbe molto la routine\r
3324                 double vdiff = 0;\r
3325                 for (k=gray_min;k<=i;k++)\r
3326                         vdiff += p[k]*(i-k)*(i-k);\r
3327                 double vsum = vdiff;\r
3328                 for (k;k<=gray_max;k++){\r
3329                         double dv = p[k]*(k-i)*(k-i);\r
3330                         vdiff -= dv;\r
3331                         vsum += dv;\r
3332                 }\r
3333                 if (vsum>0) L = -fabs(vdiff/vsum); else L = 0;\r
3334                 if (L4max < L || th4<0){\r
3335                         L4max = L;\r
3336                         th4 = i;\r
3337                 }\r
3338         }\r
3339 \r
3340         int threshold;\r
3341         switch (method){\r
3342         case 1: //Otsu\r
3343                 threshold = th1;\r
3344                 break;\r
3345         case 2: //Kittler and Illingworth\r
3346                 threshold = th2;\r
3347                 break;\r
3348         case 3: //max entropy\r
3349                 threshold = th3;\r
3350                 break;\r
3351         case 4: //potential difference\r
3352                 threshold = th4;\r
3353                 break;\r
3354         default: //auto\r
3355                 {\r
3356                         int nt = 0;\r
3357                         threshold = 0;\r
3358                         if (th1>=0) { threshold += th1; nt++;}\r
3359                         if (th2>=0) { threshold += th2; nt++;}\r
3360                         if (th3>=0) { threshold += th3; nt++;}\r
3361                         if (th4>=0) { threshold += th4; nt++;}\r
3362                         if (nt)\r
3363                                 threshold /= nt;\r
3364                         else\r
3365                                 threshold = (gray_min+gray_max)/2;\r
3366 \r
3367                         /*better(?) but really expensive alternative:\r
3368                         n = 0:255;\r
3369                         pth1 = c1(th1)/sqrt(2*pi*s1(th1))*exp(-((n - m1(th1)).^2)/2/s1(th1)) + c2(th1)/sqrt(2*pi*s2(th1))*exp(-((n - m2(th1)).^2)/2/s2(th1));\r
3370                         pth2 = c1(th2)/sqrt(2*pi*s1(th2))*exp(-((n - m1(th2)).^2)/2/s1(th2)) + c2(th2)/sqrt(2*pi*s2(th2))*exp(-((n - m2(th2)).^2)/2/s2(th2));\r
3371                         ...\r
3372                         mse_th1 = sum((p-pth1).^2);\r
3373                         mse_th2 = sum((p-pth2).^2);\r
3374                         ...\r
3375                         select th# that gives minimum mse_th#\r
3376                         */\r
3377 \r
3378                 }\r
3379         }\r
3380 \r
3381         if (threshold <= gray_min || threshold >= gray_max)\r
3382                 threshold = (gray_min+gray_max)/2;\r
3383         \r
3384         return threshold;\r
3385 }\r
3386 ///////////////////////////////////////////////////////////////////////////////\r
3387 /**\r
3388  * Converts the image to B&W, using an optimal threshold mask\r
3389  * \param method: 0 = average all methods (default); 1 = Otsu; 2 = Kittler & Illingworth; 3 = max entropy; 4 = potential difference;\r
3390  * \param nBoxSize: the image is divided into "nBoxSize x nBoxSize" blocks, from where the threshold is computed; min = 8; default = 64.\r
3391  * \param pContrastMask: limit the computation only in regions with contrasted (!=0) pixels; default = 0.\r
3392  * \param nBias: global offset added to the threshold mask; default = 0.\r
3393  * \param fGlobalLocalBalance: balance between local and global threshold. default = 0.5\r
3394  * fGlobalLocalBalance can be from 0.0 (use only local threshold) to 1.0 (use only global threshold)\r
3395  * the pContrastMask image must be grayscale with same with and height of the current image,\r
3396  * \return true if everything is ok.\r
3397  * \sa OptimalThreshold\r
3398  */\r
3399 bool CxImage::AdaptiveThreshold(long method, long nBoxSize, CxImage* pContrastMask, long nBias, float fGlobalLocalBalance)\r
3400 {\r
3401         if (!pDib)\r
3402                 return false;\r
3403 \r
3404         if (pContrastMask){\r
3405                 if (!pContrastMask->IsValid() ||\r
3406                         !pContrastMask->IsGrayScale() ||\r
3407                         pContrastMask->GetWidth() != GetWidth() ||\r
3408                         pContrastMask->GetHeight() != GetHeight()){\r
3409                         strcpy(info.szLastError,"AdaptiveThreshold invalid ContrastMask");\r
3410                         return false;\r
3411                 }\r
3412         }\r
3413 \r
3414         if (nBoxSize<8) nBoxSize = 8;\r
3415         if (fGlobalLocalBalance<0.0f) fGlobalLocalBalance = 0.0f;\r
3416         if (fGlobalLocalBalance>1.0f) fGlobalLocalBalance = 1.0f;\r
3417 \r
3418         long mw = (head.biWidth + nBoxSize - 1)/nBoxSize;\r
3419         long mh = (head.biHeight + nBoxSize - 1)/nBoxSize;\r
3420 \r
3421         CxImage mask(mw,mh,8);\r
3422         if(!mask.GrayScale())\r
3423                 return false;\r
3424 \r
3425         if(!GrayScale())\r
3426                 return false;\r
3427 \r
3428         int globalthreshold = OptimalThreshold(method, 0, pContrastMask);\r
3429         if (globalthreshold <0)\r
3430                 return false;\r
3431 \r
3432         for (long y=0; y<mh; y++){\r
3433                 for (long x=0; x<mw; x++){\r
3434                         info.nProgress = (long)(100*(x+y*mw)/(mw*mh));\r
3435                         if (info.nEscape) break;\r
3436                         RECT r;\r
3437                         r.left = x*nBoxSize;\r
3438                         r.right = r.left + nBoxSize;\r
3439                         r.bottom = y*nBoxSize;\r
3440                         r.top = r.bottom + nBoxSize;\r
3441                         int threshold = OptimalThreshold(method, &r, pContrastMask);\r
3442                         if (threshold <0) return false;\r
3443                         mask.SetPixelIndex(x,y,(BYTE)max(0,min(255,nBias+((1.0f-fGlobalLocalBalance)*threshold + fGlobalLocalBalance*globalthreshold))));\r
3444                 }\r
3445         }\r
3446 \r
3447         mask.Resample(mw*nBoxSize,mh*nBoxSize,0);\r
3448         mask.Crop(0,head.biHeight,head.biWidth,0);\r
3449 \r
3450         if(!Threshold(&mask))\r
3451                 return false;\r
3452 \r
3453         return true;\r
3454 }\r
3455 \r
3456 ////////////////////////////////////////////////////////////////////////////////\r
3457 #include <queue>\r
3458 ////////////////////////////////////////////////////////////////////////////////\r
3459 /**\r
3460  * Flood Fill\r
3461  * \param xStart, yStart: starting point\r
3462  * \param cFillColor: filling color\r
3463  * \param nTolerance: deviation from the starting point color\r
3464  * \param nOpacity: can be from 0 (transparent) to 255 (opaque, default)\r
3465  * \param bSelectFilledArea: if true, the pixels in the region are also set in the selection layer; default = false\r
3466  * \param nSelectionLevel: if bSelectFilledArea is true, the selected pixels are set to nSelectionLevel; default = 255\r
3467  * Note: nOpacity=0 && bSelectFilledArea=true act as a "magic wand"\r
3468  * \return true if everything is ok\r
3469  */\r
3470 bool CxImage::FloodFill(const long xStart, const long yStart, const RGBQUAD cFillColor, const BYTE nTolerance,\r
3471                                                 BYTE nOpacity, const bool bSelectFilledArea, const BYTE nSelectionLevel)\r
3472 {\r
3473         if (!pDib)\r
3474                 return false;\r
3475 \r
3476         if (!IsInside(xStart,yStart))\r
3477                 return true;\r
3478 \r
3479 #if CXIMAGE_SUPPORT_SELECTION\r
3480         if (!SelectionIsInside(xStart,yStart))\r
3481                 return true;\r
3482 #endif //CXIMAGE_SUPPORT_SELECTION\r
3483 \r
3484         RGBQUAD* pPalette=NULL;\r
3485         WORD bpp = GetBpp();\r
3486         //nTolerance or nOpacity implemented only for grayscale or 24bpp images\r
3487         if ((nTolerance || nOpacity != 255) &&  !(head.biBitCount == 24 || IsGrayScale())){\r
3488                 pPalette = new RGBQUAD[head.biClrUsed];\r
3489                 memcpy(pPalette, GetPalette(),GetPaletteSize());\r
3490                 if (!IncreaseBpp(24))\r
3491                         return false;\r
3492         }\r
3493 \r
3494         BYTE* pFillMask = (BYTE*)calloc(head.biWidth * head.biHeight,1);\r
3495         if (!pFillMask)\r
3496                 return false;\r
3497 \r
3498 //------------------------------------- Begin of Flood Fill\r
3499         POINT offset[4] = {{-1,0},{0,-1},{1,0},{0,1}};\r
3500         std::queue<POINT> q;\r
3501         POINT point = {xStart,yStart};\r
3502         q.push(point);\r
3503 \r
3504         if (IsIndexed()){ //--- Generic indexed image, no tolerance OR Grayscale image with tolerance\r
3505                 BYTE idxRef = GetPixelIndex(xStart,yStart);\r
3506                 BYTE idxFill = GetNearestIndex(cFillColor);\r
3507                 BYTE idxMin = (BYTE)min(255, max(0,(int)(idxRef - nTolerance)));\r
3508                 BYTE idxMax = (BYTE)min(255, max(0,(int)(idxRef + nTolerance)));\r
3509 \r
3510                 while(!q.empty())\r
3511                 {\r
3512                         point = q.front();\r
3513                         q.pop();\r
3514 \r
3515                         for (int z=0; z<4; z++){\r
3516                                 int x = point.x + offset[z].x;\r
3517                                 int y = point.y + offset[z].y;\r
3518                                 if(IsInside(x,y)){\r
3519 #if CXIMAGE_SUPPORT_SELECTION\r
3520                                   if (BlindSelectionIsInside(x,y))\r
3521 #endif //CXIMAGE_SUPPORT_SELECTION\r
3522                                   {\r
3523                                         BYTE idx = BlindGetPixelIndex(x, y);\r
3524                                         BYTE* pFill = pFillMask + x + y * head.biWidth;\r
3525                                         if (*pFill==0 && idxMin <= idx && idx <= idxMax )\r
3526                                         {\r
3527                                                 if (nOpacity>0){\r
3528                                                         if (nOpacity == 255)\r
3529                                                                 BlindSetPixelIndex(x, y, idxFill);\r
3530                                                         else\r
3531                                                                 BlindSetPixelIndex(x, y, (BYTE)((idxFill * nOpacity + idx * (255-nOpacity))>>8));\r
3532                                                 }\r
3533                                                 POINT pt = {x,y};\r
3534                                                 q.push(pt);\r
3535                                                 *pFill = 1;\r
3536                                         }\r
3537                                   }\r
3538                                 }\r
3539                         }\r
3540                 }\r
3541         } else { //--- RGB image\r
3542                 RGBQUAD cRef = GetPixelColor(xStart,yStart);\r
3543                 RGBQUAD cRefMin, cRefMax;\r
3544                 cRefMin.rgbRed   = (BYTE)min(255, max(0,(int)(cRef.rgbRed   - nTolerance)));\r
3545                 cRefMin.rgbGreen = (BYTE)min(255, max(0,(int)(cRef.rgbGreen - nTolerance)));\r
3546                 cRefMin.rgbBlue  = (BYTE)min(255, max(0,(int)(cRef.rgbBlue  - nTolerance)));\r
3547                 cRefMax.rgbRed   = (BYTE)min(255, max(0,(int)(cRef.rgbRed   + nTolerance)));\r
3548                 cRefMax.rgbGreen = (BYTE)min(255, max(0,(int)(cRef.rgbGreen + nTolerance)));\r
3549                 cRefMax.rgbBlue  = (BYTE)min(255, max(0,(int)(cRef.rgbBlue  + nTolerance)));\r
3550 \r
3551                 while(!q.empty())\r
3552                 {\r
3553                         point = q.front();\r
3554                         q.pop();\r
3555 \r
3556                         for (int z=0; z<4; z++){\r
3557                                 int x = point.x + offset[z].x;\r
3558                                 int y = point.y + offset[z].y;\r
3559                                 if(IsInside(x,y)){\r
3560 #if CXIMAGE_SUPPORT_SELECTION\r
3561                                   if (BlindSelectionIsInside(x,y))\r
3562 #endif //CXIMAGE_SUPPORT_SELECTION\r
3563                                   {\r
3564                                         RGBQUAD cc = BlindGetPixelColor(x, y);\r
3565                                         BYTE* pFill = pFillMask + x + y * head.biWidth;\r
3566                                         if (*pFill==0 &&\r
3567                                                 cRefMin.rgbRed   <= cc.rgbRed   && cc.rgbRed   <= cRefMax.rgbRed   &&\r
3568                                                 cRefMin.rgbGreen <= cc.rgbGreen && cc.rgbGreen <= cRefMax.rgbGreen &&\r
3569                                                 cRefMin.rgbBlue  <= cc.rgbBlue  && cc.rgbBlue  <= cRefMax.rgbBlue )\r
3570                                         {\r
3571                                                 if (nOpacity>0){\r
3572                                                         if (nOpacity == 255)\r
3573                                                                 BlindSetPixelColor(x, y, cFillColor);\r
3574                                                         else\r
3575                                                         {\r
3576                                                                 cc.rgbRed   = (BYTE)((cFillColor.rgbRed   * nOpacity + cc.rgbRed   * (255-nOpacity))>>8);\r
3577                                                                 cc.rgbGreen = (BYTE)((cFillColor.rgbGreen * nOpacity + cc.rgbGreen * (255-nOpacity))>>8);\r
3578                                                                 cc.rgbBlue  = (BYTE)((cFillColor.rgbBlue  * nOpacity + cc.rgbBlue  * (255-nOpacity))>>8);\r
3579                                                                 BlindSetPixelColor(x, y, cc);\r
3580                                                         }\r
3581                                                 }\r
3582                                                 POINT pt = {x,y};\r
3583                                                 q.push(pt);\r
3584                                                 *pFill = 1;\r
3585                                         }\r
3586                                   }\r
3587                                 }\r
3588                         }\r
3589                 }\r
3590         }\r
3591         if (pFillMask[xStart+yStart*head.biWidth] == 0 && nOpacity>0){\r
3592                 if (nOpacity == 255)\r
3593                         BlindSetPixelColor(xStart, yStart, cFillColor);\r
3594                 else\r
3595                 {\r
3596                         RGBQUAD cc = BlindGetPixelColor(xStart, yStart);\r
3597                         cc.rgbRed   = (BYTE)((cFillColor.rgbRed   * nOpacity + cc.rgbRed   * (255-nOpacity))>>8);\r
3598                         cc.rgbGreen = (BYTE)((cFillColor.rgbGreen * nOpacity + cc.rgbGreen * (255-nOpacity))>>8);\r
3599                         cc.rgbBlue  = (BYTE)((cFillColor.rgbBlue  * nOpacity + cc.rgbBlue  * (255-nOpacity))>>8);\r
3600                         BlindSetPixelColor(xStart, yStart, cc);\r
3601                 }\r
3602         }\r
3603         pFillMask[xStart+yStart*head.biWidth] = 1;\r
3604 //------------------------------------- End of Flood Fill\r
3605 \r
3606         //if necessary, restore the original BPP and palette\r
3607         if (pPalette){\r
3608                 DecreaseBpp(bpp, false, pPalette);\r
3609                 delete [] pPalette;\r
3610         }\r
3611 \r
3612 #if CXIMAGE_SUPPORT_SELECTION\r
3613         if (bSelectFilledArea){\r
3614                 if (!SelectionIsValid()){\r
3615                         if (!SelectionCreate()){\r
3616                                 return false;\r
3617                         }\r
3618                         SelectionClear();\r
3619                         info.rSelectionBox.right = head.biWidth;\r
3620                         info.rSelectionBox.top = head.biHeight;\r
3621                         info.rSelectionBox.left = info.rSelectionBox.bottom = 0;\r
3622                 }\r
3623                 RECT r;\r
3624                 SelectionGetBox(r);\r
3625                 for (long y = r.bottom; y < r.top; y++){\r
3626                         BYTE* pFill = pFillMask + r.left + y * head.biWidth;\r
3627                         for (long x = r.left; x<r.right; x++){\r
3628                                 if (*pFill)     SelectionSet(x,y,nSelectionLevel);\r
3629                                 pFill++;\r
3630                         }\r
3631                 }\r
3632                 SelectionRebuildBox();\r
3633         }\r
3634 #endif //CXIMAGE_SUPPORT_SELECTION\r
3635 \r
3636         free(pFillMask);\r
3637 \r
3638         return true;\r
3639 }\r
3640 \r
3641 ////////////////////////////////////////////////////////////////////////////////\r
3642 #endif //CXIMAGE_SUPPORT_DSP\r