KImgIO
tga.cpp
Go to the documentation of this file.
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 <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 { // 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 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 /*qDebug() << "id_length: " << head.id_length << " - colormap_type: " << head.colormap_type << " - image_type: " << head.image_type; 00092 qDebug() << "colormap_index: " << head.colormap_index << " - colormap_length: " << head.colormap_length << " - colormap_size: " << head.colormap_size; 00093 qDebug() << "x_origin: " << head.x_origin << " - y_origin: " << head.y_origin << " - width:" << head.width << " - height:" << head.height << " - pixelsize: " << head.pixel_size << " - flags: " << head.flags;*/ 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 // no break is intended! 00156 case TGA_TYPE_INDEXED: 00157 pal = true; 00158 break; 00159 00160 case TGA_TYPE_RLE_RGB: 00161 rle = true; 00162 // no break is intended! 00163 case TGA_TYPE_RGB: 00164 rgb = true; 00165 break; 00166 00167 case TGA_TYPE_RLE_GREY: 00168 rle = true; 00169 // no break is intended! 00170 case TGA_TYPE_GREY: 00171 grey = true; 00172 break; 00173 00174 default: 00175 // Error, unknown image type. 00176 break; 00177 } 00178 } 00179 }; 00180 00181 00182 00183 static bool LoadTGA( QDataStream & s, const TgaHeader & tga, QImage &img ) 00184 { 00185 // Create image. 00186 img = QImage( tga.width, tga.height, QImage::Format_RGB32 ); 00187 00188 TgaHeaderInfo info(tga); 00189 00190 // Bits 0-3 are the numbers of alpha bits (can be zero!) 00191 const int numAlphaBits = tga.flags & 0xf; 00192 // However alpha exists only in the 32 bit format. 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 // Read palette. 00207 char palette[768]; 00208 if( info.pal ) { 00209 // @todo Support palettes in other formats! 00210 s.readRawData( palette, 3 * tga.colormap_length ); 00211 } 00212 00213 // Allocate image. 00214 uchar * const image = new uchar[size]; 00215 00216 if( info.rle ) { 00217 // Decode image. 00218 char * dst = (char *)image; 00219 int num = size; 00220 00221 while (num > 0) { 00222 // Get packet header. 00223 uchar c; 00224 s >> c; 00225 00226 uint count = (c & 0x7f) + 1; 00227 num -= count * pixel_size; 00228 00229 if (c & 0x80) { 00230 // RLE pixels. 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 // Raw pixels. 00241 count *= pixel_size; 00242 s.readRawData( dst, count ); 00243 dst += count; 00244 } 00245 } 00246 } 00247 else { 00248 // Read raw image. 00249 s.readRawData( (char *)image, size ); 00250 } 00251 00252 // Convert image to internal format. 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 // Paletted. 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 // Greyscale. 00279 for( int x = 0; x < tga.width; x++ ) { 00280 scanline[x] = qRgb( *src, *src, *src ); 00281 src++; 00282 } 00283 } 00284 else { 00285 // True Color. 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 // ### TODO: verify with images having really some alpha data 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 // Free image. 00311 delete [] image; 00312 00313 return true; 00314 } 00315 00316 } // namespace 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 //kDebug(399) << "Loading TGA file!"; 00335 00336 QDataStream s( device() ); 00337 s.setByteOrder( QDataStream::LittleEndian ); 00338 00339 00340 // Read image header. 00341 TgaHeader tga; 00342 s >> tga; 00343 s.device()->seek( TgaHeader::SIZE + tga.id_length ); 00344 00345 // Check image file format. 00346 if( s.atEnd() ) { 00347 kDebug(399) << "This TGA file is not valid."; 00348 return false; 00349 } 00350 00351 // Check supported file types. 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 // write header 00382 s << quint16( img.width() ); // width 00383 s << quint16( img.height() ); // height 00384 s << quint8( hasAlpha ? 32 : 24 ); // depth (24 bit RGB + 8 bit alpha) 00385 s << quint8( hasAlpha ? 0x24 : 0x20 ); // top left image (0x20) + 8 bit alpha (0x4) 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)
KDE 4.6 API Reference