1 /*
2 Copyright (c) 2012-2019 Ronie Martinez (ronmarti18@gmail.com)
3 All rights reserved.
4 
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9 
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied
12 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13 PURPOSE.  See the GNU Lesser General Public License for more
14 details.
15 
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301  USA
20 */
21 
22 #include "qpsdhandler.h"
23 
24 #ifdef QT_DEBUG
25 #include <QDebug>
26 #endif
27 
28 /* tristimulus reference: Adobe RGB (1998) Color Image Encoding
29  * http://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf
30  * D65 0.9505, 1.0000, 1.0891
31  * D50 0.9642, 1.000, 0.8249
32  */
33 
34 static const qreal refX = 0.9642;
35 static const qreal refY = 1.0000;
36 static const qreal refZ = 0.8249;
37 static const qreal e = 216 / 24389;
38 static const qreal k = 24389 / 27;
39 static const qreal gammaPsd = 563 / 256; //2.19921875
40 
xyzToRgb(qreal x,qreal y,qreal z,qreal alpha=1.0)41 static QRgb xyzToRgb(qreal x, qreal y, qreal z, qreal alpha = 1.0)
42 {
43     /* http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
44      * Adobe RGB
45      * D65
46          2.0413690 -0.5649464 -0.3446944
47         -0.9692660  1.8760108  0.0415560
48          0.0134474 -0.1183897  1.0154096
49      * D50
50          1.9624274 -0.6105343 -0.3413404
51         -0.9787684  1.9161415  0.0334540
52          0.0286869 -0.1406752  1.3487655
53      */
54     /* D65 */
55     /* qreal varR = (x * 2.0413690) + (y * -0.5649464) + (z * -0.3446944);
56      * qreal varG = (x * -0.9692660) + (y * 1.8760108) + (z * 0.0415560);
57      * qreal varB = (x * 0.0134474) + (y * -0.1183897) + (z * 1.0154096);
58      */
59 
60     /* D50 */
61     qreal varR = (x * 1.9624274) + (y * -0.6105343) + (z * -0.3413404);
62     qreal varG = (x * -0.9787684) + (y * 1.9161415) + (z * 0.0334540);
63     qreal varB = (x * 0.0286869) + (y * -0.1406752) + (z * 1.3487655);
64 
65     /* gamma companding */
66     qreal red = qPow(varR, 1 / gammaPsd);
67     qreal green = qPow(varG, 1 / gammaPsd);
68     qreal blue = qPow(varB, 1 / gammaPsd);
69 
70     /* values sometimes fall outside the range 0-1 */
71     red = (red < 0) ? 0 : ((red > 1) ? 1 : red);
72     green = (green < 0) ? 0 : ((green > 1) ? 1 : green);
73     blue = (blue < 0) ? 0 : ((blue > 1) ? 1 : blue);
74 
75     /* QColor::fromRgbF accepts value from 0-1, therefore, there's
76      * no need to multiply it by 255 and use qRgb()
77      */
78     return QColor::fromRgbF(red, green, blue, alpha).rgba();
79 }
80 
labToRgb(qreal lightness,qreal a,qreal b,quint8 alpha=255)81 static QRgb labToRgb(qreal lightness, qreal a, qreal b, quint8 alpha = 255)
82 {
83     /* ranges:
84      * lightness = 0 to 100
85      * a = -128 to 127
86      * b = -128 to 127
87      */
88 
89     lightness /= 2.55;
90     a -= 128;
91     b -= 128;
92     qreal fy = (lightness + 16.0) / 116.0;
93     qreal fx = (a / 500.0) + fy;
94     qreal fz = fy - (b / 200.0);
95 
96     qreal varY = (lightness > k * e) ? pow(fy, 3) : lightness / k;
97     qreal varX = (pow(fx, 3) > e) ? pow(fx, 3) : ((116.0 * fx) - 16.0) / k;
98     qreal varZ = (pow(fz, 3) > e) ? pow(fz, 3) : ((116.0 * fz) - 16.0) / k;
99 
100     return xyzToRgb(refX * varX, refY * varY, refZ * varZ, alpha / 255);
101 }
102 
103 /**
104  * @brief QPsdHandler::isValidSignature Validates if signature is 8BPS.
105  * @param signature Signature.
106  * @return bool.
107  */
isValidSignature(quint32 signature)108 bool QPsdHandler::isValidSignature(quint32 signature)
109 {
110     return signature == 0x38425053; //'8BPS'
111 }
112 
113 /**
114  * @brief QPsdHandler::isValidVersion Validates if version is 1(PSD) or 2(PSB).
115  * @param version Version.
116  * @return bool.
117  */
isValidVersion(quint16 version)118 bool QPsdHandler::isValidVersion(quint16 version)
119 {
120     switch (version) {
121     case 1:
122         //check if format is empty or it is not psd
123         if (format().isEmpty() || format() != "psd")
124             setFormat("psd");
125         return true;
126         break;
127     case 2:
128         //check if format is empty or it is not psb
129         if (format().isEmpty() || format() != "psb")
130             setFormat("psb");
131         return true;
132         break;
133     default:
134         return false;
135         break;
136     }
137 }
138 
139 /**
140  * @brief QPsdHandler::isChannelCountSupported Validates if the number of channels is supported.
141  *      Supported range is 1 to 56.
142  * @param channels Number of channels.
143  * @return bool.
144  */
isChannelCountSupported(quint16 channels)145 bool QPsdHandler::isChannelCountSupported(quint16 channels)
146 {
147 //    return channels >= 1 && channels <= 56;
148 //    NOTE: found a sample file with channels > 56 and Photoshop can still read it
149 //    though the documentation says it should be within 1 to 56 channels
150     return channels >= 1; //breaking "56-max rule"
151 }
152 
153 /**
154  * @brief QPsdHandler::isValidWidthOrHeight Validates if width or height is within valid range.
155  *      Supported range is 1 to 30,000 for PSD and 1 to 300,000 for PSB.
156  * @param version Version.
157  * @param value Value of width or height.
158  * @return bool.
159  */
isValidWidthOrHeight(quint16 version,quint32 value)160 bool QPsdHandler::isValidWidthOrHeight(quint16 version, quint32 value)
161 {
162     if (version == 1 && (value > 30000 || value == 0))
163         return false;
164     if (version == 2 && (value > 300000 || value == 0))
165         return false;
166     return true;
167 }
168 
169 /**
170  * @brief QPsdHandler::isSupportedDepth Validates if depth is supported.
171  * Supported values are 1, 8, 16 and 32.
172  * @param depth Depth.
173  * @return bool.
174  */
isSupportedDepth(quint16 depth)175 bool QPsdHandler::isSupportedDepth(quint16 depth)
176 {
177     switch (depth) {
178     case 1:
179     case 8:
180     case 16:
181     case 32:
182         return true;
183         break;
184     default:
185         return false;
186         break;
187     }
188 }
189 
190 /**
191  * @brief QPsdHandler::isSupportedColorMode Validates if color mode is supported.
192  *      Supported values are Bitmap = 0; Grayscale = 1; Indexed = 2; RGB = 3; CMYK = 4;
193  *      Multichannel = 7; Duotone = 8; Lab = 9
194  * @param colorMode
195  * @return
196  */
isSupportedColorMode(quint16 colorMode)197 bool QPsdHandler::isSupportedColorMode(quint16 colorMode)
198 {
199     switch (colorMode) {
200     case 0:
201     case 1:
202     case 2:
203     case 3:
204     case 4:
205     case 7:
206     case 8:
207     case 9:
208         return true;
209         break;
210     default:
211         return false;
212         break;
213     }
214 }
215 
216 /**
217  * @brief QPsdHandler::getColorData Extracts the color data from the input stream.
218  * @param input QDataStream.
219  * @return bool.
220  */
readColorData(QDataStream & input)221 QByteArray QPsdHandler::readColorData(QDataStream &input)
222 {
223     quint32 length;
224     QByteArray colorData;
225     input >> length;
226     if (length != 0) {
227         colorData.resize(length);
228         input.readRawData(colorData.data(), length);
229     }
230     return colorData;
231 }
232 
233 /**
234  * @brief QPsdHandler::skipImageResources Skips the image resources section.
235  * @param input QDataStream.
236  */
skipImageResources(QDataStream & input)237 void QPsdHandler::skipImageResources(QDataStream &input)
238 {
239     quint32 length;
240     input >> length;
241     input.skipRawData(length);
242 }
243 
244 /**
245  * @brief QPsdHandler::skipLayerAndMaskSection Skips the layer and mask section.
246  *      The size of Layer and Mask Section is stored in 4 bytes for PSD files
247  *      and 8 bytes for PSB files.
248  * @param input QDataStream.
249  */
skipLayerAndMaskSection(QDataStream & input)250 void QPsdHandler::skipLayerAndMaskSection(QDataStream &input)
251 {
252     if (format() == "psd") {
253         quint32 layerAndMaskInfoLength;
254         input >> layerAndMaskInfoLength;
255         input.skipRawData(layerAndMaskInfoLength);
256     } else if (format() == "psb") {
257         quint64 layerAndMaskInfoLength;
258         input >> layerAndMaskInfoLength;
259         input.skipRawData(layerAndMaskInfoLength);
260     }
261 }
262 
readImageData(QDataStream & input,QPsdHandler::Compression compression,quint64 size)263 QByteArray QPsdHandler::readImageData(QDataStream &input,
264                                       QPsdHandler::Compression compression,
265                                       quint64 size)
266 {
267     QByteArray imageData;
268     switch (compression) {
269     case RAW:
270     {
271         imageData.resize(size);
272         input.readRawData(imageData.data(), size);
273     }
274         break;
275     case RLE: /*RLE COMPRESSED DATA*/
276     {
277         quint8 byte, count;
278         /* Code based on PackBits implementation which is primarily used by
279          * Photoshop for RLE encoding/decoding */
280         while (!input.atEnd()) {
281             input >> byte;
282             if (byte > 128) {
283                 count = 256 - byte ;
284                 input >>  byte;
285                 for (quint8 i = 0; i <= count; ++i) {
286                     imageData.append(byte);
287                 }
288             } else if (byte < 128) {
289                 count = byte + 1;
290                 quint64 size = imageData.size();
291                 imageData.resize(size + count);
292                 input.readRawData(imageData.data() + size, count);
293             }
294         }
295     }
296         break;
297     case ZIP_WITHOUT_PREDICTION:
298         /*NOT IMPLEMENTED*/
299         break;
300     case ZIP_WITH_PREDICTION:
301         /*NOT IMPLEMENTED*/
302         break;
303     }
304     return imageData;
305 }
306 
307 /**
308  * @brief QPsdHandler::processBitmap Generates bitmap image from imageData.
309  * @param imageData QByteArray.
310  * @param width quint32.
311  * @param height quint32.
312  * @return QImage.
313  */
processBitmap(QByteArray & imageData,quint32 width,quint32 height)314 QImage QPsdHandler::processBitmap(QByteArray& imageData, quint32 width, quint32 height)
315 {
316 #ifdef QT_DEBUG
317     qDebug() << "Bitmap";
318 #endif
319     QString head = QString("P4\n%1 %2\n").arg(width).arg(height);
320     QByteArray buffer(head.toUtf8());
321     buffer.append(imageData);
322     return QImage::fromData(buffer);
323 }
324 
processGrayscale8(QByteArray & imageData,quint32 width,quint32 height)325 QImage QPsdHandler::processGrayscale8(QByteArray& imageData, quint32 width, quint32 height)
326 {
327 #ifdef QT_DEBUG
328     qDebug() << "8-bit Grayscale";
329 #endif
330     QImage result(width, height, QImage::Format_RGB32);
331     quint8 *data = (quint8*)imageData.constData();
332     QRgb  *p, *end;
333     for (quint32 y = 0; y < height; ++y) {
334         p = (QRgb *)result.scanLine(y);
335         end = p + width;
336         while (p < end) {
337             *p = qRgb(*data, *data, *data);
338             ++p; ++data;
339         }
340     }
341     return result;
342 }
343 
processGrayscale8WithAlpha(QByteArray & imageData,quint32 width,quint32 height,quint64 totalBytesPerChannel)344 QImage QPsdHandler::processGrayscale8WithAlpha(QByteArray& imageData, quint32 width, quint32 height,
345                                                quint64 totalBytesPerChannel)
346 {
347 #ifdef QT_DEBUG
348     qDebug() << "8-bit Grayscale with Alpha";
349 #endif
350     QImage result(width, height, QImage::Format_ARGB32);
351     quint8 *data = (quint8*)imageData.constData();
352     quint8 *alpha = data + totalBytesPerChannel;
353     QRgb  *p, *end;
354     for (quint32 y = 0; y < height; ++y) {
355         p = (QRgb *)result.scanLine(y);
356         end = p + width;
357         while (p < end) {
358             *p = qRgba(*data, *data, *data, *alpha);
359             ++p; ++data; ++alpha;
360         }
361     }
362     return result;
363 }
364 
processGrayscale16(QByteArray & imageData,quint32 width,quint32 height)365 QImage QPsdHandler::processGrayscale16(QByteArray& imageData, quint32 width, quint32 height)
366 {
367 #ifdef QT_DEBUG
368     qDebug() << "16-bit Grayscale";
369 #endif
370     const qreal scale = (qPow(2, 8) -1 ) / (qPow(2, 16) - 1);
371     quint16 data16;
372     QImage result(width, height, QImage::Format_RGB32);
373     quint8 *data8 = (quint8*)imageData.constData();
374     QRgb  *p, *end;
375     for (quint32 y = 0; y < height; ++y) {
376         p = (QRgb *)result.scanLine(y);
377         end = p + width;
378         while (p < end) {
379             data16 = ((*data8 << 8) + *(data8 + 1)) * scale;
380             *p = qRgb(data16, data16, data16);
381             ++p; data8 += 2;
382         }
383     }
384     return result;
385 }
386 
processGrayscale16WithAlpha(QByteArray & imageData,quint32 width,quint32 height,quint64 totalBytesPerChannel)387 QImage QPsdHandler::processGrayscale16WithAlpha(QByteArray& imageData, quint32 width, quint32 height,
388                                                 quint64 totalBytesPerChannel)
389 {
390 #ifdef QT_DEBUG
391     qDebug() << "8-bit Grayscale with Alpha";
392 #endif
393     const qreal scale = (qPow(2, 8) -1 ) / (qPow(2, 16) - 1);
394     quint16 data16, alpha16;
395     QImage result(width, height, QImage::Format_ARGB32);
396     quint8 *data8 = (quint8*)imageData.constData();
397     quint8 *alpha8 = data8 + totalBytesPerChannel;
398     QRgb  *p, *end;
399     for (quint32 y = 0; y < height; ++y) {
400         p = (QRgb *)result.scanLine(y);
401         end = p + width;
402         while (p < end) {
403             data16 = ((*data8 << 8) + *(data8 + 1)) * scale;
404             alpha16 = ((*alpha8 << 8) + *(alpha8 + 1)) * scale;
405             *p = qRgba(data16, data16, data16, alpha16);
406             ++p; data8 += 2; alpha8 += 2;
407         }
408     }
409     return result;
410 }
411 
processIndexed(QByteArray & colorData,QByteArray & imageData,quint32 width,quint32 height)412 QImage QPsdHandler::processIndexed(QByteArray& colorData, QByteArray& imageData, quint32 width,
413                                    quint32 height)
414 {
415 #ifdef QT_DEBUG
416     qDebug() << "Indexed";
417 #endif
418     QImage result(width, height, QImage::Format_Indexed8);
419     int indexCount = colorData.size() / 3;
420     quint8 *red = (quint8*)colorData.constData();
421     quint8 *green = red + indexCount;
422     quint8 *blue = green + indexCount;
423     for (int i = 0; i < indexCount; ++i) {
424         result.setColor(i, qRgb(*red, *green, *blue));
425         ++red; ++green; ++blue;
426     }
427     quint8 *data = (quint8*)imageData.constData();
428     for (quint32 i = 0; i < height; ++i) {
429         for (quint32 j = 0; j < width; ++j) {
430             result.setPixel(j, i, *data);
431             ++data;
432         }
433     }
434     return result;
435 }
436 
processRGB8(QByteArray & imageData,quint32 width,quint32 height,quint64 totalBytesPerChannel)437 QImage QPsdHandler::processRGB8(QByteArray& imageData, quint32 width, quint32 height,
438                                 quint64 totalBytesPerChannel)
439 {
440 #ifdef QT_DEBUG
441     qDebug() << "8-bit RGB";
442 #endif
443     QImage result(width, height, QImage::Format_RGB32);
444     quint8 *red = (quint8*)imageData.constData();
445     quint8 *green = red + totalBytesPerChannel;
446     quint8 *blue = green + totalBytesPerChannel;
447     QRgb  *p, *end;
448     for (quint32 y = 0; y < height; ++y) {
449         p = (QRgb *)result.scanLine(y);
450         end = p + width;
451         while (p < end) {
452             *p = qRgb(*red, *green, *blue);
453             ++p; ++red; ++green; ++blue;
454         }
455     }
456     return result;
457 }
458 
processRGB16(QByteArray & imageData,quint32 width,quint32 height,quint64 totalBytesPerChannel)459 QImage QPsdHandler::processRGB16(QByteArray& imageData, quint32 width, quint32 height,
460                                  quint64 totalBytesPerChannel)
461 {
462 #ifdef QT_DEBUG
463     qDebug() << "16-bit RGB";
464 #endif
465     const qreal scale = (qPow(2, 8) - 1) / (qPow(2, 16) - 1);
466     QImage result(width, height, QImage::Format_RGB32);
467     quint16 red16, blue16, green16;
468     quint8 *red8 = (quint8*)imageData.constData();
469     quint8 *green8 = red8 + totalBytesPerChannel;
470     quint8 *blue8 = green8 + totalBytesPerChannel;
471     QRgb  *p, *end;
472     for (quint32 y = 0; y < height; ++y) {
473         p = (QRgb *)result.scanLine(y);
474         end = p + width;
475         while (p < end) {
476             red16 = ((*red8 << 8) + *(red8 + 1)) * scale;
477             green16 = ((*green8 << 8) + *(green8 + 1)) * scale;
478             blue16 = ((*blue8 << 8) + *(blue8 + 1)) * scale;
479             *p = qRgb(red16, green16, blue16);
480             ++p;  red8 += 2; green8 += 2; blue8 += 2;
481         }
482     }
483     return result;
484 }
485 
processRGB8WithAlpha(QByteArray & imageData,quint32 width,quint32 height,quint64 totalBytesPerChannel)486 QImage QPsdHandler::processRGB8WithAlpha(QByteArray& imageData, quint32 width, quint32 height,
487                                          quint64 totalBytesPerChannel)
488 {
489 #ifdef QT_DEBUG
490     qDebug() << "8-bit RGB with Alpha";
491 #endif
492 	QImage result(width, height, QImage::Format_ARGB32);
493     quint8 *red = (quint8*)imageData.constData();
494     quint8 *green = red + totalBytesPerChannel;
495     quint8 *blue = green + totalBytesPerChannel;
496     quint8 *alpha = blue + totalBytesPerChannel;
497     QRgb  *p, *end;
498     for (quint32 y = 0; y < height; ++y) {
499         p = (QRgb *)result.scanLine(y);
500         end = p + width;
501         while (p < end) {
502             // Fix for blending image with white
503             if(*alpha != 0) {
504                 quint8 r = *red;
505                 quint8 g = *green;
506                 quint8 b = *blue;
507                 quint8 a = *alpha;
508 
509                 quint8 rFixed = (((r + a) - 255) * 255) / a;
510                 quint8 gFixed = (((g + a) - 255) * 255) / a;
511                 quint8 bFixed = (((b + a) - 255) * 255) / a;
512 
513                 *p = qRgba(rFixed, gFixed, bFixed, a);
514             } else {
515                 *p = qRgba(*red, *green, *blue, *alpha);
516             }
517             ++p; ++red; ++green; ++blue; ++alpha;
518         }
519     }
520     return result;
521 }
522 
processRGB16WithAlpha(QByteArray & imageData,quint32 width,quint32 height,quint64 totalBytesPerChannel)523 QImage QPsdHandler::processRGB16WithAlpha(QByteArray& imageData, quint32 width, quint32 height,
524                                           quint64 totalBytesPerChannel)
525 {
526 #ifdef QT_DEBUG
527     qDebug() << "16-bit RGB with Alpha";
528 #endif
529 //    FIXME: blending image with white (see QPsdHandler::processRGB8WithAlpha)
530     const qreal scale = (qPow(2, 8) - 1) / (qPow(2, 16) - 1);
531     QImage result(width, height, QImage::Format_ARGB32);
532     quint16 red16, blue16, green16, alpha16;
533     quint8 *red8 = (quint8*)imageData.constData();
534     quint8 *green8 = red8 + totalBytesPerChannel;
535     quint8 *blue8 = green8 + totalBytesPerChannel;
536     quint8 *alpha8 = blue8 + totalBytesPerChannel;
537     QRgb  *p, *end;
538     for (quint32 y = 0; y < height; ++y) {
539         p = (QRgb *)result.scanLine(y);
540         end = p + width;
541         while (p < end) {
542             red16 = ((*red8 << 8) + *(red8 + 1)) * scale;
543             green16 = ((*green8 << 8) + *(green8 + 1)) * scale;
544             blue16 = ((*blue8 << 8) + *(blue8 + 1)) * scale;
545             alpha16 = ((*alpha8 << 8) + *(alpha8 + 1)) * scale;
546             *p = qRgba((quint8)red16, (quint8)green16, (quint8)blue16, (quint8)alpha16);
547             ++p;  red8 += 2; green8 += 2; blue8 += 2; alpha8 += 2;
548         }
549     }
550     return result;
551 }
552 
processCMY8(QByteArray & imageData,quint32 width,quint32 height,quint64 totalBytesPerChannel)553 QImage QPsdHandler::processCMY8(QByteArray& imageData, quint32 width, quint32 height,
554                                  quint64 totalBytesPerChannel)
555 {
556 #ifdef QT_DEBUG
557     qDebug() << "8-bit CMY";
558 #endif
559     QImage result(width, height, QImage::Format_RGB32);
560     quint8 *cyan = (quint8*)imageData.constData();
561     quint8 *magenta = cyan + totalBytesPerChannel;
562     quint8 *yellow = magenta + totalBytesPerChannel;
563     QRgb  *p, *end;
564     for (quint32 y = 0; y < height; ++y) {
565         p = (QRgb *)result.scanLine(y);
566         end = p + width;
567         while (p < end) {
568             *p = QColor::fromCmyk(255 - *cyan, 255 - *magenta, 255 - *yellow, 0).rgba();
569             ++p; ++cyan; ++magenta; ++yellow;;
570         }
571     }
572     return result;
573 }
574 
processCMYK8(QByteArray & imageData,quint32 width,quint32 height,quint64 totalBytesPerChannel)575 QImage QPsdHandler::processCMYK8(QByteArray& imageData, quint32 width, quint32 height,
576                                  quint64 totalBytesPerChannel)
577 {
578 #ifdef QT_DEBUG
579     qDebug() << "8-bit CMYK";
580 #endif
581     QImage result(width, height, QImage::Format_RGB32);
582     quint8 *cyan = (quint8*)imageData.constData();
583     quint8 *magenta = cyan + totalBytesPerChannel;
584     quint8 *yellow = magenta + totalBytesPerChannel;
585     quint8 *key = yellow + totalBytesPerChannel;
586     QRgb  *p, *end;
587     for (quint32 y = 0; y < height; ++y) {
588         p = (QRgb *)result.scanLine(y);
589         end = p + width;
590         while (p < end) {
591             *p = QColor::fromCmyk(255 - *cyan, 255 - *magenta, 255 - *yellow, 255 - *key).rgba();
592             ++p; ++cyan; ++magenta; ++yellow; ++key;
593         }
594     }
595     return result;
596 }
597 
processCMYK8WithAlpha(QByteArray & imageData,quint32 width,quint32 height,quint64 totalBytesPerChannel)598 QImage QPsdHandler::processCMYK8WithAlpha(QByteArray& imageData, quint32 width, quint32 height,
599                                           quint64 totalBytesPerChannel)
600 {
601 #ifdef QT_DEBUG
602     qDebug() << "8-bit CMYK with Alpha";
603 #endif
604     QImage result(width, height, QImage::Format_ARGB32);
605     quint8 *cyan = (quint8*)imageData.constData();
606     quint8 *magenta = cyan + totalBytesPerChannel;
607     quint8 *yellow = magenta + totalBytesPerChannel;
608     quint8 *key = yellow + totalBytesPerChannel;
609     quint8 *alpha = key + totalBytesPerChannel;
610     QRgb  *p, *end;
611     for (quint32 y = 0; y < height; ++y) {
612         p = (QRgb *)result.scanLine(y);
613         end = p + width;
614         while (p < end) {
615             *p = QColor::fromCmyk(255 - *cyan, 255 - *magenta, 255 - *yellow, 255 - *key,
616                                   *alpha).rgba();
617             ++p; ++alpha; ++cyan; ++magenta; ++yellow; ++key;
618         }
619     }
620     return result;
621 }
622 
processCMYK16(QByteArray & imageData,quint32 width,quint32 height,quint64 totalBytesPerChannel)623 QImage QPsdHandler::processCMYK16(QByteArray& imageData, quint32 width, quint32 height,
624                                   quint64 totalBytesPerChannel)
625 {
626 #ifdef QT_DEBUG
627     qDebug() << "16-bit CMYK";
628 #endif
629     const qreal scale = (qPow(2, 8) -1 ) / (qPow(2, 16) - 1);
630     QImage result(width, height, QImage::Format_RGB32);
631     quint16 cyan16, magenta16, yellow16, key16;
632     quint8 *cyan8 = (quint8*)imageData.constData();
633     quint8 *magenta8 = cyan8 + totalBytesPerChannel;
634     quint8 *yellow8 = magenta8 + totalBytesPerChannel;
635     quint8 *key8 = yellow8 + totalBytesPerChannel;
636     QRgb  *p, *end;
637     for (quint32 y = 0; y < height; ++y) {
638         p = (QRgb *)result.scanLine(y);
639         end = p + width;
640         while (p < end) {
641             cyan16 = ((*cyan8 << 8) + *(cyan8 + 1)) * scale;
642             magenta16 = ((*magenta8 << 8) + *(magenta8 + 1)) * scale;
643             yellow16 = ((*yellow8 << 8) + *(yellow8 + 1)) * scale;
644             key16 = ((*key8 << 8) + *(key8 + 1)) * scale;
645             *p = QColor::fromCmyk(255 - (quint8)cyan16,
646                                   255 - (quint8)magenta16,
647                                   255 - (quint8)yellow16,
648                                   255 - (quint8)key16).rgba();
649             ++p;  cyan8 += 2; magenta8 += 2; yellow8 += 2; key8 += 2;
650         }
651     }
652     return result;
653 }
654 
processCMYK16WithAlpha(QByteArray & imageData,quint32 width,quint32 height,quint64 totalBytesPerChannel)655 QImage QPsdHandler::processCMYK16WithAlpha(QByteArray& imageData, quint32 width, quint32 height,
656                                            quint64 totalBytesPerChannel)
657 {
658 #ifdef QT_DEBUG
659     qDebug() << "16-bit CMYK with Alpha";
660 #endif
661     const qreal scale = (qPow(2, 8) -1 ) / (qPow(2, 16) - 1);
662     QImage result(width, height, QImage::Format_ARGB32);
663     quint16 cyan16, magenta16, yellow16, key16, alpha16;
664     quint8 *cyan8 = (quint8*)imageData.constData();
665     quint8 *magenta8 = cyan8 + totalBytesPerChannel;
666     quint8 *yellow8 = magenta8 + totalBytesPerChannel;
667     quint8 *key8 = yellow8 + totalBytesPerChannel;
668     quint8 *alpha8 = key8 + totalBytesPerChannel;
669     QRgb  *p, *end;
670     for (quint32 y = 0; y < height; ++y) {
671         p = (QRgb *)result.scanLine(y);
672         end = p + width;
673         while (p < end) {
674             cyan16 = ((*cyan8 << 8) + *(cyan8 + 1)) * scale;
675             magenta16 = ((*magenta8 << 8) + *(magenta8 + 1)) * scale;
676             yellow16 = ((*yellow8 << 8) + *(yellow8 + 1)) * scale;
677             key16 = ((*key8 << 8) + *(key8 + 1)) * scale;
678             alpha16 = ((*alpha8 << 8) + *(alpha8 + 1)) * scale;
679             *p = QColor::fromCmyk(255 - (quint8)cyan16,
680                                   255 - (quint8)magenta16,
681                                   255 - (quint8)yellow16,
682                                   255 - (quint8)key16,
683                                   255 - (quint8)alpha16).rgba();
684             ++p;  cyan8 += 2; magenta8 += 2; yellow8 += 2; key8 += 2, alpha8 += 2;
685         }
686     }
687     return result;
688 }
689 
processDuotone(QByteArray & imageData,quint32 width,quint32 height)690 QImage QPsdHandler::processDuotone(QByteArray& imageData, quint32 width, quint32 height)
691 {
692 #ifdef QT_DEBUG
693     qDebug() << "Duotone";
694 #endif
695     QImage result(width, height, QImage::Format_RGB32);
696     quint8 *data = (quint8*)imageData.constData();
697     QRgb  *p, *end;
698     for (quint32 y = 0; y < height; ++y) {
699         p = (QRgb *)result.scanLine(y);
700         end = p + width;
701         while (p < end) {
702             *p = qRgb(*data, *data, *data);
703             ++p; ++data;
704         }
705     }
706     return result;
707 }
708 
processLAB8(QByteArray & imageData,quint32 width,quint32 height,quint64 totalBytesPerChannel)709 QImage QPsdHandler::processLAB8(QByteArray& imageData, quint32 width, quint32 height,
710                                 quint64 totalBytesPerChannel)
711 {
712 #ifdef QT_DEBUG
713     qDebug() << "8-bit LAB";
714 #endif
715     QImage result(width, height, QImage::Format_RGB32);
716     quint8 *lightness = (quint8*)imageData.constData();
717     quint8 *a = lightness + totalBytesPerChannel;
718     quint8 *b = a + totalBytesPerChannel;
719     QRgb  *p, *end;
720     for (quint32 y = 0; y < height; ++y) {
721         p = (QRgb *)result.scanLine(y);
722         end = p + width;
723         while (p < end) {
724             *p = labToRgb(*lightness, *a, *b);
725             ++p; ++lightness; ++a; ++b;
726         }
727     }
728     return result;
729 }
730 
processLAB8WithAlpha(QByteArray & imageData,quint32 width,quint32 height,quint64 totalBytesPerChannel)731 QImage QPsdHandler::processLAB8WithAlpha(QByteArray& imageData, quint32 width, quint32 height,
732                                          quint64 totalBytesPerChannel)
733 {
734 #ifdef QT_DEBUG
735     qDebug() << "8-bit LAB with Alpha";
736 #endif
737     QImage result(width, height, QImage::Format_ARGB32);
738     quint8 *lightness = (quint8*)imageData.constData();
739     quint8 *a = lightness + totalBytesPerChannel;
740     quint8 *b = a + totalBytesPerChannel;
741     quint8 *alpha = b + totalBytesPerChannel;
742     QRgb  *p, *end;
743     for (quint32 y = 0; y < height; ++y) {
744         p = (QRgb *)result.scanLine(y);
745         end = p + width;
746         while (p < end) {
747             *p = labToRgb(*lightness, *a, *b, *alpha);
748             ++p; ++alpha; ++lightness; ++a; ++b;
749         }
750     }
751     return result;
752 }
753 
processLAB16(QByteArray & imageData,quint32 width,quint32 height,quint64 totalBytesPerChannel)754 QImage QPsdHandler::processLAB16(QByteArray& imageData, quint32 width, quint32 height,
755                                  quint64 totalBytesPerChannel)
756 {
757 #ifdef QT_DEBUG
758     qDebug() << "16-bit LAB";
759 #endif
760     const qreal scale = (qPow(2, 8) - 1 ) / (qPow(2, 16) - 1);
761     quint16 lightness16, a16, b16;
762     QImage result(width, height, QImage::Format_RGB32);
763     quint8 *lightness8 = (quint8*)imageData.constData();
764     quint8 *a8 = lightness8 + totalBytesPerChannel;
765     quint8 *b8 = a8 + totalBytesPerChannel;
766     QRgb  *p, *end;
767     for (quint32 y = 0; y < height; ++y) {
768         p = (QRgb *)result.scanLine(y);
769         end = p + width;
770         while (p < end) {
771             lightness16 = ((*lightness8 << 8) + *(lightness8 + 1)) * scale;
772             a16 = ((*a8 << 8) + *(a8 + 1)) * scale;
773             b16 = ((*b8 << 8) + *(b8 + 1)) * scale;
774             *p = labToRgb(lightness16, a16, b16);
775             ++p; lightness8 += 2; a8 += 2; b8 += 2;
776         }
777     }
778     return result;
779 }
780 
processLAB16WithAlpha(QByteArray & imageData,quint32 width,quint32 height,quint64 totalBytesPerChannel)781 QImage QPsdHandler::processLAB16WithAlpha(QByteArray& imageData, quint32 width, quint32 height,
782                                           quint64 totalBytesPerChannel)
783 {
784 #ifdef QT_DEBUG
785     qDebug() << "8-bit LAB with Alpha";
786 #endif
787     const qreal scale = (qPow(2, 8) - 1 ) / (qPow(2, 16) - 1);
788     quint16 lightness16, a16, b16, alpha16;
789     QImage result(width, height, QImage::Format_ARGB32);
790     quint8 *lightness8 = (quint8*)imageData.constData();
791     quint8 *a8 = lightness8 + totalBytesPerChannel;
792     quint8 *b8 = a8 + totalBytesPerChannel;
793     quint8 *alpha8 = b8 + totalBytesPerChannel;
794     QRgb  *p, *end;
795     for (quint32 y = 0; y < height; ++y) {
796         p = (QRgb *)result.scanLine(y);
797         end = p + width;
798         while (p < end) {
799             lightness16 = ((*lightness8 << 8) + *(lightness8 + 1)) * scale;
800             a16 = ((*a8 << 8) + *(a8 + 1)) * scale;
801             b16 = ((*b8 << 8) + *(b8 + 1)) * scale;
802             alpha16 = ((*alpha8 << 8) + *(alpha8 + 1)) * scale;
803             *p = labToRgb(lightness16, a16, b16, alpha16);
804             ++p; lightness8 += 2; a8 += 2; b8 += 2; alpha8 += 2;
805         }
806     }
807     return result;
808 }
809