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
00092
00093
00094 return s;
00095 }
00096
00097 static bool IsSupported( const TgaHeader & head )
00098 {
00099 if( head.image_type != TGA_TYPE_INDEXED &&
00100 head.image_type != TGA_TYPE_RGB &&
00101 head.image_type != TGA_TYPE_GREY &&
00102 head.image_type != TGA_TYPE_RLE_INDEXED &&
00103 head.image_type != TGA_TYPE_RLE_RGB &&
00104 head.image_type != TGA_TYPE_RLE_GREY )
00105 {
00106 return false;
00107 }
00108 if( head.image_type == TGA_TYPE_INDEXED ||
00109 head.image_type == TGA_TYPE_RLE_INDEXED )
00110 {
00111 if( head.colormap_length > 256 || head.colormap_size != 24 || head.colormap_type != 1 )
00112 {
00113 return false;
00114 }
00115 }
00116 if( head.image_type == TGA_TYPE_RGB ||
00117 head.image_type == TGA_TYPE_GREY ||
00118 head.image_type == TGA_TYPE_RLE_RGB ||
00119 head.image_type == TGA_TYPE_RLE_GREY )
00120 {
00121 if( head.colormap_type != 0 )
00122 {
00123 return false;
00124 }
00125 }
00126 if( head.width == 0 || head.height == 0 )
00127 {
00128 return false;
00129 }
00130 if( head.pixel_size != 8 && head.pixel_size != 16 &&
00131 head.pixel_size != 24 && head.pixel_size != 32 )
00132 {
00133 return false;
00134 }
00135 return true;
00136 }
00137
00138 struct Color555 {
00139 ushort b : 5;
00140 ushort g : 5;
00141 ushort r : 5;
00142 };
00143
00144 struct TgaHeaderInfo {
00145 bool rle;
00146 bool pal;
00147 bool rgb;
00148 bool grey;
00149
00150 TgaHeaderInfo( const TgaHeader & tga ) : rle(false), pal(false), rgb(false), grey(false)
00151 {
00152 switch( tga.image_type ) {
00153 case TGA_TYPE_RLE_INDEXED:
00154 rle = true;
00155
00156 case TGA_TYPE_INDEXED:
00157 pal = true;
00158 break;
00159
00160 case TGA_TYPE_RLE_RGB:
00161 rle = true;
00162
00163 case TGA_TYPE_RGB:
00164 rgb = true;
00165 break;
00166
00167 case TGA_TYPE_RLE_GREY:
00168 rle = true;
00169
00170 case TGA_TYPE_GREY:
00171 grey = true;
00172 break;
00173
00174 default:
00175
00176 break;
00177 }
00178 }
00179 };
00180
00181
00182
00183 static bool LoadTGA( QDataStream & s, const TgaHeader & tga, QImage &img )
00184 {
00185
00186 img = QImage( tga.width, tga.height, QImage::Format_RGB32 );
00187
00188 TgaHeaderInfo info(tga);
00189
00190
00191 const int numAlphaBits = tga.flags & 0xf;
00192
00193 if( ( tga.pixel_size == 32 ) && ( tga.flags & 0xf ) ) {
00194 img = QImage( tga.width, tga.height, QImage::Format_ARGB32 );
00195 }
00196
00197 uint pixel_size = (tga.pixel_size/8);
00198 uint size = tga.width * tga.height * pixel_size;
00199
00200 if (size < 1)
00201 {
00202 kDebug(399) << "This TGA file is broken with size " << size;
00203 return false;
00204 }
00205
00206
00207 char palette[768];
00208 if( info.pal ) {
00209
00210 s.readRawData( palette, 3 * tga.colormap_length );
00211 }
00212
00213
00214 uchar * const image = new uchar[size];
00215
00216 if( info.rle ) {
00217
00218 char * dst = (char *)image;
00219 int num = size;
00220
00221 while (num > 0) {
00222
00223 uchar c;
00224 s >> c;
00225
00226 uint count = (c & 0x7f) + 1;
00227 num -= count * pixel_size;
00228
00229 if (c & 0x80) {
00230
00231 assert(pixel_size <= 8);
00232 char pixel[8];
00233 s.readRawData( pixel, pixel_size );
00234 do {
00235 memcpy(dst, pixel, pixel_size);
00236 dst += pixel_size;
00237 } while (--count);
00238 }
00239 else {
00240
00241 count *= pixel_size;
00242 s.readRawData( dst, count );
00243 dst += count;
00244 }
00245 }
00246 }
00247 else {
00248
00249 s.readRawData( (char *)image, size );
00250 }
00251
00252
00253 int y_start, y_step, y_end;
00254 if( tga.flags & TGA_ORIGIN_UPPER ) {
00255 y_start = 0;
00256 y_step = 1;
00257 y_end = tga.height;
00258 }
00259 else {
00260 y_start = tga.height - 1;
00261 y_step = -1;
00262 y_end = -1;
00263 }
00264
00265 uchar * src = image;
00266
00267 for( int y = y_start; y != y_end; y += y_step ) {
00268 QRgb * scanline = (QRgb *) img.scanLine( y );
00269
00270 if( info.pal ) {
00271
00272 for( int x = 0; x < tga.width; x++ ) {
00273 uchar idx = *src++;
00274 scanline[x] = qRgb( palette[3*idx+2], palette[3*idx+1], palette[3*idx+0] );
00275 }
00276 }
00277 else if( info.grey ) {
00278
00279 for( int x = 0; x < tga.width; x++ ) {
00280 scanline[x] = qRgb( *src, *src, *src );
00281 src++;
00282 }
00283 }
00284 else {
00285
00286 if( tga.pixel_size == 16 ) {
00287 for( int x = 0; x < tga.width; x++ ) {
00288 Color555 c = *reinterpret_cast<Color555 *>(src);
00289 scanline[x] = qRgb( (c.r << 3) | (c.r >> 2), (c.g << 3) | (c.g >> 2), (c.b << 3) | (c.b >> 2) );
00290 src += 2;
00291 }
00292 }
00293 else if( tga.pixel_size == 24 ) {
00294 for( int x = 0; x < tga.width; x++ ) {
00295 scanline[x] = qRgb( src[2], src[1], src[0] );
00296 src += 3;
00297 }
00298 }
00299 else if( tga.pixel_size == 32 ) {
00300 for( int x = 0; x < tga.width; x++ ) {
00301
00302 const uchar alpha = ( src[3] << ( 8 - numAlphaBits ) );
00303 scanline[x] = qRgba( src[2], src[1], src[0], alpha );
00304 src += 4;
00305 }
00306 }
00307 }
00308 }
00309
00310
00311 delete [] image;
00312
00313 return true;
00314 }
00315
00316 }
00317
00318
00319 TGAHandler::TGAHandler()
00320 {
00321 }
00322
00323 bool TGAHandler::canRead() const
00324 {
00325 if (canRead(device())) {
00326 setFormat("tga");
00327 return true;
00328 }
00329 return false;
00330 }
00331
00332 bool TGAHandler::read(QImage *outImage)
00333 {
00334
00335
00336 QDataStream s( device() );
00337 s.setByteOrder( QDataStream::LittleEndian );
00338
00339
00340
00341 TgaHeader tga;
00342 s >> tga;
00343 s.device()->seek( TgaHeader::SIZE + tga.id_length );
00344
00345
00346 if( s.atEnd() ) {
00347 kDebug(399) << "This TGA file is not valid.";
00348 return false;
00349 }
00350
00351
00352 if( !IsSupported(tga) ) {
00353 kDebug(399) << "This TGA file is not supported.";
00354 return false;
00355 }
00356
00357
00358 QImage img;
00359 bool result = LoadTGA(s, tga, img);
00360
00361 if( result == false ) {
00362 kDebug(399) << "Error loading TGA file.";
00363 return false;
00364 }
00365
00366
00367 *outImage = img;
00368 return true;
00369 }
00370
00371 bool TGAHandler::write(const QImage &image)
00372 {
00373 QDataStream s( device() );
00374 s.setByteOrder( QDataStream::LittleEndian );
00375
00376 const QImage& img = image;
00377 const bool hasAlpha = (img.format() == QImage::Format_ARGB32);
00378 for( int i = 0; i < 12; i++ )
00379 s << targaMagic[i];
00380
00381
00382 s << quint16( img.width() );
00383 s << quint16( img.height() );
00384 s << quint8( hasAlpha ? 32 : 24 );
00385 s << quint8( hasAlpha ? 0x24 : 0x20 );
00386
00387 for( int y = 0; y < img.height(); y++ )
00388 for( int x = 0; x < img.width(); x++ ) {
00389 const QRgb color = img.pixel( x, y );
00390 s << quint8( qBlue( color ) );
00391 s << quint8( qGreen( color ) );
00392 s << quint8( qRed( color ) );
00393 if( hasAlpha )
00394 s << quint8( qAlpha( color ) );
00395 }
00396
00397 return true;
00398 }
00399
00400 QByteArray TGAHandler::name() const
00401 {
00402 return "tga";
00403 }
00404
00405 bool TGAHandler::canRead(QIODevice *device)
00406 {
00407 if (!device) {
00408 qWarning("TGAHandler::canRead() called with no device");
00409 return false;
00410 }
00411
00412 qint64 oldPos = device->pos();
00413 QByteArray head = device->read(TgaHeader::SIZE);
00414 int readBytes = head.size();
00415
00416 if (device->isSequential()) {
00417 for (int pos = readBytes - 1; pos >= 0; --pos) {
00418 device->ungetChar(head[pos]);
00419 }
00420 } else {
00421 device->seek(oldPos);
00422 }
00423
00424 if (readBytes < TgaHeader::SIZE) {
00425 return false;
00426 }
00427
00428 QDataStream stream(head);
00429 stream.setByteOrder(QDataStream::LittleEndian);
00430 TgaHeader tga;
00431 stream >> tga;
00432 return IsSupported(tga);
00433 }
00434
00435
00436 class TGAPlugin : public QImageIOPlugin
00437 {
00438 public:
00439 QStringList keys() const;
00440 Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
00441 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
00442 };
00443
00444 QStringList TGAPlugin::keys() const
00445 {
00446 return QStringList() << "tga" << "TGA";
00447 }
00448
00449 QImageIOPlugin::Capabilities TGAPlugin::capabilities(QIODevice *device, const QByteArray &format) const
00450 {
00451 if (format == "tga" || format == "TGA")
00452 return Capabilities(CanRead | CanWrite);
00453 if (!format.isEmpty())
00454 return 0;
00455 if (!device->isOpen())
00456 return 0;
00457
00458 Capabilities cap;
00459 if (device->isReadable() && TGAHandler::canRead(device))
00460 cap |= CanRead;
00461 if (device->isWritable())
00462 cap |= CanWrite;
00463 return cap;
00464 }
00465
00466 QImageIOHandler *TGAPlugin::create(QIODevice *device, const QByteArray &format) const
00467 {
00468 QImageIOHandler *handler = new TGAHandler;
00469 handler->setDevice(device);
00470 handler->setFormat(format);
00471 return handler;
00472 }
00473
00474 Q_EXPORT_STATIC_PLUGIN(TGAPlugin)
00475 Q_EXPORT_PLUGIN2(tga, TGAPlugin)