[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [calligra/calligra/2.9] krita/plugins/formats/psd: Make PSD pixel data saving code be shared between
From:       Dmitry Kazakov <dimula73 () gmail ! com>
Date:       2015-08-13 17:15:20
Message-ID: E1ZPw64-0001eb-AD () scm ! kde ! org
[Download RAW message or body]

Git commit f3e3cb3596c2970e424b57430a53eb4ca0320d04 by Dmitry Kazakov.
Committed on 13/08/2015 at 15:46.
Pushed by dkazakov into branch 'calligra/2.9'.

Make PSD pixel data saving code be shared between KisImageData and KisLayerRecord

It also fixes a set of loading/saving bugs we had

M  +23   -83   krita/plugins/formats/psd/psd_image_data.cpp
M  +1    -1    krita/plugins/formats/psd/psd_image_data.h
M  +26   -117  krita/plugins/formats/psd/psd_layer_record.cpp
M  +2    -5    krita/plugins/formats/psd/psd_layer_record.h
M  +173  -0    krita/plugins/formats/psd/psd_pixel_utils.cpp
M  +28   -0    krita/plugins/formats/psd/psd_pixel_utils.h
M  +8    -5    krita/plugins/formats/psd/psd_saver.cpp

http://commits.kde.org/calligra/f3e3cb3596c2970e424b57430a53eb4ca0320d04

diff --git a/krita/plugins/formats/psd/psd_image_data.cpp \
b/krita/plugins/formats/psd/psd_image_data.cpp index f609110..693f143 100644
--- a/krita/plugins/formats/psd/psd_image_data.cpp
+++ b/krita/plugins/formats/psd/psd_image_data.cpp
@@ -149,7 +149,7 @@ bool PSDImageData::read(QIODevice *io, KisPaintDeviceSP dev ) {
     return true;
 }
 
-bool PSDImageData::write(QIODevice *io, KisPaintDeviceSP dev)
+bool PSDImageData::write(QIODevice *io, KisPaintDeviceSP dev, bool hasAlpha)
 {
     // XXX: make the compression settting configurable. For now, always use RLE.
     psdwrite(io, (quint16)Compression::RLE);
@@ -157,93 +157,33 @@ bool PSDImageData::write(QIODevice *io, KisPaintDeviceSP dev)
     // now write all the channels in display order
     // fill in the channel chooser, in the display order, but store the pixel index as well.
     QRect rc(0, 0, m_header->width, m_header->height);
-    QVector<quint8* > tmp = dev->readPlanarBytes(0, 0, rc.width(), rc.height());
-    // then reorder the planes to fit the psd model -- alpha first, then display order
-    QVector<quint8* > planes;
-    QList<KoChannelInfo*> origChannels = dev->colorSpace()->channels();
-
-    quint8* alphaPlane = 0;
-    foreach(KoChannelInfo *ch, KoChannelInfo::displayOrderSorted(origChannels)) {
-        int channelIndex = KoChannelInfo::displayPositionToChannelIndex(ch->displayPosition(), \
                origChannels);
-        //qDebug() << ppVar(ch->name()) << ppVar(ch->pos()) << ppVar(ch->displayPosition()) << \
                ppVar(channelIndex);
-        if (ch->channelType() == KoChannelInfo::ALPHA) {
-            alphaPlane = tmp[channelIndex];
-        } else {
-            planes.append(tmp[channelIndex]);
-        }
-    }
-    planes.append(alphaPlane); // alpha is last, in contrast with layers, where it's first.
-    // now planes are holding pointers to quint8 arrays
-    tmp.clear();
-
-    // Now fix up the cmyk channels, we need to invert them
-    if (m_header->colormode == CMYK || m_header->colormode == CMYK64) {
-        for (int i = 0; i < 4; ++i) {
-            if (m_header->channelDepth == 8) {
-                for (int j = 0; j < rc.width() * rc.height(); ++j) {
-                    planes[i][j] = 255 - planes[i][j];
-                }
-            }
-            else if (m_header->channelDepth == 16) {
-                quint16 val;
-                for (int j = 0; j < rc.width() * rc.height(); ++j) {
-                    val = reinterpret_cast<quint16*>(planes[i])[j];
-                    val = quint16_MAX - ntohs(val);
-                    reinterpret_cast<quint16*>(planes[i])[j] = val;
-                }
-            }
-        }
-    }
 
-    quint64 channelLengthPos = io->pos();
-    // write zero's for the channel lengths section
-    for (uint i = 0; i < dev->colorSpace()->channelCount() * rc.height(); ++i) {
-        psdwrite(io, (quint16)0);
-    }
-    // here the actual channel data starts
-    quint64 channelStartPos = io->pos();
-
-    for (int channelInfoIndex = 0; channelInfoIndex  < planes.size(); ++channelInfoIndex) {
-        quint8 *plane = planes[channelInfoIndex];
-
-        quint32 stride = (m_header->channelDepth / 8) * rc.width();
-        for (qint32 row = 0; row < rc.height(); ++row) {
-
-            QByteArray uncompressed = QByteArray::fromRawData((const char*)plane + row * \
                stride, stride);
-            if (m_header->channelDepth == 8) {
-            } else if (m_header->channelDepth == 16) {
-                quint16 *dataPtr = reinterpret_cast<quint16 *>(uncompressed.data());
-                for (int i = 0; i < rc.width(); i++) {
-                    quint16 val = htons(*dataPtr);
-                    *dataPtr = val;
-                    ++dataPtr;
-                }
-            } else if (m_header->channelDepth == 32) {
-                quint32 *dataPtr = reinterpret_cast<quint32 *>(uncompressed.data());
-                for (int i = 0; i < rc.width(); i++) {
-                    quint32 val = htonl(*dataPtr);
-                    *dataPtr = val;
-                    ++dataPtr;
-                }
-            }
-            QByteArray compressed = Compression::compress(uncompressed, Compression::RLE);
+    const int channelSize = m_header->channelDepth / 8;
+    const psd_color_mode colorMode = m_header->colormode;
 
-            io->seek(channelLengthPos);
-            psdwrite(io, (quint16)compressed.size());
-            channelLengthPos +=2;
-            io->seek(channelStartPos);
+    QVector<PsdPixelUtils::ChannelWritingInfo> writingInfoList;
 
-            if (io->write(compressed) != compressed.size()) {
-                error = "Could not write image data";
-                return false;
-            }
+    bool writeAlpha = hasAlpha &&
+        dev->colorSpace()->channelCount() != dev->colorSpace()->colorChannelCount();
 
-            channelStartPos += compressed.size();
-        }
-    }
+    const int numChannels =
+        writeAlpha ?
+        dev->colorSpace()->channelCount() :
+        dev->colorSpace()->colorChannelCount();
 
-    qDeleteAll(planes);
-    planes.clear();
+    for (int i = 0; i < numChannels; i++) {
+        const int rleOffset = io->pos();
+
+        int channelId = writeAlpha && i == numChannels - 1 ? -1 : i;
+
+        writingInfoList <<
+            PsdPixelUtils::ChannelWritingInfo(channelId, -1, rleOffset);
+
+        io->seek(io->pos() + rc.height() * sizeof(quint16));
+    }
 
+    PsdPixelUtils::writePixelDataCommon(io, dev, rc,
+                                        colorMode, channelSize,
+                                        false, false, writingInfoList);
     return true;
 }
diff --git a/krita/plugins/formats/psd/psd_image_data.h \
b/krita/plugins/formats/psd/psd_image_data.h index 8d7c294..61529bc 100644
--- a/krita/plugins/formats/psd/psd_image_data.h
+++ b/krita/plugins/formats/psd/psd_image_data.h
@@ -38,7 +38,7 @@ public:
     virtual ~PSDImageData();
 
     bool read(QIODevice *io, KisPaintDeviceSP dev);
-    bool write(QIODevice *io, KisPaintDeviceSP dev);
+    bool write(QIODevice *io, KisPaintDeviceSP dev, bool hasAlpha);
 
 
     QString error;
diff --git a/krita/plugins/formats/psd/psd_layer_record.cpp \
b/krita/plugins/formats/psd/psd_layer_record.cpp index b7b391c..c572147 100644
--- a/krita/plugins/formats/psd/psd_layer_record.cpp
+++ b/krita/plugins/formats/psd/psd_layer_record.cpp
@@ -587,36 +587,6 @@ void PSDLayerRecord::write(QIODevice* io,
     }
 }
 
