Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package kimageformats for openSUSE:Factory 
checked in at 2022-07-11 19:08:47
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/kimageformats (Old)
 and      /work/SRC/openSUSE:Factory/.kimageformats.new.1523 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "kimageformats"

Mon Jul 11 19:08:47 2022 rev:108 rq:988144 version:5.96.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/kimageformats/kimageformats.changes      
2022-06-17 21:22:03.858758776 +0200
+++ /work/SRC/openSUSE:Factory/.kimageformats.new.1523/kimageformats.changes    
2022-07-11 19:10:07.663672337 +0200
@@ -1,0 +2,20 @@
+Sun Jul  3 11:56:00 UTC 2022 - Christophe Giboudeaux <christo...@krop.fr>
+
+- Update to 5.96.0
+  * New feature release
+  * For more details please see:
+  * https://kde.org/announcements/frameworks/5/5.96.0
+- Changes since 5.95.0:
+  * PSD header checks according to specifications
+  * Improved detection of alpha channel on CMYK images
+  * Minor code optimization
+  * Minor code improvements (tested on all my MCYK PSD/PSB files)
+  * Fix Alpha + testcase images
+  * Fix regression
+  * Basic support to CMYK 8/16 bits (not fully tested)
+  * Require passing tests for the CI to pass
+  * jxl: support both old 0.6.1 and new 0.7.0 libjxl API
+  * Remove extra ';'
+  * avif: read performance improvements
+
+-------------------------------------------------------------------

Old:
----
  kimageformats-5.95.0.tar.xz
  kimageformats-5.95.0.tar.xz.sig

New:
----
  kimageformats-5.96.0.tar.xz
  kimageformats-5.96.0.tar.xz.sig

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ kimageformats.spec ++++++
--- /var/tmp/diff_new_pack.BeGfhV/_old  2022-07-11 19:10:08.555673632 +0200
+++ /var/tmp/diff_new_pack.BeGfhV/_new  2022-07-11 19:10:08.559673637 +0200
@@ -22,7 +22,7 @@
 %if 0%{?suse_version} > 1500 || (0%{?is_opensuse} && 0%{?sle_version} >= 
150300)
 %define with_heif 1
 %endif
-%define _tar_path 5.95
+%define _tar_path 5.96
 # Full KF5 version (e.g. 5.33.0)
 %{!?_kf5_version: %global _kf5_version %{version}}
 # Last major and minor KF5 version (e.g. 5.33)
@@ -30,7 +30,7 @@
 # Only needed for the package signature condition
 %bcond_without released
 Name:           kimageformats
-Version:        5.95.0
+Version:        5.96.0
 Release:        0
 Summary:        Image format plugins for Qt
 License:        LGPL-2.1-or-later


++++++ kimageformats-5.95.0.tar.xz -> kimageformats-5.96.0.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kimageformats-5.95.0/.kde-ci.yml 
new/kimageformats-5.96.0/.kde-ci.yml
--- old/kimageformats-5.95.0/.kde-ci.yml        2022-06-04 10:19:33.000000000 
+0200
+++ new/kimageformats-5.96.0/.kde-ci.yml        2022-07-02 16:33:58.000000000 
+0200
@@ -6,3 +6,4 @@
 
 Options:
   test-before-installing: True
+  require-passing-tests-on: [ 'Linux', 'FreeBSD', 'Windows' ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kimageformats-5.95.0/CMakeLists.txt 
new/kimageformats-5.96.0/CMakeLists.txt
--- old/kimageformats-5.95.0/CMakeLists.txt     2022-06-04 10:19:33.000000000 
+0200
+++ new/kimageformats-5.96.0/CMakeLists.txt     2022-07-02 16:33:58.000000000 
+0200
@@ -3,7 +3,7 @@
 project(KImageFormats)
 
 include(FeatureSummary)
-find_package(ECM 5.95.0  NO_MODULE)
+find_package(ECM 5.96.0  NO_MODULE)
 set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake 
Modules." URL "https://commits.kde.org/extra-cmake-modules";)
 feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND 
FATAL_ON_MISSING_REQUIRED_PACKAGES)
 
@@ -13,9 +13,9 @@
 include(KDEInstallDirs)
 include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE)
 include(KDECMakeSettings)
-include(KDEGitCommitHooks)
-
 
+include(KDEGitCommitHooks)
+include(ECMDeprecationSettings)
 include(CheckIncludeFiles)
 include(FindPkgConfig)
 
@@ -70,8 +70,11 @@
 endif()
 add_feature_info(LibJXL LibJXL_FOUND "required for the QImage plugin for JPEG 
XL images")
 
-add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050f02)
-add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x055900)
+ecm_set_disabled_deprecation_versions(
+    QT 5.15.2
+    KF 5.95
+)
+
 add_subdirectory(src)
 if (BUILD_TESTING)
     add_subdirectory(autotests)
Binary files old/kimageformats-5.95.0/autotests/read/psd/cmyka-16bits.png and 
new/kimageformats-5.96.0/autotests/read/psd/cmyka-16bits.png differ
Binary files old/kimageformats-5.95.0/autotests/read/psd/cmyka-16bits.psd and 
new/kimageformats-5.96.0/autotests/read/psd/cmyka-16bits.psd differ
Binary files old/kimageformats-5.95.0/autotests/read/psd/cmyka-8bits.png and 
new/kimageformats-5.96.0/autotests/read/psd/cmyka-8bits.png differ
Binary files old/kimageformats-5.95.0/autotests/read/psd/cmyka-8bits.psd and 
new/kimageformats-5.96.0/autotests/read/psd/cmyka-8bits.psd differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kimageformats-5.95.0/src/imageformats/CMakeLists.txt 
new/kimageformats-5.96.0/src/imageformats/CMakeLists.txt
--- old/kimageformats-5.95.0/src/imageformats/CMakeLists.txt    2022-06-04 
10:19:33.000000000 +0200
+++ new/kimageformats-5.96.0/src/imageformats/CMakeLists.txt    2022-07-02 
16:33:58.000000000 +0200
@@ -4,6 +4,7 @@
 
 function(kimageformats_add_plugin plugin)
     set(options)
