tga.cpp
00001 /* This file is part of the KDE project 00002 Copyright (C) 2003 Dominik Seichter <domseichter@web.de> 00003 Copyright (C) 2004 Ignacio Castaņo <castano@ludicon.com> 00004 00005 This program is free software; you can redistribute it and/or 00006 modify it under the terms of the Lesser GNU General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 */ 00010 00011 /* this code supports: 00012 * reading: 00013 * uncompressed and run length encoded indexed, grey and color tga files. 00014 * image types 1, 2, 3, 9, 10 and 11. 00015 * only RGB color maps with no more than 256 colors. 00016 * pixel formats 8, 15, 24 and 32. 00017 * writing: 00018 * uncompressed true color tga files 00019 */ 00020 00021 #include "tga.h" 00022 00023 #include <assert.h> 00024 00025 #include <tqimage.h> 00026 #include <tqdatastream.h> 00027 00028 #include <kdebug.h> 00029 00030 typedef TQ_UINT32 uint; 00031 typedef TQ_UINT16 ushort; 00032 typedef TQ_UINT8 uchar; 00033 00034 namespace { // Private. 00035 00036 // Header format of saved files. 00037 uchar targaMagic[12] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 00038 00039 enum TGAType { 00040 TGA_TYPE_INDEXED = 1, 00041 TGA_TYPE_RGB = 2, 00042 TGA_TYPE_GREY = 3, 00043 TGA_TYPE_RLE_INDEXED = 9, 00044 TGA_TYPE_RLE_RGB = 10, 00045 TGA_TYPE_RLE_GREY = 11 00046 }; 00047 00048 #define TGA_INTERLEAVE_MASK 0xc0 00049 #define TGA_INTERLEAVE_NONE 0x00 00050 #define TGA_INTERLEAVE_2WAY 0x40 00051 #define TGA_INTERLEAVE_4WAY 0x80 00052 00053 #define TGA_ORIGIN_MASK 0x30 00054 #define TGA_ORIGIN_LEFT 0x00 00055 #define TGA_ORIGIN_RIGHT 0x10 00056 #define TGA_ORIGIN_LOWER 0x00 00057 #define TGA_ORIGIN_UPPER 0x20 00058 00060 struct TgaHeader { 00061 uchar id_length; 00062 uchar colormap_type; 00063 uchar image_type; 00064 ushort colormap_index; 00065 ushort colormap_length; 00066 uchar colormap_size; 00067 ushort x_origin; 00068 ushort y_origin; 00069 ushort width; 00070 ushort height; 00071 uchar pixel_size; 00072 uchar flags; 00073 00074 enum { SIZE = 18 }; // const static int SIZE = 18; 00075 }; 00076 00077 static TQDataStream & operator>> ( TQDataStream & s, TgaHeader & head ) 00078 { 00079 s >> head.id_length; 00080 s >> head.colormap_type; 00081 s >> head.image_type; 00082 s >> head.colormap_index; 00083 s >> head.colormap_length; 00084 s >> head.colormap_size; 00085 s >> head.x_origin; 00086 s >> head.y_origin; 00087 s >> head.width; 00088 s >> head.height; 00089 s >> head.pixel_size; 00090 s >> head.flags; 00091 return s; 00092 } 00093 00094 static bool IsSupported( const TgaHeader & head ) 00095 { 00096 if( head.image_type != TGA_TYPE_INDEXED && 00097 head.image_type != TGA_TYPE_RGB && 00098 head.image_type != TGA_TYPE_GREY && 00099 head.image_type != TGA_TYPE_RLE_INDEXED && 00100 head.image_type != TGA_TYPE_RLE_RGB && 00101 head.image_type != TGA_TYPE_RLE_GREY ) 00102 { 00103 return false; 00104 } 00105 if( head.image_type == TGA_TYPE_INDEXED || 00106 head.image_type == TGA_TYPE_RLE_INDEXED ) 00107 { 00108 if( head.colormap_length > 256 || head.colormap_size != 24 ) 00109 { 00110 return false; 00111 } 00112 } 00113 if( head.width == 0 || head.height == 0 ) 00114 { 00115 return false; 00116 } 00117 if( head.pixel_size != 8 && head.pixel_size != 16 && 00118 head.pixel_size != 24 && head.pixel_size != 32 ) 00119 { 00120 return false; 00121 } 00122 return true; 00123 } 00124 00125 struct Color555 { 00126 ushort b : 5; 00127 ushort g : 5; 00128 ushort r : 5; 00129 }; 00130 00131 struct TgaHeaderInfo { 00132 bool rle; 00133 bool pal; 00134 bool rgb; 00135 bool grey; 00136 bool supported; 00137 00138 TgaHeaderInfo( const TgaHeader & tga ) : rle(false), pal(false), rgb(false), grey(false), supported(true) 00139 { 00140 switch( tga.image_type ) { 00141 case TGA_TYPE_RLE_INDEXED: 00142 rle = true; 00143 // no break is intended! 00144 case TGA_TYPE_INDEXED: 00145 if( tga.colormap_type!=1 || tga.colormap_size!=24 || tga.colormap_length>256 ) { 00146 supported = false; 00147 } 00148 pal = true; 00149 break; 00150 00151 case TGA_TYPE_RLE_RGB: 00152 rle = true; 00153 // no break is intended! 00154 case TGA_TYPE_RGB: 00155 rgb = true; 00156 break; 00157 00158 case TGA_TYPE_RLE_GREY: 00159 rle = true; 00160 // no break is intended! 00161 case TGA_TYPE_GREY: 00162 grey = true; 00163 break; 00164 00165 default: 00166 // Error, unknown image type. 00167 supported = false; 00168 } 00169 } 00170 }; 00171 00172 static bool LoadTGA( TQDataStream & s, const TgaHeader & tga, TQImage &img ) 00173 { 00174 // Create image. 00175 if( !img.create( tga.width, tga.height, 32 )) { 00176 return false; 00177 } 00178 00179 TgaHeaderInfo info(tga); 00180 if( !info.supported ) { 00181 // File not supported. 00182 kdDebug(399) << "This TGA file is not supported." << endl; 00183 return false; 00184 } 00185 00186 // Bits 0-3 are the numbers of alpha bits (can be zero!) 00187 const int numAlphaBits = tga.flags & 0xf; 00188 // However alpha exists only in the 32 bit format. 00189 if( ( tga.pixel_size == 32 ) && ( tga.flags & 0xf ) ) { 00190 img.setAlphaBuffer( true ); 00191 } 00192 00193 uint pixel_size = (tga.pixel_size/8); 00194 uint size = tga.width * tga.height * pixel_size; 00195 00196 if (size < 1) 00197 { 00198 kdDebug(399) << "This TGA file is broken with size " << size << endl; 00199 return false; 00200 } 00201 00202 00203 // Read palette. 00204 char palette[768]; 00205 if( info.pal ) { 00206 // @todo Support palettes in other formats! 00207 s.readRawBytes( palette, 3 * tga.colormap_length ); 00208 } 00209 00210 // Allocate image. 00211 uchar * const image = new uchar[size]; 00212 00213 if( info.rle ) { 00214 // Decode image. 00215 char * dst = (char *)image; 00216 int num = size; 00217 00218 while (num > 0) { 00219 // Get packet header. 00220 uchar c; 00221 s >> c; 00222 00223 uint count = (c & 0x7f) + 1; 00224 num -= count * pixel_size; 00225 00226 if (c & 0x80) { 00227 // RLE pixels. 00228 assert(pixel_size <= 8); 00229 char pixel[8]; 00230 s.readRawBytes( pixel, pixel_size ); 00231 do { 00232 memcpy(dst, pixel, pixel_size); 00233 dst += pixel_size; 00234 } while (--count); 00235 } 00236 else { 00237 // Raw pixels. 00238 count *= pixel_size; 00239 s.readRawBytes( dst, count ); 00240 dst += count; 00241 } 00242 } 00243 } 00244 else { 00245 // Read raw image. 00246 s.readRawBytes( (char *)image, size ); 00247 } 00248 00249 // Convert image to internal format. 00250 int y_start, y_step, y_end; 00251 if( tga.flags & TGA_ORIGIN_UPPER ) { 00252 y_start = 0; 00253 y_step = 1; 00254 y_end = tga.height; 00255 } 00256 else { 00257 y_start = tga.height - 1; 00258 y_step = -1; 00259 y_end = -1; 00260 } 00261 00262 uchar * src = image; 00263 00264 for( int y = y_start; y != y_end; y += y_step ) { 00265 QRgb * scanline = (QRgb *) img.scanLine( y ); 00266 00267 if( info.pal ) { 00268 // Paletted. 00269 for( int x = 0; x < tga.width; x++ ) { 00270 uchar idx = *src++; 00271 scanline[x] = tqRgb( palette[3*idx+2], palette[3*idx+1], palette[3*idx+0] ); 00272 } 00273 } 00274 else if( info.grey ) { 00275 // Greyscale. 00276 for( int x = 0; x < tga.width; x++ ) { 00277 scanline[x] = tqRgb( *src, *src, *src ); 00278 src++; 00279 } 00280 } 00281 else { 00282 // True Color. 00283 if( tga.pixel_size == 16 ) { 00284 for( int x = 0; x < tga.width; x++ ) { 00285 Color555 c = *reinterpret_cast<Color555 *>(src); 00286 scanline[x] = tqRgb( (c.r << 3) | (c.r >> 2), (c.g << 3) | (c.g >> 2), (c.b << 3) | (c.b >> 2) ); 00287 src += 2; 00288 } 00289 } 00290 else if( tga.pixel_size == 24 ) { 00291 for( int x = 0; x < tga.width; x++ ) { 00292 scanline[x] = tqRgb( src[2], src[1], src[0] ); 00293 src += 3; 00294 } 00295 } 00296 else if( tga.pixel_size == 32 ) { 00297 for( int x = 0; x < tga.width; x++ ) { 00298 // ### TODO: verify with images having really some alpha data 00299 const uchar alpha = ( src[3] << ( 8 - numAlphaBits ) ); 00300 scanline[x] = tqRgba( src[2], src[1], src[0], alpha ); 00301 src += 4; 00302 } 00303 } 00304 } 00305 } 00306 00307 // Free image. 00308 delete [] image; 00309 00310 return true; 00311 } 00312 00313 } // namespace 00314 00315 00316 KDE_EXPORT void kimgio_tga_read( TQImageIO *io ) 00317 { 00318 //kdDebug(399) << "Loading TGA file!" << endl; 00319 00320 TQDataStream s( io->ioDevice() ); 00321 s.setByteOrder( TQDataStream::LittleEndian ); 00322 00323 00324 // Read image header. 00325 TgaHeader tga; 00326 s >> tga; 00327 s.device()->at( TgaHeader::SIZE + tga.id_length ); 00328 00329 // Check image file format. 00330 if( s.atEnd() ) { 00331 kdDebug(399) << "This TGA file is not valid." << endl; 00332 io->setImage( TQImage() ); 00333 io->setStatus( -1 ); 00334 return; 00335 } 00336 00337 // Check supported file types. 00338 if( !IsSupported(tga) ) { 00339 kdDebug(399) << "This TGA file is not supported." << endl; 00340 io->setImage( TQImage() ); 00341 io->setStatus( -1 ); 00342 return; 00343 } 00344 00345 00346 TQImage img; 00347 bool result = LoadTGA(s, tga, img); 00348 00349 if( result == false ) { 00350 kdDebug(399) << "Error loading TGA file." << endl; 00351 io->setImage( TQImage() ); 00352 io->setStatus( -1 ); 00353 return; 00354 } 00355 00356 00357 io->setImage( img ); 00358 io->setStatus( 0 ); 00359 } 00360 00361 00362 KDE_EXPORT void kimgio_tga_write( TQImageIO *io ) 00363 { 00364 TQDataStream s( io->ioDevice() ); 00365 s.setByteOrder( TQDataStream::LittleEndian ); 00366 00367 const TQImage img = io->image(); 00368 const bool hasAlpha = img.hasAlphaBuffer(); 00369 for( int i = 0; i < 12; i++ ) 00370 s << targaMagic[i]; 00371 00372 // write header 00373 s << TQ_UINT16( img.width() ); // width 00374 s << TQ_UINT16( img.height() ); // height 00375 s << TQ_UINT8( hasAlpha ? 32 : 24 ); // depth (24 bit RGB + 8 bit alpha) 00376 s << TQ_UINT8( hasAlpha ? 0x24 : 0x20 ); // top left image (0x20) + 8 bit alpha (0x4) 00377 00378 for( int y = 0; y < img.height(); y++ ) 00379 for( int x = 0; x < img.width(); x++ ) { 00380 const QRgb color = img.pixel( x, y ); 00381 s << TQ_UINT8( tqBlue( color ) ); 00382 s << TQ_UINT8( tqGreen( color ) ); 00383 s << TQ_UINT8( tqRed( color ) ); 00384 if( hasAlpha ) 00385 s << TQ_UINT8( tqAlpha( color ) ); 00386 } 00387 00388 io->setStatus( 0 ); 00389 } 00390