KImgIO
ras.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 Copyright (C) 2003 Dominik Seichter <domseichter@web.de> 00003 Copyright (C) 2004 Ignacio CastaƱo <castano@ludicon.com> 00004 Copyright (C) 2010 Troy Unrau <troy@kde.org> 00005 00006 This program is free software; you can redistribute it and/or 00007 modify it under the terms of the Lesser GNU General Public 00008 License as published by the Free Software Foundation; either 00009 version 2 of the License, or (at your option) any later version. 00010 */ 00011 00012 #include "ras.h" 00013 00014 #include <QtGui/QImage> 00015 #include <QtCore/QDataStream> 00016 00017 #include <kdebug.h> 00018 00019 namespace { // Private. 00020 // format info from http://www.fileformat.info/format/sunraster/egff.htm 00021 00022 // Header format of saved files. 00023 quint32 rasMagicBigEndian = 0x59a66a95; 00024 // quint32 rasMagicLittleEndian = 0x956aa659; # used to support wrong encoded files 00025 00026 enum RASType { 00027 RAS_TYPE_OLD = 0x0, 00028 RAS_TYPE_STANDARD = 0x1, 00029 RAS_TYPE_BYTE_ENCODED = 0x2, 00030 RAS_TYPE_RGB_FORMAT = 0x3, 00031 RAS_TYPE_TIFF_FORMAT = 0x4, 00032 RAS_TYPE_IFF_FORMAT = 0x5, 00033 RAS_TYPE_EXPERIMENTAL = 0xFFFF 00034 }; 00035 00036 enum RASColorMapType { 00037 RAS_COLOR_MAP_TYPE_NONE = 0x0, 00038 RAS_COLOR_MAP_TYPE_RGB = 0x1, 00039 RAS_COLOR_MAP_TYPE_RAW = 0x2 00040 }; 00041 00042 struct RasHeader { 00043 quint32 MagicNumber; 00044 quint32 Width; 00045 quint32 Height; 00046 quint32 Depth; 00047 quint32 Length; 00048 quint32 Type; 00049 quint32 ColorMapType; 00050 quint32 ColorMapLength; 00051 enum { SIZE = 32 }; // 8 fields of four bytes each 00052 }; 00053 00054 static QDataStream & operator>> ( QDataStream & s, RasHeader & head ) 00055 { 00056 s >> head.MagicNumber; 00057 s >> head.Width; 00058 s >> head.Height; 00059 s >> head.Depth; 00060 s >> head.Length; 00061 s >> head.Type; 00062 s >> head.ColorMapType; 00063 s >> head.ColorMapLength; 00064 /*qDebug() << "MagicNumber: " << head.MagicNumber 00065 << "Width: " << head.Width 00066 << "Height: " << head.Height 00067 << "Depth: " << head.Depth 00068 << "Length: " << head.Length 00069 << "Type: " << head.Type 00070 << "ColorMapType: " << head.ColorMapType 00071 << "ColorMapLength: " << head.ColorMapLength;*/ 00072 return s; 00073 } 00074 00075 static bool IsSupported( const RasHeader & head ) 00076 { 00077 // check magic number 00078 if ( head.MagicNumber != rasMagicBigEndian) { 00079 return false; 00080 } 00081 // check for an appropriate depth 00082 // we support 8bit+palette, 24bit and 32bit ONLY! 00083 // TODO: add support for 1bit 00084 if ( ! ((head.Depth == 8 && head.ColorMapType == 1) 00085 || head.Depth == 24 || head.Depth == 32) ){ 00086 return false; 00087 } 00088 // the Type field adds support for RLE(BGR), RGB and other encodings 00089 // we support Type 1: Normal(BGR) and Type 3: Normal(RGB) ONLY! 00090 // TODO: add support for Type 2: RLE(BGR) & Type 4,5: TIFF/IFF 00091 if ( ! (head.Type == 1 || head.Type == 3) ){ 00092 return false; 00093 } 00094 // Old files didn't have Length set - reject them for now 00095 // TODO: add length recalculation to support old files 00096 if ( !head.Length ) { 00097 return false; 00098 } 00099 return true; 00100 } 00101 00102 static bool LoadRAS( QDataStream & s, const RasHeader & ras, QImage &img ) 00103 { 00104 s.device()->seek(RasHeader::SIZE); 00105 // Read palette if needed. 00106 QVector<quint8> palette(ras.ColorMapLength); 00107 if ( ras.ColorMapType == 1 ) { 00108 for (quint32 i = 0; i < ras.ColorMapLength; ++i) { s >> palette[i]; } 00109 } 00110 00111 // each line must be a factor of 16 bits, so they may contain padding 00112 // this will be 1 if padding required, 0 otherwise 00113 int paddingrequired = (ras.Width*(ras.Depth/8) % 2); 00114 00115 // qDebug() << "paddingrequired: " << paddingrequired; 00116 // don't trust ras.Length 00117 QVector<quint8> input(ras.Length); 00118 00119 int i = 0; 00120 while ( ! s.atEnd()) { 00121 s >> input[i]; 00122 // I guess we need to find out if we're at the end of a line 00123 if ( paddingrequired && i != 0 && !(i % (ras.Width*(ras.Depth/8))) ) { 00124 s >> input[i]; 00125 } 00126 i++; 00127 } 00128 00129 // Allocate image 00130 img = QImage(ras.Width, ras.Height, QImage::Format_ARGB32); 00131 00132 // Reconstruct image from RGB palette if we have a palette 00133 // TODO: make generic so it works with 24bit or 32bit palettes 00134 if ( ras.ColorMapType == 1 && ras.Depth == 8) { 00135 quint8 red, green, blue; 00136 for ( quint32 y = 0; y < ras.Height; y++ ){ 00137 for ( quint32 x = 0; x < ras.Width; x++ ) { 00138 red = palette[(int)input[y*ras.Width + x]]; 00139 green = palette[(int)input[y*ras.Width + x] + (ras.ColorMapLength/3)]; 00140 blue = palette[(int)input[y*ras.Width + x] + 2*(ras.ColorMapLength/3)]; 00141 img.setPixel(x, y, qRgb(red, green, blue)); 00142 } 00143 } 00144 00145 } 00146 00147 if ( ras.ColorMapType == 0 && ras.Depth == 24 && (ras.Type == 1 || ras.Type == 2)) { 00148 quint8 red, green, blue; 00149 for ( quint32 y = 0; y < ras.Height; y++ ){ 00150 for ( quint32 x = 0; x < ras.Width; x++ ) { 00151 red = input[y*3*ras.Width + x*3 + 2]; 00152 green = input[y*3*ras.Width + x*3 + 1]; 00153 blue = input[y*3*ras.Width + x*3]; 00154 img.setPixel(x, y, qRgb(red, green, blue)); 00155 } 00156 } 00157 } 00158 00159 if ( ras.ColorMapType == 0 && ras.Depth == 24 && ras.Type == 3) { 00160 quint8 red, green, blue; 00161 for ( quint32 y = 0; y < ras.Height; y++ ){ 00162 for ( quint32 x = 0; x < ras.Width; x++ ) { 00163 red = input[y*3*ras.Width + x*3]; 00164 green = input[y*3*ras.Width + x*3 + 1]; 00165 blue = input[y*3*ras.Width + x*3 + 2]; 00166 img.setPixel(x, y, qRgb(red, green, blue)); 00167 } 00168 } 00169 } 00170 00171 if ( ras.ColorMapType == 0 && ras.Depth == 32 && (ras.Type == 1 || ras.Type == 2)) { 00172 quint8 red, green, blue; 00173 for ( quint32 y = 0; y < ras.Height; y++ ){ 00174 for ( quint32 x = 0; x < ras.Width; x++ ) { 00175 red = input[y*4*ras.Width + x*4 + 3]; 00176 green = input[y*4*ras.Width + x*4 + 2]; 00177 blue = input[y*4*ras.Width + x*4 + 1]; 00178 img.setPixel(x, y, qRgb(red, green, blue)); 00179 } 00180 } 00181 } 00182 00183 if ( ras.ColorMapType == 0 && ras.Depth == 32 && ras.Type == 3 ) { 00184 quint8 red, green, blue; 00185 for ( quint32 y = 0; y < ras.Height; y++ ){ 00186 for ( quint32 x = 0; x < ras.Width; x++ ) { 00187 red = input[y*4*ras.Width + x*4 + 1]; 00188 green = input[y*4*ras.Width + x*4 + 2]; 00189 blue = input[y*4*ras.Width + x*4 + 3]; 00190 img.setPixel(x, y, qRgb(red, green, blue)); 00191 } 00192 } 00193 } 00194 00195 return true; 00196 } 00197 } // namespace 00198 00199 RASHandler::RASHandler() 00200 { 00201 } 00202 00203 QByteArray RASHandler::name() const 00204 { 00205 return "ras"; 00206 } 00207 00208 bool RASHandler::canRead() const 00209 { 00210 if (canRead(device())) { 00211 setFormat("ras"); 00212 return true; 00213 } 00214 return false; 00215 } 00216 00217 bool RASHandler::canRead(QIODevice *device) 00218 { 00219 if (!device) { 00220 qWarning("RASHandler::canRead() called with no device"); 00221 return false; 00222 } 00223 00224 if (device->isSequential()) { 00225 qWarning("Reading ras files from sequential devices not supported"); 00226 return false; 00227 } 00228 00229 qint64 oldPos = device->pos(); 00230 QByteArray head = device->read(RasHeader::SIZE); // header is exactly 32 bytes, always FIXME 00231 int readBytes = head.size(); // this should always be 32 bytes 00232 00233 device->seek(oldPos); 00234 00235 if (readBytes < RasHeader::SIZE) { 00236 return false; 00237 } 00238 00239 QDataStream stream(head); 00240 stream.setByteOrder(QDataStream::BigEndian); 00241 RasHeader ras; 00242 stream >> ras; 00243 return IsSupported(ras); 00244 } 00245 00246 bool RASHandler::read(QImage *outImage) 00247 { 00248 QDataStream s( device() ); 00249 s.setByteOrder( QDataStream::BigEndian ); 00250 00251 // Read image header. 00252 RasHeader ras; 00253 s >> ras; 00254 // TODO: add support for old versions of RAS where Length may be zero in header 00255 s.device()->seek( RasHeader::SIZE + ras.Length + ras.ColorMapLength ); 00256 00257 // Check image file format. Type 2 is RLE, which causing seeking to be silly. 00258 if( !s.atEnd() && ras.Type != 2) { 00259 kDebug(399) << "This RAS file is not valid, or an older version of the format."; 00260 return false; 00261 } 00262 00263 // Check supported file types. 00264 if( !IsSupported(ras) ) { 00265 kDebug(399) << "This RAS file is not supported."; 00266 return false; 00267 } 00268 00269 QImage img; 00270 bool result = LoadRAS(s, ras, img); 00271 00272 if( result == false ) { 00273 kDebug(399) << "Error loading RAS file."; 00274 return false; 00275 } 00276 00277 *outImage = img; 00278 return true; 00279 } 00280 /* 00281 bool RASHandler::write(const QImage &image){ 00282 return false; 00283 }*/ 00284 00285 class RASPlugin : public QImageIOPlugin 00286 { 00287 public: 00288 QStringList keys() const; 00289 Capabilities capabilities(QIODevice *device, const QByteArray &format) const; 00290 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; 00291 }; 00292 00293 QStringList RASPlugin::keys() const 00294 { 00295 return QStringList() << "ras" << "RAS"; 00296 } 00297 00298 QImageIOPlugin::Capabilities RASPlugin::capabilities(QIODevice *device, const QByteArray &format) const 00299 { 00300 00301 if (format == "ras" || format == "RAS") 00302 return Capabilities(CanRead); 00303 // return Capabilities(CanRead | CanWrite); 00304 if (!format.isEmpty()) 00305 return 0; 00306 if (!device->isOpen()) 00307 return 0; 00308 00309 Capabilities cap; 00310 if (device->isReadable() && RASHandler::canRead(device)) 00311 cap |= CanRead; 00312 if (device->isWritable()) 00313 cap |= CanWrite; 00314 return cap; 00315 } 00316 00317 QImageIOHandler *RASPlugin::create(QIODevice *device, const QByteArray &format) const 00318 { 00319 QImageIOHandler *handler = new RASHandler; 00320 handler->setDevice(device); 00321 handler->setFormat(format); 00322 return handler; 00323 } 00324 00325 00326 Q_EXPORT_STATIC_PLUGIN(RASPlugin) 00327 Q_EXPORT_PLUGIN2(ras, RASPlugin)
KDE 4.6 API Reference