1/* This file is part of the KDE project
2 Copyright (C) 2003 Ignacio CastaƱo <castano@ludicon.com>
3
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.
8
9 This code is based on Thacher Ulrich PSD loading code released
10 on public domain. See: http://tulrich.com/geekstuff/
11*/
12
13/* this code supports:
14 * reading:
15 * rle and raw psd files
16 * writing:
17 * not supported
18 */
19
20#include "psd.h"
21
22#include <QtGui/QImage>
23#include <QtCore/QDataStream>
24
25#include <kdebug.h>
26
27typedef quint32 uint;
28typedef quint16 ushort;
29typedef quint8 uchar;
30
31namespace { // Private.
32
33 enum ColorMode {
34 CM_BITMAP = 0,
35 CM_GRAYSCALE = 1,
36 CM_INDEXED = 2,
37 CM_RGB = 3,
38 CM_CMYK = 4,
39 CM_MULTICHANNEL = 7,
40 CM_DUOTONE = 8,
41 CM_LABCOLOR = 9
42 };
43
44 struct PSDHeader {
45 uint signature;
46 ushort version;
47 uchar reserved[6];
48 ushort channel_count;
49 uint height;
50 uint width;
51 ushort depth;
52 ushort color_mode;
53 };
54
55 static QDataStream & operator>> ( QDataStream & s, PSDHeader & header )
56 {
57 s >> header.signature;
58 s >> header.version;
59 for( int i = 0; i < 6; i++ ) {
60 s >> header.reserved[i];
61 }
62 s >> header.channel_count;
63 s >> header.height;
64 s >> header.width;
65 s >> header.depth;
66 s >> header.color_mode;
67 return s;
68 }
69 static bool seekBy(QDataStream& s, unsigned int bytes)
70 {
71 char buf[4096];
72 while (bytes) {
73 unsigned int num= qMin(bytes,( unsigned int )sizeof(buf));
74 unsigned int l = num;
75 s.readRawData(buf, l);
76 if(l != num)
77 return false;
78 bytes -= num;
79 }
80 return true;
81 }
82
83 // Check that the header is a valid PSD.
84 static bool IsValid( const PSDHeader & header )
85 {
86 if( header.signature != 0x38425053 ) { // '8BPS'
87 return false;
88 }
89 return true;
90 }
91
92 // Check that the header is supported.
93 static bool IsSupported( const PSDHeader & header )
94 {
95 if( header.version != 1 ) {
96 return false;
97 }
98 if( header.channel_count > 16 ) {
99 return false;
100 }
101 if( header.depth != 8 ) {
102 return false;
103 }
104 if( header.color_mode != CM_RGB ) {
105 return false;
106 }
107 return true;
108 }
109
110 // Load the PSD image.
111 static bool LoadPSD( QDataStream & s, const PSDHeader & header, QImage & img )
112 {
113 // Create dst image.
114 img = QImage( header.width, header.height, QImage::Format_RGB32 );
115
116 uint tmp;
117
118 // Skip mode data.
119 s >> tmp;
120 s.device()->seek( s.device()->pos() + tmp );
121
122 // Skip image resources.
123 s >> tmp;
124 s.device()->seek( s.device()->pos() + tmp );
125
126 // Skip the reserved data.
127 s >> tmp;
128 s.device()->seek( s.device()->pos() + tmp );
129
130 // Find out if the data is compressed.
131 // Known values:
132 // 0: no compression
133 // 1: RLE compressed
134 ushort compression;
135 s >> compression;
136
137 if( compression > 1 ) {
138 // Unknown compression type.
139 return false;
140 }
141
142 uint channel_num = header.channel_count;
143
144 // Clear the image.
145 if( channel_num < 4 ) {
146 img.fill(qRgba(0, 0, 0, 0xFF));
147 }
148 else {
149 // Enable alpha.
150 img = img.convertToFormat(QImage::Format_ARGB32);
151
152 // Ignore the other channels.
153 channel_num = 4;
154 }
155
156 const uint pixel_count = header.height * header.width;
157
158 static const uint components[4] = {2, 1, 0, 3}; // @@ Is this endian dependant?
159
160 if( compression ) {
161
162 // Skip row lengths.
163 if(!seekBy(s, header.height*header.channel_count*sizeof(ushort)))
164 return false;
165
166 // Read RLE data.
167 for(uint channel = 0; channel < channel_num; channel++) {
168
169 uchar * ptr = img.bits() + components[channel];
170
171 uint count = 0;
172 while( count < pixel_count ) {
173 uchar c;
174 if(s.atEnd())
175 return false;
176 s >> c;
177 uint len = c;
178
179 if( len < 128 ) {
180 // Copy next len+1 bytes literally.
181 len++;
182 count += len;
183 if ( count > pixel_count )
184 return false;
185
186 while( len != 0 ) {
187 s >> *ptr;
188 ptr += 4;
189 len--;
190 }
191 }
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.)
195 len ^= 0xFF;
196 len += 2;
197 count += len;
198 if(s.atEnd() || count > pixel_count)
199 return false;
200 uchar val;
201 s >> val;
202 while( len != 0 ) {
203 *ptr = val;
204 ptr += 4;
205 len--;
206 }
207 }
208 else if( len == 128 ) {
209 // No-op.
210 }
211 }
212 }
213 }
214 else {
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.
217
218 // Read the data by channel.
219 for(uint channel = 0; channel < channel_num; channel++) {
220
221 uchar * ptr = img.bits() + components[channel];
222
223 // Read the data.
224 uint count = pixel_count;
225 while( count != 0 ) {
226 s >> *ptr;
227 ptr += 4;
228 count--;
229 }
230 }
231 }
232
233 return true;
234 }
235
236} // Private
237
238
239PSDHandler::PSDHandler()
240{
241}
242
243bool PSDHandler::canRead() const
244{
245 if (canRead(device())) {
246 setFormat("psd");
247 return true;
248 }
249 return false;
250}
251
252bool PSDHandler::read(QImage *image)
253{
254 QDataStream s( device() );
255 s.setByteOrder( QDataStream::BigEndian );
256
257 PSDHeader header;
258 s >> header;
259
260 // Check image file format.
261 if( s.atEnd() || !IsValid( header ) ) {
262 kDebug(399) << "This PSD file is not valid.";
263 return false;
264 }
265
266 // Check if it's a supported format.
267 if( !IsSupported( header ) ) {
268 kDebug(399) << "This PSD file is not supported.";
269 return false;
270 }
271
272 QImage img;
273 if( !LoadPSD(s, header, img) ) {
274 kDebug(399) << "Error loading PSD file.";
275 return false;
276 }
277
278 *image = img;
279 return true;
280}
281
282bool PSDHandler::write(const QImage &)
283{
284 // TODO Stub!
285 return false;
286}
287
288QByteArray PSDHandler::name() const
289{
290 return "psd";
291}
292
293bool PSDHandler::canRead(QIODevice *device)
294{
295 if (!device) {
296 qWarning("PSDHandler::canRead() called with no device");
297 return false;
298 }
299
300 qint64 oldPos = device->pos();
301
302 char head[4];
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]);
308 } else {
309 device->seek(oldPos);
310 }
311 return false;
312 }
313
314 if (device->isSequential()) {
315 while (readBytes > 0)
316 device->ungetChar(head[readBytes-- - 1]);
317 } else {
318 device->seek(oldPos);
319 }
320
321 return qstrncmp(head, "8BPS", 4) == 0;
322}
323
324
325class PSDPlugin : public QImageIOPlugin
326{
327public:
328 QStringList keys() const;
329 Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
330 QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
331};
332
333QStringList PSDPlugin::keys() const
334{
335 return QStringList() << "psd" << "PSD";
336}
337
338QImageIOPlugin::Capabilities PSDPlugin::capabilities(QIODevice *device, const QByteArray &format) const
339{
340 if (format == "psd" || format == "PSD")
341 return Capabilities(CanRead);
342 if (!format.isEmpty())
343 return 0;
344 if (!device->isOpen())
345 return 0;
346
347 Capabilities cap;
348 if (device->isReadable() && PSDHandler::canRead(device))
349 cap |= CanRead;
350 return cap;
351}
352
353QImageIOHandler *PSDPlugin::create(QIODevice *device, const QByteArray &format) const
354{
355 QImageIOHandler *handler = new PSDHandler;
356 handler->setDevice(device);
357 handler->setFormat(format);
358 return handler;
359}
360
361Q_EXPORT_STATIC_PLUGIN(PSDPlugin)
362Q_EXPORT_PLUGIN2(psd, PSDPlugin)
363