rgb.cpp
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 <tqimage.h> 00028 #include <kdebug.h> 00029 00030 00032 00033 00034 KDE_EXPORT void kimgio_rgb_read(TQImageIO *io) 00035 { 00036 SGIImage sgi(io); 00037 TQImage img; 00038 00039 if (!sgi.readImage(img)) { 00040 io->setImage(TQImage()); 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(TQImageIO *io) 00051 { 00052 SGIImage sgi(io); 00053 TQImage 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(TQImageIO *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(TQImage& img) 00122 { 00123 QRgb *c; 00124 TQ_UINT32 *start = m_starttab; 00125 TQByteArray 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 = tqRgb(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 = tqRgb(tqRed(*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 = tqRgb(tqRed(*c), tqGreen(*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 = tqRgba(tqRed(*c), tqGreen(*c), tqBlue(*c), line[x]); 00178 } 00179 00180 return true; 00181 } 00182 00183 00184 bool SGIImage::readImage(TQImage& img) 00185 { 00186 TQ_INT8 u8; 00187 TQ_INT16 u16; 00188 TQ_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 TQ_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(TQ_UINT32); 00263 } 00264 00265 m_lengthtab = new TQ_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(TQString desc) const 00295 { 00296 TQString s = desc + ": "; 00297 for (uint i = 0; i < size(); i++) 00298 s += TQString::number(at(i)) + ","; 00299 kdDebug() << "--- " << s << endl; 00300 } 00301 00302 00303 void RLEData::write(TQDataStream& 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 < TQMIN(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 TQMap<RLEData, uint>::insert(data, m_counter++).data(); 00332 } 00333 00334 00335 TQPtrVector<RLEData> RLEMap::vector() 00336 { 00337 TQPtrVector<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 TQImage& img) 00391 { 00392 TQ_UINT32 *start = m_starttab; 00393 TQCString lineguard(m_xsize * 2); 00394 TQCString 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 *>(const_cast<TQImage&>(img).scanLine(m_ysize - y - 1)); 00403 for (x = 0; x < m_xsize; x++) 00404 buf[x] = intensity(tqRed(*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 *>(const_cast<TQImage&>(img).scanLine(m_ysize - y - 1)); 00415 for (x = 0; x < m_xsize; x++) 00416 buf[x] = intensity(tqGreen(*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 *>(const_cast<TQImage&>(img).scanLine(m_ysize - y - 1)); 00423 for (x = 0; x < m_xsize; x++) 00424 buf[x] = intensity(tqBlue(*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 *>(const_cast<TQImage&>(img).scanLine(m_ysize - y - 1)); 00435 for (x = 0; x < m_xsize; x++) 00436 buf[x] = intensity(tqAlpha(*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 << TQ_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 << TQ_UINT32(0); 00452 00453 uint i; 00454 TQString 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 << TQ_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 << TQ_UINT32(m_rlevector[m_starttab[i]]->offset()); 00480 00481 // write length table 00482 for (i = 0; i < m_numrows; i++) 00483 m_stream << TQ_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 TQImage& 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 *>(const_cast<TQImage&>(img).scanLine(m_ysize - y - 1)); 00502 for (x = 0; x < m_xsize; x++) 00503 m_stream << TQ_UINT8(tqRed(*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 *>(const_cast<TQImage&>(img).scanLine(m_ysize - y - 1)); 00512 for (x = 0; x < m_xsize; x++) 00513 m_stream << TQ_UINT8(tqGreen(*c++)); 00514 } 00515 00516 for (y = 0; y < m_ysize; y++) { 00517 c = reinterpret_cast<QRgb *>(const_cast<TQImage&>(img).scanLine(m_ysize - y - 1)); 00518 for (x = 0; x < m_xsize; x++) 00519 m_stream << TQ_UINT8(tqBlue(*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 *>(const_cast<TQImage&>(img).scanLine(m_ysize - y - 1)); 00528 for (x = 0; x < m_xsize; x++) 00529 m_stream << TQ_UINT8(tqAlpha(*c++)); 00530 } 00531 } 00532 00533 00534 bool SGIImage::writeImage(TQImage& 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 TQ_UINT32[m_numrows]; 00562 m_rlemap.setBaseOffset(512 + m_numrows * 2 * sizeof(TQ_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(TQ_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