Compare commits

..

32 Commits

Author SHA1 Message Date
7af4eea253 GIT_SILENT Upgrade ECM and KF version requirements for 5.101.0 release. 2022-12-03 09:47:44 +00:00
a3049f6740 Fix missing DCI-P3 color space set 2022-11-24 15:02:31 +01:00
3b1e8f7054 minor tweaks in HEIF and AVIF plugins
It is mostly only about casting between types.
2022-11-19 11:38:49 +00:00
dcab3a06ab raw: LibRaw_QIODevice::read: fixed possible partial reading of an item
- If the size of an item is greater than 1 byte, it must be ensured that it is not partially read.
- In many readings, LibRAW gives an error if a partial number of items are read so I always try to read everything.
2022-11-19 10:14:16 +00:00
361f9e867e PSD multichannel testcases 2022-11-15 16:25:22 +00:00
35883aa604 Support to MCH with 4+ channels (treat as CMYK) 2022-11-15 16:25:22 +00:00
50846f224f avif: Check if encoder/decoder is available in capabilities()
The plugin can be compiled even with decoder libraries only. In a similar way of HEIC plugin I check if an encoder or a decoder is available.
2022-11-15 13:14:09 +00:00
9ad82ed608 Fix condition for installing desktop files 2022-11-07 00:37:09 +01:00
c9f32a226f GIT_SILENT Upgrade ECM and KF version requirements for 5.100.0 release. 2022-11-05 12:28:10 +00:00
a0df142408 Don't install desktop files for image formats when building against Qt6
These are only for kdelibs4support/KServiceTypeTrader
2022-11-03 21:36:38 +01:00
8586bb4719 raw: Don't seek back if we were asked to read too much
Otherwise we will return false when LibRaw::derror
calls for eof() when it is actually that we have reached eof
2022-10-30 23:59:55 +01:00
d734f28727 jxl: indicate when all frames have been read
and return correct loop count
2022-10-18 17:58:40 +02:00
afa7399b36 avif: minor fixes
libavif 0.11.0 uses new default dimension limit 32768 pixels,
we allow 65535 which was used by us previously.
Minor tweaks in jumpToNextImage, jumpToImage
to handle rare situations.
2022-10-18 15:05:15 +02:00
bfb12093ad avif: indicate when all frames have been read 2022-10-15 20:08:39 +02:00
1190e53e9b avif: always indicate endless loop
avif does not support loops but endless loop was the behavior before
460085 was fixed, so a workaround is added.

See also: https://github.com/AOMediaCodec/libavif/issues/347

CCBUG: 460085
2022-10-15 14:11:56 +08:00
350ce1b990 avif: return false in canRead() when imageIndex >= imageCount
Otherwise when `cache: false` is set in AnimatedImage, QMovie will try
to read the image forever.

BUG: 460085
FIXED-IN: 5.100
2022-10-15 14:02:38 +08:00
bcbf45e23a Add JXL test files corresponding to 8 EXIF orientation values 2022-10-13 15:47:04 +02:00
c71a7984d6 Add AVIF test files with rotation and mirror operations 2022-10-12 15:39:27 +02:00
b1f3a87896 Auto-rotate input images in readtest
Currently, there is no difference in the tests but in the future
we will add images with various transformations defined.
2022-10-12 15:33:08 +02:00
8af9a0f9d9 jxl: remove C-style casts 2022-10-11 15:38:55 +02:00
3790a89cd1 avif: Use reinterpret_cast instead C cast
No longer using int for QByteArray size because Qt6 uses qsizetype
2022-10-11 15:05:20 +02:00
f475a4b24a avif: revert 9ac923ad09 commit
Changes to libavif's avifImageRGBToYUV() API were reverted too.
2022-10-11 14:36:17 +02:00
d2f38b8b9c heif: replace C cast with static_cast 2022-10-10 09:28:41 +00:00
9ab64dbf22 heif: use heif_init/heif_deinit with libheif 1.13.0+
In recent libheif, application should use
heif_init/heif_deinit calls. Unfortunately, these calls are
not thread-safe in released 1.13.0 version yet (will be in the
future) and HEIFHandler is often created in different threads.
So we have to guard their use with a mutex.
2022-10-10 09:28:41 +00:00
20f74ce5e6 FindLibRaw: fix include dir, should not contain prefix libraw/
See also the examples at https://www.libraw.org/docs/API-CXX.html

BUG: 460105
2022-10-08 02:15:18 +02:00
54129819d5 Fix duplicated tests 2022-10-02 06:01:23 +00:00
181eb253c6 ANI partial test and PIC test added 2022-10-02 06:01:23 +00:00
c5f7ea7eac PSD: impreved support to sequential access device 2022-10-02 06:01:23 +00:00
ea14882ff7 Fix messages 2022-10-02 06:01:23 +00:00
f8bfdce285 CMakeLists: enable EXR test 2022-10-02 06:01:23 +00:00
524f083ee4 Added EXR test image 2022-10-02 06:01:23 +00:00
c96ad6ba8a Fixes for sequential devices 2022-10-02 06:01:23 +00:00
66 changed files with 477 additions and 186 deletions

View File

@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.16)
project(KImageFormats) project(KImageFormats)
include(FeatureSummary) include(FeatureSummary)
find_package(ECM 5.99.0 NO_MODULE) find_package(ECM 5.101.0 NO_MODULE)
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules") 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) feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)

View File

@ -136,7 +136,9 @@ kimageformats_write_tests(
# kimageformats_write_tests(eps) # kimageformats_write_tests(eps)
#endif() #endif()
if (OpenEXR_FOUND) if (OpenEXR_FOUND)
# FIXME: OpenEXR tests kimageformats_read_tests(
exr
)
endif() endif()
if (LibRaw_FOUND) if (LibRaw_FOUND)

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

View File

