1 // kimgio module for SGI images
3 // Copyright (C) 2004 Melchior FRANZ <mfranz@kde.org>
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the Lesser GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
11 /* this code supports:
13 * everything, except images with 1 dimension or images with
14 * mapmode != NORMAL (e.g. dithered); Images with 16 bit
15 * precision or more than 4 layers are stripped down.
17 * Run Length Encoded (RLE) or Verbatim (uncompressed)
18 * (whichever is smaller)
20 * Please report if you come across rgb/rgba/sgi/bw files that aren't
21 * recognized. Also report applications that can't deal with images
22 * saved by this filter.
27 #include <QtGui/QImage>
31 SGIImage::SGIImage(QIODevice
*io
) :
36 _stream
.setDevice(_dev
);
47 ///////////////////////////////////////////////////////////////////////////////
50 bool SGIImage::getRow(uchar
*dest
)
54 for (i
= 0; i
< _xsize
; i
++) {
55 if (_pos
>= _data
.end())
57 dest
[i
] = uchar(*_pos
);
63 for (i
= 0; i
< _xsize
;) {
71 for (; i
< _xsize
&& n
--; i
++) {
76 for (; i
< _xsize
&& n
--; i
++)
86 bool SGIImage::readData(QImage
& img
)
89 quint32
*start
= _starttab
;
90 QByteArray
lguard(_xsize
, 0);
91 uchar
*line
= (uchar
*)lguard
.data();
97 for (y
= 0; y
< _ysize
; y
++) {
99 _pos
= _data
.begin() + *start
++;
102 c
= (QRgb
*)img
.scanLine(_ysize
- y
- 1);
103 for (x
= 0; x
< _xsize
; x
++, c
++)
104 *c
= qRgb(line
[x
], line
[x
], line
[x
]);
111 for (y
= 0; y
< _ysize
; y
++) {
113 _pos
= _data
.begin() + *start
++;
116 c
= (QRgb
*)img
.scanLine(_ysize
- y
- 1);
117 for (x
= 0; x
< _xsize
; x
++, c
++)
118 *c
= qRgb(qRed(*c
), line
[x
], line
[x
]);
121 for (y
= 0; y
< _ysize
; y
++) {
123 _pos
= _data
.begin() + *start
++;
126 c
= (QRgb
*)img
.scanLine(_ysize
- y
- 1);
127 for (x
= 0; x
< _xsize
; x
++, c
++)
128 *c
= qRgb(qRed(*c
), qGreen(*c
), line
[x
]);
135 for (y
= 0; y
< _ysize
; y
++) {
137 _pos
= _data
.begin() + *start
++;
140 c
= (QRgb
*)img
.scanLine(_ysize
- y
- 1);
141 for (x
= 0; x
< _xsize
; x
++, c
++)
142 *c
= qRgba(qRed(*c
), qGreen(*c
), qBlue(*c
), line
[x
]);
149 bool SGIImage::readImage(QImage
& img
)
155 kDebug(399) << "reading rgb ";
164 kDebug(399) << (_rle
? "RLE" : "verbatim");
170 kDebug(399) << "bytes per channel: " << int(_bpc
);
174 kDebug(399) << "dropping least significant byte";
178 // number of dimensions
180 kDebug(399) << "dimensions: " << _dim
;
181 if (_dim
< 1 || _dim
> 3)
184 _stream
>> _xsize
>> _ysize
>> _zsize
>> _pixmin
>> _pixmax
>> u32
;
185 kDebug(399) << "x: " << _xsize
;
186 kDebug(399) << "y: " << _ysize
;
187 kDebug(399) << "z: " << _zsize
;
190 _stream
.readRawData(_imagename
, 80);
191 _imagename
[79] = '\0';
193 _stream
>> _colormap
;
194 kDebug(399) << "colormap: " << _colormap
;
195 if (_colormap
!= NORMAL
)
196 return false; // only NORMAL supported
198 for (int i
= 0; i
< 404; i
++)
202 kDebug(399) << "1-dimensional images aren't supported yet";
209 _numrows
= _ysize
* _zsize
;
211 img
= QImage( _xsize
, _ysize
, QImage::Format_RGB32
);
213 if (_zsize
== 2 || _zsize
== 4)
214 img
= img
.convertToFormat(QImage::Format_ARGB32
);
216 kDebug(399) << "using first 4 of " << _zsize
<< " channels";
220 _starttab
= new quint32
[_numrows
];
221 for (l
= 0; !_stream
.atEnd() && l
< _numrows
; l
++) {
222 _stream
>> _starttab
[l
];
223 _starttab
[l
] -= 512 + _numrows
* 2 * sizeof(quint32
);
226 _lengthtab
= new quint32
[_numrows
];
227 for (l
= 0; l
< _numrows
; l
++)
228 _stream
>> _lengthtab
[l
];
231 _data
= _dev
->readAll();
235 for (uint o
= 0; o
< _numrows
; o
++)
236 // don't change to greater-or-equal!
237 if (_starttab
[o
] + _lengthtab
[o
] > (uint
)_data
.size()) {
238 kDebug(399) << "image corrupt (sanity check failed)";
242 if (!readData(img
)) {
243 kDebug(399) << "image corrupt (incomplete scanline)";
251 ///////////////////////////////////////////////////////////////////////////////
254 void RLEData::write(QDataStream
& s
)
256 for (int i
= 0; i
< size(); i
++)
261 bool RLEData::operator<(const RLEData
& b
) const
264 for (int i
= 0; i
< qMin(size(), b
.size()); i
++) {
270 return size() < b
.size();
274 uint
RLEMap::insert(const uchar
*d
, uint l
)
276 RLEData data
= RLEData(d
, l
, _offset
);
277 Iterator it
= find(data
);
282 return QMap
<RLEData
, uint
>::insert(data
, _counter
++).value();
286 QVector
<const RLEData
*> RLEMap::vector()
288 QVector
<const RLEData
*> v(size());
289 for (Iterator it
= begin(); it
!= end(); ++it
)
290 v
.replace(it
.value(), &it
.key());
296 uchar
SGIImage::intensity(uchar c
)
306 uint
SGIImage::compact(uchar
*d
, uchar
*s
)
308 uchar
*dest
= d
, *src
= s
, patt
, *t
, *end
= s
+ _xsize
;
311 for (n
= 0, t
= src
; t
+ 2 < end
&& !(*t
== t
[1] && *t
== t
[2]); t
++)
315 i
= n
> 126 ? 126 : n
;
326 for (n
= 1; src
< end
&& *src
== patt
; src
++)
330 i
= n
> 126 ? 126 : n
;
341 bool SGIImage::scanData(const QImage
& img
)
343 quint32
*start
= _starttab
;
344 QByteArray
lineguard(_xsize
* 2, 0);
345 QByteArray
bufguard(_xsize
, 0);
346 uchar
*line
= (uchar
*)lineguard
.data();
347 uchar
*buf
= (uchar
*)bufguard
.data();
352 for (y
= 0; y
< _ysize
; y
++) {
353 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(_ysize
- y
- 1));
354 for (x
= 0; x
< _xsize
; x
++)
355 buf
[x
] = intensity(qRed(*c
++));
356 len
= compact(line
, buf
);
357 *start
++ = _rlemap
.insert(line
, len
);
364 for (y
= 0; y
< _ysize
; y
++) {
365 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(_ysize
- y
- 1));
366 for (x
= 0; x
< _xsize
; x
++)
367 buf
[x
] = intensity(qGreen(*c
++));
368 len
= compact(line
, buf
);
369 *start
++ = _rlemap
.insert(line
, len
);
372 for (y
= 0; y
< _ysize
; y
++) {
373 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(_ysize
- y
- 1));
374 for (x
= 0; x
< _xsize
; x
++)
375 buf
[x
] = intensity(qBlue(*c
++));
376 len
= compact(line
, buf
);
377 *start
++ = _rlemap
.insert(line
, len
);
384 for (y
= 0; y
< _ysize
; y
++) {
385 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(_ysize
- y
- 1));
386 for (x
= 0; x
< _xsize
; x
++)
387 buf
[x
] = intensity(qAlpha(*c
++));
388 len
= compact(line
, buf
);
389 *start
++ = _rlemap
.insert(line
, len
);
396 void SGIImage::writeHeader()
398 _stream
<< quint16(0x01da);
399 _stream
<< _rle
<< _bpc
<< _dim
;
400 _stream
<< _xsize
<< _ysize
<< _zsize
;
401 _stream
<< _pixmin
<< _pixmax
;
402 _stream
<< quint32(0);
404 for (int i
= 0; i
< 80; i
++)
405 _imagename
[i
] = '\0';
406 _stream
.writeRawData(_imagename
, 80);
408 _stream
<< _colormap
;
409 for (int i
= 0; i
< 404; i
++)
410 _stream
<< quint8(0);
414 void SGIImage::writeRle()
417 kDebug(399) << "writing RLE data";
422 for (i
= 0; i
< _numrows
; i
++)
423 _stream
<< quint32(_rlevector
[_starttab
[i
]]->offset());
425 // write length table
426 for (i
= 0; i
< _numrows
; i
++)
427 _stream
<< quint32(_rlevector
[_starttab
[i
]]->size());
430 for (i
= 0; (int)i
< _rlevector
.size(); i
++)
431 const_cast<RLEData
*>(_rlevector
[i
])->write(_stream
);
435 void SGIImage::writeVerbatim(const QImage
& img
)
438 kDebug(399) << "writing verbatim data";
444 for (y
= 0; y
< _ysize
; y
++) {
445 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(_ysize
- y
- 1));
446 for (x
= 0; x
< _xsize
; x
++)
447 _stream
<< quint8(qRed(*c
++));
454 for (y
= 0; y
< _ysize
; y
++) {
455 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(_ysize
- y
- 1));
456 for (x
= 0; x
< _xsize
; x
++)
457 _stream
<< quint8(qGreen(*c
++));
460 for (y
= 0; y
< _ysize
; y
++) {
461 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(_ysize
- y
- 1));
462 for (x
= 0; x
< _xsize
; x
++)
463 _stream
<< quint8(qBlue(*c
++));
470 for (y
= 0; y
< _ysize
; y
++) {
471 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(_ysize
- y
- 1));
472 for (x
= 0; x
< _xsize
; x
++)
473 _stream
<< quint8(qAlpha(*c
++));
478 bool SGIImage::writeImage(const QImage
& image
)
480 kDebug(399) << "writing "; // TODO add filename
483 _dim
= 2, _zsize
= 1;
485 _dim
= 3, _zsize
= 3;
487 if (img
.format() == QImage::Format_ARGB32
)
490 img
= img
.convertToFormat(QImage::Format_RGB32
);
492 kDebug(399) << "can't convert image to depth 32";
497 _xsize
= img
.width();
498 _ysize
= img
.height();
502 _numrows
= _ysize
* _zsize
;
503 _starttab
= new quint32
[_numrows
];
504 _rlemap
.setBaseOffset(512 + _numrows
* 2 * sizeof(quint32
));
506 if (!scanData(img
)) {
507 kDebug(399) << "this can't happen";
511 _rlevector
= _rlemap
.vector();
513 long verbatim_size
= _numrows
* _xsize
;
514 long rle_size
= _numrows
* 2 * sizeof(quint32
);
515 for (int i
= 0; i
< _rlevector
.size(); i
++)
516 rle_size
+= _rlevector
[i
]->size();
518 kDebug(399) << "minimum intensity: " << _pixmin
;
519 kDebug(399) << "maximum intensity: " << _pixmax
;
520 kDebug(399) << "saved scanlines: " << _numrows
- _rlemap
.size();
521 kDebug(399) << "total savings: " << (verbatim_size
- rle_size
) << " bytes";
522 kDebug(399) << "compression: " << (rle_size
* 100.0 / verbatim_size
) << '%';
524 if (verbatim_size
<= rle_size
)
532 ///////////////////////////////////////////////////////////////////////////////
535 RGBHandler::RGBHandler()
540 bool RGBHandler::canRead() const
542 if (canRead(device())) {
550 bool RGBHandler::read(QImage
*outImage
)
552 SGIImage
sgi(device());
553 return sgi
.readImage(*outImage
);
557 bool RGBHandler::write(const QImage
&image
)
559 SGIImage
sgi(device());
560 return sgi
.writeImage(image
);
564 QByteArray
RGBHandler::name() const
570 bool RGBHandler::canRead(QIODevice
*device
)
573 qWarning("RGBHandler::canRead() called with no device");
577 qint64 oldPos
= device
->pos();
578 QByteArray head
= device
->readLine(64);
579 int readBytes
= head
.size();
581 if (device
->isSequential()) {
582 while (readBytes
> 0)
583 device
->ungetChar(head
[readBytes
-- - 1]);
586 device
->seek(oldPos
);
589 const QRegExp
regexp("^\x01\xda\x01[\x01\x02]");
592 return data
.contains(regexp
);
596 ///////////////////////////////////////////////////////////////////////////////
599 class RGBPlugin
: public QImageIOPlugin
602 QStringList
keys() const;
603 Capabilities
capabilities(QIODevice
*device
, const QByteArray
&format
) const;
604 QImageIOHandler
*create(QIODevice
*device
, const QByteArray
&format
= QByteArray()) const;
608 QStringList
RGBPlugin::keys() const
610 return QStringList() << "rgb" << "RGB" << "rgba" << "RGBA" << "bw" << "BW" << "sgi" << "SGI";
614 QImageIOPlugin::Capabilities
RGBPlugin::capabilities(QIODevice
*device
, const QByteArray
&format
) const
616 if (format
== "rgb" || format
== "RGB" || format
== "rgba" || format
== "RGBA"
617 || format
== "bw" || format
== "BW" || format
== "sgi" || format
== "SGI")
618 return Capabilities(CanRead
|CanWrite
);
620 if (!format
.isEmpty())
622 if (!device
->isOpen())
626 if (device
->isReadable() && RGBHandler::canRead(device
))
628 if (device
->isWritable())
634 QImageIOHandler
*RGBPlugin::create(QIODevice
*device
, const QByteArray
&format
) const
636 QImageIOHandler
*handler
= new RGBHandler
;
637 handler
->setDevice(device
);
638 handler
->setFormat(format
);
643 Q_EXPORT_STATIC_PLUGIN(RGBPlugin
)
644 Q_EXPORT_PLUGIN2(rgb
, RGBPlugin
)