File indexing completed on 2024-05-12 15:59:40

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
0003  *  SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "psd_pixel_utils.h"
0009 
0010 #include <QIODevice>
0011 #include <QMap>
0012 #include <QtEndian>
0013 #include <QtGlobal>
0014 
0015 #include <KoColorSpace.h>
0016 #include <KoColorSpaceMaths.h>
0017 #include <KoColorSpaceTraits.h>
0018 #include <colorspaces/KoAlphaColorSpace.h>
0019 #include <kis_global.h>
0020 #include <kis_iterator_ng.h>
0021 
0022 #include <asl/kis_asl_reader_utils.h>
0023 #include <asl/kis_asl_writer_utils.h>
0024 #include <asl/kis_offset_keeper.h>
0025 #include <compression.h>
0026 #include <psd.h>
0027 #include <psd_layer_record.h>
0028 
0029 namespace PsdPixelUtils
0030 {
0031 template<class Traits>
0032 typename Traits::channels_type convertByteOrder(typename Traits::channels_type value);
0033 // default implementation is undefined for every color space should be added manually
0034 
0035 template<>
0036 inline quint8 convertByteOrder<AlphaU8Traits>(quint8 value)
0037 {
0038     return value;
0039 }
0040 
0041 template<>
0042 inline quint16 convertByteOrder<AlphaU16Traits>(quint16 value)
0043 {
0044     return qFromBigEndian((quint16)value);
0045 }
0046 
0047 template<>
0048 inline float convertByteOrder<AlphaF32Traits>(float value)
0049 {
0050     return qFromBigEndian((quint32)value);
0051 }
0052 
0053 template<>
0054 inline quint8 convertByteOrder<KoGrayU8Traits>(quint8 value)
0055 {
0056     return value;
0057 }
0058 
0059 template<>
0060 inline quint16 convertByteOrder<KoGrayU16Traits>(quint16 value)
0061 {
0062     return qFromBigEndian((quint16)value);
0063 }
0064 
0065 template<>
0066 inline quint32 convertByteOrder<KoGrayU32Traits>(quint32 value)
0067 {
0068     return qFromBigEndian((quint32)value);
0069 }
0070 
0071 template<>
0072 inline quint8 convertByteOrder<KoBgrU8Traits>(quint8 value)
0073 {
0074     return value;
0075 }
0076 
0077 template<>
0078 inline quint16 convertByteOrder<KoBgrU16Traits>(quint16 value)
0079 {
0080     return qFromBigEndian((quint16)value);
0081 }
0082 
0083 template<>
0084 inline quint32 convertByteOrder<KoBgrU32Traits>(quint32 value)
0085 {
0086     return qFromBigEndian((quint32)value);
0087 }
0088 
0089 template<>
0090 inline quint8 convertByteOrder<KoCmykU8Traits>(quint8 value)
0091 {
0092     return value;
0093 }
0094 
0095 template<>
0096 inline quint16 convertByteOrder<KoCmykU16Traits>(quint16 value)
0097 {
0098     return qFromBigEndian((quint16)value);
0099 }
0100 
0101 template<>
0102 inline float convertByteOrder<KoCmykF32Traits>(float value)
0103 {
0104     return qFromBigEndian((quint32)value);
0105 }
0106 
0107 template<>
0108 inline quint8 convertByteOrder<KoLabU8Traits>(quint8 value)
0109 {
0110     return value;
0111 }
0112 
0113 template<>
0114 inline quint16 convertByteOrder<KoLabU16Traits>(quint16 value)
0115 {
0116     return qFromBigEndian((quint16)value);
0117 }
0118 
0119 template<>
0120 inline float convertByteOrder<KoLabF32Traits>(float value)
0121 {
0122     return qFromBigEndian((quint32)value);
0123 }
0124 
0125 template<class Traits>
0126 inline quint8 truncateToOpacity(typename Traits::channels_type value);
0127 
0128 template<>
0129 inline quint8 truncateToOpacity<AlphaU8Traits>(typename AlphaU8Traits::channels_type value)
0130 {
0131     return value;
0132 }
0133 
0134 template<>
0135 inline quint8 truncateToOpacity<AlphaU16Traits>(typename AlphaU16Traits::channels_type value)
0136 {
0137     return value >> 8;
0138 }
0139 
0140 template<>
0141 inline quint8 truncateToOpacity<AlphaF32Traits>(typename AlphaF32Traits::channels_type value)
0142 {
0143     return static_cast<quint8>(value * 255U);
0144 }
0145 
0146 template<class Traits, psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0147 void readAlphaMaskPixel(const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
0148 {
0149     using channels_type = typename Traits::channels_type;
0150 
0151     const channels_type data = reinterpret_cast<const channels_type *>(channelBytes.first().constData())[col];
0152     if (byteOrder == psd_byte_order::psdBigEndian) {
0153         *dstPtr = truncateToOpacity<Traits>(convertByteOrder<Traits>(data));
0154     } else {
0155         *dstPtr = truncateToOpacity<Traits>(data);
0156     }
0157 }
0158 
0159 template<class Traits, psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0160 inline typename Traits::channels_type
0161 readChannelValue(const QMap<quint16, QByteArray> &channelBytes, quint16 channelId, int col, typename Traits::channels_type defaultValue)
0162 {
0163     using channels_type = typename Traits::channels_type;
0164 
0165     if (channelBytes.contains(channelId)) {
0166         const QByteArray &bytes = channelBytes[channelId];
0167         if (col < bytes.size()) {
0168             const channels_type data = reinterpret_cast<const channels_type *>(bytes.constData())[col];
0169             if (byteOrder == psd_byte_order::psdBigEndian) {
0170                 return convertByteOrder<Traits>(data);
0171             } else {
0172                 return data;
0173             }
0174         }
0175 
0176         dbgFile << "col index out of range channelId: " << channelId << " col:" << col;
0177     }
0178 
0179     return defaultValue;
0180 }
0181 
0182 template<class Traits, psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0183 void readGrayPixel(const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
0184 {
0185     using Pixel = typename Traits::Pixel;
0186     using channels_type = typename Traits::channels_type;
0187 
0188     const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
0189     Pixel *pixelPtr = reinterpret_cast<Pixel *>(dstPtr);
0190 
0191     pixelPtr->gray = readChannelValue<Traits, byteOrder>(channelBytes, 0, col, unitValue);
0192     pixelPtr->alpha = readChannelValue<Traits, byteOrder>(channelBytes, -1, col, unitValue);
0193 }
0194 
0195 template<class Traits, psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0196 void readRgbPixel(const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
0197 {
0198     using Pixel = typename Traits::Pixel;
0199     using channels_type = typename Traits::channels_type;
0200 
0201     const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
0202     Pixel *pixelPtr = reinterpret_cast<Pixel *>(dstPtr);
0203 
0204     pixelPtr->blue = readChannelValue<Traits, byteOrder>(channelBytes, 2, col, unitValue);
0205     pixelPtr->green = readChannelValue<Traits, byteOrder>(channelBytes, 1, col, unitValue);
0206     pixelPtr->red = readChannelValue<Traits, byteOrder>(channelBytes, 0, col, unitValue);
0207     pixelPtr->alpha = readChannelValue<Traits, byteOrder>(channelBytes, -1, col, unitValue);
0208 }
0209 
0210 template<class Traits, psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0211 void readCmykPixel(const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
0212 {
0213     using Pixel = typename Traits::Pixel;
0214     using channels_type = typename Traits::channels_type;
0215 
0216     const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
0217     Pixel *pixelPtr = reinterpret_cast<Pixel *>(dstPtr);
0218 
0219     pixelPtr->cyan = unitValue - readChannelValue<Traits, byteOrder>(channelBytes, 0, col, unitValue);
0220     pixelPtr->magenta = unitValue - readChannelValue<Traits, byteOrder>(channelBytes, 1, col, unitValue);
0221     pixelPtr->yellow = unitValue - readChannelValue<Traits, byteOrder>(channelBytes, 2, col, unitValue);
0222     pixelPtr->black = unitValue - readChannelValue<Traits, byteOrder>(channelBytes, 3, col, unitValue);
0223     pixelPtr->alpha = readChannelValue<Traits, byteOrder>(channelBytes, -1, col, unitValue);
0224 }
0225 
0226 template<class Traits, psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0227 void readLabPixel(const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
0228 {
0229     using Pixel = typename Traits::Pixel;
0230     using channels_type = typename Traits::channels_type;
0231 
0232     const channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
0233     Pixel *pixelPtr = reinterpret_cast<Pixel *>(dstPtr);
0234 
0235     pixelPtr->L = readChannelValue<Traits, byteOrder>(channelBytes, 0, col, unitValue);
0236     pixelPtr->a = readChannelValue<Traits, byteOrder>(channelBytes, 1, col, unitValue);
0237     pixelPtr->b = readChannelValue<Traits, byteOrder>(channelBytes, 2, col, unitValue);
0238     pixelPtr->alpha = readChannelValue<Traits, byteOrder>(channelBytes, -1, col, unitValue);
0239 }
0240 
0241 template<psd_byte_order byteOrder>
0242 void readRgbPixelCommon(int channelSize, const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
0243 {
0244     if (channelSize == 1) {
0245         readRgbPixel<KoBgrU8Traits, byteOrder>(channelBytes, col, dstPtr);
0246     } else if (channelSize == 2) {
0247         readRgbPixel<KoBgrU16Traits, byteOrder>(channelBytes, col, dstPtr);
0248     } else if (channelSize == 4) {
0249         readRgbPixel<KoBgrU16Traits, byteOrder>(channelBytes, col, dstPtr);
0250     }
0251 }
0252 
0253 template<psd_byte_order byteOrder>
0254 void readGrayPixelCommon(int channelSize, const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
0255 {
0256     if (channelSize == 1) {
0257         readGrayPixel<KoGrayU8Traits, byteOrder>(channelBytes, col, dstPtr);
0258     } else if (channelSize == 2) {
0259         readGrayPixel<KoGrayU16Traits, byteOrder>(channelBytes, col, dstPtr);
0260     } else if (channelSize == 4) {
0261         readGrayPixel<KoGrayU32Traits, byteOrder>(channelBytes, col, dstPtr);
0262     }
0263 }
0264 
0265 template<psd_byte_order byteOrder>
0266 void readCmykPixelCommon(int channelSize, const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
0267 {
0268     if (channelSize == 1) {
0269         readCmykPixel<KoCmykU8Traits, byteOrder>(channelBytes, col, dstPtr);
0270     } else if (channelSize == 2) {
0271         readCmykPixel<KoCmykU16Traits, byteOrder>(channelBytes, col, dstPtr);
0272     } else if (channelSize == 4) {
0273         readCmykPixel<KoCmykF32Traits, byteOrder>(channelBytes, col, dstPtr);
0274     }
0275 }
0276 
0277 template<psd_byte_order byteOrder>
0278 void readLabPixelCommon(int channelSize, const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
0279 {
0280     if (channelSize == 1) {
0281         readLabPixel<KoLabU8Traits, byteOrder>(channelBytes, col, dstPtr);
0282     } else if (channelSize == 2) {
0283         readLabPixel<KoLabU16Traits, byteOrder>(channelBytes, col, dstPtr);
0284     } else if (channelSize == 4) {
0285         readLabPixel<KoLabF32Traits, byteOrder>(channelBytes, col, dstPtr);
0286     }
0287 }
0288 
0289 template<psd_byte_order byteOrder>
0290 void readAlphaMaskPixelCommon(int channelSize, const QMap<quint16, QByteArray> &channelBytes, int col, quint8 *dstPtr)
0291 {
0292     if (channelSize == 1) {
0293         readAlphaMaskPixel<AlphaU8Traits, byteOrder>(channelBytes, col, dstPtr);
0294     } else if (channelSize == 2) {
0295         readAlphaMaskPixel<AlphaU16Traits, byteOrder>(channelBytes, col, dstPtr);
0296     } else if (channelSize == 4) {
0297         readAlphaMaskPixel<AlphaF32Traits, byteOrder>(channelBytes, col, dstPtr);
0298     }
0299 }
0300 
0301 QMap<quint16, QByteArray> fetchChannelsBytes(QIODevice &io, QVector<ChannelInfo *> channelInfoRecords, int row, int width, int channelSize, bool processMasks)
0302 {
0303     const int uncompressedLength = width * channelSize;
0304 
0305     QMap<quint16, QByteArray> channelBytes;
0306 
0307     Q_FOREACH (ChannelInfo *channelInfo, channelInfoRecords) {
0308         // user supplied masks are ignored here
0309         if (!processMasks && channelInfo->channelId < -1)
0310             continue;
0311 
0312         io.seek(channelInfo->channelDataStart + channelInfo->channelOffset);
0313 
0314         if (channelInfo->compressionType == psd_compression_type::Uncompressed) {
0315             channelBytes[channelInfo->channelId] = io.read(uncompressedLength);
0316             channelInfo->channelOffset += uncompressedLength;
0317         } else if (channelInfo->compressionType == psd_compression_type::RLE) {
0318             int rleLength = channelInfo->rleRowLengths[row];
0319             QByteArray compressedBytes = io.read(rleLength);
0320             QByteArray uncompressedBytes = Compression::uncompress(uncompressedLength, compressedBytes, channelInfo->compressionType);
0321             channelBytes.insert(channelInfo->channelId, uncompressedBytes);
0322             channelInfo->channelOffset += rleLength;
0323         } else {
0324             QString error = QString("Unsupported Compression mode: %1")
0325                                 .arg(static_cast<std::uint16_t>(channelInfo->compressionType));
0326             dbgFile << "ERROR: fetchChannelsBytes:" << error;
0327             throw KisAslReaderUtils::ASLParseException(error);
0328         }
0329     }
0330 
0331     return channelBytes;
0332 }
0333 
0334 using PixelFunc = std::function<void(int, const QMap<quint16, QByteArray> &, int, quint8 *)>;
0335 
0336 void readCommon(KisPaintDeviceSP dev,
0337                 QIODevice &io,
0338                 const QRect &layerRect,
0339                 QVector<ChannelInfo *> infoRecords,
0340                 int channelSize,
0341                 PixelFunc pixelFunc,
0342                 bool processMasks)
0343 {
0344     KisOffsetKeeper keeper(io);
0345 
0346     if (layerRect.isEmpty()) {
0347         dbgFile << "Empty layer!";
0348         return;
0349     }
0350 
0351     if (infoRecords.first()->compressionType == psd_compression_type::ZIP || infoRecords.first()->compressionType == psd_compression_type::ZIPWithPrediction) {
0352         const int numPixels = channelSize * layerRect.width() * layerRect.height();
0353 
0354         QMap<quint16, QByteArray> channelBytes;
0355 
0356         Q_FOREACH (ChannelInfo *info, infoRecords) {
0357             io.seek(info->channelDataStart);
0358             QByteArray compressedBytes = io.read(info->channelDataLength);
0359             QByteArray uncompressedBytes;
0360 
0361             uncompressedBytes = Compression::uncompress(numPixels, compressedBytes, infoRecords.first()->compressionType, layerRect.width(), channelSize * 8);
0362 
0363             if (uncompressedBytes.size() != numPixels) {
0364                 QString error = QString("Failed to unzip channel data: id = %1, compression = %2")
0365                                     .arg(info->channelId)
0366                                     .arg(static_cast<std::uint16_t>(info->compressionType));
0367                 dbgFile << "ERROR:" << error;
0368                 dbgFile << "      " << ppVar(info->channelId);
0369                 dbgFile << "      " << ppVar(info->channelDataStart);
0370                 dbgFile << "      " << ppVar(info->channelDataLength);
0371                 dbgFile << "      " << ppVar(info->compressionType);
0372                 throw KisAslReaderUtils::ASLParseException(error);
0373             }
0374 
0375             channelBytes.insert(info->channelId, uncompressedBytes);
0376         }
0377 
0378         KisSequentialIterator it(dev, layerRect);
0379         int col = 0;
0380         while (it.nextPixel()) {
0381             pixelFunc(channelSize, channelBytes, col, it.rawData());
0382             col++;
0383         }
0384 
0385     } else {
0386         KisHLineIteratorSP it = dev->createHLineIteratorNG(layerRect.left(), layerRect.top(), layerRect.width());
0387         for (int i = 0; i < layerRect.height(); i++) {
0388             QMap<quint16, QByteArray> channelBytes;
0389 
0390             channelBytes = fetchChannelsBytes(io, infoRecords, i, layerRect.width(), channelSize, processMasks);
0391 
0392             for (int col = 0; col < layerRect.width(); col++) {
0393                 pixelFunc(channelSize, channelBytes, col, it->rawData());
0394                 it->nextPixel();
0395             }
0396 
0397             /// don't write-access the row right after the
0398             /// the end of the read area
0399             if (i < layerRect.height() - 1) {
0400                 it->nextRow();
0401             }
0402         }
0403     }
0404 }
0405 
0406 template<psd_byte_order byteOrder>
0407 void readChannelsImpl(QIODevice &io,
0408                       KisPaintDeviceSP device,
0409                       psd_color_mode colorMode,
0410                       int channelSize,
0411                       const QRect &layerRect,
0412                       QVector<ChannelInfo *> infoRecords)
0413 {
0414     switch (colorMode) {
0415     case Grayscale:
0416         readCommon(device, io, layerRect, infoRecords, channelSize, &readGrayPixelCommon<byteOrder>, false);
0417         break;
0418     case RGB:
0419         readCommon(device, io, layerRect, infoRecords, channelSize, &readRgbPixelCommon<byteOrder>, false);
0420         break;
0421     case CMYK:
0422         readCommon(device, io, layerRect, infoRecords, channelSize, &readCmykPixelCommon<byteOrder>, false);
0423         break;
0424     case Lab:
0425         readCommon(device, io, layerRect, infoRecords, channelSize, &readLabPixelCommon<byteOrder>, false);
0426         break;
0427     case Bitmap:
0428     case Indexed:
0429     case MultiChannel:
0430     case DuoTone:
0431     case COLORMODE_UNKNOWN:
0432     default:
0433         QString error = QString("Unsupported color mode: %1").arg(colorMode);
0434         throw KisAslReaderUtils::ASLParseException(error);
0435     }
0436 }
0437 
0438 void readChannels(QIODevice &io,
0439                   KisPaintDeviceSP device,
0440                   psd_color_mode colorMode,
0441                   int channelSize,
0442                   const QRect &layerRect,
0443                   QVector<ChannelInfo *> infoRecords,
0444                   psd_byte_order byteOrder)
0445 {
0446     switch (byteOrder) {
0447     case psd_byte_order::psdLittleEndian:
0448         return readChannelsImpl<psd_byte_order::psdLittleEndian>(io, device, colorMode, channelSize, layerRect, infoRecords);
0449     default:
0450         return readChannelsImpl<psd_byte_order::psdBigEndian>(io, device, colorMode, channelSize, layerRect, infoRecords);
0451     }
0452 }
0453 
0454 template<psd_byte_order byteOrder>
0455 void readAlphaMaskChannelsImpl(QIODevice &io, KisPaintDeviceSP device, int channelSize, const QRect &layerRect, QVector<ChannelInfo *> infoRecords)
0456 {
0457     KIS_SAFE_ASSERT_RECOVER_RETURN(infoRecords.size() == 1);
0458     readCommon(device, io, layerRect, infoRecords, channelSize, &readAlphaMaskPixelCommon<byteOrder>, true);
0459 }
0460 
0461 void readAlphaMaskChannels(QIODevice &io,
0462                            KisPaintDeviceSP device,
0463                            int channelSize,
0464                            const QRect &layerRect,
0465                            QVector<ChannelInfo *> infoRecords,
0466                            psd_byte_order byteOrder)
0467 {
0468     switch (byteOrder) {
0469     case psd_byte_order::psdLittleEndian:
0470         return readAlphaMaskChannelsImpl<psd_byte_order::psdLittleEndian>(io, device, channelSize, layerRect, infoRecords);
0471     default:
0472         return readAlphaMaskChannelsImpl<psd_byte_order::psdBigEndian>(io, device, channelSize, layerRect, infoRecords);
0473     }
0474 }
0475 
0476 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0477 void writeChannelDataRLEImpl(QIODevice &io,
0478                              const quint8 *plane,
0479                              const int channelSize,
0480                              const QRect &rc,
0481                              const qint64 sizeFieldOffset,
0482                              const qint64 rleBlockOffset,
0483                              const bool writeCompressionType)
0484 {
0485     using Pusher = KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder>;
0486     QScopedPointer<Pusher> channelBlockSizeExternalTag;
0487     if (sizeFieldOffset >= 0) {
0488         channelBlockSizeExternalTag.reset(new Pusher(io, 0, sizeFieldOffset));
0489     }
0490 
0491     if (writeCompressionType) {
0492         SAFE_WRITE_EX(byteOrder, io, static_cast<quint16>(psd_compression_type::RLE));
0493     }
0494 
0495     const bool externalRleBlock = rleBlockOffset >= 0;
0496 
0497     // the start of RLE sizes block
0498     const qint64 channelRLESizePos = externalRleBlock ? rleBlockOffset : io.pos();
0499 
0500     {
0501         QScopedPointer<KisOffsetKeeper> rleOffsetKeeper;
0502 
0503         if (externalRleBlock) {
0504             rleOffsetKeeper.reset(new KisOffsetKeeper(io));
0505             io.seek(rleBlockOffset);
0506         }
0507 
0508         // write zero's for the channel lengths block
0509         for (int i = 0; i < rc.height(); ++i) {
0510             // XXX: choose size for PSB!
0511             const quint16 fakeRLEBLockSize = 0;
0512             SAFE_WRITE_EX(byteOrder, io, fakeRLEBLockSize);
0513         }
0514     }
0515 
0516     const int stride = channelSize * rc.width();
0517     for (qint32 row = 0; row < rc.height(); ++row) {
0518         QByteArray uncompressed = QByteArray::fromRawData((const char *)plane + row * stride, stride);
0519         QByteArray compressed = Compression::compress(uncompressed, psd_compression_type::RLE);
0520 
0521         KisAslWriterUtils::OffsetStreamPusher<quint16, byteOrder> rleExternalTag(io, 0, channelRLESizePos + row * static_cast<qint64>(sizeof(quint16)));
0522 
0523         if (io.write(compressed) != compressed.size()) {
0524             throw KisAslWriterUtils::ASLWriteException("Failed to write image data");
0525         }
0526     }
0527 }
0528 
0529 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0530 void writeChannelDataZIPImpl(QIODevice &io,
0531                              const quint8 *plane,
0532                              const int channelSize,
0533                              const QRect &rc,
0534                              const qint64 sizeFieldOffset,
0535                              const bool writeCompressionType)
0536 {
0537     using Pusher = KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder>;
0538     QScopedPointer<Pusher> channelBlockSizeExternalTag;
0539     if (sizeFieldOffset >= 0) {
0540         channelBlockSizeExternalTag.reset(new Pusher(io, 0, sizeFieldOffset));
0541     }
0542 
0543     if (writeCompressionType) {
0544         SAFE_WRITE_EX(byteOrder, io, static_cast<quint16>(psd_compression_type::ZIP));
0545     }
0546 
0547     QByteArray uncompressed(reinterpret_cast<const char *>(plane), rc.width() * rc.height() * channelSize);
0548     QByteArray compressed(Compression::compress(uncompressed, psd_compression_type::ZIP));
0549 
0550     if (compressed.size() == 0 || io.write(compressed) != compressed.size()) {
0551         throw KisAslWriterUtils::ASLWriteException("Failed to write image data");
0552     }
0553 }
0554 
0555 void writeChannelDataRLE(QIODevice &io,
0556                          const quint8 *plane,
0557                          const int channelSize,
0558                          const QRect &rc,
0559                          const qint64 sizeFieldOffset,
0560                          const qint64 rleBlockOffset,
0561                          const bool writeCompressionType,
0562                          psd_byte_order byteOrder)
0563 {
0564     switch (byteOrder) {
0565     case psd_byte_order::psdLittleEndian:
0566         return writeChannelDataRLEImpl<psd_byte_order::psdLittleEndian>(io, plane, channelSize, rc, sizeFieldOffset, rleBlockOffset, writeCompressionType);
0567     default:
0568         return writeChannelDataRLEImpl(io, plane, channelSize, rc, sizeFieldOffset, rleBlockOffset, writeCompressionType);
0569     }
0570 }
0571 
0572 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0573 inline void preparePixelForWrite(quint8 *dataPlane, int numPixels, int channelSize, int channelId, psd_color_mode colorMode)
0574 {
0575     // if the bitdepth > 8, place the bytes in the right order
0576     // if cmyk, invert the pixel value
0577     if (channelSize == 1) {
0578         if (channelId >= 0 && (colorMode == CMYK || colorMode == CMYK64)) {
0579             for (int i = 0; i < numPixels; ++i) {
0580                 dataPlane[i] = 255 - dataPlane[i];
0581             }
0582         }
0583     } else if (channelSize == 2) {
0584         quint16 val;
0585         for (int i = 0; i < numPixels; ++i) {
0586             quint16 *pixelPtr = reinterpret_cast<quint16 *>(dataPlane) + i;
0587 
0588             val = *pixelPtr;
0589             if (byteOrder == psd_byte_order::psdBigEndian)
0590                 val = qFromBigEndian(val);
0591             if (channelId >= 0 && (colorMode == CMYK || colorMode == CMYK64)) {
0592                 val = quint16_MAX - val;
0593             }
0594             *pixelPtr = val;
0595         }
0596     } else if (channelSize == 4) {
0597         quint32 val;
0598         for (int i = 0; i < numPixels; ++i) {
0599             quint32 *pixelPtr = reinterpret_cast<quint32 *>(dataPlane) + i;
0600 
0601             val = *pixelPtr;
0602             if (byteOrder == psd_byte_order::psdBigEndian)
0603                 val = qFromBigEndian(val);
0604             if (channelId >= 0 && (colorMode == CMYK || colorMode == CMYK64)) {
0605                 val = std::numeric_limits<quint32>::max() - val;
0606             }
0607             *pixelPtr = val;
0608         }
0609     }
0610 }
0611 
0612 template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
0613 void writePixelDataCommonImpl(QIODevice &io,
0614                               KisPaintDeviceSP dev,
0615                               const QRect &rc,
0616                               psd_color_mode colorMode,
0617                               int channelSize,
0618                               bool alphaFirst,
0619                               const bool writeCompressionType,
0620                               QVector<ChannelWritingInfo> &writingInfoList,
0621                               psd_compression_type compressionType)
0622 {
0623     // Empty rects must be processed separately on a higher level!
0624     KIS_ASSERT_RECOVER_RETURN(!rc.isEmpty());
0625 
0626     QVector<quint8 *> tmp = dev->readPlanarBytes(rc.x() - dev->x(), rc.y() - dev->y(), rc.width(), rc.height());
0627     const KoColorSpace *colorSpace = dev->colorSpace();
0628 
0629     QVector<quint8 *> planes;
0630 
0631     { // prepare 'planes' array
0632 
0633         quint8 *alphaPlanePtr = 0;
0634 
0635         QList<KoChannelInfo *> origChannels = colorSpace->channels();
0636         Q_FOREACH (KoChannelInfo *ch, KoChannelInfo::displayOrderSorted(origChannels)) {
0637             int channelIndex = KoChannelInfo::displayPositionToChannelIndex(ch->displayPosition(), origChannels);
0638 
0639             quint8 *holder = 0;
0640             std::swap(holder, tmp[channelIndex]);
0641 
0642             if (ch->channelType() == KoChannelInfo::ALPHA) {
0643                 std::swap(holder, alphaPlanePtr);
0644             } else {
0645                 planes.append(holder);
0646             }
0647         }
0648 
0649         if (alphaPlanePtr) {
0650             if (alphaFirst) {
0651                 planes.insert(0, alphaPlanePtr);
0652                 KIS_ASSERT_RECOVER_NOOP(writingInfoList.first().channelId == -1);
0653             } else {
0654                 planes.append(alphaPlanePtr);
0655                 KIS_ASSERT_RECOVER_NOOP((writingInfoList.size() == planes.size() - 1) || (writingInfoList.last().channelId == -1));
0656             }
0657         }
0658 
0659         // now planes are holding pointers to quint8 arrays
0660         tmp.clear();
0661     }
0662 
0663     KIS_ASSERT_RECOVER_RETURN(planes.size() >= writingInfoList.size());
0664 
0665     const int numPixels = rc.width() * rc.height();
0666 
0667     // write down the planes
0668 
0669     try {
0670         for (int i = 0; i < writingInfoList.size(); i++) {
0671             const ChannelWritingInfo &info = writingInfoList[i];
0672 
0673             dbgFile << "\tWriting channel" << i << "psd channel id" << info.channelId;
0674 
0675             // WARNING: Pixel data is ALWAYS in big endian!!!
0676             preparePixelForWrite<psd_byte_order::psdBigEndian>(planes[i], numPixels, channelSize, info.channelId, colorMode);
0677 
0678             dbgFile << "\t\tchannel start" << ppVar(io.pos()) << ", compression type" << compressionType;
0679 
0680             switch (compressionType) {
0681             case psd_compression_type::ZIP:
0682             case psd_compression_type::ZIPWithPrediction: {
0683                 writeChannelDataZIPImpl<byteOrder>(io, planes[i], channelSize, rc, info.sizeFieldOffset, writeCompressionType);
0684                 break;
0685             }
0686             case psd_compression_type::RLE:
0687             default: {
0688                 writeChannelDataRLEImpl<byteOrder>(io, planes[i], channelSize, rc, info.sizeFieldOffset, info.rleBlockOffset, writeCompressionType);
0689                 break;
0690             }
0691             }
0692         }
0693 
0694     } catch (KisAslWriterUtils::ASLWriteException &e) {
0695         Q_FOREACH (quint8 *plane, planes) {
0696             delete[] plane;
0697         }
0698         planes.clear();
0699 
0700         throw KisAslWriterUtils::ASLWriteException(PREPEND_METHOD(e.what()));
0701     }
0702 
0703     Q_FOREACH (quint8 *plane, planes) {
0704         delete[] plane;
0705     }
0706     planes.clear();
0707 }
0708 
0709 void writePixelDataCommon(QIODevice &io,
0710                           KisPaintDeviceSP dev,
0711                           const QRect &rc,
0712                           psd_color_mode colorMode,
0713                           int channelSize,
0714                           bool alphaFirst,
0715                           const bool writeCompressionType,
0716                           QVector<ChannelWritingInfo> &writingInfoList,
0717                           psd_compression_type compressionType,
0718                           psd_byte_order byteOrder)
0719 {
0720     switch (byteOrder) {
0721     case psd_byte_order::psdLittleEndian:
0722         return writePixelDataCommonImpl<psd_byte_order::psdLittleEndian>(io,
0723                                                                          dev,
0724                                                                          rc,
0725                                                                          colorMode,
0726                                                                          channelSize,
0727                                                                          alphaFirst,
0728                                                                          writeCompressionType,
0729                                                                          writingInfoList,
0730                                                                          compressionType);
0731     default:
0732         return writePixelDataCommonImpl(io, dev, rc, colorMode, channelSize, alphaFirst, writeCompressionType, writingInfoList, compressionType);
0733     }
0734 }
0735 }