@ -9,6 +9,7 @@
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDir> #include <QDir>
#include <QFile>
#include <QFileInfo> #include <QFileInfo>
#include <QImage> #include <QImage>
#include <QImageReader> #include <QImageReader>
@ -18,6 +19,44 @@
#include "fuzzyeq.cpp" #include "fuzzyeq.cpp"
/**
* @brief The SequentialFile class
* Class to make a file a sequential access device. This class is used to check if the plugins could works
* on a sequential device such as a socket.
*/
class SequentialFile : public QFile
{
public:
SequentialFile()
: QFile()
{
}
explicit SequentialFile(const QString &name)
: QFile(name)
{
}
#ifndef QT_NO_QOBJECT
explicit SequentialFile(QObject *parent)
: QFile(parent)
{
}
SequentialFile(const QString &name, QObject *parent)
: QFile(name, parent)
{
}
#endif
bool isSequential() const override
{
return true;
}
qint64 size() const override
{
return bytesAvailable();
}
};
static void writeImageData(const char *name, const QString &filename, const QImage &image) static void writeImageData(const char *name, const QString &filename, const QImage &image)
{ {
QFile file(filename); QFile file(filename);
@ -56,7 +95,7 @@ int main(int argc, char **argv)
QCoreApplication::removeLibraryPath(QStringLiteral(PLUGIN_DIR)); QCoreApplication::removeLibraryPath(QStringLiteral(PLUGIN_DIR));
QCoreApplication::addLibraryPath(QStringLiteral(PLUGIN_DIR)); QCoreApplication::addLibraryPath(QStringLiteral(PLUGIN_DIR));
QCoreApplication::setApplicationName(QStringLiteral("readtest")); QCoreApplication::setApplicationName(QStringLiteral("readtest"));
QCoreApplication::setApplicationVersion(QStringLiteral("1.0.0")); QCoreApplication::setApplicationVersion(QStringLiteral("1.1.0"));
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription(QStringLiteral("Performs basic image conversion checking.")); parser.setApplicationDescription(QStringLiteral("Performs basic image conversion checking."));
@ -98,6 +137,7 @@ int main(int argc, char **argv)
int passed = 0; int passed = 0;
int failed = 0; int failed = 0;
int skipped = 0;
QTextStream(stdout) << "********* " QTextStream(stdout) << "********* "
<< "Starting basic read tests for " << suffix << " images *********\n"; << "Starting basic read tests for " << suffix << " images *********\n";
@ -111,6 +151,13 @@ int main(int argc, char **argv)
QTextStream(stdout) << "QImageReader::supportedImageFormats: " << formatStrings.join(", ") << "\n"; QTextStream(stdout) << "QImageReader::supportedImageFormats: " << formatStrings.join(", ") << "\n";
const QFileInfoList lstImgDir = imgdir.entryInfoList(); const QFileInfoList lstImgDir = imgdir.entryInfoList();
// Launch 2 runs for each test: first run on a random access device, second run on a sequential access device
for (int seq = 0; seq < 2; ++seq) {
if (seq) {
QTextStream(stdout) << "* Run on SEQUENTIAL ACCESS device\n";
} else {
QTextStream(stdout) << "* Run on RANDOM ACCESS device\n";
}
for (const QFileInfo &fi : lstImgDir) { for (const QFileInfo &fi : lstImgDir) {
if (!fi.suffix().compare("png", Qt::CaseInsensitive)) { if (!fi.suffix().compare("png", Qt::CaseInsensitive)) {
continue; continue;
@ -120,20 +167,31 @@ int main(int argc, char **argv)
QString expfile = fi.filePath().replace(suffixPos, suffix.count(), QStringLiteral("png")); QString expfile = fi.filePath().replace(suffixPos, suffix.count(), QStringLiteral("png"));
QString expfilename = QFileInfo(expfile).fileName(); QString expfilename = QFileInfo(expfile).fileName();
QImageReader inputReader(inputfile, format); std::unique_ptr<QIODevice> inputDevice(seq ? new SequentialFile(inputfile) : new QFile(inputfile));
QImageReader inputReader(inputDevice.get(), format);
QImageReader expReader(expfile, "png"); QImageReader expReader(expfile, "png");
QImage inputImage; QImage inputImage;
QImage expImage; QImage expImage;
// inputImage is auto-rotated to final orientation
inputReader.setAutoTransform(true);
if (!expReader.read(&expImage)) { if (!expReader.read(&expImage)) {
QTextStream(stdout) << "ERROR: " << fi.fileName() << ": could not load " << expfilename << ": " << expReader.errorString() << "\n"; QTextStream(stdout) << "ERROR: " << fi.fileName() << ": could not load " << expfilename << ": " << expReader.errorString() << "\n";
++failed; ++failed;
continue; continue;
} }
if (!inputReader.canRead()) { if (!inputReader.canRead()) {
// All plugins must pass the test on a random device.
// canRead() must also return false if the plugin is unable to run on a sequential device.
if (inputDevice->isSequential()) {
QTextStream(stdout) << "SKIP : " << fi.fileName() << ": cannot read on a sequential device (don't worry, it's ok)\n";
++skipped;
} else {
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": failed can read: " << inputReader.errorString() << "\n"; QTextStream(stdout) << "FAIL : " << fi.fileName() << ": failed can read: " << inputReader.errorString() << "\n";
++failed; ++failed;
}
continue; continue;
} }
if (!inputReader.read(&inputImage)) { if (!inputReader.read(&inputImage)) {
@ -160,8 +218,8 @@ int main(int argc, char **argv)
inputImage = inputImage.convertToFormat(cmpFormat); inputImage = inputImage.convertToFormat(cmpFormat);
} }
if (expImage.format() != cmpFormat) { if (expImage.format() != cmpFormat) {
QTextStream(stdout) << "INFO : " << fi.fileName() << ": converting " << expfilename << " from " << formatToString(expImage.format()) << " to " QTextStream(stdout) << "INFO : " << fi.fileName() << ": converting " << expfilename << " from " << formatToString(expImage.format())
<< formatToString(cmpFormat) << '\n'; << " to " << formatToString(cmpFormat) << '\n';
expImage = expImage.convertToFormat(cmpFormat); expImage = expImage.convertToFormat(cmpFormat);
} }
if (fuzzyeq(inputImage, expImage, fuzziness)) { if (fuzzyeq(inputImage, expImage, fuzziness)) {
@ -175,8 +233,9 @@ int main(int argc, char **argv)
} }
} }
} }
}
QTextStream(stdout) << "Totals: " << passed << " passed, " << failed << " failed\n"; QTextStream(stdout) << "Totals: " << passed << " passed, " << skipped << " skipped, " << failed << " failed\n";
QTextStream(stdout) << "********* " QTextStream(stdout) << "********* "
<< "Finished basic read tests for " << suffix << " images *********\n"; << "Finished basic read tests for " << suffix << " images *********\n";

View File

@ -2,7 +2,7 @@
# Find the LibRaw library <https://www.libraw.org> # Find the LibRaw library <https://www.libraw.org>
# This module defines # This module defines
# LibRaw_VERSION, the version string of LibRaw # LibRaw_VERSION, the version string of LibRaw
# LibRaw_INCLUDE_DIR, where to find libraw.h # LibRaw_INCLUDE_DIR, where to find libraw/libraw.h
# LibRaw_LIBRARIES, the libraries needed to use LibRaw (non-thread-safe) # LibRaw_LIBRARIES, the libraries needed to use LibRaw (non-thread-safe)
# LibRaw_r_LIBRARIES, the libraries needed to use LibRaw (thread-safe) # LibRaw_r_LIBRARIES, the libraries needed to use LibRaw (thread-safe)
# LibRaw_DEFINITIONS, the definitions needed to use LibRaw (non-thread-safe) # LibRaw_DEFINITIONS, the definitions needed to use LibRaw (non-thread-safe)
@ -23,11 +23,10 @@ IF(PKG_CONFIG_FOUND)
SET(LibRaw_r_DEFINITIONS ${PC_LIBRAW_R_CFLAGS_OTHER}) SET(LibRaw_r_DEFINITIONS ${PC_LIBRAW_R_CFLAGS_OTHER})
ENDIF() ENDIF()
FIND_PATH(LibRaw_INCLUDE_DIR libraw.h FIND_PATH(LibRaw_INCLUDE_DIR libraw/libraw.h
HINTS HINTS
${PC_LIBRAW_INCLUDEDIR} ${PC_LIBRAW_INCLUDEDIR}
${PC_LibRaw_INCLUDE_DIRS} ${PC_LibRaw_INCLUDE_DIRS}
PATH_SUFFIXES libraw
) )
FIND_LIBRARY(LibRaw_LIBRARIES NAMES raw FIND_LIBRARY(LibRaw_LIBRARIES NAMES raw
@ -43,7 +42,7 @@ FIND_LIBRARY(LibRaw_r_LIBRARIES NAMES raw_r
) )
IF(LibRaw_INCLUDE_DIR) IF(LibRaw_INCLUDE_DIR)
FILE(READ ${LibRaw_INCLUDE_DIR}/libraw_version.h _libraw_version_content) FILE(READ ${LibRaw_INCLUDE_DIR}/libraw/libraw_version.h _libraw_version_content)
STRING(REGEX MATCH "#define LIBRAW_MAJOR_VERSION[ \t]*([0-9]*)\n" _version_major_match ${_libraw_version_content}) STRING(REGEX MATCH "#define LIBRAW_MAJOR_VERSION[ \t]*([0-9]*)\n" _version_major_match ${_libraw_version_content})
SET(_libraw_version_major "${CMAKE_MATCH_1}") SET(_libraw_version_major "${CMAKE_MATCH_1}")
@ -58,7 +57,7 @@ IF(LibRaw_INCLUDE_DIR)
SET(LibRaw_VERSION "${_libraw_version_major}.${_libraw_version_minor}.${_libraw_version_patch}") SET(LibRaw_VERSION "${_libraw_version_major}.${_libraw_version_minor}.${_libraw_version_patch}")
ELSE() ELSE()
IF(NOT LibRaw_FIND_QUIETLY) IF(NOT LibRaw_FIND_QUIETLY)
MESSAGE(STATUS "Failed to get version information from ${LibRaw_INCLUDE_DIR}/libraw_version.h") MESSAGE(STATUS "Failed to get version information from ${LibRaw_INCLUDE_DIR}/libraw/libraw_version.h")
ENDIF() ENDIF()
ENDIF() ENDIF()
ENDIF() ENDIF()

View File

@ -20,19 +20,26 @@ endfunction()
################################## ##################################
kimageformats_add_plugin(kimg_ani SOURCES ani.cpp) kimageformats_add_plugin(kimg_ani SOURCES ani.cpp)
install(FILES ani.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES ani.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
if (TARGET avif) if (TARGET avif)
kimageformats_add_plugin(kimg_avif SOURCES "avif.cpp") kimageformats_add_plugin(kimg_avif SOURCES "avif.cpp")
target_link_libraries(kimg_avif "avif") target_link_libraries(kimg_avif "avif")
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES avif.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) install(FILES avif.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif() endif()
################################## ##################################
install(FILES dds-qt.desktop RENAME dds.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES dds-qt.desktop RENAME dds.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
@ -40,14 +47,18 @@ if (BUILD_EPS_PLUGIN)
if (TARGET Qt${QT_MAJOR_VERSION}::PrintSupport) if (TARGET Qt${QT_MAJOR_VERSION}::PrintSupport)
kimageformats_add_plugin(kimg_eps SOURCES eps.cpp) kimageformats_add_plugin(kimg_eps SOURCES eps.cpp)
target_link_libraries(kimg_eps Qt${QT_MAJOR_VERSION}::PrintSupport) target_link_libraries(kimg_eps Qt${QT_MAJOR_VERSION}::PrintSupport)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES eps.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) install(FILES eps.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif() endif()
endif()
endif() endif()
################################## ##################################
# need this for Qt's version of the plugin if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES jp2.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) # need this for Qt's version of the plugin
install(FILES jp2.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
@ -65,13 +76,17 @@ if(OpenEXR_FOUND)
endif() endif()
kde_target_enable_exceptions(kimg_exr PRIVATE) kde_target_enable_exceptions(kimg_exr PRIVATE)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES exr.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) install(FILES exr.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif() endif()
################################## ##################################
kimageformats_add_plugin(kimg_hdr SOURCES hdr.cpp) kimageformats_add_plugin(kimg_hdr SOURCES hdr.cpp)
install(FILES hdr.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES hdr.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
@ -79,7 +94,10 @@ if (LibHeif_FOUND)
kimageformats_add_plugin(kimg_heif SOURCES heif.cpp) kimageformats_add_plugin(kimg_heif SOURCES heif.cpp)
target_link_libraries(kimg_heif PkgConfig::LibHeif) target_link_libraries(kimg_heif PkgConfig::LibHeif)
kde_target_enable_exceptions(kimg_heif PRIVATE) kde_target_enable_exceptions(kimg_heif PRIVATE)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES heif.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) install(FILES heif.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif() endif()
################################## ##################################
@ -90,43 +108,60 @@ if (LibJXL_FOUND AND LibJXLThreads_FOUND)
if (LibJXL_VERSION VERSION_GREATER_EQUAL "0.7.0") if (LibJXL_VERSION VERSION_GREATER_EQUAL "0.7.0")
target_compile_definitions(kimg_jxl PRIVATE KIMG_JXL_API_VERSION=70) target_compile_definitions(kimg_jxl PRIVATE KIMG_JXL_API_VERSION=70)
endif() endif()
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES jxl.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) install(FILES jxl.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif() endif()
################################## ##################################
kimageformats_add_plugin(kimg_pcx SOURCES pcx.cpp) kimageformats_add_plugin(kimg_pcx SOURCES pcx.cpp)
install(FILES pcx.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES pcx.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
kimageformats_add_plugin(kimg_pic SOURCES pic.cpp) kimageformats_add_plugin(kimg_pic SOURCES pic.cpp)
install(FILES pic.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES pic.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
kimageformats_add_plugin(kimg_psd SOURCES psd.cpp) kimageformats_add_plugin(kimg_psd SOURCES psd.cpp)
install(FILES psd.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES psd.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
kimageformats_add_plugin(kimg_ras SOURCES ras.cpp) kimageformats_add_plugin(kimg_ras SOURCES ras.cpp)
install(FILES ras.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES ras.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
kimageformats_add_plugin(kimg_rgb SOURCES rgb.cpp) kimageformats_add_plugin(kimg_rgb SOURCES rgb.cpp)
install(FILES rgb.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES rgb.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
kimageformats_add_plugin(kimg_tga SOURCES tga.cpp) kimageformats_add_plugin(kimg_tga SOURCES tga.cpp)
install(FILES tga.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES tga.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
kimageformats_add_plugin(kimg_xcf SOURCES xcf.cpp) kimageformats_add_plugin(kimg_xcf SOURCES xcf.cpp)
install(FILES xcf.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES xcf.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
################################## ##################################
@ -134,7 +169,9 @@ if (LibRaw_FOUND)
kimageformats_add_plugin(kimg_raw SOURCES raw.cpp) kimageformats_add_plugin(kimg_raw SOURCES raw.cpp)
kde_enable_exceptions() kde_enable_exceptions()
target_link_libraries(kimg_raw LibRaw::LibRaw) target_link_libraries(kimg_raw LibRaw::LibRaw)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES raw.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) install(FILES raw.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif() endif()
################################## ##################################
@ -143,10 +180,14 @@ if (KF5Archive_FOUND)
kimageformats_add_plugin(kimg_kra SOURCES kra.cpp) kimageformats_add_plugin(kimg_kra SOURCES kra.cpp)
target_link_libraries(kimg_kra KF5::Archive) target_link_libraries(kimg_kra KF5::Archive)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES kra.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) install(FILES kra.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
kimageformats_add_plugin(kimg_ora SOURCES ora.cpp) kimageformats_add_plugin(kimg_ora SOURCES ora.cpp)
target_link_libraries(kimg_ora KF5::Archive) target_link_libraries(kimg_ora KF5::Archive)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES ora.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/) install(FILES ora.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif() endif()

View File

@ -521,6 +521,9 @@ bool ANIHandler::canRead(QIODevice *device)
qWarning("ANIHandler::canRead() called with no device"); qWarning("ANIHandler::canRead() called with no device");
return false; return false;
} }
if (device->isSequential()) {
return false;
}
const QByteArray riffIntro = device->peek(12); const QByteArray riffIntro = device->peek(12);

View File

@ -42,6 +42,11 @@ bool QAVIFHandler::canRead() const
if (m_parseState != ParseAvifError) { if (m_parseState != ParseAvifError) {
setFormat("avif"); setFormat("avif");
if (m_parseState == ParseAvifFinished) {
return false;
}
return true; return true;
} }
return false; return false;
@ -58,7 +63,7 @@ bool QAVIFHandler::canRead(QIODevice *device)
} }
avifROData input; avifROData input;
input.data = (const uint8_t *)header.constData(); input.data = reinterpret_cast<const uint8_t *>(header.constData());
input.size = header.size(); input.size = header.size();
if (avifPeekCompatibleFileType(&input)) { if (avifPeekCompatibleFileType(&input)) {
@ -69,7 +74,7 @@ bool QAVIFHandler::canRead(QIODevice *device)
bool QAVIFHandler::ensureParsed() const bool QAVIFHandler::ensureParsed() const
{ {
if (m_parseState == ParseAvifSuccess || m_parseState == ParseAvifMetadata) { if (m_parseState == ParseAvifSuccess || m_parseState == ParseAvifMetadata || m_parseState == ParseAvifFinished) {
return true; return true;
} }
if (m_parseState == ParseAvifError) { if (m_parseState == ParseAvifError) {
@ -83,7 +88,7 @@ bool QAVIFHandler::ensureParsed() const
bool QAVIFHandler::ensureOpened() const bool QAVIFHandler::ensureOpened() const
{ {
if (m_parseState == ParseAvifSuccess) { if (m_parseState == ParseAvifSuccess || m_parseState == ParseAvifFinished) {
return true; return true;
} }
if (m_parseState == ParseAvifError) { if (m_parseState == ParseAvifError) {
@ -111,7 +116,7 @@ bool QAVIFHandler::ensureDecoder()
m_rawData = device()->readAll(); m_rawData = device()->readAll();
m_rawAvifData.data = (const uint8_t *)m_rawData.constData(); m_rawAvifData.data = reinterpret_cast<const uint8_t *>(m_rawData.constData());
m_rawAvifData.size = m_rawData.size(); m_rawAvifData.size = m_rawData.size();
if (avifPeekCompatibleFileType(&m_rawAvifData) == AVIF_FALSE) { if (avifPeekCompatibleFileType(&m_rawAvifData) == AVIF_FALSE) {
@ -132,6 +137,10 @@ bool QAVIFHandler::ensureDecoder()
m_decoder->strictFlags = AVIF_STRICT_DISABLED; m_decoder->strictFlags = AVIF_STRICT_DISABLED;
#endif #endif
#if AVIF_VERSION >= 110000
m_decoder->imageDimensionLimit = 65535;
#endif
avifResult decodeResult; avifResult decodeResult;
decodeResult = avifDecoderSetIOMemory(m_decoder, m_rawAvifData.data, m_rawAvifData.size); decodeResult = avifDecoderSetIOMemory(m_decoder, m_rawAvifData.data, m_rawAvifData.size);
@ -246,7 +255,7 @@ bool QAVIFHandler::decode_one_frame()
QColorSpace colorspace; QColorSpace colorspace;
if (m_decoder->image->icc.data && (m_decoder->image->icc.size > 0)) { if (m_decoder->image->icc.data && (m_decoder->image->icc.size > 0)) {
const QByteArray icc_data((const char *)m_decoder->image->icc.data, (int)m_decoder->image->icc.size); const QByteArray icc_data(reinterpret_cast<const char *>(m_decoder->image->icc.data), m_decoder->image->icc.size);
colorspace = QColorSpace::fromIccProfile(icc_data); colorspace = QColorSpace::fromIccProfile(icc_data);
if (!colorspace.isValid()) { if (!colorspace.isValid()) {
qWarning("AVIF image has Qt-unsupported or invalid ICC profile!"); qWarning("AVIF image has Qt-unsupported or invalid ICC profile!");
@ -336,7 +345,7 @@ bool QAVIFHandler::decode_one_frame()
rgb.format = AVIF_RGB_FORMAT_ARGB; rgb.format = AVIF_RGB_FORMAT_ARGB;
#endif #endif
#if (AVIF_VERSION >= 80400) && (AVIF_VERSION <= 100100) #if AVIF_VERSION >= 80400
if (m_decoder->imageCount > 1) { if (m_decoder->imageCount > 1) {
/* accelerate animated AVIF */ /* accelerate animated AVIF */
rgb.chromaUpsampling = AVIF_CHROMA_UPSAMPLING_FASTEST; rgb.chromaUpsampling = AVIF_CHROMA_UPSAMPLING_FASTEST;
@ -351,12 +360,7 @@ bool QAVIFHandler::decode_one_frame()
rgb.rowBytes = result.bytesPerLine(); rgb.rowBytes = result.bytesPerLine();
rgb.pixels = result.bits(); rgb.pixels = result.bits();
#if AVIF_VERSION >= 100101
// use faster decoding for animations
avifResult res = avifImageYUVToRGB(m_decoder->image, &rgb, (m_decoder->imageCount > 1) ? AVIF_CHROMA_UPSAMPLING_NEAREST : AVIF_YUV_TO_RGB_DEFAULT);
#else
avifResult res = avifImageYUVToRGB(m_decoder->image, &rgb); avifResult res = avifImageYUVToRGB(m_decoder->image, &rgb);
#endif
if (res != AVIF_RESULT_OK) { if (res != AVIF_RESULT_OK) {
qWarning("ERROR in avifImageYUVToRGB: %s", avifResultToString(res)); qWarning("ERROR in avifImageYUVToRGB: %s", avifResultToString(res));
return false; return false;
@ -459,6 +463,13 @@ bool QAVIFHandler::read(QImage *image)
*image = m_current_image; *image = m_current_image;
if (imageCount() >= 2) { if (imageCount() >= 2) {
m_must_jump_to_next_image = true; m_must_jump_to_next_image = true;
if (m_decoder->imageIndex >= m_decoder->imageCount - 1) {
// all frames in animation have been read
m_parseState = ParseAvifFinished;
}
} else {
// the static image has been read
m_parseState = ParseAvifFinished;
} }
return true; return true;
} }
@ -756,7 +767,7 @@ bool QAVIFHandler::write(const QImage &image)
avif->transferCharacteristics = transfer_to_save; avif->transferCharacteristics = transfer_to_save;
if (iccprofile.size() > 0) { if (iccprofile.size() > 0) {
avifImageSetProfileICC(avif, (const uint8_t *)iccprofile.constData(), iccprofile.size()); avifImageSetProfileICC(avif, reinterpret_cast<const uint8_t *>(iccprofile.constData()), iccprofile.size());
} }
avifRGBImage rgb; avifRGBImage rgb;
@ -782,11 +793,7 @@ bool QAVIFHandler::write(const QImage &image)
} }
} }
#if AVIF_VERSION >= 100101
res = avifImageRGBToYUV(avif, &rgb, AVIF_RGB_TO_YUV_DEFAULT);
#else
res = avifImageRGBToYUV(avif, &rgb); res = avifImageRGBToYUV(avif, &rgb);
#endif
if (res != AVIF_RESULT_OK) { if (res != AVIF_RESULT_OK) {
qWarning("ERROR in avifImageRGBToYUV: %s", avifResultToString(res)); qWarning("ERROR in avifImageRGBToYUV: %s", avifResultToString(res));
return false; return false;
@ -914,6 +921,7 @@ bool QAVIFHandler::jumpToNextImage()
if (m_decoder->imageIndex >= 0) { if (m_decoder->imageIndex >= 0) {
if (m_decoder->imageCount < 2) { if (m_decoder->imageCount < 2) {
m_parseState = ParseAvifSuccess;
return true; return true;
} }
@ -958,11 +966,13 @@ bool QAVIFHandler::jumpToImage(int imageNumber)
if (m_decoder->imageCount < 2) { // not an animation if (m_decoder->imageCount < 2) { // not an animation
if (imageNumber == 0) { if (imageNumber == 0) {
return ensureOpened(); if (ensureOpened()) {
} else { m_parseState = ParseAvifSuccess;
return false; return true;
} }
} }
return false;
}
if (imageNumber < 0 || imageNumber >= m_decoder->imageCount) { // wrong index if (imageNumber < 0 || imageNumber >= m_decoder->imageCount) { // wrong index
return false; return false;
@ -970,6 +980,7 @@ bool QAVIFHandler::jumpToImage(int imageNumber)
if (imageNumber == m_decoder->imageIndex) { // we are here already if (imageNumber == m_decoder->imageIndex) { // we are here already
m_must_jump_to_next_image = false; m_must_jump_to_next_image = false;
m_parseState = ParseAvifSuccess;
return true; return true;
} }
@ -1028,7 +1039,8 @@ int QAVIFHandler::loopCount() const
return 0; return 0;
} }
return 1; // Endless loop to work around https://github.com/AOMediaCodec/libavif/issues/347
return -1;
} }
QPointF QAVIFHandler::CompatibleChromacity(qreal chrX, qreal chrY) QPointF QAVIFHandler::CompatibleChromacity(qreal chrX, qreal chrY)
@ -1045,12 +1057,26 @@ QPointF QAVIFHandler::CompatibleChromacity(qreal chrX, qreal chrY)
QImageIOPlugin::Capabilities QAVIFPlugin::capabilities(QIODevice *device, const QByteArray &format) const QImageIOPlugin::Capabilities QAVIFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
{ {
static const bool isAvifDecoderAvailable(avifCodecName(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_DECODE) != nullptr);
static const bool isAvifEncoderAvailable(avifCodecName(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_ENCODE) != nullptr);
if (format == "avif") { if (format == "avif") {
return Capabilities(CanRead | CanWrite); Capabilities format_cap;
if (isAvifDecoderAvailable) {
format_cap |= CanRead;
}
if (isAvifEncoderAvailable) {
format_cap |= CanWrite;
}
return format_cap;
} }
if (format == "avifs") { if (format == "avifs") {
return Capabilities(CanRead); Capabilities format_cap;
if (isAvifDecoderAvailable) {
format_cap |= CanRead;
}
return format_cap;
} }
if (!format.isEmpty()) { if (!format.isEmpty()) {
@ -1061,10 +1087,10 @@ QImageIOPlugin::Capabilities QAVIFPlugin::capabilities(QIODevice *device, const
} }
Capabilities cap; Capabilities cap;
if (device->isReadable() && QAVIFHandler::canRead(device)) { if (device->isReadable() && QAVIFHandler::canRead(device) && isAvifDecoderAvailable) {
cap |= CanRead; cap |= CanRead;
} }
if (device->isWritable()) { if (device->isWritable() && isAvifEncoderAvailable) {
cap |= CanWrite; cap |= CanWrite;
} }
return cap; return cap;

View File

@ -55,6 +55,7 @@ private:
ParseAvifNotParsed = 0, ParseAvifNotParsed = 0,
ParseAvifSuccess = 1, ParseAvifSuccess = 1,
ParseAvifMetadata = 2, ParseAvifMetadata = 2,
ParseAvifFinished = 3,
}; };
ParseAvifState m_parseState; ParseAvifState m_parseState;

View File

@ -15,6 +15,7 @@
#include <QDebug> #include <QDebug>
#include <QPointF> #include <QPointF>
#include <QSysInfo> #include <QSysInfo>
#include <limits>
#include <string.h> #include <string.h>
namespace // Private. namespace // Private.
@ -52,6 +53,11 @@ private:
} // namespace } // namespace
size_t HEIFHandler::m_initialized_count = 0;
bool HEIFHandler::m_plugins_queried = false;
bool HEIFHandler::m_heif_decoder_available = false;
bool HEIFHandler::m_heif_encoder_available = false;
HEIFHandler::HEIFHandler() HEIFHandler::HEIFHandler()
: m_parseState(ParseHeicNotParsed) : m_parseState(ParseHeicNotParsed)
, m_quality(100) , m_quality(100)
@ -88,6 +94,21 @@ bool HEIFHandler::write(const QImage &image)
return false; return false;
} }
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
startHeifLib();
#endif
bool success = write_helper(image);
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
finishHeifLib();
#endif
return success;
}
bool HEIFHandler::write_helper(const QImage &image)
{
int save_depth; // 8 or 10bit per channel int save_depth; // 8 or 10bit per channel
QImage::Format tmpformat; // format for temporary image QImage::Format tmpformat; // format for temporary image
const bool save_alpha = image.hasAlphaChannel(); const bool save_alpha = image.hasAlphaChannel();
@ -356,8 +377,18 @@ bool HEIFHandler::ensureParsed() const
HEIFHandler *that = const_cast<HEIFHandler *>(this); HEIFHandler *that = const_cast<HEIFHandler *>(this);
return that->ensureDecoder(); #if LIBHEIF_HAVE_VERSION(1, 13, 0)
startHeifLib();
#endif
bool success = that->ensureDecoder();
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
finishHeifLib();
#endif
return success;
} }
bool HEIFHandler::ensureDecoder() bool HEIFHandler::ensureDecoder()
{ {
if (m_parseState != ParseHeicNotParsed) { if (m_parseState != ParseHeicNotParsed) {
@ -375,7 +406,7 @@ bool HEIFHandler::ensureDecoder()
try { try {
heif::Context ctx; heif::Context ctx;
ctx.read_from_memory_without_copy((const void *)(buffer.constData()), buffer.size()); ctx.read_from_memory_without_copy(static_cast<const void *>(buffer.constData()), buffer.size());
heif::ImageHandle handle = ctx.get_primary_image_handle(); heif::ImageHandle handle = ctx.get_primary_image_handle();
@ -607,8 +638,8 @@ bool HEIFHandler::ensureDecoder()
heif_color_profile_type profileType = heif_image_handle_get_color_profile_type(handle.get_raw_image_handle()); heif_color_profile_type profileType = heif_image_handle_get_color_profile_type(handle.get_raw_image_handle());
struct heif_error err; struct heif_error err;
if (profileType == heif_color_profile_type_prof || profileType == heif_color_profile_type_rICC) { if (profileType == heif_color_profile_type_prof || profileType == heif_color_profile_type_rICC) {
int rawProfileSize = (int)heif_image_handle_get_raw_color_profile_size(handle.get_raw_image_handle()); size_t rawProfileSize = heif_image_handle_get_raw_color_profile_size(handle.get_raw_image_handle());
if (rawProfileSize > 0) { if (rawProfileSize > 0 && rawProfileSize < std::numeric_limits<int>::max()) {
QByteArray ba(rawProfileSize, 0); QByteArray ba(rawProfileSize, 0);
err = heif_image_handle_get_raw_color_profile(handle.get_raw_image_handle(), ba.data()); err = heif_image_handle_get_raw_color_profile(handle.get_raw_image_handle(), ba.data());
if (err.code) { if (err.code) {
@ -620,7 +651,7 @@ bool HEIFHandler::ensureDecoder()
} }
} }
} else { } else {
qWarning() << "icc profile is empty"; qWarning() << "icc profile is empty or above limits";
} }
} else if (profileType == heif_color_profile_type_nclx) { } else if (profileType == heif_color_profile_type_nclx) {
@ -696,14 +727,100 @@ bool HEIFHandler::ensureDecoder()
return true; return true;
} }
bool HEIFHandler::isHeifDecoderAvailable()
{
QMutexLocker locker(&getHEIFHandlerMutex());
if (!m_plugins_queried) {
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
if (m_initialized_count == 0) {
heif_init(nullptr);
}
#endif
m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC);
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
m_plugins_queried = true;
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
if (m_initialized_count == 0) {
heif_deinit();
}
#endif
}
return m_heif_decoder_available;
}
bool HEIFHandler::isHeifEncoderAvailable()
{
QMutexLocker locker(&getHEIFHandlerMutex());
if (!m_plugins_queried) {
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
if (m_initialized_count == 0) {
heif_init(nullptr);
}
#endif
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC);
m_plugins_queried = true;
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
if (m_initialized_count == 0) {
heif_deinit();
}
#endif
}
return m_heif_encoder_available;
}
void HEIFHandler::startHeifLib()
{
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
QMutexLocker locker(&getHEIFHandlerMutex());
if (m_initialized_count == 0) {
heif_init(nullptr);
}
m_initialized_count++;
#endif
}
void HEIFHandler::finishHeifLib()
{
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
QMutexLocker locker(&getHEIFHandlerMutex());
if (m_initialized_count == 0) {
return;
}
m_initialized_count--;
if (m_initialized_count == 0) {
heif_deinit();
}
#endif
}
QMutex &HEIFHandler::getHEIFHandlerMutex()
{
static QMutex heif_handler_mutex;
return heif_handler_mutex;
}
QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const QByteArray &format) const QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
{ {
if (format == "heif" || format == "heic") { if (format == "heif" || format == "heic") {
Capabilities format_cap; Capabilities format_cap;
if (heif_have_decoder_for_format(heif_compression_HEVC)) { if (HEIFHandler::isHeifDecoderAvailable()) {
format_cap |= CanRead; format_cap |= CanRead;
} }
if (heif_have_encoder_for_format(heif_compression_HEVC)) { if (HEIFHandler::isHeifEncoderAvailable()) {
format_cap |= CanWrite; format_cap |= CanWrite;
} }
return format_cap; return format_cap;
@ -716,11 +833,11 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q
} }
Capabilities cap; Capabilities cap;
if (device->isReadable() && HEIFHandler::canRead(device) && heif_have_decoder_for_format(heif_compression_HEVC)) { if (device->isReadable() && HEIFHandler::canRead(device) && HEIFHandler::isHeifDecoderAvailable()) {
cap |= CanRead; cap |= CanRead;
} }
if (device->isWritable() && heif_have_encoder_for_format(heif_compression_HEVC)) { if (device->isWritable() && HEIFHandler::isHeifEncoderAvailable()) {
cap |= CanWrite; cap |= CanWrite;
} }
return cap; return cap;

View File

@ -13,6 +13,7 @@
#include <QByteArray> #include <QByteArray>
#include <QImage> #include <QImage>
#include <QImageIOPlugin> #include <QImageIOPlugin>
#include <QMutex>
class HEIFHandler : public QImageIOHandler class HEIFHandler : public QImageIOHandler
{ {
@ -29,6 +30,9 @@ public:
void setOption(ImageOption option, const QVariant &value) override; void setOption(ImageOption option, const QVariant &value) override;
bool supportsOption(ImageOption option) const override; bool supportsOption(ImageOption option) const override;
static bool isHeifDecoderAvailable();
static bool isHeifEncoderAvailable();
private: private:
static bool isSupportedBMFFType(const QByteArray &header); static bool isSupportedBMFFType(const QByteArray &header);
bool ensureParsed() const; bool ensureParsed() const;
@ -43,6 +47,18 @@ private:
ParseHeicState m_parseState; ParseHeicState m_parseState;
int m_quality; int m_quality;
QImage m_current_image; QImage m_current_image;
bool write_helper(const QImage &image);
static void startHeifLib();
static void finishHeifLib();
static size_t m_initialized_count;
static bool m_plugins_queried;
static bool m_heif_decoder_available;
static bool m_heif_encoder_available;
static QMutex &getHEIFHandlerMutex();
}; };
class HEIFPlugin : public QImageIOPlugin class HEIFPlugin : public QImageIOPlugin

View File

@ -48,6 +48,11 @@ bool QJpegXLHandler::canRead() const
if (m_parseState != ParseJpegXLError) { if (m_parseState != ParseJpegXLError) {
setFormat("jxl"); setFormat("jxl");
if (m_parseState == ParseJpegXLFinished) {
return false;
}
return true; return true;
} }
return false; return false;
@ -63,7 +68,7 @@ bool QJpegXLHandler::canRead(QIODevice *device)
return false; return false;
} }
JxlSignature signature = JxlSignatureCheck((const uint8_t *)header.constData(), header.size()); JxlSignature signature = JxlSignatureCheck(reinterpret_cast<const uint8_t *>(header.constData()), header.size());
if (signature == JXL_SIG_CODESTREAM || signature == JXL_SIG_CONTAINER) { if (signature == JXL_SIG_CODESTREAM || signature == JXL_SIG_CONTAINER) {
return true; return true;
} }
@ -72,7 +77,7 @@ bool QJpegXLHandler::canRead(QIODevice *device)
bool QJpegXLHandler::ensureParsed() const bool QJpegXLHandler::ensureParsed() const
{ {
if (m_parseState == ParseJpegXLSuccess || m_parseState == ParseJpegXLBasicInfoParsed) { if (m_parseState == ParseJpegXLSuccess || m_parseState == ParseJpegXLBasicInfoParsed || m_parseState == ParseJpegXLFinished) {
return true; return true;
} }
if (m_parseState == ParseJpegXLError) { if (m_parseState == ParseJpegXLError) {
@ -90,7 +95,7 @@ bool QJpegXLHandler::ensureALLCounted() const
return false; return false;
} }
if (m_parseState == ParseJpegXLSuccess) { if (m_parseState == ParseJpegXLSuccess || m_parseState == ParseJpegXLFinished) {
return true; return true;
} }
@ -111,7 +116,7 @@ bool QJpegXLHandler::ensureDecoder()
return false; return false;
} }
JxlSignature signature = JxlSignatureCheck((const uint8_t *)m_rawData.constData(), m_rawData.size()); JxlSignature signature = JxlSignatureCheck(reinterpret_cast<const uint8_t *>(m_rawData.constData()), m_rawData.size());
if (signature != JXL_SIG_CODESTREAM && signature != JXL_SIG_CONTAINER) { if (signature != JXL_SIG_CODESTREAM && signature != JXL_SIG_CONTAINER) {
m_parseState = ParseJpegXLError; m_parseState = ParseJpegXLError;
return false; return false;
@ -139,7 +144,7 @@ bool QJpegXLHandler::ensureDecoder()
} }
} }
if (JxlDecoderSetInput(m_decoder, (const uint8_t *)m_rawData.constData(), m_rawData.size()) != JXL_DEC_SUCCESS) { if (JxlDecoderSetInput(m_decoder, reinterpret_cast<const uint8_t *>(m_rawData.constData()), m_rawData.size()) != JXL_DEC_SUCCESS) {
qWarning("ERROR: JxlDecoderSetInput failed"); qWarning("ERROR: JxlDecoderSetInput failed");
m_parseState = ParseJpegXLError; m_parseState = ParseJpegXLError;
return false; return false;
@ -273,8 +278,12 @@ bool QJpegXLHandler::countALLFrames()
size_t icc_size = 0; size_t icc_size = 0;
if (JxlDecoderGetICCProfileSize(m_decoder, &m_input_pixel_format, JXL_COLOR_PROFILE_TARGET_DATA, &icc_size) == JXL_DEC_SUCCESS) { if (JxlDecoderGetICCProfileSize(m_decoder, &m_input_pixel_format, JXL_COLOR_PROFILE_TARGET_DATA, &icc_size) == JXL_DEC_SUCCESS) {
if (icc_size > 0) { if (icc_size > 0) {
QByteArray icc_data((int)icc_size, 0); QByteArray icc_data(icc_size, 0);
if (JxlDecoderGetColorAsICCProfile(m_decoder, &m_input_pixel_format, JXL_COLOR_PROFILE_TARGET_DATA, (uint8_t *)icc_data.data(), icc_data.size()) if (JxlDecoderGetColorAsICCProfile(m_decoder,
&m_input_pixel_format,
JXL_COLOR_PROFILE_TARGET_DATA,
reinterpret_cast<uint8_t *>(icc_data.data()),
icc_data.size())
== JXL_DEC_SUCCESS) { == JXL_DEC_SUCCESS) {
m_colorspace = QColorSpace::fromIccProfile(icc_data); m_colorspace = QColorSpace::fromIccProfile(icc_data);
@ -397,7 +406,15 @@ bool QJpegXLHandler::decode_one_frame()
if (!rewind()) { if (!rewind()) {
return false; return false;
} }
// all frames in animation have been read
m_parseState = ParseJpegXLFinished;
} else {
m_parseState = ParseJpegXLSuccess;
} }
} else {
// the static image has been read
m_parseState = ParseJpegXLFinished;
} }
return true; return true;
@ -609,7 +626,7 @@ bool QJpegXLHandler::write(const QImage &image)
} }
if (!convert_color_profile && iccprofile.size() > 0) { if (!convert_color_profile && iccprofile.size() > 0) {
status = JxlEncoderSetICCProfile(encoder, (const uint8_t *)iccprofile.constData(), iccprofile.size()); status = JxlEncoderSetICCProfile(encoder, reinterpret_cast<const uint8_t *>(iccprofile.constData()), iccprofile.size());
if (status != JXL_ENC_SUCCESS) { if (status != JXL_ENC_SUCCESS) {
qWarning("JxlEncoderSetICCProfile failed!"); qWarning("JxlEncoderSetICCProfile failed!");
if (runner) { if (runner) {
@ -648,7 +665,7 @@ bool QJpegXLHandler::write(const QImage &image)
#endif #endif
if (image.hasAlphaChannel() || ((save_depth == 8) && (xsize % 4 == 0))) { if (image.hasAlphaChannel() || ((save_depth == 8) && (xsize % 4 == 0))) {
status = JxlEncoderAddImageFrame(encoder_options, &pixel_format, (void *)tmpimage.constBits(), buffer_size); status = JxlEncoderAddImageFrame(encoder_options, &pixel_format, static_cast<const void *>(tmpimage.constBits()), buffer_size);
} else { } else {
if (save_depth > 8) { // 16bit depth without alpha channel if (save_depth > 8) { // 16bit depth without alpha channel
uint16_t *tmp_buffer = new (std::nothrow) uint16_t[3 * xsize * ysize]; uint16_t *tmp_buffer = new (std::nothrow) uint16_t[3 * xsize * ysize];
@ -679,7 +696,7 @@ bool QJpegXLHandler::write(const QImage &image)
src_pixels += 2; // skipalpha src_pixels += 2; // skipalpha
} }
} }
status = JxlEncoderAddImageFrame(encoder_options, &pixel_format, (void *)tmp_buffer, buffer_size); status = JxlEncoderAddImageFrame(encoder_options, &pixel_format, static_cast<const void *>(tmp_buffer), buffer_size);
delete[] tmp_buffer; delete[] tmp_buffer;
} else { // 8bit depth without alpha channel } else { // 8bit depth without alpha channel
uchar *tmp_buffer8 = new (std::nothrow) uchar[3 * xsize * ysize]; uchar *tmp_buffer8 = new (std::nothrow) uchar[3 * xsize * ysize];
@ -698,7 +715,7 @@ bool QJpegXLHandler::write(const QImage &image)
memcpy(dest_pixels8, tmpimage.constScanLine(y), rowbytes); memcpy(dest_pixels8, tmpimage.constScanLine(y), rowbytes);
dest_pixels8 += rowbytes; dest_pixels8 += rowbytes;
} }
status = JxlEncoderAddImageFrame(encoder_options, &pixel_format, (void *)tmp_buffer8, buffer_size); status = JxlEncoderAddImageFrame(encoder_options, &pixel_format, static_cast<const void *>(tmp_buffer8), buffer_size);
delete[] tmp_buffer8; delete[] tmp_buffer8;
} }
} }
@ -856,6 +873,7 @@ bool QJpegXLHandler::jumpToNextImage()
} }
} }
m_parseState = ParseJpegXLSuccess;
return true; return true;
} }
@ -870,12 +888,14 @@ bool QJpegXLHandler::jumpToImage(int imageNumber)
} }
if (imageNumber == m_currentimage_index) { if (imageNumber == m_currentimage_index) {
m_parseState = ParseJpegXLSuccess;
return true; return true;
} }
if (imageNumber > m_currentimage_index) { if (imageNumber > m_currentimage_index) {
JxlDecoderSkipFrames(m_decoder, imageNumber - m_currentimage_index); JxlDecoderSkipFrames(m_decoder, imageNumber - m_currentimage_index);
m_currentimage_index = imageNumber; m_currentimage_index = imageNumber;
m_parseState = ParseJpegXLSuccess;
return true; return true;
} }
@ -887,6 +907,7 @@ bool QJpegXLHandler::jumpToImage(int imageNumber)
JxlDecoderSkipFrames(m_decoder, imageNumber); JxlDecoderSkipFrames(m_decoder, imageNumber);
} }
m_currentimage_index = imageNumber; m_currentimage_index = imageNumber;
m_parseState = ParseJpegXLSuccess;
return true; return true;
} }
@ -910,7 +931,7 @@ int QJpegXLHandler::loopCount() const
} }
if (m_basicinfo.have_animation) { if (m_basicinfo.have_animation) {
return 1; return (m_basicinfo.animation.num_loops > 0) ? m_basicinfo.animation.num_loops - 1 : -1;
} else { } else {
return 0; return 0;
} }
@ -930,7 +951,7 @@ bool QJpegXLHandler::rewind()
} }
} }
if (JxlDecoderSetInput(m_decoder, (const uint8_t *)m_rawData.constData(), m_rawData.size()) != JXL_DEC_SUCCESS) { if (JxlDecoderSetInput(m_decoder, reinterpret_cast<const uint8_t *>(m_rawData.constData()), m_rawData.size()) != JXL_DEC_SUCCESS) {
qWarning("ERROR: JxlDecoderSetInput failed"); qWarning("ERROR: JxlDecoderSetInput failed");
m_parseState = ParseJpegXLError; m_parseState = ParseJpegXLError;
return false; return false;

View File

@ -57,6 +57,7 @@ private:
ParseJpegXLNotParsed = 0, ParseJpegXLNotParsed = 0,
ParseJpegXLSuccess = 1, ParseJpegXLSuccess = 1,
ParseJpegXLBasicInfoParsed = 2, ParseJpegXLBasicInfoParsed = 2,
ParseJpegXLFinished = 3,
}; };
ParseJpegXLState m_parseState; ParseJpegXLState m_parseState;

View File

@ -57,6 +57,9 @@ bool KraHandler::canRead(QIODevice *device)
qWarning("KraHandler::canRead() called with no device"); qWarning("KraHandler::canRead() called with no device");
return false; return false;
} }
if (device->isSequential()) {
return false;
}
char buff[57]; char buff[57];
if (device->peek(buff, sizeof(buff)) == sizeof(buff)) { if (device->peek(buff, sizeof(buff)) == sizeof(buff)) {

View File

@ -56,6 +56,9 @@ bool OraHandler::canRead(QIODevice *device)
qWarning("OraHandler::canRead() called with no device"); qWarning("OraHandler::canRead() called with no device");
return false; return false;
} }
if (device->isSequential()) {
return false;
}
char buff[54]; char buff[54];
if (device->peek(buff, sizeof(buff)) == sizeof(buff)) { if (device->peek(buff, sizeof(buff)) == sizeof(buff)) {

View File

@ -174,7 +174,7 @@ static QDataStream &operator>>(QDataStream &s, PCXHEADER &ph)
// Skip the rest of the header // Skip the rest of the header
quint8 byte; quint8 byte;
while (s.device()->pos() < 128) { for (auto i = 0; i < 54; ++i) {
s >> byte; s >> byte;
} }
@ -684,12 +684,6 @@ bool PCXHandler::canRead(QIODevice *device)
return false; return false;
} }
// We do not support sequential images
// We need to know the current position to properly read the header
if (device->isSequential()) {
return false;
}
qint64 oldPos = device->pos(); qint64 oldPos = device->pos();
char head[1]; char head[1];

