Compare commits

...

41 Commits

Author SHA1 Message Date
63056c52f9 GIT_SILENT Upgrade ECM and KF version requirements for 5.103.0 release. 2023-02-05 09:22:42 +00:00
2997f7ae8d psd: conversion speed improvements (kf5)
- Improved performance converting CMYK files by \~10% by replacing divisions with multiplications.
- Improved performance converting LAB files by \~50% by replacing std::pow with fastPow (approximated pow function).
2023-02-03 20:55:49 +00:00
0b4741f4b7 Fix writing TGA alpha depth flag
Correctly write alpha channel depth as 8-bit.

(cherry picked from commit 20cec27ae8)
2023-02-02 01:11:32 +01:00
bc52c03981 HDR support removed from RAW plugin 2023-01-30 21:59:09 +00:00
c1c57d9a11 heif: reject invalid files with zero size 2023-01-29 16:21:01 +01:00
4c6d2b92b6 GIT_SILENT Upgrade ECM and KF version requirements for 5.102.0 release. 2023-01-07 00:28:55 +00:00
05bd9397b3 raw: tweak seek implementation
libraw uses fseek when doing files, which allows seeking past the end
without problems, so do the same, otherwise when we report oss-fuzz
issues they say "give me an example to reproduce" and since our seek
and their seek don't behave the same it's hard to convince them
to fix their code
2022-12-14 23:56:20 +01:00
f4ca3f6783 heif: fix error handling 2022-12-13 11:11:38 +01:00
a30f043e5d heif: rewrite plugin to use only libheif C API
Using C-API instead of C++ libheif API has following advantages:
- More libheif features available (for ex.: strict decoding)
- Linking with static build of libheif is possible
- No need to enable exceptions
2022-12-05 22:43:41 +01:00
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
73 changed files with 1062 additions and 658 deletions

View File

@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.16)
project(KImageFormats)
include(FeatureSummary)
find_package(ECM 5.99.0 NO_MODULE)
find_package(ECM 5.103.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)

View File

