00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "rgb.h"
00027 #include <QtGui/QImage>
00028 #include <kdebug.h>
00029
00030
00031 SGIImage::SGIImage(QIODevice *io) :
00032 _starttab(0),
00033 _lengthtab(0)
00034 {
00035 _dev = io;
00036 _stream.setDevice(_dev);
00037 }
00038
00039
00040 SGIImage::~SGIImage()
00041 {
00042 delete[] _starttab;
00043 delete[] _lengthtab;
00044 }
00045
00046
00048
00049
00050 bool SGIImage::getRow(uchar *dest)
00051 {
00052 int n, i;
00053 if (!_rle) {
00054 for (i = 0; i < _xsize; i++) {
00055 if (_pos >= _data.end())
00056 return false;
00057 dest[i] = uchar(*_pos);
00058 _pos += _bpc;
00059 }
00060 return true;
00061 }
00062
00063 for (i = 0; i < _xsize;) {
00064 if (_bpc == 2)
00065 _pos++;
00066 n = *_pos & 0x7f;
00067 if (!n)
00068 break;
00069
00070 if (*_pos++ & 0x80) {
00071 for (; i < _xsize && n--; i++) {
00072 *dest++ = *_pos;
00073 _pos += _bpc;
00074 }
00075 } else {
00076 for (; i < _xsize && n--; i++)
00077 *dest++ = *_pos;
00078
00079 _pos += _bpc;
00080 }
00081 }
00082 return i == _xsize;
00083 }
00084
00085
00086 bool SGIImage::readData(QImage& img)
00087 {
00088 QRgb *c;
00089 quint32 *start = _starttab;
00090 QByteArray lguard(_xsize, 0);
00091 uchar *line = (uchar *)lguard.data();
00092 unsigned x, y;
00093
00094 if (!_rle)
00095 _pos = _data.begin();
00096
00097 for (y = 0; y < _ysize; y++) {
00098 if (_rle)
00099 _pos = _data.begin() + *start++;
00100 if (!getRow(line))
00101 return false;
00102 c = (QRgb *)img.scanLine(_ysize - y - 1);
00103 for (x = 0; x < _xsize; x++, c++)
00104 *c = qRgb(line[x], line[x], line[x]);
00105 }
00106
00107 if (_zsize == 1)
00108 return true;
00109
00110 if (_zsize != 2) {
00111 for (y = 0; y < _ysize; y++) {
00112 if (_rle)
00113 _pos = _data.begin() + *start++;
00114 if (!getRow(line))
00115 return false;
00116 c = (QRgb *)img.scanLine(_ysize - y - 1);
00117 for (x = 0; x < _xsize; x++, c++)
00118 *c = qRgb(qRed(*c), line[x], line[x]);
00119 }
00120
00121 for (y = 0; y < _ysize; y++) {
00122 if (_rle)
00123 _pos = _data.begin() + *start++;
00124 if (!getRow(line))
00125 return false;
00126 c = (QRgb *)img.scanLine(_ysize - y - 1);
00127 for (x = 0; x < _xsize; x++, c++)
00128 *c = qRgb(qRed(*c), qGreen(*c), line[x]);
00129 }
00130
00131 if (_zsize == 3)
00132 return true;
00133 }
00134
00135 for (y = 0; y < _ysize; y++) {
00136 if (_rle)
00137 _pos = _data.begin() + *start++;
00138 if (!getRow(line))
00139 return false;
00140 c = (QRgb *)img.scanLine(_ysize - y - 1);
00141 for (x = 0; x < _xsize; x++, c++)
00142 *c = qRgba(qRed(*c), qGreen(*c), qBlue(*c), line[x]);
00143 }
00144
00145 return true;
00146 }
00147
00148
00149 bool SGIImage::readImage(QImage& img)
00150 {
00151 qint8 u8;
00152 qint16 u16;
00153 qint32 u32;
00154
00155 kDebug(399) << "reading rgb ";
00156
00157
00158 _stream >> u16;
00159 if (u16 != 0x01da)
00160 return false;
00161
00162
00163 _stream >> _rle;
00164 kDebug(399) << (_rle ? "RLE" : "verbatim");
00165 if (_rle > 1)
00166 return false;
00167
00168
00169 _stream >> _bpc;
00170 kDebug(399) << "bytes per channel: " << int(_bpc);
00171 if (_bpc == 1)
00172 ;
00173 else if (_bpc == 2)
00174 kDebug(399) << "dropping least significant byte";
00175 else
00176 return false;
00177
00178
00179 _stream >> _dim;
00180 kDebug(399) << "dimensions: " << _dim;
00181 if (_dim < 1 || _dim > 3)
00182 return false;
00183
00184 _stream >> _xsize >> _ysize >> _zsize >> _pixmin >> _pixmax >> u32;
00185 kDebug(399) << "x: " << _xsize;
00186 kDebug(399) << "y: " << _ysize;
00187 kDebug(399) << "z: " << _zsize;
00188
00189
00190 _stream.readRawData(_imagename, 80);
00191 _imagename[79] = '\0';
00192
00193 _stream >> _colormap;
00194 kDebug(399) << "colormap: " << _colormap;
00195 if (_colormap != NORMAL)
00196 return false;
00197
00198 for (int i = 0; i < 404; i++)
00199 _stream >> u8;
00200
00201 if (_dim == 1) {
00202 kDebug(399) << "1-dimensional images aren't supported yet";
00203 return false;
00204 }
00205
00206 if( _stream.atEnd())
00207 return false;
00208
00209 _numrows = _ysize * _zsize;
00210
00211 img = QImage( _xsize, _ysize, QImage::Format_RGB32 );
00212
00213 if (_zsize == 2 || _zsize == 4)
00214 img = img.convertToFormat(QImage::Format_ARGB32);
00215 else if (_zsize > 4)
00216 kDebug(399) << "using first 4 of " << _zsize << " channels";
00217
00218 if (_rle) {
00219 uint l;
00220 _starttab = new quint32[_numrows];
00221 for (l = 0; !_stream.atEnd() && l < _numrows; l++) {
00222 _stream >> _starttab[l];
00223 _starttab[l] -= 512 + _numrows * 2 * sizeof(quint32);
00224 }
00225
00226 _lengthtab = new quint32[_numrows];
00227 for (l = 0; l < _numrows; l++)
00228 _stream >> _lengthtab[l];
00229 }
00230
00231 _data = _dev->readAll();
00232
00233
00234 if (_rle)
00235 for (uint o = 0; o < _numrows; o++)
00236
00237 if (_starttab[o] + _lengthtab[o] > (uint)_data.size()) {
00238 kDebug(399) << "image corrupt (sanity check failed)";
00239 return false;
00240 }
00241
00242 if (!readData(img)) {
00243 kDebug(399) << "image corrupt (incomplete scanline)";
00244 return false;
00245 }
00246
00247 return true;
00248 }
00249
00250
00252
00253
00254 void RLEData::write(QDataStream& s)
00255 {
00256 for (int i = 0; i < size(); i++)
00257 s << at(i);
00258 }
00259
00260
00261 bool RLEData::operator<(const RLEData& b) const
00262 {
00263 uchar ac, bc;
00264 for (int i = 0; i < qMin(size(), b.size()); i++) {
00265 ac = at(i);
00266 bc = b[i];
00267 if (ac != bc)
00268 return ac < bc;
00269 }
00270 return size() < b.size();
00271 }
00272
00273
00274 uint RLEMap::insert(const uchar *d, uint l)
00275 {
00276 RLEData data = RLEData(d, l, _offset);
00277 Iterator it = find(data);
00278 if (it != end())
00279 return it.value();
00280
00281 _offset += l;
00282 return QMap<RLEData, uint>::insert(data, _counter++).value();
00283 }
00284
00285
00286 QVector<const RLEData*> RLEMap::vector()
00287 {
00288 QVector<const RLEData*> v(size());
00289 for (Iterator it = begin(); it != end(); ++it)
00290 v.replace(it.value(), &it.key());
00291
00292 return v;
00293 }
00294
00295
00296 uchar SGIImage::intensity(uchar c)
00297 {
00298 if (c < _pixmin)
00299 _pixmin = c;
00300 if (c > _pixmax)
00301 _pixmax = c;
00302 return c;
00303 }
00304
00305
00306 uint SGIImage::compact(uchar *d, uchar *s)
00307 {
00308 uchar *dest = d, *src = s, patt, *t, *end = s + _xsize;
00309 int i, n;
00310 while (src < end) {
00311 for (n = 0, t = src; t + 2 < end && !(*t == t[1] && *t == t[2]); t++)
00312 n++;
00313
00314 while (n) {
00315 i = n > 126 ? 126 : n;
00316 n -= i;
00317 *dest++ = 0x80 | i;
00318 while (i--)
00319 *dest++ = *src++;
00320 }
00321
00322 if (src == end)
00323 break;
00324
00325 patt = *src++;
00326 for (n = 1; src < end && *src == patt; src++)
00327 n++;
00328
00329 while (n) {
00330 i = n > 126 ? 126 : n;
00331 n -= i;
00332 *dest++ = i;
00333 *dest++ = patt;
00334 }
00335 }
00336 *dest++ = 0;
00337 return dest - d;
00338 }
00339
00340
00341 bool SGIImage::scanData(const QImage& img)
00342 {
00343 quint32 *start = _starttab;
00344 QByteArray lineguard(_xsize * 2, 0);
00345 QByteArray bufguard(_xsize, 0);
00346 uchar *line = (uchar *)lineguard.data();
00347 uchar *buf = (uchar *)bufguard.data();
00348 const QRgb *c;
00349 unsigned x, y;
00350 uint len;
00351
00352 for (y = 0; y < _ysize; y++) {
00353 c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1));
00354 for (x = 0; x < _xsize; x++)
00355 buf[x] = intensity(qRed(*c++));
00356 len = compact(line, buf);
00357 *start++ = _rlemap.insert(line, len);
00358 }
00359
00360 if (_zsize == 1)
00361 return true;
00362
00363 if (_zsize != 2) {
00364 for (y = 0; y < _ysize; y++) {
00365 c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1));
00366 for (x = 0; x < _xsize; x++)
00367 buf[x] = intensity(qGreen(*c++));
00368 len = compact(line, buf);
00369 *start++ = _rlemap.insert(line, len);
00370 }
00371
00372 for (y = 0; y < _ysize; y++) {
00373 c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1));
00374 for (x = 0; x < _xsize; x++)
00375 buf[x] = intensity(qBlue(*c++));
00376 len = compact(line, buf);
00377 *start++ = _rlemap.insert(line, len);
00378 }
00379
00380 if (_zsize == 3)
00381 return true;
00382 }
00383
00384 for (y = 0; y < _ysize; y++) {
00385 c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1));
00386 for (x = 0; x < _xsize; x++)
00387 buf[x] = intensity(qAlpha(*c++));
00388 len = compact(line, buf);
00389 *start++ = _rlemap.insert(line, len);
00390 }
00391
00392 return true;
00393 }
00394
00395
00396 void SGIImage::writeHeader()
00397 {
00398 _stream << quint16(0x01da);
00399 _stream << _rle << _bpc << _dim;
00400 _stream << _xsize << _ysize << _zsize;
00401 _stream << _pixmin << _pixmax;
00402 _stream << quint32(0);
00403
00404 for (int i = 0; i < 80; i++)
00405 _imagename[i] = '\0';
00406 _stream.writeRawData(_imagename, 80);
00407
00408 _stream << _colormap;
00409 for (int i = 0; i < 404; i++)
00410 _stream << quint8(0);
00411 }
00412
00413
00414 void SGIImage::writeRle()
00415 {
00416 _rle = 1;
00417 kDebug(399) << "writing RLE data";
00418 writeHeader();
00419 uint i;
00420
00421
00422 for (i = 0; i < _numrows; i++)
00423 _stream << quint32(_rlevector[_starttab[i]]->offset());
00424
00425
00426 for (i = 0; i < _numrows; i++)
00427 _stream << quint32(_rlevector[_starttab[i]]->size());
00428
00429
00430 for (i = 0; (int)i < _rlevector.size(); i++)
00431 const_cast<RLEData*>(_rlevector[i])->write(_stream);
00432 }
00433
00434
00435 void SGIImage::writeVerbatim(const QImage& img)
00436 {
00437 _rle = 0;
00438 kDebug(399) << "writing verbatim data";
00439 writeHeader();
00440
00441 const QRgb *c;
00442 unsigned x, y;
00443
00444 for (y = 0; y < _ysize; y++) {
00445 c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1));
00446 for (x = 0; x < _xsize; x++)
00447 _stream << quint8(qRed(*c++));
00448 }
00449
00450 if (_zsize == 1)
00451 return;
00452
00453 if (_zsize != 2) {
00454 for (y = 0; y < _ysize; y++) {
00455 c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1));
00456 for (x = 0; x < _xsize; x++)
00457 _stream << quint8(qGreen(*c++));
00458 }
00459
00460 for (y = 0; y < _ysize; y++) {
00461 c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1));
00462 for (x = 0; x < _xsize; x++)
00463 _stream << quint8(qBlue(*c++));
00464 }
00465
00466 if (_zsize == 3)
00467 return;
00468 }
00469
00470 for (y = 0; y < _ysize; y++) {
00471 c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1));
00472 for (x = 0; x < _xsize; x++)
00473 _stream << quint8(qAlpha(*c++));
00474 }
00475 }
00476
00477
00478 bool SGIImage::writeImage(const QImage& image)
00479 {
00480 kDebug(399) << "writing ";
00481 QImage img = image;
00482 if (img.allGray())
00483 _dim = 2, _zsize = 1;
00484 else
00485 _dim = 3, _zsize = 3;
00486
00487 if (img.format() == QImage::Format_ARGB32)
00488 _dim = 3, _zsize++;
00489
00490 img = img.convertToFormat(QImage::Format_RGB32);
00491 if (img.isNull()) {
00492 kDebug(399) << "can't convert image to depth 32";
00493 return false;
00494 }
00495
00496 _bpc = 1;
00497 _xsize = img.width();
00498 _ysize = img.height();
00499 _pixmin = ~0u;
00500 _pixmax = 0;
00501 _colormap = NORMAL;
00502 _numrows = _ysize * _zsize;
00503 _starttab = new quint32[_numrows];
00504 _rlemap.setBaseOffset(512 + _numrows * 2 * sizeof(quint32));
00505
00506 if (!scanData(img)) {
00507 kDebug(399) << "this can't happen";
00508 return false;
00509 }
00510
00511 _rlevector = _rlemap.vector();
00512
00513 long verbatim_size = _numrows * _xsize;
00514 long rle_size = _numrows * 2 * sizeof(quint32);
00515 for (int i = 0; i < _rlevector.size(); i++)
00516 rle_size += _rlevector[i]->size();
00517
00518 kDebug(399) << "minimum intensity: " << _pixmin;
00519 kDebug(399) << "maximum intensity: " << _pixmax;
00520 kDebug(399) << "saved scanlines: " << _numrows - _rlemap.size();
00521 kDebug(399) << "total savings: " << (verbatim_size - rle_size) << " bytes";
00522 kDebug(399) << "compression: " << (rle_size * 100.0 / verbatim_size) << '%';
00523
00524 if (verbatim_size <= rle_size)
00525 writeVerbatim(img);
00526 else
00527 writeRle();
00528 return true;
00529 }
00530
00531
00533
00534
00535 RGBHandler::RGBHandler()
00536 {
00537 }
00538
00539
00540 bool RGBHandler::canRead() const
00541 {
00542 if (canRead(device())) {
00543 setFormat("rgb");
00544 return true;
00545 }
00546 return false;
00547 }
00548
00549
00550 bool RGBHandler::read(QImage *outImage)
00551 {
00552 SGIImage sgi(device());
00553 return sgi.readImage(*outImage);
00554 }
00555
00556
00557 bool RGBHandler::write(const QImage &image)
00558 {
00559 SGIImage sgi(device());
00560 return sgi.writeImage(image);
00561 }
00562
00563
00564 QByteArray RGBHandler::name() const
00565 {
00566 return "rgb";
00567 }
00568
00569
00570 bool RGBHandler::canRead(QIODevice *device)
00571 {
00572 if (!device) {
00573 qWarning("RGBHandler::canRead() called with no device");
00574 return false;
00575 }
00576
00577 qint64 oldPos = device->pos();
00578 QByteArray head = device->readLine(64);
00579 int readBytes = head.size();
00580
00581 if (device->isSequential()) {
00582 while (readBytes > 0)
00583 device->ungetChar(head[readBytes-- - 1]);
00584
00585 } else {
00586 device->seek(oldPos);
00587 }
00588
00589 const QRegExp regexp("^\x01\xda\x01[\x01\x02]");
00590 QString data(head);
00591
00592 return data.contains(regexp);
00593 }
00594
00595
00597
00598
00599 class RGBPlugin : public QImageIOPlugin
00600 {
00601 public:
00602 QStringList keys() const;
00603 Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
00604 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
00605 };
00606
00607
00608 QStringList RGBPlugin::keys() const
00609 {
00610 return QStringList() << "rgb" << "RGB" << "rgba" << "RGBA" << "bw" << "BW" << "sgi" << "SGI";
00611 }
00612
00613
00614 QImageIOPlugin::Capabilities RGBPlugin::capabilities(QIODevice *device, const QByteArray &format) const
00615 {
00616 if (format == "rgb" || format == "RGB" || format == "rgba" || format == "RGBA"
00617 || format == "bw" || format == "BW" || format == "sgi" || format == "SGI")
00618 return Capabilities(CanRead|CanWrite);
00619
00620 if (!format.isEmpty())
00621 return 0;
00622 if (!device->isOpen())
00623 return 0;
00624
00625 Capabilities cap;
00626 if (device->isReadable() && RGBHandler::canRead(device))
00627 cap |= CanRead;
00628 if (device->isWritable())
00629 cap |= CanWrite;
00630 return cap;
00631 }
00632
00633
00634 QImageIOHandler *RGBPlugin::create(QIODevice *device, const QByteArray &format) const
00635 {
00636 QImageIOHandler *handler = new RGBHandler;
00637 handler->setDevice(device);
00638 handler->setFormat(format);
00639 return handler;
00640 }
00641
00642
00643 Q_EXPORT_STATIC_PLUGIN(RGBPlugin)
00644 Q_EXPORT_PLUGIN2(rgb, RGBPlugin)
00645