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 <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
00193 m_stream >> u16;
00194 if (u16 != 0x01da)
00195 return false;
00196
00197
00198 m_stream >> m_rle;
00199 kdDebug(399) << (m_rle ? "RLE" : "verbatim") << endl;
00200 if (m_rle > 1)
00201 return false;
00202
00203
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
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
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;
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
00273 if (m_rle)
00274 for (uint o = 0; o < m_numrows; o++)
00275
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
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
00478 for (i = 0; i < m_numrows; i++)
00479 m_stream << Q_UINT32(m_rlevector[m_starttab[i]]->offset());
00480
00481
00482 for (i = 0; i < m_numrows; i++)
00483 m_stream << Q_UINT32(m_rlevector[m_starttab[i]]->size());
00484
00485
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