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 }