• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

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)

KImgIO

Skip menu "KImgIO"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal