1 /* This file is part of the KDE project
2 Copyright (C) 2003 Ignacio CastaƱo <castano@ludicon.com>
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the Lesser GNU General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This code is based on Thacher Ulrich PSD loading code released
10 on public domain. See: http://tulrich.com/geekstuff/
13 /* this code supports:
15 * rle and raw psd files
22 #include <QtGui/QImage>
23 #include <QtCore/QDataStream>
28 typedef quint16 ushort
;
31 namespace { // Private.
55 static QDataStream
& operator>> ( QDataStream
& s
, PSDHeader
& header
)
57 s
>> header
.signature
;
59 for( int i
= 0; i
< 6; i
++ ) {
60 s
>> header
.reserved
[i
];
62 s
>> header
.channel_count
;
66 s
>> header
.color_mode
;
69 static bool seekBy(QDataStream
& s
, unsigned int bytes
)
73 unsigned int num
= qMin(bytes
,( unsigned int )sizeof(buf
));
75 s
.readRawData(buf
, l
);
83 // Check that the header is a valid PSD.
84 static bool IsValid( const PSDHeader
& header
)
86 if( header
.signature
!= 0x38425053 ) { // '8BPS'
92 // Check that the header is supported.
93 static bool IsSupported( const PSDHeader
& header
)
95 if( header
.version
!= 1 ) {
98 if( header
.channel_count
> 16 ) {
101 if( header
.depth
!= 8 ) {
104 if( header
.color_mode
!= CM_RGB
) {
110 // Load the PSD image.
111 static bool LoadPSD( QDataStream
& s
, const PSDHeader
& header
, QImage
& img
)
114 img
= QImage( header
.width
, header
.height
, QImage::Format_RGB32
);
120 s
.device()->seek( s
.device()->pos() + tmp
);
122 // Skip image resources.
124 s
.device()->seek( s
.device()->pos() + tmp
);
126 // Skip the reserved data.
128 s
.device()->seek( s
.device()->pos() + tmp
);
130 // Find out if the data is compressed.
137 if( compression
> 1 ) {
138 // Unknown compression type.
142 uint channel_num
= header
.channel_count
;
145 if( channel_num
< 4 ) {
146 img
.fill(qRgba(0, 0, 0, 0xFF));
150 img
= img
.convertToFormat(QImage::Format_ARGB32
);
152 // Ignore the other channels.
156 const uint pixel_count
= header
.height
* header
.width
;
158 static const uint components
[4] = {2, 1, 0, 3}; // @@ Is this endian dependant?
163 if(!seekBy(s
, header
.height
*header
.channel_count
*sizeof(ushort
)))
167 for(uint channel
= 0; channel
< channel_num
; channel
++) {
169 uchar
* ptr
= img
.bits() + components
[channel
];
172 while( count
< pixel_count
) {
180 // Copy next len+1 bytes literally.
183 if ( count
> pixel_count
)
192 else if( len
> 128 ) {
193 // Next -len+1 bytes in the dest are replicated from next source byte.
194 // (Interpret len as a negative 8-bit int.)
198 if(s
.atEnd() || count
> pixel_count
)
208 else if( len
== 128 ) {
215 // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...)
216 // where each channel consists of an 8-bit value for each pixel in the image.
218 // Read the data by channel.
219 for(uint channel
= 0; channel
< channel_num
; channel
++) {
221 uchar
* ptr
= img
.bits() + components
[channel
];
224 uint count
= pixel_count
;
225 while( count
!= 0 ) {
239 PSDHandler::PSDHandler()
243 bool PSDHandler::canRead() const
245 if (canRead(device())) {
252 bool PSDHandler::read(QImage
*image
)
254 QDataStream
s( device() );
255 s
.setByteOrder( QDataStream::BigEndian
);
260 // Check image file format.
261 if( s
.atEnd() || !IsValid( header
) ) {
262 kDebug(399) << "This PSD file is not valid.";
266 // Check if it's a supported format.
267 if( !IsSupported( header
) ) {
268 kDebug(399) << "This PSD file is not supported.";
273 if( !LoadPSD(s
, header
, img
) ) {
274 kDebug(399) << "Error loading PSD file.";
282 bool PSDHandler::write(const QImage
&)
288 QByteArray
PSDHandler::name() const
293 bool PSDHandler::canRead(QIODevice
*device
)
296 qWarning("PSDHandler::canRead() called with no device");
300 qint64 oldPos
= device
->pos();
303 qint64 readBytes
= device
->read(head
, sizeof(head
));
304 if (readBytes
!= sizeof(head
)) {
305 if (device
->isSequential()) {
306 while (readBytes
> 0)
307 device
->ungetChar(head
[readBytes
-- - 1]);
309 device
->seek(oldPos
);
314 if (device
->isSequential()) {
315 while (readBytes
> 0)
316 device
->ungetChar(head
[readBytes
-- - 1]);
318 device
->seek(oldPos
);
321 return qstrncmp(head
, "8BPS", 4) == 0;
325 class PSDPlugin
: public QImageIOPlugin
328 QStringList
keys() const;
329 Capabilities
capabilities(QIODevice
*device
, const QByteArray
&format
) const;
330 QImageIOHandler
*create(QIODevice
*device
, const QByteArray
&format
= QByteArray()) const;
333 QStringList
PSDPlugin::keys() const
335 return QStringList() << "psd" << "PSD";
338 QImageIOPlugin::Capabilities
PSDPlugin::capabilities(QIODevice
*device
, const QByteArray
&format
) const
340 if (format
== "psd" || format
== "PSD")
341 return Capabilities(CanRead
);
342 if (!format
.isEmpty())
344 if (!device
->isOpen())
348 if (device
->isReadable() && PSDHandler::canRead(device
))
353 QImageIOHandler
*PSDPlugin::create(QIODevice
*device
, const QByteArray
&format
) const
355 QImageIOHandler
*handler
= new PSDHandler
;
356 handler
->setDevice(device
);
357 handler
->setFormat(format
);
361 Q_EXPORT_STATIC_PLUGIN(PSDPlugin
)
362 Q_EXPORT_PLUGIN2(psd
, PSDPlugin
)