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

KImgIO

rgb.cpp

Go to the documentation of this file.
00001 // kimgio module for SGI images
00002 //
00003 // Copyright (C) 2004  Melchior FRANZ  <mfranz@kde.org>
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 License as
00007 // published by the Free Software Foundation; either version 2 of the
00008 // License, or (at your option) any later version.
00009 
00010 
00011 /* this code supports:
00012  * reading:
00013  *     everything, except images with 1 dimension or images with
00014  *     mapmode != NORMAL (e.g. dithered); Images with 16 bit
00015  *     precision or more than 4 layers are stripped down.
00016  * writing:
00017  *     Run Length Encoded (RLE) or Verbatim (uncompressed)
00018  *     (whichever is smaller)
00019  *
00020  * Please report if you come across rgb/rgba/sgi/bw files that aren't
00021  * recognized. Also report applications that can't deal with images
00022  * saved by this filter.
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     // magic
00158     _stream >> u16;
00159     if (u16 != 0x01da)
00160         return false;
00161 
00162     // verbatim/rle
00163     _stream >> _rle;
00164     kDebug(399) << (_rle ? "RLE" : "verbatim");
00165     if (_rle > 1)
00166         return false;
00167 
00168     // bytes per channel
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     // number of dimensions
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     // name
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;        // only NORMAL supported
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     // sanity check
00234     if (_rle)
00235         for (uint o = 0; o < _numrows; o++)
00236             // don't change to greater-or-equal!
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     // write start table
00422     for (i = 0; i < _numrows; i++)
00423         _stream << quint32(_rlevector[_starttab[i]]->offset());
00424 
00425     // write length table
00426     for (i = 0; i < _numrows; i++)
00427         _stream << quint32(_rlevector[_starttab[i]]->size());
00428 
00429     // write data
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 "; // TODO add filename
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 

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