]> Creatis software - clitk.git/blob - utilities/CxImage/ximage.cpp
Merge branch 'master' of git.creatis.insa-lyon.fr:clitk
[clitk.git] / utilities / CxImage / ximage.cpp
1 // ximage.cpp : main implementation file\r
2 /* 07/08/2001 v1.00 - Davide Pizzolato - www.xdp.it\r
3  * CxImage version 6.0.0 02/Feb/2008\r
4  */\r
5 \r
6 #include "ximage.h"\r
7 \r
8 ////////////////////////////////////////////////////////////////////////////////\r
9 // CxImage \r
10 ////////////////////////////////////////////////////////////////////////////////\r
11 /**\r
12  * Initialize the internal structures\r
13  */\r
14 void CxImage::Startup(DWORD imagetype)\r
15 {\r
16         //init pointers\r
17         pDib = pSelection = pAlpha = NULL;\r
18         ppLayers = ppFrames = NULL;\r
19         //init structures\r
20         memset(&head,0,sizeof(BITMAPINFOHEADER));\r
21         memset(&info,0,sizeof(CXIMAGEINFO));\r
22         //init default attributes\r
23     info.dwType = imagetype;\r
24         info.fQuality = 90.0f;\r
25         info.nAlphaMax = 255;\r
26         info.nBkgndIndex = -1;\r
27         info.bEnabled = true;\r
28         SetXDPI(CXIMAGE_DEFAULT_DPI);\r
29         SetYDPI(CXIMAGE_DEFAULT_DPI);\r
30 \r
31         short test = 1;\r
32         info.bLittleEndianHost = (*((char *) &test) == 1);\r
33 }\r
34 ////////////////////////////////////////////////////////////////////////////////\r
35 /**\r
36  * Empty image constructor\r
37  * \param imagetype: (optional) set the image format, see ENUM_CXIMAGE_FORMATS\r
38  */\r
39 CxImage::CxImage(DWORD imagetype)\r
40 {\r
41         Startup(imagetype);\r
42 }\r
43 ////////////////////////////////////////////////////////////////////////////////\r
44 /**\r
45  * Call this function to destroy image pixels, alpha channel, selection and sub layers.\r
46  * - Attributes are not erased, but IsValid returns false.\r
47  *\r
48  * \return true if everything is freed, false if the image is a Ghost\r
49  */\r
50 bool CxImage::Destroy()\r
51 {\r
52         //free this only if it's valid and it's not a ghost\r
53         if (info.pGhost==NULL){\r
54                 if (ppLayers) { \r
55                         for(long n=0; n<info.nNumLayers;n++){ delete ppLayers[n]; }\r
56                         delete [] ppLayers; ppLayers=0; info.nNumLayers = 0;\r
57                 }\r
58                 if (pSelection) {free(pSelection); pSelection=0;}\r
59                 if (pAlpha) {free(pAlpha); pAlpha=0;}\r
60                 if (pDib) {free(pDib); pDib=0;}\r
61                 return true;\r
62         }\r
63         return false;\r
64 }\r
65 ////////////////////////////////////////////////////////////////////////////////\r
66 bool CxImage::DestroyFrames()\r
67 {\r
68         if (info.pGhost==NULL) {\r
69                 if (ppFrames) {\r
70                         for (long n=0; n<info.nNumFrames; n++) { delete ppFrames[n]; }\r
71                         delete [] ppFrames; ppFrames = NULL; info.nNumFrames = 0;\r
72                 }\r
73                 return true;\r
74         }\r
75         return false;\r
76 }\r
77 ////////////////////////////////////////////////////////////////////////////////\r
78 /**\r
79  * Sized image constructor\r
80  * \param dwWidth: width\r
81  * \param dwHeight: height\r
82  * \param wBpp: bit per pixel, can be 1, 4, 8, 24\r
83  * \param imagetype: (optional) set the image format, see ENUM_CXIMAGE_FORMATS\r
84  */\r
85 CxImage::CxImage(DWORD dwWidth, DWORD dwHeight, DWORD wBpp, DWORD imagetype)\r
86 {\r
87         Startup(imagetype);\r
88         Create(dwWidth,dwHeight,wBpp,imagetype);\r
89 }\r
90 ////////////////////////////////////////////////////////////////////////////////\r
91 /**\r
92  * image constructor from existing source\r
93  * \param src: source image.\r
94  * \param copypixels: copy the pixels from the source image into the new image.\r
95  * \param copyselection: copy the selection from source\r
96  * \param copyalpha: copy the alpha channel from source\r
97  * \sa Copy\r
98  */\r
99 CxImage::CxImage(const CxImage &src, bool copypixels, bool copyselection, bool copyalpha)\r
100 {\r
101         Startup(src.GetType());\r
102         Copy(src,copypixels,copyselection,copyalpha);\r
103 }\r
104 ////////////////////////////////////////////////////////////////////////////////\r
105 /**\r
106  * Copies the image from an exsisting source\r
107  * \param src: source image.\r
108  * \param copypixels: copy the pixels from the source image into the new image.\r
109  * \param copyselection: copy the selection from source\r
110  * \param copyalpha: copy the alpha channel from source\r
111  */\r
112 void CxImage::Copy(const CxImage &src, bool copypixels, bool copyselection, bool copyalpha)\r
113 {\r
114         // if the source is a ghost, the copy is still a ghost\r
115         if (src.info.pGhost){\r
116                 Ghost(&src);\r
117                 return;\r
118         }\r
119         //copy the attributes\r
120         memcpy(&info,&src.info,sizeof(CXIMAGEINFO));\r
121         memcpy(&head,&src.head,sizeof(BITMAPINFOHEADER)); // [andy] - fix for bitmap header DPI\r
122         //rebuild the image\r
123         Create(src.GetWidth(),src.GetHeight(),src.GetBpp(),src.GetType());\r
124         //copy the pixels and the palette, or at least copy the palette only.\r
125         if (copypixels && pDib && src.pDib) memcpy(pDib,src.pDib,GetSize());\r
126         else SetPalette(src.GetPalette());\r
127         long nSize = head.biWidth * head.biHeight;\r
128         //copy the selection\r
129         if (copyselection && src.pSelection){\r
130                 if (pSelection) free(pSelection);\r
131                 pSelection = (BYTE*)malloc(nSize);\r
132                 memcpy(pSelection,src.pSelection,nSize);\r
133         }\r
134         //copy the alpha channel\r
135         if (copyalpha && src.pAlpha){\r
136                 if (pAlpha) free(pAlpha);\r
137                 pAlpha = (BYTE*)malloc(nSize);\r
138                 memcpy(pAlpha,src.pAlpha,nSize);\r
139         }\r
140 }\r
141 ////////////////////////////////////////////////////////////////////////////////\r
142 /**\r
143  * Copies the image attributes from an existing image.\r
144  * - Works only on an empty image, and the image will be still empty.\r
145  * - <b> Use it before Create() </b>\r
146  */\r
147 void CxImage::CopyInfo(const CxImage &src)\r
148 {\r
149         if (pDib==NULL) memcpy(&info,&src.info,sizeof(CXIMAGEINFO));\r
150 }\r
151 ////////////////////////////////////////////////////////////////////////////////\r
152 /**\r
153  * \sa Copy\r
154  */\r
155 CxImage& CxImage::operator = (const CxImage& isrc)\r
156 {\r
157         if (this != &isrc) Copy(isrc);\r
158         return *this;\r
159 }\r
160 ////////////////////////////////////////////////////////////////////////////////\r
161 /**\r
162  * Initializes or rebuilds the image.\r
163  * \param dwWidth: width\r
164  * \param dwHeight: height\r
165  * \param wBpp: bit per pixel, can be 1, 4, 8, 24\r
166  * \param imagetype: (optional) set the image format, see ENUM_CXIMAGE_FORMATS\r
167  * \return pointer to the internal pDib object; NULL if an error occurs.\r
168  */\r
169 void* CxImage::Create(DWORD dwWidth, DWORD dwHeight, DWORD wBpp, DWORD imagetype)\r
170 {\r
171         // destroy the existing image (if any)\r
172         if (!Destroy())\r
173                 return NULL;\r
174 \r
175         // prevent further actions if width or height are not vaild <Balabasnia>\r
176         if ((dwWidth == 0) || (dwHeight == 0)){\r
177                 strcpy(info.szLastError,"CxImage::Create : width and height must be greater than zero");\r
178                 return NULL;\r
179         }\r
180 \r
181     // Make sure bits per pixel is valid\r
182     if          (wBpp <= 1)     wBpp = 1;\r
183     else if (wBpp <= 4) wBpp = 4;\r
184     else if (wBpp <= 8) wBpp = 8;\r
185     else                                wBpp = 24;\r
186 \r
187         // limit memory requirements (and also a check for bad parameters)\r
188         if (((dwWidth*dwHeight*wBpp)>>3) > CXIMAGE_MAX_MEMORY ||\r
189                 ((dwWidth*dwHeight*wBpp)/wBpp) != (dwWidth*dwHeight))\r
190         {\r
191                 strcpy(info.szLastError,"CXIMAGE_MAX_MEMORY exceeded");\r
192                 return NULL;\r
193         }\r
194 \r
195         // set the correct bpp value\r
196     switch (wBpp){\r
197         case 1:\r
198             head.biClrUsed = 2; break;\r
199         case 4:\r
200             head.biClrUsed = 16; break;\r
201         case 8:\r
202             head.biClrUsed = 256; break;\r
203         default:\r
204             head.biClrUsed = 0;\r
205     }\r
206 \r
207         //set the common image informations\r
208     info.dwEffWidth = ((((wBpp * dwWidth) + 31) / 32) * 4);\r
209     info.dwType = imagetype;\r
210 \r
211     // initialize BITMAPINFOHEADER\r
212         head.biSize = sizeof(BITMAPINFOHEADER); //<ralphw>\r
213     head.biWidth = dwWidth;             // fill in width from parameter\r
214     head.biHeight = dwHeight;   // fill in height from parameter\r
215     head.biPlanes = 1;                  // must be 1\r
216     head.biBitCount = (WORD)wBpp;               // from parameter\r
217     head.biCompression = BI_RGB;    \r
218     head.biSizeImage = info.dwEffWidth * dwHeight;\r
219 //    head.biXPelsPerMeter = 0; See SetXDPI\r
220 //    head.biYPelsPerMeter = 0; See SetYDPI\r
221 //    head.biClrImportant = 0;  See SetClrImportant\r
222 \r
223         pDib = malloc(GetSize()); // alloc memory block to store our bitmap\r
224     if (!pDib){\r
225                 strcpy(info.szLastError,"CxImage::Create can't allocate memory");\r
226                 return NULL;\r
227         }\r
228 \r
229         //clear the palette\r
230         RGBQUAD* pal=GetPalette();\r
231         if (pal) memset(pal,0,GetPaletteSize());\r
232         //Destroy the existing selection\r
233 #if CXIMAGE_SUPPORT_SELECTION\r
234         if (pSelection) SelectionDelete();\r
235 #endif //CXIMAGE_SUPPORT_SELECTION\r
236         //Destroy the existing alpha channel\r
237 #if CXIMAGE_SUPPORT_ALPHA\r
238         if (pAlpha) AlphaDelete();\r
239 #endif //CXIMAGE_SUPPORT_ALPHA\r
240 \r
241     // use our bitmap info structure to fill in first part of\r
242     // our DIB with the BITMAPINFOHEADER\r
243     BITMAPINFOHEADER*  lpbi;\r
244         lpbi = (BITMAPINFOHEADER*)(pDib);\r
245     *lpbi = head;\r
246 \r
247         info.pImage=GetBits();\r
248 \r
249     return pDib; //return handle to the DIB\r
250 }\r
251 ////////////////////////////////////////////////////////////////////////////////\r
252 /**\r
253  * \return pointer to the image pixels. <b> USE CAREFULLY </b>\r
254  */\r
255 BYTE* CxImage::GetBits(DWORD row)\r
256\r
257         if (pDib){\r
258                 if (row) {\r
259                         if (row<(DWORD)head.biHeight){\r
260                                 return ((BYTE*)pDib + *(DWORD*)pDib + GetPaletteSize() + (info.dwEffWidth * row));\r
261                         } else {\r
262                                 return NULL;\r
263                         }\r
264                 } else {\r
265                         return ((BYTE*)pDib + *(DWORD*)pDib + GetPaletteSize());\r
266                 }\r
267         }\r
268         return NULL;\r
269 }\r
270 ////////////////////////////////////////////////////////////////////////////////\r
271 /**\r
272  * \return the size in bytes of the internal pDib object\r
273  */\r
274 long CxImage::GetSize()\r
275 {\r
276         return head.biSize + head.biSizeImage + GetPaletteSize();\r
277 }\r
278 ////////////////////////////////////////////////////////////////////////////////\r
279 /**\r
280  * Checks if the coordinates are inside the image\r
281  * \return true if x and y are both inside the image\r
282  */\r
283 bool CxImage::IsInside(long x, long y)\r
284 {\r
285   return (0<=y && y<head.biHeight && 0<=x && x<head.biWidth);\r
286 }\r
287 ////////////////////////////////////////////////////////////////////////////////\r
288 /**\r
289  * Sets the image bits to the specified value\r
290  * - for indexed images, the output color is set by the palette entries.\r
291  * - for RGB images, the output color is a shade of gray.\r
292  */\r
293 void CxImage::Clear(BYTE bval)\r
294 {\r
295         if (pDib == 0) return;\r
296 \r
297         if (GetBpp() == 1){\r
298                 if (bval > 0) bval = 255;\r
299         }\r
300         if (GetBpp() == 4){\r
301                 bval = (BYTE)(17*(0x0F & bval));\r
302         }\r
303 \r
304         memset(info.pImage,bval,head.biSizeImage);\r
305 }\r
306 ////////////////////////////////////////////////////////////////////////////////\r
307 /**\r
308  * Transfers the image from an existing source image. The source becomes empty.\r
309  * \return true if everything is ok\r
310  */\r
311 bool CxImage::Transfer(CxImage &from, bool bTransferFrames /*=true*/)\r
312 {\r
313         if (!Destroy())\r
314                 return false;\r
315 \r
316         memcpy(&head,&from.head,sizeof(BITMAPINFOHEADER));\r
317         memcpy(&info,&from.info,sizeof(CXIMAGEINFO));\r
318 \r
319         pDib = from.pDib;\r
320         pSelection = from.pSelection;\r
321         pAlpha = from.pAlpha;\r
322         ppLayers = from.ppLayers;\r
323 \r
324         memset(&from.head,0,sizeof(BITMAPINFOHEADER));\r
325         memset(&from.info,0,sizeof(CXIMAGEINFO));\r
326         from.pDib = from.pSelection = from.pAlpha = NULL;\r
327         from.ppLayers = NULL;\r
328 \r
329         if (bTransferFrames){\r
330                 DestroyFrames();\r
331                 ppFrames = from.ppFrames;\r
332                 from.ppFrames = NULL;\r
333         }\r
334 \r
335         return true;\r
336 }\r
337 ////////////////////////////////////////////////////////////////////////////////\r
338 /**\r
339  * (this) points to the same pDib owned by (*from), the image remains in (*from)\r
340  * but (this) has the access to the pixels. <b>Use carefully !!!</b>\r
341  */\r
342 void CxImage::Ghost(const CxImage *from)\r
343 {\r
344         if (from){\r
345                 memcpy(&head,&from->head,sizeof(BITMAPINFOHEADER));\r
346                 memcpy(&info,&from->info,sizeof(CXIMAGEINFO));\r
347                 pDib = from->pDib;\r
348                 pSelection = from->pSelection;\r
349                 pAlpha = from->pAlpha;\r
350                 ppLayers = from->ppLayers;\r
351                 ppFrames = from->ppFrames;\r
352                 info.pGhost=(CxImage *)from;\r
353         }\r
354 }\r
355 ////////////////////////////////////////////////////////////////////////////////\r
356 /**\r
357  * turns a 16 or 32 bit bitfield image into a RGB image\r
358  */\r
359 void CxImage::Bitfield2RGB(BYTE *src, DWORD redmask, DWORD greenmask, DWORD bluemask, BYTE bpp)\r
360 {\r
361         switch (bpp){\r
362         case 16:\r
363         {\r
364                 DWORD ns[3]={0,0,0};\r
365                 // compute the number of shift for each mask\r
366                 for (int i=0;i<16;i++){\r
367                         if ((redmask>>i)&0x01) ns[0]++;\r
368                         if ((greenmask>>i)&0x01) ns[1]++;\r
369                         if ((bluemask>>i)&0x01) ns[2]++;\r
370                 }\r
371                 ns[1]+=ns[0]; ns[2]+=ns[1];     ns[0]=8-ns[0]; ns[1]-=8; ns[2]-=8;\r
372                 // dword aligned width for 16 bit image\r
373                 long effwidth2=(((head.biWidth + 1) / 2) * 4);\r
374                 WORD w;\r
375                 long y2,y3,x2,x3;\r
376                 BYTE *p=info.pImage;\r
377                 // scan the buffer in reverse direction to avoid reallocations\r
378                 for (long y=head.biHeight-1; y>=0; y--){\r
379                         y2=effwidth2*y;\r
380                         y3=info.dwEffWidth*y;\r
381                         for (long x=head.biWidth-1; x>=0; x--){\r
382                                 x2 = 2*x+y2;\r
383                                 x3 = 3*x+y3;\r
384                                 w = (WORD)(src[x2]+256*src[1+x2]);\r
385                                 p[  x3]=(BYTE)((w & bluemask)<<ns[0]);\r
386                                 p[1+x3]=(BYTE)((w & greenmask)>>ns[1]);\r
387                                 p[2+x3]=(BYTE)((w & redmask)>>ns[2]);\r
388                         }\r
389                 }\r
390                 break;\r
391         }\r
392         case 32:\r
393         {\r
394                 DWORD ns[3]={0,0,0};\r
395                 // compute the number of shift for each mask\r
396                 for (int i=8;i<32;i+=8){\r
397                         if (redmask>>i) ns[0]++;\r
398                         if (greenmask>>i) ns[1]++;\r
399                         if (bluemask>>i) ns[2]++;\r
400                 }\r
401                 // dword aligned width for 32 bit image\r
402                 long effwidth4 = head.biWidth * 4;\r
403                 long y4,y3,x4,x3;\r
404                 BYTE *p=info.pImage;\r
405                 // scan the buffer in reverse direction to avoid reallocations\r
406                 for (long y=head.biHeight-1; y>=0; y--){\r
407                         y4=effwidth4*y;\r
408                         y3=info.dwEffWidth*y;\r
409                         for (long x=head.biWidth-1; x>=0; x--){\r
410                                 x4 = 4*x+y4;\r
411                                 x3 = 3*x+y3;\r
412                                 p[  x3]=src[ns[2]+x4];\r
413                                 p[1+x3]=src[ns[1]+x4];\r
414                                 p[2+x3]=src[ns[0]+x4];\r
415                         }\r
416                 }\r
417         }\r
418 \r
419         }\r
420         return;\r
421 }\r
422 ////////////////////////////////////////////////////////////////////////////////\r
423 /**\r
424  * Creates an image from a generic buffer\r
425  * \param pArray: source memory buffer\r
426  * \param dwWidth: image width\r
427  * \param dwHeight: image height\r
428  * \param dwBitsperpixel: can be 1,4,8,24,32\r
429  * \param dwBytesperline: line alignment, in bytes, for a single row stored in pArray\r
430  * \param bFlipImage: tune this parameter if the image is upsidedown\r
431  * \return true if everything is ok\r
432  */\r
433 bool CxImage::CreateFromArray(BYTE* pArray,DWORD dwWidth,DWORD dwHeight,DWORD dwBitsperpixel, DWORD dwBytesperline, bool bFlipImage)\r
434 {\r
435         if (pArray==NULL) return false;\r
436         if (!((dwBitsperpixel==1)||(dwBitsperpixel==4)||(dwBitsperpixel==8)||\r
437                 (dwBitsperpixel==24)||(dwBitsperpixel==32))) return false;\r
438 \r
439         if (!Create(dwWidth,dwHeight,dwBitsperpixel)) return false;\r
440 \r
441         if (dwBitsperpixel<24) SetGrayPalette();\r
442 \r
443 #if CXIMAGE_SUPPORT_ALPHA\r
444         if (dwBitsperpixel==32) AlphaCreate();\r
445 #endif //CXIMAGE_SUPPORT_ALPHA\r
446 \r
447         BYTE *dst,*src;\r
448 \r
449         for (DWORD y = 0; y<dwHeight; y++) {\r
450                 dst = info.pImage + (bFlipImage?(dwHeight-1-y):y) * info.dwEffWidth;\r
451                 src = pArray + y * dwBytesperline;\r
452                 if (dwBitsperpixel==32){\r
453                         for(DWORD x=0;x<dwWidth;x++){\r
454                                 *dst++=src[0];\r
455                                 *dst++=src[1];\r
456                                 *dst++=src[2];\r
457 #if CXIMAGE_SUPPORT_ALPHA\r
458                                 AlphaSet(x,(bFlipImage?(dwHeight-1-y):y),src[3]);\r
459 #endif //CXIMAGE_SUPPORT_ALPHA\r
460                                 src+=4;\r
461                         }\r
462                 } else {\r
463                         memcpy(dst,src,min(info.dwEffWidth,dwBytesperline));\r
464                 }\r
465         }\r
466         return true;\r
467 }\r
468 ////////////////////////////////////////////////////////////////////////////////\r
469 /**\r
470  * \sa CreateFromArray\r
471  */\r
472 bool CxImage::CreateFromMatrix(BYTE** ppMatrix,DWORD dwWidth,DWORD dwHeight,DWORD dwBitsperpixel, DWORD dwBytesperline, bool bFlipImage)\r
473 {\r
474         if (ppMatrix==NULL) return false;\r
475         if (!((dwBitsperpixel==1)||(dwBitsperpixel==4)||(dwBitsperpixel==8)||\r
476                 (dwBitsperpixel==24)||(dwBitsperpixel==32))) return false;\r
477 \r
478         if (!Create(dwWidth,dwHeight,dwBitsperpixel)) return false;\r
479 \r
480         if (dwBitsperpixel<24) SetGrayPalette();\r
481 \r
482 #if CXIMAGE_SUPPORT_ALPHA\r
483         if (dwBitsperpixel==32) AlphaCreate();\r
484 #endif //CXIMAGE_SUPPORT_ALPHA\r
485 \r
486         BYTE *dst,*src;\r
487 \r
488         for (DWORD y = 0; y<dwHeight; y++) {\r
489                 dst = info.pImage + (bFlipImage?(dwHeight-1-y):y) * info.dwEffWidth;\r
490                 src = ppMatrix[y];\r
491                 if (src){\r
492                         if (dwBitsperpixel==32){\r
493                                 for(DWORD x=0;x<dwWidth;x++){\r
494                                         *dst++=src[0];\r
495                                         *dst++=src[1];\r
496                                         *dst++=src[2];\r
497 #if CXIMAGE_SUPPORT_ALPHA\r
498                                         AlphaSet(x,(bFlipImage?(dwHeight-1-y):y),src[3]);\r
499 #endif //CXIMAGE_SUPPORT_ALPHA\r
500                                         src+=4;\r
501                                 }\r
502                         } else {\r
503                                 memcpy(dst,src,min(info.dwEffWidth,dwBytesperline));\r
504                         }\r
505                 }\r
506         }\r
507         return true;\r
508 }\r
509 ////////////////////////////////////////////////////////////////////////////////\r
510 /**\r
511  * \return lightness difference between elem1 and elem2\r
512  */\r
513 int CxImage::CompareColors(const void *elem1, const void *elem2)\r
514 {\r
515         RGBQUAD* c1 = (RGBQUAD*)elem1;\r
516         RGBQUAD* c2 = (RGBQUAD*)elem2;\r
517 \r
518         int g1 = (int)RGB2GRAY(c1->rgbRed,c1->rgbGreen,c1->rgbBlue);\r
519         int g2 = (int)RGB2GRAY(c2->rgbRed,c2->rgbGreen,c2->rgbBlue);\r
520         \r
521         return (g1-g2);\r
522 }\r
523 ////////////////////////////////////////////////////////////////////////////////\r
524 /**\r
525  * simply calls "if (memblock) free(memblock);".\r
526  * Useful when calling Encode for a memory buffer,\r
527  * from a DLL compiled with different memory management options.\r
528  * CxImage::FreeMemory will use the same memory environment used by Encode. \r
529  * \author [livecn]\r
530  */\r
531 void CxImage::FreeMemory(void* memblock)\r
532 {\r
533         if (memblock)\r
534                 free(memblock);\r
535 }\r
536 ////////////////////////////////////////////////////////////////////////////////\r
537 //EOF\r