source: liacs/MIR2010/SourceCode/cximage/ximatran.cpp@ 145

Last change on this file since 145 was 95, checked in by Rick van der Zwet, 15 years ago

Bad boy, improper move of directory

File size: 83.5 KB
RevLine 
[95]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////////////////////////////////////////////////////////////////////////////////
11bool 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 */
88bool 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 */
127bool 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////////////////////////////////////////////////////////////////////////////////
191bool 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////////////////////////////////////////////////////////////////////////////////
345bool 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////////////////////////////////////////////////////////////////////////////////
490bool 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////////////////////////////////////////////////////////////////////////////////
561bool 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 */
670bool 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////////////////////////////////////////////////////////////////////////////////
834bool 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 */
879bool 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 */
1141bool 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 */
1253bool 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 */
1343bool 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 */
1459bool 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 */
2001bool 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////////////////////////////////////////////////////////////////////////////////
2053bool CxImage::Crop(const RECT& rect, CxImage* iDst)
2054{
2055 return Crop(rect.left, rect.top, rect.right, rect.bottom, iDst);
2056}
2057////////////////////////////////////////////////////////////////////////////////
2058bool 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 */
2136bool 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 */
2195bool 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////////////////////////////////////////////////////////////////////////////////
2301bool 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 */
2323bool 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 */
2369bool 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 */
2462bool 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
Note: See TracBrowser for help on using the repository browser.