00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "tga.h"
00022
00023 #include <assert.h>
00024
00025 #include <qimage.h>
00026 #include <qdatastream.h>
00027
00028 #include <kdebug.h>
00029
00030 typedef Q_UINT32 uint;
00031 typedef Q_UINT16 ushort;
00032 typedef Q_UINT8 uchar;
00033
00034 namespace {
00035
00036
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 };
00075 };
00076
00077 static QDataStream & operator>> ( QDataStream & 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
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
00154 case TGA_TYPE_RGB:
00155 rgb = true;
00156 break;
00157
00158 case TGA_TYPE_RLE_GREY:
00159 rle = true;
00160
00161 case TGA_TYPE_GREY:
00162 grey = true;
00163 break;
00164
00165 default:
00166
00167 supported = false;
00168 }
00169 }
00170 };
00171
00172 static bool LoadTGA( QDataStream & s, const TgaHeader & tga, QImage &img )
00173 {
00174
00175 if( !img.create( tga.width, tga.height, 32 )) {
00176 return false;
00177 }
00178
00179 TgaHeaderInfo info(tga);
00180 if( !info.supported ) {
00181
00182 kdDebug(399) << "This TGA file is not supported." << endl;
00183 return false;
00184 }
00185
00186
00187 const int numAlphaBits = tga.flags & 0xf;
00188
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
00204 char palette[768];
00205 if( info.pal ) {
00206
00207 s.readRawBytes( palette, 3 * tga.colormap_length );
00208 }
00209
00210
00211 uchar * const image = new uchar[size];
00212
00213 if( info.rle ) {
00214
00215 char * dst = (char *)image;
00216 int num = size;
00217
00218 while (num > 0) {
00219
00220 uchar c;
00221 s >> c;
00222
00223 uint count = (c & 0x7f) + 1;
00224 num -= count * pixel_size;
00225
00226 if (c & 0x80) {
00227
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
00238 count *= pixel_size;
00239 s.readRawBytes( dst, count );
00240 dst += count;
00241 }
00242 }
00243 }
00244 else {
00245
00246 s.readRawBytes( (char *)image, size );
00247 }
00248
00249
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
00269 for( int x = 0; x < tga.width; x++ ) {
00270 uchar idx = *src++;
00271 scanline[x] = qRgb( palette[3*idx+2], palette[3*idx+1], palette[3*idx+0] );
00272 }
00273 }
00274 else if( info.grey ) {
00275
00276 for( int x = 0; x < tga.width; x++ ) {
00277 scanline[x] = qRgb( *src, *src, *src );
00278 src++;
00279 }
00280 }
00281 else {
00282
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] = qRgb( (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] = qRgb( 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
00299 const uchar alpha = ( src[3] << ( 8 - numAlphaBits ) );
00300 scanline[x] = qRgba( src[2], src[1], src[0], alpha );
00301 src += 4;
00302 }
00303 }
00304 }
00305 }
00306
00307
00308 delete [] image;
00309
00310 return true;
00311 }
00312
00313 }
00314
00315
00316 KDE_EXPORT void kimgio_tga_read( QImageIO *io )
00317 {
00318
00319
00320 QDataStream s( io->ioDevice() );
00321 s.setByteOrder( QDataStream::LittleEndian );
00322
00323
00324
00325 TgaHeader tga;
00326 s >> tga;
00327 s.device()->at( TgaHeader::SIZE + tga.id_length );
00328
00329
00330 if( s.atEnd() ) {
00331 kdDebug(399) << "This TGA file is not valid." << endl;
00332 io->setImage( 0 );
00333 io->setStatus( -1 );
00334 return;
00335 }
00336
00337
00338 if( !IsSupported(tga) ) {
00339 kdDebug(399) << "This TGA file is not supported." << endl;
00340 io->setImage( 0 );
00341 io->setStatus( -1 );
00342 return;
00343 }
00344
00345
00346 QImage img;
00347 bool result = LoadTGA(s, tga, img);
00348
00349 if( result == false ) {
00350 kdDebug(399) << "Error loading TGA file." << endl;
00351 io->setImage( 0 );
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( QImageIO *io )
00363 {
00364 QDataStream s( io->ioDevice() );
00365 s.setByteOrder( QDataStream::LittleEndian );
00366
00367 const QImage img = io->image();
00368 const bool hasAlpha = img.hasAlphaBuffer();
00369 for( int i = 0; i < 12; i++ )
00370 s << targaMagic[i];
00371
00372
00373 s << Q_UINT16( img.width() );
00374 s << Q_UINT16( img.height() );
00375 s << Q_UINT8( hasAlpha ? 32 : 24 );
00376 s << Q_UINT8( hasAlpha ? 0x24 : 0x20 );
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 << Q_UINT8( qBlue( color ) );
00382 s << Q_UINT8( qGreen( color ) );
00383 s << Q_UINT8( qRed( color ) );
00384 if( hasAlpha )
00385 s << Q_UINT8( qAlpha( color ) );
00386 }
00387
00388 io->setStatus( 0 );
00389 }
00390