+    set(oneValueArgs)
     set(multiValueArgs SOURCES)
     cmake_parse_arguments(KIF_ADD_PLUGIN "${options}" "${oneValueArgs}" 
"${multiValueArgs}" ${ARGN})
     if(NOT KIF_ADD_PLUGIN_SOURCES)
@@ -86,6 +87,9 @@
 if (LibJXL_FOUND AND LibJXLThreads_FOUND)
     kimageformats_add_plugin(kimg_jxl SOURCES jxl.cpp)
     target_link_libraries(kimg_jxl PkgConfig::LibJXL PkgConfig::LibJXLThreads)
+    if (LibJXL_VERSION VERSION_GREATER_EQUAL "0.7.0")
+        target_compile_definitions(kimg_jxl PRIVATE KIMG_JXL_API_VERSION=70)
+    endif()
     install(FILES jxl.desktop DESTINATION 
${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
 endif()
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kimageformats-5.95.0/src/imageformats/avif.cpp 
new/kimageformats-5.96.0/src/imageformats/avif.cpp
--- old/kimageformats-5.95.0/src/imageformats/avif.cpp  2022-06-04 
10:19:33.000000000 +0200
+++ new/kimageformats-5.96.0/src/imageformats/avif.cpp  2022-07-02 
16:33:58.000000000 +0200
@@ -67,7 +67,7 @@
 
 bool QAVIFHandler::ensureParsed() const
 {
-    if (m_parseState == ParseAvifSuccess) {
+    if (m_parseState == ParseAvifSuccess || m_parseState == ParseAvifMetadata) 
{
         return true;
     }
     if (m_parseState == ParseAvifError) {
@@ -79,6 +79,28 @@
     return that->ensureDecoder();
 }
 
+bool QAVIFHandler::ensureOpened() const
+{
+    if (m_parseState == ParseAvifSuccess) {
+        return true;
+    }
+    if (m_parseState == ParseAvifError) {
+        return false;
+    }
+
+    QAVIFHandler *that = const_cast<QAVIFHandler *>(this);
+    if (ensureParsed()) {
+        if (m_parseState == ParseAvifMetadata) {
+            bool success = that->jumpToNextImage();
+            that->m_parseState = success ? ParseAvifSuccess : ParseAvifError;
+            return success;
+        }
+    }
+
+    that->m_parseState = ParseAvifError;
+    return false;
+}
+
 bool QAVIFHandler::ensureDecoder()
 {
     if (m_decoder) {
@@ -97,6 +119,9 @@
 
     m_decoder = avifDecoderCreate();
 
+    m_decoder->ignoreExif = AVIF_TRUE;
+    m_decoder->ignoreXMP = AVIF_TRUE;
+
 #if AVIF_VERSION >= 80400
     m_decoder->maxThreads = qBound(1, QThread::idealThreadCount(), 64);
 #endif
@@ -127,45 +152,58 @@
         return false;
     }
 
-    decodeResult = avifDecoderNextImage(m_decoder);
+    m_container_width = m_decoder->image->width;
+    m_container_height = m_decoder->image->height;
 
-    if (decodeResult == AVIF_RESULT_OK) {
-        m_container_width = m_decoder->image->width;
-        m_container_height = m_decoder->image->height;
-
-        if ((m_container_width > 65535) || (m_container_height > 65535)) {
-            qWarning("AVIF image (%dx%d) is too large!", m_container_width, 
m_container_height);
-            m_parseState = ParseAvifError;
-            return false;
-        }
+    if ((m_container_width > 65535) || (m_container_height > 65535)) {
+        qWarning("AVIF image (%dx%d) is too large!", m_container_width, 
m_container_height);
+        m_parseState = ParseAvifError;
+        return false;
+    }
 
-        if ((m_container_width == 0) || (m_container_height == 0)) {
-            qWarning("Empty image, nothing to decode");
-            m_parseState = ParseAvifError;
-            return false;
-        }
+    if ((m_container_width == 0) || (m_container_height == 0)) {
+        qWarning("Empty image, nothing to decode");
+        m_parseState = ParseAvifError;
+        return false;
+    }
 
-        if (m_container_width > ((16384 * 16384) / m_container_height)) {
-            qWarning("AVIF image (%dx%d) has more than 256 megapixels!", 
m_container_width, m_container_height);
-            m_parseState = ParseAvifError;
-            return false;
+    if (m_container_width > ((16384 * 16384) / m_container_height)) {
+        qWarning("AVIF image (%dx%d) has more than 256 megapixels!", 
m_container_width, m_container_height);
+        m_parseState = ParseAvifError;
+        return false;
+    }
+
+    // calculate final dimensions with crop and rotate operations applied
+    int new_width = m_container_width;
+    int new_height = m_container_height;
+
+    if (m_decoder->image->transformFlags & AVIF_TRANSFORM_CLAP) {
+        if ((m_decoder->image->clap.widthD > 0) && 
(m_decoder->image->clap.heightD > 0) && (m_decoder->image->clap.horizOffD > 0)
+            && (m_decoder->image->clap.vertOffD > 0)) {
+            int crop_width = (int)((double)(m_decoder->image->clap.widthN) / 
(m_decoder->image->clap.widthD) + 0.5);
+            if (crop_width < new_width && crop_width > 0) {
+                new_width = crop_width;
+            }
+            int crop_height = (int)((double)(m_decoder->image->clap.heightN) / 
(m_decoder->image->clap.heightD) + 0.5);
+            if (crop_height < new_height && crop_height > 0) {
+                new_height = crop_height;
+            }
         }
+    }
 
-        m_parseState = ParseAvifSuccess;
-        if (decode_one_frame()) {
-            return true;
-        } else {
-            m_parseState = ParseAvifError;
-            return false;
+    if (m_decoder->image->transformFlags & AVIF_TRANSFORM_IROT) {
+        if (m_decoder->image->irot.angle == 1 || m_decoder->image->irot.angle 
== 3) {
+            int tmp = new_width;
+            new_width = new_height;
+            new_height = tmp;
         }
-    } else {
-        qWarning("ERROR: Failed to decode image: %s", 
avifResultToString(decodeResult));
     }
 
-    avifDecoderDestroy(m_decoder);
-    m_decoder = nullptr;
-    m_parseState = ParseAvifError;
-    return false;
+    m_estimated_dimensions.setWidth(new_width);
+    m_estimated_dimensions.setHeight(new_height);
+
+    m_parseState = ParseAvifMetadata;
+    return true;
 }
 
 bool QAVIFHandler::decode_one_frame()
@@ -192,9 +230,9 @@
         }
     } else {
         if (loadalpha) {
-            resultformat = QImage::Format_RGBA8888;
+            resultformat = QImage::Format_ARGB32;
         } else {
-            resultformat = QImage::Format_RGBX8888;
+            resultformat = QImage::Format_RGB32;
         }
     }
     QImage result(m_decoder->image->width, m_decoder->image->height, 
resultformat);
@@ -285,14 +323,16 @@
         rgb.depth = 16;
         rgb.format = AVIF_RGB_FORMAT_RGBA;
 
-        if (!loadalpha) {
-            if (m_decoder->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
-                resultformat = QImage::Format_Grayscale16;
-            }
+        if (!loadalpha && (m_decoder->image->yuvFormat == 
AVIF_PIXEL_FORMAT_YUV400)) {
+            resultformat = QImage::Format_Grayscale16;
         }
     } else {
         rgb.depth = 8;
-        rgb.format = AVIF_RGB_FORMAT_RGBA;
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+        rgb.format = AVIF_RGB_FORMAT_BGRA;
+#else
+        rgb.format = AVIF_RGB_FORMAT_ARGB;
+#endif
 
 #if AVIF_VERSION >= 80400
         if (m_decoder->imageCount > 1) {
@@ -301,14 +341,8 @@
         }
 #endif
 
-        if (loadalpha) {
-            resultformat = QImage::Format_ARGB32;
-        } else {
-            if (m_decoder->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
-                resultformat = QImage::Format_Grayscale8;
-            } else {
-                resultformat = QImage::Format_RGB32;
-            }
+        if (!loadalpha && (m_decoder->image->yuvFormat == 
AVIF_PIXEL_FORMAT_YUV400)) {
+            resultformat = QImage::Format_Grayscale8;
         }
     }
 
@@ -399,13 +433,15 @@
         m_current_image = result.convertToFormat(resultformat);
     }
 
+    m_estimated_dimensions = m_current_image.size();
+
     m_must_jump_to_next_image = false;
     return true;
 }
 
 bool QAVIFHandler::read(QImage *image)
 {
-    if (!ensureParsed()) {
+    if (!ensureOpened()) {
         return false;
     }
 
@@ -792,7 +828,7 @@
 
     switch (option) {
     case Size:
-        return m_current_image.size();
+        return m_estimated_dimensions;
     case Animation:
         if (imageCount() >= 2) {
             return true;
@@ -848,6 +884,14 @@
         return 0;
     }
 
+    if (m_parseState == ParseAvifMetadata) {
+        if (m_decoder->imageCount >= 2) {
+            return -1;
+        } else {
+            return 0;
+        }
+    }
+
     return m_decoder->imageIndex;
 }
 
@@ -857,12 +901,14 @@
         return false;
     }
 
-    if (m_decoder->imageCount < 2) {
-        return true;
-    }
+    if (m_decoder->imageIndex >= 0) {
+        if (m_decoder->imageCount < 2) {
+            return true;
+        }
 
-    if (m_decoder->imageIndex >= m_decoder->imageCount - 1) { // start from 
beginning
-        avifDecoderReset(m_decoder);
+        if (m_decoder->imageIndex >= m_decoder->imageCount - 1) { // start 
from beginning
+            avifDecoderReset(m_decoder);
+        }
     }
 
     avifResult decodeResult = avifDecoderNextImage(m_decoder);
@@ -885,6 +931,7 @@
     }
 
     if (decode_one_frame()) {
+        m_parseState = ParseAvifSuccess;
         return true;
     } else {
         m_parseState = ParseAvifError;
@@ -900,7 +947,7 @@
 
     if (m_decoder->imageCount < 2) { // not an animation
         if (imageNumber == 0) {
-            return true;
+            return ensureOpened();
         } else {
             return false;
         }
@@ -935,6 +982,7 @@
     }
 
     if (decode_one_frame()) {
+        m_parseState = ParseAvifSuccess;
         return true;
     } else {
         m_parseState = ParseAvifError;
@@ -944,7 +992,7 @@
 
 int QAVIFHandler::nextImageDelay() const
 {
-    if (!ensureParsed()) {
+    if (!ensureOpened()) {
         return 0;
     }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kimageformats-5.95.0/src/imageformats/avif_p.h 
new/kimageformats-5.96.0/src/imageformats/avif_p.h
--- old/kimageformats-5.95.0/src/imageformats/avif_p.h  2022-06-04 
10:19:33.000000000 +0200
+++ new/kimageformats-5.96.0/src/imageformats/avif_p.h  2022-07-02 
16:33:58.000000000 +0200
@@ -13,6 +13,7 @@
 #include <QImage>
 #include <QImageIOPlugin>
 #include <QPointF>
+#include <QSize>
 #include <QVariant>
 #include <avif/avif.h>
 #include <qimageiohandler.h>
@@ -45,6 +46,7 @@
 private:
     static QPointF CompatibleChromacity(qreal chrX, qreal chrY);
     bool ensureParsed() const;
+    bool ensureOpened() const;
     bool ensureDecoder();
     bool decode_one_frame();
 
@@ -52,6 +54,7 @@
         ParseAvifError = -1,
         ParseAvifNotParsed = 0,
         ParseAvifSuccess = 1,
+        ParseAvifMetadata = 2,
     };
 
     ParseAvifState m_parseState;
@@ -59,6 +62,7 @@
 
     uint32_t m_container_width;
     uint32_t m_container_height;
+    QSize m_estimated_dimensions;
 
     QByteArray m_rawData;
     avifROData m_rawAvifData;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kimageformats-5.95.0/src/imageformats/jxl.cpp 
new/kimageformats-5.96.0/src/imageformats/jxl.cpp
--- old/kimageformats-5.95.0/src/imageformats/jxl.cpp   2022-06-04 
10:19:33.000000000 +0200
+++ new/kimageformats-5.96.0/src/imageformats/jxl.cpp   2022-07-02 
16:33:58.000000000 +0200
@@ -143,6 +143,10 @@
         return false;
     }
 
+#ifdef KIMG_JXL_API_VERSION
+    JxlDecoderCloseInput(m_decoder);
+#endif
+
     JxlDecoderStatus status = JxlDecoderSubscribeEvents(m_decoder, 
JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME);
     if (status == JXL_DEC_ERROR) {
         qWarning("ERROR: JxlDecoderSubscribeEvents failed");
@@ -482,37 +486,15 @@
         return false;
     }
 
-    void *runner = nullptr;
-    int num_worker_threads = qBound(1, QThread::idealThreadCount(), 64);
-
-    if (num_worker_threads > 1) {
-        runner = JxlThreadParallelRunnerCreate(nullptr, num_worker_threads);
-        if (JxlEncoderSetParallelRunner(encoder, JxlThreadParallelRunner, 
runner) != JXL_ENC_SUCCESS) {
-            qWarning("JxlEncoderSetParallelRunner failed");
-            JxlThreadParallelRunnerDestroy(runner);
-            JxlEncoderDestroy(encoder);
-            return false;
-        }
-    }
-
-    JxlEncoderOptions *encoder_options = JxlEncoderOptionsCreate(encoder, 
nullptr);
-
     if (m_quality > 100) {
         m_quality = 100;
     } else if (m_quality < 0) {
         m_quality = 90;
     }
 
-    JxlEncoderOptionsSetDistance(encoder_options, (100.0f - m_quality) / 
10.0f);
-
-    JxlEncoderOptionsSetLossless(encoder_options, (m_quality == 100) ? 
JXL_TRUE : JXL_FALSE);
-
     JxlBasicInfo output_info;
     JxlEncoderInitBasicInfo(&output_info);
 
-    JxlColorEncoding color_profile;
-    JxlColorEncodingSetToSRGB(&color_profile, JXL_FALSE);
-
     bool convert_color_profile;
     QByteArray iccprofile;
 
@@ -526,7 +508,28 @@
         convert_color_profile = false;
         iccprofile = image.colorSpace().iccProfile();
         if (iccprofile.size() > 0 || m_quality == 100) {
-            output_info.uses_original_profile = 1;
+            output_info.uses_original_profile = JXL_TRUE;
+        }
+    }
+
+    if (save_depth == 16 && (image.hasAlphaChannel() || 
output_info.uses_original_profile)) {
+        output_info.have_container = JXL_TRUE;
+        JxlEncoderUseContainer(encoder, JXL_TRUE);
+#ifdef KIMG_JXL_API_VERSION
+        JxlEncoderSetCodestreamLevel(encoder, 10);
+#endif
+    }
+
+    void *runner = nullptr;
+    int num_worker_threads = qBound(1, QThread::idealThreadCount(), 64);
+
+    if (num_worker_threads > 1) {
+        runner = JxlThreadParallelRunnerCreate(nullptr, num_worker_threads);
+        if (JxlEncoderSetParallelRunner(encoder, JxlThreadParallelRunner, 
runner) != JXL_ENC_SUCCESS) {
+            qWarning("JxlEncoderSetParallelRunner failed");
+            JxlThreadParallelRunnerDestroy(runner);
+            JxlEncoderDestroy(encoder);
+            return false;
         }
     }
 
@@ -537,7 +540,6 @@
     pixel_format.endianness = JXL_NATIVE_ENDIAN;
     pixel_format.align = 0;
 
-    output_info.intensity_target = 255.0f;
     output_info.orientation = JXL_ORIENT_IDENTITY;
     output_info.num_color_channels = 3;
     output_info.animation.tps_numerator = 10;
@@ -615,6 +617,9 @@
             return false;
         }
     } else {
+        JxlColorEncoding color_profile;
+        JxlColorEncodingSetToSRGB(&color_profile, JXL_FALSE);
+
         status = JxlEncoderSetColorEncoding(encoder, &color_profile);
         if (status != JXL_ENC_SUCCESS) {
             qWarning("JxlEncoderSetColorEncoding failed!");
@@ -626,6 +631,20 @@
         }
     }
 
+#ifdef KIMG_JXL_API_VERSION
+    JxlEncoderFrameSettings *encoder_options = 
JxlEncoderFrameSettingsCreate(encoder, nullptr);
+
+    JxlEncoderSetFrameDistance(encoder_options, (100.0f - m_quality) / 10.0f);
+
+    JxlEncoderSetFrameLossless(encoder_options, (m_quality == 100) ? JXL_TRUE 
: JXL_FALSE);
+#else
+    JxlEncoderOptions *encoder_options = JxlEncoderOptionsCreate(encoder, 
nullptr);
+
+    JxlEncoderOptionsSetDistance(encoder_options, (100.0f - m_quality) / 
10.0f);
+
+    JxlEncoderOptionsSetLossless(encoder_options, (m_quality == 100) ? 
JXL_TRUE : JXL_FALSE);
+#endif
+
     if (image.hasAlphaChannel() || ((save_depth == 8) && (xsize % 4 == 0))) {
         status = JxlEncoderAddImageFrame(encoder_options, &pixel_format, (void 
*)tmpimage.constBits(), buffer_size);
     } else {
@@ -915,6 +934,10 @@
         return false;
     }
 
+#ifdef KIMG_JXL_API_VERSION
+    JxlDecoderCloseInput(m_decoder);
+#endif
+
     if (m_basicinfo.uses_original_profile) {
         if (JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_FULL_IMAGE) != 
JXL_DEC_SUCCESS) {
             qWarning("ERROR: JxlDecoderSubscribeEvents failed");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kimageformats-5.95.0/src/imageformats/psd.cpp 
new/kimageformats-5.96.0/src/imageformats/psd.cpp
--- old/kimageformats-5.95.0/src/imageformats/psd.cpp   2022-06-04 
10:19:33.000000000 +0200
+++ new/kimageformats-5.96.0/src/imageformats/psd.cpp   2022-07-02 
16:33:58.000000000 +0200
@@ -101,6 +101,10 @@
 
 using PSDImageResourceSection = QHash<quint16, PSDImageResourceBlock>;
 
+struct PSDLayerAndMaskSection {
+    qint16 layerCount = 0;
+};
+
 /*!
  * \brief fixedPointToDouble
  * Converts a fixed point number to floating point one.
@@ -112,6 +116,28 @@
     return (i+d);
 }
 
+static bool skip_section(QDataStream &s, bool psb = false)
+{
+    qint64 section_length;
+    if (!psb) {
+        quint32 tmp;
+        s >> tmp;
+        section_length = tmp;
+    }
+    else {
+        s >> section_length;
+    }
+
+    // Skip mode data.
+    for (qint32 i32 = 0; section_length; section_length -= i32) {
+        i32 = std::min(section_length, 
qint64(std::numeric_limits<qint32>::max()));
+        i32 = s.skipRawData(i32);
+        if (i32 < 1)
+            return false;
+    }
+    return true;
+}
+
 /*!
  * \brief readPascalString
  * Reads the Pascal string as defined in the PSD specification.
@@ -218,12 +244,12 @@
         size -= sizeof(dataSize);
         // NOTE: Qt device::read() and QDataStream::readRawData() could read 
less data than specified.
         //       The read code should be improved.
-        if(auto dev = s.device())
+        if (auto dev = s.device())
             irb.data = dev->read(dataSize);
         auto read = irb.data.size();
         if (read > 0)
             size -= read;
-        if (read != dataSize) {
+        if (quint32(read) != dataSize) {
             qDebug() << "Image Resource Block Read Error!";
             *ok = false;
             break;
@@ -250,6 +276,46 @@
     return irs;
 }
 
+
+PSDLayerAndMaskSection readLayerAndMaskSection(QDataStream &s, bool isPsb, 
bool *ok = nullptr)
+{
+    PSDLayerAndMaskSection lms;
+
+    bool tmp = true;
+    if (ok == nullptr)
+        ok = &tmp;
+    *ok = true;
+
+    // try to read layerCount: if less than zero, means that there is an alpha 
channel
+    if (auto device = s.device()) {
+        device->startTransaction();
+        qint64 size = 0;
+        if (isPsb) {
+            qint64 tmpSize;
+            s >> tmpSize; // global size
+            if (tmpSize >= 8)
+                s >> size; // layer info size
+        }
+        else {
+            quint32 tmpSize;
+            s >> tmpSize; // global size
+            if (tmpSize >= 4) {
+                s >> tmpSize; // layer info size
+                size = tmpSize;
+            }
+        }
+
+        if (s.status() == QDataStream::Ok) {
+            if (size >= 2)
+                s >> lms.layerCount;
+        }
+        device->rollbackTransaction();
+    }
+
+    *ok = skip_section(s, isPsb);
+    return lms;
+}
+
 /*!
  * \brief readColorModeDataSection
  * Read the color mode section
@@ -424,55 +490,65 @@
     return s;
 }
 
-// Check that the header is a valid PSD.
+// Check that the header is a valid PSD (as written in the PSD specification).
 static bool IsValid(const PSDHeader &header)
 {
     if (header.signature != 0x38425053) { // '8BPS'
+        //qDebug() << "PSD header: invalid signature" << header.signature;
         return false;
     }
-    return true;
-}
-
-// Check that the header is supported.
-static bool IsSupported(const PSDHeader &header)
-{
     if (header.version != 1 && header.version != 2) {
+        qDebug() << "PSD header: invalid version" << header.version;
         return false;
     }
     if (header.depth != 8 &&
         header.depth != 16 &&
         header.depth != 32 &&
         header.depth != 1) {
+        qDebug() << "PSD header: invalid depth" << header.depth;
         return false;
     }
     if (header.color_mode != CM_RGB &&
         header.color_mode != CM_GRAYSCALE &&
         header.color_mode != CM_INDEXED &&
         header.color_mode != CM_DUOTONE &&
+        header.color_mode != CM_CMYK &&
+        header.color_mode != CM_LABCOLOR &&
+        header.color_mode != CM_MULTICHANNEL &&
         header.color_mode != CM_BITMAP) {
+        qDebug() << "PSD header: invalid color mode" << header.color_mode;
+        return false;
+    }
+    if (header.channel_count < 1 || header.channel_count > 56) {
+        qDebug() << "PSD header: invalid number of channels" << 
header.channel_count;
+        return false;
+    }
+    if (header.width > 300000 || header.height > 300000) {
+        qDebug() << "PSD header: invalid image size" << header.width << "x" << 
header.height;
         return false;
     }
     return true;
 }
 
-static bool skip_section(QDataStream &s, bool psb = false)
+// Check that the header is supported by this plugin.
+static bool IsSupported(const PSDHeader &header)
 {
-    qint64 section_length;
-    if (!psb) {
-        quint32 tmp;
-        s >> tmp;
-        section_length = tmp;
+    if (header.version != 1 && header.version != 2) {
+        return false;
     }
-    else {
-        s >> section_length;
+    if (header.depth != 8 &&
+        header.depth != 16 &&
+        header.depth != 32 &&
+        header.depth != 1) {
+        return false;
     }
-
-    // Skip mode data.
-    for (qint32 i32 = 0; section_length; section_length -= i32) {
-        i32 = std::min(section_length, 
qint64(std::numeric_limits<qint32>::max()));
-        i32 = s.skipRawData(i32);
-        if (i32 < 1)
-            return false;
+    if (header.color_mode != CM_RGB &&
+        header.color_mode != CM_GRAYSCALE &&
+        header.color_mode != CM_INDEXED &&
+        header.color_mode != CM_DUOTONE &&
+        header.color_mode != CM_CMYK &&
+        header.color_mode != CM_BITMAP) {
+        return false;
     }
     return true;
 }
@@ -525,7 +601,7 @@
  * \param header The PSD header.
  * \return The Qt image format.
  */
-static QImage::Format imageFormat(const PSDHeader &header)
+static QImage::Format imageFormat(const PSDHeader &header, qint32 alpha)
 {
     if (header.channel_count == 0) {
         return QImage::Format_Invalid;
@@ -539,6 +615,12 @@
         else
             format = header.channel_count < 4 ? QImage::Format_RGB888 : 
QImage::Format_RGBA8888;
         break;
+    case CM_CMYK:   // PSD supports CMYK 8-bits and 16-bits only
+        if (header.depth == 16)
+            format = header.channel_count < 5 || alpha >= 0 ? 
QImage::Format_RGBX64 : QImage::Format_RGBA64;
+        else if (header.depth == 8)
+            format = header.channel_count < 5 || alpha >= 0 ? 
QImage::Format_RGB888 : QImage::Format_RGBA8888;
+        break;
     case CM_GRAYSCALE:
     case CM_DUOTONE:
         format = header.depth == 8 ? QImage::Format_Grayscale8 : 
QImage::Format_Grayscale16;
@@ -597,24 +679,33 @@
 #endif
 }
 
+inline qint32 xchg(qint32 v) {
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+    return qint32( (quint32(v)>>24) | ((quint32(v) & 0x00FF0000)>>8) | 
((quint32(v) & 0x0000FF00)<<8) | (quint32(v)<<24) );
+#else
+    return v;  // never tested
+#endif
+}
+
 template<class T>
-inline void planarToChunchy(uchar *target, const char* source, qint32 width, 
qint32 c, qint32 cn)
+inline void planarToChunchy(uchar *target, const char *source, qint32 width, 
qint32 c, qint32 cn)
 {
     auto s = reinterpret_cast<const T*>(source);
     auto t = reinterpret_cast<T*>(target);
-    for (qint32 x = 0; x < width; ++x)
+    for (qint32 x = 0; x < width; ++x) {
         t[x*cn+c] = xchg(s[x]);
+    }
 }
 
-template<class T>
-inline void planarToChunchyFloat(uchar *target, const char* source, qint32 
width, qint32 c, qint32 cn)
+template<class T, T min = 0, T max = 1>
+inline void planarToChunchyFloat(uchar *target, const char *source, qint32 
width, qint32 c, qint32 cn)
 {
     auto s = reinterpret_cast<const T*>(source);
     auto t = reinterpret_cast<quint16*>(target);
     for (qint32 x = 0; x < width; ++x) {
         auto tmp = xchg(s[x]);
-        t[x*cn+c] = std::min(quint16(*reinterpret_cast<float*>(&tmp) * 
std::numeric_limits<quint16>::max() + 0.5),
-                             std::numeric_limits<quint16>::max());
+        auto ftmp = (*reinterpret_cast<float*>(&tmp) - double(min)) / 
(double(max) - double(min));
+        t[x*cn+c] = quint16(std::min(ftmp * 
std::numeric_limits<quint16>::max() + 0.5, 
double(std::numeric_limits<quint16>::max())));
     }
 }
 
@@ -622,8 +713,60 @@
 {
     auto s = reinterpret_cast<const quint8*>(source);
     auto t = reinterpret_cast<quint8*>(target);
-    for (qint32 x = 0; x < bytes; ++x)
+    for (qint32 x = 0; x < bytes; ++x) {
         t[x] = ~s[x];
+    }
+}
+
+template<class T>
+inline void cmykToRgb(uchar *target, qint32 targetChannels, const char 
*source, qint32 sourceChannels, qint32 width, bool noAlpha)
+{
+    auto s = reinterpret_cast<const T*>(source);
+    auto t = reinterpret_cast<T*>(target);
+    auto max = double(std::numeric_limits<T>::max());
+
+    if (sourceChannels < 4) {
+        qDebug() << "cmykToRgb: image is not a valid CMYK!";
+        return;
+    }
+
+    for (qint32 w = 0; w < width; ++w) {
+        auto ps = s + sourceChannels * w;
+        auto C = 1 - *(ps + 0) / double(max);
+        auto M = 1 - *(ps + 1) / double(max);
+        auto Y = 1 - *(ps + 2) / double(max);
+        auto K = 1 - *(ps + 3) / double(max);
+
+        auto pt = t + targetChannels * w;
+        *(pt + 0) = T(std::min(max - (C * (1 - K) + K) * max + 0.5, max));
+        *(pt + 1) = T(std::min(max - (M * (1 - K) + K) * max + 0.5, max));
+        *(pt + 2) = T(std::min(max - (Y * (1 - K) + K) * max + 0.5, max));
+        if (targetChannels == 4) {
+            if (sourceChannels >= 5 && !noAlpha)
+                *(pt + 3) = *(ps + 4);
+            else
+                *(pt + 3) = std::numeric_limits<T>::max();
+        }
+    }
+}
+
+bool readChannel(QByteArray& target, QDataStream &stream, quint32 
compressedSize, quint16 compression)
+{
+    if (compression) {
+        QByteArray tmp;
+        tmp.resize(compressedSize);
+        if (stream.readRawData(tmp.data(), tmp.size()) != tmp.size()) {
+            return false;
+        }
+        if (decompress(tmp.data(), tmp.size(), target.data(), target.size()) < 
0) {
+            return false;
+        }
+    }
+    else if (stream.readRawData(target.data(), target.size()) != 
target.size()) {
+        return false;
+    }
+
+    return stream.status() == QDataStream::Ok;
 }
 
 // Load the PSD image.
@@ -653,7 +796,8 @@
     }
 
     // Layer and Mask section
-    if (!skip_section(stream, isPsb)) {
+    auto lms = readLayerAndMaskSection(stream, isPsb, &ok);
+    if (!ok) {
         qDebug() << "Error while skipping Layer and Mask section";
         return false;
     }
@@ -669,7 +813,11 @@
         return false;
     }
 
-    const QImage::Format format = imageFormat(header);
+    // Try to identify the nature of spots: note that this is just one of many 
ways to identify the presence
+    // of alpha channels: should work in most cases where colorspaces != 
RGB/Gray
+    auto alpha = lms.layerCount; // < 0 alpha present, > 0 spots are not 
alpha, 0 does not decide
+
+    const QImage::Format format = imageFormat(header, alpha);
     if (format == QImage::Format_Invalid) {
         qWarning() << "Unsupported image format. color_mode:" << 
header.color_mode << "depth:" << header.depth << "channel_count:" << 
header.channel_count;
         return false;
@@ -697,7 +845,7 @@
 
     QVector<quint32> strides(header.height * header.channel_count, raw_count);
     // Read the compressed stride sizes
-    if (compression)
+    if (compression) {
         for (auto&& v : strides) {
             if (isPsb) {
                 stream >> v;
@@ -707,46 +855,89 @@
             stream >> tmp;
             v = tmp;
         }
+    }
+    // calculate the absolute file positions of each stride (required when a 
colorspace conversion should be done)
+    auto device = stream.device();
+    QVector<quint64> stridePositions(strides.size());
+    if (!stridePositions.isEmpty()) {
+        stridePositions[0] = device->pos();
+    }
+    for (qsizetype i = 1, n = stridePositions.size(); i < n; ++i) {
+        stridePositions[i] = stridePositions[i-1] + strides.at(i-1);
+    }
 
     // Read the image
     QByteArray rawStride;
     rawStride.resize(raw_count);
-    for (qint32 c = 0; c < channel_num; ++c) {
-        for(qint32 y = 0, h = header.height; y < h; ++y) {
-            auto&& strideSize = strides.at(c*qsizetype(h)+y);
-            if (compression) {
-                QByteArray tmp;
-                tmp.resize(strideSize);
-                if (stream.readRawData(tmp.data(), tmp.size()) != tmp.size()) {
-                    qDebug() << "Error while reading the stream of channel" << 
c << "line" << y;
+
+    if (header.color_mode == CM_CMYK || header.color_mode == CM_LABCOLOR || 
header.color_mode == CM_MULTICHANNEL) {
+        // In order to make a colorspace transformation, we need all channels 
of a scanline
+        QByteArray psdScanline;
+        psdScanline.resize(qsizetype(header.width * std::min(header.depth, 
quint16(16)) * header.channel_count + 7) / 8);
+        for (qint32 y = 0, h = header.height; y < h; ++y) {
+            for (qint32 c = 0; c < header.channel_count; ++c) {
+                auto strideNumber = c * qsizetype(h) + y;
+                if (!device->seek(stridePositions.at(strideNumber))) {
+                    qDebug() << "Error while seeking the stream of channel" << 
c << "line" << y;
                     return false;
                 }
-                if (decompress(tmp.data(), tmp.size(), rawStride.data(), 
rawStride.size()) < 0) {
-                    qDebug() << "Error while decompressing the channel" << c 
<< "line" << y;
+                auto&& strideSize = strides.at(strideNumber);
+                if (!readChannel(rawStride, stream, strideSize, compression)) {
+                    qDebug() << "Error while reading the stream of channel" << 
c << "line" << y;
                     return false;
                 }
+
+                auto scanLine = reinterpret_cast<unsigned 
char*>(psdScanline.data());
+                if (header.depth == 8) {
+                    planarToChunchy<quint8>(scanLine, rawStride.data(), 
header.width, c, header.channel_count);
+                }
+                else if (header.depth == 16) {
+                    planarToChunchy<quint16>(scanLine, rawStride.data(), 
header.width, c, header.channel_count);
+                }
+                else if (header.depth == 32) { // Not currently used
+                    // LAB float uses LAB real values: L(0 to 100), a/b(-128 
to 127)
+                    if (header.color_mode == CM_LABCOLOR && c == 0)
+                        planarToChunchyFloat<quint32, 0, 100>(scanLine, 
rawStride.data(), header.width, c, header.channel_count);
+                    else if (header.color_mode == CM_LABCOLOR && c < 3)
+                        planarToChunchyFloat<qint32, -128, 127>(scanLine, 
rawStride.data(), header.width, c, header.channel_count);
+                    else // RGB, gray, spots, etc...
+                        planarToChunchyFloat<quint32>(scanLine, 
rawStride.data(), header.width, c, header.channel_count);
+                }
+            }
+
+            // Conversion to RGB
+            if (header.color_mode == CM_CMYK) {
+                if (header.depth == 8)
+                    cmykToRgb<quint8>(img.scanLine(y), imgChannels, 
psdScanline.data(), header.channel_count, header.width, alpha >= 0);
+                else
+                    cmykToRgb<quint16>(img.scanLine(y), imgChannels, 
psdScanline.data(), header.channel_count, header.width, alpha >= 0);
             }
-            else {
-                if (stream.readRawData(rawStride.data(), rawStride.size()) != 
rawStride.size()) {
+        }
+    }
+    else {
+        // Linear read (no position jumps): optimized code usable only for the 
colorspaces supported by QImage
+        for (qint32 c = 0; c < channel_num; ++c) {
+            for (qint32 y = 0, h = header.height; y < h; ++y) {
+                auto&& strideSize = strides.at(c * qsizetype(h) + y);
+                if (!readChannel(rawStride, stream, strideSize, compression)) {
                     qDebug() << "Error while reading the stream of channel" << 
c << "line" << y;
                     return false;
                 }
-            }
 
-            if (stream.status() != QDataStream::Ok) {
-                qDebug() << "Stream read error" << stream.status();
-                return false;
+                auto scanLine = img.scanLine(y);
+                if (header.depth == 1) {        // Bitmap
+                    monoInvert(scanLine, rawStride.data(), 
std::min(rawStride.size(), img.bytesPerLine()));
+                }
+                else if (header.depth == 8) {   // 8-bits images: Indexed, 
Grayscale, RGB/RGBA
+                    planarToChunchy<quint8>(scanLine, rawStride.data(), 
header.width, c, imgChannels);
+                }
+                else if (header.depth == 16) {  // 16-bits integer images: 
Grayscale, RGB/RGBA
+                    planarToChunchy<quint16>(scanLine, rawStride.data(), 
header.width, c, imgChannels);
+                }
+                else if (header.depth == 32) {  // 32-bits float images: 
Grayscale, RGB/RGBA (coverted to equivalent integer 16-bits)
+                    planarToChunchyFloat<quint32>(scanLine, rawStride.data(), 
header.width, c, imgChannels);
+                }
             }
-
-            auto scanLine = img.scanLine(y);
-            if (header.depth == 1)          // Bitmap
-                monoInvert(scanLine, rawStride.data(), 
std::min(rawStride.size(), img.bytesPerLine()));
-            else if (header.depth == 8)     // 8-bits images: Indexed, 
Grayscale, RGB/RGBA
-                planarToChunchy<quint8>(scanLine, rawStride.data(), 
header.width, c, imgChannels);
-            else if (header.depth == 16)    // 16-bits integer images: 
Grayscale, RGB/RGBA
-                planarToChunchy<quint16>(scanLine, rawStride.data(), 
header.width, c, imgChannels);
-            else if (header.depth == 32)    // 32-bits float images: 
Grayscale, RGB/RGBA (coverted to equivalent integer 16-bits)
-                planarToChunchyFloat<quint32>(scanLine, rawStride.data(), 
header.width, c, imgChannels);
         }
     }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kimageformats-5.95.0/src/imageformats/xcf.cpp 
new/kimageformats-5.96.0/src/imageformats/xcf.cpp
--- old/kimageformats-5.95.0/src/imageformats/xcf.cpp   2022-06-04 
10:19:33.000000000 +0200
+++ new/kimageformats-5.96.0/src/imageformats/xcf.cpp   2022-07-02 
16:33:58.000000000 +0200
@@ -135,7 +135,7 @@
         PROP_SAMPLE_POINTS = 39,
         MAX_SUPPORTED_PROPTYPE, // should always be at the end so its value is 
last + 1
     };
-    Q_ENUM(PropType);
+    Q_ENUM(PropType)
 
     //! Compression type used in layer tiles.
     enum XcfCompressionType {
@@ -145,7 +145,7 @@
         COMPRESS_ZLIB = 2, /* unused */
         COMPRESS_FRACTAL = 3, /* unused */
     };
-    Q_ENUM(XcfCompressionType);
+    Q_ENUM(XcfCompressionType)
 
     enum LayerModeType {
         GIMP_LAYER_MODE_NORMAL_LEGACY,
@@ -212,7 +212,7 @@
         GIMP_LAYER_MODE_PASS_THROUGH,
         GIMP_LAYER_MODE_COUNT,
     };
-    Q_ENUM(LayerModeType);
+    Q_ENUM(LayerModeType)
 
     //! Type of individual layers in an XCF file.
     enum GimpImageType {
@@ -223,7 +223,7 @@
         INDEXED_GIMAGE,
         INDEXEDA_GIMAGE,
     };
-    Q_ENUM(GimpImageType);
+    Q_ENUM(GimpImageType)
 
     //! Type of individual layers in an XCF file.
     enum GimpColorSpace {

Reply via email to