View File

@ -172,7 +172,8 @@ struct PSDLayerAndMaskSection {
if (globalLayerMaskInfo.size > -1) { if (globalLayerMaskInfo.size > -1) {
currentSize += globalLayerMaskInfo.size + 4; currentSize += globalLayerMaskInfo.size + 4;
} }
for (auto && v : additionalLayerInfo.values()) { auto aliv = additionalLayerInfo.values();
for (auto &&v : aliv) {
currentSize += (12 + v.size); currentSize += (12 + v.size);
if (v.signature == S_8B64) if (v.signature == S_8B64)
currentSize += 4; currentSize += 4;
@ -286,12 +287,6 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
qint32 sectioSize; qint32 sectioSize;
s >> sectioSize; s >> sectioSize;
#ifdef QT_DEBUG
auto pos = qint64();
if (auto dev = s.device())
pos = dev->pos();
#endif
// Reading Image resource block // Reading Image resource block
for (auto size = sectioSize; size > 0;) { for (auto size = sectioSize; size > 0;) {
// Length Description // Length Description
@ -356,14 +351,6 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
irs.insert(id, irb); irs.insert(id, irb);
} }
#ifdef QT_DEBUG
if (auto dev = s.device()) {
if ((dev->pos() - pos) != sectioSize) {
*ok = false;
}
}
#endif
return irs; return irs;
} }
@ -659,6 +646,9 @@ static bool IsValid(const PSDHeader &header)
// Check that the header is supported by this plugin. // Check that the header is supported by this plugin.
static bool IsSupported(const PSDHeader &header) static bool IsSupported(const PSDHeader &header)
{ {
if (!IsValid(header)) {
return false;
}
if (header.version != 1 && header.version != 2) { if (header.version != 1 && header.version != 2) {
return false; return false;
} }
@ -673,10 +663,15 @@ static bool IsSupported(const PSDHeader &header)
header.color_mode != CM_INDEXED && header.color_mode != CM_INDEXED &&
header.color_mode != CM_DUOTONE && header.color_mode != CM_DUOTONE &&
header.color_mode != CM_CMYK && header.color_mode != CM_CMYK &&
header.color_mode != CM_MULTICHANNEL &&
header.color_mode != CM_LABCOLOR && header.color_mode != CM_LABCOLOR &&
header.color_mode != CM_BITMAP) { header.color_mode != CM_BITMAP) {
return false; return false;
} }
if (header.color_mode == CM_MULTICHANNEL &&
header.channel_count < 4) {
return false;
}
return true; return true;
} }
@ -742,7 +737,8 @@ static QImage::Format imageFormat(const PSDHeader &header, bool alpha)
else else
format = header.channel_count < 4 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888; format = header.channel_count < 4 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888;
break; break;
case CM_CMYK: // Photoshop supports CMYK 8-bits and 16-bits only case CM_MULTICHANNEL: // Treat MCH as CMYK (number of channel check is done in IsSupported())
case CM_CMYK: // Photoshop supports CMYK/MCH 8-bits and 16-bits only
if (header.depth == 16) if (header.depth == 16)
format = header.channel_count < 5 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64; format = header.channel_count < 5 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64;
else if (header.depth == 8) else if (header.depth == 8)
@ -1091,7 +1087,7 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
} }
// Conversion to RGB // Conversion to RGB
if (header.color_mode == CM_CMYK) { if (header.color_mode == CM_CMYK || header.color_mode == CM_MULTICHANNEL) {
if (header.depth == 8) if (header.depth == 8)
cmykToRgb<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha); cmykToRgb<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
else else
@ -1250,35 +1246,27 @@ bool PSDHandler::canRead(QIODevice *device)
return false; return false;
} }
qint64 oldPos = device->pos(); device->startTransaction();
char head[4]; QDataStream s(device);
qint64 readBytes = device->read(head, sizeof(head)); s.setByteOrder(QDataStream::BigEndian);
if (readBytes < 0) {
qWarning() << "Read failed" << device->errorString();
return false;
}
if (readBytes != sizeof(head)) { PSDHeader header;
if (device->isSequential()) { s >> header;
while (readBytes > 0) {
device->ungetChar(head[readBytes-- - 1]); device->rollbackTransaction();
}
} else { if (s.status() != QDataStream::Ok) {
device->seek(oldPos);
}
return false; return false;
} }
if (device->isSequential()) { if (device->isSequential()) {
while (readBytes > 0) { if (header.color_mode == CM_CMYK || header.color_mode == CM_LABCOLOR || header.color_mode == CM_MULTICHANNEL) {
device->ungetChar(head[readBytes-- - 1]); return false;
} }
} else {
device->seek(oldPos);
} }
return qstrncmp(head, "8BPS", 4) == 0; return IsSupported(header);
} }
QImageIOPlugin::Capabilities PSDPlugin::capabilities(QIODevice *device, const QByteArray &format) const QImageIOPlugin::Capabilities PSDPlugin::capabilities(QIODevice *device, const QByteArray &format) const

