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
KDE 4.6 API Reference