[95] | 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 |
|
---|