• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • 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 <qimage.h>
00028 #include <kdebug.h>
00029 
00030 
00032 
00033 
00034 KDE_EXPORT void kimgio_rgb_read(QImageIO *io)
00035 {
00036     SGIImage sgi(io);
00037     QImage img;
00038 
00039     if (!sgi.readImage(img)) {
00040         io->setImage(0);
00041         io->setStatus(-1);
00042         return;
00043     }
00044 
00045     io->setImage(img);
00046     io->setStatus(0);
00047 }
00048 
00049 
00050 KDE_EXPORT void kimgio_rgb_write(QImageIO *io)
00051 {
00052     SGIImage sgi(io);
00053     QImage img = io->image();
00054 
00055     if (!sgi.writeImage(img))
00056         io->setStatus(-1);
00057 
00058     io->setStatus(0);
00059 }
00060 
00061 
00063 
00064 
00065 SGIImage::SGIImage(QImageIO *io) :
00066     m_io(io),
00067     m_starttab(0),
00068     m_lengthtab(0)
00069 {
00070     m_dev = io->ioDevice();
00071     m_stream.setDevice(m_dev);
00072 }
00073 
00074 
00075 SGIImage::~SGIImage()
00076 {
00077     delete[] m_starttab;
00078     delete[] m_lengthtab;
00079 }
00080 
00081 
00083 
00084 
00085 bool SGIImage::getRow(uchar *dest)
00086 {
00087     int n, i;
00088     if (!m_rle) {
00089         for (i = 0; i < m_xsize; i++) {
00090             if (m_pos >= m_data.end())
00091                 return false;
00092             dest[i] = uchar(*m_pos);
00093             m_pos += m_bpc;
00094         }
00095         return true;
00096     }
00097 
00098     for (i = 0; i < m_xsize;) {
00099         if (m_bpc == 2)
00100             m_pos++;
00101         n = *m_pos & 0x7f;
00102         if (!n)
00103             break;
00104 
00105         if (*m_pos++ & 0x80) {
00106             for (; i < m_xsize && n--; i++) {
00107                 *dest++ = *m_pos;
00108                 m_pos += m_bpc;
00109             }
00110         } else {
00111             for (; i < m_xsize && n--; i++)
00112                 *dest++ = *m_pos;
00113 
00114             m_pos += m_bpc;
00115         }
00116     }
00117     return i == m_xsize;
00118 }
00119 
00120 
00121 bool SGIImage::readData(QImage& img)
00122 {
00123     QRgb *c;
00124     Q_UINT32 *start = m_starttab;
00125     QByteArray lguard(m_xsize);
00126     uchar *line = (uchar *)lguard.data();
00127     unsigned x, y;
00128 
00129     if (!m_rle)
00130         m_pos = m_data.begin();
00131 
00132     for (y = 0; y < m_ysize; y++) {
00133         if (m_rle)
00134             m_pos = m_data.begin() + *start++;
00135         if (!getRow(line))
00136             return false;
00137         c = (QRgb *)img.scanLine(m_ysize - y - 1);
00138         for (x = 0; x < m_xsize; x++, c++)
00139             *c = qRgb(line[x], line[x], line[x]);
00140     }
00141 
00142     if (m_zsize == 1)
00143         return true;
00144 
00145     if (m_zsize != 2) {
00146         for (y = 0; y < m_ysize; y++) {
00147             if (m_rle)
00148                 m_pos = m_data.begin() + *start++;
00149             if (!getRow(line))
00150                 return false;
00151             c = (QRgb *)img.scanLine(m_ysize - y - 1);
00152             for (x = 0; x < m_xsize; x++, c++)
00153                 *c = qRgb(qRed(*c), line[x], line[x]);
00154         }
00155 
00156         for (y = 0; y < m_ysize; y++) {
00157             if (m_rle)
00158                 m_pos = m_data.begin() + *start++;
00159             if (!getRow(line))
00160                 return false;
00161             c = (QRgb *)img.scanLine(m_ysize - y - 1);
00162             for (x = 0; x < m_xsize; x++, c++)
00163                 *c = qRgb(qRed(*c), qGreen(*c), line[x]);
00164         }
00165 
00166         if (m_zsize == 3)
00167             return true;
00168     }
00169 
00170     for (y = 0; y < m_ysize; y++) {
00171         if (m_rle)
00172             m_pos = m_data.begin() + *start++;
00173         if (!getRow(line))
00174             return false;
00175         c = (QRgb *)img.scanLine(m_ysize - y - 1);
00176         for (x = 0; x < m_xsize; x++, c++)
00177             *c = qRgba(qRed(*c), qGreen(*c), qBlue(*c), line[x]);
00178     }
00179 
00180     return true;
00181 }
00182 
00183 
00184 bool SGIImage::readImage(QImage& img)
00185 {
00186     Q_INT8 u8;
00187     Q_INT16 u16;
00188     Q_INT32 u32;
00189 
00190     kdDebug(399) << "reading '" << m_io->fileName() << '\'' << endl;
00191 
00192     // magic
00193     m_stream >> u16;
00194     if (u16 != 0x01da)
00195         return false;
00196 
00197     // verbatim/rle
00198     m_stream >> m_rle;
00199     kdDebug(399) << (m_rle ? "RLE" : "verbatim") << endl;
00200     if (m_rle > 1)
00201         return false;
00202 
00203     // bytes per channel
00204     m_stream >> m_bpc;
00205     kdDebug(399) << "bytes per channel: " << int(m_bpc) << endl;
00206     if (m_bpc == 1)
00207         ;
00208     else if (m_bpc == 2)
00209         kdDebug(399) << "dropping least significant byte" << endl;
00210     else
00211         return false;
00212 
00213     // number of dimensions
00214     m_stream >> m_dim;
00215     kdDebug(399) << "dimensions: " << m_dim << endl;
00216     if (m_dim < 1 || m_dim > 3)
00217         return false;
00218 
00219     m_stream >> m_xsize >> m_ysize >> m_zsize >> m_pixmin >> m_pixmax >> u32;
00220     kdDebug(399) << "x: " << m_xsize << endl;
00221     kdDebug(399) << "y: " << m_ysize << endl;
00222     kdDebug(399) << "z: " << m_zsize << endl;
00223 
00224     // name
00225     m_stream.readRawBytes(m_imagename, 80);
00226     m_imagename[79] = '\0';
00227     m_io->setDescription(m_imagename);
00228 
00229     m_stream >> m_colormap;
00230     kdDebug(399) << "colormap: " << m_colormap << endl;
00231     if (m_colormap != NORMAL)
00232         return false;       // only NORMAL supported
00233 
00234     for (int i = 0; i < 404; i++)
00235         m_stream >> u8;
00236 
00237     if (m_dim == 1) {
00238         kdDebug(399) << "1-dimensional images aren't supported yet" << endl;
00239         return false;
00240     }
00241 
00242     if( m_stream.atEnd())
00243         return false;
00244 
00245     m_numrows = m_ysize * m_zsize;
00246 
00247     if (!img.create(m_xsize, m_ysize, 32)) {
00248         kdDebug(399) << "cannot create image" << endl;
00249         return false;
00250     }
00251 
00252     if (m_zsize == 2 || m_zsize == 4)
00253         img.setAlphaBuffer(true);
00254     else if (m_zsize > 4)
00255         kdDebug(399) << "using first 4 of " << m_zsize << " channels" << endl;
00256 
00257     if (m_rle) {
00258         uint l;
00259         m_starttab = new Q_UINT32[m_numrows];
00260         for (l = 0; !m_stream.atEnd() && l < m_numrows; l++) {
00261             m_stream >> m_starttab[l];
00262             m_starttab[l] -= 512 + m_numrows * 2 * sizeof(Q_UINT32);
00263         }
00264 
00265         m_lengthtab = new Q_UINT32[m_numrows];
00266         for (l = 0; l < m_numrows; l++)
00267             m_stream >> m_lengthtab[l];
00268     }
00269 
00270     m_data = m_dev->readAll();
00271 
00272     // sanity check
00273     if (m_rle)
00274         for (uint o = 0; o < m_numrows; o++)
00275             // don't change to greater-or-equal!
00276             if (m_starttab[o] + m_lengthtab[o] > m_data.size()) {
00277                 kdDebug(399) << "image corrupt (sanity check failed)" << endl;
00278                 return false;
00279             }
00280 
00281     if (!readData(img)) {
00282         kdDebug(399) << "image corrupt (incomplete scanline)" << endl;
00283         return false;
00284     }
00285 
00286     return true;
00287 }
00288 
00289 
00291 
00292 
00293 // TODO remove; for debugging purposes only
00294 void RLEData::print(QString desc) const
00295 {
00296     QString s = desc + ": ";
00297     for (uint i = 0; i < size(); i++)
00298         s += QString::number(at(i)) + ",";
00299     kdDebug() << "--- " << s << endl;
00300 }
00301 
00302 
00303 void RLEData::write(QDataStream& s)
00304 {
00305     for (unsigned i = 0; i < size(); i++)
00306         s << at(i);
00307 }
00308 
00309 
00310 bool RLEData::operator<(const RLEData& b) const
00311 {
00312     uchar ac, bc;
00313     for (unsigned i = 0; i < QMIN(size(), b.size()); i++) {
00314         ac = at(i);
00315         bc = b[i];
00316         if (ac != bc)
00317             return ac < bc;
00318     }
00319     return size() < b.size();
00320 }
00321 
00322 
00323 uint RLEMap::insert(const uchar *d, uint l)
00324 {
00325     RLEData data = RLEData(d, l, m_offset);
00326     Iterator it = find(data);
00327     if (it != end())
00328         return it.data();
00329 
00330     m_offset += l;
00331     return QMap<RLEData, uint>::insert(data, m_counter++).data();
00332 }
00333 
00334 
00335 QPtrVector<RLEData> RLEMap::vector()
00336 {
00337     QPtrVector<RLEData> v(size());
00338     for (Iterator it = begin(); it != end(); ++it)
00339         v.insert(it.data(), &it.key());
00340 
00341     return v;
00342 }
00343 
00344 
00345 uchar SGIImage::intensity(uchar c)
00346 {
00347     if (c < m_pixmin)
00348         m_pixmin = c;
00349     if (c > m_pixmax)
00350         m_pixmax = c;
00351     return c;
00352 }
00353 
00354 
00355 uint SGIImage::compact(uchar *d, uchar *s)
00356 {
00357     uchar *dest = d, *src = s, patt, *t, *end = s + m_xsize;
00358     int i, n;
00359     while (src < end) {
00360         for (n = 0, t = src; t + 2 < end && !(*t == t[1] && *t == t[2]); t++)
00361             n++;
00362 
00363         while (n) {
00364             i = n > 126 ? 126 : n;
00365             n -= i;
00366             *dest++ = 0x80 | i;
00367             while (i--)
00368                 *dest++ = *src++;
00369         }
00370 
00371         if (src == end)
00372             break;
00373 
00374         patt = *src++;
00375         for (n = 1; src < end && *src == patt; src++)
00376             n++;
00377 
00378         while (n) {
00379             i = n > 126 ? 126 : n;
00380             n -= i;
00381             *dest++ = i;
00382             *dest++ = patt;
00383         }
00384     }
00385     *dest++ = 0;
00386     return dest - d;
00387 }
00388 
00389 
00390 bool SGIImage::scanData(const QImage& img)
00391 {
00392     Q_UINT32 *start = m_starttab;
00393     QCString lineguard(m_xsize * 2);
00394     QCString bufguard(m_xsize);
00395     uchar *line = (uchar *)lineguard.data();
00396     uchar *buf = (uchar *)bufguard.data();
00397     QRgb *c;
00398     unsigned x, y;
00399     uint len;
00400 
00401     for (y = 0; y < m_ysize; y++) {
00402         c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
00403         for (x = 0; x < m_xsize; x++)
00404             buf[x] = intensity(qRed(*c++));
00405         len = compact(line, buf);
00406         *start++ = m_rlemap.insert(line, len);
00407     }
00408 
00409     if (m_zsize == 1)
00410         return true;
00411 
00412     if (m_zsize != 2) {
00413         for (y = 0; y < m_ysize; y++) {
00414             c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
00415             for (x = 0; x < m_xsize; x++)
00416                 buf[x] = intensity(qGreen(*c++));
00417             len = compact(line, buf);
00418             *start++ = m_rlemap.insert(line, len);
00419         }
00420 
00421         for (y = 0; y < m_ysize; y++) {
00422             c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
00423             for (x = 0; x < m_xsize; x++)
00424                 buf[x] = intensity(qBlue(*c++));
00425             len = compact(line, buf);
00426             *start++ = m_rlemap.insert(line, len);
00427         }
00428 
00429         if (m_zsize == 3)
00430             return true;
00431     }
00432 
00433     for (y = 0; y < m_ysize; y++) {
00434         c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
00435         for (x = 0; x < m_xsize; x++)
00436             buf[x] = intensity(qAlpha(*c++));
00437         len = compact(line, buf);
00438         *start++ = m_rlemap.insert(line, len);
00439     }
00440 
00441     return true;
00442 }
00443 
00444 
00445 void SGIImage::writeHeader()
00446 {
00447     m_stream << Q_UINT16(0x01da);
00448     m_stream << m_rle << m_bpc << m_dim;
00449     m_stream << m_xsize << m_ysize << m_zsize;
00450     m_stream << m_pixmin << m_pixmax;
00451     m_stream << Q_UINT32(0);
00452 
00453     uint i;
00454     QString desc = m_io->description();
00455     kdDebug(399) << "Description: " << desc << endl;
00456     desc.truncate(79);
00457 
00458     for (i = 0; i < desc.length(); i++)
00459         m_imagename[i] = desc.latin1()[i];
00460     for (; i < 80; i++)
00461         m_imagename[i] = '\0';
00462     m_stream.writeRawBytes(m_imagename, 80);
00463 
00464     m_stream << m_colormap;
00465     for (i = 0; i < 404; i++)
00466         m_stream << Q_UINT8(0);
00467 }
00468 
00469 
00470 void SGIImage::writeRle()
00471 {
00472     m_rle = 1;
00473     kdDebug(399) << "writing RLE data" << endl;
00474     writeHeader();
00475     uint i;
00476 
00477     // write start table
00478     for (i = 0; i < m_numrows; i++)
00479         m_stream << Q_UINT32(m_rlevector[m_starttab[i]]->offset());
00480 
00481     // write length table
00482     for (i = 0; i < m_numrows; i++)
00483         m_stream << Q_UINT32(m_rlevector[m_starttab[i]]->size());
00484 
00485     // write data
00486     for (i = 0; i < m_rlevector.size(); i++)
00487         m_rlevector[i]->write(m_stream);
00488 }
00489 
00490 
00491 void SGIImage::writeVerbatim(const QImage& img)
00492 {
00493     m_rle = 0;
00494     kdDebug(399) << "writing verbatim data" << endl;
00495     writeHeader();
00496 
00497     QRgb *c;
00498     unsigned x, y;
00499 
00500     for (y = 0; y < m_ysize; y++) {
00501         c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
00502         for (x = 0; x < m_xsize; x++)
00503             m_stream << Q_UINT8(qRed(*c++));
00504     }
00505 
00506     if (m_zsize == 1)
00507         return;
00508 
00509     if (m_zsize != 2) {
00510         for (y = 0; y < m_ysize; y++) {
00511             c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
00512             for (x = 0; x < m_xsize; x++)
00513                 m_stream << Q_UINT8(qGreen(*c++));
00514         }
00515 
00516         for (y = 0; y < m_ysize; y++) {
00517             c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
00518             for (x = 0; x < m_xsize; x++)
00519                 m_stream << Q_UINT8(qBlue(*c++));
00520         }
00521 
00522         if (m_zsize == 3)
00523             return;
00524     }
00525 
00526     for (y = 0; y < m_ysize; y++) {
00527         c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
00528         for (x = 0; x < m_xsize; x++)
00529             m_stream << Q_UINT8(qAlpha(*c++));
00530     }
00531 }
00532 
00533 
00534 bool SGIImage::writeImage(QImage& img)
00535 {
00536     kdDebug(399) << "writing '" << m_io->fileName() << '\'' << endl;
00537 
00538     if (img.allGray())
00539         m_dim = 2, m_zsize = 1;
00540     else
00541         m_dim = 3, m_zsize = 3;
00542 
00543     if (img.hasAlphaBuffer())
00544         m_dim = 3, m_zsize++;
00545 
00546     img = img.convertDepth(32);
00547     if (img.isNull()) {
00548         kdDebug(399) << "can't convert image to depth 32" << endl;
00549         return false;
00550     }
00551 
00552     m_bpc = 1;
00553     m_xsize = img.width();
00554     m_ysize = img.height();
00555     m_pixmin = ~0;
00556     m_pixmax = 0;
00557     m_colormap = NORMAL;
00558 
00559     m_numrows = m_ysize * m_zsize;
00560 
00561     m_starttab = new Q_UINT32[m_numrows];
00562     m_rlemap.setBaseOffset(512 + m_numrows * 2 * sizeof(Q_UINT32));
00563 
00564     if (!scanData(img)) {
00565         kdDebug(399) << "this can't happen" << endl;
00566         return false;
00567     }
00568 
00569     m_rlevector = m_rlemap.vector();
00570 
00571     long verbatim_size = m_numrows * m_xsize;
00572     long rle_size = m_numrows * 2 * sizeof(Q_UINT32);
00573     for (uint i = 0; i < m_rlevector.size(); i++)
00574         rle_size += m_rlevector[i]->size();
00575 
00576     kdDebug(399) << "minimum intensity: " << m_pixmin << endl;
00577     kdDebug(399) << "maximum intensity: " << m_pixmax << endl;
00578     kdDebug(399) << "saved scanlines: " << m_numrows - m_rlemap.size() << endl;
00579     kdDebug(399) << "total savings: " << (verbatim_size - rle_size) << " bytes" << endl;
00580     kdDebug(399) << "compression: " << (rle_size * 100.0 / verbatim_size) << '%' << endl;
00581 
00582     if (verbatim_size <= rle_size || m_io->quality() > 50)
00583         writeVerbatim(img);
00584     else
00585         writeRle();
00586     return true;
00587 }
00588 
00589 

KImgIO

Skip menu "KImgIO"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
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