1 /* This file is part of the KDE project
2 Copyright (C) 2003 Dominik Seichter <domseichter@web.de>
3 Copyright (C) 2004 Ignacio CastaƱo <castano@ludicon.com>
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the Lesser GNU General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
11 /* this code supports:
13 * uncompressed and run length encoded indexed, grey and color tga files.
14 * image types 1, 2, 3, 9, 10 and 11.
15 * only RGB color maps with no more than 256 colors.
16 * pixel formats 8, 15, 24 and 32.
18 * uncompressed true color tga files
26 #include <qdatastream.h>
31 typedef quint16 ushort
;
34 namespace { // Private.
36 // Header format of saved files.
37 uchar targaMagic
[12] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
43 TGA_TYPE_RLE_INDEXED
= 9,
44 TGA_TYPE_RLE_RGB
= 10,
45 TGA_TYPE_RLE_GREY
= 11
48 #define TGA_INTERLEAVE_MASK 0xc0
49 #define TGA_INTERLEAVE_NONE 0x00
50 #define TGA_INTERLEAVE_2WAY 0x40
51 #define TGA_INTERLEAVE_4WAY 0x80
53 #define TGA_ORIGIN_MASK 0x30
54 #define TGA_ORIGIN_LEFT 0x00
55 #define TGA_ORIGIN_RIGHT 0x10
56 #define TGA_ORIGIN_LOWER 0x00
57 #define TGA_ORIGIN_UPPER 0x20
64 ushort colormap_index
;
65 ushort colormap_length
;
74 enum { SIZE
= 18 }; // const static int SIZE = 18;
77 static QDataStream
& operator>> ( QDataStream
& s
, TgaHeader
& head
)
80 s
>> head
.colormap_type
;
82 s
>> head
.colormap_index
;
83 s
>> head
.colormap_length
;
84 s
>> head
.colormap_size
;
94 static bool IsSupported( const TgaHeader
& head
)
96 if( head
.image_type
!= TGA_TYPE_INDEXED
&&
97 head
.image_type
!= TGA_TYPE_RGB
&&
98 head
.image_type
!= TGA_TYPE_GREY
&&
99 head
.image_type
!= TGA_TYPE_RLE_INDEXED
&&
100 head
.image_type
!= TGA_TYPE_RLE_RGB
&&
101 head
.image_type
!= TGA_TYPE_RLE_GREY
)
105 if( head
.image_type
== TGA_TYPE_INDEXED
||
106 head
.image_type
== TGA_TYPE_RLE_INDEXED
)
108 if( head
.colormap_length
> 256 || head
.colormap_size
!= 24 )
113 if( head
.width
== 0 || head
.height
== 0 )
117 if( head
.pixel_size
!= 8 && head
.pixel_size
!= 16 &&
118 head
.pixel_size
!= 24 && head
.pixel_size
!= 32 )
131 struct TgaHeaderInfo
{
138 TgaHeaderInfo( const TgaHeader
& tga
) : rle(false), pal(false), rgb(false), grey(false), supported(true)
140 switch( tga
.image_type
) {
141 case TGA_TYPE_RLE_INDEXED
:
143 // no break is intended!
144 case TGA_TYPE_INDEXED
:
145 if( tga
.colormap_type
!=1 || tga
.colormap_size
!=24 || tga
.colormap_length
>256 ) {
151 case TGA_TYPE_RLE_RGB
:
153 // no break is intended!
158 case TGA_TYPE_RLE_GREY
:
160 // no break is intended!
166 // Error, unknown image type.
174 static bool LoadTGA( QDataStream
& s
, const TgaHeader
& tga
, QImage
&img
)
177 img
= QImage( tga
.width
, tga
.height
, QImage::Format_RGB32
);
179 TgaHeaderInfo
info(tga
);
180 if( !info
.supported
) {
181 // File not supported.
182 kDebug(399) << "This TGA file is not supported." << endl
;
186 // Bits 0-3 are the numbers of alpha bits (can be zero!)
187 const int numAlphaBits
= tga
.flags
& 0xf;
188 // However alpha exists only in the 32 bit format.
189 if( ( tga
.pixel_size
== 32 ) && ( tga
.flags
& 0xf ) ) {
190 img
.convertToFormat( QImage::Format_ARGB32
);
193 uint pixel_size
= (tga
.pixel_size
/8);
194 uint size
= tga
.width
* tga
.height
* pixel_size
;
198 kDebug(399) << "This TGA file is broken with size " << size
<< endl
;
205 // @todo Support palettes in other formats!
206 s
.readRawData( palette
, 3 * tga
.colormap_length
);
210 uchar
* const image
= new uchar
[size
];
214 char * dst
= (char *)image
;
218 // Get packet header.
222 uint count
= (c
& 0x7f) + 1;
223 num
-= count
* pixel_size
;
227 assert(pixel_size
<= 8);
229 s
.readRawData( pixel
, pixel_size
);
231 memcpy(dst
, pixel
, pixel_size
);
238 s
.readRawData( dst
, count
);
245 s
.readRawData( (char *)image
, size
);
248 // Convert image to internal format.
249 int y_start
, y_step
, y_end
;
250 if( tga
.flags
& TGA_ORIGIN_UPPER
) {
256 y_start
= tga
.height
- 1;
263 for( int y
= y_start
; y
!= y_end
; y
+= y_step
) {
264 QRgb
* scanline
= (QRgb
*) img
.scanLine( y
);
268 for( int x
= 0; x
< tga
.width
; x
++ ) {
270 scanline
[x
] = qRgb( palette
[3*idx
+2], palette
[3*idx
+1], palette
[3*idx
+0] );
273 else if( info
.grey
) {
275 for( int x
= 0; x
< tga
.width
; x
++ ) {
276 scanline
[x
] = qRgb( *src
, *src
, *src
);
282 if( tga
.pixel_size
== 16 ) {
283 for( int x
= 0; x
< tga
.width
; x
++ ) {
284 Color555 c
= *reinterpret_cast<Color555
*>(src
);
285 scanline
[x
] = qRgb( (c
.r
<< 3) | (c
.r
>> 2), (c
.g
<< 3) | (c
.g
>> 2), (c
.b
<< 3) | (c
.b
>> 2) );
289 else if( tga
.pixel_size
== 24 ) {
290 for( int x
= 0; x
< tga
.width
; x
++ ) {
291 scanline
[x
] = qRgb( src
[2], src
[1], src
[0] );
295 else if( tga
.pixel_size
== 32 ) {
296 for( int x
= 0; x
< tga
.width
; x
++ ) {
297 // ### TODO: verify with images having really some alpha data
298 const uchar alpha
= ( src
[3] << ( 8 - numAlphaBits
) );
299 scanline
[x
] = qRgba( src
[2], src
[1], src
[0], alpha
);
315 TGAHandler::TGAHandler()
319 bool TGAHandler::canRead() const
321 return canRead(device());
324 bool TGAHandler::read(QImage
*outImage
)
326 //kDebug(399) << "Loading TGA file!" << endl;
328 QDataStream
s( device() );
329 s
.setByteOrder( QDataStream::LittleEndian
);
332 // Read image header.
335 s
.device()->seek( TgaHeader::SIZE
+ tga
.id_length
);
337 // Check image file format.
339 kDebug(399) << "This TGA file is not valid." << endl
;
343 // Check supported file types.
344 if( !IsSupported(tga
) ) {
345 kDebug(399) << "This TGA file is not supported." << endl
;
351 bool result
= LoadTGA(s
, tga
, img
);
353 if( result
== false ) {
354 kDebug(399) << "Error loading TGA file." << endl
;
363 bool TGAHandler::write(const QImage
&image
)
365 QDataStream
s( device() );
366 s
.setByteOrder( QDataStream::LittleEndian
);
368 const QImage
& img
= image
;
369 const bool hasAlpha
= (img
.format() == QImage::Format_ARGB32
);
370 for( int i
= 0; i
< 12; i
++ )
374 s
<< quint16( img
.width() ); // width
375 s
<< quint16( img
.height() ); // height
376 s
<< quint8( hasAlpha
? 32 : 24 ); // depth (24 bit RGB + 8 bit alpha)
377 s
<< quint8( hasAlpha
? 0x24 : 0x20 ); // top left image (0x20) + 8 bit alpha (0x4)
379 for( int y
= 0; y
< img
.height(); y
++ )
380 for( int x
= 0; x
< img
.width(); x
++ ) {
381 const QRgb color
= img
.pixel( x
, y
);
382 s
<< quint8( qBlue( color
) );
383 s
<< quint8( qGreen( color
) );
384 s
<< quint8( qRed( color
) );
386 s
<< quint8( qAlpha( color
) );
392 QByteArray
TGAHandler::name() const
397 bool TGAHandler::canRead(QIODevice
*device
)
400 qWarning("TGAHandler::canRead() called with no device");
404 qint64 oldPos
= device
->pos();
405 QByteArray head
= device
->readLine(64);
406 int readBytes
= head
.size();
408 if (device
->isSequential()) {
409 while (readBytes
> 0)
410 device
->ungetChar(head
[readBytes
-- - 1]);
412 device
->seek(oldPos
);
415 const QRegExp
regexp("^.\x01[\x01-\x03\x09-\x0b]\x01{3}.[\x01\x18]");
418 return data
.contains(regexp
);
422 class TGAPlugin
: public QImageIOPlugin
425 QStringList
keys() const;
426 Capabilities
capabilities(QIODevice
*device
, const QByteArray
&format
) const;
427 QImageIOHandler
*create(QIODevice
*device
, const QByteArray
&format
= QByteArray()) const;
430 QStringList
TGAPlugin::keys() const
432 return QStringList() << "tga" << "TGA";
435 QImageIOPlugin::Capabilities
TGAPlugin::capabilities(QIODevice
*device
, const QByteArray
&format
) const
437 if (format
== "tga" || format
== "TGA")
438 return Capabilities(CanRead
| CanWrite
);
439 if (!format
.isEmpty())
441 if (!device
->isOpen())
445 if (device
->isReadable() && TGAHandler::canRead(device
))
447 if (device
->isWritable())
452 QImageIOHandler
*TGAPlugin::create(QIODevice
*device
, const QByteArray
&format
) const
454 QImageIOHandler
*handler
= new TGAHandler
;
455 handler
->setDevice(device
);
456 handler
->setFormat(format
);
460 Q_EXPORT_STATIC_PLUGIN(TGAPlugin
)
461 Q_EXPORT_PLUGIN2(tga
, TGAPlugin
)