@ -136,7 +136,9 @@ kimageformats_write_tests(
# kimageformats_write_tests(eps)
#endif()
if (OpenEXR_FOUND)
# FIXME: OpenEXR tests
kimageformats_read_tests(
exr
)
endif()
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.

Before

Width:  |  Height:  |  Size: 189 KiB

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 115 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 <QCoreApplication>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QImage>
#include <QImageReader>
@ -18,6 +19,44 @@
#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)
{
QFile file(filename);
@ -56,7 +95,7 @@ int main(int argc, char **argv)
QCoreApplication::removeLibraryPath(QStringLiteral(PLUGIN_DIR));
QCoreApplication::addLibraryPath(QStringLiteral(PLUGIN_DIR));
QCoreApplication::setApplicationName(QStringLiteral("readtest"));
QCoreApplication::setApplicationVersion(QStringLiteral("1.0.0"));
QCoreApplication::setApplicationVersion(QStringLiteral("1.1.0"));
QCommandLineParser parser;
parser.setApplicationDescription(QStringLiteral("Performs basic image conversion checking."));
@ -98,6 +137,7 @@ int main(int argc, char **argv)
int passed = 0;
int failed = 0;
int skipped = 0;
QTextStream(stdout) << "********* "
<< "Starting basic read tests for " << suffix << " images *********\n";
@ -111,72 +151,91 @@ int main(int argc, char **argv)
QTextStream(stdout) << "QImageReader::supportedImageFormats: " << formatStrings.join(", ") << "\n";
const QFileInfoList lstImgDir = imgdir.entryInfoList();
for (const QFileInfo &fi : lstImgDir) {
if (!fi.suffix().compare("png", Qt::CaseInsensitive)) {
continue;
}
int suffixPos = fi.filePath().count() - suffix.count();
QString inputfile = fi.filePath();
QString expfile = fi.filePath().replace(suffixPos, suffix.count(), QStringLiteral("png"));
QString expfilename = QFileInfo(expfile).fileName();
QImageReader inputReader(inputfile, format);
QImageReader expReader(expfile, "png");
QImage inputImage;
QImage expImage;
if (!expReader.read(&expImage)) {
QTextStream(stdout) << "ERROR: " << fi.fileName() << ": could not load " << expfilename << ": " << expReader.errorString() << "\n";
++failed;
continue;
}
if (!inputReader.canRead()) {
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": failed can read: " << inputReader.errorString() << "\n";
++failed;
continue;
}
if (!inputReader.read(&inputImage)) {
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": failed to load: " << inputReader.errorString() << "\n";
++failed;
continue;
}
if (expImage.width() != inputImage.width()) {
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": width was " << inputImage.width() << " but " << expfilename << " width was "
<< expImage.width() << "\n";
++failed;
} else if (expImage.height() != inputImage.height()) {
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": height was " << inputImage.height() << " but " << expfilename << " height was "
<< expImage.height() << "\n";
++failed;
// 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 {
QImage::Format inputFormat = preferredFormat(inputImage.format());
QImage::Format expFormat = preferredFormat(expImage.format());
QImage::Format cmpFormat = inputFormat == expFormat ? inputFormat : QImage::Format_ARGB32;
QTextStream(stdout) << "* Run on RANDOM ACCESS device\n";
}
for (const QFileInfo &fi : lstImgDir) {
if (!fi.suffix().compare("png", Qt::CaseInsensitive)) {
continue;
}
int suffixPos = fi.filePath().count() - suffix.count();
QString inputfile = fi.filePath();
QString expfile = fi.filePath().replace(suffixPos, suffix.count(), QStringLiteral("png"));
QString expfilename = QFileInfo(expfile).fileName();
if (inputImage.format() != cmpFormat) {
QTextStream(stdout) << "INFO : " << fi.fileName() << ": converting " << fi.fileName() << " from " << formatToString(inputImage.format())
<< " to " << formatToString(cmpFormat) << '\n';
inputImage = inputImage.convertToFormat(cmpFormat);
}
if (expImage.format() != cmpFormat) {
QTextStream(stdout) << "INFO : " << fi.fileName() << ": converting " << expfilename << " from " << formatToString(expImage.format()) << " to "
<< formatToString(cmpFormat) << '\n';
expImage = expImage.convertToFormat(cmpFormat);
}
if (fuzzyeq(inputImage, expImage, fuzziness)) {
QTextStream(stdout) << "PASS : " << fi.fileName() << "\n";
++passed;
} else {
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": differs from " << expfilename << "\n";
writeImageData("expected data", fi.fileName() + QLatin1String("-expected.data"), expImage);
writeImageData("actual data", fi.fileName() + QLatin1String("-actual.data"), inputImage);
std::unique_ptr<QIODevice> inputDevice(seq ? new SequentialFile(inputfile) : new QFile(inputfile));
QImageReader inputReader(inputDevice.get(), format);
QImageReader expReader(expfile, "png");
QImage inputImage;
QImage expImage;
// inputImage is auto-rotated to final orientation
inputReader.setAutoTransform(true);
if (!expReader.read(&expImage)) {
QTextStream(stdout) << "ERROR: " << fi.fileName() << ": could not load " << expfilename << ": " << expReader.errorString() << "\n";
++failed;
continue;
}
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";
++failed;
}
continue;
}
if (!inputReader.read(&inputImage)) {
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": failed to load: " << inputReader.errorString() << "\n";
++failed;
continue;
}
if (expImage.width() != inputImage.width()) {
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": width was " << inputImage.width() << " but " << expfilename << " width was "
<< expImage.width() << "\n";
++failed;
} else if (expImage.height() != inputImage.height()) {
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": height was " << inputImage.height() << " but " << expfilename << " height was "
<< expImage.height() << "\n";
++failed;
} else {
QImage::Format inputFormat = preferredFormat(inputImage.format());
QImage::Format expFormat = preferredFormat(expImage.format());
QImage::Format cmpFormat = inputFormat == expFormat ? inputFormat : QImage::Format_ARGB32;
if (inputImage.format() != cmpFormat) {
QTextStream(stdout) << "INFO : " << fi.fileName() << ": converting " << fi.fileName() << " from " << formatToString(inputImage.format())
<< " to " << formatToString(cmpFormat) << '\n';
inputImage = inputImage.convertToFormat(cmpFormat);
}
if (expImage.format() != cmpFormat) {
QTextStream(stdout) << "INFO : " << fi.fileName() << ": converting " << expfilename << " from " << formatToString(expImage.format())
<< " to " << formatToString(cmpFormat) << '\n';
expImage = expImage.convertToFormat(cmpFormat);
}
if (fuzzyeq(inputImage, expImage, fuzziness)) {
QTextStream(stdout) << "PASS : " << fi.fileName() << "\n";
++passed;
} else {
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": differs from " << expfilename << "\n";
writeImageData("expected data", fi.fileName() + QLatin1String("-expected.data"), expImage);
writeImageData("actual data", fi.fileName() + QLatin1String("-actual.data"), inputImage);
++failed;
}
}
}
}
QTextStream(stdout) << "Totals: " << passed << " passed, " << failed << " failed\n";
QTextStream(stdout) << "Totals: " << passed << " passed, " << skipped << " skipped, " << failed << " failed\n";
QTextStream(stdout) << "********* "
<< "Finished basic read tests for " << suffix << " images *********\n";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -2,7 +2,7 @@
# Find the LibRaw library <https://www.libraw.org>
# This module defines
# 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_r_LIBRARIES, the libraries needed to use LibRaw (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})
ENDIF()
FIND_PATH(LibRaw_INCLUDE_DIR libraw.h
FIND_PATH(LibRaw_INCLUDE_DIR libraw/libraw.h
HINTS
${PC_LIBRAW_INCLUDEDIR}
${PC_LibRaw_INCLUDE_DIRS}
PATH_SUFFIXES libraw
)
FIND_LIBRARY(LibRaw_LIBRARIES NAMES raw
@ -43,7 +42,7 @@ FIND_LIBRARY(LibRaw_r_LIBRARIES NAMES raw_r
)
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})
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}")
ELSE()
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()

