• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

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)

KImgIO

Skip menu "KImgIO"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal