[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [calligra/calligra/2.9] krita/plugins/formats/psd: [FEATURE] Implement loading for ZIP compressed PS
From: Dmitry Kazakov <dimula73 () gmail ! com>
Date: 2015-08-14 8:08:22
Message-ID: E1ZQA2I-0002Ii-FV () scm ! kde ! org
[Download RAW message or body]
Git commit 02b1733ba001f1b60b1563f005e9a8984ce53bea by Dmitry Kazakov.
Committed on 14/08/2015 at 08:07.
Pushed by dkazakov into branch 'calligra/2.9'.
[FEATURE] Implement loading for ZIP compressed PSD files
Now all types of 8 bit and 16 bit PSD files should load fine, including
multilayered ones. Saving should work as well.
STILL NEEDS TESTING:
We need to test if Photoshop will load 16bit multilayered files saved
in Krita. The point is we still save them differently from what PS is
used to.
CC:kimageshop@kde.org
M +5 -4 krita/plugins/formats/psd/CMakeLists.txt
M +3 -1 krita/plugins/formats/psd/psd_additional_layer_info_block.cpp
M +1 -4 krita/plugins/formats/psd/psd_layer_section.cpp
M +152 -12 krita/plugins/formats/psd/psd_pixel_utils.cpp
M +4 -3 krita/plugins/formats/psd/tests/kis_psd_test.cpp
http://commits.kde.org/calligra/02b1733ba001f1b60b1563f005e9a8984ce53bea
diff --git a/krita/plugins/formats/psd/CMakeLists.txt \
b/krita/plugins/formats/psd/CMakeLists.txt index 99b4e14..1ffe2d3 100644
--- a/krita/plugins/formats/psd/CMakeLists.txt
+++ b/krita/plugins/formats/psd/CMakeLists.txt
@@ -4,6 +4,7 @@ macro_optional_find_package(ZLIB)
macro_bool_to_01(ZLIB_FOUND HAVE_ZLIB)
configure_file(config_psd.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config_psd.h)
+include_directories(${ZLIB_INCLUDE_DIR})
set(LIB_PSD_SRCS
psd_header.cpp
@@ -25,9 +26,9 @@ set(kritapsdimport_PART_SRCS
kde4_add_plugin(kritapsdimport ${kritapsdimport_PART_SRCS})
if (WIN32)
- target_link_libraries(kritapsdimport kritaui kritapsd ${KDE4_KIO_LIBS} \
${WIN32_PLATFORM_NET_LIBS}) + target_link_libraries(kritapsdimport kritaui kritapsd \
${KDE4_KIO_LIBS} ${WIN32_PLATFORM_NET_LIBS} ${ZLIB_LIBRARIES}) else ()
- target_link_libraries(kritapsdimport kritaui kritapsd ${KDE4_KIO_LIBS})
+ target_link_libraries(kritapsdimport kritaui kritapsd ${KDE4_KIO_LIBS} \
${ZLIB_LIBRARIES}) endif ()
install(TARGETS kritapsdimport DESTINATION ${PLUGIN_INSTALL_DIR})
@@ -43,9 +44,9 @@ set(kritapsdexport_PART_SRCS
kde4_add_plugin(kritapsdexport ${kritapsdexport_PART_SRCS})
if (WIN32)
- target_link_libraries(kritapsdexport kritaui ${WIN32_PLATFORM_NET_LIBS})
+ target_link_libraries(kritapsdexport kritaui ${WIN32_PLATFORM_NET_LIBS} \
${ZLIB_LIBRARIES}) else ()
- target_link_libraries(kritapsdexport kritaui )
+ target_link_libraries(kritapsdexport kritaui ${ZLIB_LIBRARIES})
endif ()
install(TARGETS kritapsdexport DESTINATION ${PLUGIN_INSTALL_DIR})
diff --git a/krita/plugins/formats/psd/psd_additional_layer_info_block.cpp \
b/krita/plugins/formats/psd/psd_additional_layer_info_block.cpp index \
c0d4bb7..e52b543 100644
--- a/krita/plugins/formats/psd/psd_additional_layer_info_block.cpp
+++ b/krita/plugins/formats/psd/psd_additional_layer_info_block.cpp
@@ -95,7 +95,9 @@ void PsdAdditionalLayerInfoBlock::readImpl(QIODevice* io)
}
keys << key;
- if (key == "Lr16" || key == "Lr32") {
+ // TODO: Loading of 32 bit files is not supported yet
+
+ if (key == "Lr16"/* || key == "Lr32"*/) {
if (m_layerInfoBlockHandler) {
int offset = m_header.version > 1 ? 8 : 4;
io->seek(io->pos() - offset);
diff --git a/krita/plugins/formats/psd/psd_layer_section.cpp \
b/krita/plugins/formats/psd/psd_layer_section.cpp index b12c79d..5a8eea0 100644
--- a/krita/plugins/formats/psd/psd_layer_section.cpp
+++ b/krita/plugins/formats/psd/psd_layer_section.cpp
@@ -274,11 +274,8 @@ bool PSDLayerMaskSection::readImpl(QIODevice* io)
*
* Here we pass the callback which should be used when such
* additional section is recognized.
- *
- * NOTE: atm, we do not support ZIP compression, which is used in
- * this block, so we just comment it out for now!
*/
- // globalInfoSection.setExtraLayerInfoBlockHandler(boost::bind(&PSDLayerMaskSection::readLayerInfoImpl, \
this, _1)); + globalInfoSection.setExtraLayerInfoBlockHandler(boost::bind(&PSDLayerMaskSection::readLayerInfoImpl, \
this, _1));
globalInfoSection.read(io);
diff --git a/krita/plugins/formats/psd/psd_pixel_utils.cpp \
b/krita/plugins/formats/psd/psd_pixel_utils.cpp index a9b4884..60784fe 100644
--- a/krita/plugins/formats/psd/psd_pixel_utils.cpp
+++ b/krita/plugins/formats/psd/psd_pixel_utils.cpp
@@ -37,6 +37,10 @@
#include <asl/kis_offset_keeper.h>
#include "kis_iterator_ng.h"
+#include "config_psd.h"
+#ifdef HAVE_ZLIB
+#include "zlib.h"
+#endif
namespace PsdPixelUtils {
@@ -114,7 +118,7 @@ void readGrayPixel(const QMap<quint16, QByteArray> &channelBytes,
const channels_type unitValue = \
KoColorSpaceMathsTraits<channels_type>::unitValue; channels_type opacity = \
unitValue; if (channelBytes.contains(-1)) {
- opacity = channelBytes[-1].constData()[col];
+ opacity = convertByteOrder<Traits>(reinterpret_cast<const channels_type \
*>(channelBytes[-1].constData())[col]); }
Pixel *pixelPtr = reinterpret_cast<Pixel*>(dstPtr);
@@ -135,7 +139,7 @@ void readRgbPixel(const QMap<quint16, QByteArray> &channelBytes,
const channels_type unitValue = \
KoColorSpaceMathsTraits<channels_type>::unitValue; channels_type opacity = \
unitValue; if (channelBytes.contains(-1)) {
- opacity = channelBytes[-1].constData()[col];
+ opacity = convertByteOrder<Traits>(reinterpret_cast<const channels_type \
*>(channelBytes[-1].constData())[col]); }
Pixel *pixelPtr = reinterpret_cast<Pixel*>(dstPtr);
@@ -160,7 +164,7 @@ void readCmykPixel(const QMap<quint16, QByteArray> &channelBytes,
const channels_type unitValue = \
KoColorSpaceMathsTraits<channels_type>::unitValue; channels_type opacity = \
unitValue; if (channelBytes.contains(-1)) {
- opacity = channelBytes[-1].constData()[col];
+ opacity = convertByteOrder<Traits>(reinterpret_cast<const channels_type \
*>(channelBytes[-1].constData())[col]); }
Pixel *pixelPtr = reinterpret_cast<Pixel*>(dstPtr);
@@ -187,7 +191,7 @@ void readLabPixel(const QMap<quint16, QByteArray> &channelBytes,
const channels_type unitValue = \
KoColorSpaceMathsTraits<channels_type>::unitValue; channels_type opacity = \
unitValue; if (channelBytes.contains(-1)) {
- opacity = channelBytes[-1].constData()[col];
+ opacity = convertByteOrder<Traits>(reinterpret_cast<const channels_type \
*>(channelBytes[-1].constData())[col]); }
Pixel *pixelPtr = reinterpret_cast<Pixel*>(dstPtr);
@@ -254,6 +258,99 @@ void readLabPixelCommon(int channelSize,
}
}
+/**********************************************************************/
+/* Two functions copied from the abandoned PSDParse library (GPL) */
+/* See: http://www.telegraphics.com.au/svn/psdparse/trunk/psd_zip.c */
+/* Created by Patrick in 2007.02.02, libpsd@graphest.com */
+/* Modifications by Toby Thain <toby@telegraphics.com.au> */
+/**********************************************************************/
+
+typedef bool psd_status;
+typedef quint8 psd_uchar;
+typedef int psd_int;
+typedef quint8 Bytef;
+
+psd_status psd_unzip_without_prediction(psd_uchar *src_buf, psd_int src_len,
+ psd_uchar *dst_buf, psd_int dst_len)
+{
+#ifdef HAVE_ZLIB
+ z_stream stream;
+ psd_int state;
+
+ memset(&stream, 0, sizeof(z_stream));
+ stream.data_type = Z_BINARY;
+
+ stream.next_in = (Bytef *)src_buf;
+ stream.avail_in = src_len;
+ stream.next_out = (Bytef *)dst_buf;
+ stream.avail_out = dst_len;
+
+ if(inflateInit(&stream) != Z_OK)
+ return 0;
+
+ do {
+ state = inflate(&stream, Z_PARTIAL_FLUSH);
+ if(state == Z_STREAM_END)
+ break;
+ if(state == Z_DATA_ERROR || state != Z_OK)
+ break;
+ } while (stream.avail_out > 0);
+
+ if (state != Z_STREAM_END && state != Z_OK)
+ return 0;
+
+ return 1;
+
+#endif /* HAVE_ZLIB */
+
+ return 0;
+}
+
+psd_status psd_unzip_with_prediction(psd_uchar *src_buf, psd_int src_len,
+ psd_uchar *dst_buf, psd_int dst_len,
+ psd_int row_size, psd_int color_depth)
+{
+ psd_status status;
+ int len;
+ psd_uchar * buf;
+
+ status = psd_unzip_without_prediction(src_buf, src_len, dst_buf, dst_len);
+ if(!status)
+ return status;
+
+ buf = dst_buf;
+ do {
+ len = row_size;
+ if (color_depth == 16)
+ {
+ while(--len)
+ {
+ buf[2] += buf[0] + ((buf[1] + buf[3]) >> 8);
+ buf[3] += buf[1];
+ buf += 2;
+ }
+ buf += 2;
+ dst_len -= row_size * 2;
+ }
+ else
+ {
+ while(--len)
+ {
+ *(buf + 1) += *buf;
+ buf ++;
+ }
+ buf ++;
+ dst_len -= row_size;
+ }
+ } while(dst_len > 0);
+
+ return 1;
+}
+
+/**********************************************************************/
+/* End of third party block */
+/**********************************************************************/
+
QMap<quint16, QByteArray> fetchChannelsBytes(QIODevice *io, QVector<ChannelInfo*> \
channelInfoRecords,
int row, int width, int channelSize)
{
@@ -304,19 +401,62 @@ void readCommon(KisPaintDeviceSP dev,
return;
}
- KisHLineIteratorSP it = dev->createHLineIteratorNG(layerRect.left(), \
layerRect.top(), layerRect.width()); + if (infoRecords.first()->compressionType == \
Compression::ZIP || + infoRecords.first()->compressionType == \
Compression::ZIPWithPrediction) { +
+ const int numPixels = channelSize * layerRect.width() * layerRect.height();
- for (int i = 0 ; i < layerRect.height(); i++) {
QMap<quint16, QByteArray> channelBytes;
- channelBytes = fetchChannelsBytes(io, infoRecords,
- i, layerRect.width(), channelSize);
+ foreach (ChannelInfo *info, infoRecords) {
+ io->seek(info->channelDataStart);
+ QByteArray compressedBytes = io->read(info->channelDataLength);
+ QByteArray uncompressedBytes(numPixels, 0);
+
+ bool status = false;
+ if (infoRecords.first()->compressionType == Compression::ZIP) {
+ status = \
psd_unzip_without_prediction((quint8*)compressedBytes.data(), compressedBytes.size(), \
+ \
(quint8*)uncompressedBytes.data(), uncompressedBytes.size()); + } else {
+ status = psd_unzip_with_prediction((quint8*)compressedBytes.data(), \
compressedBytes.size(), + \
(quint8*)uncompressedBytes.data(), uncompressedBytes.size(), + \
layerRect.width(), channelSize * 8); + }
- for (qint64 col = 0; col < layerRect.width(); col++){
- pixelFunc(channelSize, channelBytes, col, it->rawData());
- it->nextPixel();
+ if (!status) {
+ QString error = QString("Failed to unzip channel data: id = %1, \
compression = %2").arg(info->channelId).arg(info->compressionType); + \
dbgFile << "ERROR:" << error; + dbgFile << " " << \
ppVar(info->channelId); + dbgFile << " " << \
ppVar(info->channelDataStart); + dbgFile << " " << \
ppVar(info->channelDataLength); + dbgFile << " " << \
ppVar(info->compressionType); + throw \
KisAslReaderUtils::ASLParseException(error); + }
+
+ channelBytes.insert(info->channelId, uncompressedBytes);
+ }
+
+ KisSequentialIterator it(dev, layerRect);
+ int col = 0;
+ do {
+ pixelFunc(channelSize, channelBytes, col, it.rawData());
+ col++;
+ } while(it.nextPixel());
+
+ } else {
+ KisHLineIteratorSP it = dev->createHLineIteratorNG(layerRect.left(), \
layerRect.top(), layerRect.width()); + for (int i = 0 ; i < \
layerRect.height(); i++) { + QMap<quint16, QByteArray> channelBytes;
+
+ channelBytes = fetchChannelsBytes(io, infoRecords,
+ i, layerRect.width(), channelSize);
+
+ for (qint64 col = 0; col < layerRect.width(); col++){
+ pixelFunc(channelSize, channelBytes, col, it->rawData());
+ it->nextPixel();
+ }
+ it->nextRow();
}
- it->nextRow();
}
}
diff --git a/krita/plugins/formats/psd/tests/kis_psd_test.cpp \
b/krita/plugins/formats/psd/tests/kis_psd_test.cpp index 58e0717..b0914ee 100644
--- a/krita/plugins/formats/psd/tests/kis_psd_test.cpp
+++ b/krita/plugins/formats/psd/tests/kis_psd_test.cpp
@@ -249,7 +249,8 @@ void KisPSDTest::testOpeningFromOpenCanvas()
void KisPSDTest::testOpeningAllFormats()
{
- QDir dirSources(QString(FILES_DATA_DIR) + QDir::separator() + "format_set/");
+ QString path = TestUtil::fetchExternalDataFileName("psd_format_test_files");
+ QDir dirSources(path);
foreach(QFileInfo sourceFileInfo, dirSources.entryInfoList()) {
Q_ASSERT(sourceFileInfo.exists());
@@ -258,8 +259,8 @@ void KisPSDTest::testOpeningAllFormats()
continue;
}
- if (sourceFileInfo.fileName() != "sl_cmyk_8b.psd") {
- continue;
+ if (sourceFileInfo.fileName() != "ml_cmyk_16b.psd") {
+ //continue;
}
//qDebug() << "Opening" << ppVar(sourceFileInfo.fileName());
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic