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 <QtGui/QImage>
00026 #include <QtCore/QDataStream>
00027
00028 #include <kdebug.h>
00029
00030 typedef quint32 uint;
00031 typedef quint16 ushort;
00032 typedef quint8 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
00173
00174 static bool LoadTGA( QDataStream & s, const TgaHeader & tga, QImage &img )
00175 {
00176
00177 img = QImage( tga.width, tga.height, QImage::Format_RGB32 );
00178
00179 TgaHeaderInfo info(tga);
00180 if( !info.supported ) {
00181
00182 kDebug(399) << "This TGA file is not supported.";
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 = QImage( tga.width, tga.height, QImage::Format_ARGB32 );
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 kDebug(399) << "This TGA file is broken with size " << size;
00199 return false;
00200 }
00201
00202
00203 char palette[768];
00204 if( info.pal ) {
00205
00206 s.readRawData( palette, 3 * tga.colormap_length );
00207 }
00208
00209
00210 uchar * const image = new uchar[size];
00211
00212 if( info.rle ) {
00213
00214 char * dst = (char *)image;
00215 int num = size;
00216
00217 while (num > 0) {
00218
00219 uchar c;
00220 s >> c;
00221
00222 uint count = (c & 0x7f) + 1;
00223 num -= count * pixel_size;
00224
00225 if (c & 0x80) {
00226
00227 assert(pixel_size <= 8);
00228 char pixel[8];
00229 s.readRawData( pixel, pixel_size );
00230 do {
00231 memcpy(dst, pixel, pixel_size);
00232 dst += pixel_size;
00233 } while (--count);
00234 }
00235 else {
00236
00237 count *= pixel_size;
00238 s.readRawData( dst, count );
00239 dst += count;
00240 }
00241 }
00242 }
00243 else {
00244
00245 s.readRawData( (char *)image, size );
00246 }
00247
00248
00249 int y_start, y_step, y_end;
00250 if( tga.flags & TGA_ORIGIN_UPPER ) {
00251 y_start = 0;
00252 y_step = 1;
00253 y_end = tga.height;
00254 }
00255 else {
00256 y_start = tga.height - 1;
00257 y_step = -1;
00258 y_end = -1;
00259 }
00260
00261 uchar * src = image;
00262
00263 for( int y = y_start; y != y_end; y += y_step ) {
00264 QRgb * scanline = (QRgb *) img.scanLine( y );
00265
00266 if( info.pal ) {
00267
00268 for( int x = 0; x < tga.width; x++ ) {
00269 uchar idx = *src++;
00270 scanline[x] = qRgb( palette[3*idx+2], palette[3*idx+1], palette[3*idx+0] );
00271 }
00272 }
00273 else if( info.grey ) {
00274
00275 for( int x = 0; x < tga.width; x++ ) {
00276 scanline[x] = qRgb( *src, *src, *src );
00277 src++;
00278 }
00279 }
00280 else {
00281
00282 if( tga.pixel_size == 16 ) {
00283 for( int x = 0; x < tga.width; x++ ) {
00284 Color555 c = *reinterpret_cast<Color555 *>(src);
00285 scanline[x] = qRgb( (c.r << 3) | (c.r >> 2), (c.g << 3) | (c.g >> 2), (c.b << 3) | (c.b >> 2) );
00286 src += 2;
00287 }
00288 }
00289 else if( tga.pixel_size == 24 ) {
00290 for( int x = 0; x < tga.width; x++ ) {
00291 scanline[x] = qRgb( src[2], src[1], src[0] );
00292 src += 3;
00293 }
00294 }
00295 else if( tga.pixel_size == 32 ) {
00296 for( int x = 0; x < tga.width; x++ ) {
00297
00298 const uchar alpha = ( src[3] << ( 8 - numAlphaBits ) );
00299 scanline[x] = qRgba( src[2], src[1], src[0], alpha );
00300 src += 4;
00301 }
00302 }
00303 }
00304 }
00305
00306
00307 delete [] image;
00308
00309 return true;
00310 }
00311
00312 }
00313
00314
00315 TGAHandler::TGAHandler()
00316 {
00317 }
00318
00319 bool TGAHandler::canRead() const
00320 {
00321 if (canRead(device())) {
00322 setFormat("tga");
00323 return true;
00324 }
00325 return false;
00326 }
00327
00328 bool TGAHandler::read(QImage *outImage)
00329 {
00330
00331
00332 QDataStream s( device() );
00333 s.setByteOrder( QDataStream::LittleEndian );
00334
00335
00336
00337 TgaHeader tga;
00338 s >> tga;
00339 s.device()->seek( TgaHeader::SIZE + tga.id_length );
00340
00341
00342 if( s.atEnd() ) {
00343 kDebug(399) << "This TGA file is not valid.";
00344 return false;
00345 }
00346
00347
00348 if( !IsSupported(tga) ) {
00349 kDebug(399) << "This TGA file is not supported.";
00350 return false;
00351 }
00352
00353
00354 QImage img;
00355 bool result = LoadTGA(s, tga, img);
00356
00357 if( result == false ) {
00358 kDebug(399) << "Error loading TGA file.";
00359 return false;
00360 }
00361
00362
00363 *outImage = img;
00364 return true;
00365 }
00366
00367 bool TGAHandler::write(const QImage &image)
00368 {
00369 QDataStream s( device() );
00370 s.setByteOrder( QDataStream::LittleEndian );
00371
00372 const QImage& img = image;
00373 const bool hasAlpha = (img.format() == QImage::Format_ARGB32);
00374 for( int i = 0; i < 12; i++ )
00375 s << targaMagic[i];
00376
00377
00378 s << quint16( img.width() );
00379 s << quint16( img.height() );
00380 s << quint8( hasAlpha ? 32 : 24 );
00381 s << quint8( hasAlpha ? 0x24 : 0x20 );
00382
00383 for( int y = 0; y < img.height(); y++ )
00384 for( int x = 0; x < img.width(); x++ ) {
00385 const QRgb color = img.pixel( x, y );
00386 s << quint8( qBlue( color ) );
00387 s << quint8( qGreen( color ) );
00388 s << quint8( qRed( color ) );
00389 if( hasAlpha )
00390 s << quint8( qAlpha( color ) );
00391 }
00392
00393 return true;
00394 }
00395
00396 QByteArray TGAHandler::name() const
00397 {
00398 return "tga";
00399 }
00400
00401 bool TGAHandler::canRead(QIODevice *device)
00402 {
00403 if (!device) {
00404 qWarning("TGAHandler::canRead() called with no device");
00405 return false;
00406 }
00407
00408 qint64 oldPos = device->pos();
00409 QByteArray head = device->readLine(64);
00410 int readBytes = head.size();
00411
00412 if (device->isSequential()) {
00413 while (readBytes > 0)
00414 device->ungetChar(head[readBytes-- - 1]);
00415 } else {
00416 device->seek(oldPos);
00417 }
00418
00419 const QRegExp regexp("^.\x01[\x01-\x03\x09-\x0b]\x01{3}.[\x01\x18]");
00420 QString data(head);
00421
00422 return data.contains(regexp);
00423 }
00424
00425
00426 class TGAPlugin : public QImageIOPlugin
00427 {
00428 public:
00429 QStringList keys() const;
00430 Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
00431 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
00432 };
00433
00434 QStringList TGAPlugin::keys() const
00435 {
00436 return QStringList() << "tga" << "TGA";
00437 }
00438
00439 QImageIOPlugin::Capabilities TGAPlugin::capabilities(QIODevice *device, const QByteArray &format) const
00440 {
00441 if (format == "tga" || format == "TGA")
00442 return Capabilities(CanRead | CanWrite);
00443 if (!format.isEmpty())
00444 return 0;
00445 if (!device->isOpen())
00446 return 0;
00447
00448 Capabilities cap;
00449 if (device->isReadable() && TGAHandler::canRead(device))
00450 cap |= CanRead;
00451 if (device->isWritable())
00452 cap |= CanWrite;
00453 return cap;
00454 }
00455
00456 QImageIOHandler *TGAPlugin::create(QIODevice *device, const QByteArray &format) const
00457 {
00458 QImageIOHandler *handler = new TGAHandler;
00459 handler->setDevice(device);
00460 handler->setFormat(format);
00461 return handler;
00462 }
00463
00464 Q_EXPORT_STATIC_PLUGIN(TGAPlugin)
00465 Q_EXPORT_PLUGIN2(tga, TGAPlugin)