File indexing completed on 2024-04-28 15:25:43
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 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "ras_p.h" 0011 #include "util_p.h" 0012 0013 #include <QDataStream> 0014 #include <QDebug> 0015 #include <QImage> 0016 0017 namespace // Private. 0018 { 0019 // format info from http://www.fileformat.info/format/sunraster/egff.htm 0020 0021 // Header format of saved files. 0022 quint32 rasMagicBigEndian = 0x59a66a95; 0023 // quint32 rasMagicLittleEndian = 0x956aa659; # used to support wrong encoded files 0024 0025 enum RASType { 0026 RAS_TYPE_OLD = 0x0, 0027 RAS_TYPE_STANDARD = 0x1, 0028 RAS_TYPE_BYTE_ENCODED = 0x2, 0029 RAS_TYPE_RGB_FORMAT = 0x3, 0030 RAS_TYPE_TIFF_FORMAT = 0x4, 0031 RAS_TYPE_IFF_FORMAT = 0x5, 0032 RAS_TYPE_EXPERIMENTAL = 0xFFFF, 0033 }; 0034 0035 enum RASColorMapType { 0036 RAS_COLOR_MAP_TYPE_NONE = 0x0, 0037 RAS_COLOR_MAP_TYPE_RGB = 0x1, 0038 RAS_COLOR_MAP_TYPE_RAW = 0x2, 0039 }; 0040 0041 struct RasHeader { 0042 quint32 MagicNumber; 0043 quint32 Width; 0044 quint32 Height; 0045 quint32 Depth; 0046 quint32 Length; 0047 quint32 Type; 0048 quint32 ColorMapType; 0049 quint32 ColorMapLength; 0050 enum { 0051 SIZE = 32, 0052 }; // 8 fields of four bytes each 0053 }; 0054 0055 static QDataStream &operator>>(QDataStream &s, RasHeader &head) 0056 { 0057 s >> head.MagicNumber; 0058 s >> head.Width; 0059 s >> head.Height; 0060 s >> head.Depth; 0061 s >> head.Length; 0062 s >> head.Type; 0063 s >> head.ColorMapType; 0064 s >> head.ColorMapLength; 0065 /*qDebug() << "MagicNumber: " << head.MagicNumber 0066 << "Width: " << head.Width 0067 << "Height: " << head.Height 0068 << "Depth: " << head.Depth 0069 << "Length: " << head.Length 0070 << "Type: " << head.Type 0071 << "ColorMapType: " << head.ColorMapType 0072 << "ColorMapLength: " << head.ColorMapLength;*/ 0073 return s; 0074 } 0075 0076 static bool IsSupported(const RasHeader &head) 0077 { 0078 // check magic number 0079 if (head.MagicNumber != rasMagicBigEndian) { 0080 return false; 0081 } 0082 // check for an appropriate depth 0083 // we support 8bit+palette, 24bit and 32bit ONLY! 0084 // TODO: add support for 1bit 0085 if (!((head.Depth == 8 && head.ColorMapType == 1) || head.Depth == 24 || head.Depth == 32)) { 0086 return false; 0087 } 0088 // the Type field adds support for RLE(BGR), RGB and other encodings 0089 // we support Type 1: Normal(BGR) and Type 3: Normal(RGB) ONLY! 0090 // TODO: add support for Type 2: RLE(BGR) & Type 4,5: TIFF/IFF 0091 if (!(head.Type == 1 || head.Type == 3)) { 0092 return false; 0093 } 0094 // Old files didn't have Length set - reject them for now 0095 // TODO: add length recalculation to support old files 0096 if (!head.Length) { 0097 return false; 0098 } 0099 return true; 0100 } 0101 0102 static bool LoadRAS(QDataStream &s, const RasHeader &ras, QImage &img) 0103 { 0104 s.device()->seek(RasHeader::SIZE); 0105 0106 if (ras.ColorMapLength > kMaxQVectorSize) { 0107 qWarning() << "LoadRAS() unsupported image color map length in file header" << ras.ColorMapLength; 0108 return false; 0109 } 0110 0111 // Read palette if needed. 0112 QVector<quint8> palette(ras.ColorMapLength); 0113 if (ras.ColorMapType == 1) { 0114 for (quint32 i = 0; i < ras.ColorMapLength; ++i) { 0115 s >> palette[i]; 0116 } 0117 } 0118 0119 const int bpp = ras.Depth / 8; 0120 if (ras.Height == 0) { 0121 return false; 0122 } 0123 if (bpp == 0) { 0124 return false; 0125 } 0126 if (ras.Length / ras.Height / bpp < ras.Width) { 0127 qWarning() << "LoadRAS() mistmatch between height and width" << ras.Width << ras.Height << ras.Length << ras.Depth; 0128 return false; 0129 } 0130 if (ras.Length > kMaxQVectorSize) { 0131 qWarning() << "LoadRAS() unsupported image length in file header" << ras.Length; 0132 return false; 0133 } 0134 0135 // each line must be a factor of 16 bits, so they may contain padding 0136 // this will be 1 if padding required, 0 otherwise 0137 const int paddingrequired = (ras.Width * bpp % 2); 0138 0139 // qDebug() << "paddingrequired: " << paddingrequired; 0140 // don't trust ras.Length 0141 QVector<quint8> input(ras.Length); 0142 0143 int i = 0; 0144 while (!s.atEnd() && i < input.size()) { 0145 s >> input[i]; 0146 // I guess we need to find out if we're at the end of a line 0147 if (paddingrequired && i != 0 && !(i % (ras.Width * bpp))) { 0148 s >> input[i]; 0149 } 0150 i++; 0151 } 0152 0153 // Allocate image 0154 img = imageAlloc(ras.Width, ras.Height, QImage::Format_ARGB32); 0155 if (img.isNull()) { 0156 return false; 0157 } 0158 0159 // Reconstruct image from RGB palette if we have a palette 0160 // TODO: make generic so it works with 24bit or 32bit palettes 0161 if (ras.ColorMapType == 1 && ras.Depth == 8) { 0162 quint8 red; 0163 quint8 green; 0164 quint8 blue; 0165 for (quint32 y = 0; y < ras.Height; y++) { 0166 for (quint32 x = 0; x < ras.Width; x++) { 0167 red = palette.value((int)input[y * ras.Width + x]); 0168 green = palette.value((int)input[y * ras.Width + x] + (ras.ColorMapLength / 3)); 0169 blue = palette.value((int)input[y * ras.Width + x] + 2 * (ras.ColorMapLength / 3)); 0170 img.setPixel(x, y, qRgb(red, green, blue)); 0171 } 0172 } 0173 } 0174 0175 if (ras.ColorMapType == 0 && ras.Depth == 24 && (ras.Type == 1 || ras.Type == 2)) { 0176 quint8 red; 0177 quint8 green; 0178 quint8 blue; 0179 for (quint32 y = 0; y < ras.Height; y++) { 0180 for (quint32 x = 0; x < ras.Width; x++) { 0181 red = input[y * 3 * ras.Width + x * 3 + 2]; 0182 green = input[y * 3 * ras.Width + x * 3 + 1]; 0183 blue = input[y * 3 * ras.Width + x * 3]; 0184 img.setPixel(x, y, qRgb(red, green, blue)); 0185 } 0186 } 0187 } 0188 0189 if (ras.ColorMapType == 0 && ras.Depth == 24 && ras.Type == 3) { 0190 quint8 red; 0191 quint8 green; 0192 quint8 blue; 0193 for (quint32 y = 0; y < ras.Height; y++) { 0194 for (quint32 x = 0; x < ras.Width; x++) { 0195 red = input[y * 3 * ras.Width + x * 3]; 0196 green = input[y * 3 * ras.Width + x * 3 + 1]; 0197 blue = input[y * 3 * ras.Width + x * 3 + 2]; 0198 img.setPixel(x, y, qRgb(red, green, blue)); 0199 } 0200 } 0201 } 0202 0203 if (ras.ColorMapType == 0 && ras.Depth == 32 && (ras.Type == 1 || ras.Type == 2)) { 0204 quint8 red; 0205 quint8 green; 0206 quint8 blue; 0207 for (quint32 y = 0; y < ras.Height; y++) { 0208 for (quint32 x = 0; x < ras.Width; x++) { 0209 red = input[y * 4 * ras.Width + x * 4 + 3]; 0210 green = input[y * 4 * ras.Width + x * 4 + 2]; 0211 blue = input[y * 4 * ras.Width + x * 4 + 1]; 0212 img.setPixel(x, y, qRgb(red, green, blue)); 0213 } 0214 } 0215 } 0216 0217 if (ras.ColorMapType == 0 && ras.Depth == 32 && ras.Type == 3) { 0218 quint8 red; 0219 quint8 green; 0220 quint8 blue; 0221 for (quint32 y = 0; y < ras.Height; y++) { 0222 for (quint32 x = 0; x < ras.Width; x++) { 0223 red = input[y * 4 * ras.Width + x * 4 + 1]; 0224 green = input[y * 4 * ras.Width + x * 4 + 2]; 0225 blue = input[y * 4 * ras.Width + x * 4 + 3]; 0226 img.setPixel(x, y, qRgb(red, green, blue)); 0227 } 0228 } 0229 } 0230 0231 return true; 0232 } 0233 } // namespace 0234 0235 RASHandler::RASHandler() 0236 { 0237 } 0238 0239 bool RASHandler::canRead() const 0240 { 0241 if (canRead(device())) { 0242 setFormat("ras"); 0243 return true; 0244 } 0245 return false; 0246 } 0247 0248 bool RASHandler::canRead(QIODevice *device) 0249 { 0250 if (!device) { 0251 qWarning("RASHandler::canRead() called with no device"); 0252 return false; 0253 } 0254 0255 if (device->isSequential()) { 0256 // qWarning("Reading ras files from sequential devices not supported"); 0257 return false; 0258 } 0259 0260 qint64 oldPos = device->pos(); 0261 QByteArray head = device->read(RasHeader::SIZE); // header is exactly 32 bytes, always FIXME 0262 int readBytes = head.size(); // this should always be 32 bytes 0263 0264 device->seek(oldPos); 0265 0266 if (readBytes < RasHeader::SIZE) { 0267 return false; 0268 } 0269 0270 QDataStream stream(head); 0271 stream.setByteOrder(QDataStream::BigEndian); 0272 RasHeader ras; 0273 stream >> ras; 0274 return IsSupported(ras); 0275 } 0276 0277 bool RASHandler::read(QImage *outImage) 0278 { 0279 QDataStream s(device()); 0280 s.setByteOrder(QDataStream::BigEndian); 0281 0282 // Read image header. 0283 RasHeader ras; 0284 s >> ras; 0285 0286 if (ras.ColorMapLength > std::numeric_limits<int>::max()) { 0287 return false; 0288 } 0289 0290 // TODO: add support for old versions of RAS where Length may be zero in header 0291 s.device()->seek(RasHeader::SIZE + ras.Length + ras.ColorMapLength); 0292 0293 // Check image file format. Type 2 is RLE, which causing seeking to be silly. 0294 if (!s.atEnd() && ras.Type != 2) { 0295 // qDebug() << "This RAS file is not valid, or an older version of the format."; 0296 return false; 0297 } 0298 0299 // Check supported file types. 0300 if (!IsSupported(ras)) { 0301 // qDebug() << "This RAS file is not supported."; 0302 return false; 0303 } 0304 0305 QImage img; 0306 bool result = LoadRAS(s, ras, img); 0307 0308 if (result == false) { 0309 // qDebug() << "Error loading RAS file."; 0310 return false; 0311 } 0312 0313 *outImage = img; 0314 return true; 0315 } 0316 0317 QImageIOPlugin::Capabilities RASPlugin::capabilities(QIODevice *device, const QByteArray &format) const 0318 { 0319 if (format == "ras") { 0320 return Capabilities(CanRead); 0321 } 0322 if (!format.isEmpty()) { 0323 return {}; 0324 } 0325 if (!device->isOpen()) { 0326 return {}; 0327 } 0328 0329 Capabilities cap; 0330 if (device->isReadable() && RASHandler::canRead(device)) { 0331 cap |= CanRead; 0332 } 0333 return cap; 0334 } 0335 0336 QImageIOHandler *RASPlugin::create(QIODevice *device, const QByteArray &format) const 0337 { 0338 QImageIOHandler *handler = new RASHandler; 0339 handler->setDevice(device); 0340 handler->setFormat(format); 0341 return handler; 0342 } 0343 0344 #include "moc_ras_p.cpp"