Commit 1f8f6593 authored by Amy spark's avatar Amy spark 👉 Committed by Halla Rempt
Browse files

TIFF: add support for writing Photoshop layers and metadata

parent 3a61a882
......@@ -155,6 +155,9 @@ void PsdAdditionalLayerInfoBlock::readImpl(QIODevice &io)
unicodeLayerName = readUnicodeString<byteOrder>(io);
dbgFile << "unicodeLayerName" << unicodeLayerName;
} else if (key == "lyid") {
quint32 id;
psdread<byteOrder>(io, id);
dbgFile << "layer ID:" << id;
} else if (key == "lfx2" || key == "lfxs") {
// lfxs is a special variant of layer styles for group layers
layerStyleXml = KisAslReader::readLfx2PsdSection(io, byteOrder);
......@@ -343,7 +346,8 @@ void PsdAdditionalLayerInfoBlock::writePattBlockExImpl(QIODevice &io, const QDom
{
KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
KisAslWriterUtils::writeFixedString<byteOrder>("Patt", io);
KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> pattSizeTag(io, 2);
const quint32 padding = m_header.tiffStyleLayerBlock ? 4 : 2;
KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> pattSizeTag(io, padding);
try {
KisAslPatternsWriter writer(patternsXmlDoc, io, byteOrder);
......
......@@ -356,37 +356,41 @@ bool PSDLayerRecord::readImpl(QIODevice &io)
return false;
}
// dbgFile << "blending block data length" << blendingDataLength << ", pos" << io.pos();
// XXX: Check what endianness this data has
blendingRanges.data = io.read(blendingDataLength);
if ((quint32)blendingRanges.data.size() != blendingDataLength) {
error = QString("Got %1 bytes for the blending range block, needed %2").arg(blendingRanges.data.size(), blendingDataLength);
}
/*
// XXX: reading this block correctly failed, I have more channel ranges than I'd expected.
if (!psdread(io, blendingRanges.blackValues[0]) ||
!psdread(io, blendingRanges.blackValues[1]) ||
!psdread(io, blendingRanges.whiteValues[0]) ||
!psdread(io, blendingRanges.whiteValues[1]) ||
!psdread(io, blendingRanges.compositeGrayBlendDestinationRange)) {
error = "Could not read blending black/white values";
return false;
}
for (int i = 0; i < nChannels; ++i) {
quint32 src;
quint32 dst;
if (!psdread(io, src) || !psdread(io, dst)) {
error = QString("could not read src/dst range for channel %1").arg(i);
quint32 blendingNchannels = blendingDataLength > 0 ? (blendingDataLength - 8) / 4 / 2 : 0;
dbgFile << "\tNumber of blending channels:" << blendingNchannels;
if (blendingNchannels > 0) {
if (!psdread<byteOrder>(io, blendingRanges.compositeGrayRange.first.blackValues[0])
|| !psdread<byteOrder>(io, blendingRanges.compositeGrayRange.first.blackValues[1])
|| !psdread<byteOrder>(io, blendingRanges.compositeGrayRange.first.whiteValues[0])
|| !psdread<byteOrder>(io, blendingRanges.compositeGrayRange.first.whiteValues[1])
|| !psdread<byteOrder>(io, blendingRanges.compositeGrayRange.second.blackValues[0])
|| !psdread<byteOrder>(io, blendingRanges.compositeGrayRange.second.blackValues[1])
|| !psdread<byteOrder>(io, blendingRanges.compositeGrayRange.second.whiteValues[0])
|| !psdread<byteOrder>(io, blendingRanges.compositeGrayRange.second.whiteValues[1])) {
error = "Could not read blending black/white values";
return false;
}
dbgFile << "\tread range " << src << "to" << dst << "for channel" << i;
blendingRanges.sourceDestinationRanges << QPair<quint32, quint32>(src, dst);
dbgFile << "\tBlending ranges:";
dbgFile << "\t\tcomposite gray (source) :" << blendingRanges.compositeGrayRange.first;
dbgFile << "\t\tcomposite gray (dest):" << blendingRanges.compositeGrayRange.second;
for (quint32 i = 0; i < blendingNchannels; ++i) {
LayerBlendingRanges::LayerBlendingRange src;
LayerBlendingRanges::LayerBlendingRange dst;
if (!psdread<byteOrder>(io, src.blackValues[0]) || !psdread<byteOrder>(io, src.blackValues[1]) || !psdread<byteOrder>(io, src.whiteValues[0])
|| !psdread<byteOrder>(io, src.whiteValues[1]) || !psdread<byteOrder>(io, dst.blackValues[0]) || !psdread<byteOrder>(io, dst.blackValues[1])
|| !psdread<byteOrder>(io, dst.whiteValues[0]) || !psdread<byteOrder>(io, dst.whiteValues[1])) {
error = QString("could not read src/dst range for channel %1").arg(i);
return false;
}
dbgFile << "\t\tread range " << src << "to" << dst << "for channel" << i;
blendingRanges.sourceDestinationRanges << qMakePair(src, dst);
}
}
*/
dbgFile << "\tGoing to read layer name at" << io.pos();
quint8 layerNameLength;
if (!psdread<byteOrder>(io, layerNameLength)) {
......@@ -402,6 +406,8 @@ bool PSDLayerRecord::readImpl(QIODevice &io)
layerName = io.read(layerNameLength);
dbgFile << "\tlayer name" << layerName << io.pos();
dbgFile << "\tAbout to read additional info blocks at" << io.pos();
if (!infoBlocks.read(io)) {
error = infoBlocks.error;
return false;
......@@ -460,23 +466,9 @@ void PSDLayerRecord::writeImpl(QIODevice &io,
Q_ASSERT(nChannels > 0);
try {
QBuffer buf_little_endian;
buf_little_endian.open(QIODevice::WriteOnly);
QBuffer buf_big_endian;
buf_big_endian.open(QIODevice::WriteOnly);
QBuffer buf;
buf.open(QIODevice::WriteOnly);
const QRect layerRect(left, top, right - left, bottom - top);
KisAslWriterUtils::writeRect<psd_byte_order::psdBigEndian>(layerRect, buf_big_endian);
KisAslWriterUtils::writeRect<psd_byte_order::psdLittleEndian>(layerRect, buf_little_endian);
KisAslWriterUtils::writeRect<byteOrder>(layerRect, buf);
KisAslWriterUtils::writeRect<byteOrder>(layerRect, io);
if (byteOrder == psd_byte_order::psdLittleEndian) {
dbgFile << "Testing LE equality of QRect: " << std::equal(buf_little_endian.data().begin(), buf_little_endian.data().end(), buf.data().begin());
} else {
dbgFile << "Testing BE equality of QRect: " << std::equal(buf_big_endian.data().begin(), buf_big_endian.data().end(), buf.data().begin());
{
const QRect layerRect(left, top, right - left, bottom - top);
KisAslWriterUtils::writeRect<byteOrder>(layerRect, io);
}
{
......@@ -519,8 +511,9 @@ void PSDLayerRecord::writeImpl(QIODevice &io,
flags |= 1;
if (!visible)
flags |= 2;
flags |= (1 << 3);
if (irrelevant) {
flags |= (1 << 3) | (1 << 4);
flags |= (1 << 4);
}
SAFE_WRITE_EX(byteOrder, io, flags);
......@@ -606,6 +599,7 @@ KisPaintDeviceSP PSDLayerRecord::convertMaskDeviceIfNeeded(KisPaintDeviceSP dev)
return result;
}
template<psd_byte_order byteOrder>
void PSDLayerRecord::writeTransparencyMaskPixelData(QIODevice &io)
{
if (m_onlyTransparencyMask) {
......@@ -620,19 +614,28 @@ void PSDLayerRecord::writeTransparencyMaskPixelData(QIODevice &io)
m_onlyTransparencyMaskRect,
m_transparencyMaskSizeOffset,
-1,
true);
true,
byteOrder);
}
}
void PSDLayerRecord::writePixelData(QIODevice &io)
{
try {
writePixelDataImpl(io);
switch (m_header.byteOrder) {
case psd_byte_order::psdLittleEndian:
writePixelDataImpl<psd_byte_order::psdLittleEndian>(io);
break;
default:
writePixelDataImpl(io);
break;
}
} catch (KisAslWriterUtils::ASLWriteException &e) {
throw KisAslWriterUtils::ASLWriteException(PREPEND_METHOD(e.what()));
}
}
template<psd_byte_order byteOrder>
void PSDLayerRecord::writePixelDataImpl(QIODevice &io)
{
dbgFile << "writing pixel data for layer" << layerName << "at" << io.pos();
......@@ -645,11 +648,11 @@ void PSDLayerRecord::writePixelDataImpl(QIODevice &io)
for (int i = 0; i < nChannels; i++) {
const ChannelInfo *channelInfo = channelInfoRecords[i];
KisAslWriterUtils::OffsetStreamPusher<quint32, psd_byte_order::psdBigEndian> channelBlockSizeExternalTag(io, 0, channelInfo->channelInfoPosition);
SAFE_WRITE_EX(psd_byte_order::psdBigEndian, io, (quint16)Compression::Uncompressed);
KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> channelBlockSizeExternalTag(io, 0, channelInfo->channelInfoPosition);
SAFE_WRITE_EX(byteOrder, io, (quint16)Compression::Uncompressed);
}
writeTransparencyMaskPixelData(io);
writeTransparencyMaskPixelData<byteOrder>(io);
return;
}
......@@ -665,8 +668,8 @@ void PSDLayerRecord::writePixelDataImpl(QIODevice &io)
writingInfoList << PsdPixelUtils::ChannelWritingInfo(channelInfo->channelId, channelInfo->channelInfoPosition);
}
PsdPixelUtils::writePixelDataCommon(io, dev, rc, colorMode, channelSize, true, true, writingInfoList);
writeTransparencyMaskPixelData(io);
PsdPixelUtils::writePixelDataCommon(io, dev, rc, colorMode, channelSize, true, true, writingInfoList, byteOrder);
writeTransparencyMaskPixelData<byteOrder>(io);
}
bool PSDLayerRecord::valid()
......
......@@ -131,12 +131,15 @@ public:
LayerMaskData layerMask;
struct LayerBlendingRanges {
struct LayerBlendingRange {
std::array<quint8, 2> blackValues;
std::array<quint8, 2> whiteValues;
};
QByteArray data;
quint8 blackValues[2];
quint8 whiteValues[2];
quint32 compositeGrayBlendDestinationRange;
QVector<QPair<quint32, quint32>> sourceDestinationRanges;
QPair<LayerBlendingRange, LayerBlendingRange> compositeGrayRange;
QVector<QPair<LayerBlendingRange, LayerBlendingRange>> sourceDestinationRanges;
};
LayerBlendingRanges blendingRanges;
......@@ -158,8 +161,10 @@ private:
const QDomDocument &stylesXmlDoc,
bool useLfxsLayerStyleFormat);
template<psd_byte_order = psd_byte_order::psdBigEndian>
void writeTransparencyMaskPixelData(QIODevice &io);
template<psd_byte_order = psd_byte_order::psdBigEndian>
void writePixelDataImpl(QIODevice &io);
KisPaintDeviceSP convertMaskDeviceIfNeeded(KisPaintDeviceSP dev);
......@@ -176,4 +181,9 @@ private:
KRITAPSD_EXPORT QDebug operator<<(QDebug dbg, const PSDLayerRecord &layer);
KRITAPSD_EXPORT QDebug operator<<(QDebug dbg, const ChannelInfo &layer);
inline QDebug &operator<<(QDebug dbg, const PSDLayerRecord::LayerBlendingRanges::LayerBlendingRange &data)
{
return dbg << data.blackValues[0] << data.blackValues[1] << data.whiteValues[0] << data.whiteValues[1];
}
#endif // PSD_LAYER_RECORD_H
......@@ -6,6 +6,7 @@
*/
#include "psd_layer_section.h"
#include <QBuffer>
#include <QIODevice>
#include <KoColor.h>
......@@ -487,13 +488,17 @@ bool PSDLayerMaskSection::write(QIODevice &io, KisNodeSP rootLayer)
bool retval = true;
try {
switch (m_header.byteOrder) {
case psd_byte_order::psdLittleEndian:
writeImpl<psd_byte_order::psdLittleEndian>(io, rootLayer);
break;
default:
writeImpl(io, rootLayer);
break;
if (m_header.tiffStyleLayerBlock) {
switch (m_header.byteOrder) {
case psd_byte_order::psdLittleEndian:
writeTiffImpl<psd_byte_order::psdLittleEndian>(io, rootLayer);
break;
default:
writeTiffImpl(io, rootLayer);
break;
}
} else {
writePsdImpl(io, rootLayer);
}
} catch (KisAslWriterUtils::ASLWriteException &e) {
error = PREPEND_METHOD(e.what());
......@@ -503,8 +508,7 @@ bool PSDLayerMaskSection::write(QIODevice &io, KisNodeSP rootLayer)
return retval;
}
template<psd_byte_order byteOrder>
void PSDLayerMaskSection::writeImpl(QIODevice &io, KisNodeSP rootLayer)
void PSDLayerMaskSection::writePsdImpl(QIODevice &io, KisNodeSP rootLayer)
{
dbgFile << "Writing layer layer section";
......@@ -518,16 +522,16 @@ void PSDLayerMaskSection::writeImpl(QIODevice &io, KisNodeSP rootLayer)
}
{
KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> layerAndMaskSectionSizeTag(io, 2);
KisAslWriterUtils::OffsetStreamPusher<quint32, psd_byte_order::psdBigEndian> layerAndMaskSectionSizeTag(io, 2);
QDomDocument mergedPatternsXmlDoc;
{
KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> layerInfoSizeTag(io, 4);
KisAslWriterUtils::OffsetStreamPusher<quint32, psd_byte_order::psdBigEndian> layerInfoSizeTag(io, 2);
{
// number of layers (negative, because krita always has alpha)
const qint16 layersSize = static_cast<qint16>(-nodes.size());
SAFE_WRITE_EX(byteOrder, io, layersSize);
SAFE_WRITE_EX(psd_byte_order::psdBigEndian, io, layersSize);
dbgFile << "Number of layers" << layersSize << "at" << io.pos();
}
......@@ -635,8 +639,6 @@ void PSDLayerMaskSection::writeImpl(QIODevice &io, KisNodeSP rootLayer)
// Now save the pixel data
for (PSDLayerRecord *layerRecord : layers) {
// XXX: endianness?
// Make consistent with PSDLayerRecord::readPixelData
layerRecord->writePixelData(io);
}
}
......@@ -644,9 +646,164 @@ void PSDLayerMaskSection::writeImpl(QIODevice &io, KisNodeSP rootLayer)
{
// write the global layer mask info -- which is empty
const quint32 globalMaskSize = 0;
SAFE_WRITE_EX(byteOrder, io, globalMaskSize);
SAFE_WRITE_EX(psd_byte_order::psdBigEndian, io, globalMaskSize);
}
globalInfoSection.writePattBlockEx(io, mergedPatternsXmlDoc);
}
}
template<psd_byte_order byteOrder>
void PSDLayerMaskSection::writeTiffImpl(QIODevice &io, KisNodeSP rootLayer)
{
dbgFile << "(TIFF) Writing layer section";
// Build the whole layer structure
QList<FlattenedNode> nodes;
addBackgroundIfNeeded(rootLayer, nodes);
flattenNodes(rootLayer, nodes);
if (nodes.isEmpty()) {
throw KisAslWriterUtils::ASLWriteException("Could not find paint layers to save");
}
{
QDomDocument mergedPatternsXmlDoc;
{
KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
KisAslWriterUtils::writeFixedString<byteOrder>("Layr", io);
KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> layerAndMaskSectionSizeTag(io, 4);
// number of layers (negative, because krita always has alpha)
const qint16 layersSize = nodes.size();
SAFE_WRITE_EX(byteOrder, io, layersSize);
dbgFile << "Number of layers" << layersSize << "at" << io.pos();
// Layer records section
for (const FlattenedNode &item : nodes) {
KisNodeSP node = item.node;
PSDLayerRecord *layerRecord = new PSDLayerRecord(m_header);
layers.append(layerRecord);
const QRect maskRect;
const bool nodeVisible = node->visible();
const KoColorSpace *colorSpace = node->colorSpace();
const quint8 nodeOpacity = node->opacity();
const quint8 nodeClipping = 0;
const KisPaintLayer *paintLayer = qobject_cast<KisPaintLayer *>(node.data());
const bool alphaLocked = (paintLayer && paintLayer->alphaLocked());
const QString nodeCompositeOp = node->compositeOpId();
const KisGroupLayer *groupLayer = qobject_cast<KisGroupLayer *>(node.data());
const bool nodeIsPassThrough = groupLayer && groupLayer->passThroughMode();
QDomDocument stylesXmlDoc = fetchLayerStyleXmlData(node);
if (mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
mergedPatternsXmlDoc = stylesXmlDoc;
} else if (!mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
mergePatternsXMLSection(stylesXmlDoc, mergedPatternsXmlDoc);
}
bool nodeIrrelevant = false;
QString nodeName;
KisPaintDeviceSP layerContentDevice;
psd_section_type sectionType;
if (item.type == FlattenedNode::RASTER_LAYER) {
nodeIrrelevant = false;
nodeName = node->name();
layerContentDevice = node->projection();
sectionType = psd_other;
} else {
nodeIrrelevant = true;
nodeName = item.type == FlattenedNode::SECTION_DIVIDER ? QString("</Layer group>") : node->name();
layerContentDevice = 0;
sectionType = item.type == FlattenedNode::SECTION_DIVIDER ? psd_bounding_divider
: item.type == FlattenedNode::FOLDER_OPEN ? psd_open_folder
: psd_closed_folder;
}
// === no access to node anymore
QRect layerRect;
if (layerContentDevice) {
QRect rc = layerContentDevice->exactBounds();
rc = rc.normalized();
// keep to the max of photoshop's capabilities
// XXX: update this to PSB
if (rc.width() > 30000)
rc.setWidth(30000);
if (rc.height() > 30000)
rc.setHeight(30000);
layerRect = rc;
}
layerRecord->top = layerRect.y();
layerRecord->left = layerRect.x();
layerRecord->bottom = layerRect.y() + layerRect.height();
layerRecord->right = layerRect.x() + layerRect.width();
// colors + alpha channel
// note: transparency mask not included
layerRecord->nChannels = static_cast<quint16>(colorSpace->colorChannelCount() + 1);
ChannelInfo *info = new ChannelInfo;
info->channelId = -1; // For the alpha channel, which we always have in Krita, and should be saved first in
layerRecord->channelInfoRecords << info;
// the rest is in display order: rgb, cmyk, lab...
for (quint32 i = 0; i < colorSpace->colorChannelCount(); ++i) {
info = new ChannelInfo;
info->channelId = static_cast<qint16>(i); // 0 for red, 1 = green, etc
layerRecord->channelInfoRecords << info;
}
layerRecord->blendModeKey = composite_op_to_psd_blendmode(nodeCompositeOp);
layerRecord->isPassThrough = nodeIsPassThrough;
layerRecord->opacity = nodeOpacity;
layerRecord->clipping = nodeClipping;
layerRecord->transparencyProtected = alphaLocked;
layerRecord->visible = nodeVisible;
layerRecord->irrelevant = nodeIrrelevant;
layerRecord->layerName = nodeName.isEmpty() ? i18n("Unnamed Layer") : nodeName;
layerRecord->write(io, layerContentDevice, nullptr, QRect(), sectionType, stylesXmlDoc, node->inherits("KisGroupLayer"));
}
dbgFile << "start writing layer pixel data" << io.pos();
// Now save the pixel data
for (PSDLayerRecord *layerRecord : layers) {
layerRecord->writePixelData(io);
}
}
// {
// // write the global layer mask info -- which is NOT empty but fixed
// KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
// KisAslWriterUtils::writeFixedString<byteOrder>("LMsk", io);
// KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> layerAndMaskSectionSizeTag(io, 4);
// // https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_22664
// psdwrite<byteOrder>(io, quint16(0)); // CS: RGB
// psdwrite<byteOrder>(io, quint16(65535)); // Pure red verification
// psdwrite<byteOrder>(io, quint16(0));
// psdwrite<byteOrder>(io, quint16(0));
// psdwrite<byteOrder>(io, quint16(0));
// psdwrite<byteOrder>(io, quint16(50)); // opacity
// psdwrite<byteOrder>(io, quint16(128)); // kind
// }
globalInfoSection.writePattBlockEx(io, mergedPatternsXmlDoc);
}
}
......@@ -59,8 +59,9 @@ private:
bool readTiffImpl(QIODevice &io);
template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
bool readGlobalMask(QIODevice &io);
void writePsdImpl(QIODevice &io, KisNodeSP rootLayer);
template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
void writeImpl(QIODevice &io, KisNodeSP rootLayer);
void writeTiffImpl(QIODevice &io, KisNodeSP rootLayer);
private:
const PSDHeader m_header;
......
......@@ -569,22 +569,23 @@ void readAlphaMaskChannels(QIODevice &io,
}
}
void writeChannelDataRLE(QIODevice &io,
const quint8 *plane,
const int channelSize,
const QRect &rc,
const qint64 sizeFieldOffset,
const qint64 rleBlockOffset,
const bool writeCompressionType)
{
using Pusher = KisAslWriterUtils::OffsetStreamPusher<quint32, psd_byte_order::psdBigEndian>;
template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
void writeChannelDataRLEImpl(QIODevice &io,
const quint8 *plane,
const int channelSize,
const QRect &rc,
const qint64 sizeFieldOffset,
const qint64 rleBlockOffset,
const bool writeCompressionType)
{
using Pusher = KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder>;
QScopedPointer<Pusher> channelBlockSizeExternalTag;
if (sizeFieldOffset >= 0) {
channelBlockSizeExternalTag.reset(new Pusher(io, 0, sizeFieldOffset));
}
if (writeCompressionType) {
SAFE_WRITE_EX(psd_byte_order::psdBigEndian, io, (quint16)Compression::RLE);
SAFE_WRITE_EX(byteOrder, io, (quint16)Compression::RLE);
}
const bool externalRleBlock = rleBlockOffset >= 0;
......@@ -604,7 +605,7 @@ void writeChannelDataRLE(QIODevice &io,
for (int i = 0; i < rc.height(); ++i) {
// XXX: choose size for PSB!
const quint16 fakeRLEBLockSize = 0;
SAFE_WRITE_EX(psd_byte_order::psdBigEndian, io, fakeRLEBLockSize);
SAFE_WRITE_EX(byteOrder, io, fakeRLEBLockSize);
}
}
......@@ -613,10 +614,7 @@ void writeChannelDataRLE(QIODevice &io,
QByteArray uncompressed = QByteArray::fromRawData((const char *)plane + row * stride, stride);
QByteArray compressed = Compression::compress(uncompressed, Compression::RLE);
KisAslWriterUtils::OffsetStreamPusher<quint16, psd_byte_order::psdBigEndian> rleExternalTag(io,
0,
channelRLESizePos
+ row * static_cast<qint64>(sizeof(quint16)));
KisAslWriterUtils::OffsetStreamPusher<quint16, byteOrder> rleExternalTag(io, 0, channelRLESizePos + row * static_cast<qint64>(sizeof(quint16)));
if (io.write(compressed) != compressed.size()) {
throw KisAslWriterUtils::ASLWriteException("Failed to write image data");
......@@ -624,6 +622,24 @@ void writeChannelDataRLE(QIODevice &io,
}
}
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)
{
switch (byteOrder) {
case psd_byte_order::psdLittleEndian:
return writeChannelDataRLEImpl<psd_byte_order::psdLittleEndian>(io, plane, channelSize, rc, sizeFieldOffset, rleBlockOffset, writeCompressionType);
default:
return writeChannelDataRLEImpl(io, plane, channelSize, rc, sizeFieldOffset, rleBlockOffset, writeCompressionType);
}
}
template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
inline void preparePixelForWrite(quint8 *dataPlane, int numPixels, int channelSize, int channelId, psd_color_mode colorMode)
{
// if the bitdepth > 8, place the bytes in the right order
......@@ -640,7 +656,8 @@ inline void preparePixelForWrite(quint8 *dataPlane, int numPixels, int channelSi
quint16 *pixelPtr = reinterpret_cast<quint16 *>(dataPlane) + i;
val = *pixelPtr;
val = qFromBigEndian(val);
if (byteOrder == psd_byte_order::psdBigEndian)
val = qFromBigEndian(val);
if (channelId >= 0 && (colorMode == CMYK || colorMode == CMYK64)) {
val = quint16_MAX - val;
}
......@@ -652,23 +669,25 @@ inline void preparePixelForWrite(quint8 *dataPlane, int numPixels, int channelSi
quint32 *pixelPtr = reinterpret_cast<quint32 *>(dataPlane) + i;
val = *pixelPtr;
val = qFromBigEndian(val);
if (byteOrder == psd_byte_order::psdBigEndian)
val = qFromBigEndian(val);
if (channelId >= 0 && (colorMode == CMYK || colorMode == CMYK64)) {
val = quint16_MAX - val;
val = std::numeric_limits<quint32>::max() - val;
}
*pixelPtr = val;
}
}
}
void writePixelDataCommon(QIODevice &io,
KisPaintDeviceSP dev,
const QRect &rc,
psd_color_mode colorMode,
int channelSize,
bool alphaFirst,