KImgIO
psd.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "psd.h"
00021
00022 #include <QtGui/QImage>
00023 #include <QtCore/QDataStream>
00024
00025 #include <kdebug.h>
00026
00027 typedef quint32 uint;
00028 typedef quint16 ushort;
00029 typedef quint8 uchar;
00030
00031 namespace {
00032
00033 enum ColorMode {
00034 CM_BITMAP = 0,
00035 CM_GRAYSCALE = 1,
00036 CM_INDEXED = 2,
00037 CM_RGB = 3,
00038 CM_CMYK = 4,
00039 CM_MULTICHANNEL = 7,
00040 CM_DUOTONE = 8,
00041 CM_LABCOLOR = 9
00042 };
00043
00044 struct PSDHeader {
00045 uint signature;
00046 ushort version;
00047 uchar reserved[6];
00048 ushort channel_count;
00049 uint height;
00050 uint width;
00051 ushort depth;
00052 ushort color_mode;
00053 };
00054
00055 static QDataStream & operator>> ( QDataStream & s, PSDHeader & header )
00056 {
00057 s >> header.signature;
00058 s >> header.version;
00059 for( int i = 0; i < 6; i++ ) {
00060 s >> header.reserved[i];
00061 }
00062 s >> header.channel_count;
00063 s >> header.height;
00064 s >> header.width;
00065 s >> header.depth;
00066 s >> header.color_mode;
00067 return s;
00068 }
00069 static bool seekBy(QDataStream& s, unsigned int bytes)
00070 {
00071 char buf[4096];
00072 while (bytes) {
00073 unsigned int num= qMin(bytes,( unsigned int )sizeof(buf));
00074 unsigned int l = num;
00075 s.readRawData(buf, l);
00076 if(l != num)
00077 return false;
00078 bytes -= num;
00079 }
00080 return true;
00081 }
00082
00083
00084 static bool IsValid( const PSDHeader & header )
00085 {
00086 if( header.signature != 0x38425053 ) {
00087 return false;
00088 }
00089 return true;
00090 }
00091
00092
00093 static bool IsSupported( const PSDHeader & header )
00094 {
00095 if( header.version != 1 ) {
00096 return false;
00097 }
00098 if( header.channel_count > 16 ) {
00099 return false;
00100 }
00101 if( header.depth != 8 ) {
00102 return false;
00103 }
00104 if( header.color_mode != CM_RGB ) {
00105 return false;
00106 }
00107 return true;
00108 }
00109
00110
00111 static bool LoadPSD( QDataStream & s, const PSDHeader & header, QImage & img )
00112 {
00113
00114 img = QImage( header.width, header.height, QImage::Format_RGB32 );
00115
00116 uint tmp;
00117
00118
00119 s >> tmp;
00120 s.device()->seek( s.device()->pos() + tmp );
00121
00122
00123 s >> tmp;
00124 s.device()->seek( s.device()->pos() + tmp );
00125
00126
00127 s >> tmp;
00128 s.device()->seek( s.device()->pos() + tmp );
00129
00130
00131
00132
00133
00134 ushort compression;
00135 s >> compression;
00136
00137 if( compression > 1 ) {
00138
00139 return false;
00140 }
00141
00142 uint channel_num = header.channel_count;
00143
00144
00145 if( channel_num < 4 ) {
00146 img.fill(qRgba(0, 0, 0, 0xFF));
00147 }
00148 else {
00149
00150 img = img.convertToFormat(QImage::Format_ARGB32);
00151
00152
00153 channel_num = 4;
00154 }
00155
00156 const uint pixel_count = header.height * header.width;
00157
00158 static const uint components[4] = {2, 1, 0, 3};
00159
00160 if( compression ) {
00161
00162
00163 if(!seekBy(s, header.height*header.channel_count*sizeof(ushort)))
00164 return false;
00165
00166
00167 for(uint channel = 0; channel < channel_num; channel++) {
00168
00169 uchar * ptr = img.bits() + components[channel];
00170
00171 uint count = 0;
00172 while( count < pixel_count ) {
00173 uchar c;
00174 if(s.atEnd())
00175 return false;
00176 s >> c;
00177 uint len = c;
00178
00179 if( len < 128 ) {
00180
00181 len++;
00182 count += len;
00183 if ( count > pixel_count )
00184 return false;
00185
00186 while( len != 0 ) {
00187 s >> *ptr;
00188 ptr += 4;
00189 len--;
00190 }
00191 }
00192 else if( len > 128 ) {
00193
00194
00195 len ^= 0xFF;
00196 len += 2;
00197 count += len;
00198 if(s.atEnd() || count > pixel_count)
00199 return false;
00200 uchar val;
00201 s >> val;
00202 while( len != 0 ) {
00203 *ptr = val;
00204 ptr += 4;
00205 len--;
00206 }
00207 }
00208 else if( len == 128 ) {
00209
00210 }
00211 }
00212 }
00213 }
00214 else {
00215
00216
00217
00218
00219 for(uint channel = 0; channel < channel_num; channel++) {
00220
00221 uchar * ptr = img.bits() + components[channel];
00222
00223
00224 uint count = pixel_count;
00225 while( count != 0 ) {
00226 s >> *ptr;
00227 ptr += 4;
00228 count--;
00229 }
00230 }
00231 }
00232
00233 return true;
00234 }
00235
00236 }
00237
00238
00239 PSDHandler::PSDHandler()
00240 {
00241 }
00242
00243 bool PSDHandler::canRead() const
00244 {
00245 if (canRead(device())) {
00246 setFormat("psd");
00247 return true;
00248 }
00249 return false;
00250 }
00251
00252 bool PSDHandler::read(QImage *image)
00253 {
00254 QDataStream s( device() );
00255 s.setByteOrder( QDataStream::BigEndian );
00256
00257 PSDHeader header;
00258 s >> header;
00259
00260
00261 if( s.atEnd() || !IsValid( header ) ) {
00262 kDebug(399) << "This PSD file is not valid.";
00263 return false;
00264 }
00265
00266
00267 if( !IsSupported( header ) ) {
00268 kDebug(399) << "This PSD file is not supported.";
00269 return false;
00270 }
00271
00272 QImage img;
00273 if( !LoadPSD(s, header, img) ) {
00274 kDebug(399) << "Error loading PSD file.";
00275 return false;
00276 }
00277
00278 *image = img;
00279 return true;
00280 }
00281
00282 bool PSDHandler::write(const QImage &)
00283 {
00284
00285 return false;
00286 }
00287
00288 QByteArray PSDHandler::name() const
00289 {
00290 return "psd";
00291 }
00292
00293 bool PSDHandler::canRead(QIODevice *device)
00294 {
00295 if (!device) {
00296 qWarning("PSDHandler::canRead() called with no device");
00297 return false;
00298 }
00299
00300 qint64 oldPos = device->pos();
00301
00302 char head[4];
00303 qint64 readBytes = device->read(head, sizeof(head));
00304 if (readBytes != sizeof(head)) {
00305 if (device->isSequential()) {
00306 while (readBytes > 0)
00307 device->ungetChar(head[readBytes-- - 1]);
00308 } else {
00309 device->seek(oldPos);
00310 }
00311 return false;
00312 }
00313
00314 if (device->isSequential()) {
00315 while (readBytes > 0)
00316 device->ungetChar(head[readBytes-- - 1]);
00317 } else {
00318 device->seek(oldPos);
00319 }
00320
00321 return qstrncmp(head, "8BPS", 4) == 0;
00322 }
00323
00324
00325 class PSDPlugin : public QImageIOPlugin
00326 {
00327 public:
00328 QStringList keys() const;
00329 Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
00330 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
00331 };
00332
00333 QStringList PSDPlugin::keys() const
00334 {
00335 return QStringList() << "psd" << "PSD";
00336 }
00337
00338 QImageIOPlugin::Capabilities PSDPlugin::capabilities(QIODevice *device, const QByteArray &format) const
00339 {
00340 if (format == "psd" || format == "PSD")
00341 return Capabilities(CanRead);
00342 if (!format.isEmpty())
00343 return 0;
00344 if (!device->isOpen())
00345 return 0;
00346
00347 Capabilities cap;
00348 if (device->isReadable() && PSDHandler::canRead(device))
00349 cap |= CanRead;
00350 return cap;
00351 }
00352
00353 QImageIOHandler *PSDPlugin::create(QIODevice *device, const QByteArray &format) const
00354 {
00355 QImageIOHandler *handler = new PSDHandler;
00356 handler->setDevice(device);
00357 handler->setFormat(format);
00358 return handler;
00359 }
00360
00361 Q_EXPORT_STATIC_PLUGIN(PSDPlugin)
00362 Q_EXPORT_PLUGIN2(psd, PSDPlugin)