View File

@ -19,11 +19,7 @@
#define NOMINMAX #define NOMINMAX
#endif #endif
#if defined(Q_OS_WINDOWS)
#include <libraw.h>
#else
#include <libraw/libraw.h> #include <libraw/libraw.h>
#endif
#ifdef QT_DEBUG #ifdef QT_DEBUG
// This should be used to exclude the local QIODevice wrapper of the LibRaw_abstract_datastream interface. // This should be used to exclude the local QIODevice wrapper of the LibRaw_abstract_datastream interface.
@ -115,12 +111,19 @@ public:
} }
virtual int read(void *ptr, size_t sz, size_t nmemb) override virtual int read(void *ptr, size_t sz, size_t nmemb) override
{ {
auto read = m_device->read(reinterpret_cast<char *>(ptr), sz * nmemb); qint64 read = 0;
if (read < 1) { if (sz == 0) {
return 0; return 0;
} }
if (auto o = read % sz) { auto data = reinterpret_cast<char*>(ptr);
seek(-(sz - o), SEEK_CUR); for (qint64 r = 0, size = sz * nmemb; read < size; read += r) {
if (m_device->atEnd()) {
break;
}
r = m_device->read(data + read, size - read);
if (r < 1) {
break;
}
} }
return read / sz; return read / sz;
} }
@ -673,6 +676,9 @@ bool LoadRAW(QImageIOHandler *handler, QImage &img)
if (params.output_color == 4) { if (params.output_color == 4) {
img.setColorSpace(QColorSpace(QColorSpace::ProPhotoRgb)); img.setColorSpace(QColorSpace(QColorSpace::ProPhotoRgb));
} }
if (params.output_color == 7) {
img.setColorSpace(QColorSpace(QColorSpace::DisplayP3));
}
} }
// *** Set the metadata // *** Set the metadata
@ -855,6 +861,14 @@ int RAWHandler::currentImageNumber() const
bool RAWHandler::canRead(QIODevice *device) bool RAWHandler::canRead(QIODevice *device)
{ {
if (!device) {
qWarning("RAWHandler::canRead() called with no device");
return false;
}
if (device->isSequential()) {
return false;
}
device->startTransaction(); device->startTransaction();
std::unique_ptr<LibRaw> rawProcessor(new LibRaw); std::unique_ptr<LibRaw> rawProcessor(new LibRaw);

View File

@ -3341,6 +3341,9 @@ bool XCFHandler::canRead(QIODevice *device)
qCDebug(XCFPLUGIN) << "XCFHandler::canRead() called with no device"; qCDebug(XCFPLUGIN) << "XCFHandler::canRead() called with no device";
return false; return false;
} }
if (device->isSequential()) {
return false;
}
qint64 oldPos = device->pos(); qint64 oldPos = device->pos();