]> Creatis software - clitk.git/blob - utilities/CxImage/ximatran.cpp
cosmetic
[clitk.git] / utilities / CxImage / ximatran.cpp
1 // xImaTran.cpp : Transformation functions
2 /* 07/08/2001 v1.00 - Davide Pizzolato - www.xdp.it
3  * CxImage version 6.0.0 02/Feb/2008
4  */
5
6 #include "ximage.h"
7 #include "ximath.h"
8
9 #if CXIMAGE_SUPPORT_BASICTRANSFORMATIONS
10 ////////////////////////////////////////////////////////////////////////////////
11 bool CxImage::GrayScale()
12 {
13         if (!pDib) return false;
14         if (head.biBitCount<=8){
15                 RGBQUAD* ppal=GetPalette();
16                 int gray;
17                 //converts the colors to gray, use the blue channel only
18                 for(DWORD i=0;i<head.biClrUsed;i++){
19                         gray=(int)RGB2GRAY(ppal[i].rgbRed,ppal[i].rgbGreen,ppal[i].rgbBlue);
20                         ppal[i].rgbBlue = (BYTE)gray;
21                 }
22                 // preserve transparency
23                 if (info.nBkgndIndex >= 0) info.nBkgndIndex = ppal[info.nBkgndIndex].rgbBlue;
24                 //create a "real" 8 bit gray scale image
25                 if (head.biBitCount==8){
26                         BYTE *img=info.pImage;
27                         for(DWORD i=0;i<head.biSizeImage;i++) img[i]=ppal[img[i]].rgbBlue;
28                         SetGrayPalette();
29                 }
30                 //transform to 8 bit gray scale
31                 if (head.biBitCount==4 || head.biBitCount==1){
32                         CxImage ima;
33                         ima.CopyInfo(*this);
34                         if (!ima.Create(head.biWidth,head.biHeight,8,info.dwType)) return false;
35                         ima.SetGrayPalette();
36 #if CXIMAGE_SUPPORT_SELECTION
37                         ima.SelectionCopy(*this);
38 #endif //CXIMAGE_SUPPORT_SELECTION
39 #if CXIMAGE_SUPPORT_ALPHA
40                         ima.AlphaCopy(*this);
41 #endif //CXIMAGE_SUPPORT_ALPHA
42                         for (long y=0;y<head.biHeight;y++){
43                                 BYTE *iDst = ima.GetBits(y);
44                                 BYTE *iSrc = GetBits(y);
45                                 for (long x=0;x<head.biWidth; x++){
46                                         //iDst[x]=ppal[BlindGetPixelIndex(x,y)].rgbBlue;
47                                         if (head.biBitCount==4){
48                                                 BYTE pos = (BYTE)(4*(1-x%2));
49                                                 iDst[x]= ppal[(BYTE)((iSrc[x >> 1]&((BYTE)0x0F<<pos)) >> pos)].rgbBlue;
50                                         } else {
51                                                 BYTE pos = (BYTE)(7-x%8);
52                                                 iDst[x]= ppal[(BYTE)((iSrc[x >> 3]&((BYTE)0x01<<pos)) >> pos)].rgbBlue;
53                                         }
54                                 }
55                         }
56                         Transfer(ima);
57                 }
58         } else { //from RGB to 8 bit gray scale
59                 BYTE *iSrc=info.pImage;
60                 CxImage ima;
61                 ima.CopyInfo(*this);
62                 if (!ima.Create(head.biWidth,head.biHeight,8,info.dwType)) return false;
63                 ima.SetGrayPalette();
64 #if CXIMAGE_SUPPORT_SELECTION
65                 ima.SelectionCopy(*this);
66 #endif //CXIMAGE_SUPPORT_SELECTION
67 #if CXIMAGE_SUPPORT_ALPHA
68                 ima.AlphaCopy(*this);
69 #endif //CXIMAGE_SUPPORT_ALPHA
70                 BYTE *img=ima.GetBits();
71                 long l8=ima.GetEffWidth();
72                 long l=head.biWidth * 3;
73                 for(long y=0; y < head.biHeight; y++) {
74                         for(long x=0,x8=0; x < l; x+=3,x8++) {
75                                 img[x8+y*l8]=(BYTE)RGB2GRAY(*(iSrc+x+2),*(iSrc+x+1),*(iSrc+x+0));
76                         }
77                         iSrc+=info.dwEffWidth;
78                 }
79                 Transfer(ima);
80         }
81         return true;
82 }
83 ////////////////////////////////////////////////////////////////////////////////
84 /**
85  * \sa Mirror
86  * \author [qhbo]
87  */
88 bool CxImage::Flip(bool bFlipSelection, bool bFlipAlpha)
89 {
90         if (!pDib) return false;
91
92         BYTE *buff = (BYTE*)malloc(info.dwEffWidth);
93         if (!buff) return false;
94
95         BYTE *iSrc,*iDst;
96         iSrc = GetBits(head.biHeight-1);
97         iDst = GetBits(0);
98         for (long i=0; i<(head.biHeight/2); ++i)
99         {
100                 memcpy(buff, iSrc, info.dwEffWidth);
101                 memcpy(iSrc, iDst, info.dwEffWidth);
102                 memcpy(iDst, buff, info.dwEffWidth);
103                 iSrc-=info.dwEffWidth;
104                 iDst+=info.dwEffWidth;
105         }
106
107         free(buff);
108
109         if (bFlipSelection){
110 #if CXIMAGE_SUPPORT_SELECTION
111                 SelectionFlip();
112 #endif //CXIMAGE_SUPPORT_SELECTION
113         }
114
115         if (bFlipAlpha){
116 #if CXIMAGE_SUPPORT_ALPHA
117                 AlphaFlip();
118 #endif //CXIMAGE_SUPPORT_ALPHA
119         }
120
121         return true;
122 }
123 ////////////////////////////////////////////////////////////////////////////////
124 /**
125  * \sa Flip
126  */
127 bool CxImage::Mirror(bool bMirrorSelection, bool bMirrorAlpha)
128 {
129         if (!pDib) return false;
130
131         CxImage* imatmp = new CxImage(*this,false,true,true);
132         if (!imatmp) return false;
133         if (!imatmp->IsValid()){
134                 delete imatmp;
135                 return false;
136         }
137
138         BYTE *iSrc,*iDst;
139         long wdt=(head.biWidth-1) * (head.biBitCount==24 ? 3:1);
140         iSrc=info.pImage + wdt;
141         iDst=imatmp->info.pImage;
142         long x,y;
143         switch (head.biBitCount){
144         case 24:
145                 for(y=0; y < head.biHeight; y++){
146                         for(x=0; x <= wdt; x+=3){
147                                 *(iDst+x)=*(iSrc-x);
148                                 *(iDst+x+1)=*(iSrc-x+1);
149                                 *(iDst+x+2)=*(iSrc-x+2);
150                         }
151                         iSrc+=info.dwEffWidth;
152                         iDst+=info.dwEffWidth;
153                 }
154                 break;
155         case 8:
156                 for(y=0; y < head.biHeight; y++){
157                         for(x=0; x <= wdt; x++)
158                                 *(iDst+x)=*(iSrc-x);
159                         iSrc+=info.dwEffWidth;
160                         iDst+=info.dwEffWidth;
161                 }
162                 break;
163         default:
164                 for(y=0; y < head.biHeight; y++){
165                         for(x=0; x <= wdt; x++)
166                                 imatmp->SetPixelIndex(x,y,GetPixelIndex(wdt-x,y));
167                 }
168         }
169
170         if (bMirrorSelection){
171 #if CXIMAGE_SUPPORT_SELECTION
172                 imatmp->SelectionMirror();
173 #endif //CXIMAGE_SUPPORT_SELECTION
174         }
175
176         if (bMirrorAlpha){
177 #if CXIMAGE_SUPPORT_ALPHA
178                 imatmp->AlphaMirror();
179 #endif //CXIMAGE_SUPPORT_ALPHA
180         }
181
182         Transfer(*imatmp);
183         delete imatmp;
184         return true;
185 }
186
187 ////////////////////////////////////////////////////////////////////////////////
188 #define RBLOCK 64
189
190 ////////////////////////////////////////////////////////////////////////////////
191 bool CxImage::RotateLeft(CxImage* iDst)
192 {
193         if (!pDib) return false;
194
195         long newWidth = GetHeight();
196         long newHeight = GetWidth();
197
198         CxImage imgDest;
199         imgDest.CopyInfo(*this);
200         imgDest.Create(newWidth,newHeight,GetBpp(),GetType());
201         imgDest.SetPalette(GetPalette());
202
203 #if CXIMAGE_SUPPORT_ALPHA
204         if (AlphaIsValid()) imgDest.AlphaCreate();
205 #endif
206
207 #if CXIMAGE_SUPPORT_SELECTION
208         if (SelectionIsValid()) imgDest.SelectionCreate();
209 #endif
210
211         long x,x2,y,dlineup;
212         
213         // Speedy rotate for BW images <Robert Abram>
214         if (head.biBitCount == 1) {
215         
216                 BYTE *sbits, *dbits, *dbitsmax, bitpos, *nrow,*srcdisp;
217                 ldiv_t div_r;
218
219                 BYTE *bsrc = GetBits(), *bdest = imgDest.GetBits();
220                 dbitsmax = bdest + imgDest.head.biSizeImage - 1;
221                 dlineup = 8 * imgDest.info.dwEffWidth - imgDest.head.biWidth;
222
223                 imgDest.Clear(0);
224                 for (y = 0; y < head.biHeight; y++) {
225                         // Figure out the Column we are going to be copying to
226                         div_r = ldiv(y + dlineup, (long)8);
227                         // set bit pos of src column byte                               
228                         bitpos = (BYTE)(1 << div_r.rem);
229                         srcdisp = bsrc + y * info.dwEffWidth;
230                         for (x = 0; x < (long)info.dwEffWidth; x++) {
231                                 // Get Source Bits
232                                 sbits = srcdisp + x;
233                                 // Get destination column
234                                 nrow = bdest + (x * 8) * imgDest.info.dwEffWidth + imgDest.info.dwEffWidth - 1 - div_r.quot;
235                                 for (long z = 0; z < 8; z++) {
236                                    // Get Destination Byte
237                                         dbits = nrow + z * imgDest.info.dwEffWidth;
238                                         if ((dbits < bdest) || (dbits > dbitsmax)) break;
239                                         if (*sbits & (128 >> z)) *dbits |= bitpos;
240                                 }
241                         }
242                 }//for y
243
244 #if CXIMAGE_SUPPORT_ALPHA
245                 if (AlphaIsValid()) {
246                         for (x = 0; x < newWidth; x++){
247                                 x2=newWidth-x-1;
248                                 for (y = 0; y < newHeight; y++){
249                                         imgDest.AlphaSet(x,y,BlindAlphaGet(y, x2));
250                                 }//for y
251                         }//for x
252                 }
253 #endif //CXIMAGE_SUPPORT_ALPHA
254
255 #if CXIMAGE_SUPPORT_SELECTION
256                 if (SelectionIsValid()) {
257                         imgDest.info.rSelectionBox.left = newWidth-info.rSelectionBox.top;
258                         imgDest.info.rSelectionBox.right = newWidth-info.rSelectionBox.bottom;
259                         imgDest.info.rSelectionBox.bottom = info.rSelectionBox.left;
260                         imgDest.info.rSelectionBox.top = info.rSelectionBox.right;
261                         for (x = 0; x < newWidth; x++){
262                                 x2=newWidth-x-1;
263                                 for (y = 0; y < newHeight; y++){
264                                         imgDest.SelectionSet(x,y,BlindSelectionGet(y, x2));
265                                 }//for y
266                         }//for x
267                 }
268 #endif //CXIMAGE_SUPPORT_SELECTION
269
270         } else {
271         //anything other than BW:
272         //bd, 10. 2004: This optimized version of rotation rotates image by smaller blocks. It is quite
273         //a bit faster than obvious algorithm, because it produces much less CPU cache misses.
274         //This optimization can be tuned by changing block size (RBLOCK). 96 is good value for current
275         //CPUs (tested on Athlon XP and Celeron D). Larger value (if CPU has enough cache) will increase
276         //speed somehow, but once you drop out of CPU's cache, things will slow down drastically.
277         //For older CPUs with less cache, lower value would yield better results.
278                 
279                 BYTE *srcPtr, *dstPtr;                        //source and destionation for 24-bit version
280                 int xs, ys;                                   //x-segment and y-segment
281                 for (xs = 0; xs < newWidth; xs+=RBLOCK) {       //for all image blocks of RBLOCK*RBLOCK pixels
282                         for (ys = 0; ys < newHeight; ys+=RBLOCK) {
283                                 if (head.biBitCount==24) {
284                                         //RGB24 optimized pixel access:
285                                         for (x = xs; x < min(newWidth, xs+RBLOCK); x++){    //do rotation
286                                                 info.nProgress = (long)(100*x/newWidth);
287                                                 x2=newWidth-x-1;
288                                                 dstPtr = (BYTE*) imgDest.BlindGetPixelPointer(x,ys);
289                                                 srcPtr = (BYTE*) BlindGetPixelPointer(ys, x2);
290                                                 for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
291                                                         //imgDest.SetPixelColor(x, y, GetPixelColor(y, x2));
292                                                         *(dstPtr) = *(srcPtr);
293                                                         *(dstPtr+1) = *(srcPtr+1);
294                                                         *(dstPtr+2) = *(srcPtr+2);
295                                                         srcPtr += 3;
296                                                         dstPtr += imgDest.info.dwEffWidth;
297                                                 }//for y
298                                         }//for x
299                                 } else {
300                                         //anything else than 24bpp (and 1bpp): palette
301                                         for (x = xs; x < min(newWidth, xs+RBLOCK); x++){
302                                                 info.nProgress = (long)(100*x/newWidth); //<Anatoly Ivasyuk>
303                                                 x2=newWidth-x-1;
304                                                 for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
305                                                         imgDest.SetPixelIndex(x, y, BlindGetPixelIndex(y, x2));
306                                                 }//for y
307                                         }//for x
308                                 }//if (version selection)
309 #if CXIMAGE_SUPPORT_ALPHA
310                                 if (AlphaIsValid()) {
311                                         for (x = xs; x < min(newWidth, xs+RBLOCK); x++){
312                                                 x2=newWidth-x-1;
313                                                 for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
314                                                         imgDest.AlphaSet(x,y,BlindAlphaGet(y, x2));
315                                                 }//for y
316                                         }//for x
317                                 }//if (alpha channel)
318 #endif //CXIMAGE_SUPPORT_ALPHA
319
320 #if CXIMAGE_SUPPORT_SELECTION
321                                 if (SelectionIsValid()) {
322                                         imgDest.info.rSelectionBox.left = newWidth-info.rSelectionBox.top;
323                                         imgDest.info.rSelectionBox.right = newWidth-info.rSelectionBox.bottom;
324                                         imgDest.info.rSelectionBox.bottom = info.rSelectionBox.left;
325                                         imgDest.info.rSelectionBox.top = info.rSelectionBox.right;
326                                         for (x = xs; x < min(newWidth, xs+RBLOCK); x++){
327                                                 x2=newWidth-x-1;
328                                                 for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
329                                                         imgDest.SelectionSet(x,y,BlindSelectionGet(y, x2));
330                                                 }//for y
331                                         }//for x
332                                 }//if (selection)
333 #endif //CXIMAGE_SUPPORT_SELECTION
334                         }//for ys
335                 }//for xs
336         }//if
337
338         //select the destination
339         if (iDst) iDst->Transfer(imgDest);
340         else Transfer(imgDest);
341         return true;
342 }
343
344 ////////////////////////////////////////////////////////////////////////////////
345 bool CxImage::RotateRight(CxImage* iDst)
346 {
347         if (!pDib) return false;
348
349         long newWidth = GetHeight();
350         long newHeight = GetWidth();
351
352         CxImage imgDest;
353         imgDest.CopyInfo(*this);
354         imgDest.Create(newWidth,newHeight,GetBpp(),GetType());
355         imgDest.SetPalette(GetPalette());
356
357 #if CXIMAGE_SUPPORT_ALPHA
358         if (AlphaIsValid()) imgDest.AlphaCreate();
359 #endif
360
361 #if CXIMAGE_SUPPORT_SELECTION
362         if (SelectionIsValid()) imgDest.SelectionCreate();
363 #endif
364
365         long x,y,y2;
366         // Speedy rotate for BW images <Robert Abram>
367         if (head.biBitCount == 1) {
368         
369                 BYTE *sbits, *dbits, *dbitsmax, bitpos, *nrow,*srcdisp;
370                 ldiv_t div_r;
371
372                 BYTE *bsrc = GetBits(), *bdest = imgDest.GetBits();
373                 dbitsmax = bdest + imgDest.head.biSizeImage - 1;
374
375                 imgDest.Clear(0);
376                 for (y = 0; y < head.biHeight; y++) {
377                         // Figure out the Column we are going to be copying to
378                         div_r = ldiv(y, (long)8);
379                         // set bit pos of src column byte                               
380                         bitpos = (BYTE)(128 >> div_r.rem);
381                         srcdisp = bsrc + y * info.dwEffWidth;
382                         for (x = 0; x < (long)info.dwEffWidth; x++) {
383                                 // Get Source Bits
384                                 sbits = srcdisp + x;
385                                 // Get destination column
386                                 nrow = bdest + (imgDest.head.biHeight-1-(x*8)) * imgDest.info.dwEffWidth + div_r.quot;
387                                 for (long z = 0; z < 8; z++) {
388                                    // Get Destination Byte
389                                         dbits = nrow - z * imgDest.info.dwEffWidth;
390                                         if ((dbits < bdest) || (dbits > dbitsmax)) break;
391                                         if (*sbits & (128 >> z)) *dbits |= bitpos;
392                                 }
393                         }
394                 }
395
396 #if CXIMAGE_SUPPORT_ALPHA
397                 if (AlphaIsValid()){
398                         for (y = 0; y < newHeight; y++){
399                                 y2=newHeight-y-1;
400                                 for (x = 0; x < newWidth; x++){
401                                         imgDest.AlphaSet(x,y,BlindAlphaGet(y2, x));
402                                 }
403                         }
404                 }
405 #endif //CXIMAGE_SUPPORT_ALPHA
406
407 #if CXIMAGE_SUPPORT_SELECTION
408                 if (SelectionIsValid()){
409                         imgDest.info.rSelectionBox.left = info.rSelectionBox.bottom;
410                         imgDest.info.rSelectionBox.right = info.rSelectionBox.top;
411                         imgDest.info.rSelectionBox.bottom = newHeight-info.rSelectionBox.right;
412                         imgDest.info.rSelectionBox.top = newHeight-info.rSelectionBox.left;
413                         for (y = 0; y < newHeight; y++){
414                                 y2=newHeight-y-1;
415                                 for (x = 0; x < newWidth; x++){
416                                         imgDest.SelectionSet(x,y,BlindSelectionGet(y2, x));
417                                 }
418                         }
419                 }
420 #endif //CXIMAGE_SUPPORT_SELECTION
421
422         } else {
423                 //anything else but BW
424                 BYTE *srcPtr, *dstPtr;                        //source and destionation for 24-bit version
425                 int xs, ys;                                   //x-segment and y-segment
426                 for (xs = 0; xs < newWidth; xs+=RBLOCK) {
427                         for (ys = 0; ys < newHeight; ys+=RBLOCK) {
428                                 if (head.biBitCount==24) {
429                                         //RGB24 optimized pixel access:
430                                         for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
431                                                 info.nProgress = (long)(100*y/newHeight); //<Anatoly Ivasyuk>
432                                                 y2=newHeight-y-1;
433                                                 dstPtr = (BYTE*) imgDest.BlindGetPixelPointer(xs,y);
434                                                 srcPtr = (BYTE*) BlindGetPixelPointer(y2, xs);
435                                                 for (x = xs; x < min(newWidth, xs+RBLOCK); x++){
436                                                         //imgDest.SetPixelColor(x, y, GetPixelColor(y2, x));
437                                                         *(dstPtr) = *(srcPtr);
438                                                         *(dstPtr+1) = *(srcPtr+1);
439                                                         *(dstPtr+2) = *(srcPtr+2);
440                                                         dstPtr += 3;
441                                                         srcPtr += info.dwEffWidth;
442                                                 }//for x
443                                         }//for y
444                                 } else {
445                                         //anything else than BW & RGB24: palette
446                                         for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
447                                                 info.nProgress = (long)(100*y/newHeight); //<Anatoly Ivasyuk>
448                                                 y2=newHeight-y-1;
449                                                 for (x = xs; x < min(newWidth, xs+RBLOCK); x++){
450                                                         imgDest.SetPixelIndex(x, y, BlindGetPixelIndex(y2, x));
451                                                 }//for x
452                                         }//for y
453                                 }//if
454 #if CXIMAGE_SUPPORT_ALPHA
455                                 if (AlphaIsValid()){
456                                         for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
457                                                 y2=newHeight-y-1;
458                                                 for (x = xs; x < min(newWidth, xs+RBLOCK); x++){
459                                                         imgDest.AlphaSet(x,y,BlindAlphaGet(y2, x));
460                                                 }//for x
461                                         }//for y
462                                 }//if (has alpha)
463 #endif //CXIMAGE_SUPPORT_ALPHA
464
465 #if CXIMAGE_SUPPORT_SELECTION
466                                 if (SelectionIsValid()){
467                                         imgDest.info.rSelectionBox.left = info.rSelectionBox.bottom;
468                                         imgDest.info.rSelectionBox.right = info.rSelectionBox.top;
469                                         imgDest.info.rSelectionBox.bottom = newHeight-info.rSelectionBox.right;
470                                         imgDest.info.rSelectionBox.top = newHeight-info.rSelectionBox.left;
471                                         for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
472                                                 y2=newHeight-y-1;
473                                                 for (x = xs; x < min(newWidth, xs+RBLOCK); x++){
474                                                         imgDest.SelectionSet(x,y,BlindSelectionGet(y2, x));
475                                                 }//for x
476                                         }//for y
477                                 }//if (has alpha)
478 #endif //CXIMAGE_SUPPORT_SELECTION
479                         }//for ys
480                 }//for xs
481         }//if
482
483         //select the destination
484         if (iDst) iDst->Transfer(imgDest);
485         else Transfer(imgDest);
486         return true;
487 }
488
489 ////////////////////////////////////////////////////////////////////////////////
490 bool CxImage::Negative()
491 {
492         if (!pDib) return false;
493
494         if (head.biBitCount<=8){
495                 if (IsGrayScale()){ //GRAYSCALE, selection
496                         if (pSelection){
497                                 for(long y=info.rSelectionBox.bottom; y<info.rSelectionBox.top; y++){
498                                         for(long x=info.rSelectionBox.left; x<info.rSelectionBox.right; x++){
499 #if CXIMAGE_SUPPORT_SELECTION
500                                                 if (BlindSelectionIsInside(x,y))
501 #endif //CXIMAGE_SUPPORT_SELECTION
502                                                 {
503                                                         BlindSetPixelIndex(x,y,(BYTE)(255-BlindGetPixelIndex(x,y)));
504                                                 }
505                                         }
506                                 }
507                         } else {
508                                 BYTE *iSrc=info.pImage;
509                                 for(unsigned long i=0; i < head.biSizeImage; i++){
510                                         *iSrc=(BYTE)~(*(iSrc));
511                                         iSrc++;
512                                 }
513                         }
514                 } else { //PALETTE, full image
515                         RGBQUAD* ppal=GetPalette();
516                         for(DWORD i=0;i<head.biClrUsed;i++){
517                                 ppal[i].rgbBlue =(BYTE)(255-ppal[i].rgbBlue);
518                                 ppal[i].rgbGreen =(BYTE)(255-ppal[i].rgbGreen);
519                                 ppal[i].rgbRed =(BYTE)(255-ppal[i].rgbRed);
520                         }
521                 }
522         } else {
523                 if (pSelection==NULL){ //RGB, full image
524                         BYTE *iSrc=info.pImage;
525                         for(unsigned long i=0; i < head.biSizeImage; i++){
526                                 *iSrc=(BYTE)~(*(iSrc));
527                                 iSrc++;
528                         }
529                 } else { // RGB with selection
530                         RGBQUAD color;
531                         for(long y=info.rSelectionBox.bottom; y<info.rSelectionBox.top; y++){
532                                 for(long x=info.rSelectionBox.left; x<info.rSelectionBox.right; x++){
533 #if CXIMAGE_SUPPORT_SELECTION
534                                         if (BlindSelectionIsInside(x,y))
535 #endif //CXIMAGE_SUPPORT_SELECTION
536                                         {
537                                                 color = BlindGetPixelColor(x,y);
538                                                 color.rgbRed = (BYTE)(255-color.rgbRed);
539                                                 color.rgbGreen = (BYTE)(255-color.rgbGreen);
540                                                 color.rgbBlue = (BYTE)(255-color.rgbBlue);
541                                                 BlindSetPixelColor(x,y,color);
542                                         }
543                                 }
544                         }
545                 }
546                 //<DP> invert transparent color too
547                 info.nBkgndColor.rgbBlue = (BYTE)(255-info.nBkgndColor.rgbBlue);
548                 info.nBkgndColor.rgbGreen = (BYTE)(255-info.nBkgndColor.rgbGreen);
549                 info.nBkgndColor.rgbRed = (BYTE)(255-info.nBkgndColor.rgbRed);
550         }
551         return true;
552 }
553
554 ////////////////////////////////////////////////////////////////////////////////
555 #endif //CXIMAGE_SUPPORT_BASICTRANSFORMATIONS
556 ////////////////////////////////////////////////////////////////////////////////
557 #if CXIMAGE_SUPPORT_TRANSFORMATION
558 ////////////////////////////////////////////////////////////////////////////////
559
560 ////////////////////////////////////////////////////////////////////////////////
561 bool CxImage::Rotate(float angle, CxImage* iDst)
562 {
563         if (!pDib) return false;
564
565         //  Copyright (c) 1996-1998 Ulrich von Zadow
566
567         // Negative the angle, because the y-axis is negative.
568         double ang = -angle*acos((float)0)/90;
569         int newWidth, newHeight;
570         int nWidth = GetWidth();
571         int nHeight= GetHeight();
572         double cos_angle = cos(ang);
573         double sin_angle = sin(ang);
574
575         // Calculate the size of the new bitmap
576         POINT p1={0,0};
577         POINT p2={nWidth,0};
578         POINT p3={0,nHeight};
579         POINT p4={nWidth,nHeight};
580         CxPoint2 newP1,newP2,newP3,newP4, leftTop, rightTop, leftBottom, rightBottom;
581
582         newP1.x = (float)p1.x;
583         newP1.y = (float)p1.y;
584         newP2.x = (float)(p2.x*cos_angle - p2.y*sin_angle);
585         newP2.y = (float)(p2.x*sin_angle + p2.y*cos_angle);
586         newP3.x = (float)(p3.x*cos_angle - p3.y*sin_angle);
587         newP3.y = (float)(p3.x*sin_angle + p3.y*cos_angle);
588         newP4.x = (float)(p4.x*cos_angle - p4.y*sin_angle);
589         newP4.y = (float)(p4.x*sin_angle + p4.y*cos_angle);
590
591         leftTop.x = min(min(newP1.x,newP2.x),min(newP3.x,newP4.x));
592         leftTop.y = min(min(newP1.y,newP2.y),min(newP3.y,newP4.y));
593         rightBottom.x = max(max(newP1.x,newP2.x),max(newP3.x,newP4.x));
594         rightBottom.y = max(max(newP1.y,newP2.y),max(newP3.y,newP4.y));
595         leftBottom.x = leftTop.x;
596         leftBottom.y = rightBottom.y;
597         rightTop.x = rightBottom.x;
598         rightTop.y = leftTop.y;
599
600         newWidth = (int) floor(0.5f + rightTop.x - leftTop.x);
601         newHeight= (int) floor(0.5f + leftBottom.y - leftTop.y);
602         CxImage imgDest;
603         imgDest.CopyInfo(*this);
604         imgDest.Create(newWidth,newHeight,GetBpp(),GetType());
605         imgDest.SetPalette(GetPalette());
606
607 #if CXIMAGE_SUPPORT_ALPHA
608         if(AlphaIsValid())      //MTA: Fix for rotation problem when the image has an alpha channel
609         {
610                 imgDest.AlphaCreate();
611                 imgDest.AlphaClear();
612         }
613 #endif //CXIMAGE_SUPPORT_ALPHA
614
615         int x,y,newX,newY,oldX,oldY;
616
617         if (head.biClrUsed==0){ //RGB
618                 for (y = (int)leftTop.y, newY = 0; y<=(int)leftBottom.y; y++,newY++){
619                         info.nProgress = (long)(100*newY/newHeight);
620                         if (info.nEscape) break;
621                         for (x = (int)leftTop.x, newX = 0; x<=(int)rightTop.x; x++,newX++){
622                                 oldX = (long)(x*cos_angle + y*sin_angle + 0.5);
623                                 oldY = (long)(y*cos_angle - x*sin_angle + 0.5);
624                                 imgDest.SetPixelColor(newX,newY,GetPixelColor(oldX,oldY));
625 #if CXIMAGE_SUPPORT_ALPHA
626                                 imgDest.AlphaSet(newX,newY,AlphaGet(oldX,oldY));                                //MTA: copy the alpha value
627 #endif //CXIMAGE_SUPPORT_ALPHA
628                         }
629                 }
630         } else { //PALETTE
631                 for (y = (int)leftTop.y, newY = 0; y<=(int)leftBottom.y; y++,newY++){
632                         info.nProgress = (long)(100*newY/newHeight);
633                         if (info.nEscape) break;
634                         for (x = (int)leftTop.x, newX = 0; x<=(int)rightTop.x; x++,newX++){
635                                 oldX = (long)(x*cos_angle + y*sin_angle + 0.5);
636                                 oldY = (long)(y*cos_angle - x*sin_angle + 0.5);
637                                 imgDest.SetPixelIndex(newX,newY,GetPixelIndex(oldX,oldY));
638 #if CXIMAGE_SUPPORT_ALPHA
639                                 imgDest.AlphaSet(newX,newY,AlphaGet(oldX,oldY));                                //MTA: copy the alpha value
640 #endif //CXIMAGE_SUPPORT_ALPHA
641                         }
642                 }
643         }
644         //select the destination
645         if (iDst) iDst->Transfer(imgDest);
646         else Transfer(imgDest);
647
648         return true;
649 }
650 ////////////////////////////////////////////////////////////////////////////////
651 /**
652  * Rotates image around it's center.
653  * Method can use interpolation with paletted images, but does not change pallete, so results vary.
654  * (If you have only four colours in a palette, there's not much room for interpolation.)
655  * 
656  * \param  angle - angle in degrees (positive values rotate clockwise)
657  * \param  *iDst - destination image (if null, this image is changed)
658  * \param  inMethod - interpolation method used
659  *              (IM_NEAREST_NEIGHBOUR produces aliasing (fast), IM_BILINEAR softens picture a bit (slower)
660  *               IM_SHARPBICUBIC is slower and produces some halos...)
661  * \param  ofMethod - overflow method (how to choose colour of pixels that have no source)
662  * \param  replColor - replacement colour to use (OM_COLOR, OM_BACKGROUND with no background colour...)
663  * \param  optimizeRightAngles - call faster methods for 90, 180, and 270 degree rotations. Faster methods
664  *                         are called for angles, where error (in location of corner pixels) is less
665  *                         than 0.25 pixels.
666  * \param  bKeepOriginalSize - rotates the image without resizing.
667  *
668  * \author ***bd*** 2.2004
669  */
670 bool CxImage::Rotate2(float angle, 
671                        CxImage *iDst, 
672                        InterpolationMethod inMethod, 
673                        OverflowMethod ofMethod, 
674                        RGBQUAD *replColor,
675                        bool const optimizeRightAngles,
676                                            bool const bKeepOriginalSize)
677 {
678         if (!pDib) return false;                                        //no dib no go
679         
680         double ang = -angle*acos(0.0f)/90.0f;           //convert angle to radians and invert (positive angle performs clockwise rotation)
681         float cos_angle = (float) cos(ang);                     //these two are needed later (to rotate)
682         float sin_angle = (float) sin(ang);
683         
684         //Calculate the size of the new bitmap (rotate corners of image)
685         CxPoint2 p[4];                                                          //original corners of the image
686         p[0]=CxPoint2(-0.5f,-0.5f);
687         p[1]=CxPoint2(GetWidth()-0.5f,-0.5f);
688         p[2]=CxPoint2(-0.5f,GetHeight()-0.5f);
689         p[3]=CxPoint2(GetWidth()-0.5f,GetHeight()-0.5f);
690         CxPoint2 newp[4];                                                               //rotated positions of corners
691         //(rotate corners)
692         if (bKeepOriginalSize){
693                 for (int i=0; i<4; i++) {
694                         newp[i].x = p[i].x;
695                         newp[i].y = p[i].y;
696                 }//for
697         } else {
698                 for (int i=0; i<4; i++) {
699                         newp[i].x = (p[i].x*cos_angle - p[i].y*sin_angle);
700                         newp[i].y = (p[i].x*sin_angle + p[i].y*cos_angle);
701                 }//for i
702                 
703                 if (optimizeRightAngles) { 
704                         //For rotations of 90, -90 or 180 or 0 degrees, call faster routines
705                         if (newp[3].Distance(CxPoint2(GetHeight()-0.5f, 0.5f-GetWidth())) < 0.25) 
706                                 //rotation right for circa 90 degrees (diagonal pixels less than 0.25 pixel away from 90 degree rotation destination)
707                                 return RotateRight(iDst);
708                         if (newp[3].Distance(CxPoint2(0.5f-GetHeight(), -0.5f+GetWidth())) < 0.25) 
709                                 //rotation left for ~90 degrees
710                                 return RotateLeft(iDst);
711                         if (newp[3].Distance(CxPoint2(0.5f-GetWidth(), 0.5f-GetHeight())) < 0.25) 
712                                 //rotation left for ~180 degrees
713                                 return Rotate180(iDst);
714                         if (newp[3].Distance(p[3]) < 0.25) {
715                                 //rotation not significant
716                                 if (iDst) iDst->Copy(*this);            //copy image to iDst, if required
717                                 return true;                                            //and we're done
718                         }//if
719                 }//if
720         }//if
721
722         //(read new dimensions from location of corners)
723         float minx = (float) min(min(newp[0].x,newp[1].x),min(newp[2].x,newp[3].x));
724         float miny = (float) min(min(newp[0].y,newp[1].y),min(newp[2].y,newp[3].y));
725         float maxx = (float) max(max(newp[0].x,newp[1].x),max(newp[2].x,newp[3].x));
726         float maxy = (float) max(max(newp[0].y,newp[1].y),max(newp[2].y,newp[3].y));
727         int newWidth = (int) floor(maxx-minx+0.5f);
728         int newHeight= (int) floor(maxy-miny+0.5f);
729         float ssx=((maxx+minx)- ((float) newWidth-1))/2.0f;   //start for x
730         float ssy=((maxy+miny)- ((float) newHeight-1))/2.0f;  //start for y
731
732         float newxcenteroffset = 0.5f * newWidth;
733         float newycenteroffset = 0.5f * newHeight;
734         if (bKeepOriginalSize){
735                 ssx -= 0.5f * GetWidth();
736                 ssy -= 0.5f * GetHeight();
737         }
738
739         //create destination image
740         CxImage imgDest;
741         imgDest.CopyInfo(*this);
742         imgDest.Create(newWidth,newHeight,GetBpp(),GetType());
743         imgDest.SetPalette(GetPalette());
744 #if CXIMAGE_SUPPORT_ALPHA
745         if(AlphaIsValid()) imgDest.AlphaCreate(); //MTA: Fix for rotation problem when the image has an alpha channel
746 #endif //CXIMAGE_SUPPORT_ALPHA
747         
748         RGBQUAD rgb;                    //pixel colour
749         RGBQUAD rc;
750         if (replColor!=0) 
751                 rc=*replColor; 
752         else {
753                 rc.rgbRed=255; rc.rgbGreen=255; rc.rgbBlue=255; rc.rgbReserved=0;
754         }//if
755         float x,y;              //destination location (float, with proper offset)
756         float origx, origy;     //origin location
757         int destx, desty;       //destination location
758         
759         y=ssy;                  //initialize y
760         if (!IsIndexed()){ //RGB24
761                 //optimized RGB24 implementation (direct write to destination):
762                 BYTE *pxptr;
763 #if CXIMAGE_SUPPORT_ALPHA
764                 BYTE *pxptra=0;
765 #endif //CXIMAGE_SUPPORT_ALPHA
766                 for (desty=0; desty<newHeight; desty++) {
767                         info.nProgress = (long)(100*desty/newHeight);
768                         if (info.nEscape) break;
769                         //initialize x
770                         x=ssx;
771                         //calculate pointer to first byte in row
772                         pxptr=(BYTE *)imgDest.BlindGetPixelPointer(0, desty);
773 #if CXIMAGE_SUPPORT_ALPHA
774                         //calculate pointer to first byte in row
775                         if (AlphaIsValid()) pxptra=imgDest.AlphaGetPointer(0, desty);
776 #endif //CXIMAGE_SUPPORT_ALPHA
777                         for (destx=0; destx<newWidth; destx++) {
778                                 //get source pixel coordinate for current destination point
779                                 //origx = (cos_angle*(x-head.biWidth/2)+sin_angle*(y-head.biHeight/2))+newWidth/2;
780                                 //origy = (cos_angle*(y-head.biHeight/2)-sin_angle*(x-head.biWidth/2))+newHeight/2;
781                                 origx = cos_angle*x+sin_angle*y;
782                                 origy = cos_angle*y-sin_angle*x;
783                                 if (bKeepOriginalSize){
784                                         origx += newxcenteroffset;
785                                         origy += newycenteroffset;
786                                 }
787                                 rgb = GetPixelColorInterpolated(origx, origy, inMethod, ofMethod, &rc);   //get interpolated colour value
788                                 //copy alpha and colour value to destination
789 #if CXIMAGE_SUPPORT_ALPHA
790                                 if (pxptra) *pxptra++ = rgb.rgbReserved;
791 #endif //CXIMAGE_SUPPORT_ALPHA
792                                 *pxptr++ = rgb.rgbBlue;
793                                 *pxptr++ = rgb.rgbGreen;
794                                 *pxptr++ = rgb.rgbRed;
795                                 x++;
796                         }//for destx
797                         y++;
798                 }//for desty
799         } else { 
800                 //non-optimized implementation for paletted images
801                 for (desty=0; desty<newHeight; desty++) {
802                         info.nProgress = (long)(100*desty/newHeight);
803                         if (info.nEscape) break;
804                         x=ssx;
805                         for (destx=0; destx<newWidth; destx++) {
806                                 //get source pixel coordinate for current destination point
807                                 origx=(cos_angle*x+sin_angle*y);
808                                 origy=(cos_angle*y-sin_angle*x);
809                                 if (bKeepOriginalSize){
810                                         origx += newxcenteroffset;
811                                         origy += newycenteroffset;
812                                 }
813                                 rgb = GetPixelColorInterpolated(origx, origy, inMethod, ofMethod, &rc);
814                                 //***!*** SetPixelColor is slow for palleted images
815 #if CXIMAGE_SUPPORT_ALPHA
816                                 if (AlphaIsValid()) 
817                                         imgDest.SetPixelColor(destx,desty,rgb,true);
818                                 else 
819 #endif //CXIMAGE_SUPPORT_ALPHA     
820                                         imgDest.SetPixelColor(destx,desty,rgb,false);
821                                 x++;
822                         }//for destx
823                         y++;
824                 }//for desty
825         }
826         //select the destination
827         
828         if (iDst) iDst->Transfer(imgDest);
829         else Transfer(imgDest);
830         
831         return true;
832 }
833 ////////////////////////////////////////////////////////////////////////////////
834 bool CxImage::Rotate180(CxImage* iDst)
835 {
836         if (!pDib) return false;
837
838         long wid = GetWidth();
839         long ht = GetHeight();
840
841         CxImage imgDest;
842         imgDest.CopyInfo(*this);
843         imgDest.Create(wid,ht,GetBpp(),GetType());
844         imgDest.SetPalette(GetPalette());
845
846 #if CXIMAGE_SUPPORT_ALPHA
847         if (AlphaIsValid())     imgDest.AlphaCreate();
848 #endif //CXIMAGE_SUPPORT_ALPHA
849
850         long x,y,y2;
851         for (y = 0; y < ht; y++){
852                 info.nProgress = (long)(100*y/ht); //<Anatoly Ivasyuk>
853                 y2=ht-y-1;
854                 for (x = 0; x < wid; x++){
855                         if(head.biClrUsed==0)//RGB
856                                 imgDest.SetPixelColor(wid-x-1, y2, BlindGetPixelColor(x, y));
857                         else  //PALETTE
858                                 imgDest.SetPixelIndex(wid-x-1, y2, BlindGetPixelIndex(x, y));
859
860 #if CXIMAGE_SUPPORT_ALPHA
861                         if (AlphaIsValid())     imgDest.AlphaSet(wid-x-1, y2,BlindAlphaGet(x, y));
862 #endif //CXIMAGE_SUPPORT_ALPHA
863
864                 }
865         }
866
867         //select the destination
868         if (iDst) iDst->Transfer(imgDest);
869         else Transfer(imgDest);
870         return true;
871 }
872
873 ////////////////////////////////////////////////////////////////////////////////
874 /**
875  * Resizes the image. mode can be 0 for slow (bilinear) method ,
876  * 1 for fast (nearest pixel) method, or 2 for accurate (bicubic spline interpolation) method.
877  * The function is faster with 24 and 1 bpp images, slow for 4 bpp images and slowest for 8 bpp images.
878  */
879 bool CxImage::Resample(long newx, long newy, int mode, CxImage* iDst)
880 {
881         if (newx==0 || newy==0) return false;
882
883         if (head.biWidth==newx && head.biHeight==newy){
884                 if (iDst) iDst->Copy(*this);
885                 return true;
886         }
887
888         float xScale, yScale, fX, fY;
889         xScale = (float)head.biWidth  / (float)newx;
890         yScale = (float)head.biHeight / (float)newy;
891
892         CxImage newImage;
893         newImage.CopyInfo(*this);
894         newImage.Create(newx,newy,head.biBitCount,GetType());
895         newImage.SetPalette(GetPalette());
896         if (!newImage.IsValid()){
897                 strcpy(info.szLastError,newImage.GetLastError());
898                 return false;
899         }
900
901         switch (mode) {
902         case 1: // nearest pixel
903         { 
904                 for(long y=0; y<newy; y++){
905                         info.nProgress = (long)(100*y/newy);
906                         if (info.nEscape) break;
907                         fY = y * yScale;
908                         for(long x=0; x<newx; x++){
909                                 fX = x * xScale;
910                                 newImage.SetPixelColor(x,y,GetPixelColor((long)fX,(long)fY));
911                         }
912                 }
913                 break;
914         }
915         case 2: // bicubic interpolation by Blake L. Carlson <blake-carlson(at)uiowa(dot)edu
916         {
917                 float f_x, f_y, a, b, rr, gg, bb, r1, r2;
918                 int   i_x, i_y, xx, yy;
919                 RGBQUAD rgb;
920                 BYTE* iDst;
921                 for(long y=0; y<newy; y++){
922                         info.nProgress = (long)(100*y/newy);
923                         if (info.nEscape) break;
924                         f_y = (float) y * yScale - 0.5f;
925                         i_y = (int) floor(f_y);
926                         a   = f_y - (float)floor(f_y);
927                         for(long x=0; x<newx; x++){
928                                 f_x = (float) x * xScale - 0.5f;
929                                 i_x = (int) floor(f_x);
930                                 b   = f_x - (float)floor(f_x);
931
932                                 rr = gg = bb = 0.0f;
933                                 for(int m=-1; m<3; m++) {
934                                         r1 = KernelBSpline((float) m - a);
935                                         yy = i_y+m;
936                                         if (yy<0) yy=0;
937                                         if (yy>=head.biHeight) yy = head.biHeight-1;
938                                         for(int n=-1; n<3; n++) {
939                                                 r2 = r1 * KernelBSpline(b - (float)n);
940                                                 xx = i_x+n;
941                                                 if (xx<0) xx=0;
942                                                 if (xx>=head.biWidth) xx=head.biWidth-1;
943
944                                                 if (head.biClrUsed){
945                                                         rgb = GetPixelColor(xx,yy);
946                                                 } else {
947                                                         iDst  = info.pImage + yy*info.dwEffWidth + xx*3;
948                                                         rgb.rgbBlue = *iDst++;
949                                                         rgb.rgbGreen= *iDst++;
950                                                         rgb.rgbRed  = *iDst;
951                                                 }
952
953                                                 rr += rgb.rgbRed * r2;
954                                                 gg += rgb.rgbGreen * r2;
955                                                 bb += rgb.rgbBlue * r2;
956                                         }
957                                 }
958
959                                 if (head.biClrUsed)
960                                         newImage.SetPixelColor(x,y,RGB(rr,gg,bb));
961                                 else {
962                                         iDst = newImage.info.pImage + y*newImage.info.dwEffWidth + x*3;
963                                         *iDst++ = (BYTE)bb;
964                                         *iDst++ = (BYTE)gg;
965                                         *iDst   = (BYTE)rr;
966                                 }
967
968                         }
969                 }
970                 break;
971         }
972         default: // bilinear interpolation
973                 if (!(head.biWidth>newx && head.biHeight>newy && head.biBitCount==24)) {
974                         // (c) 1999 Steve McMahon (steve@dogma.demon.co.uk)
975                         long ifX, ifY, ifX1, ifY1, xmax, ymax;
976                         float ir1, ir2, ig1, ig2, ib1, ib2, dx, dy;
977                         BYTE r,g,b;
978                         RGBQUAD rgb1, rgb2, rgb3, rgb4;
979                         xmax = head.biWidth-1;
980                         ymax = head.biHeight-1;
981                         for(long y=0; y<newy; y++){
982                                 info.nProgress = (long)(100*y/newy);
983                                 if (info.nEscape) break;
984                                 fY = y * yScale;
985                                 ifY = (int)fY;
986                                 ifY1 = min(ymax, ifY+1);
987                                 dy = fY - ifY;
988                                 for(long x=0; x<newx; x++){
989                                         fX = x * xScale;
990                                         ifX = (int)fX;
991                                         ifX1 = min(xmax, ifX+1);
992                                         dx = fX - ifX;
993                                         // Interpolate using the four nearest pixels in the source
994                                         if (head.biClrUsed){
995                                                 rgb1=GetPaletteColor(GetPixelIndex(ifX,ifY));
996                                                 rgb2=GetPaletteColor(GetPixelIndex(ifX1,ifY));
997                                                 rgb3=GetPaletteColor(GetPixelIndex(ifX,ifY1));
998                                                 rgb4=GetPaletteColor(GetPixelIndex(ifX1,ifY1));
999                                         }
1000                                         else {
1001                                                 BYTE* iDst;
1002                                                 iDst = info.pImage + ifY*info.dwEffWidth + ifX*3;
1003                                                 rgb1.rgbBlue = *iDst++; rgb1.rgbGreen= *iDst++; rgb1.rgbRed =*iDst;
1004                                                 iDst = info.pImage + ifY*info.dwEffWidth + ifX1*3;
1005                                                 rgb2.rgbBlue = *iDst++; rgb2.rgbGreen= *iDst++; rgb2.rgbRed =*iDst;
1006                                                 iDst = info.pImage + ifY1*info.dwEffWidth + ifX*3;
1007                                                 rgb3.rgbBlue = *iDst++; rgb3.rgbGreen= *iDst++; rgb3.rgbRed =*iDst;
1008                                                 iDst = info.pImage + ifY1*info.dwEffWidth + ifX1*3;
1009                                                 rgb4.rgbBlue = *iDst++; rgb4.rgbGreen= *iDst++; rgb4.rgbRed =*iDst;
1010                                         }
1011                                         // Interplate in x direction:
1012                                         ir1 = rgb1.rgbRed   + (rgb3.rgbRed   - rgb1.rgbRed)   * dy;
1013                                         ig1 = rgb1.rgbGreen + (rgb3.rgbGreen - rgb1.rgbGreen) * dy;
1014                                         ib1 = rgb1.rgbBlue  + (rgb3.rgbBlue  - rgb1.rgbBlue)  * dy;
1015                                         ir2 = rgb2.rgbRed   + (rgb4.rgbRed   - rgb2.rgbRed)   * dy;
1016                                         ig2 = rgb2.rgbGreen + (rgb4.rgbGreen - rgb2.rgbGreen) * dy;
1017                                         ib2 = rgb2.rgbBlue  + (rgb4.rgbBlue  - rgb2.rgbBlue)  * dy;
1018                                         // Interpolate in y:
1019                                         r = (BYTE)(ir1 + (ir2-ir1) * dx);
1020                                         g = (BYTE)(ig1 + (ig2-ig1) * dx);
1021                                         b = (BYTE)(ib1 + (ib2-ib1) * dx);
1022                                         // Set output
1023                                         newImage.SetPixelColor(x,y,RGB(r,g,b));
1024                                 }
1025                         } 
1026                 } else {
1027                         //high resolution shrink, thanks to Henrik Stellmann <henrik.stellmann@volleynet.de>
1028                         const long ACCURACY = 1000;
1029                         long i,j; // index for faValue
1030                         long x,y; // coordinates in  source image
1031                         BYTE* pSource;
1032                         BYTE* pDest = newImage.info.pImage;
1033                         long* naAccu  = new long[3 * newx + 3];
1034                         long* naCarry = new long[3 * newx + 3];
1035                         long* naTemp;
1036                         long  nWeightX,nWeightY;
1037                         float fEndX;
1038                         long nScale = (long)(ACCURACY * xScale * yScale);
1039
1040                         memset(naAccu,  0, sizeof(long) * 3 * newx);
1041                         memset(naCarry, 0, sizeof(long) * 3 * newx);
1042
1043                         int u, v = 0; // coordinates in dest image
1044                         float fEndY = yScale - 1.0f;
1045                         for (y = 0; y < head.biHeight; y++){
1046                                 info.nProgress = (long)(100*y/head.biHeight); //<Anatoly Ivasyuk>
1047                                 if (info.nEscape) break;
1048                                 pSource = info.pImage + y * info.dwEffWidth;
1049                                 u = i = 0;
1050                                 fEndX = xScale - 1.0f;
1051                                 if ((float)y < fEndY) {       // complete source row goes into dest row
1052                                         for (x = 0; x < head.biWidth; x++){
1053                                                 if ((float)x < fEndX){       // complete source pixel goes into dest pixel
1054                                                         for (j = 0; j < 3; j++) naAccu[i + j] += (*pSource++) * ACCURACY;
1055                                                 } else {       // source pixel is splitted for 2 dest pixels
1056                                                         nWeightX = (long)(((float)x - fEndX) * ACCURACY);
1057                                                         for (j = 0; j < 3; j++){
1058                                                                 naAccu[i] += (ACCURACY - nWeightX) * (*pSource);
1059                                                                 naAccu[3 + i++] += nWeightX * (*pSource++);
1060                                                         }
1061                                                         fEndX += xScale;
1062                                                         u++;
1063                                                 }
1064                                         }
1065                                 } else {       // source row is splitted for 2 dest rows       
1066                                         nWeightY = (long)(((float)y - fEndY) * ACCURACY);
1067                                         for (x = 0; x < head.biWidth; x++){
1068                                                 if ((float)x < fEndX){       // complete source pixel goes into 2 pixel
1069                                                         for (j = 0; j < 3; j++){
1070                                                                 naAccu[i + j] += ((ACCURACY - nWeightY) * (*pSource));
1071                                                                 naCarry[i + j] += nWeightY * (*pSource++);
1072                                                         }
1073                                                 } else {       // source pixel is splitted for 4 dest pixels
1074                                                         nWeightX = (int)(((float)x - fEndX) * ACCURACY);
1075                                                         for (j = 0; j < 3; j++) {
1076                                                                 naAccu[i] += ((ACCURACY - nWeightY) * (ACCURACY - nWeightX)) * (*pSource) / ACCURACY;
1077                                                                 *pDest++ = (BYTE)(naAccu[i] / nScale);
1078                                                                 naCarry[i] += (nWeightY * (ACCURACY - nWeightX) * (*pSource)) / ACCURACY;
1079                                                                 naAccu[i + 3] += ((ACCURACY - nWeightY) * nWeightX * (*pSource)) / ACCURACY;
1080                                                                 naCarry[i + 3] = (nWeightY * nWeightX * (*pSource)) / ACCURACY;
1081                                                                 i++;
1082                                                                 pSource++;
1083                                                         }
1084                                                         fEndX += xScale;
1085                                                         u++;
1086                                                 }
1087                                         }
1088                                         if (u < newx){ // possibly not completed due to rounding errors
1089                                                 for (j = 0; j < 3; j++) *pDest++ = (BYTE)(naAccu[i++] / nScale);
1090                                         }
1091                                         naTemp = naCarry;
1092                                         naCarry = naAccu;
1093                                         naAccu = naTemp;
1094                                         memset(naCarry, 0, sizeof(int) * 3);    // need only to set first pixel zero
1095                                         pDest = newImage.info.pImage + (++v * newImage.info.dwEffWidth);
1096                                         fEndY += yScale;
1097                                 }
1098                         }
1099                         if (v < newy){  // possibly not completed due to rounding errors
1100                                 for (i = 0; i < 3 * newx; i++) *pDest++ = (BYTE)(naAccu[i] / nScale);
1101                         }
1102                         delete [] naAccu;
1103                         delete [] naCarry;
1104                 }
1105         }
1106
1107 #if CXIMAGE_SUPPORT_ALPHA
1108         if (AlphaIsValid()){
1109                 newImage.AlphaCreate();
1110                 for(long y=0; y<newy; y++){
1111                         fY = y * yScale;
1112                         for(long x=0; x<newx; x++){
1113                                 fX = x * xScale;
1114                                 newImage.AlphaSet(x,y,AlphaGet((long)fX,(long)fY));
1115                         }
1116                 }
1117         }
1118 #endif //CXIMAGE_SUPPORT_ALPHA
1119
1120         //select the destination
1121         if (iDst) iDst->Transfer(newImage);
1122         else Transfer(newImage);
1123
1124         return true;
1125 }
1126 ////////////////////////////////////////////////////////////////////////////////
1127 /**
1128  * New simpler resample. Adds new interpolation methods and simplifies code (using GetPixelColorInterpolated
1129  * and GetAreaColorInterpolated). It also (unlike old method) interpolates alpha layer. 
1130  *
1131  * \param  newx, newy - size of resampled image
1132  * \param  inMethod - interpolation method to use (see comments at GetPixelColorInterpolated)
1133  *              If image size is being reduced, averaging is used instead (or simultaneously with) inMethod.
1134  * \param  ofMethod - what to replace outside pixels by (only significant for bordering pixels of enlarged image)
1135  * \param  iDst - pointer to destination CxImage or NULL.
1136  * \param  disableAveraging - force no averaging when shrinking images (Produces aliasing.
1137  *                      You probably just want to leave this off...)
1138  *
1139  * \author ***bd*** 2.2004
1140  */
1141 bool CxImage::Resample2(
1142   long newx, long newy, 
1143   InterpolationMethod const inMethod, 
1144   OverflowMethod const ofMethod, 
1145   CxImage* const iDst,
1146   bool const disableAveraging)
1147 {
1148         if (newx<=0 || newy<=0 || !pDib) return false;
1149         
1150         if (head.biWidth==newx && head.biHeight==newy) {
1151                 //image already correct size (just copy and return)
1152                 if (iDst) iDst->Copy(*this);
1153                 return true;
1154         }//if
1155         
1156         //calculate scale of new image (less than 1 for enlarge)
1157         float xScale, yScale;
1158         xScale = (float)head.biWidth  / (float)newx;    
1159         yScale = (float)head.biHeight / (float)newy;
1160         
1161         //create temporary destination image
1162         CxImage newImage;
1163         newImage.CopyInfo(*this);
1164         newImage.Create(newx,newy,head.biBitCount,GetType());
1165         newImage.SetPalette(GetPalette());
1166         if (!newImage.IsValid()){
1167                 strcpy(info.szLastError,newImage.GetLastError());
1168                 return false;
1169         }
1170         
1171         //and alpha channel if required
1172 #if CXIMAGE_SUPPORT_ALPHA
1173         if (AlphaIsValid()) newImage.AlphaCreate();
1174         BYTE *pxptra = 0;       // destination alpha data
1175 #endif
1176         
1177         float sX, sY;         //source location
1178         long dX,dY;           //destination pixel (int value)
1179         if ((xScale<=1 && yScale<=1) || disableAveraging) {
1180                 //image is being enlarged (or interpolation on demand)
1181                 if (!IsIndexed()) {
1182                         //RGB24 image (optimized version with direct writes)
1183                         RGBQUAD q;              //pixel colour
1184                         BYTE *pxptr;            //pointer to destination pixel
1185                         for(dY=0; dY<newy; dY++){
1186                                 info.nProgress = (long)(100*dY/newy);
1187                                 if (info.nEscape) break;
1188                                 sY = (dY + 0.5f) * yScale - 0.5f;
1189                                 pxptr=(BYTE*)(newImage.BlindGetPixelPointer(0,dY));
1190 #if CXIMAGE_SUPPORT_ALPHA
1191                                 pxptra=newImage.AlphaGetPointer(0,dY);
1192 #endif
1193                                 for(dX=0; dX<newx; dX++){
1194                                         sX = (dX + 0.5f) * xScale - 0.5f;
1195                                         q=GetPixelColorInterpolated(sX,sY,inMethod,ofMethod,0);
1196                                         *pxptr++=q.rgbBlue;
1197                                         *pxptr++=q.rgbGreen;
1198                                         *pxptr++=q.rgbRed;
1199 #if CXIMAGE_SUPPORT_ALPHA
1200                                         if (pxptra) *pxptra++=q.rgbReserved;
1201 #endif
1202                                 }//for dX
1203                         }//for dY
1204                 } else {
1205                         //enlarge paletted image. Slower method.
1206                         for(dY=0; dY<newy; dY++){
1207                                 info.nProgress = (long)(100*dY/newy);
1208                                 if (info.nEscape) break;
1209                                 sY = (dY + 0.5f) * yScale - 0.5f;
1210                                 for(dX=0; dX<newx; dX++){
1211                                         sX = (dX + 0.5f) * xScale - 0.5f;
1212                                         newImage.SetPixelColor(dX,dY,GetPixelColorInterpolated(sX,sY,inMethod,ofMethod,0),true);
1213                                 }//for x
1214                         }//for y
1215                 }//if
1216         } else {
1217                 //image size is being reduced (averaging enabled)
1218                 for(dY=0; dY<newy; dY++){
1219                         info.nProgress = (long)(100*dY/newy); if (info.nEscape) break;
1220                         sY = (dY+0.5f) * yScale - 0.5f;
1221                         for(dX=0; dX<newx; dX++){
1222                                 sX = (dX+0.5f) * xScale - 0.5f;
1223                                 newImage.SetPixelColor(dX,dY,GetAreaColorInterpolated(sX, sY, xScale, yScale, inMethod, ofMethod,0),true);
1224                         }//for x
1225                 }//for y
1226         }//if
1227
1228 #if CXIMAGE_SUPPORT_ALPHA
1229         if (AlphaIsValid() && pxptra == 0){
1230                 for(long y=0; y<newy; y++){
1231                         dY = (long)(y * yScale);
1232                         for(long x=0; x<newx; x++){
1233                                 dX = (long)(x * xScale);
1234                                 newImage.AlphaSet(x,y,AlphaGet(dX,dY));
1235                         }
1236                 }
1237         }
1238 #endif //CXIMAGE_SUPPORT_ALPHA
1239
1240         //copy new image to the destination
1241         if (iDst) 
1242                 iDst->Transfer(newImage);
1243         else 
1244                 Transfer(newImage);
1245         return true;
1246 }
1247 ////////////////////////////////////////////////////////////////////////////////
1248 /**
1249  * Reduces the number of bits per pixel to nbit (1, 4 or 8).
1250  * ppal points to a valid palette for the final image; if not supplied the function will use a standard palette.
1251  * ppal is not necessary for reduction to 1 bpp.
1252  */
1253 bool CxImage::DecreaseBpp(DWORD nbit, bool errordiffusion, RGBQUAD* ppal, DWORD clrimportant)
1254 {
1255         if (!pDib) return false;
1256         if (head.biBitCount <  nbit){
1257                 strcpy(info.szLastError,"DecreaseBpp: target BPP greater than source BPP");
1258                 return false;
1259         }
1260         if (head.biBitCount == nbit){
1261                 if (clrimportant==0) return true;
1262                 if (head.biClrImportant && (head.biClrImportant<clrimportant)) return true;
1263         }
1264
1265         long er,eg,eb;
1266         RGBQUAD c,ce;
1267
1268         CxImage tmp;
1269         tmp.CopyInfo(*this);
1270         tmp.Create(head.biWidth,head.biHeight,(WORD)nbit,info.dwType);
1271         if (clrimportant) tmp.SetClrImportant(clrimportant);
1272         if (!tmp.IsValid()){
1273                 strcpy(info.szLastError,tmp.GetLastError());
1274                 return false;
1275         }
1276
1277 #if CXIMAGE_SUPPORT_SELECTION
1278         tmp.SelectionCopy(*this);
1279 #endif //CXIMAGE_SUPPORT_SELECTION
1280
1281 #if CXIMAGE_SUPPORT_ALPHA
1282         tmp.AlphaCopy(*this);
1283 #endif //CXIMAGE_SUPPORT_ALPHA
1284
1285         if (ppal) {
1286                 if (clrimportant) {
1287                         tmp.SetPalette(ppal,clrimportant);
1288                 } else {
1289                         tmp.SetPalette(ppal,1<<tmp.head.biBitCount);
1290                 }
1291         } else {
1292                 tmp.SetStdPalette();
1293         }
1294
1295         for (long y=0;y<head.biHeight;y++){
1296                 if (info.nEscape) break;
1297                 info.nProgress = (long)(100*y/head.biHeight);
1298                 for (long x=0;x<head.biWidth;x++){
1299                         if (!errordiffusion){
1300                                 tmp.BlindSetPixelColor(x,y,BlindGetPixelColor(x,y));
1301                         } else {
1302                                 c = BlindGetPixelColor(x,y);
1303                                 tmp.BlindSetPixelColor(x,y,c);
1304
1305                                 ce = tmp.BlindGetPixelColor(x,y);
1306                                 er=(long)c.rgbRed - (long)ce.rgbRed;
1307                                 eg=(long)c.rgbGreen - (long)ce.rgbGreen;
1308                                 eb=(long)c.rgbBlue - (long)ce.rgbBlue;
1309
1310                                 c = GetPixelColor(x+1,y);
1311                                 c.rgbRed = (BYTE)min(255L,max(0L,(long)c.rgbRed + ((er*7)/16)));
1312                                 c.rgbGreen = (BYTE)min(255L,max(0L,(long)c.rgbGreen + ((eg*7)/16)));
1313                                 c.rgbBlue = (BYTE)min(255L,max(0L,(long)c.rgbBlue + ((eb*7)/16)));
1314                                 SetPixelColor(x+1,y,c);
1315                                 int coeff=1;
1316                                 for(int i=-1; i<2; i++){
1317                                         switch(i){
1318                                         case -1:
1319                                                 coeff=2; break;
1320                                         case 0:
1321                                                 coeff=4; break;
1322                                         case 1:
1323                                                 coeff=1; break;
1324                                         }
1325                                         c = GetPixelColor(x+i,y+1);
1326                                         c.rgbRed = (BYTE)min(255L,max(0L,(long)c.rgbRed + ((er * coeff)/16)));
1327                                         c.rgbGreen = (BYTE)min(255L,max(0L,(long)c.rgbGreen + ((eg * coeff)/16)));
1328                                         c.rgbBlue = (BYTE)min(255L,max(0L,(long)c.rgbBlue + ((eb * coeff)/16)));
1329                                         SetPixelColor(x+i,y+1,c);
1330                                 }
1331                         }
1332                 }
1333         }
1334
1335         Transfer(tmp);
1336         return true;
1337 }
1338 ////////////////////////////////////////////////////////////////////////////////
1339 /**
1340  * Increases the number of bits per pixel of the image.
1341  * \param nbit: 4, 8, 24
1342  */
1343 bool CxImage::IncreaseBpp(DWORD nbit)
1344 {
1345         if (!pDib) return false;
1346         switch (nbit){
1347         case 4:
1348                 {
1349                         if (head.biBitCount==4) return true;
1350                         if (head.biBitCount>4) return false;
1351
1352                         CxImage tmp;
1353                         tmp.CopyInfo(*this);
1354                         tmp.Create(head.biWidth,head.biHeight,4,info.dwType);
1355                         tmp.SetPalette(GetPalette(),GetNumColors());
1356                         if (!tmp.IsValid()){
1357                                 strcpy(info.szLastError,tmp.GetLastError());
1358                                 return false;
1359                         }
1360
1361
1362 #if CXIMAGE_SUPPORT_SELECTION
1363                         tmp.SelectionCopy(*this);
1364 #endif //CXIMAGE_SUPPORT_SELECTION
1365
1366 #if CXIMAGE_SUPPORT_ALPHA
1367                         tmp.AlphaCopy(*this);
1368 #endif //CXIMAGE_SUPPORT_ALPHA
1369
1370                         for (long y=0;y<head.biHeight;y++){
1371                                 if (info.nEscape) break;
1372                                 for (long x=0;x<head.biWidth;x++){
1373                                         tmp.BlindSetPixelIndex(x,y,BlindGetPixelIndex(x,y));
1374                                 }
1375                         }
1376                         Transfer(tmp);
1377                         return true;
1378                 }
1379         case 8:
1380                 {
1381                         if (head.biBitCount==8) return true;
1382                         if (head.biBitCount>8) return false;
1383
1384                         CxImage tmp;
1385                         tmp.CopyInfo(*this);
1386                         tmp.Create(head.biWidth,head.biHeight,8,info.dwType);
1387                         tmp.SetPalette(GetPalette(),GetNumColors());
1388                         if (!tmp.IsValid()){
1389                                 strcpy(info.szLastError,tmp.GetLastError());
1390                                 return false;
1391                         }
1392
1393 #if CXIMAGE_SUPPORT_SELECTION
1394                         tmp.SelectionCopy(*this);
1395 #endif //CXIMAGE_SUPPORT_SELECTION
1396
1397 #if CXIMAGE_SUPPORT_ALPHA
1398                         tmp.AlphaCopy(*this);
1399 #endif //CXIMAGE_SUPPORT_ALPHA
1400
1401                         for (long y=0;y<head.biHeight;y++){
1402                                 if (info.nEscape) break;
1403                                 for (long x=0;x<head.biWidth;x++){
1404                                         tmp.BlindSetPixelIndex(x,y,BlindGetPixelIndex(x,y));
1405                                 }
1406                         }
1407                         Transfer(tmp);
1408                         return true;
1409                 }
1410         case 24:
1411                 {
1412                         if (head.biBitCount==24) return true;
1413                         if (head.biBitCount>24) return false;
1414
1415                         CxImage tmp;
1416                         tmp.CopyInfo(*this);
1417                         tmp.Create(head.biWidth,head.biHeight,24,info.dwType);
1418                         if (!tmp.IsValid()){
1419                                 strcpy(info.szLastError,tmp.GetLastError());
1420                                 return false;
1421                         }
1422
1423                         if (info.nBkgndIndex>=0) //translate transparency
1424                                 tmp.info.nBkgndColor=GetPaletteColor((BYTE)info.nBkgndIndex);
1425
1426 #if CXIMAGE_SUPPORT_SELECTION
1427                         tmp.SelectionCopy(*this);
1428 #endif //CXIMAGE_SUPPORT_SELECTION
1429
1430 #if CXIMAGE_SUPPORT_ALPHA
1431                         tmp.AlphaCopy(*this);
1432                         if (AlphaPaletteIsValid() && !AlphaIsValid()) tmp.AlphaCreate();
1433 #endif //CXIMAGE_SUPPORT_ALPHA
1434
1435                         for (long y=0;y<head.biHeight;y++){
1436                                 if (info.nEscape) break;
1437                                 for (long x=0;x<head.biWidth;x++){
1438                                         tmp.BlindSetPixelColor(x,y,BlindGetPixelColor(x,y),true);
1439                                 }
1440                         }
1441                         Transfer(tmp);
1442                         return true;
1443                 }
1444         }
1445         return false;
1446 }
1447 ////////////////////////////////////////////////////////////////////////////////
1448 /**
1449  * Converts the image to B&W using the desired method :
1450  * - 0 = Floyd-Steinberg
1451  * - 1 = Ordered-Dithering (4x4) 
1452  * - 2 = Burkes
1453  * - 3 = Stucki
1454  * - 4 = Jarvis-Judice-Ninke
1455  * - 5 = Sierra
1456  * - 6 = Stevenson-Arce
1457  * - 7 = Bayer (4x4 ordered dithering) 
1458  */
1459 bool CxImage::Dither(long method)
1460 {
1461         if (!pDib) return false;
1462         if (head.biBitCount == 1) return true;
1463         
1464         GrayScale();
1465
1466         CxImage tmp;
1467         tmp.CopyInfo(*this);
1468         tmp.Create(head.biWidth, head.biHeight, 1, info.dwType);
1469         if (!tmp.IsValid()){
1470                 strcpy(info.szLastError,tmp.GetLastError());
1471                 return false;
1472         }
1473
1474 #if CXIMAGE_SUPPORT_SELECTION
1475         tmp.SelectionCopy(*this);
1476 #endif //CXIMAGE_SUPPORT_SELECTION
1477
1478 #if CXIMAGE_SUPPORT_ALPHA
1479         tmp.AlphaCopy(*this);
1480 #endif //CXIMAGE_SUPPORT_ALPHA
1481
1482         switch (method){
1483         case 1:
1484         {
1485                 // Multi-Level Ordered-Dithering by Kenny Hoff (Oct. 12, 1995)
1486                 #define dth_NumRows 4
1487                 #define dth_NumCols 4
1488                 #define dth_NumIntensityLevels 2
1489                 #define dth_NumRowsLessOne (dth_NumRows-1)
1490                 #define dth_NumColsLessOne (dth_NumCols-1)
1491                 #define dth_RowsXCols (dth_NumRows*dth_NumCols)
1492                 #define dth_MaxIntensityVal 255
1493                 #define dth_MaxDitherIntensityVal (dth_NumRows*dth_NumCols*(dth_NumIntensityLevels-1))
1494
1495                 int DitherMatrix[dth_NumRows][dth_NumCols] = {{0,8,2,10}, {12,4,14,6}, {3,11,1,9}, {15,7,13,5} };
1496                 
1497                 unsigned char Intensity[dth_NumIntensityLevels] = { 0,1 };                       // 2 LEVELS B/W
1498                 //unsigned char Intensity[NumIntensityLevels] = { 0,255 };                       // 2 LEVELS
1499                 //unsigned char Intensity[NumIntensityLevels] = { 0,127,255 };                   // 3 LEVELS
1500                 //unsigned char Intensity[NumIntensityLevels] = { 0,85,170,255 };                // 4 LEVELS
1501                 //unsigned char Intensity[NumIntensityLevels] = { 0,63,127,191,255 };            // 5 LEVELS
1502                 //unsigned char Intensity[NumIntensityLevels] = { 0,51,102,153,204,255 };        // 6 LEVELS
1503                 //unsigned char Intensity[NumIntensityLevels] = { 0,42,85,127,170,213,255 };     // 7 LEVELS
1504                 //unsigned char Intensity[NumIntensityLevels] = { 0,36,73,109,145,182,219,255 }; // 8 LEVELS
1505                 int DitherIntensity, DitherMatrixIntensity, Offset, DeviceIntensity;
1506                 unsigned char DitherValue;
1507   
1508                 for (long y=0;y<head.biHeight;y++){
1509                         info.nProgress = (long)(100*y/head.biHeight);
1510                         if (info.nEscape) break;
1511                         for (long x=0;x<head.biWidth;x++){
1512
1513                                 DeviceIntensity = BlindGetPixelIndex(x,y);
1514                                 DitherIntensity = DeviceIntensity*dth_MaxDitherIntensityVal/dth_MaxIntensityVal;
1515                                 DitherMatrixIntensity = DitherIntensity % dth_RowsXCols;
1516                                 Offset = DitherIntensity / dth_RowsXCols;
1517                                 if (DitherMatrix[y&dth_NumRowsLessOne][x&dth_NumColsLessOne] < DitherMatrixIntensity)
1518                                         DitherValue = Intensity[1+Offset];
1519                                 else
1520                                         DitherValue = Intensity[0+Offset];
1521
1522                                 tmp.BlindSetPixelIndex(x,y,DitherValue);
1523                         }
1524                 }
1525                 break;
1526         }
1527         case 2:
1528         {
1529                 //Burkes error diffusion (Thanks to Franco Gerevini)
1530                 int TotalCoeffSum = 32;
1531                 long error, nlevel, coeff=1;
1532                 BYTE level;
1533
1534                 for (long y = 0; y < head.biHeight; y++) {
1535                         info.nProgress = (long)(100 * y / head.biHeight);
1536                         if (info.nEscape) 
1537                                 break;
1538                         for (long x = 0; x < head.biWidth; x++) {
1539                                 level = BlindGetPixelIndex(x, y);
1540                                 if (level > 128) {
1541                                         tmp.SetPixelIndex(x, y, 1);
1542                                         error = level - 255;
1543                                 } else {
1544                                         tmp.SetPixelIndex(x, y, 0);
1545                                         error = level;
1546                                 }
1547
1548                                 nlevel = GetPixelIndex(x + 1, y) + (error * 8) / TotalCoeffSum;
1549                                 level = (BYTE)min(255, max(0, (int)nlevel));
1550                                 SetPixelIndex(x + 1, y, level);
1551                                 nlevel = GetPixelIndex(x + 2, y) + (error * 4) / TotalCoeffSum;
1552                                 level = (BYTE)min(255, max(0, (int)nlevel));
1553                                 SetPixelIndex(x + 2, y, level);
1554                                 int i;
1555                                 for (i = -2; i < 3; i++) {
1556                                         switch (i) {
1557                                         case -2:
1558                                                 coeff = 2;
1559                                                 break;
1560                                         case -1:
1561                                                 coeff = 4;
1562                                                 break;
1563                                         case 0:
1564                                                 coeff = 8; 
1565                                                 break;
1566                                         case 1:
1567                                                 coeff = 4; 
1568                                                 break;
1569                                         case 2:
1570                                                 coeff = 2; 
1571                                                 break;
1572                                         }
1573                                         nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum;
1574                                         level = (BYTE)min(255, max(0, (int)nlevel));
1575                                         SetPixelIndex(x + i, y + 1, level);
1576                                 }
1577                         }
1578                 }
1579                 break;
1580         }
1581         case 3:
1582         {
1583                 //Stucki error diffusion (Thanks to Franco Gerevini)
1584                 int TotalCoeffSum = 42;
1585                 long error, nlevel, coeff=1;
1586                 BYTE level;
1587
1588                 for (long y = 0; y < head.biHeight; y++) {
1589                         info.nProgress = (long)(100 * y / head.biHeight);
1590                         if (info.nEscape) 
1591                                 break;
1592                         for (long x = 0; x < head.biWidth; x++) {
1593                                 level = BlindGetPixelIndex(x, y);
1594                                 if (level > 128) {
1595                                         tmp.SetPixelIndex(x, y, 1);
1596                                         error = level - 255;
1597                                 } else {
1598                                         tmp.SetPixelIndex(x, y, 0);
1599                                         error = level;
1600                                 }
1601
1602                                 nlevel = GetPixelIndex(x + 1, y) + (error * 8) / TotalCoeffSum;
1603                                 level = (BYTE)min(255, max(0, (int)nlevel));
1604                                 SetPixelIndex(x + 1, y, level);
1605                                 nlevel = GetPixelIndex(x + 2, y) + (error * 4) / TotalCoeffSum;
1606                                 level = (BYTE)min(255, max(0, (int)nlevel));
1607                                 SetPixelIndex(x + 2, y, level);
1608                                 int i;
1609                                 for (i = -2; i < 3; i++) {
1610                                         switch (i) {
1611                                         case -2:
1612                                                 coeff = 2;
1613                                                 break;
1614                                         case -1:
1615                                                 coeff = 4;
1616                                                 break;
1617                                         case 0:
1618                                                 coeff = 8; 
1619                                                 break;
1620                                         case 1:
1621                                                 coeff = 4; 
1622                                                 break;
1623                                         case 2:
1624                                                 coeff = 2; 
1625                                                 break;
1626                                         }
1627                                         nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum;
1628                                         level = (BYTE)min(255, max(0, (int)nlevel));
1629                                         SetPixelIndex(x + i, y + 1, level);
1630                                 }
1631                                 for (i = -2; i < 3; i++) {
1632                                         switch (i) {
1633                                         case -2:
1634                                                 coeff = 1;
1635                                                 break;
1636                                         case -1:
1637                                                 coeff = 2;
1638                                                 break;
1639                                         case 0:
1640                                                 coeff = 4; 
1641                                                 break;
1642                                         case 1:
1643                                                 coeff = 2; 
1644                                                 break;
1645                                         case 2:
1646                                                 coeff = 1; 
1647                                                 break;
1648                                         }
1649                                         nlevel = GetPixelIndex(x + i, y + 2) + (error * coeff) / TotalCoeffSum;
1650                                         level = (BYTE)min(255, max(0, (int)nlevel));
1651                                         SetPixelIndex(x + i, y + 2, level);
1652                                 }
1653                         }
1654                 }
1655                 break;
1656         }
1657         case 4:
1658         {
1659                 //Jarvis, Judice and Ninke error diffusion (Thanks to Franco Gerevini)
1660                 int TotalCoeffSum = 48;
1661                 long error, nlevel, coeff=1;
1662                 BYTE level;
1663
1664                 for (long y = 0; y < head.biHeight; y++) {
1665                         info.nProgress = (long)(100 * y / head.biHeight);
1666                         if (info.nEscape) 
1667                                 break;
1668                         for (long x = 0; x < head.biWidth; x++) {
1669                                 level = BlindGetPixelIndex(x, y);
1670                                 if (level > 128) {
1671                                         tmp.SetPixelIndex(x, y, 1);
1672                                         error = level - 255;
1673                                 } else {
1674                                         tmp.SetPixelIndex(x, y, 0);
1675                                         error = level;
1676                                 }
1677
1678                                 nlevel = GetPixelIndex(x + 1, y) + (error * 7) / TotalCoeffSum;
1679                                 level = (BYTE)min(255, max(0, (int)nlevel));
1680                                 SetPixelIndex(x + 1, y, level);
1681                                 nlevel = GetPixelIndex(x + 2, y) + (error * 5) / TotalCoeffSum;
1682                                 level = (BYTE)min(255, max(0, (int)nlevel));
1683                                 SetPixelIndex(x + 2, y, level);
1684                                 int i;
1685                                 for (i = -2; i < 3; i++) {
1686                                         switch (i) {
1687                                         case -2:
1688                                                 coeff = 3;
1689                                                 break;
1690                                         case -1:
1691                                                 coeff = 5;
1692                                                 break;
1693                                         case 0:
1694                                                 coeff = 7; 
1695                                                 break;
1696                                         case 1:
1697                                                 coeff = 5; 
1698                                                 break;
1699                                         case 2:
1700                                                 coeff = 3; 
1701                                                 break;
1702                                         }
1703                                         nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum;
1704                                         level = (BYTE)min(255, max(0, (int)nlevel));
1705                                         SetPixelIndex(x + i, y + 1, level);
1706                                 }
1707                                 for (i = -2; i < 3; i++) {
1708                                         switch (i) {
1709                                         case -2:
1710                                                 coeff = 1;
1711                                                 break;
1712                                         case -1:
1713                                                 coeff = 3;
1714                                                 break;
1715                                         case 0:
1716                                                 coeff = 5; 
1717                                                 break;
1718                                         case 1:
1719                                                 coeff = 3; 
1720                                                 break;
1721                                         case 2:
1722                                                 coeff = 1; 
1723                                                 break;
1724                                         }
1725                                         nlevel = GetPixelIndex(x + i, y + 2) + (error * coeff) / TotalCoeffSum;
1726                                         level = (BYTE)min(255, max(0, (int)nlevel));
1727                                         SetPixelIndex(x + i, y + 2, level);
1728                                 }
1729                         }
1730                 }
1731                 break;
1732         }
1733         case 5:
1734         {
1735                 //Sierra error diffusion (Thanks to Franco Gerevini)
1736                 int TotalCoeffSum = 32;
1737                 long error, nlevel, coeff=1;
1738                 BYTE level;
1739
1740                 for (long y = 0; y < head.biHeight; y++) {
1741                         info.nProgress = (long)(100 * y / head.biHeight);
1742                         if (info.nEscape) 
1743                                 break;
1744                         for (long x = 0; x < head.biWidth; x++) {
1745                                 level = BlindGetPixelIndex(x, y);
1746                                 if (level > 128) {
1747                                         tmp.SetPixelIndex(x, y, 1);
1748                                         error = level - 255;
1749                                 } else {
1750                                         tmp.SetPixelIndex(x, y, 0);
1751                                         error = level;
1752                                 }
1753
1754                                 nlevel = GetPixelIndex(x + 1, y) + (error * 5) / TotalCoeffSum;
1755                                 level = (BYTE)min(255, max(0, (int)nlevel));
1756                                 SetPixelIndex(x + 1, y, level);
1757                                 nlevel = GetPixelIndex(x + 2, y) + (error * 3) / TotalCoeffSum;
1758                                 level = (BYTE)min(255, max(0, (int)nlevel));
1759                                 SetPixelIndex(x + 2, y, level);
1760                                 int i;
1761                                 for (i = -2; i < 3; i++) {
1762                                         switch (i) {
1763                                         case -2:
1764                                                 coeff = 2;
1765                                                 break;
1766                                         case -1:
1767                                                 coeff = 4;
1768                                                 break;
1769                                         case 0:
1770                                                 coeff = 5; 
1771                                                 break;
1772                                         case 1:
1773                                                 coeff = 4; 
1774                                                 break;
1775                                         case 2:
1776                                                 coeff = 2; 
1777                                                 break;
1778                                         }
1779                                         nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum;
1780                                         level = (BYTE)min(255, max(0, (int)nlevel));
1781                                         SetPixelIndex(x + i, y + 1, level);
1782                                 }
1783                                 for (i = -1; i < 2; i++) {
1784                                         switch (i) {
1785                                         case -1:
1786                                                 coeff = 2;
1787                                                 break;
1788                                         case 0:
1789                                                 coeff = 3; 
1790                                                 break;
1791                                         case 1:
1792                                                 coeff = 2; 
1793                                                 break;
1794                                         }
1795                                         nlevel = GetPixelIndex(x + i, y + 2) + (error * coeff) / TotalCoeffSum;
1796                                         level = (BYTE)min(255, max(0, (int)nlevel));
1797                                         SetPixelIndex(x + i, y + 2, level);
1798                                 }
1799                         }
1800                 }
1801                 break;
1802         }
1803         case 6:
1804         {
1805                 //Stevenson and Arce error diffusion (Thanks to Franco Gerevini)
1806                 int TotalCoeffSum = 200;
1807                 long error, nlevel;
1808                 BYTE level;
1809
1810                 for (long y = 0; y < head.biHeight; y++) {
1811                         info.nProgress = (long)(100 * y / head.biHeight);
1812                         if (info.nEscape) 
1813                                 break;
1814                         for (long x = 0; x < head.biWidth; x++) {
1815                                 level = BlindGetPixelIndex(x, y);
1816                                 if (level > 128) {
1817                                         tmp.SetPixelIndex(x, y, 1);
1818                                         error = level - 255;
1819                                 } else {
1820                                         tmp.SetPixelIndex(x, y, 0);
1821                                         error = level;
1822                                 }
1823
1824                                 int tmp_index_x = x + 2;
1825                                 int tmp_index_y = y;
1826                                 int tmp_coeff = 32;
1827                                 nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
1828                                 level = (BYTE)min(255, max(0, (int)nlevel));
1829                                 SetPixelIndex(tmp_index_x, tmp_index_y, level);
1830
1831                                 tmp_index_x = x - 3;
1832                                 tmp_index_y = y + 1;
1833                                 tmp_coeff = 12;
1834                                 nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
1835                                 level = (BYTE)min(255, max(0, (int)nlevel));
1836                                 SetPixelIndex(tmp_index_x, tmp_index_y, level);
1837
1838                                 tmp_index_x = x - 1;
1839                                 tmp_coeff = 26;
1840                                 nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
1841                                 level = (BYTE)min(255, max(0, (int)nlevel));
1842                                 SetPixelIndex(tmp_index_x, tmp_index_y, level);
1843
1844                                 tmp_index_x = x + 1;
1845                                 tmp_coeff = 30;
1846                                 nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
1847                                 level = (BYTE)min(255, max(0, (int)nlevel));
1848                                 SetPixelIndex(tmp_index_x, tmp_index_y, level);
1849
1850                                 tmp_index_x = x + 3;
1851                                 tmp_coeff = 16;
1852                                 nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
1853                                 level = (BYTE)min(255, max(0, (int)nlevel));
1854                                 SetPixelIndex(tmp_index_x, tmp_index_y, level);
1855
1856                                 tmp_index_x = x - 2;
1857                                 tmp_index_y = y + 2;
1858                                 tmp_coeff = 12;
1859                                 nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
1860                                 level = (BYTE)min(255, max(0, (int)nlevel));
1861                                 SetPixelIndex(tmp_index_x, tmp_index_y, level);
1862
1863                                 tmp_index_x = x;
1864                                 tmp_coeff = 26;
1865                                 nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
1866                                 level = (BYTE)min(255, max(0, (int)nlevel));
1867                                 SetPixelIndex(tmp_index_x, tmp_index_y, level);
1868
1869                                 tmp_index_x = x + 2;
1870                                 tmp_coeff = 12;
1871                                 nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
1872                                 level = (BYTE)min(255, max(0, (int)nlevel));
1873                                 SetPixelIndex(tmp_index_x, tmp_index_y, level);
1874
1875                                 tmp_index_x = x - 3;
1876                                 tmp_index_y = y + 3;
1877                                 tmp_coeff = 5;
1878                                 nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
1879                                 level = (BYTE)min(255, max(0, (int)nlevel));
1880                                 SetPixelIndex(tmp_index_x, tmp_index_y, level);
1881
1882                                 tmp_index_x = x - 1;
1883                                 tmp_coeff = 12;
1884                                 nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
1885                                 level = (BYTE)min(255, max(0, (int)nlevel));
1886                                 SetPixelIndex(tmp_index_x, tmp_index_y, level);
1887
1888                                 tmp_index_x = x + 1;
1889                                 tmp_coeff = 12;
1890                                 nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
1891                                 level = (BYTE)min(255, max(0, (int)nlevel));
1892                                 SetPixelIndex(tmp_index_x, tmp_index_y, level);
1893
1894                                 tmp_index_x = x + 3;
1895                                 tmp_coeff = 5;
1896                                 nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
1897                                 level = (BYTE)min(255, max(0, (int)nlevel));
1898                                 SetPixelIndex(tmp_index_x, tmp_index_y, level);
1899                         }
1900                 }
1901                 break;
1902         }
1903         case 7:
1904         {
1905                 // Bayer ordered dither
1906                 int order = 4;
1907                 //create Bayer matrix
1908                 if (order>4) order = 4;
1909                 int size = (1 << (2*order));
1910                 BYTE* Bmatrix = (BYTE*) malloc(size * sizeof(BYTE));
1911                 for(int i = 0; i < size; i++) {
1912                         int n = order;
1913                         int x = i / n;
1914                         int y = i % n;
1915                         int dither = 0;
1916                         while (n-- > 0){
1917                                 dither = (((dither<<1)|((x&1) ^ (y&1)))<<1) | (y&1);
1918                                 x >>= 1;
1919                                 y >>= 1;
1920                         }
1921                         Bmatrix[i] = (BYTE)(dither);
1922                 }
1923
1924                 int scale = max(0,(8-2*order));
1925                 int level;
1926                 for (long y=0;y<head.biHeight;y++){
1927                         info.nProgress = (long)(100*y/head.biHeight);
1928                         if (info.nEscape) break;
1929                         for (long x=0;x<head.biWidth;x++){
1930                                 level = BlindGetPixelIndex(x,y) >> scale;
1931                                 if(level > Bmatrix[ (x % order) + order * (y % order) ]){
1932                                         tmp.SetPixelIndex(x,y,1);
1933                                 } else {
1934                                         tmp.SetPixelIndex(x,y,0);
1935                                 }
1936                         }
1937                 }
1938
1939                 free(Bmatrix);
1940
1941                 break;
1942         }
1943         default:
1944         {
1945                 // Floyd-Steinberg error diffusion (Thanks to Steve McMahon)
1946                 long error,nlevel,coeff=1;
1947                 BYTE level;
1948
1949                 for (long y=0;y<head.biHeight;y++){
1950                         info.nProgress = (long)(100*y/head.biHeight);
1951                         if (info.nEscape) break;
1952                         for (long x=0;x<head.biWidth;x++){
1953
1954                                 level = BlindGetPixelIndex(x,y);
1955                                 if (level > 128){
1956                                         tmp.SetPixelIndex(x,y,1);
1957                                         error = level-255;
1958                                 } else {
1959                                         tmp.SetPixelIndex(x,y,0);
1960                                         error = level;
1961                                 }
1962
1963                                 nlevel = GetPixelIndex(x+1,y) + (error * 7)/16;
1964                                 level = (BYTE)min(255,max(0,(int)nlevel));
1965                                 SetPixelIndex(x+1,y,level);
1966                                 for(int i=-1; i<2; i++){
1967                                         switch(i){
1968                                         case -1:
1969                                                 coeff=3; break;
1970                                         case 0:
1971                                                 coeff=5; break;
1972                                         case 1:
1973                                                 coeff=1; break;
1974                                         }
1975                                         nlevel = GetPixelIndex(x+i,y+1) + (error * coeff)/16;
1976                                         level = (BYTE)min(255,max(0,(int)nlevel));
1977                                         SetPixelIndex(x+i,y+1,level);
1978                                 }
1979                         }
1980                 }
1981         }
1982         }
1983
1984         tmp.SetPaletteColor(0,0,0,0);
1985         tmp.SetPaletteColor(1,255,255,255);
1986         Transfer(tmp);
1987
1988         return true;
1989 }
1990 ////////////////////////////////////////////////////////////////////////////////
1991 /**
1992  *      CropRotatedRectangle
1993  * \param topx,topy : topmost and leftmost point of the rectangle 
1994           (topmost, and if there are 2 topmost points, the left one)
1995  * \param  width     : size of the right hand side of rect, from (topx,topy) roundwalking clockwise
1996  * \param  height    : size of the left hand side of rect, from (topx,topy) roundwalking clockwise
1997  * \param  angle     : angle of the right hand side of rect, from (topx,topy)
1998  * \param  iDst      : pointer to destination image (if 0, this image is modified)
1999  * \author  [VATI]
2000  */
2001 bool CxImage::CropRotatedRectangle( long topx, long topy, long width, long height, float angle, CxImage* iDst)
2002 {
2003         if (!pDib) return false;
2004
2005         
2006         long startx,starty,endx,endy;
2007         double cos_angle = cos(angle/*/57.295779513082320877*/);
2008     double sin_angle = sin(angle/*/57.295779513082320877*/);
2009
2010         // if there is nothing special, call the original Crop():
2011         if ( fabs(angle)<0.0002 )
2012                 return Crop( topx, topy, topx+width, topy+height, iDst);
2013
2014         startx = min(topx, topx - (long)(sin_angle*(double)height));
2015         endx   = topx + (long)(cos_angle*(double)width);
2016         endy   = topy + (long)(cos_angle*(double)height + sin_angle*(double)width);
2017         // check: corners of the rectangle must be inside
2018         if ( IsInside( startx, topy )==false ||
2019                  IsInside( endx, endy ) == false )
2020                  return false;
2021
2022         // first crop to bounding rectangle
2023         CxImage tmp(*this, true, false, true);
2024         // tmp.Copy(*this, true, false, true);
2025         if (!tmp.IsValid()){
2026                 strcpy(info.szLastError,tmp.GetLastError());
2027                 return false;
2028         }
2029     if (!tmp.Crop( startx, topy, endx, endy)){
2030                 strcpy(info.szLastError,tmp.GetLastError());
2031                 return false;
2032         }
2033         
2034         // the midpoint of the image now became the same as the midpoint of the rectangle
2035         // rotate new image with minus angle amount
2036     if ( false == tmp.Rotate( (float)(-angle*57.295779513082320877) ) ) // Rotate expects angle in degrees
2037                 return false;
2038
2039         // crop rotated image to the original selection rectangle
2040     endx   = (tmp.head.biWidth+width)/2;
2041         startx = (tmp.head.biWidth-width)/2;
2042         starty = (tmp.head.biHeight+height)/2;
2043     endy   = (tmp.head.biHeight-height)/2;
2044     if ( false == tmp.Crop( startx, starty, endx, endy ) )
2045                 return false;
2046
2047         if (iDst) iDst->Transfer(tmp);
2048         else Transfer(tmp);
2049
2050         return true;
2051 }
2052 ////////////////////////////////////////////////////////////////////////////////
2053 bool CxImage::Crop(const RECT& rect, CxImage* iDst)
2054 {
2055         return Crop(rect.left, rect.top, rect.right, rect.bottom, iDst);
2056 }
2057 ////////////////////////////////////////////////////////////////////////////////
2058 bool CxImage::Crop(long left, long top, long right, long bottom, CxImage* iDst)
2059 {
2060         if (!pDib) return false;
2061
2062         long startx = max(0L,min(left,head.biWidth));
2063         long endx = max(0L,min(right,head.biWidth));
2064         long starty = head.biHeight - max(0L,min(top,head.biHeight));
2065         long endy = head.biHeight - max(0L,min(bottom,head.biHeight));
2066
2067         if (startx==endx || starty==endy) return false;
2068
2069         if (startx>endx) {long tmp=startx; startx=endx; endx=tmp;}
2070         if (starty>endy) {long tmp=starty; starty=endy; endy=tmp;}
2071
2072         CxImage tmp(endx-startx,endy-starty,head.biBitCount,info.dwType);
2073         if (!tmp.IsValid()){
2074                 strcpy(info.szLastError,tmp.GetLastError());
2075                 return false;
2076         }
2077
2078         tmp.SetPalette(GetPalette(),head.biClrUsed);
2079         tmp.info.nBkgndIndex = info.nBkgndIndex;
2080         tmp.info.nBkgndColor = info.nBkgndColor;
2081
2082         switch (head.biBitCount) {
2083         case 1:
2084         case 4:
2085         {
2086                 for(long y=starty, yd=0; y<endy; y++, yd++){
2087                         info.nProgress = (long)(100*(y-starty)/(endy-starty)); //<Anatoly Ivasyuk>
2088                         for(long x=startx, xd=0; x<endx; x++, xd++){
2089                                 tmp.SetPixelIndex(xd,yd,GetPixelIndex(x,y));
2090                         }
2091                 }
2092                 break;
2093         }
2094         case 8:
2095         case 24:
2096         {
2097                 int linelen = tmp.head.biWidth * tmp.head.biBitCount >> 3;
2098                 BYTE* pDest = tmp.info.pImage;
2099                 BYTE* pSrc = info.pImage + starty * info.dwEffWidth + (startx*head.biBitCount >> 3);
2100                 for(long y=starty; y<endy; y++){
2101                         info.nProgress = (long)(100*(y-starty)/(endy-starty)); //<Anatoly Ivasyuk>
2102                         memcpy(pDest,pSrc,linelen);
2103                         pDest+=tmp.info.dwEffWidth;
2104                         pSrc+=info.dwEffWidth;
2105                 }
2106     }
2107         }
2108
2109 #if CXIMAGE_SUPPORT_ALPHA
2110         if (AlphaIsValid()){ //<oboolo>
2111                 tmp.AlphaCreate();
2112                 if (!tmp.AlphaIsValid()) return false;
2113                 BYTE* pDest = tmp.pAlpha;
2114                 BYTE* pSrc = pAlpha + startx + starty*head.biWidth;
2115                 for (long y=starty; y<endy; y++){
2116                         memcpy(pDest,pSrc,endx-startx);
2117                         pDest+=tmp.head.biWidth;
2118                         pSrc+=head.biWidth;
2119                 }
2120         }
2121 #endif //CXIMAGE_SUPPORT_ALPHA
2122
2123         //select the destination
2124         if (iDst) iDst->Transfer(tmp);
2125         else Transfer(tmp);
2126
2127         return true;
2128 }
2129 ////////////////////////////////////////////////////////////////////////////////
2130 /**
2131  * \param xgain, ygain : can be from 0 to 1.
2132  * \param xpivot, ypivot : is the center of the transformation.
2133  * \param bEnableInterpolation : if true, enables bilinear interpolation.
2134  * \return true if everything is ok 
2135  */
2136 bool CxImage::Skew(float xgain, float ygain, long xpivot, long ypivot, bool bEnableInterpolation)
2137 {
2138         if (!pDib) return false;
2139         float nx,ny;
2140
2141         CxImage tmp(*this);
2142         if (!tmp.IsValid()){
2143                 strcpy(info.szLastError,tmp.GetLastError());
2144                 return false;
2145         }
2146
2147         long xmin,xmax,ymin,ymax;
2148         if (pSelection){
2149                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;
2150                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;
2151         } else {
2152                 xmin = ymin = 0;
2153                 xmax = head.biWidth; ymax=head.biHeight;
2154         }
2155         for(long y=ymin; y<ymax; y++){
2156                 info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));
2157                 if (info.nEscape) break;
2158                 for(long x=xmin; x<xmax; x++){
2159 #if CXIMAGE_SUPPORT_SELECTION
2160                         if (BlindSelectionIsInside(x,y))
2161 #endif //CXIMAGE_SUPPORT_SELECTION
2162                         {
2163                                 nx = x + (xgain*(y - ypivot));
2164                                 ny = y + (ygain*(x - xpivot));
2165 #if CXIMAGE_SUPPORT_INTERPOLATION
2166                                 if (bEnableInterpolation){
2167                                         tmp.SetPixelColor(x,y,GetPixelColorInterpolated(nx, ny, CxImage::IM_BILINEAR, CxImage::OM_BACKGROUND),true);
2168                                 } else
2169 #endif //CXIMAGE_SUPPORT_INTERPOLATION
2170                                 {
2171                                         if (head.biClrUsed==0){
2172                                                 tmp.SetPixelColor(x,y,GetPixelColor((long)nx,(long)ny));
2173                                         } else {
2174                                                 tmp.SetPixelIndex(x,y,GetPixelIndex((long)nx,(long)ny));
2175                                         }
2176 #if CXIMAGE_SUPPORT_ALPHA
2177                                         tmp.AlphaSet(x,y,AlphaGet((long)nx,(long)ny));
2178 #endif //CXIMAGE_SUPPORT_ALPHA
2179                                 }
2180                         }
2181                 }
2182         }
2183         Transfer(tmp);
2184         return true;
2185 }
2186 ////////////////////////////////////////////////////////////////////////////////
2187 /**
2188  * Expands the borders.
2189  * \param left, top, right, bottom = additional dimensions, should be greater than 0.
2190  * \param canvascolor = border color. canvascolor.rgbReserved will set the alpha channel (if any) in the border.
2191  * \param iDst = pointer to destination image (if it's 0, this image is modified)
2192  * \return true if everything is ok 
2193  * \author [Colin Urquhart]; changes [DP]
2194  */
2195 bool CxImage::Expand(long left, long top, long right, long bottom, RGBQUAD canvascolor, CxImage* iDst)
2196 {
2197     if (!pDib) return false;
2198
2199     if ((left < 0) || (right < 0) || (bottom < 0) || (top < 0)) return false;
2200
2201     long newWidth = head.biWidth + left + right;
2202     long newHeight = head.biHeight + top + bottom;
2203
2204     right = left + head.biWidth - 1;
2205     top = bottom + head.biHeight - 1;
2206     
2207     CxImage tmp;
2208         tmp.CopyInfo(*this);
2209         if (!tmp.Create(newWidth, newHeight, head.biBitCount, info.dwType)){
2210                 strcpy(info.szLastError,tmp.GetLastError());
2211                 return false;
2212         }
2213
2214     tmp.SetPalette(GetPalette(),head.biClrUsed);
2215
2216     switch (head.biBitCount) {
2217     case 1:
2218     case 4:
2219                 {
2220                         BYTE pixel = tmp.GetNearestIndex(canvascolor);
2221                         for(long y=0; y < newHeight; y++){
2222                                 info.nProgress = (long)(100*y/newHeight);
2223                                 for(long x=0; x < newWidth; x++){
2224                                         if ((y < bottom) || (y > top) || (x < left) || (x > right)) {
2225                                                 tmp.SetPixelIndex(x,y, pixel);
2226                                         } else {
2227                                                 tmp.SetPixelIndex(x,y,GetPixelIndex(x-left,y-bottom));
2228                                         }
2229                                 }
2230                         }
2231                         break;
2232                 }
2233     case 8:
2234     case 24:
2235                 {
2236                         if (head.biBitCount == 8) {
2237                                 BYTE pixel = tmp.GetNearestIndex( canvascolor);
2238                                 memset(tmp.info.pImage, pixel,  + (tmp.info.dwEffWidth * newHeight));
2239                         } else {
2240                                 for (long y = 0; y < newHeight; ++y) {
2241                                         BYTE *pDest = tmp.info.pImage + (y * tmp.info.dwEffWidth);
2242                                         for (long x = 0; x < newWidth; ++x) {
2243                                                 *pDest++ = canvascolor.rgbBlue;
2244                                                 *pDest++ = canvascolor.rgbGreen;
2245                                                 *pDest++ = canvascolor.rgbRed;
2246                                         }
2247                                 }
2248                         }
2249
2250                         BYTE* pDest = tmp.info.pImage + (tmp.info.dwEffWidth * bottom) + (left*(head.biBitCount >> 3));
2251                         BYTE* pSrc = info.pImage;
2252                         for(long y=bottom; y <= top; y++){
2253                                 info.nProgress = (long)(100*y/(1 + top - bottom));
2254                                 memcpy(pDest,pSrc,(head.biBitCount >> 3) * (right - left + 1));
2255                                 pDest+=tmp.info.dwEffWidth;
2256                                 pSrc+=info.dwEffWidth;
2257                         }
2258                 }
2259     }
2260
2261 #if CXIMAGE_SUPPORT_SELECTION
2262         if (SelectionIsValid()){
2263                 if (!tmp.SelectionCreate())
2264                         return false;
2265                 BYTE* pSrc = SelectionGetPointer();
2266                 BYTE* pDst = tmp.SelectionGetPointer(left,bottom);
2267                 for(long y=bottom; y <= top; y++){
2268                         memcpy(pDst,pSrc, (right - left + 1));
2269                         pSrc+=head.biWidth;
2270                         pDst+=tmp.head.biWidth;
2271                 }
2272                 tmp.info.rSelectionBox.left = info.rSelectionBox.left + left;
2273                 tmp.info.rSelectionBox.right = info.rSelectionBox.right + left;
2274                 tmp.info.rSelectionBox.top = info.rSelectionBox.top + bottom;
2275                 tmp.info.rSelectionBox.bottom = info.rSelectionBox.bottom + bottom;
2276         }
2277 #endif //CXIMAGE_SUPPORT_SELECTION
2278
2279 #if CXIMAGE_SUPPORT_ALPHA
2280         if (AlphaIsValid()){
2281                 if (!tmp.AlphaCreate())
2282                         return false;
2283                 tmp.AlphaSet(canvascolor.rgbReserved);
2284                 BYTE* pSrc = AlphaGetPointer();
2285                 BYTE* pDst = tmp.AlphaGetPointer(left,bottom);
2286                 for(long y=bottom; y <= top; y++){
2287                         memcpy(pDst,pSrc, (right - left + 1));
2288                         pSrc+=head.biWidth;
2289                         pDst+=tmp.head.biWidth;
2290                 }
2291         }
2292 #endif //CXIMAGE_SUPPORT_ALPHA
2293
2294     //select the destination
2295         if (iDst) iDst->Transfer(tmp);
2296     else Transfer(tmp);
2297
2298     return true;
2299 }
2300 ////////////////////////////////////////////////////////////////////////////////
2301 bool CxImage::Expand(long newx, long newy, RGBQUAD canvascolor, CxImage* iDst)
2302 {
2303         //thanks to <Colin Urquhart>
2304
2305     if (!pDib) return false;
2306
2307     if ((newx < head.biWidth) || (newy < head.biHeight)) return false;
2308
2309     int nAddLeft = (newx - head.biWidth) / 2;
2310     int nAddTop = (newy - head.biHeight) / 2;
2311
2312     return Expand(nAddLeft, nAddTop, newx - (head.biWidth + nAddLeft), newy - (head.biHeight + nAddTop), canvascolor, iDst);
2313 }
2314 ////////////////////////////////////////////////////////////////////////////////
2315 /**
2316  * Resamples the image with the correct aspect ratio, and fills the borders.
2317  * \param newx, newy = thumbnail size.
2318  * \param canvascolor = border color.
2319  * \param iDst = pointer to destination image (if it's 0, this image is modified).
2320  * \return true if everything is ok.
2321  * \author [Colin Urquhart]
2322  */
2323 bool CxImage::Thumbnail(long newx, long newy, RGBQUAD canvascolor, CxImage* iDst)
2324 {
2325     if (!pDib) return false;
2326
2327     if ((newx <= 0) || (newy <= 0)) return false;
2328
2329     CxImage tmp(*this);
2330         if (!tmp.IsValid()){
2331                 strcpy(info.szLastError,tmp.GetLastError());
2332                 return false;
2333         }
2334
2335     // determine whether we need to shrink the image
2336     if ((head.biWidth > newx) || (head.biHeight > newy)) {
2337         float fScale;
2338         float fAspect = (float) newx / (float) newy;
2339         if (fAspect * head.biHeight > head.biWidth) {
2340             fScale = (float) newy / head.biHeight;
2341         } else {
2342             fScale = (float) newx / head.biWidth;
2343         }
2344         tmp.Resample((long) (fScale * head.biWidth), (long) (fScale * head.biHeight), 0);
2345     }
2346
2347     // expand the frame
2348     tmp.Expand(newx, newy, canvascolor, iDst);
2349
2350     //select the destination
2351     if (iDst) iDst->Transfer(tmp);
2352     else Transfer(tmp);
2353     return true;
2354 }
2355 ////////////////////////////////////////////////////////////////////////////////
2356 /**
2357  * Perform circle_based transformations.
2358  * \param type - for different transformations
2359  * - 0 for normal (proturberant) FishEye
2360  * - 1 for reverse (concave) FishEye
2361  * - 2 for Swirle 
2362  * - 3 for Cilinder mirror
2363  * - 4 for bathroom
2364  *
2365  * \param rmax - effect radius. If 0, the whole image is processed
2366  * \param Koeff - only for swirle
2367  * \author Arkadiy Olovyannikov ark(at)msun(dot)ru
2368  */
2369 bool CxImage::CircleTransform(int type,long rmax,float Koeff)
2370 {
2371         if (!pDib) return false;
2372
2373         long nx,ny;
2374         double angle,radius,rnew;
2375
2376         CxImage tmp(*this);
2377         if (!tmp.IsValid()){
2378                 strcpy(info.szLastError,tmp.GetLastError());
2379                 return false;
2380         }
2381
2382         long xmin,xmax,ymin,ymax,xmid,ymid;
2383         if (pSelection){
2384                 xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right;
2385                 ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top;
2386         } else {
2387                 xmin = ymin = 0;
2388                 xmax = head.biWidth; ymax=head.biHeight;
2389         }
2390         
2391         xmid = (long) (tmp.GetWidth()/2);
2392         ymid = (long) (tmp.GetHeight()/2);
2393
2394         if (!rmax) rmax=(long)sqrt((float)((xmid-xmin)*(xmid-xmin)+(ymid-ymin)*(ymid-ymin)));
2395         if (Koeff==0.0f) Koeff=1.0f;
2396
2397         for(long y=ymin; y<ymax; y++){
2398                 info.nProgress = (long)(100*(y-ymin)/(ymax-ymin));
2399                 if (info.nEscape) break;
2400                 for(long x=xmin; x<xmax; x++){
2401 #if CXIMAGE_SUPPORT_SELECTION
2402                         if (BlindSelectionIsInside(x,y))
2403 #endif //CXIMAGE_SUPPORT_SELECTION
2404                         {
2405                                 nx=xmid-x;
2406                                 ny=ymid-y;
2407                                 radius=sqrt((float)(nx*nx+ny*ny));
2408                                 if (radius<rmax) {
2409                                         angle=atan2((double)ny,(double)nx);
2410                                         if (type==0)      rnew=radius*radius/rmax;
2411                                         else if (type==1) rnew=sqrt(radius*rmax);
2412                                         else if (type==2) {rnew=radius;angle += radius / Koeff;}
2413                                         else rnew = 1; // potentially uninitialized
2414                                         if (type<3){
2415                                                 nx = xmid + (long)(rnew * cos(angle));
2416                                                 ny = ymid - (long)(rnew * sin(angle));
2417                                         }
2418                                         else if (type==3){
2419                                                 nx = (long)fabs((angle*xmax/6.2831852));
2420                                                 ny = (long)fabs((radius*ymax/rmax));
2421                                         }
2422                                         else {
2423                                                 nx=x+(x%32)-16;
2424                                                 ny=y;
2425                                         }
2426 //                                      nx=max(xmin,min(nx,xmax));
2427 //                                      ny=max(ymin,min(ny,ymax));
2428                                 }
2429                                 else { nx=-1;ny=-1;}
2430                                 if (head.biClrUsed==0){
2431                                         tmp.SetPixelColor(x,y,GetPixelColor(nx,ny));
2432                                 } else {
2433                                         tmp.SetPixelIndex(x,y,GetPixelIndex(nx,ny));
2434                                 }
2435 #if CXIMAGE_SUPPORT_ALPHA
2436                                 tmp.AlphaSet(x,y,AlphaGet(nx,ny));
2437 #endif //CXIMAGE_SUPPORT_ALPHA
2438                         }
2439                 }
2440         }
2441         Transfer(tmp);
2442         return true;
2443 }
2444 ////////////////////////////////////////////////////////////////////////////////
2445 /**
2446  * Faster way to almost properly shrink image. Algorithm produces results comparable with "high resoultion shrink"
2447  * when resulting image is much smaller (that would be 3 times or more) than original. When
2448  * resulting image is only slightly smaller, results are closer to nearest pixel.
2449  * This algorithm works by averaging, but it does not calculate fractions of pixels. It adds whole
2450  * source pixels to the best destionation. It is not geometrically "correct".
2451  * It's main advantage over "high" resulution shrink is speed, so it's useful, when speed is most
2452  * important (preview thumbnails, "map" view, ...).
2453  * Method is optimized for RGB24 images.
2454  * 
2455  * \param  newx, newy - size of destination image (must be smaller than original!)
2456  * \param  iDst - pointer to destination image (if it's 0, this image is modified)
2457  * \param  bChangeBpp - flag points to change result image bpp (if it's true, this result image bpp = 24 (useful for B/W image thumbnails))
2458  *
2459  * \return true if everything is ok
2460  * \author [bd], 9.2004; changes [Artiom Mirolubov], 1.2005
2461  */
2462 bool CxImage::QIShrink(long newx, long newy, CxImage* const iDst, bool bChangeBpp)
2463 {
2464         if (!pDib) return false;
2465         
2466         if (newx>head.biWidth || newy>head.biHeight) { 
2467                 //let me repeat... this method can't enlarge image
2468                 strcpy(info.szLastError,"QIShrink can't enlarge image");
2469                 return false;
2470         }
2471
2472         if (newx==head.biWidth && newy==head.biHeight) {
2473                 //image already correct size (just copy and return)
2474                 if (iDst) iDst->Copy(*this);
2475                 return true;
2476         }//if
2477         
2478         //create temporary destination image
2479         CxImage newImage;
2480         newImage.CopyInfo(*this);
2481         newImage.Create(newx,newy,(bChangeBpp)?24:head.biBitCount,GetType());
2482         newImage.SetPalette(GetPalette());
2483         if (!newImage.IsValid()){
2484                 strcpy(info.szLastError,newImage.GetLastError());
2485                 return false;
2486         }
2487
2488         //and alpha channel if required
2489 #if CXIMAGE_SUPPORT_ALPHA
2490         if (AlphaIsValid()) newImage.AlphaCreate();
2491 #endif
2492
2493     const int oldx = head.biWidth;
2494     const int oldy = head.biHeight;
2495
2496     int accuCellSize = 4;
2497 #if CXIMAGE_SUPPORT_ALPHA
2498         BYTE *alphaPtr;
2499         if (AlphaIsValid()) accuCellSize=5;
2500 #endif
2501
2502     unsigned int *accu = new unsigned int[newx*accuCellSize];      //array for suming pixels... one pixel for every destination column
2503     unsigned int *accuPtr;                              //pointer for walking through accu
2504     //each cell consists of blue, red, green component and count of pixels summed in this cell
2505     memset(accu, 0, newx * accuCellSize * sizeof(unsigned int));  //clear accu
2506
2507     if (!IsIndexed()) {
2508                 //RGB24 version with pointers
2509                 BYTE *destPtr, *srcPtr, *destPtrS, *srcPtrS;        //destination and source pixel, and beginnings of current row
2510                 srcPtrS=(BYTE*)BlindGetPixelPointer(0,0);
2511                 destPtrS=(BYTE*)newImage.BlindGetPixelPointer(0,0);
2512                 int ex=0, ey=0;                                               //ex and ey replace division... 
2513                 int dy=0;
2514                 //(we just add pixels, until by adding newx or newy we get a number greater than old size... then
2515                 // it's time to move to next pixel)
2516         
2517                 for(int y=0; y<oldy; y++){                                    //for all source rows
2518                         info.nProgress = (long)(100*y/oldy); if (info.nEscape) break;
2519                         ey += newy;                                                   
2520                         ex = 0;                                                       //restart with ex = 0
2521                         accuPtr=accu;                                                 //restart from beginning of accu
2522                         srcPtr=srcPtrS;                                               //and from new source line
2523 #if CXIMAGE_SUPPORT_ALPHA
2524                         alphaPtr = AlphaGetPointer(0, y);
2525 #endif
2526
2527                         for(int x=0; x<oldx; x++){                                    //for all source columns
2528                                 ex += newx;
2529                                 *accuPtr     += *(srcPtr++);                                  //add current pixel to current accu slot
2530                                 *(accuPtr+1) += *(srcPtr++);
2531                                 *(accuPtr+2) += *(srcPtr++);
2532                                 (*(accuPtr+3)) ++;
2533 #if CXIMAGE_SUPPORT_ALPHA
2534                                 if (alphaPtr) *(accuPtr+4) += *(alphaPtr++);
2535 #endif
2536                                 if (ex>oldx) {                                                //when we reach oldx, it's time to move to new slot
2537                                         accuPtr += accuCellSize;
2538                                         ex -= oldx;                                                   //(substract oldx from ex and resume from there on)
2539                                 }//if (ex overflow)
2540                         }//for x
2541
2542                         if (ey>=oldy) {                                                 //now when this happens
2543                                 ey -= oldy;                                                     //it's time to move to new destination row
2544                                 destPtr = destPtrS;                                             //reset pointers to proper initial values
2545                                 accuPtr = accu;
2546 #if CXIMAGE_SUPPORT_ALPHA
2547                                 alphaPtr = newImage.AlphaGetPointer(0, dy++);
2548 #endif
2549                                 for (int k=0; k<newx; k++) {                                    //copy accu to destination row (divided by number of pixels in each slot)
2550                                         *(destPtr++) = (BYTE)(*(accuPtr) / *(accuPtr+3));
2551                                         *(destPtr++) = (BYTE)(*(accuPtr+1) / *(accuPtr+3));
2552                                         *(destPtr++) = (BYTE)(*(accuPtr+2) / *(accuPtr+3));
2553 #if CXIMAGE_SUPPORT_ALPHA
2554                                         if (alphaPtr) *(alphaPtr++) = (BYTE)(*(accuPtr+4) / *(accuPtr+3));
2555 #endif
2556                                         accuPtr += accuCellSize;
2557                                 }//for k
2558                                 memset(accu, 0, newx * accuCellSize * sizeof(unsigned int));                   //clear accu
2559                                 destPtrS += newImage.info.dwEffWidth;
2560                         }//if (ey overflow)
2561
2562                         srcPtrS += info.dwEffWidth;                                     //next round we start from new source row
2563                 }//for y
2564     } else {
2565                 //standard version with GetPixelColor...
2566                 int ex=0, ey=0;                                               //ex and ey replace division... 
2567                 int dy=0;
2568                 //(we just add pixels, until by adding newx or newy we get a number greater than old size... then
2569                 // it's time to move to next pixel)
2570                 RGBQUAD rgb;
2571         
2572                 for(int y=0; y<oldy; y++){                                    //for all source rows
2573                         info.nProgress = (long)(100*y/oldy); if (info.nEscape) break;
2574                         ey += newy;                                                   
2575                         ex = 0;                                                       //restart with ex = 0
2576                         accuPtr=accu;                                                 //restart from beginning of accu
2577                         for(int x=0; x<oldx; x++){                                    //for all source columns
2578                                 ex += newx;
2579                                 rgb = GetPixelColor(x, y, true);
2580                                 *accuPtr     += rgb.rgbBlue;                                  //add current pixel to current accu slot
2581                                 *(accuPtr+1) += rgb.rgbRed;
2582                                 *(accuPtr+2) += rgb.rgbGreen;
2583                                 (*(accuPtr+3)) ++;
2584 #if CXIMAGE_SUPPORT_ALPHA
2585                                 if (pAlpha) *(accuPtr+4) += rgb.rgbReserved;
2586 #endif
2587                                 if (ex>oldx) {                                                //when we reach oldx, it's time to move to new slot
2588                                         accuPtr += accuCellSize;
2589                                         ex -= oldx;                                                   //(substract oldx from ex and resume from there on)
2590                                 }//if (ex overflow)
2591                         }//for x
2592
2593                         if (ey>=oldy) {                                                 //now when this happens
2594                                 ey -= oldy;                                                     //it's time to move to new destination row
2595                                 accuPtr = accu;
2596                                 for (int dx=0; dx<newx; dx++) {                                 //copy accu to destination row (divided by number of pixels in each slot)
2597                                         rgb.rgbBlue = (BYTE)(*(accuPtr) / *(accuPtr+3));
2598                                         rgb.rgbRed  = (BYTE)(*(accuPtr+1) / *(accuPtr+3));
2599                                         rgb.rgbGreen= (BYTE)(*(accuPtr+2) / *(accuPtr+3));
2600 #if CXIMAGE_SUPPORT_ALPHA
2601                                         if (pAlpha) rgb.rgbReserved = (BYTE)(*(accuPtr+4) / *(accuPtr+3));
2602 #endif
2603                                         newImage.SetPixelColor(dx, dy, rgb, pAlpha!=0);
2604                                         accuPtr += accuCellSize;
2605                                 }//for dx
2606                                 memset(accu, 0, newx * accuCellSize * sizeof(unsigned int));                   //clear accu
2607                                 dy++;
2608                         }//if (ey overflow)
2609                 }//for y
2610     }//if
2611
2612     delete [] accu;                                                 //delete helper array
2613         
2614         //copy new image to the destination
2615         if (iDst) 
2616                 iDst->Transfer(newImage);
2617         else 
2618                 Transfer(newImage);
2619     return true;
2620
2621 }
2622
2623 ////////////////////////////////////////////////////////////////////////////////
2624 #endif //CXIMAGE_SUPPORT_TRANSFORMATION