summaryrefslogtreecommitdiffstats
path: root/kimgio/rgb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kimgio/rgb.cpp')
-rw-r--r--kimgio/rgb.cpp589
1 files changed, 589 insertions, 0 deletions
diff --git a/kimgio/rgb.cpp b/kimgio/rgb.cpp
new file mode 100644
index 000000000..2e62f4f6f
--- /dev/null
+++ b/kimgio/rgb.cpp
@@ -0,0 +1,589 @@
+// kimgio module for SGI images
+//
+// Copyright (C) 2004 Melchior FRANZ <mfranz@kde.org>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the Lesser GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+
+
+/* this code supports:
+ * reading:
+ * everything, except images with 1 dimension or images with
+ * mapmode != NORMAL (e.g. dithered); Images with 16 bit
+ * precision or more than 4 layers are stripped down.
+ * writing:
+ * Run Length Encoded (RLE) or Verbatim (uncompressed)
+ * (whichever is smaller)
+ *
+ * Please report if you come across rgb/rgba/sgi/bw files that aren't
+ * recognized. Also report applications that can't deal with images
+ * saved by this filter.
+ */
+
+
+#include "rgb.h"
+#include <qimage.h>
+#include <kdebug.h>
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+KDE_EXPORT void kimgio_rgb_read(QImageIO *io)
+{
+ SGIImage sgi(io);
+ QImage img;
+
+ if (!sgi.readImage(img)) {
+ io->setImage(0);
+ io->setStatus(-1);
+ return;
+ }
+
+ io->setImage(img);
+ io->setStatus(0);
+}
+
+
+KDE_EXPORT void kimgio_rgb_write(QImageIO *io)
+{
+ SGIImage sgi(io);
+ QImage img = io->image();
+
+ if (!sgi.writeImage(img))
+ io->setStatus(-1);
+
+ io->setStatus(0);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+SGIImage::SGIImage(QImageIO *io) :
+ m_io(io),
+ m_starttab(0),
+ m_lengthtab(0)
+{
+ m_dev = io->ioDevice();
+ m_stream.setDevice(m_dev);
+}
+
+
+SGIImage::~SGIImage()
+{
+ delete[] m_starttab;
+ delete[] m_lengthtab;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+bool SGIImage::getRow(uchar *dest)
+{
+ int n, i;
+ if (!m_rle) {
+ for (i = 0; i < m_xsize; i++) {
+ if (m_pos >= m_data.end())
+ return false;
+ dest[i] = uchar(*m_pos);
+ m_pos += m_bpc;
+ }
+ return true;
+ }
+
+ for (i = 0; i < m_xsize;) {
+ if (m_bpc == 2)
+ m_pos++;
+ n = *m_pos & 0x7f;
+ if (!n)
+ break;
+
+ if (*m_pos++ & 0x80) {
+ for (; i < m_xsize && n--; i++) {
+ *dest++ = *m_pos;
+ m_pos += m_bpc;
+ }
+ } else {
+ for (; i < m_xsize && n--; i++)
+ *dest++ = *m_pos;
+
+ m_pos += m_bpc;
+ }
+ }
+ return i == m_xsize;
+}
+
+
+bool SGIImage::readData(QImage& img)
+{
+ QRgb *c;
+ Q_UINT32 *start = m_starttab;
+ QByteArray lguard(m_xsize);
+ uchar *line = (uchar *)lguard.data();
+ unsigned x, y;
+
+ if (!m_rle)
+ m_pos = m_data.begin();
+
+ for (y = 0; y < m_ysize; y++) {
+ if (m_rle)
+ m_pos = m_data.begin() + *start++;
+ if (!getRow(line))
+ return false;
+ c = (QRgb *)img.scanLine(m_ysize - y - 1);
+ for (x = 0; x < m_xsize; x++, c++)
+ *c = qRgb(line[x], line[x], line[x]);
+ }
+
+ if (m_zsize == 1)
+ return true;
+
+ if (m_zsize != 2) {
+ for (y = 0; y < m_ysize; y++) {
+ if (m_rle)
+ m_pos = m_data.begin() + *start++;
+ if (!getRow(line))
+ return false;
+ c = (QRgb *)img.scanLine(m_ysize - y - 1);
+ for (x = 0; x < m_xsize; x++, c++)
+ *c = qRgb(qRed(*c), line[x], line[x]);
+ }
+
+ for (y = 0; y < m_ysize; y++) {
+ if (m_rle)
+ m_pos = m_data.begin() + *start++;
+ if (!getRow(line))
+ return false;
+ c = (QRgb *)img.scanLine(m_ysize - y - 1);
+ for (x = 0; x < m_xsize; x++, c++)
+ *c = qRgb(qRed(*c), qGreen(*c), line[x]);
+ }
+
+ if (m_zsize == 3)
+ return true;
+ }
+
+ for (y = 0; y < m_ysize; y++) {
+ if (m_rle)
+ m_pos = m_data.begin() + *start++;
+ if (!getRow(line))
+ return false;
+ c = (QRgb *)img.scanLine(m_ysize - y - 1);
+ for (x = 0; x < m_xsize; x++, c++)
+ *c = qRgba(qRed(*c), qGreen(*c), qBlue(*c), line[x]);
+ }
+
+ return true;
+}
+
+
+bool SGIImage::readImage(QImage& img)
+{
+ Q_INT8 u8;
+ Q_INT16 u16;
+ Q_INT32 u32;
+
+ kdDebug(399) << "reading '" << m_io->fileName() << '\'' << endl;
+
+ // magic
+ m_stream >> u16;
+ if (u16 != 0x01da)
+ return false;
+
+ // verbatim/rle
+ m_stream >> m_rle;
+ kdDebug(399) << (m_rle ? "RLE" : "verbatim") << endl;
+ if (m_rle > 1)
+ return false;
+
+ // bytes per channel
+ m_stream >> m_bpc;
+ kdDebug(399) << "bytes per channel: " << int(m_bpc) << endl;
+ if (m_bpc == 1)
+ ;
+ else if (m_bpc == 2)
+ kdDebug(399) << "dropping least significant byte" << endl;
+ else
+ return false;
+
+ // number of dimensions
+ m_stream >> m_dim;
+ kdDebug(399) << "dimensions: " << m_dim << endl;
+ if (m_dim < 1 || m_dim > 3)
+ return false;
+
+ m_stream >> m_xsize >> m_ysize >> m_zsize >> m_pixmin >> m_pixmax >> u32;
+ kdDebug(399) << "x: " << m_xsize << endl;
+ kdDebug(399) << "y: " << m_ysize << endl;
+ kdDebug(399) << "z: " << m_zsize << endl;
+
+ // name
+ m_stream.readRawBytes(m_imagename, 80);
+ m_imagename[79] = '\0';
+ m_io->setDescription(m_imagename);
+
+ m_stream >> m_colormap;
+ kdDebug(399) << "colormap: " << m_colormap << endl;
+ if (m_colormap != NORMAL)
+ return false; // only NORMAL supported
+
+ for (int i = 0; i < 404; i++)
+ m_stream >> u8;
+
+ if (m_dim == 1) {
+ kdDebug(399) << "1-dimensional images aren't supported yet" << endl;
+ return false;
+ }
+
+ if( m_stream.atEnd())
+ return false;
+
+ m_numrows = m_ysize * m_zsize;
+
+ if (!img.create(m_xsize, m_ysize, 32)) {
+ kdDebug(399) << "cannot create image" << endl;
+ return false;
+ }
+
+ if (m_zsize == 2 || m_zsize == 4)
+ img.setAlphaBuffer(true);
+ else if (m_zsize > 4)
+ kdDebug(399) << "using first 4 of " << m_zsize << " channels" << endl;
+
+ if (m_rle) {
+ uint l;
+ m_starttab = new Q_UINT32[m_numrows];
+ for (l = 0; !m_stream.atEnd() && l < m_numrows; l++) {
+ m_stream >> m_starttab[l];
+ m_starttab[l] -= 512 + m_numrows * 2 * sizeof(Q_UINT32);
+ }
+
+ m_lengthtab = new Q_UINT32[m_numrows];
+ for (l = 0; l < m_numrows; l++)
+ m_stream >> m_lengthtab[l];
+ }
+
+ m_data = m_dev->readAll();
+
+ // sanity check
+ if (m_rle)
+ for (uint o = 0; o < m_numrows; o++)
+ // don't change to greater-or-equal!
+ if (m_starttab[o] + m_lengthtab[o] > m_data.size()) {
+ kdDebug(399) << "image corrupt (sanity check failed)" << endl;
+ return false;
+ }
+
+ if (!readData(img)) {
+ kdDebug(399) << "image corrupt (incomplete scanline)" << endl;
+ return false;
+ }
+
+ return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+// TODO remove; for debugging purposes only
+void RLEData::print(QString desc) const
+{
+ QString s = desc + ": ";
+ for (uint i = 0; i < size(); i++)
+ s += QString::number(at(i)) + ",";
+ kdDebug() << "--- " << s << endl;
+}
+
+
+void RLEData::write(QDataStream& s)
+{
+ for (unsigned i = 0; i < size(); i++)
+ s << at(i);
+}
+
+
+bool RLEData::operator<(const RLEData& b) const
+{
+ uchar ac, bc;
+ for (unsigned i = 0; i < QMIN(size(), b.size()); i++) {
+ ac = at(i);
+ bc = b[i];
+ if (ac != bc)
+ return ac < bc;
+ }
+ return size() < b.size();
+}
+
+
+uint RLEMap::insert(const uchar *d, uint l)
+{
+ RLEData data = RLEData(d, l, m_offset);
+ Iterator it = find(data);
+ if (it != end())
+ return it.data();
+
+ m_offset += l;
+ return QMap<RLEData, uint>::insert(data, m_counter++).data();
+}
+
+
+QPtrVector<RLEData> RLEMap::vector()
+{
+ QPtrVector<RLEData> v(size());
+ for (Iterator it = begin(); it != end(); ++it)
+ v.insert(it.data(), &it.key());
+
+ return v;
+}
+
+
+uchar SGIImage::intensity(uchar c)
+{
+ if (c < m_pixmin)
+ m_pixmin = c;
+ if (c > m_pixmax)
+ m_pixmax = c;
+ return c;
+}
+
+
+uint SGIImage::compact(uchar *d, uchar *s)
+{
+ uchar *dest = d, *src = s, patt, *t, *end = s + m_xsize;
+ int i, n;
+ while (src < end) {
+ for (n = 0, t = src; t + 2 < end && !(*t == t[1] && *t == t[2]); t++)
+ n++;
+
+ while (n) {
+ i = n > 126 ? 126 : n;
+ n -= i;
+ *dest++ = 0x80 | i;
+ while (i--)
+ *dest++ = *src++;
+ }
+
+ if (src == end)
+ break;
+
+ patt = *src++;
+ for (n = 1; src < end && *src == patt; src++)
+ n++;
+
+ while (n) {
+ i = n > 126 ? 126 : n;
+ n -= i;
+ *dest++ = i;
+ *dest++ = patt;
+ }
+ }
+ *dest++ = 0;
+ return dest - d;
+}
+
+
+bool SGIImage::scanData(const QImage& img)
+{
+ Q_UINT32 *start = m_starttab;
+ QCString lineguard(m_xsize * 2);
+ QCString bufguard(m_xsize);
+ uchar *line = (uchar *)lineguard.data();
+ uchar *buf = (uchar *)bufguard.data();
+ QRgb *c;
+ unsigned x, y;
+ uint len;
+
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ buf[x] = intensity(qRed(*c++));
+ len = compact(line, buf);
+ *start++ = m_rlemap.insert(line, len);
+ }
+
+ if (m_zsize == 1)
+ return true;
+
+ if (m_zsize != 2) {
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ buf[x] = intensity(qGreen(*c++));
+ len = compact(line, buf);
+ *start++ = m_rlemap.insert(line, len);
+ }
+
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ buf[x] = intensity(qBlue(*c++));
+ len = compact(line, buf);
+ *start++ = m_rlemap.insert(line, len);
+ }
+
+ if (m_zsize == 3)
+ return true;
+ }
+
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ buf[x] = intensity(qAlpha(*c++));
+ len = compact(line, buf);
+ *start++ = m_rlemap.insert(line, len);
+ }
+
+ return true;
+}
+
+
+void SGIImage::writeHeader()
+{
+ m_stream << Q_UINT16(0x01da);
+ m_stream << m_rle << m_bpc << m_dim;
+ m_stream << m_xsize << m_ysize << m_zsize;
+ m_stream << m_pixmin << m_pixmax;
+ m_stream << Q_UINT32(0);
+
+ uint i;
+ QString desc = m_io->description();
+ kdDebug(399) << "Description: " << desc << endl;
+ desc.truncate(79);
+
+ for (i = 0; i < desc.length(); i++)
+ m_imagename[i] = desc.latin1()[i];
+ for (; i < 80; i++)
+ m_imagename[i] = '\0';
+ m_stream.writeRawBytes(m_imagename, 80);
+
+ m_stream << m_colormap;
+ for (i = 0; i < 404; i++)
+ m_stream << Q_UINT8(0);
+}
+
+
+void SGIImage::writeRle()
+{
+ m_rle = 1;
+ kdDebug(399) << "writing RLE data" << endl;
+ writeHeader();
+ uint i;
+
+ // write start table
+ for (i = 0; i < m_numrows; i++)
+ m_stream << Q_UINT32(m_rlevector[m_starttab[i]]->offset());
+
+ // write length table
+ for (i = 0; i < m_numrows; i++)
+ m_stream << Q_UINT32(m_rlevector[m_starttab[i]]->size());
+
+ // write data
+ for (i = 0; i < m_rlevector.size(); i++)
+ m_rlevector[i]->write(m_stream);
+}
+
+
+void SGIImage::writeVerbatim(const QImage& img)
+{
+ m_rle = 0;
+ kdDebug(399) << "writing verbatim data" << endl;
+ writeHeader();
+
+ QRgb *c;
+ unsigned x, y;
+
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ m_stream << Q_UINT8(qRed(*c++));
+ }
+
+ if (m_zsize == 1)
+ return;
+
+ if (m_zsize != 2) {
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ m_stream << Q_UINT8(qGreen(*c++));
+ }
+
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ m_stream << Q_UINT8(qBlue(*c++));
+ }
+
+ if (m_zsize == 3)
+ return;
+ }
+
+ for (y = 0; y < m_ysize; y++) {
+ c = reinterpret_cast<QRgb *>(img.scanLine(m_ysize - y - 1));
+ for (x = 0; x < m_xsize; x++)
+ m_stream << Q_UINT8(qAlpha(*c++));
+ }
+}
+
+
+bool SGIImage::writeImage(QImage& img)
+{
+ kdDebug(399) << "writing '" << m_io->fileName() << '\'' << endl;
+
+ if (img.allGray())
+ m_dim = 2, m_zsize = 1;
+ else
+ m_dim = 3, m_zsize = 3;
+
+ if (img.hasAlphaBuffer())
+ m_dim = 3, m_zsize++;
+
+ img = img.convertDepth(32);
+ if (img.isNull()) {
+ kdDebug(399) << "can't convert image to depth 32" << endl;
+ return false;
+ }
+
+ m_bpc = 1;
+ m_xsize = img.width();
+ m_ysize = img.height();
+ m_pixmin = ~0;
+ m_pixmax = 0;
+ m_colormap = NORMAL;
+
+ m_numrows = m_ysize * m_zsize;
+
+ m_starttab = new Q_UINT32[m_numrows];
+ m_rlemap.setBaseOffset(512 + m_numrows * 2 * sizeof(Q_UINT32));
+
+ if (!scanData(img)) {
+ kdDebug(399) << "this can't happen" << endl;
+ return false;
+ }
+
+ m_rlevector = m_rlemap.vector();
+
+ long verbatim_size = m_numrows * m_xsize;
+ long rle_size = m_numrows * 2 * sizeof(Q_UINT32);
+ for (uint i = 0; i < m_rlevector.size(); i++)
+ rle_size += m_rlevector[i]->size();
+
+ kdDebug(399) << "minimum intensity: " << m_pixmin << endl;
+ kdDebug(399) << "maximum intensity: " << m_pixmax << endl;
+ kdDebug(399) << "saved scanlines: " << m_numrows - m_rlemap.size() << endl;
+ kdDebug(399) << "total savings: " << (verbatim_size - rle_size) << " bytes" << endl;
+ kdDebug(399) << "compression: " << (rle_size * 100.0 / verbatim_size) << '%' << endl;
+
+ if (verbatim_size <= rle_size || m_io->quality() > 50)
+ writeVerbatim(img);
+ else
+ writeRle();
+ return true;
+}
+
+