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"