-void writeChannelDataRLE(QIODevice *io, const quint8 *plane, const int channelSize, const \
                QRect &rc, const qint64 sizeFieldOffset)
-{
-    KisAslWriterUtils::OffsetStreamPusher<quint32> channelBlockSizeExternalTag(io, 0, \
                sizeFieldOffset);
-
-    SAFE_WRITE_EX(io, (quint16)Compression::RLE);
-
-    // the start of RLE sizes block
-    const qint64 channelRLESizePos = io->pos();
-
-    // write zero's for the channel lengths block
-    for(int i = 0; i < rc.height(); ++i) {
-        // XXX: choose size for PSB!
-        const quint16 fakeRLEBLockSize = 0;
-        SAFE_WRITE_EX(io, fakeRLEBLockSize);
-    }
-
-    quint32 stride = channelSize * rc.width();
-    for (qint32 row = 0; row < rc.height(); ++row) {
-
-        QByteArray uncompressed = QByteArray::fromRawData((const char*)plane + row * stride, \
                stride);
-        QByteArray compressed = Compression::compress(uncompressed, Compression::RLE);
-
-        KisAslWriterUtils::OffsetStreamPusher<quint16> rleExternalTag(io, 0, channelRLESizePos \
                + row * sizeof(quint16));
-
-        if (io->write(compressed) != compressed.size()) {
-            throw KisAslWriterUtils::ASLWriteException("Failed to write image data");
-        }
-    }
-}
-
 void PSDLayerRecord::writeTransparencyMaskPixelData(QIODevice *io)
 {
     if (m_onlyTransparencyMask) {
@@ -626,116 +596,55 @@ void PSDLayerRecord::writeTransparencyMaskPixelData(QIODevice *io)
         QByteArray buffer(m_onlyTransparencyMaskRect.width() * \
                m_onlyTransparencyMaskRect.height(), 0);
         device->readBytes((quint8*)buffer.data(), m_onlyTransparencyMaskRect);
 
-        writeChannelDataRLE(io, (quint8*)buffer.data(), 1, m_onlyTransparencyMaskRect, \
m_transparencyMaskSizeOffset); +        PsdPixelUtils::writeChannelDataRLE(io, \
(quint8*)buffer.data(), 1, m_onlyTransparencyMaskRect, m_transparencyMaskSizeOffset, -1, true); \
}  }
 
 void PSDLayerRecord::writePixelData(QIODevice *io)
 {
+    try {
+        writePixelDataImpl(io);
+    }  catch (KisAslWriterUtils::ASLWriteException &e) {
+        throw KisAslWriterUtils::ASLWriteException(PREPEND_METHOD(e.what()));
+    }
+}
+
+void PSDLayerRecord::writePixelDataImpl(QIODevice *io)
+{
     dbgFile << "writing pixel data for layer" << layerName << "at" << io->pos();
 
     KisPaintDeviceSP dev = m_layerContentDevice;
     const QRect rc(left, top, right - left, bottom - top);
 
     if (rc.isEmpty()) {
-        try {
-            dbgFile << "Layer is empty! Writing placeholder information.";
+        dbgFile << "Layer is empty! Writing placeholder information.";
 
-            for (int i = 0; i < nChannels; i++) {
-                const ChannelInfo *channelInfo = channelInfoRecords[i];
-                KisAslWriterUtils::OffsetStreamPusher<quint32> channelBlockSizeExternalTag(io, \
                0, channelInfo->channelInfoPosition);
-                SAFE_WRITE_EX(io, (quint16)Compression::Uncompressed);
-            }
-
-            writeTransparencyMaskPixelData(io);
-
-        } catch (KisAslWriterUtils::ASLWriteException &e) {
-            throw KisAslWriterUtils::ASLWriteException(PREPEND_METHOD(e.what()));
+        for (int i = 0; i < nChannels; i++) {
+            const ChannelInfo *channelInfo = channelInfoRecords[i];
+            KisAslWriterUtils::OffsetStreamPusher<quint32> channelBlockSizeExternalTag(io, 0, \
channelInfo->channelInfoPosition); +            SAFE_WRITE_EX(io, \
(quint16)Compression::Uncompressed);  }
 
+        writeTransparencyMaskPixelData(io);
+
         return;
     }
 
     // now write all the channels in display order
     dbgFile << "layer" << layerName;
-    QVector<quint8* > tmp = dev->readPlanarBytes(rc.x() - dev->x(), rc.y() - dev->y(), \
                rc.width(), rc.height());
-
-    // then reorder the planes to fit the psd model -- alpha first, then display order
-    QVector<quint8*> planes;
-    QList<KoChannelInfo*> origChannels = dev->colorSpace()->channels();
-
-    foreach(KoChannelInfo *ch, KoChannelInfo::displayOrderSorted(origChannels)) {
-        int channelIndex = KoChannelInfo::displayPositionToChannelIndex(ch->displayPosition(), \
origChannels);  
-        quint8 *holder = tmp[channelIndex];
-        tmp[channelIndex] = 0;
-
-        if (ch->channelType() == KoChannelInfo::ALPHA) {
-            planes.insert(0, holder);
-        } else {
-            planes.append(holder);
-        }
-    }
-
-    // now planes are holding pointers to quint8 arrays
-    tmp.clear();
-
-    try {
-        // here's where we save the total size of the channel data
-        for (int channelInfoIndex = 0; channelInfoIndex  < nChannels; ++channelInfoIndex) {
-
-            const ChannelInfo *channelInfo = channelInfoRecords[channelInfoIndex];
-
-            dbgFile << "\tWriting channel" << channelInfoIndex << "psd channel id" << \
                channelInfo->channelId;
-
-            // if the bitdepth > 8, place the bytes in the right order
-            // if cmyk, invert the pixel value
-            if (m_header.channelDepth == 8) {
-                if (channelInfo->channelId >= 0 && (m_header.colormode == CMYK || \
                m_header.colormode == CMYK64)) {
-                    for (int i = 0; i < rc.width() * rc.height(); ++i) {
-                        planes[channelInfoIndex][i] = 255 - planes[channelInfoIndex][i];
-                    }
-                }
-            }
-            else if (m_header.channelDepth == 16) {
-                quint16 val;
-                for (int i = 0; i < rc.width() * rc.height(); ++i) {
-                    val = reinterpret_cast<quint16*>(planes[channelInfoIndex])[i];
-                    val = ntohs(val);
-                    if (channelInfo->channelId >= 0 && (m_header.colormode == CMYK || \
                m_header.colormode == CMYK64)) {
-                        val = quint16_MAX - val;
-                    }
-                    reinterpret_cast<quint16*>(planes[channelInfoIndex])[i] = val;
-                }
-            }
-            else if (m_header.channelDepth == 32) {
-                quint32 val;
-                for (int i = 0; i < rc.width() * rc.height(); ++i) {
-                    val = reinterpret_cast<quint32*>(planes[channelInfoIndex])[i];
-                    val = ntohl(val);
-                    if (channelInfo->channelId >= 0 && (m_header.colormode == CMYK || \
                m_header.colormode == CMYK64)) {
-                        val = quint16_MAX - val;
-                    }
-                    reinterpret_cast<quint32*>(planes[channelInfoIndex])[i] = val;
-                }
-            }
-
-            dbgFile << "\t\tchannel start" << ppVar(io->pos());
-
-            writeChannelDataRLE(io, planes[channelInfoIndex], m_header.channelDepth / 8, rc, \
                channelInfo->channelInfoPosition);
-        }
-
-        writeTransparencyMaskPixelData(io);
-
-    } catch (KisAslWriterUtils::ASLWriteException &e) {
-        qDeleteAll(planes);
-        planes.clear();
+    const int channelSize = m_header.channelDepth / 8;
+    const psd_color_mode colorMode = m_header.colormode;
 
-        throw KisAslWriterUtils::ASLWriteException(PREPEND_METHOD(e.what()));
+    QVector<PsdPixelUtils::ChannelWritingInfo> writingInfoList;
+    foreach (const ChannelInfo *channelInfo, channelInfoRecords) {
+        writingInfoList <<
+            PsdPixelUtils::ChannelWritingInfo(channelInfo->channelId,
+                                              channelInfo->channelInfoPosition);
     }
 
-    qDeleteAll(planes);
-    planes.clear();
+    PsdPixelUtils::writePixelDataCommon(io, dev, rc, colorMode, channelSize, true, true, \
writingInfoList); +    writeTransparencyMaskPixelData(io);
 }
 
 bool PSDLayerRecord::valid()
diff --git a/krita/plugins/formats/psd/psd_layer_record.h \
b/krita/plugins/formats/psd/psd_layer_record.h index a58a3e83..3270eca 100644
--- a/krita/plugins/formats/psd/psd_layer_record.h
+++ b/krita/plugins/formats/psd/psd_layer_record.h
@@ -158,12 +158,9 @@ public:
 private:
     void writeTransparencyMaskPixelData(QIODevice *io);
 
-private:
+    void writePixelDataImpl(QIODevice *io);
 
-    bool readRGB(KisPaintDeviceSP dev ,QIODevice *io);
-    bool readCMYK(KisPaintDeviceSP dev ,QIODevice *io);
-    bool readLAB(KisPaintDeviceSP dev ,QIODevice *io);
-    bool readGray(KisPaintDeviceSP dev ,QIODevice *io);
+private:
 
     KisPaintDeviceSP m_layerContentDevice;
     KisNodeSP m_onlyTransparencyMask;
diff --git a/krita/plugins/formats/psd/psd_pixel_utils.cpp \
b/krita/plugins/formats/psd/psd_pixel_utils.cpp index 50be4b9..a9b4884 100644
--- a/krita/plugins/formats/psd/psd_pixel_utils.cpp
+++ b/krita/plugins/formats/psd/psd_pixel_utils.cpp
@@ -30,6 +30,7 @@
 
 #include <netinet/in.h> // htonl
 
+#include <asl/kis_asl_writer_utils.h>
 #include <asl/kis_asl_reader_utils.h>
 
 #include "psd_layer_record.h"
@@ -349,4 +350,176 @@ void readChannels(QIODevice *io,
         throw KisAslReaderUtils::ASLParseException(error);
     }
 }
+
+void writeChannelDataRLE(QIODevice *io, const quint8 *plane, const int channelSize, const \
QRect &rc, const qint64 sizeFieldOffset, const qint64 rleBlockOffset, const bool \
writeCompressionType) +{
+    typedef KisAslWriterUtils::OffsetStreamPusher<quint32> Pusher;
+    QScopedPointer<Pusher> channelBlockSizeExternalTag;
+    if (sizeFieldOffset >= 0) {
+        channelBlockSizeExternalTag.reset(new Pusher(io, 0, sizeFieldOffset));
+    }
+
+    if (writeCompressionType) {
+        SAFE_WRITE_EX(io, (quint16)Compression::RLE);
+    }
+
+    const bool externalRleBlock = rleBlockOffset >= 0;
+
+    // the start of RLE sizes block
+    const qint64 channelRLESizePos = externalRleBlock ? rleBlockOffset : io->pos();
+
+    {
+        QScopedPointer<KisOffsetKeeper> rleOffsetKeeper;
+
+        if (externalRleBlock) {
+            rleOffsetKeeper.reset(new KisOffsetKeeper(io));
+            io->seek(rleBlockOffset);
+        }
+
+        // write zero's for the channel lengths block
+        for(int i = 0; i < rc.height(); ++i) {
+            // XXX: choose size for PSB!
+            const quint16 fakeRLEBLockSize = 0;
+            SAFE_WRITE_EX(io, fakeRLEBLockSize);
+        }
+    }
+
+    quint32 stride = channelSize * rc.width();
+    for (qint32 row = 0; row < rc.height(); ++row) {
+
+        QByteArray uncompressed = QByteArray::fromRawData((const char*)plane + row * stride, \
stride); +        QByteArray compressed = Compression::compress(uncompressed, \
Compression::RLE); +
+        KisAslWriterUtils::OffsetStreamPusher<quint16> rleExternalTag(io, 0, channelRLESizePos \
+ row * sizeof(quint16)); +
+        if (io->write(compressed) != compressed.size()) {
+            throw KisAslWriterUtils::ASLWriteException("Failed to write image data");
+        }
+    }
+}
+
+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
+    // if cmyk, invert the pixel value
+    if (channelSize == 1) {
+        if (channelId >= 0 && (colorMode == CMYK || colorMode == CMYK64)) {
+            for (int i = 0; i < numPixels; ++i) {
+                dataPlane[i] = 255 - dataPlane[i];
+            }
+        }
+    }
+    else if (channelSize == 2) {
+        quint16 val;
+        for (int i = 0; i < numPixels; ++i) {
+            quint16 *pixelPtr = reinterpret_cast<quint16*>(dataPlane) + i;
+
+            val = *pixelPtr;
+            val = ntohs(val);
+            if (channelId >= 0 && (colorMode == CMYK || colorMode == CMYK64)) {
+                val = quint16_MAX - val;
+            }
+            *pixelPtr = val;
+        }
+    }
+    else if (channelSize == 4) {
+        quint32 val;
+        for (int i = 0; i < numPixels; ++i) {
+            quint32 *pixelPtr = reinterpret_cast<quint32*>(dataPlane) + i;
+
+            val = *pixelPtr;
+            val = ntohl(val);
+            if (channelId >= 0 && (colorMode == CMYK || colorMode == CMYK64)) {
+                val = quint16_MAX - val;
+            }
+            *pixelPtr = val;
+        }
+    }
+}
+
+void writePixelDataCommon(QIODevice *io,
+                          KisPaintDeviceSP dev,
+                          const QRect &rc,
+                          psd_color_mode colorMode,
+                          int channelSize,
+                          bool alphaFirst,
+                          const bool writeCompressionType,
+                          QVector<ChannelWritingInfo> &writingInfoList)
+{
+    // Empty rects must be processed separately on a higher level!
+    KIS_ASSERT_RECOVER_RETURN(!rc.isEmpty());
+
+    QVector<quint8* > tmp = dev->readPlanarBytes(rc.x() - dev->x(), rc.y() - dev->y(), \
rc.width(), rc.height()); +    const KoColorSpace *colorSpace = dev->colorSpace();
+
+    QVector<quint8*> planes;
+
+    { // prepare 'planes' array
+
+        quint8 *alphaPlanePtr = 0;
+
+        QList<KoChannelInfo*> origChannels = colorSpace->channels();
+        foreach(KoChannelInfo *ch, KoChannelInfo::displayOrderSorted(origChannels)) {
+            int channelIndex = \
KoChannelInfo::displayPositionToChannelIndex(ch->displayPosition(), origChannels); +
+            quint8 *holder = 0;
+            qSwap(holder, tmp[channelIndex]);
+
+            if (ch->channelType() == KoChannelInfo::ALPHA) {
+                qSwap(holder, alphaPlanePtr);
+            } else {
+                planes.append(holder);
+            }
+        }
+
+        if (alphaPlanePtr) {
+            if (alphaFirst) {
+                planes.insert(0, alphaPlanePtr);
+                KIS_ASSERT_RECOVER_NOOP(writingInfoList.first().channelId == -1);
+            } else {
+                planes.append(alphaPlanePtr);
+                KIS_ASSERT_RECOVER_NOOP(
+                    (writingInfoList.size() == planes.size() - 1) ||
+                    (writingInfoList.last().channelId == -1));
+            }
+        }
+
+        // now planes are holding pointers to quint8 arrays
+        tmp.clear();
+    }
+
+    KIS_ASSERT_RECOVER_RETURN(planes.size() >= writingInfoList.size());
+
+    const int numPixels = rc.width() * rc.height();
+
+    // write down the planes
+
+    try {
+        for (int i = 0; i < writingInfoList.size(); i++) {
+            const ChannelWritingInfo &info = writingInfoList[i];
+
+            dbgFile << "\tWriting channel" << i << "psd channel id" << info.channelId;
+
+            preparePixelForWrite(planes[i], numPixels, channelSize, info.channelId, \
colorMode); +
+            dbgFile << "\t\tchannel start" << ppVar(io->pos());
+
+            writeChannelDataRLE(io, planes[i], channelSize, rc, info.sizeFieldOffset, \
info.rleBlockOffset, writeCompressionType); +        }
+
+    } catch (KisAslWriterUtils::ASLWriteException &e) {
+        qDeleteAll(planes);
+        planes.clear();
+
+        throw KisAslWriterUtils::ASLWriteException(PREPEND_METHOD(e.what()));
+    }
+
+    qDeleteAll(planes);
+    planes.clear();
+}
+
 }
