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-03-14 19:34:32 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kimageformats (Old) and /work/SRC/openSUSE:Factory/.kimageformats.new.25692 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kimageformats" Mon Mar 14 19:34:32 2022 rev:104 rq:961252 version:5.92.0 Changes: -------- --- /work/SRC/openSUSE:Factory/kimageformats/kimageformats.changes 2022-02-24 18:21:51.494683870 +0100 +++ /work/SRC/openSUSE:Factory/.kimageformats.new.25692/kimageformats.changes 2022-03-14 19:35:58.502047873 +0100 @@ -1,0 +2,13 @@ +Mon Mar 7 09:26:59 UTC 2022 - Christophe Giboudeaux <[email protected]> + +- Update to 5.92.0 + * New feature release + * For more details please see: + * https://kde.org/announcements/frameworks/5/5.92.0 +- Changes since 5.91.0: + * Add Qt6 Android CI + * Add write tests for heif/avif/jxl + * jxl: encoding improvements + * avif: adjust dimension and memory limits + +------------------------------------------------------------------- Old: ---- kimageformats-5.91.0.tar.xz kimageformats-5.91.0.tar.xz.sig New: ---- kimageformats-5.92.0.tar.xz kimageformats-5.92.0.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kimageformats.spec ++++++ --- /var/tmp/diff_new_pack.IYbKnk/_old 2022-03-14 19:35:59.134048631 +0100 +++ /var/tmp/diff_new_pack.IYbKnk/_new 2022-03-14 19:35:59.138048636 +0100 @@ -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.91 +%define _tar_path 5.92 # 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.91.0 +Version: 5.92.0 Release: 0 Summary: Image format plugins for Qt License: LGPL-2.1-or-later ++++++ kimageformats-5.91.0.tar.xz -> kimageformats-5.92.0.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kimageformats-5.91.0/.gitlab-ci.yml new/kimageformats-5.92.0/.gitlab-ci.yml --- old/kimageformats-5.91.0/.gitlab-ci.yml 2022-02-05 16:14:16.000000000 +0100 +++ new/kimageformats-5.92.0/.gitlab-ci.yml 2022-03-05 12:15:00.000000000 +0100 @@ -6,3 +6,4 @@ - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd.yml - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml + - https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android-qt6.yml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kimageformats-5.91.0/CMakeLists.txt new/kimageformats-5.92.0/CMakeLists.txt --- old/kimageformats-5.91.0/CMakeLists.txt 2022-02-05 16:14:16.000000000 +0100 +++ new/kimageformats-5.92.0/CMakeLists.txt 2022-03-05 12:15:00.000000000 +0100 @@ -3,7 +3,7 @@ project(KImageFormats) include(FeatureSummary) -find_package(ECM 5.91.0 NO_MODULE) +find_package(ECM 5.92.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) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kimageformats-5.91.0/autotests/CMakeLists.txt new/kimageformats-5.92.0/autotests/CMakeLists.txt --- old/kimageformats-5.91.0/autotests/CMakeLists.txt 2022-02-05 16:14:16.000000000 +0100 +++ new/kimageformats-5.92.0/autotests/CMakeLists.txt 2022-03-05 12:15:00.000000000 +0100 @@ -30,6 +30,12 @@ endmacro() macro(kimageformats_write_tests) + cmake_parse_arguments(KIF_RT "" "FUZZ" "" ${ARGN}) + set(_fuzzarg) + if (KIF_RT_FUZZ) + set(_fuzzarg -f ${KIF_RT_FUZZ}) + endif() + if (NOT TARGET writetest) add_executable(writetest writetest.cpp) target_link_libraries(writetest Qt${QT_MAJOR_VERSION}::Gui) @@ -37,16 +43,22 @@ PRIVATE IMAGEDIR="${CMAKE_CURRENT_SOURCE_DIR}/write") ecm_mark_as_test(writetest) endif() - foreach(_testname ${ARGN}) + foreach(_testname ${KIF_RT_UNPARSED_ARGUMENTS}) string(REGEX MATCH "-lossless$" _is_lossless "${_testname}") + string(REGEX MATCH "-nodatacheck" _is_no_data_check "${_testname}") unset(lossless_arg) + unset(no_data_check_arg) if (_is_lossless) set(lossless_arg "--lossless") string(REGEX REPLACE "-lossless$" "" _testname "${_testname}") endif() + if (_is_no_data_check) + set(no_data_check_arg "--no-data-check") + string(REGEX REPLACE "-nodatacheck$" "" _testname "${_testname}") + endif() add_test( NAME kimageformats-write-${_testname} - COMMAND writetest ${lossless_arg} ${_testname} + COMMAND writetest ${lossless_arg} ${no_data_check_arg} ${_fuzzarg} ${_testname} ) endforeach(_testname) endmacro() @@ -74,18 +86,29 @@ kimageformats_read_tests( avif ) + # because the plug-ins use RGB->YUV conversion which sometimes results in 1 value difference. + kimageformats_write_tests(FUZZ 1 + avif-nodatacheck-lossless + ) endif() if (LibHeif_FOUND) kimageformats_read_tests( heif ) + # because the plug-ins use RGB->YUV conversion which sometimes results in 1 value difference. + kimageformats_write_tests(FUZZ 1 + heif-nodatacheck-lossless + ) endif() if (LibJXL_FOUND AND LibJXLThreads_FOUND) kimageformats_read_tests( jxl ) + kimageformats_write_tests( + jxl-nodatacheck-lossless + ) endif() # Allow some fuzziness when reading this formats, to allow for diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kimageformats-5.91.0/autotests/fuzzyeq.cpp new/kimageformats-5.92.0/autotests/fuzzyeq.cpp --- old/kimageformats-5.91.0/autotests/fuzzyeq.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/kimageformats-5.92.0/autotests/fuzzyeq.cpp 2022-03-05 12:15:00.000000000 +0100 @@ -0,0 +1,37 @@ +/* + SPDX-FileCopyrightText: 2014 Alex Merry <[email protected]> + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +template<class Trait> +static bool fuzzyeq(const QImage &im1, const QImage &im2, uchar fuzziness) +{ + Q_ASSERT(im1.format() == im2.format()); + Q_ASSERT(im1.depth() == 24 || im1.depth() == 32 || im1.depth() == 64); + + const int height = im1.height(); + const int width = im1.width(); + for (int i = 0; i < height; ++i) { + const Trait *line1 = reinterpret_cast<const Trait *>(im1.scanLine(i)); + const Trait *line2 = reinterpret_cast<const Trait *>(im2.scanLine(i)); + for (int j = 0; j < width; ++j) { + if (line1[j] > line2[j]) { + if (line1[j] - line2[j] > fuzziness) { + return false; + } + } else { + if (line2[j] - line1[j] > fuzziness) { + return false; + } + } + } + } + return true; +} + +// allow each byte to be different by up to 1, to allow for rounding errors +static bool fuzzyeq(const QImage &im1, const QImage &im2, uchar fuzziness) +{ + return (im1.depth() == 64) ? fuzzyeq<quint16>(im1, im2, fuzziness) : fuzzyeq<quint8>(im1, im2, fuzziness); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kimageformats-5.91.0/autotests/readtest.cpp new/kimageformats-5.92.0/autotests/readtest.cpp --- old/kimageformats-5.91.0/autotests/readtest.cpp 2022-02-05 16:14:16.000000000 +0100 +++ new/kimageformats-5.92.0/autotests/readtest.cpp 2022-03-05 12:15:00.000000000 +0100 @@ -16,6 +16,8 @@ #include "../tests/format-enum.h" +#include "fuzzyeq.cpp" + static void writeImageData(const char *name, const QString &filename, const QImage &image) { QFile file(filename); @@ -31,38 +33,6 @@ } } -template<class Trait> -static bool fuzzyeq(const QImage &im1, const QImage &im2, uchar fuzziness) -{ - Q_ASSERT(im1.format() == im2.format()); - Q_ASSERT(im1.depth() == 24 || im1.depth() == 32 || im1.depth() == 64); - - const int height = im1.height(); - const int width = im1.width(); - for (int i = 0; i < height; ++i) { - const Trait *line1 = reinterpret_cast<const Trait *>(im1.scanLine(i)); - const Trait *line2 = reinterpret_cast<const Trait *>(im2.scanLine(i)); - for (int j = 0; j < width; ++j) { - if (line1[j] > line2[j]) { - if (line1[j] - line2[j] > fuzziness) { - return false; - } - } else { - if (line2[j] - line1[j] > fuzziness) { - return false; - } - } - } - } - return true; -} - -// allow each byte to be different by up to 1, to allow for rounding errors -static bool fuzzyeq(const QImage &im1, const QImage &im2, uchar fuzziness) -{ - return (im1.depth() == 64) ? fuzzyeq<quint16>(im1, im2, fuzziness) : fuzzyeq<quint8>(im1, im2, fuzziness); -} - // Returns the original format if we support, or returns // format which we preferred to use for `fuzzyeq()`. // We do only support formats with 8-bits/16-bits pre pixel. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kimageformats-5.91.0/autotests/writetest.cpp new/kimageformats-5.92.0/autotests/writetest.cpp --- old/kimageformats-5.91.0/autotests/writetest.cpp 2022-02-05 16:14:16.000000000 +0100 +++ new/kimageformats-5.92.0/autotests/writetest.cpp 2022-03-05 12:15:00.000000000 +0100 @@ -16,6 +16,8 @@ #include <QImageWriter> #include <QTextStream> +#include "fuzzyeq.cpp" + int main(int argc, char **argv) { QCoreApplication app(argc, argv); @@ -31,7 +33,13 @@ parser.addPositionalArgument(QStringLiteral("format"), QStringLiteral("format to test.")); QCommandLineOption lossless(QStringList() << QStringLiteral("l") << QStringLiteral("lossless"), QStringLiteral("Check that reading back the data gives the same image.")); + QCommandLineOption ignoreDataCheck({QStringLiteral("no-data-check")}, QStringLiteral("Don't check that write data is exactly the same.")); + QCommandLineOption fuzz(QStringList() << QStringLiteral("f") << QStringLiteral("fuzz"), + QStringLiteral("Allow for some deviation in ARGB data."), + QStringLiteral("max")); parser.addOption(lossless); + parser.addOption(ignoreDataCheck); + parser.addOption(fuzz); parser.process(app); @@ -44,11 +52,26 @@ parser.showHelp(1); } + uchar fuzziness = 0; + if (parser.isSet(fuzz)) { + bool ok; + uint fuzzarg = parser.value(fuzz).toUInt(&ok); + if (!ok || fuzzarg > 255) { + QTextStream(stderr) << "Error: max fuzz argument must be a number between 0 and 255\n"; + parser.showHelp(1); + } + fuzziness = uchar(fuzzarg); + } + QString suffix = args.at(0); QByteArray format = suffix.toLatin1(); QDir imgdir(QStringLiteral(IMAGEDIR)); - imgdir.setNameFilters(QStringList(QLatin1String("*.") + suffix)); + if (parser.isSet(ignoreDataCheck)) { + imgdir.setNameFilters({QLatin1String("*.png")}); + } else { + imgdir.setNameFilters(QStringList(QLatin1String("*.") + suffix)); + } imgdir.setFilter(QDir::Files); int passed = 0; @@ -58,8 +81,13 @@ << "Starting basic write tests for " << suffix << " images *********\n"; const QFileInfoList lstImgDir = imgdir.entryInfoList(); for (const QFileInfo &fi : lstImgDir) { - int suffixPos = fi.filePath().count() - suffix.count(); - QString pngfile = fi.filePath().replace(suffixPos, suffix.count(), QStringLiteral("png")); + QString pngfile; + if (parser.isSet(ignoreDataCheck)) { + pngfile = fi.filePath(); + } else { + int suffixPos = fi.filePath().count() - suffix.count(); + pngfile = fi.filePath().replace(suffixPos, suffix.count(), QStringLiteral("png")); + } QString pngfilename = QFileInfo(pngfile).fileName(); QImageReader pngReader(pngfile, "png"); @@ -70,29 +98,13 @@ continue; } - QFile expFile(fi.filePath()); - if (!expFile.open(QIODevice::ReadOnly)) { - QTextStream(stdout) << "ERROR: " << fi.fileName() << ": could not open " << fi.fileName() << ": " << expFile.errorString() << "\n"; - ++failed; - continue; - } - QByteArray expData = expFile.readAll(); - if (expData.isEmpty()) { - // check if there was actually anything to read - expFile.reset(); - char buf[1]; - qint64 result = expFile.read(buf, 1); - if (result < 0) { - QTextStream(stdout) << "ERROR: " << fi.fileName() << ": could not load " << fi.fileName() << ": " << expFile.errorString() << "\n"; - ++failed; - continue; - } - } - QByteArray writtenData; { QBuffer buffer(&writtenData); QImageWriter imgWriter(&buffer, format.constData()); + if (parser.isSet(lossless)) { + imgWriter.setQuality(100); + } if (!imgWriter.write(pngImage)) { QTextStream(stdout) << "FAIL : " << fi.fileName() << ": failed to write image data\n"; ++failed; @@ -100,10 +112,31 @@ } } - if (expData != writtenData) { - QTextStream(stdout) << "FAIL : " << fi.fileName() << ": written data differs from " << fi.fileName() << "\n"; - ++failed; - continue; + if (!parser.isSet(ignoreDataCheck)) { + QFile expFile(fi.filePath()); + if (!expFile.open(QIODevice::ReadOnly)) { + QTextStream(stdout) << "ERROR: " << fi.fileName() << ": could not open " << fi.fileName() << ": " << expFile.errorString() << "\n"; + ++failed; + continue; + } + QByteArray expData = expFile.readAll(); + if (expData.isEmpty()) { + // check if there was actually anything to read + expFile.reset(); + char buf[1]; + qint64 result = expFile.read(buf, 1); + if (result < 0) { + QTextStream(stdout) << "ERROR: " << fi.fileName() << ": could not load " << fi.fileName() << ": " << expFile.errorString() << "\n"; + ++failed; + continue; + } + } + + if (expData != writtenData) { + QTextStream(stdout) << "FAIL : " << fi.fileName() << ": written data differs from " << fi.fileName() << "\n"; + ++failed; + continue; + } } QImage reReadImage; @@ -119,8 +152,18 @@ } if (parser.isSet(lossless)) { - if (pngImage != reReadImage) { + if (!fuzzyeq(pngImage, reReadImage, fuzziness)) { QTextStream(stdout) << "FAIL : " << fi.fileName() << ": re-reading the data resulted in a different image\n"; + if (pngImage.size() == reReadImage.size()) { + for (int i = 0; i < pngImage.width(); ++i) { + for (int j = 0; j < pngImage.height(); ++j) { + if (pngImage.pixel(i, j) != reReadImage.pixel(i, j)) { + QTextStream(stdout) << "Pixel is different " << i << ',' << j << ' ' << pngImage.pixel(i, j) << ' ' << reReadImage.pixel(i, j) + << '\n'; + } + } + } + } ++failed; continue; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kimageformats-5.91.0/src/imageformats/avif.cpp new/kimageformats-5.92.0/src/imageformats/avif.cpp --- old/kimageformats-5.91.0/src/imageformats/avif.cpp 2022-02-05 16:14:16.000000000 +0100 +++ new/kimageformats-5.92.0/src/imageformats/avif.cpp 2022-03-05 12:15:00.000000000 +0100 @@ -133,7 +133,7 @@ m_container_width = m_decoder->image->width; m_container_height = m_decoder->image->height; - if ((m_container_width > 32768) || (m_container_height > 32768)) { + 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; @@ -145,6 +145,12 @@ 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; + } + m_parseState = ParseAvifSuccess; if (decode_one_frame()) { return true; @@ -417,12 +423,26 @@ bool QAVIFHandler::write(const QImage &image) { if (image.format() == QImage::Format_Invalid) { - qWarning("No image data to save"); + qWarning("No image data to save!"); return false; } - if ((image.width() > 32768) || (image.height() > 32768)) { - qWarning("Image is too large"); + if ((image.width() > 0) && (image.height() > 0)) { + if ((image.width() > 65535) || (image.height() > 65535)) { + qWarning("Image (%dx%d) is too large to save!", image.width(), image.height()); + return false; + } + + if (image.width() > ((16384 * 16384) / image.height())) { + qWarning("Image (%dx%d) will not be saved because it has more than 256 megapixels!", image.width(), image.height()); + return false; + } + + if ((image.width() > 32768) || (image.height() > 32768)) { + qWarning("Image (%dx%d) has a dimension above 32768 pixels, saved AVIF may not work in other software!", image.width(), image.height()); + } + } else { + qWarning("Image has zero dimension!"); return false; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kimageformats-5.91.0/src/imageformats/jxl.cpp new/kimageformats-5.92.0/src/imageformats/jxl.cpp --- old/kimageformats-5.91.0/src/imageformats/jxl.cpp 2022-02-05 16:14:16.000000000 +0100 +++ new/kimageformats-5.92.0/src/imageformats/jxl.cpp 2022-03-05 12:15:00.000000000 +0100 @@ -12,6 +12,7 @@ #include "jxl_p.h" #include <jxl/encode.h> #include <jxl/thread_parallel_runner.h> +#include <string.h> QJpegXLHandler::QJpegXLHandler() : m_parseState(ParseJpegXLNotParsed) @@ -174,19 +175,30 @@ return false; } - if (m_basicinfo.xsize > 32768 || m_basicinfo.ysize > 32768) { + if (m_basicinfo.xsize > 65535 || m_basicinfo.ysize > 65535) { qWarning("JXL image (%dx%d) is too large", m_basicinfo.xsize, m_basicinfo.ysize); m_parseState = ParseJpegXLError; return false; - } else if (sizeof(void *) <= 4) { + } + + if (sizeof(void *) <= 4) { /* On 32bit systems, there is limited address space. * We skip imagess bigger than 8192 x 8192 pixels. * If we don't do it, abort() in libjxl may close whole application */ - if ((m_basicinfo.xsize * m_basicinfo.ysize) > 67108864) { + if (m_basicinfo.xsize > ((8192 * 8192) / m_basicinfo.ysize)) { qWarning("JXL image (%dx%d) is too large for 32bit build of the plug-in", m_basicinfo.xsize, m_basicinfo.ysize); m_parseState = ParseJpegXLError; return false; } + } else { + /* On 64bit systems + * We skip images bigger than 16384 x 16384 pixels. + * It is an artificial limit not to use extreme amount of memory */ + if (m_basicinfo.xsize > ((16384 * 16384) / m_basicinfo.ysize)) { + qWarning("JXL image (%dx%d) is bigger than security limit 256 megapixels", m_basicinfo.xsize, m_basicinfo.ysize); + m_parseState = ParseJpegXLError; + return false; + } } m_parseState = ParseJpegXLBasicInfoParsed; @@ -411,11 +423,59 @@ return false; } - if ((image.width() > 32768) || (image.height() > 32768)) { - qWarning("Image is too large"); + if ((image.width() > 0) && (image.height() > 0)) { + if ((image.width() > 65535) || (image.height() > 65535)) { + qWarning("Image (%dx%d) is too large to save!", image.width(), image.height()); + return false; + } + + if (sizeof(void *) <= 4) { + if (image.width() > ((8192 * 8192) / image.height())) { + qWarning("Image (%dx%d) is too large save via 32bit build of JXL plug-in", image.width(), image.height()); + return false; + } + } else { + if (image.width() > ((16384 * 16384) / image.height())) { + qWarning("Image (%dx%d) will not be saved because it has more than 256 megapixels", image.width(), image.height()); + return false; + } + } + } else { + qWarning("Image has zero dimension!"); return false; } + int save_depth = 8; // 8 or 16 + // depth detection + switch (image.format()) { + case QImage::Format_BGR30: + case QImage::Format_A2BGR30_Premultiplied: + case QImage::Format_RGB30: + case QImage::Format_A2RGB30_Premultiplied: + case QImage::Format_Grayscale16: + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + save_depth = 16; + break; + case QImage::Format_RGB32: + case QImage::Format_ARGB32: + case QImage::Format_ARGB32_Premultiplied: + case QImage::Format_RGB888: + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: + case QImage::Format_RGBA8888_Premultiplied: + save_depth = 8; + break; + default: + if (image.depth() > 32) { + save_depth = 16; + } else { + save_depth = 8; + } + break; + } + JxlEncoder *encoder = JxlEncoderCreate(nullptr); if (!encoder) { qWarning("Failed to create Jxl encoder"); @@ -456,16 +516,16 @@ bool convert_color_profile; QByteArray iccprofile; - if (image.colorSpace().isValid()) { + if (image.colorSpace().isValid() && (m_quality < 100)) { if (image.colorSpace().primaries() != QColorSpace::Primaries::SRgb || image.colorSpace().transferFunction() != QColorSpace::TransferFunction::SRgb) { convert_color_profile = true; } else { convert_color_profile = false; } - } else { // no profile or Qt-unsupported ICC profile + } else { // lossless or no profile or Qt-unsupported ICC profile convert_color_profile = false; iccprofile = image.colorSpace().iccProfile(); - if (iccprofile.size() > 0) { + if (iccprofile.size() > 0 || m_quality == 100) { output_info.uses_original_profile = 1; } } @@ -474,19 +534,45 @@ QImage::Format tmpformat; JxlEncoderStatus status; - pixel_format.data_type = JXL_TYPE_UINT16; pixel_format.endianness = JXL_NATIVE_ENDIAN; pixel_format.align = 0; - if (image.hasAlphaChannel()) { - tmpformat = QImage::Format_RGBA64; - pixel_format.num_channels = 4; - output_info.alpha_bits = 16; - output_info.num_extra_channels = 1; - } else { - tmpformat = QImage::Format_RGBX64; - pixel_format.num_channels = 3; - output_info.alpha_bits = 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; + output_info.animation.tps_denominator = 1; + + if (save_depth > 8) { // 16bit depth + pixel_format.data_type = JXL_TYPE_UINT16; + + output_info.bits_per_sample = 16; + + if (image.hasAlphaChannel()) { + tmpformat = QImage::Format_RGBA64; + pixel_format.num_channels = 4; + output_info.alpha_bits = 16; + output_info.num_extra_channels = 1; + } else { + tmpformat = QImage::Format_RGBX64; + pixel_format.num_channels = 3; + output_info.alpha_bits = 0; + } + } else { // 8bit depth + pixel_format.data_type = JXL_TYPE_UINT8; + + output_info.bits_per_sample = 8; + + if (image.hasAlphaChannel()) { + tmpformat = QImage::Format_RGBA8888; + pixel_format.num_channels = 4; + output_info.alpha_bits = 8; + output_info.num_extra_channels = 1; + } else { + tmpformat = QImage::Format_RGB888; + pixel_format.num_channels = 3; + output_info.alpha_bits = 0; + } } const QImage tmpimage = @@ -494,7 +580,7 @@ const size_t xsize = tmpimage.width(); const size_t ysize = tmpimage.height(); - const size_t buffer_size = 2 * pixel_format.num_channels * xsize * ysize; + const size_t buffer_size = (save_depth > 8) ? (2 * pixel_format.num_channels * xsize * ysize) : (pixel_format.num_channels * xsize * ysize); if (xsize == 0 || ysize == 0 || tmpimage.isNull()) { qWarning("Unable to allocate memory for output image"); @@ -507,12 +593,6 @@ output_info.xsize = tmpimage.width(); output_info.ysize = tmpimage.height(); - output_info.bits_per_sample = 16; - output_info.intensity_target = 255.0f; - output_info.orientation = JXL_ORIENT_IDENTITY; - output_info.num_color_channels = 3; - output_info.animation.tps_numerator = 10; - output_info.animation.tps_denominator = 1; status = JxlEncoderSetBasicInfo(encoder, &output_info); if (status != JXL_ENC_SUCCESS) { @@ -546,39 +626,60 @@ } } - if (image.hasAlphaChannel()) { + if (image.hasAlphaChannel() || ((save_depth == 8) && (xsize % 4 == 0))) { status = JxlEncoderAddImageFrame(encoder_options, &pixel_format, (void *)tmpimage.constBits(), buffer_size); } else { - uint16_t *tmp_buffer = new (std::nothrow) uint16_t[3 * xsize * ysize]; - if (!tmp_buffer) { - qWarning("Memory allocation error"); - if (runner) { - JxlThreadParallelRunnerDestroy(runner); + if (save_depth > 8) { // 16bit depth without alpha channel + uint16_t *tmp_buffer = new (std::nothrow) uint16_t[3 * xsize * ysize]; + if (!tmp_buffer) { + qWarning("Memory allocation error"); + if (runner) { + JxlThreadParallelRunnerDestroy(runner); + } + JxlEncoderDestroy(encoder); + return false; + } + + uint16_t *dest_pixels = tmp_buffer; + for (int y = 0; y < tmpimage.height(); y++) { + const uint16_t *src_pixels = reinterpret_cast<const uint16_t *>(tmpimage.constScanLine(y)); + for (int x = 0; x < tmpimage.width(); x++) { + // R + *dest_pixels = *src_pixels; + dest_pixels++; + src_pixels++; + // G + *dest_pixels = *src_pixels; + dest_pixels++; + src_pixels++; + // B + *dest_pixels = *src_pixels; + dest_pixels++; + src_pixels += 2; // skipalpha + } + } + status = JxlEncoderAddImageFrame(encoder_options, &pixel_format, (void *)tmp_buffer, buffer_size); + delete[] tmp_buffer; + } else { // 8bit depth without alpha channel + uchar *tmp_buffer8 = new (std::nothrow) uchar[3 * xsize * ysize]; + if (!tmp_buffer8) { + qWarning("Memory allocation error"); + if (runner) { + JxlThreadParallelRunnerDestroy(runner); + } + JxlEncoderDestroy(encoder); + return false; } - JxlEncoderDestroy(encoder); - return false; - } - uint16_t *dest_pixels = tmp_buffer; - for (int y = 0; y < tmpimage.height(); y++) { - const uint16_t *src_pixels = reinterpret_cast<const uint16_t *>(tmpimage.constScanLine(y)); - for (int x = 0; x < tmpimage.width(); x++) { - // R - *dest_pixels = *src_pixels; - dest_pixels++; - src_pixels++; - // G - *dest_pixels = *src_pixels; - dest_pixels++; - src_pixels++; - // B - *dest_pixels = *src_pixels; - dest_pixels++; - src_pixels += 2; // skipalpha + uchar *dest_pixels8 = tmp_buffer8; + const size_t rowbytes = 3 * xsize; + for (int y = 0; y < tmpimage.height(); y++) { + memcpy(dest_pixels8, tmpimage.constScanLine(y), rowbytes); + dest_pixels8 += rowbytes; } + status = JxlEncoderAddImageFrame(encoder_options, &pixel_format, (void *)tmp_buffer8, buffer_size); + delete[] tmp_buffer8; } - status = JxlEncoderAddImageFrame(encoder_options, &pixel_format, (void *)tmp_buffer, buffer_size); - delete[] tmp_buffer; } if (status == JXL_ENC_ERROR) {
