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