diff --git a/krita/plugins/formats/psd/psd_pixel_utils.h \
b/krita/plugins/formats/psd/psd_pixel_utils.h index 4af352f..a1c93b6 100644
--- a/krita/plugins/formats/psd/psd_pixel_utils.h
+++ b/krita/plugins/formats/psd/psd_pixel_utils.h
@@ -27,10 +27,21 @@
 
 class QIODevice;
 struct ChannelInfo;
+struct ChannelWritingInfo;
 
 namespace PsdPixelUtils
 {
 
+    struct ChannelWritingInfo {
+        ChannelWritingInfo() : channelId(0), sizeFieldOffset(-1), rleBlockOffset(-1) {}
+        ChannelWritingInfo(qint16 _channelId, int _sizeFieldOffset) : channelId(_channelId), \
sizeFieldOffset(_sizeFieldOffset), rleBlockOffset(-1) {} +        ChannelWritingInfo(qint16 \
_channelId, int _sizeFieldOffset, int _rleBlockOffset) : channelId(_channelId), \
sizeFieldOffset(_sizeFieldOffset), rleBlockOffset(_rleBlockOffset) {} +
+        qint16 channelId;
+        int sizeFieldOffset;
+        int rleBlockOffset;
+    };
+
     void readChannels(QIODevice *io,
                       KisPaintDeviceSP device,
                       psd_color_mode colorMode,
@@ -38,6 +49,23 @@ namespace PsdPixelUtils
                       const QRect &layerRect,
                       QVector<ChannelInfo*> infoRecords);
 
+    void writeChannelDataRLE(QIODevice *io,
+                             const quint8 *plane,
+                             const int channelSize,
+                             const QRect &rc,
+                             const qint64 sizeFieldOffset,
+                             const qint64 rleBlockOffset,
+                             const bool writeCompressionType);
+
+    void writePixelDataCommon(QIODevice *io,
+                              KisPaintDeviceSP dev,
+                              const QRect &rc,
+                              psd_color_mode colorMode,
+                              int channelSize,
+                              bool alphaFirst,
+                              const bool writeCompressionType,
+                              QVector<ChannelWritingInfo> &writingInfoList);
+
 }
 
 #endif /* __PSD_PIXEL_UTILS_H */