View File

@ -20,19 +20,26 @@ endfunction()
##################################
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)
kimageformats_add_plugin(kimg_avif SOURCES "avif.cpp")
target_link_libraries(kimg_avif "avif")
install(FILES avif.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES avif.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
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)
kimageformats_add_plugin(kimg_eps SOURCES eps.cpp)
target_link_libraries(kimg_eps Qt${QT_MAJOR_VERSION}::PrintSupport)
install(FILES eps.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES eps.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif()
endif()
##################################
# need this for Qt's version of the plugin
install(FILES jp2.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
if (QT_MAJOR_VERSION STREQUAL "5")
# need this for Qt's version of the plugin
install(FILES jp2.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
##################################
@ -65,21 +76,27 @@ if(OpenEXR_FOUND)
endif()
kde_target_enable_exceptions(kimg_exr PRIVATE)
install(FILES exr.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES exr.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif()
##################################
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()
##################################
if (LibHeif_FOUND)
kimageformats_add_plugin(kimg_heif SOURCES heif.cpp)
target_link_libraries(kimg_heif PkgConfig::LibHeif)
kde_target_enable_exceptions(kimg_heif PRIVATE)
install(FILES heif.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES heif.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif()
##################################
@ -90,43 +107,60 @@ if (LibJXL_FOUND AND LibJXLThreads_FOUND)
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/)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES jxl.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif()
##################################
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)
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)
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)
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)
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)
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)
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 +168,9 @@ if (LibRaw_FOUND)
kimageformats_add_plugin(kimg_raw SOURCES raw.cpp)
kde_enable_exceptions()
target_link_libraries(kimg_raw LibRaw::LibRaw)
install(FILES raw.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES raw.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif()
##################################
@ -143,10 +179,14 @@ if (KF5Archive_FOUND)
kimageformats_add_plugin(kimg_kra SOURCES kra.cpp)
target_link_libraries(kimg_kra KF5::Archive)
install(FILES kra.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES kra.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
kimageformats_add_plugin(kimg_ora SOURCES ora.cpp)
target_link_libraries(kimg_ora KF5::Archive)
install(FILES ora.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
if (QT_MAJOR_VERSION STREQUAL "5")
install(FILES ora.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
endif()
endif()

View File

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

View File

@ -42,6 +42,11 @@ bool QAVIFHandler::canRead() const
if (m_parseState != ParseAvifError) {
setFormat("avif");
if (m_parseState == ParseAvifFinished) {
return false;
}
return true;
}
return false;
@ -58,7 +63,7 @@ bool QAVIFHandler::canRead(QIODevice *device)
}
avifROData input;
input.data = (const uint8_t *)header.constData();
input.data = reinterpret_cast<const uint8_t *>(header.constData());
input.size = header.size();
if (avifPeekCompatibleFileType(&input)) {
@ -69,7 +74,7 @@ bool QAVIFHandler::canRead(QIODevice *device)
bool QAVIFHandler::ensureParsed() const
{
if (m_parseState == ParseAvifSuccess || m_parseState == ParseAvifMetadata) {
if (m_parseState == ParseAvifSuccess || m_parseState == ParseAvifMetadata || m_parseState == ParseAvifFinished) {
return true;
}
if (m_parseState == ParseAvifError) {
@ -83,7 +88,7 @@ bool QAVIFHandler::ensureParsed() const
bool QAVIFHandler::ensureOpened() const
{
if (m_parseState == ParseAvifSuccess) {
if (m_parseState == ParseAvifSuccess || m_parseState == ParseAvifFinished) {
return true;
}
if (m_parseState == ParseAvifError) {
@ -111,7 +116,7 @@ bool QAVIFHandler::ensureDecoder()
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();
if (avifPeekCompatibleFileType(&m_rawAvifData) == AVIF_FALSE) {
@ -132,6 +137,10 @@ bool QAVIFHandler::ensureDecoder()
m_decoder->strictFlags = AVIF_STRICT_DISABLED;
#endif
#if AVIF_VERSION >= 110000
m_decoder->imageDimensionLimit = 65535;
#endif
avifResult decodeResult;
decodeResult = avifDecoderSetIOMemory(m_decoder, m_rawAvifData.data, m_rawAvifData.size);
@ -246,7 +255,7 @@ bool QAVIFHandler::decode_one_frame()
QColorSpace colorspace;
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);
if (!colorspace.isValid()) {
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;
#endif
#if (AVIF_VERSION >= 80400) && (AVIF_VERSION <= 100100)
#if AVIF_VERSION >= 80400
if (m_decoder->imageCount > 1) {
/* accelerate animated AVIF */
rgb.chromaUpsampling = AVIF_CHROMA_UPSAMPLING_FASTEST;
@ -351,12 +360,7 @@ bool QAVIFHandler::decode_one_frame()
rgb.rowBytes = result.bytesPerLine();
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);
#endif
if (res != AVIF_RESULT_OK) {
qWarning("ERROR in avifImageYUVToRGB: %s", avifResultToString(res));
return false;
@ -459,6 +463,13 @@ bool QAVIFHandler::read(QImage *image)
*image = m_current_image;
if (imageCount() >= 2) {
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;
}
@ -756,7 +767,7 @@ bool QAVIFHandler::write(const QImage &image)
avif->transferCharacteristics = transfer_to_save;
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;
@ -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);
#endif
if (res != AVIF_RESULT_OK) {
qWarning("ERROR in avifImageRGBToYUV: %s", avifResultToString(res));
return false;
@ -914,6 +921,7 @@ bool QAVIFHandler::jumpToNextImage()
if (m_decoder->imageIndex >= 0) {
if (m_decoder->imageCount < 2) {
m_parseState = ParseAvifSuccess;
return true;
}
@ -958,10 +966,12 @@ bool QAVIFHandler::jumpToImage(int imageNumber)
if (m_decoder->imageCount < 2) { // not an animation
if (imageNumber == 0) {
return ensureOpened();
} else {
return false;
if (ensureOpened()) {
m_parseState = ParseAvifSuccess;
return true;
}
}
return false;
}
if (imageNumber < 0 || imageNumber >= m_decoder->imageCount) { // wrong index
@ -970,6 +980,7 @@ bool QAVIFHandler::jumpToImage(int imageNumber)
if (imageNumber == m_decoder->imageIndex) { // we are here already
m_must_jump_to_next_image = false;
m_parseState = ParseAvifSuccess;
return true;
}
@ -1028,7 +1039,8 @@ int QAVIFHandler::loopCount() const
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)
@ -1045,12 +1057,26 @@ QPointF QAVIFHandler::CompatibleChromacity(qreal chrX, qreal chrY)
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") {
return Capabilities(CanRead | CanWrite);
Capabilities format_cap;
if (isAvifDecoderAvailable) {
format_cap |= CanRead;
}
if (isAvifEncoderAvailable) {
format_cap |= CanWrite;
}
return format_cap;
}
if (format == "avifs") {
return Capabilities(CanRead);
Capabilities format_cap;
if (isAvifDecoderAvailable) {
format_cap |= CanRead;
}
return format_cap;
}
if (!format.isEmpty()) {
@ -1061,10 +1087,10 @@ QImageIOPlugin::Capabilities QAVIFPlugin::capabilities(QIODevice *device, const
}
Capabilities cap;
if (device->isReadable() && QAVIFHandler::canRead(device)) {
if (device->isReadable() && QAVIFHandler::canRead(device) && isAvifDecoderAvailable) {
cap |= CanRead;
}
if (device->isWritable()) {
if (device->isWritable() && isAvifEncoderAvailable) {
cap |= CanWrite;
}
return cap;

View File

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

View File

@ -0,0 +1,35 @@
/*
Approximated math functions used into conversions.
SPDX-FileCopyrightText: Edward Kmett
SPDX-FileCopyrightText: 2023 Mirco Miranda <mircomir@outlook.com>
SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef FASTMATH_P_H
#define FASTMATH_P_H
#include <QtGlobal>
/*!
* \brief fastPow
* Based on Edward Kmett code released into the public domain.
* See also: https://github.com/ekmett/approximate
*/
inline double fastPow(double x, double y)
{
union {
double d;
qint32 i[2];
} u = {x};
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
u.i[1] = qint32(y * (u.i[1] - 1072632447) + 1072632447);
u.i[0] = 0;
#else // never tested
u.i[0] = qint32(y * (u.i[0] - 1072632447) + 1072632447);
u.i[1] = 0;
#endif
return u.d;
}
#endif // FASTMATH_P_H

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@
#include <QByteArray>
#include <QImage>
#include <QImageIOPlugin>
#include <QMutex>
class HEIFHandler : public QImageIOHandler
{
@ -29,6 +30,9 @@ public:
void setOption(ImageOption option, const QVariant &value) override;
bool supportsOption(ImageOption option) const override;
static bool isHeifDecoderAvailable();
static bool isHeifEncoderAvailable();
private:
static bool isSupportedBMFFType(const QByteArray &header);
bool ensureParsed() const;
@ -43,6 +47,18 @@ private:
ParseHeicState m_parseState;
int m_quality;
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

View File

@ -48,6 +48,11 @@ bool QJpegXLHandler::canRead() const
if (m_parseState != ParseJpegXLError) {
setFormat("jxl");
if (m_parseState == ParseJpegXLFinished) {
return false;
}
return true;
}
return false;
@ -63,7 +68,7 @@ bool QJpegXLHandler::canRead(QIODevice *device)
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) {
return true;
}
@ -72,7 +77,7 @@ bool QJpegXLHandler::canRead(QIODevice *device)
bool QJpegXLHandler::ensureParsed() const
{
if (m_parseState == ParseJpegXLSuccess || m_parseState == ParseJpegXLBasicInfoParsed) {
if (m_parseState == ParseJpegXLSuccess || m_parseState == ParseJpegXLBasicInfoParsed || m_parseState == ParseJpegXLFinished) {
return true;
}
if (m_parseState == ParseJpegXLError) {
@ -90,7 +95,7 @@ bool QJpegXLHandler::ensureALLCounted() const
return false;
}
if (m_parseState == ParseJpegXLSuccess) {
if (m_parseState == ParseJpegXLSuccess || m_parseState == ParseJpegXLFinished) {
return true;
}
@ -111,7 +116,7 @@ bool QJpegXLHandler::ensureDecoder()
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) {
m_parseState = ParseJpegXLError;
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");
m_parseState = ParseJpegXLError;
return false;
@ -273,8 +278,12 @@ bool QJpegXLHandler::countALLFrames()
size_t icc_size = 0;
if (JxlDecoderGetICCProfileSize(m_decoder, &m_input_pixel_format, JXL_COLOR_PROFILE_TARGET_DATA, &icc_size) == JXL_DEC_SUCCESS) {
if (icc_size > 0) {
QByteArray icc_data((int)icc_size, 0);
if (JxlDecoderGetColorAsICCProfile(m_decoder, &m_input_pixel_format, JXL_COLOR_PROFILE_TARGET_DATA, (uint8_t *)icc_data.data(), icc_data.size())
QByteArray icc_data(icc_size, 0);
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) {
m_colorspace = QColorSpace::fromIccProfile(icc_data);
@ -397,7 +406,15 @@ bool QJpegXLHandler::decode_one_frame()
if (!rewind()) {
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;
@ -609,7 +626,7 @@ bool QJpegXLHandler::write(const QImage &image)
}
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) {
qWarning("JxlEncoderSetICCProfile failed!");
if (runner) {
@ -648,7 +665,7 @@ bool QJpegXLHandler::write(const QImage &image)
#endif
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 {
if (save_depth > 8) { // 16bit depth without alpha channel
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
}
}
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;
} else { // 8bit depth without alpha channel
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);
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;
}
}
@ -856,6 +873,7 @@ bool QJpegXLHandler::jumpToNextImage()
}
}
m_parseState = ParseJpegXLSuccess;
return true;
}
@ -870,12 +888,14 @@ bool QJpegXLHandler::jumpToImage(int imageNumber)
}
if (imageNumber == m_currentimage_index) {
m_parseState = ParseJpegXLSuccess;
return true;
}
if (imageNumber > m_currentimage_index) {
JxlDecoderSkipFrames(m_decoder, imageNumber - m_currentimage_index);
m_currentimage_index = imageNumber;
m_parseState = ParseJpegXLSuccess;
return true;
}
@ -887,6 +907,7 @@ bool QJpegXLHandler::jumpToImage(int imageNumber)
JxlDecoderSkipFrames(m_decoder, imageNumber);
}
m_currentimage_index = imageNumber;
m_parseState = ParseJpegXLSuccess;
return true;
}
@ -910,7 +931,7 @@ int QJpegXLHandler::loopCount() const
}
if (m_basicinfo.have_animation) {
return 1;
return (m_basicinfo.animation.num_loops > 0) ? m_basicinfo.animation.num_loops - 1 : -1;
} else {
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");
m_parseState = ParseJpegXLError;
return false;

View File

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

View File

@ -57,6 +57,9 @@ bool KraHandler::canRead(QIODevice *device)
qWarning("KraHandler::canRead() called with no device");
return false;
}
if (device->isSequential()) {
return false;
}
char buff[57];
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");
return false;
}
if (device->isSequential()) {
return false;
}
char buff[54];
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
quint8 byte;
while (s.device()->pos() < 128) {
for (auto i = 0; i < 54; ++i) {
s >> byte;
}
@ -684,12 +684,6 @@ bool PCXHandler::canRead(QIODevice *device)
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();
char head[1];

View File

@ -3,7 +3,7 @@
SPDX-FileCopyrightText: 2003 Ignacio Castaño <castano@ludicon.com>
SPDX-FileCopyrightText: 2015 Alex Merry <alex.merry@kde.org>
SPDX-FileCopyrightText: 2022 Mirco Miranda <mircomir@outlook.com>
SPDX-FileCopyrightText: 2022-2023 Mirco Miranda <mircomir@outlook.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
@ -21,7 +21,6 @@
/*
* Limitations of the current code:
* - 32-bit float image are converted to 16-bit integer image.
* NOTE: Qt 6.2 allow 32-bit float images (RGB only)
* - Other color spaces cannot directly be read due to lack of QImage support for
* color spaces other than RGB (and Grayscale). Where possible, a conversion
* to RGB is done:
@ -33,6 +32,7 @@
* color management engine (e.g. LittleCMS).
*/
#include "fastmath_p.h"
#include "psd_p.h"
#include "util_p.h"
@ -51,7 +51,7 @@ typedef quint8 uchar;
* This should not be a problem because the Qt's QColorSpace supports the linear
* sRgb colorspace.
*
* Using linear conversion, the loading speed is improved by 4x. Anyway, if you are using
* Using linear conversion, the loading speed is slightly improved. Anyway, if you are using
* an software that discard color info, you should comment it.
*
* At the time I'm writing (07/2022), Gwenview and Krita supports linear sRgb but KDE
@ -172,7 +172,8 @@ struct PSDLayerAndMaskSection {
if (globalLayerMaskInfo.size > -1) {
currentSize += globalLayerMaskInfo.size + 4;
}
for (auto && v : additionalLayerInfo.values()) {
auto aliv = additionalLayerInfo.values();
for (auto &&v : aliv) {
currentSize += (12 + v.size);
if (v.signature == S_8B64)
currentSize += 4;
@ -286,12 +287,6 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
qint32 sectioSize;
s >> sectioSize;
#ifdef QT_DEBUG
auto pos = qint64();
if (auto dev = s.device())
pos = dev->pos();
#endif
// Reading Image resource block
for (auto size = sectioSize; size > 0;) {
// Length Description
@ -356,14 +351,6 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
irs.insert(id, irb);
}
#ifdef QT_DEBUG
if (auto dev = s.device()) {
if ((dev->pos() - pos) != sectioSize) {
*ok = false;
}
}
#endif
return irs;
}
@ -659,6 +646,9 @@ static bool IsValid(const PSDHeader &header)
// Check that the header is supported by this plugin.
static bool IsSupported(const PSDHeader &header)
{
if (!IsValid(header)) {
return false;
}
if (header.version != 1 && header.version != 2) {
return false;
}
@ -673,10 +663,15 @@ static bool IsSupported(const PSDHeader &header)
header.color_mode != CM_INDEXED &&
header.color_mode != CM_DUOTONE &&
header.color_mode != CM_CMYK &&
header.color_mode != CM_MULTICHANNEL &&
header.color_mode != CM_LABCOLOR &&
header.color_mode != CM_BITMAP) {
return false;
}
if (header.color_mode == CM_MULTICHANNEL &&
header.channel_count < 4) {
return false;
}
return true;
}
@ -742,13 +737,14 @@ static QImage::Format imageFormat(const PSDHeader &header, bool alpha)
else
format = header.channel_count < 4 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888;
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)
format = header.channel_count < 5 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64;
else if (header.depth == 8)
format = header.channel_count < 5 || !alpha ? QImage::Format_RGB888 : QImage::Format_RGBA8888;
break;
case CM_LABCOLOR: // Photoshop supports LAB 8-bits and 16-bits only
case CM_LABCOLOR: // Photoshop supports LAB 8-bits and 16-bits only
if (header.depth == 16)
format = header.channel_count < 4 || !alpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64;
else if (header.depth == 8)
@ -849,6 +845,7 @@ inline void cmykToRgb(uchar *target, qint32 targetChannels, const char *source,
auto s = reinterpret_cast<const T*>(source);
auto t = reinterpret_cast<T*>(target);
auto max = double(std::numeric_limits<T>::max());
auto invmax = 1.0 / max; // speed improvements by ~10%
if (sourceChannels < 4) {
qDebug() << "cmykToRgb: image is not a valid CMYK!";
@ -857,10 +854,10 @@ inline void cmykToRgb(uchar *target, qint32 targetChannels, const char *source,
for (qint32 w = 0; w < width; ++w) {
auto ps = s + sourceChannels * w;
auto C = 1 - *(ps + 0) / max;
auto M = 1 - *(ps + 1) / max;
auto Y = 1 - *(ps + 2) / max;
auto K = 1 - *(ps + 3) / max;
auto C = 1 - *(ps + 0) * invmax;
auto M = 1 - *(ps + 1) * invmax;
auto Y = 1 - *(ps + 2) * invmax;
auto K = 1 - *(ps + 3) * invmax;
auto pt = t + targetChannels * w;
*(pt + 0) = T(std::min(max - (C * (1 - K) + K) * max + 0.5, max));
@ -885,8 +882,9 @@ inline double gammaCorrection(double linear)
#ifdef PSD_FAST_LAB_CONVERSION
return linear;
#else
// NOTE: pow() slow down the performance by a 4 factor :(
return (linear > 0.0031308 ? 1.055 * std::pow(linear, 1.0 / 2.4) - 0.055 : 12.92 * linear);
// Replacing fastPow with std::pow the conversion time is 2/3 times longer: using fastPow
// there are minimal differences in the conversion that are not visually noticeable.
return (linear > 0.0031308 ? 1.055 * fastPow(linear, 1.0 / 2.4) - 0.055 : 12.92 * linear);
#endif
}
@ -896,6 +894,7 @@ inline void labToRgb(uchar *target, qint32 targetChannels, const char *source, q
auto s = reinterpret_cast<const T*>(source);
auto t = reinterpret_cast<T*>(target);
auto max = double(std::numeric_limits<T>::max());
auto invmax = 1.0 / max;
if (sourceChannels < 3) {
qDebug() << "labToRgb: image is not a valid LAB!";
@ -904,14 +903,14 @@ inline void labToRgb(uchar *target, qint32 targetChannels, const char *source, q
for (qint32 w = 0; w < width; ++w) {
auto ps = s + sourceChannels * w;
auto L = (*(ps + 0) / max) * 100.0;
auto A = (*(ps + 1) / max) * 255.0 - 128.0;
auto B = (*(ps + 2) / max) * 255.0 - 128.0;
auto L = (*(ps + 0) * invmax) * 100.0;
auto A = (*(ps + 1) * invmax) * 255.0 - 128.0;
auto B = (*(ps + 2) * invmax) * 255.0 - 128.0;
// converting LAB to XYZ (D65 illuminant)
auto Y = (L + 16.0) / 116.0;
auto X = A / 500.0 + Y;
auto Z = Y - B / 200.0;
auto Y = (L + 16.0) * (1.0 / 116.0);
auto X = A * (1.0 / 500.0) + Y;
auto Z = Y - B * (1.0 / 200.0);
// NOTE: use the constants of the illuminant of the target RGB color space
X = finv(X) * 0.9504; // D50: * 0.9642
@ -1091,7 +1090,7 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
}
// Conversion to RGB
if (header.color_mode == CM_CMYK) {
if (header.color_mode == CM_CMYK || header.color_mode == CM_MULTICHANNEL) {
if (header.depth == 8)
cmykToRgb<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
else
@ -1250,35 +1249,27 @@ bool PSDHandler::canRead(QIODevice *device)
return false;
}
qint64 oldPos = device->pos();
device->startTransaction();
char head[4];
qint64 readBytes = device->read(head, sizeof(head));
if (readBytes < 0) {
qWarning() << "Read failed" << device->errorString();
return false;
}
QDataStream s(device);
s.setByteOrder(QDataStream::BigEndian);
if (readBytes != sizeof(head)) {
if (device->isSequential()) {
while (readBytes > 0) {
device->ungetChar(head[readBytes-- - 1]);
}
} else {
device->seek(oldPos);
}
PSDHeader header;
s >> header;
device->rollbackTransaction();
if (s.status() != QDataStream::Ok) {
return false;
}
if (device->isSequential()) {
while (readBytes > 0) {
device->ungetChar(head[readBytes-- - 1]);
if (header.color_mode == CM_CMYK || header.color_mode == CM_LABCOLOR || header.color_mode == CM_MULTICHANNEL) {
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

View File

@ -19,11 +19,7 @@
#define NOMINMAX
#endif
#if defined(Q_OS_WINDOWS)
#include <libraw.h>
#else
#include <libraw/libraw.h>
#endif
#ifdef QT_DEBUG
// This should be used to exclude the local QIODevice wrapper of the LibRaw_abstract_datastream interface.
@ -49,7 +45,6 @@ const auto supported_formats = QSet<QByteArray>{
"dcs", "dc2", "dcr", "dng", "drf", "dxo",
"eip", "erf",
"fff",
"hdr",
"iiq",
"k25", "kc2", "kdc",
"mdc", "mef", "mfw", "mos", "mrw",
@ -115,12 +110,19 @@ public:
}
virtual int read(void *ptr, size_t sz, size_t nmemb) override
{
auto read = m_device->read(reinterpret_cast<char *>(ptr), sz * nmemb);
if (read < 1) {
qint64 read = 0;
if (sz == 0) {
return 0;
}
if (auto o = read % sz) {
seek(-(sz - o), SEEK_CUR);
auto data = reinterpret_cast<char*>(ptr);
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;
}
@ -138,7 +140,7 @@ public:
if (whence == SEEK_END) {
pos = size + o;
}
if (pos < 0 || pos > size || m_device->isSequential()) {
if (pos < 0 || m_device->isSequential()) {
return -1;
}
return m_device->seek(pos) ? 0 : -1;
@ -673,6 +675,9 @@ bool LoadRAW(QImageIOHandler *handler, QImage &img)
if (params.output_color == 4) {
img.setColorSpace(QColorSpace(QColorSpace::ProPhotoRgb));
}
if (params.output_color == 7) {
img.setColorSpace(QColorSpace(QColorSpace::DisplayP3));
}
}
// *** Set the metadata
@ -855,6 +860,14 @@ int RAWHandler::currentImageNumber() const
bool RAWHandler::canRead(QIODevice *device)
{
if (!device) {
qWarning("RAWHandler::canRead() called with no device");
return false;
}
if (device->isSequential()) {
return false;
}
device->startTransaction();
std::unique_ptr<LibRaw> rawProcessor(new LibRaw);

View File

@ -7,7 +7,6 @@
"dcs", "dc2", "dcr", "dng", "drf", "dxo",
"eip", "erf",
"fff",
"hdr",
"iiq",
"k25", "kdc", "kc2",
"mdc", "mef", "mfw", "mos", "mrw",
@ -27,7 +26,6 @@
"image/x-kodak-dcs", "image/x-dc2", "image/x-kodak-dcr", "image/x-adobe-dng", "image/x-drf", "image/x-dxo",
"image/x-epson-eip", "image/x-epson-erf",
"image/x-fff",
"image/x-hdr",
"image/x-iiq",
"image/x-kodak-k25", "image/x-kodak-kdc", "image/x-kodak-kc2",
"image/x-minolta-mdc", "image/x-mamiya-mef", "image/x-mfw", "image/x-aptus-mos", "image/x-minolta-mrw",

View File

@ -430,6 +430,9 @@ bool TGAHandler::write(const QImage &image)
const QImage &img = image;
const bool hasAlpha = (img.format() == QImage::Format_ARGB32);
static constexpr quint8 originTopLeft = TGA_ORIGIN_UPPER + TGA_ORIGIN_LEFT; // 0x20
static constexpr quint8 alphaChannel8Bits = 0x08;
for (int i = 0; i < 12; i++) {
s << targaMagic[i];
}
@ -438,7 +441,7 @@ bool TGAHandler::write(const QImage &image)
s << quint16(img.width()); // width
s << quint16(img.height()); // height
s << quint8(hasAlpha ? 32 : 24); // depth (24 bit RGB + 8 bit alpha)
s << quint8(hasAlpha ? 0x24 : 0x20); // top left image (0x20) + 8 bit alpha (0x4)
s << quint8(hasAlpha ? originTopLeft + alphaChannel8Bits : originTopLeft); // top left image (0x20) + 8 bit alpha (0x8)
for (int y = 0; y < img.height(); y++) {
for (int x = 0; x < img.width(); x++) {

View File

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