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 m_stream
.setDevice(m_dev
);
47 ///////////////////////////////////////////////////////////////////////////////
50 bool SGIImage::getRow(uchar
*dest
)
54 for (i
= 0; i
< m_xsize
; i
++) {
55 if (m_pos
>= m_data
.end())
57 dest
[i
] = uchar(*m_pos
);
63 for (i
= 0; i
< m_xsize
;) {
70 if (*m_pos
++ & 0x80) {
71 for (; i
< m_xsize
&& n
--; i
++) {
76 for (; i
< m_xsize
&& n
--; i
++)
86 bool SGIImage::readData(QImage
& img
)
89 quint32
*start
= m_starttab
;
90 QByteArray
lguard(m_xsize
, 0);
91 uchar
*line
= (uchar
*)lguard
.data();
95 m_pos
= m_data
.begin();
97 for (y
= 0; y
< m_ysize
; y
++) {
99 m_pos
= m_data
.begin() + *start
++;
102 c
= (QRgb
*)img
.scanLine(m_ysize
- y
- 1);
103 for (x
= 0; x
< m_xsize
; x
++, c
++)
104 *c
= qRgb(line
[x
], line
[x
], line
[x
]);
111 for (y
= 0; y
< m_ysize
; y
++) {
113 m_pos
= m_data
.begin() + *start
++;
116 c
= (QRgb
*)img
.scanLine(m_ysize
- y
- 1);
117 for (x
= 0; x
< m_xsize
; x
++, c
++)
118 *c
= qRgb(qRed(*c
), line
[x
], line
[x
]);
121 for (y
= 0; y
< m_ysize
; y
++) {
123 m_pos
= m_data
.begin() + *start
++;
126 c
= (QRgb
*)img
.scanLine(m_ysize
- y
- 1);
127 for (x
= 0; x
< m_xsize
; x
++, c
++)
128 *c
= qRgb(qRed(*c
), qGreen(*c
), line
[x
]);
135 for (y
= 0; y
< m_ysize
; y
++) {
137 m_pos
= m_data
.begin() + *start
++;
140 c
= (QRgb
*)img
.scanLine(m_ysize
- y
- 1);
141 for (x
= 0; x
< m_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) << (m_rle
? "RLE" : "verbatim");
170 kDebug(399) << "bytes per channel: " << int(m_bpc
);
174 kDebug(399) << "dropping least significant byte";
178 // number of dimensions
180 kDebug(399) << "dimensions: " << m_dim
;
181 if (m_dim
< 1 || m_dim
> 3)
184 m_stream
>> m_xsize
>> m_ysize
>> m_zsize
>> m_pixmin
>> m_pixmax
>> u32
;
185 kDebug(399) << "x: " << m_xsize
;
186 kDebug(399) << "y: " << m_ysize
;
187 kDebug(399) << "z: " << m_zsize
;
190 m_stream
.readRawData(m_imagename
, 80);
191 m_imagename
[79] = '\0';
193 m_stream
>> m_colormap
;
194 kDebug(399) << "colormap: " << m_colormap
;
195 if (m_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";
206 if( m_stream
.atEnd())
209 m_numrows
= m_ysize
* m_zsize
;
211 img
= QImage( m_xsize
, m_ysize
, QImage::Format_RGB32
);
213 if (m_zsize
== 2 || m_zsize
== 4)
214 img
= img
.convertToFormat(QImage::Format_ARGB32
);
215 else if (m_zsize
> 4)
216 kDebug(399) << "using first 4 of " << m_zsize
<< " channels";
220 m_starttab
= new quint32
[m_numrows
];
221 for (l
= 0; !m_stream
.atEnd() && l
< m_numrows
; l
++) {
222 m_stream
>> m_starttab
[l
];
223 m_starttab
[l
] -= 512 + m_numrows
* 2 * sizeof(quint32
);
226 m_lengthtab
= new quint32
[m_numrows
];
227 for (l
= 0; l
< m_numrows
; l
++)
228 m_stream
>> m_lengthtab
[l
];
231 m_data
= m_dev
->readAll();
235 for (uint o
= 0; o
< m_numrows
; o
++)
236 // don't change to greater-or-equal!
237 if (m_starttab
[o
] + m_lengthtab
[o
] > (uint
)m_data
.size()) {
238 kDebug(399) << "image corrupt (sanity check failed)";
242 if (!readData(img
)) {
243 kDebug(399) << "image corrupt (incomplete scanline)";
251 ///////////////////////////////////////////////////////////////////////////////
253 void RLEData::write(QDataStream
& s
)
255 for (int i
= 0; i
< size(); i
++)
260 bool RLEData::operator<(const RLEData
& b
) const
263 for (int i
= 0; i
< qMin(size(), b
.size()); i
++) {
269 return size() < b
.size();
273 uint
RLEMap::insert(const uchar
*d
, uint l
)
275 RLEData data
= RLEData(d
, l
, m_offset
);
276 Iterator it
= find(data
);
281 return QMap
<RLEData
, uint
>::insert(data
, m_counter
++).value();
285 QVector
<const RLEData
*> RLEMap::vector()
287 QVector
<const RLEData
*> v(size());
288 for (Iterator it
= begin(); it
!= end(); ++it
)
289 v
.insert(it
.value(), &it
.key());
295 uchar
SGIImage::intensity(uchar c
)
305 uint
SGIImage::compact(uchar
*d
, uchar
*s
)
307 uchar
*dest
= d
, *src
= s
, patt
, *t
, *end
= s
+ m_xsize
;
310 for (n
= 0, t
= src
; t
+ 2 < end
&& !(*t
== t
[1] && *t
== t
[2]); t
++)
314 i
= n
> 126 ? 126 : n
;
325 for (n
= 1; src
< end
&& *src
== patt
; src
++)
329 i
= n
> 126 ? 126 : n
;
340 bool SGIImage::scanData(const QImage
& img
)
342 quint32
*start
= m_starttab
;
343 QByteArray
lineguard(m_xsize
* 2, 0);
344 QByteArray
bufguard(m_xsize
, 0);
345 uchar
*line
= (uchar
*)lineguard
.data();
346 uchar
*buf
= (uchar
*)bufguard
.data();
351 for (y
= 0; y
< m_ysize
; y
++) {
352 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(m_ysize
- y
- 1));
353 for (x
= 0; x
< m_xsize
; x
++)
354 buf
[x
] = intensity(qRed(*c
++));
355 len
= compact(line
, buf
);
356 *start
++ = m_rlemap
.insert(line
, len
);
363 for (y
= 0; y
< m_ysize
; y
++) {
364 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(m_ysize
- y
- 1));
365 for (x
= 0; x
< m_xsize
; x
++)
366 buf
[x
] = intensity(qGreen(*c
++));
367 len
= compact(line
, buf
);
368 *start
++ = m_rlemap
.insert(line
, len
);
371 for (y
= 0; y
< m_ysize
; y
++) {
372 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(m_ysize
- y
- 1));
373 for (x
= 0; x
< m_xsize
; x
++)
374 buf
[x
] = intensity(qBlue(*c
++));
375 len
= compact(line
, buf
);
376 *start
++ = m_rlemap
.insert(line
, len
);
383 for (y
= 0; y
< m_ysize
; y
++) {
384 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(m_ysize
- y
- 1));
385 for (x
= 0; x
< m_xsize
; x
++)
386 buf
[x
] = intensity(qAlpha(*c
++));
387 len
= compact(line
, buf
);
388 *start
++ = m_rlemap
.insert(line
, len
);
395 void SGIImage::writeHeader()
397 m_stream
<< quint16(0x01da);
398 m_stream
<< m_rle
<< m_bpc
<< m_dim
;
399 m_stream
<< m_xsize
<< m_ysize
<< m_zsize
;
400 m_stream
<< m_pixmin
<< m_pixmax
;
401 m_stream
<< quint32(0);
403 m_stream
<< m_colormap
;
404 for (int i
= 0; i
< 404; i
++)
405 m_stream
<< quint8(0);
409 void SGIImage::writeRle()
412 kDebug(399) << "writing RLE data";
417 for (i
= 0; i
< m_numrows
; i
++)
418 m_stream
<< quint32(m_rlevector
[m_starttab
[i
]]->offset());
420 // write length table
421 for (i
= 0; i
< m_numrows
; i
++)
422 m_stream
<< quint32(m_rlevector
[m_starttab
[i
]]->size());
425 for (i
= 0; (int)i
< m_rlevector
.size(); i
++)
426 const_cast<RLEData
*>(m_rlevector
[i
])->write(m_stream
);
430 void SGIImage::writeVerbatim(const QImage
& img
)
433 kDebug(399) << "writing verbatim data";
439 for (y
= 0; y
< m_ysize
; y
++) {
440 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(m_ysize
- y
- 1));
441 for (x
= 0; x
< m_xsize
; x
++)
442 m_stream
<< quint8(qRed(*c
++));
449 for (y
= 0; y
< m_ysize
; y
++) {
450 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(m_ysize
- y
- 1));
451 for (x
= 0; x
< m_xsize
; x
++)
452 m_stream
<< quint8(qGreen(*c
++));
455 for (y
= 0; y
< m_ysize
; y
++) {
456 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(m_ysize
- y
- 1));
457 for (x
= 0; x
< m_xsize
; x
++)
458 m_stream
<< quint8(qBlue(*c
++));
465 for (y
= 0; y
< m_ysize
; y
++) {
466 c
= reinterpret_cast<const QRgb
*>(img
.scanLine(m_ysize
- y
- 1));
467 for (x
= 0; x
< m_xsize
; x
++)
468 m_stream
<< quint8(qAlpha(*c
++));
473 bool SGIImage::writeImage(const QImage
& image
)
475 kDebug(399) << "writing ";
478 m_dim
= 2, m_zsize
= 1;
480 m_dim
= 3, m_zsize
= 3;
482 if (img
.format() == QImage::Format_ARGB32
)
483 m_dim
= 3, m_zsize
++;
485 img
= img
.convertToFormat(QImage::Format_RGB32
);
487 kDebug(399) << "can't convert image to depth 32";
492 m_xsize
= img
.width();
493 m_ysize
= img
.height();
498 m_numrows
= m_ysize
* m_zsize
;
500 m_starttab
= new quint32
[m_numrows
];
501 m_rlemap
.setBaseOffset(512 + m_numrows
* 2 * sizeof(quint32
));
503 if (!scanData(img
)) {
504 kDebug(399) << "this can't happen";
508 m_rlevector
= m_rlemap
.vector();
510 long verbatim_size
= m_numrows
* m_xsize
;
511 long rle_size
= m_numrows
* 2 * sizeof(quint32
);
512 for (int i
= 0; i
< m_rlevector
.size(); i
++)
513 rle_size
+= m_rlevector
[i
]->size();
515 kDebug(399) << "minimum intensity: " << m_pixmin
;
516 kDebug(399) << "maximum intensity: " << m_pixmax
;
517 kDebug(399) << "saved scanlines: " << m_numrows
- m_rlemap
.size();
518 kDebug(399) << "total savings: " << (verbatim_size
- rle_size
) << " bytes";
519 kDebug(399) << "compression: " << (rle_size
* 100.0 / verbatim_size
) << '%';
521 if (verbatim_size
<= rle_size
)
529 RGBHandler::RGBHandler()
533 bool RGBHandler::canRead() const
535 return canRead(device());
538 bool RGBHandler::read(QImage
*outImage
)
540 SGIImage
sgi(device());
542 if (!sgi
.readImage(*outImage
)) {
549 bool RGBHandler::write(const QImage
&image
)
551 SGIImage
sgi(device());
553 if (!sgi
.writeImage(image
))
559 QByteArray
RGBHandler::name() const
564 bool RGBHandler::canRead(QIODevice
*device
)
567 qWarning("RGBHandler::canRead() called with no device");
571 qint64 oldPos
= device
->pos();
572 QByteArray head
= device
->readLine(64);
573 int readBytes
= head
.size();
575 if (device
->isSequential()) {
576 while (readBytes
> 0)
577 device
->ungetChar(head
[readBytes
-- - 1]);
579 device
->seek(oldPos
);
582 const QRegExp
regexp("^\x01\xda\x01[\x01\x02]");
585 return data
.contains(regexp
);
589 class RGBPlugin
: public QImageIOPlugin
592 QStringList
keys() const;
593 Capabilities
capabilities(QIODevice
*device
, const QByteArray
&format
) const;
594 QImageIOHandler
*create(QIODevice
*device
, const QByteArray
&format
= QByteArray()) const;
597 QStringList
RGBPlugin::keys() const
599 return QStringList() << "rgb"<<"RGB"<<"rgba"<<"RGBA"
600 <<"bw"<<"BW"<<"sgi"<<"SGI";
603 QImageIOPlugin::Capabilities
RGBPlugin::capabilities(QIODevice
*device
, const QByteArray
&format
) const
605 if (format
== "rgb" || format
== "rgb" || format
== "RGB" ||
606 format
== "rgba" || format
== "RGBA" || format
== "bw" ||
607 format
== "BW" || format
== "sgi" || format
== "SGI")
608 return Capabilities(CanRead
| CanWrite
);
609 if (!format
.isEmpty())
611 if (!device
->isOpen())
615 if (device
->isReadable() && RGBHandler::canRead(device
))
617 if (device
->isWritable())
622 QImageIOHandler
*RGBPlugin::create(QIODevice
*device
, const QByteArray
&format
) const
624 QImageIOHandler
*handler
= new RGBHandler
;
625 handler
->setDevice(device
);
626 handler
->setFormat(format
);
630 Q_EXPORT_STATIC_PLUGIN(RGBPlugin
)
631 Q_EXPORT_PLUGIN2(rgb
, RGBPlugin
)