diff --git a/krita/plugins/formats/psd/psd_saver.cpp b/krita/plugins/formats/psd/psd_saver.cpp
index 9098f67..f9b44c0 100644
--- a/krita/plugins/formats/psd/psd_saver.cpp
+++ b/krita/plugins/formats/psd/psd_saver.cpp
@@ -142,11 +142,17 @@ KisImageBuilder_Result PSDSaver::buildFile(const KUrl& uri)
         return KisImageBuilder_RESULT_NOT_LOCAL;
     }
 
+    const bool haveLayers = m_image->rootLayer()->childCount() > 1 ||
+        checkIfHasTransparency(m_image->rootLayer()->firstChild()->projection());
+
     // HEADER
     PSDHeader header;
     header.signature = "8BPS";
     header.version = 1;
-    header.nChannels = m_image->colorSpace()->channelCount();
+    header.nChannels = haveLayers ?
+        m_image->colorSpace()->channelCount() :
+        m_image->colorSpace()->colorChannelCount();
+
     header.width = m_image->width();
     header.height = m_image->height();
 
@@ -240,9 +246,6 @@ KisImageBuilder_Result PSDSaver::buildFile(const KUrl& uri)
     // Only save layers and masks if there is more than one layer
     dbgFile << "m_image->rootLayer->childCount" << m_image->rootLayer()->childCount() << \
f.pos();  
-    bool haveLayers = m_image->rootLayer()->childCount() > 1 ||
-        checkIfHasTransparency(m_image->rootLayer()->firstChild()->projection());
-
     if (haveLayers) {
 
         PSDLayerMaskSection layerSection(header);
@@ -262,7 +265,7 @@ KisImageBuilder_Result PSDSaver::buildFile(const KUrl& uri)
     // IMAGE DATA
     dbgFile << "Saving composited image" << f.pos();
     PSDImageData imagedata(&header);
-    if (!imagedata.write(&f, m_image->projection())) {
+    if (!imagedata.write(&f, m_image->projection(), haveLayers)) {
         dbgFile << "Failed to write image data. Error:"  << imagedata.error;
         return KisImageBuilder_RESULT_FAILURE;
     }


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic