]> Creatis software - clitk.git/blob - utilities/CxImage/ximatga.cpp
cosmetic
[clitk.git] / utilities / CxImage / ximatga.cpp
1 /*
2  * File:        ximatga.cpp
3  * Purpose:     Platform Independent TGA Image Class Loader and Writer
4  * 05/Jan/2001 Davide Pizzolato - www.xdp.it
5  * CxImage version 6.0.0 02/Feb/2008
6  */
7
8 #include "ximatga.h"
9
10 #if CXIMAGE_SUPPORT_TGA
11
12 #include "ximaiter.h"
13
14 // Definitions for image types.
15 #define TGA_Null 0
16 #define TGA_Map 1
17 #define TGA_RGB 2
18 #define TGA_Mono 3
19 #define TGA_RLEMap 9
20 #define TGA_RLERGB 10
21 #define TGA_RLEMono 11
22 #define TGA_CompMap 32
23 #define TGA_CompMap4 33
24
25 ////////////////////////////////////////////////////////////////////////////////
26 #if CXIMAGE_SUPPORT_DECODE
27 ////////////////////////////////////////////////////////////////////////////////
28 bool CxImageTGA::Decode(CxFile *hFile)
29 {
30         if (hFile == NULL) return false;
31
32         TGAHEADER tgaHead;
33
34   cx_try
35   {
36         if (hFile->Read(&tgaHead,sizeof(tgaHead),1)==0)
37                 cx_throw("Not a TGA");
38
39         tga_toh(&tgaHead);
40
41         bool bCompressed;
42         switch (tgaHead.ImageType){
43         case TGA_Map:
44         case TGA_RGB:
45         case TGA_Mono:
46                 bCompressed = false;
47                 break;
48         case TGA_RLEMap:
49         case TGA_RLERGB:
50         case TGA_RLEMono:
51                 bCompressed = true;
52                 break;
53         default:
54                 cx_throw("Unknown TGA image type");
55         }
56
57         if (tgaHead.ImageWidth==0 || tgaHead.ImageHeight==0 || tgaHead.PixelDepth==0 || tgaHead.CmapLength>256)
58                 cx_throw("bad TGA header");
59
60         if (tgaHead.PixelDepth!=8 && tgaHead.PixelDepth!=15 && tgaHead.PixelDepth!=16 && tgaHead.PixelDepth!=24 && tgaHead.PixelDepth!=32)
61                 cx_throw("bad TGA header");
62
63         if (info.nEscape == -1){
64                 head.biWidth = tgaHead.ImageWidth ;
65                 head.biHeight= tgaHead.ImageHeight;
66                 info.dwType = CXIMAGE_FORMAT_TGA;
67                 return true;
68         }
69
70         if (tgaHead.IdLength>0) hFile->Seek(tgaHead.IdLength,SEEK_CUR); //skip descriptor
71
72         Create(tgaHead.ImageWidth, tgaHead.ImageHeight, tgaHead.PixelDepth, CXIMAGE_FORMAT_TGA);
73 #if CXIMAGE_SUPPORT_ALPHA       // <vho>
74         if (tgaHead.PixelDepth==32) AlphaCreate(); // Image has alpha channel
75 #endif //CXIMAGE_SUPPORT_ALPHA
76
77         if (!IsValid()) cx_throw("TGA Create failed");
78         
79         if (info.nEscape) cx_throw("Cancelled"); // <vho> - cancel decoding
80
81         if (tgaHead.CmapType != 0){ // read the palette
82                 rgb_color pal[256];
83                 hFile->Read(pal,tgaHead.CmapLength*sizeof(rgb_color), 1);
84                 for (int i=0;i<tgaHead.CmapLength; i++) SetPaletteColor((BYTE)i,pal[i].b,pal[i].g,pal[i].r);
85         }
86
87         if (tgaHead.ImageType == TGA_Mono || tgaHead.ImageType == TGA_RLEMono)
88                 SetGrayPalette();
89
90         // Bits 4 & 5 of the Image Descriptor byte control the ordering of the pixels.
91         bool bXReversed = ((tgaHead.ImagDesc & 16) == 16);
92         bool bYReversed = ((tgaHead.ImagDesc & 32) == 32);
93
94     CImageIterator iter(this);
95         BYTE rleLeftover = 255; //for images with illegal packet boundary 
96         BYTE* pDest;
97     for (int y=0; y < tgaHead.ImageHeight; y++){
98
99                 if (info.nEscape) cx_throw("Cancelled"); // <vho> - cancel decoding
100
101                 if (hFile == NULL || hFile->Eof()) cx_throw("corrupted TGA");
102
103                 if (bYReversed) pDest = iter.GetRow(tgaHead.ImageHeight-y-1);
104                 else pDest = iter.GetRow(y);
105
106                 if (bCompressed) rleLeftover = ExpandCompressedLine(pDest,&tgaHead,hFile,tgaHead.ImageWidth,y,rleLeftover);
107                 else ExpandUncompressedLine  (pDest,&tgaHead,hFile,tgaHead.ImageWidth,y,0);
108     }
109
110         if (bXReversed) Mirror();
111
112 #if CXIMAGE_SUPPORT_ALPHA
113         if (bYReversed && tgaHead.PixelDepth==32) AlphaFlip(); //<lioucr>
114 #endif //CXIMAGE_SUPPORT_ALPHA
115
116   } cx_catch {
117         if (strcmp(message,"")) strncpy(info.szLastError,message,255);
118         return false;
119   }
120     return true;
121 }
122 ////////////////////////////////////////////////////////////////////////////////
123 #endif //CXIMAGE_SUPPORT_DECODE
124 ////////////////////////////////////////////////////////////////////////////////
125 #if CXIMAGE_SUPPORT_ENCODE
126 ////////////////////////////////////////////////////////////////////////////////
127 bool CxImageTGA::Encode(CxFile * hFile)
128 {
129         if (EncodeSafeCheck(hFile)) return false;
130
131         if (head.biBitCount<8){
132                 strcpy(info.szLastError,"Bit depth must be 8 or 24");
133                 return false;
134         }
135
136         TGAHEADER tgaHead;
137
138     tgaHead.IdLength = 0;                               // Image ID Field Length
139     tgaHead.CmapType = GetPalette()!=0; // Color Map Type
140     tgaHead.ImageType = (head.biBitCount == 8) ? (BYTE)TGA_Map : (BYTE)TGA_RGB; // Image Type
141
142     tgaHead.CmapIndex=0;                                // First Entry Index
143     tgaHead.CmapLength=(head.biBitCount == 8) ? 256 : 0;   // Color Map Length
144     tgaHead.CmapEntrySize=(head.biBitCount == 8) ? (BYTE)24 : (BYTE)0; // Color Map Entry Size
145
146     tgaHead.X_Origin=0;                                 // X-origin of Image
147     tgaHead.Y_Origin=0;                                 // Y-origin of Image
148     tgaHead.ImageWidth=(WORD)head.biWidth;              // Image Width
149     tgaHead.ImageHeight=(WORD)head.biHeight;    // Image Height
150     tgaHead.PixelDepth=(BYTE)head.biBitCount;   // Pixel Depth
151     tgaHead.ImagDesc=0;                                 // Image Descriptor
152
153         if (pAlpha && head.biBitCount==24) tgaHead.PixelDepth=32; 
154
155         tga_toh(&tgaHead);
156         hFile->Write(&tgaHead,sizeof(TGAHEADER),1);
157         tga_toh(&tgaHead);
158
159         if (head.biBitCount==8){
160                 rgb_color pal[256];
161                 RGBQUAD* ppal = GetPalette();
162                 for (int i=0;i<256; i++){
163                         pal[i].r = ppal[i].rgbBlue;
164                         pal[i].g = ppal[i].rgbGreen;
165                         pal[i].b = ppal[i].rgbRed;
166                 }
167                 hFile->Write(&pal,256*sizeof(rgb_color),1);
168         }
169         
170         CImageIterator iter(this);
171         BYTE* pDest;
172         if (pAlpha==0 || head.biBitCount==8){
173                 for (int y=0; y < tgaHead.ImageHeight; y++){
174                         pDest = iter.GetRow(y);
175                         hFile->Write(pDest,tgaHead.ImageWidth * (head.biBitCount >> 3),1);
176                 }
177         } else {
178                 pDest = (BYTE*)malloc(4*tgaHead.ImageWidth);
179                 RGBQUAD c;
180                 for (int y=0; y < tgaHead.ImageHeight; y++){
181                         for(int x=0, x4=0;x<tgaHead.ImageWidth;x++, x4+=4){
182                                 c = BlindGetPixelColor(x,y);
183                                 pDest[x4+0]=c.rgbBlue;
184                                 pDest[x4+1]=c.rgbGreen;
185                                 pDest[x4+2]=c.rgbRed;
186 #if CXIMAGE_SUPPORT_ALPHA       // <vho>
187                                 pDest[x4+3]=AlphaGet(x,y);
188 #else
189                                 pDest[x4+3]=0;
190 #endif //CXIMAGE_SUPPORT_ALPHA
191                         }
192                         hFile->Write(pDest,4*tgaHead.ImageWidth,1);
193                 }
194                 free(pDest);
195         }
196         return true;
197 }
198 ////////////////////////////////////////////////////////////////////////////////
199 #endif // CXIMAGE_SUPPORT_ENCODE
200 ////////////////////////////////////////////////////////////////////////////////
201 BYTE CxImageTGA::ExpandCompressedLine(BYTE* pDest,TGAHEADER* ptgaHead,CxFile *hFile,int width, int y, BYTE rleLeftover)
202 {
203         BYTE rle;
204         long filePos=0;
205         for (int x=0; x<width; ){
206                 if (rleLeftover != 255){
207             rle = rleLeftover;
208             rleLeftover = 255;
209         } else {
210                         hFile->Read(&rle,1,1);
211                 }
212                 if (rle & 128) { // RLE-Encoded packet
213                         rle -= 127; // Calculate real repeat count.
214                         if ((x+rle)>width){
215                                 rleLeftover = (BYTE)(128 + (rle - (width - x) - 1));
216                 filePos = hFile->Tell();
217                                 rle = (BYTE)(width - x);
218                         }
219                         switch (ptgaHead->PixelDepth)
220                         {
221                         case 32: {
222                                 RGBQUAD color;
223                                 hFile->Read(&color,4,1);
224                                 for (int ix = 0; ix < rle; ix++){
225                                         memcpy(&pDest[3*ix],&color,3);
226 #if CXIMAGE_SUPPORT_ALPHA       // <vho>
227                                         AlphaSet(ix+x,y,color.rgbReserved);
228 #endif //CXIMAGE_SUPPORT_ALPHA
229                                 }
230                                 break;
231                                          } 
232                         case 24: {
233                                 rgb_color triple;
234                                 hFile->Read(&triple,3,1);
235                                 for (int ix = 0; ix < rle; ix++) memcpy(&pDest[3*ix],&triple,3);
236                                 break;
237                                          }
238                         case 15:
239                         case 16: {
240                                 WORD pixel;
241                                 hFile->Read(&pixel,2,1);
242                                 rgb_color triple;
243                                 triple.r = (BYTE)(( pixel & 0x1F ) * 8);     // red
244                                 triple.g = (BYTE)(( pixel >> 2 ) & 0x0F8);   // green
245                                 triple.b = (BYTE)(( pixel >> 7 ) & 0x0F8);   // blue
246                                 for (int ix = 0; ix < rle; ix++){
247                                         memcpy(&pDest[3*ix],&triple,3);
248                                 }
249                                 break;
250                                          }
251                         case 8: {
252                                 BYTE pixel;
253                                 hFile->Read(&pixel,1,1);
254                                 for (int ix = 0; ix < rle; ix++) pDest[ix] = pixel;
255                                         }
256                         }
257                         if (rleLeftover!=255) hFile->Seek(filePos, SEEK_SET);
258                 } else { // Raw packet
259                         rle += 1; // Calculate real repeat count.
260                         if ((x+rle)>width){
261                 rleLeftover = (BYTE)(rle - (width - x) - 1);
262                                 rle = (BYTE)(width - x);
263                         }
264                         ExpandUncompressedLine(pDest,ptgaHead,hFile,rle,y,x);
265                 }
266                 if (head.biBitCount == 24)      pDest += rle*3; else pDest += rle;
267                 x += rle;
268         }
269         return rleLeftover;
270 }
271 ////////////////////////////////////////////////////////////////////////////////
272 void CxImageTGA::ExpandUncompressedLine(BYTE* pDest,TGAHEADER* ptgaHead,CxFile *hFile,int width, int y, int xoffset)
273 {
274         switch (ptgaHead->PixelDepth){
275         case 8:
276                 hFile->Read(pDest,width,1);
277                 break;
278         case 15:
279         case 16:{
280                 BYTE* dst=pDest;
281                 WORD pixel;
282                 for (int x=0; x<width; x++){
283                         hFile->Read(&pixel,2,1);
284                         *dst++ = (BYTE)(( pixel & 0x1F ) * 8);     // blue
285                         *dst++ = (BYTE)(( pixel >> 2 ) & 0x0F8);   // green
286                         *dst++ = (BYTE)(( pixel >> 7 ) & 0x0F8);   // red
287                 }
288                 break;
289                         }
290         case 24:
291                 hFile->Read(pDest,3*width,1);
292                 break;
293         case 32:{
294                 BYTE* dst=pDest;
295                 for (int x=0; x<width; x++){
296                         RGBQUAD pixel;
297                         hFile->Read(&pixel,4,1);
298                         *dst++ = pixel.rgbBlue;
299                         *dst++ = pixel.rgbGreen;
300                         *dst++ = pixel.rgbRed;
301 #if CXIMAGE_SUPPORT_ALPHA       // <vho>
302                         AlphaSet(x+xoffset,y,pixel.rgbReserved); //alpha
303 #endif //CXIMAGE_SUPPORT_ALPHA
304                 }
305                 break;
306                         }
307         }
308 }
309 ////////////////////////////////////////////////////////////////////////////////
310 void CxImageTGA::tga_toh(TGAHEADER* p)
311 {
312     p->CmapIndex = ntohs(p->CmapIndex);
313     p->CmapLength = ntohs(p->CmapLength);
314     p->X_Origin = ntohs(p->X_Origin);
315     p->Y_Origin = ntohs(p->Y_Origin);
316     p->ImageWidth = ntohs(p->ImageWidth);
317     p->ImageHeight = ntohs(p->ImageHeight);
318 }
319 ////////////////////////////////////////////////////////////////////////////////
320 #endif  // CXIMAGE_SUPPORT_TGA
321