File indexing completed on 2024-04-28 03:54:45

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2003 Dominik Seichter <domseichter@web.de>
0004     SPDX-FileCopyrightText: 2004 Ignacio CastaƱo <castano@ludicon.com>
0005     SPDX-FileCopyrightText: 2010 Troy Unrau <troy@kde.org>
0006     SPDX-FileCopyrightText: 2023 Mirco Miranda <mircomir@outlook.com>
0007 
0008     SPDX-License-Identifier: LGPL-2.0-or-later
0009 */
0010 
0011 #include "ras_p.h"
0012 #include "util_p.h"
0013 
0014 #include <QDataStream>
0015 #include <QDebug>
0016 #include <QImage>
0017 
0018 namespace // Private.
0019 {
0020 // format info from http://www.fileformat.info/format/sunraster/egff.htm
0021 
0022 // Header format of saved files.
0023 quint32 rasMagicBigEndian = 0x59a66a95;
0024 // quint32 rasMagicLittleEndian = 0x956aa659; # used to support wrong encoded files
0025 
0026 enum RASType {
0027     RAS_TYPE_OLD = 0x0,
0028     RAS_TYPE_STANDARD = 0x1,
0029     RAS_TYPE_BYTE_ENCODED = 0x2,
0030     RAS_TYPE_RGB_FORMAT = 0x3,
0031     RAS_TYPE_TIFF_FORMAT = 0x4,
0032     RAS_TYPE_IFF_FORMAT = 0x5,
0033     RAS_TYPE_EXPERIMENTAL = 0xFFFF,
0034 };
0035 
0036 enum RASColorMapType {
0037     RAS_COLOR_MAP_TYPE_NONE = 0x0,
0038     RAS_COLOR_MAP_TYPE_RGB = 0x1,
0039     RAS_COLOR_MAP_TYPE_RAW = 0x2,
0040 };
0041 
0042 struct RasHeader {
0043     quint32 MagicNumber = 0;
0044     quint32 Width = 0;
0045     quint32 Height = 0;
0046     quint32 Depth = 0;
0047     quint32 Length = 0;
0048     quint32 Type = 0;
0049     quint32 ColorMapType = 0;
0050     quint32 ColorMapLength = 0;
0051     enum {
0052         SIZE = 32,
0053     }; // 8 fields of four bytes each
0054 };
0055 
0056 static QDataStream &operator>>(QDataStream &s, RasHeader &head)
0057 {
0058     s >> head.MagicNumber;
0059     s >> head.Width;
0060     s >> head.Height;
0061     s >> head.Depth;
0062     s >> head.Length;
0063     s >> head.Type;
0064     s >> head.ColorMapType;
0065     s >> head.ColorMapLength;
0066     /*qDebug() << "MagicNumber: " << head.MagicNumber
0067              << "Width: " << head.Width
0068              << "Height: " << head.Height
0069              << "Depth: " << head.Depth
0070              << "Length: " << head.Length
0071              << "Type: " << head.Type
0072              << "ColorMapType: " << head.ColorMapType
0073              << "ColorMapLength: " << head.ColorMapLength;*/
0074     return s;
0075 }
0076 
0077 static bool IsSupported(const RasHeader &head)
0078 {
0079     // check magic number
0080     if (head.MagicNumber != rasMagicBigEndian) {
0081         return false;
0082     }
0083     // check for an appropriate depth
0084     if (head.Depth != 1 && head.Depth != 8 && head.Depth != 24 && head.Depth != 32) {
0085         return false;
0086     }
0087     if (head.Width == 0 || head.Height == 0) {
0088         return false;
0089     }
0090     // the Type field adds support for RLE(BGR), RGB and other encodings
0091     // we support Type 1: Normal(BGR), Type 2: RLE(BGR) and Type 3: Normal(RGB) ONLY!
0092     // TODO: add support for Type 4,5: TIFF/IFF
0093     if (!(head.Type == RAS_TYPE_STANDARD || head.Type == RAS_TYPE_RGB_FORMAT || head.Type == RAS_TYPE_BYTE_ENCODED)) {
0094         return false;
0095     }
0096     return true;
0097 }
0098 
0099 static QImage::Format imageFormat(const RasHeader &header)
0100 {
0101     if (header.ColorMapType == RAS_COLOR_MAP_TYPE_RGB) {
0102         return QImage::Format_Indexed8;
0103     }
0104     if (header.Depth == 8 && header.ColorMapType == RAS_COLOR_MAP_TYPE_NONE) {
0105         return QImage::Format_Grayscale8;
0106     }
0107     if (header.Depth == 1) {
0108         return QImage::Format_Mono;
0109     }
0110     return QImage::Format_RGB32;
0111 }
0112 
0113 class LineDecoder
0114 {
0115 public:
0116     LineDecoder(QIODevice *d, const RasHeader &ras)
0117         : device(d)
0118         , header(ras)
0119     {
0120     }
0121 
0122     QByteArray readLine(qint64 size)
0123     {
0124         /* *** uncompressed
0125          */
0126         if (header.Type != RAS_TYPE_BYTE_ENCODED) {
0127             return device->read(size);
0128         }
0129 
0130         /* *** rle compressed
0131          * The Run-length encoding (RLE) scheme optionally used in Sun Raster
0132          * files (Type = 0002h) is used to encode bytes of image data
0133          * separately. RLE encoding may be found in any Sun Raster file
0134          * regardless of the type of image data it contains.
0135          *
0136          * The RLE packets are typically three bytes in size:
0137          * - The first byte is a Flag Value indicating the type of RLE packet.
0138          * - The second byte is the Run Count.
0139          * - The third byte is the Run Value.
0140          *
0141          * A Flag Value of 80h is followed by a Run Count in the range of 01h
0142          * to FFh. The Run Value follows the Run count and is in the range of
0143          * 00h to FFh. The pixel run is the Run Value repeated Run Count times.
0144          * There are two exceptions to this algorithm. First, if the Run Count
0145          * following the Flag Value is 00h, this is an indication that the run
0146          * is a single byte in length and has a value of 80h. And second, if
0147          * the Flag Value is not 80h, then it is assumed that the data is
0148          * unencoded pixel data and is written directly to the output stream.
0149          *
0150          * source: http://www.fileformat.info/format/sunraster/egff.htm
0151          */
0152         for (qsizetype psz = 0, ptr = 0; uncBuffer.size() < size;) {
0153             rleBuffer.append(device->read(std::min(qint64(32768), size)));
0154             qsizetype sz = rleBuffer.size();
0155             if (psz == sz) {
0156                 break; // avoid infinite loop (data corrupted?!)
0157             }
0158             auto data = reinterpret_cast<uchar *>(rleBuffer.data());
0159             for (; ptr < sz;) {
0160                 auto flag = data[ptr++];
0161                 if (flag == 0x80) {
0162                     if (ptr >= sz) {
0163                         ptr -= 1;
0164                         break;
0165                     }
0166                     auto cnt = data[ptr++];
0167                     if (cnt == 0) {
0168                         uncBuffer.append(char(0x80));
0169                         continue;
0170                     } else if (ptr >= sz) {
0171                         ptr -= 2;
0172                         break;
0173                     }
0174                     auto val = data[ptr++];
0175                     uncBuffer.append(QByteArray(1 + cnt, char(val)));
0176                 } else {
0177                     uncBuffer.append(char(flag));
0178                 }
0179             }
0180             if (ptr) { // remove consumed data
0181                 rleBuffer.remove(0, ptr);
0182                 ptr = 0;
0183             }
0184             psz = rleBuffer.size();
0185         }
0186         if (uncBuffer.size() < size) {
0187             return QByteArray(); // something wrong
0188         }
0189         auto line = uncBuffer.mid(0, size);
0190         uncBuffer.remove(0, line.size()); // remove consumed data
0191         return line;
0192     }
0193 
0194 private:
0195     QIODevice *device;
0196     RasHeader header;
0197 
0198     // RLE decoding buffers
0199     QByteArray rleBuffer;
0200     QByteArray uncBuffer;
0201 };
0202 
0203 static bool LoadRAS(QDataStream &s, const RasHeader &ras, QImage &img)
0204 {
0205     s.device()->seek(RasHeader::SIZE);
0206 
0207     // The width of a scan line is always a multiple of 16 bits, padded when necessary.
0208     auto rasLineSize = (qint64(ras.Width) * ras.Depth + 7) / 8;
0209     if (rasLineSize & 1)
0210         ++rasLineSize;
0211     if (rasLineSize > kMaxQVectorSize) {
0212         qWarning() << "LoadRAS() unsupported line size" << rasLineSize;
0213         return false;
0214     }
0215 
0216     // Allocate image
0217     img = imageAlloc(ras.Width, ras.Height, imageFormat(ras));
0218     if (img.isNull()) {
0219         return false;
0220     }
0221 
0222     // Read palette if needed.
0223     if (ras.ColorMapType == RAS_COLOR_MAP_TYPE_RGB) {
0224         QList<quint8> palette(ras.ColorMapLength);
0225         for (quint32 i = 0; i < ras.ColorMapLength; ++i) {
0226             s >> palette[i];
0227         }
0228         QList<QRgb> colorTable;
0229         for (quint32 i = 0, n = ras.ColorMapLength / 3; i < n; ++i) {
0230             colorTable << qRgb(palette.at(i), palette.at(i + n), palette.at(i + 2 * n));
0231         }
0232         for (; colorTable.size() < 256;) {
0233             colorTable << qRgb(255, 255, 255);
0234         }
0235         img.setColorTable(colorTable);
0236         if (s.status() != QDataStream::Ok) {
0237             return false;
0238         }
0239     }
0240 
0241     LineDecoder dec(s.device(), ras);
0242     auto bytesPerLine = std::min(img.bytesPerLine(), qsizetype(rasLineSize));
0243     for (quint32 y = 0; y < ras.Height; ++y) {
0244         auto rasLine = dec.readLine(rasLineSize);
0245         if (rasLine.size() != rasLineSize) {
0246             qWarning() << "LoadRAS() unable to read line" << y << ": the seems corrupted!";
0247             return false;
0248         }
0249 
0250         // Grayscale 1-bit / Grayscale 8-bit (never seen)
0251         if (ras.ColorMapType == RAS_COLOR_MAP_TYPE_NONE && (ras.Depth == 1 || ras.Depth == 8)) {
0252             for (auto &&b : rasLine) {
0253                 b = ~b;
0254             }
0255             std::memcpy(img.scanLine(y), rasLine.constData(), bytesPerLine);
0256             continue;
0257         }
0258 
0259         // Image with palette
0260         if (ras.ColorMapType == RAS_COLOR_MAP_TYPE_RGB && (ras.Depth == 1 || ras.Depth == 8)) {
0261             std::memcpy(img.scanLine(y), rasLine.constData(), bytesPerLine);
0262             continue;
0263         }
0264 
0265         // BGR 24-bit
0266         if (ras.ColorMapType == RAS_COLOR_MAP_TYPE_NONE && ras.Depth == 24 && (ras.Type == RAS_TYPE_STANDARD || ras.Type == RAS_TYPE_BYTE_ENCODED)) {
0267             quint8 red;
0268             quint8 green;
0269             quint8 blue;
0270             auto scanLine = reinterpret_cast<QRgb *>(img.scanLine(y));
0271             for (quint32 x = 0; x < ras.Width; x++) {
0272                 red = rasLine.at(x * 3 + 2);
0273                 green = rasLine.at(x * 3 + 1);
0274                 blue = rasLine.at(x * 3);
0275                 *(scanLine + x) = qRgb(red, green, blue);
0276             }
0277             continue;
0278         }
0279 
0280         // RGB 24-bit
0281         if (ras.ColorMapType == RAS_COLOR_MAP_TYPE_NONE && ras.Depth == 24 && ras.Type == RAS_TYPE_RGB_FORMAT) {
0282             quint8 red;
0283             quint8 green;
0284             quint8 blue;
0285             auto scanLine = reinterpret_cast<QRgb *>(img.scanLine(y));
0286             for (quint32 x = 0; x < ras.Width; x++) {
0287                 red = rasLine.at(x * 3);
0288                 green = rasLine.at(x * 3 + 1);
0289                 blue = rasLine.at(x * 3 + 2);
0290                 *(scanLine + x) = qRgb(red, green, blue);
0291             }
0292             continue;
0293         }
0294 
0295         // BGR 32-bit (not tested: test case missing)
0296         if (ras.ColorMapType == RAS_COLOR_MAP_TYPE_NONE && ras.Depth == 32 && (ras.Type == RAS_TYPE_STANDARD || ras.Type == RAS_TYPE_BYTE_ENCODED)) {
0297             quint8 red;
0298             quint8 green;
0299             quint8 blue;
0300             auto scanLine = reinterpret_cast<QRgb *>(img.scanLine(y));
0301             for (quint32 x = 0; x < ras.Width; x++) {
0302                 red = rasLine.at(x * 4 + 3);
0303                 green = rasLine.at(x * 4 + 2);
0304                 blue = rasLine.at(x * 4 + 1);
0305                 *(scanLine + x) = qRgb(red, green, blue);
0306             }
0307 
0308             continue;
0309         }
0310 
0311         // RGB 32-bit (tested: test case missing due to image too large)
0312         if (ras.ColorMapType == RAS_COLOR_MAP_TYPE_NONE && ras.Depth == 32 && ras.Type == RAS_TYPE_RGB_FORMAT) {
0313             quint8 red;
0314             quint8 green;
0315             quint8 blue;
0316             auto scanLine = reinterpret_cast<QRgb *>(img.scanLine(y));
0317             for (quint32 x = 0; x < ras.Width; x++) {
0318                 red = rasLine.at(x * 4 + 1);
0319                 green = rasLine.at(x * 4 + 2);
0320                 blue = rasLine.at(x * 4 + 3);
0321                 *(scanLine + x) = qRgb(red, green, blue);
0322             }
0323             continue;
0324         }
0325 
0326         qWarning() << "LoadRAS() unsupported format!"
0327                    << "ColorMapType:" << ras.ColorMapType << "Type:" << ras.Type << "Depth:" << ras.Depth;
0328         return false;
0329     }
0330 
0331     return true;
0332 }
0333 } // namespace
0334 
0335 RASHandler::RASHandler()
0336 {
0337 }
0338 
0339 bool RASHandler::canRead() const
0340 {
0341     if (canRead(device())) {
0342         setFormat("ras");
0343         return true;
0344     }
0345     return false;
0346 }
0347 
0348 bool RASHandler::canRead(QIODevice *device)
0349 {
0350     if (!device) {
0351         qWarning("RASHandler::canRead() called with no device");
0352         return false;
0353     }
0354 
0355     if (device->isSequential()) {
0356         // qWarning("Reading ras files from sequential devices not supported");
0357         return false;
0358     }
0359 
0360     qint64 oldPos = device->pos();
0361     QByteArray head = device->read(RasHeader::SIZE); // header is exactly 32 bytes, always FIXME
0362     int readBytes = head.size(); // this should always be 32 bytes
0363 
0364     device->seek(oldPos);
0365 
0366     if (readBytes < RasHeader::SIZE) {
0367         return false;
0368     }
0369 
0370     QDataStream stream(head);
0371     stream.setByteOrder(QDataStream::BigEndian);
0372     RasHeader ras;
0373     stream >> ras;
0374     return IsSupported(ras);
0375 }
0376 
0377 bool RASHandler::read(QImage *outImage)
0378 {
0379     QDataStream s(device());
0380     s.setByteOrder(QDataStream::BigEndian);
0381 
0382     // Read image header.
0383     RasHeader ras;
0384     s >> ras;
0385 
0386     if (ras.ColorMapLength > kMaxQVectorSize) {
0387         qWarning() << "LoadRAS() unsupported image color map length in file header" << ras.ColorMapLength;
0388         return false;
0389     }
0390 
0391     // Check supported file types.
0392     if (!IsSupported(ras)) {
0393         //         qDebug() << "This RAS file is not supported.";
0394         return false;
0395     }
0396 
0397     QImage img;
0398     bool result = LoadRAS(s, ras, img);
0399 
0400     if (result == false) {
0401         //         qDebug() << "Error loading RAS file.";
0402         return false;
0403     }
0404 
0405     *outImage = img;
0406     return true;
0407 }
0408 
0409 bool RASHandler::supportsOption(ImageOption option) const
0410 {
0411     if (option == QImageIOHandler::Size) {
0412         return true;
0413     }
0414     if (option == QImageIOHandler::ImageFormat) {
0415         return true;
0416     }
0417     return false;
0418 }
0419 
0420 QVariant RASHandler::option(ImageOption option) const
0421 {
0422     QVariant v;
0423 
0424     if (option == QImageIOHandler::Size) {
0425         if (auto d = device()) {
0426             // transactions works on both random and sequential devices
0427             d->startTransaction();
0428             auto ba = d->read(RasHeader::SIZE);
0429             d->rollbackTransaction();
0430 
0431             QDataStream s(ba);
0432             s.setByteOrder(QDataStream::BigEndian);
0433 
0434             RasHeader header;
0435             s >> header;
0436 
0437             if (s.status() == QDataStream::Ok && IsSupported(header)) {
0438                 v = QVariant::fromValue(QSize(header.Width, header.Height));
0439             }
0440         }
0441     }
0442 
0443     if (option == QImageIOHandler::ImageFormat) {
0444         if (auto d = device()) {
0445             // transactions works on both random and sequential devices
0446             d->startTransaction();
0447             auto ba = d->read(RasHeader::SIZE);
0448             d->rollbackTransaction();
0449 
0450             QDataStream s(ba);
0451             s.setByteOrder(QDataStream::BigEndian);
0452 
0453             RasHeader header;
0454             s >> header;
0455 
0456             if (s.status() == QDataStream::Ok && IsSupported(header)) {
0457                 v = QVariant::fromValue(imageFormat(header));
0458             }
0459         }
0460     }
0461 
0462     return v;
0463 }
0464 
0465 QImageIOPlugin::Capabilities RASPlugin::capabilities(QIODevice *device, const QByteArray &format) const
0466 {
0467     if (format == "im1" || format == "im8" || format == "im24" || format == "im32" || format == "ras" || format == "sun") {
0468         return Capabilities(CanRead);
0469     }
0470     if (!format.isEmpty()) {
0471         return {};
0472     }
0473     if (!device->isOpen()) {
0474         return {};
0475     }
0476 
0477     Capabilities cap;
0478     if (device->isReadable() && RASHandler::canRead(device)) {
0479         cap |= CanRead;
0480     }
0481     return cap;
0482 }
0483 
0484 QImageIOHandler *RASPlugin::create(QIODevice *device, const QByteArray &format) const
0485 {
0486     QImageIOHandler *handler = new RASHandler;
0487     handler->setDevice(device);
0488     handler->setFormat(format);
0489     return handler;
0490 }
0491 
0492 #include "moc_ras_p.cpp"