Krita Source Code Documentation
Loading...
Searching...
No Matches
psd_pixel_utils.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
3 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include "psd_pixel_utils.h"
9
10#include <QIODevice>
11#include <QMap>
12#include <QtEndian>
13#include <QtGlobal>
14
15#include <KoColorSpace.h>
16#include <KoColorSpaceMaths.h>
17#include <KoColorSpaceTraits.h>
19#include <kis_global.h>
20#include <kis_iterator_ng.h>
21
25#include <compression.h>
26#include <psd.h>
27#include <psd_layer_record.h>
28
30{
31template<class Traits>
32typename Traits::channels_type convertByteOrder(typename Traits::channels_type value);
33// default implementation is undefined for every color space should be added manually
34
35template<>
37{
38 return value;
39}
40
41template<>
43{
44 return qFromBigEndian((quint16)value);
45}
46
47template<>
49{
50 return qFromBigEndian((quint32)value);
51}
52
53template<>
55{
56 return value;
57}
58
59template<>
61{
62 return qFromBigEndian((quint16)value);
63}
64
65template<>
67{
68 return qFromBigEndian((quint32)value);
69}
70
71template<>
73{
74 return value;
75}
76
77template<>
79{
80 return qFromBigEndian((quint16)value);
81}
82
83template<>
85{
86 return qFromBigEndian((quint32)value);
87}
88
89template<>
91{
92 return value;
93}
94
95template<>
97{
98 return qFromBigEndian((quint16)value);
99}
100
101template<>
103{
104 return qFromBigEndian((quint32)value);
105}
106
107template<>
109{
110 return value;
111}
112
113template<>
115{
116 return qFromBigEndian((quint16)value);
117}
118
119template<>
121{
122 return qFromBigEndian((quint32)value);
123}
124
125template<class Traits>
126inline quint8 truncateToOpacity(typename Traits::channels_type value);
127
128template<>
130{
131 return value;
132}
133
134template<>
136{
137 return value >> 8;
138}
139
140template<>
142{
143 return static_cast<quint8>(value * 255U);
144}
145
146template<class Traits, psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
147void readAlphaMaskPixel(const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
148{
149 using channels_type = typename Traits::channels_type;
150
151 const channels_type data = reinterpret_cast<const channels_type *>(channelBytes.first().constData())[col];
152 if (byteOrder == psd_byte_order::psdBigEndian) {
153 *dstPtr = truncateToOpacity<Traits>(convertByteOrder<Traits>(data));
154 } else {
155 *dstPtr = truncateToOpacity<Traits>(data);
156 }
157}
158
159template<class Traits, psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
160inline typename Traits::channels_type
161readChannelValue(const QMap<quint16, QByteArray> &channelBytes, quint16 channelId, int col, typename Traits::channels_type defaultValue)
162{
163 using channels_type = typename Traits::channels_type;
164
165 if (channelBytes.contains(channelId)) {
166 const QByteArray &bytes = channelBytes[channelId];
167 if (col < bytes.size()) {
168 const channels_type data = reinterpret_cast<const channels_type *>(bytes.constData())[col];
169 if (byteOrder == psd_byte_order::psdBigEndian) {
170 return convertByteOrder<Traits>(data);
171 } else {
172 return data;
173 }
174 }
175
176 dbgFile << "col index out of range channelId: " << channelId << " col:" << col;
177 }
178
179 return defaultValue;
180}
181
182template<class Traits, psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
183void readGrayPixel(const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
184{
185 using Pixel = typename Traits::Pixel;
186 using channels_type = typename Traits::channels_type;
187
188 const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
189 Pixel *pixelPtr = reinterpret_cast<Pixel *>(dstPtr);
190
191 pixelPtr->gray = readChannelValue<Traits, byteOrder>(channelBytes, 0, col, unitValue);
192 pixelPtr->alpha = readChannelValue<Traits, byteOrder>(channelBytes, -1, col, unitValue);
193}
194
195template<class Traits, psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
196void readRgbPixel(const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
197{
198 using Pixel = typename Traits::Pixel;
199 using channels_type = typename Traits::channels_type;
200
201 const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
202 Pixel *pixelPtr = reinterpret_cast<Pixel *>(dstPtr);
203
204 pixelPtr->blue = readChannelValue<Traits, byteOrder>(channelBytes, 2, col, unitValue);
205 pixelPtr->green = readChannelValue<Traits, byteOrder>(channelBytes, 1, col, unitValue);
206 pixelPtr->red = readChannelValue<Traits, byteOrder>(channelBytes, 0, col, unitValue);
207 pixelPtr->alpha = readChannelValue<Traits, byteOrder>(channelBytes, -1, col, unitValue);
208}
209
210template<class Traits, psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
211void readCmykPixel(const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
212{
213 using Pixel = typename Traits::Pixel;
214 using channels_type = typename Traits::channels_type;
215
216 const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
217 Pixel *pixelPtr = reinterpret_cast<Pixel *>(dstPtr);
218
219 pixelPtr->cyan = unitValue - readChannelValue<Traits, byteOrder>(channelBytes, 0, col, unitValue);
220 pixelPtr->magenta = unitValue - readChannelValue<Traits, byteOrder>(channelBytes, 1, col, unitValue);
221 pixelPtr->yellow = unitValue - readChannelValue<Traits, byteOrder>(channelBytes, 2, col, unitValue);
222 pixelPtr->black = unitValue - readChannelValue<Traits, byteOrder>(channelBytes, 3, col, unitValue);
223 pixelPtr->alpha = readChannelValue<Traits, byteOrder>(channelBytes, -1, col, unitValue);
224}
225
226template<class Traits, psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
227void readLabPixel(const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
228{
229 using Pixel = typename Traits::Pixel;
230 using channels_type = typename Traits::channels_type;
231
232 const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
233 Pixel *pixelPtr = reinterpret_cast<Pixel *>(dstPtr);
234
235 pixelPtr->L = readChannelValue<Traits, byteOrder>(channelBytes, 0, col, unitValue);
236 pixelPtr->a = readChannelValue<Traits, byteOrder>(channelBytes, 1, col, unitValue);
237 pixelPtr->b = readChannelValue<Traits, byteOrder>(channelBytes, 2, col, unitValue);
238 pixelPtr->alpha = readChannelValue<Traits, byteOrder>(channelBytes, -1, col, unitValue);
239}
240
241template<psd_byte_order byteOrder>
242void readRgbPixelCommon(int channelSize, const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
243{
244 if (channelSize == 1) {
245 readRgbPixel<KoBgrU8Traits, byteOrder>(channelBytes, col, dstPtr);
246 } else if (channelSize == 2) {
247 readRgbPixel<KoBgrU16Traits, byteOrder>(channelBytes, col, dstPtr);
248 } else if (channelSize == 4) {
249 readRgbPixel<KoBgrU16Traits, byteOrder>(channelBytes, col, dstPtr);
250 }
251}
252
253template<psd_byte_order byteOrder>
254void readGrayPixelCommon(int channelSize, const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
255{
256 if (channelSize == 1) {
257 readGrayPixel<KoGrayU8Traits, byteOrder>(channelBytes, col, dstPtr);
258 } else if (channelSize == 2) {
259 readGrayPixel<KoGrayU16Traits, byteOrder>(channelBytes, col, dstPtr);
260 } else if (channelSize == 4) {
261 readGrayPixel<KoGrayU32Traits, byteOrder>(channelBytes, col, dstPtr);
262 }
263}
264
265template<psd_byte_order byteOrder>
266void readCmykPixelCommon(int channelSize, const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
267{
268 if (channelSize == 1) {
269 readCmykPixel<KoCmykU8Traits, byteOrder>(channelBytes, col, dstPtr);
270 } else if (channelSize == 2) {
271 readCmykPixel<KoCmykU16Traits, byteOrder>(channelBytes, col, dstPtr);
272 } else if (channelSize == 4) {
273 readCmykPixel<KoCmykF32Traits, byteOrder>(channelBytes, col, dstPtr);
274 }
275}
276
277template<psd_byte_order byteOrder>
278void readLabPixelCommon(int channelSize, const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
279{
280 if (channelSize == 1) {
281 readLabPixel<KoLabU8Traits, byteOrder>(channelBytes, col, dstPtr);
282 } else if (channelSize == 2) {
283 readLabPixel<KoLabU16Traits, byteOrder>(channelBytes, col, dstPtr);
284 } else if (channelSize == 4) {
285 readLabPixel<KoLabF32Traits, byteOrder>(channelBytes, col, dstPtr);
286 }
287}
288
289template<psd_byte_order byteOrder>
290void readAlphaMaskPixelCommon(int channelSize, const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
291{
292 if (channelSize == 1) {
293 readAlphaMaskPixel<AlphaU8Traits, byteOrder>(channelBytes, col, dstPtr);
294 } else if (channelSize == 2) {
295 readAlphaMaskPixel<AlphaU16Traits, byteOrder>(channelBytes, col, dstPtr);
296 } else if (channelSize == 4) {
297 readAlphaMaskPixel<AlphaF32Traits, byteOrder>(channelBytes, col, dstPtr);
298 }
299}
300
301QMap<quint16, QByteArray> fetchChannelsBytes(QIODevice &io, QVector<ChannelInfo *> channelInfoRecords, int row, int width, int channelSize, bool processMasks)
302{
303 const int uncompressedLength = width * channelSize;
304
305 QMap<quint16, QByteArray> channelBytes;
306
307 Q_FOREACH (ChannelInfo *channelInfo, channelInfoRecords) {
308 // user supplied masks are ignored here
309 if (!processMasks && channelInfo->channelId < -1)
310 continue;
311
312 io.seek(channelInfo->channelDataStart + channelInfo->channelOffset);
313
315 channelBytes[channelInfo->channelId] = io.read(uncompressedLength);
316 channelInfo->channelOffset += uncompressedLength;
317 } else if (channelInfo->compressionType == psd_compression_type::RLE) {
318 int rleLength = channelInfo->rleRowLengths[row];
319 QByteArray compressedBytes = io.read(rleLength);
320 QByteArray uncompressedBytes = Compression::uncompress(uncompressedLength, compressedBytes, channelInfo->compressionType);
321 channelBytes.insert(channelInfo->channelId, uncompressedBytes);
322 channelInfo->channelOffset += rleLength;
323 } else {
324 QString error = QString("Unsupported Compression mode: %1")
325 .arg(static_cast<std::uint16_t>(channelInfo->compressionType));
326 dbgFile << "ERROR: fetchChannelsBytes:" << error;
328 }
329 }
330
331 return channelBytes;
332}
333
334using PixelFunc = std::function<void(int, const QMap<quint16, QByteArray> &, int, quint8 *)>;
335
337 QIODevice &io,
338 const QRect &layerRect,
339 QVector<ChannelInfo *> infoRecords,
340 int channelSize,
341 PixelFunc pixelFunc,
342 bool processMasks)
343{
344 KisOffsetKeeper keeper(io);
345
346 if (layerRect.isEmpty()) {
347 dbgFile << "Empty layer!";
348 return;
349 }
350
351 if (infoRecords.first()->compressionType == psd_compression_type::ZIP || infoRecords.first()->compressionType == psd_compression_type::ZIPWithPrediction) {
352 const int numPixels = channelSize * layerRect.width() * layerRect.height();
353
354 QMap<quint16, QByteArray> channelBytes;
355
356 Q_FOREACH (ChannelInfo *info, infoRecords) {
357 io.seek(info->channelDataStart);
358 QByteArray compressedBytes = io.read(info->channelDataLength);
359 QByteArray uncompressedBytes;
360
361 uncompressedBytes = Compression::uncompress(numPixels, compressedBytes, infoRecords.first()->compressionType, layerRect.width(), channelSize * 8);
362
363 if (uncompressedBytes.size() != numPixels) {
364 QString error = QString("Failed to unzip channel data: id = %1, compression = %2")
365 .arg(info->channelId)
366 .arg(static_cast<std::uint16_t>(info->compressionType));
367 dbgFile << "ERROR:" << error;
368 dbgFile << " " << ppVar(info->channelId);
369 dbgFile << " " << ppVar(info->channelDataStart);
370 dbgFile << " " << ppVar(info->channelDataLength);
371 dbgFile << " " << ppVar(info->compressionType);
373 }
374
375 channelBytes.insert(info->channelId, uncompressedBytes);
376 }
377
378 KisSequentialIterator it(dev, layerRect);
379 int col = 0;
380 while (it.nextPixel()) {
381 pixelFunc(channelSize, channelBytes, col, it.rawData());
382 col++;
383 }
384
385 } else {
386 KisHLineIteratorSP it = dev->createHLineIteratorNG(layerRect.left(), layerRect.top(), layerRect.width());
387 for (int i = 0; i < layerRect.height(); i++) {
388 QMap<quint16, QByteArray> channelBytes;
389
390 channelBytes = fetchChannelsBytes(io, infoRecords, i, layerRect.width(), channelSize, processMasks);
391
392 for (int col = 0; col < layerRect.width(); col++) {
393 pixelFunc(channelSize, channelBytes, col, it->rawData());
394 it->nextPixel();
395 }
396
399 if (i < layerRect.height() - 1) {
400 it->nextRow();
401 }
402 }
403 }
404}
405
406template<psd_byte_order byteOrder>
407void readChannelsImpl(QIODevice &io,
408 KisPaintDeviceSP device,
409 psd_color_mode colorMode,
410 int channelSize,
411 const QRect &layerRect,
412 QVector<ChannelInfo *> infoRecords)
413{
414 switch (colorMode) {
415 case Grayscale:
416 readCommon(device, io, layerRect, infoRecords, channelSize, &readGrayPixelCommon<byteOrder>, false);
417 break;
418 case RGB:
419 readCommon(device, io, layerRect, infoRecords, channelSize, &readRgbPixelCommon<byteOrder>, false);
420 break;
421 case CMYK:
422 readCommon(device, io, layerRect, infoRecords, channelSize, &readCmykPixelCommon<byteOrder>, false);
423 break;
424 case Lab:
425 readCommon(device, io, layerRect, infoRecords, channelSize, &readLabPixelCommon<byteOrder>, false);
426 break;
427 case Bitmap:
428 case Indexed:
429 case MultiChannel:
430 case DuoTone:
432 default:
433 QString error = QString("Unsupported color mode: %1").arg(colorMode);
435 }
436}
437
438void readChannels(QIODevice &io,
439 KisPaintDeviceSP device,
440 psd_color_mode colorMode,
441 int channelSize,
442 const QRect &layerRect,
443 QVector<ChannelInfo *> infoRecords,
444 psd_byte_order byteOrder)
445{
446 switch (byteOrder) {
448 return readChannelsImpl<psd_byte_order::psdLittleEndian>(io, device, colorMode, channelSize, layerRect, infoRecords);
449 default:
450 return readChannelsImpl<psd_byte_order::psdBigEndian>(io, device, colorMode, channelSize, layerRect, infoRecords);
451 }
452}
453
454template<psd_byte_order byteOrder>
455void readAlphaMaskChannelsImpl(QIODevice &io, KisPaintDeviceSP device, int channelSize, const QRect &layerRect, QVector<ChannelInfo *> infoRecords)
456{
457 KIS_SAFE_ASSERT_RECOVER_RETURN(infoRecords.size() == 1);
458 readCommon(device, io, layerRect, infoRecords, channelSize, &readAlphaMaskPixelCommon<byteOrder>, true);
459}
460
461void readAlphaMaskChannels(QIODevice &io,
462 KisPaintDeviceSP device,
463 int channelSize,
464 const QRect &layerRect,
465 QVector<ChannelInfo *> infoRecords,
466 psd_byte_order byteOrder)
467{
468 switch (byteOrder) {
470 return readAlphaMaskChannelsImpl<psd_byte_order::psdLittleEndian>(io, device, channelSize, layerRect, infoRecords);
471 default:
472 return readAlphaMaskChannelsImpl<psd_byte_order::psdBigEndian>(io, device, channelSize, layerRect, infoRecords);
473 }
474}
475
476template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
477void writeChannelDataRLEImpl(QIODevice &io,
478 const quint8 *plane,
479 const int channelSize,
480 const QRect &rc,
481 const qint64 sizeFieldOffset,
482 const qint64 rleBlockOffset,
483 const bool writeCompressionType)
484{
486 QScopedPointer<Pusher> channelBlockSizeExternalTag;
487 if (sizeFieldOffset >= 0) {
488 channelBlockSizeExternalTag.reset(new Pusher(io, 0, sizeFieldOffset));
489 }
490
491 if (writeCompressionType) {
492 SAFE_WRITE_EX(byteOrder, io, static_cast<quint16>(psd_compression_type::RLE));
493 }
494
495 const bool externalRleBlock = rleBlockOffset >= 0;
496
497 // the start of RLE sizes block
498 const qint64 channelRLESizePos = externalRleBlock ? rleBlockOffset : io.pos();
499
500 {
501 QScopedPointer<KisOffsetKeeper> rleOffsetKeeper;
502
503 if (externalRleBlock) {
504 rleOffsetKeeper.reset(new KisOffsetKeeper(io));
505 io.seek(rleBlockOffset);
506 }
507
508 // write zero's for the channel lengths block
509 for (int i = 0; i < rc.height(); ++i) {
510 // XXX: choose size for PSB!
511 const quint16 fakeRLEBLockSize = 0;
512 SAFE_WRITE_EX(byteOrder, io, fakeRLEBLockSize);
513 }
514 }
515
516 const int stride = channelSize * rc.width();
517 for (qint32 row = 0; row < rc.height(); ++row) {
518 QByteArray uncompressed = QByteArray::fromRawData((const char *)plane + row * stride, stride);
519 QByteArray compressed = Compression::compress(uncompressed, psd_compression_type::RLE);
520
521 KisAslWriterUtils::OffsetStreamPusher<quint16, byteOrder> rleExternalTag(io, 0, channelRLESizePos + row * static_cast<qint64>(sizeof(quint16)));
522
523 if (io.write(compressed) != compressed.size()) {
524 throw KisAslWriterUtils::ASLWriteException("Failed to write image data");
525 }
526 }
527}
528
529template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
530void writeChannelDataZIPImpl(QIODevice &io,
531 const quint8 *plane,
532 const int channelSize,
533 const QRect &rc,
534 const qint64 sizeFieldOffset,
535 const bool writeCompressionType)
536{
538 QScopedPointer<Pusher> channelBlockSizeExternalTag;
539 if (sizeFieldOffset >= 0) {
540 channelBlockSizeExternalTag.reset(new Pusher(io, 0, sizeFieldOffset));
541 }
542
543 if (writeCompressionType) {
544 SAFE_WRITE_EX(byteOrder, io, static_cast<quint16>(psd_compression_type::ZIP));
545 }
546
547 QByteArray uncompressed(reinterpret_cast<const char *>(plane), rc.width() * rc.height() * channelSize);
548 QByteArray compressed(Compression::compress(uncompressed, psd_compression_type::ZIP));
549
550 if (compressed.size() == 0 || io.write(compressed) != compressed.size()) {
551 throw KisAslWriterUtils::ASLWriteException("Failed to write image data");
552 }
553}
554
555void writeChannelDataRLE(QIODevice &io,
556 const quint8 *plane,
557 const int channelSize,
558 const QRect &rc,
559 const qint64 sizeFieldOffset,
560 const qint64 rleBlockOffset,
561 const bool writeCompressionType,
562 psd_byte_order byteOrder)
563{
564 switch (byteOrder) {
566 return writeChannelDataRLEImpl<psd_byte_order::psdLittleEndian>(io, plane, channelSize, rc, sizeFieldOffset, rleBlockOffset, writeCompressionType);
567 default:
568 return writeChannelDataRLEImpl(io, plane, channelSize, rc, sizeFieldOffset, rleBlockOffset, writeCompressionType);
569 }
570}
571
572template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
573inline void preparePixelForWrite(quint8 *dataPlane, int numPixels, int channelSize, int channelId, psd_color_mode colorMode)
574{
575 // if the bitdepth > 8, place the bytes in the right order
576 // if cmyk, invert the pixel value
577 if (channelSize == 1) {
578 if (channelId >= 0 && (colorMode == CMYK || colorMode == CMYK64)) {
579 for (int i = 0; i < numPixels; ++i) {
580 dataPlane[i] = 255 - dataPlane[i];
581 }
582 }
583 } else if (channelSize == 2) {
584 quint16 val;
585 for (int i = 0; i < numPixels; ++i) {
586 quint16 *pixelPtr = reinterpret_cast<quint16 *>(dataPlane) + i;
587
588 val = *pixelPtr;
589 if (byteOrder == psd_byte_order::psdBigEndian)
590 val = qFromBigEndian(val);
591 if (channelId >= 0 && (colorMode == CMYK || colorMode == CMYK64)) {
592 val = quint16_MAX - val;
593 }
594 *pixelPtr = val;
595 }
596 } else if (channelSize == 4) {
597 quint32 val;
598 for (int i = 0; i < numPixels; ++i) {
599 quint32 *pixelPtr = reinterpret_cast<quint32 *>(dataPlane) + i;
600
601 val = *pixelPtr;
602 if (byteOrder == psd_byte_order::psdBigEndian)
603 val = qFromBigEndian(val);
604 if (channelId >= 0 && (colorMode == CMYK || colorMode == CMYK64)) {
605 val = std::numeric_limits<quint32>::max() - val;
606 }
607 *pixelPtr = val;
608 }
609 }
610}
611
612template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
613void writePixelDataCommonImpl(QIODevice &io,
615 const QRect &rc,
616 psd_color_mode colorMode,
617 int channelSize,
618 bool alphaFirst,
619 const bool writeCompressionType,
620 QVector<ChannelWritingInfo> &writingInfoList,
621 psd_compression_type compressionType)
622{
623 // Empty rects must be processed separately on a higher level!
624 KIS_ASSERT_RECOVER_RETURN(!rc.isEmpty());
625
626 QVector<quint8 *> tmp = dev->readPlanarBytes(rc.x() - dev->x(), rc.y() - dev->y(), rc.width(), rc.height());
627 const KoColorSpace *colorSpace = dev->colorSpace();
628
629 QVector<quint8 *> planes;
630
631 { // prepare 'planes' array
632
633 quint8 *alphaPlanePtr = 0;
634
635 const QList<KoChannelInfo *> origChannels = colorSpace->channels();
636 Q_FOREACH (KoChannelInfo *ch, KoChannelInfo::displayOrderSorted(origChannels)) {
637 int channelIndex = KoChannelInfo::displayPositionToChannelIndex(ch->displayPosition(), origChannels);
638
639 quint8 *holder = 0;
640 std::swap(holder, tmp[channelIndex]);
641
642 if (ch->channelType() == KoChannelInfo::ALPHA) {
643 std::swap(holder, alphaPlanePtr);
644 } else {
645 planes.append(holder);
646 }
647 }
648
649 if (alphaPlanePtr) {
650 if (alphaFirst) {
651 planes.insert(0, alphaPlanePtr);
652 KIS_ASSERT_RECOVER_NOOP(writingInfoList.first().channelId == -1);
653 } else {
654 planes.append(alphaPlanePtr);
655 KIS_ASSERT_RECOVER_NOOP((writingInfoList.size() == planes.size() - 1) || (writingInfoList.last().channelId == -1));
656 }
657 }
658
659 // now planes are holding pointers to quint8 arrays
660 tmp.clear();
661 }
662
663 KIS_ASSERT_RECOVER_RETURN(planes.size() >= writingInfoList.size());
664
665 const int numPixels = rc.width() * rc.height();
666
667 // write down the planes
668
669 try {
670 for (int i = 0; i < writingInfoList.size(); i++) {
671 const ChannelWritingInfo &info = writingInfoList[i];
672
673 dbgFile << "\tWriting channel" << i << "psd channel id" << info.channelId;
674
675 // WARNING: Pixel data is ALWAYS in big endian!!!
676 preparePixelForWrite<psd_byte_order::psdBigEndian>(planes[i], numPixels, channelSize, info.channelId, colorMode);
677
678 dbgFile << "\t\tchannel start" << ppVar(io.pos()) << ", compression type" << compressionType;
679
680 switch (compressionType) {
683 writeChannelDataZIPImpl<byteOrder>(io, planes[i], channelSize, rc, info.sizeFieldOffset, writeCompressionType);
684 break;
685 }
687 default: {
688 writeChannelDataRLEImpl<byteOrder>(io, planes[i], channelSize, rc, info.sizeFieldOffset, info.rleBlockOffset, writeCompressionType);
689 break;
690 }
691 }
692 }
693
695 Q_FOREACH (quint8 *plane, planes) {
696 delete[] plane;
697 }
698 planes.clear();
699
701 }
702
703 Q_FOREACH (quint8 *plane, planes) {
704 delete[] plane;
705 }
706 planes.clear();
707}
708
709void writePixelDataCommon(QIODevice &io,
711 const QRect &rc,
712 psd_color_mode colorMode,
713 int channelSize,
714 bool alphaFirst,
715 const bool writeCompressionType,
716 QVector<ChannelWritingInfo> &writingInfoList,
717 psd_compression_type compressionType,
718 psd_byte_order byteOrder)
719{
720 switch (byteOrder) {
722 return writePixelDataCommonImpl<psd_byte_order::psdLittleEndian>(io,
723 dev,
724 rc,
725 colorMode,
726 channelSize,
727 alphaFirst,
728 writeCompressionType,
729 writingInfoList,
730 compressionType);
731 default:
732 return writePixelDataCommonImpl(io, dev, rc, colorMode, channelSize, alphaFirst, writeCompressionType, writingInfoList, compressionType);
733 }
734}
735}
float value(const T *src, size_t ch)
static QByteArray uncompress(int unpacked_len, QByteArray bytes, psd_compression_type compressionType, int row_size=0, int color_depth=0)
static QByteArray compress(QByteArray bytes, psd_compression_type compressionType, int row_size=0, int color_depth=0)
QVector< quint8 * > readPlanarBytes(qint32 x, qint32 y, qint32 w, qint32 h) const
KisHLineIteratorSP createHLineIteratorNG(qint32 x, qint32 y, qint32 w)
const KoColorSpace * colorSpace() const
ALWAYS_INLINE quint8 * rawData()
@ ALPHA
The channel represents the opacity of a pixel.
enumChannelType channelType() const
static int displayPositionToChannelIndex(int displayPosition, const QList< KoChannelInfo * > &channels)
static QList< KoChannelInfo * > displayOrderSorted(const QList< KoChannelInfo * > &channels)
qint32 displayPosition() const
QList< KoChannelInfo * > channels
#define SAFE_WRITE_EX(byteOrder, device, varname)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define PREPEND_METHOD(msg)
Definition kis_debug.h:172
#define ppVar(var)
Definition kis_debug.h:155
#define dbgFile
Definition kis_debug.h:53
const quint16 quint16_MAX
Definition kis_global.h:25
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
void readCmykPixelCommon(int channelSize, const QMap< quint16, QByteArray > &channelBytes, int col, quint8 *dstPtr)
void readRgbPixelCommon(int channelSize, const QMap< quint16, QByteArray > &channelBytes, int col, quint8 *dstPtr)
Traits::channels_type convertByteOrder(typename Traits::channels_type value)
void writePixelDataCommon(QIODevice &io, KisPaintDeviceSP dev, const QRect &rc, psd_color_mode colorMode, int channelSize, bool alphaFirst, const bool writeCompressionType, QVector< ChannelWritingInfo > &writingInfoList, psd_compression_type compressionType, psd_byte_order byteOrder)
void readCommon(KisPaintDeviceSP dev, QIODevice &io, const QRect &layerRect, QVector< ChannelInfo * > infoRecords, int channelSize, PixelFunc pixelFunc, bool processMasks)
void preparePixelForWrite(quint8 *dataPlane, int numPixels, int channelSize, int channelId, psd_color_mode colorMode)
void readLabPixelCommon(int channelSize, const QMap< quint16, QByteArray > &channelBytes, int col, quint8 *dstPtr)
void readChannels(QIODevice &io, KisPaintDeviceSP device, psd_color_mode colorMode, int channelSize, const QRect &layerRect, QVector< ChannelInfo * > infoRecords, psd_byte_order byteOrder)
quint16 convertByteOrder< KoBgrU16Traits >(quint16 value)
quint16 convertByteOrder< KoCmykU16Traits >(quint16 value)
quint8 truncateToOpacity(typename Traits::channels_type value)
quint8 convertByteOrder< KoCmykU8Traits >(quint8 value)
void writePixelDataCommonImpl(QIODevice &io, KisPaintDeviceSP dev, const QRect &rc, psd_color_mode colorMode, int channelSize, bool alphaFirst, const bool writeCompressionType, QVector< ChannelWritingInfo > &writingInfoList, psd_compression_type compressionType)
void writeChannelDataRLE(QIODevice &io, const quint8 *plane, const int channelSize, const QRect &rc, const qint64 sizeFieldOffset, const qint64 rleBlockOffset, const bool writeCompressionType, psd_byte_order byteOrder)
void readAlphaMaskChannels(QIODevice &io, KisPaintDeviceSP device, int channelSize, const QRect &layerRect, QVector< ChannelInfo * > infoRecords, psd_byte_order byteOrder)
std::function< void(int, const QMap< quint16, QByteArray > &, int, quint8 *)> PixelFunc
void readAlphaMaskPixel(const QMap< quint16, QByteArray > &channelBytes, int col, quint8 *dstPtr)
void readAlphaMaskPixelCommon(int channelSize, const QMap< quint16, QByteArray > &channelBytes, int col, quint8 *dstPtr)
void writeChannelDataRLEImpl(QIODevice &io, const quint8 *plane, const int channelSize, const QRect &rc, const qint64 sizeFieldOffset, const qint64 rleBlockOffset, const bool writeCompressionType)
quint32 convertByteOrder< KoGrayU32Traits >(quint32 value)
void writeChannelDataZIPImpl(QIODevice &io, const quint8 *plane, const int channelSize, const QRect &rc, const qint64 sizeFieldOffset, const bool writeCompressionType)
float convertByteOrder< AlphaF32Traits >(float value)
quint8 convertByteOrder< AlphaU8Traits >(quint8 value)
QMap< quint16, QByteArray > fetchChannelsBytes(QIODevice &io, QVector< ChannelInfo * > channelInfoRecords, int row, int width, int channelSize, bool processMasks)
quint8 convertByteOrder< KoGrayU8Traits >(quint8 value)
void readGrayPixel(const QMap< quint16, QByteArray > &channelBytes, int col, quint8 *dstPtr)
void readAlphaMaskChannelsImpl(QIODevice &io, KisPaintDeviceSP device, int channelSize, const QRect &layerRect, QVector< ChannelInfo * > infoRecords)
quint16 convertByteOrder< KoGrayU16Traits >(quint16 value)
void readLabPixel(const QMap< quint16, QByteArray > &channelBytes, int col, quint8 *dstPtr)
quint8 truncateToOpacity< AlphaU8Traits >(typename AlphaU8Traits::channels_type value)
void readRgbPixel(const QMap< quint16, QByteArray > &channelBytes, int col, quint8 *dstPtr)
Traits::channels_type readChannelValue(const QMap< quint16, QByteArray > &channelBytes, quint16 channelId, int col, typename Traits::channels_type defaultValue)
quint16 convertByteOrder< AlphaU16Traits >(quint16 value)
float convertByteOrder< KoCmykF32Traits >(float value)
quint8 convertByteOrder< KoBgrU8Traits >(quint8 value)
void readChannelsImpl(QIODevice &io, KisPaintDeviceSP device, psd_color_mode colorMode, int channelSize, const QRect &layerRect, QVector< ChannelInfo * > infoRecords)
void readGrayPixelCommon(int channelSize, const QMap< quint16, QByteArray > &channelBytes, int col, quint8 *dstPtr)
quint32 convertByteOrder< KoBgrU32Traits >(quint32 value)
quint8 convertByteOrder< KoLabU8Traits >(quint8 value)
quint8 truncateToOpacity< AlphaU16Traits >(typename AlphaU16Traits::channels_type value)
quint8 truncateToOpacity< AlphaF32Traits >(typename AlphaF32Traits::channels_type value)
float convertByteOrder< KoLabF32Traits >(float value)
void readCmykPixel(const QMap< quint16, QByteArray > &channelBytes, int col, quint8 *dstPtr)
quint16 convertByteOrder< KoLabU16Traits >(quint16 value)
psd_byte_order
Definition psd.h:33
psd_compression_type
Definition psd.h:39
@ RLE
Definition psd.h:41
@ ZIPWithPrediction
Definition psd.h:43
@ Uncompressed
Definition psd.h:40
@ ZIP
Definition psd.h:42
psd_color_mode
Definition psd.h:50
@ CMYK64
Definition psd.h:62
@ Lab
Definition psd.h:58
@ RGB
Definition psd.h:54
@ DuoTone
Definition psd.h:57
@ CMYK
Definition psd.h:55
@ COLORMODE_UNKNOWN
Definition psd.h:65
@ Indexed
Definition psd.h:53
@ MultiChannel
Definition psd.h:56
@ Grayscale
Definition psd.h:52
@ Bitmap
Definition psd.h:51
QVector< quint32 > rleRowLengths
psd_compression_type compressionType
quint64 channelDataLength
quint64 channelDataStart
_channels_type_ channels_type
the type of the value of the channels of this color space