KImgIO
psd.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 Copyright (C) 2003 Ignacio CastaƱo <castano@ludicon.com> 00003 00004 This program is free software; you can redistribute it and/or 00005 modify it under the terms of the Lesser GNU General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This code is based on Thacher Ulrich PSD loading code released 00010 on public domain. See: http://tulrich.com/geekstuff/ 00011 */ 00012 00013 /* this code supports: 00014 * reading: 00015 * rle and raw psd files 00016 * writing: 00017 * not supported 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 { // Private. 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 // Check that the header is a valid PSD. 00084 static bool IsValid( const PSDHeader & header ) 00085 { 00086 if( header.signature != 0x38425053 ) { // '8BPS' 00087 return false; 00088 } 00089 return true; 00090 } 00091 00092 // Check that the header is supported. 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 // Load the PSD image. 00111 static bool LoadPSD( QDataStream & s, const PSDHeader & header, QImage & img ) 00112 { 00113 // Create dst image. 00114 img = QImage( header.width, header.height, QImage::Format_RGB32 ); 00115 00116 uint tmp; 00117 00118 // Skip mode data. 00119 s >> tmp; 00120 s.device()->seek( s.device()->pos() + tmp ); 00121 00122 // Skip image resources. 00123 s >> tmp; 00124 s.device()->seek( s.device()->pos() + tmp ); 00125 00126 // Skip the reserved data. 00127 s >> tmp; 00128 s.device()->seek( s.device()->pos() + tmp ); 00129 00130 // Find out if the data is compressed. 00131 // Known values: 00132 // 0: no compression 00133 // 1: RLE compressed 00134 ushort compression; 00135 s >> compression; 00136 00137 if( compression > 1 ) { 00138 // Unknown compression type. 00139 return false; 00140 } 00141 00142 uint channel_num = header.channel_count; 00143 00144 // Clear the image. 00145 if( channel_num < 4 ) { 00146 img.fill(qRgba(0, 0, 0, 0xFF)); 00147 } 00148 else { 00149 // Enable alpha. 00150 img = img.convertToFormat(QImage::Format_ARGB32); 00151 00152 // Ignore the other channels. 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}; // @@ Is this endian dependant? 00159 00160 if( compression ) { 00161 00162 // Skip row lengths. 00163 if(!seekBy(s, header.height*header.channel_count*sizeof(ushort))) 00164 return false; 00165 00166 // Read RLE data. 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 // Copy next len+1 bytes literally. 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 // Next -len+1 bytes in the dest are replicated from next source byte. 00194 // (Interpret len as a negative 8-bit int.) 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 // No-op. 00210 } 00211 } 00212 } 00213 } 00214 else { 00215 // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) 00216 // where each channel consists of an 8-bit value for each pixel in the image. 00217 00218 // Read the data by channel. 00219 for(uint channel = 0; channel < channel_num; channel++) { 00220 00221 uchar * ptr = img.bits() + components[channel]; 00222 00223 // Read the data. 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 } // Private 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 // Check image file format. 00261 if( s.atEnd() || !IsValid( header ) ) { 00262 kDebug(399) << "This PSD file is not valid."; 00263 return false; 00264 } 00265 00266 // Check if it's a supported format. 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 // TODO Stub! 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)
KDE 4.6 API Reference