Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a3c7f2c86 | ||
|
|
43e6efb0e5 | ||
|
|
b7a407a752 | ||
|
|
62c45582df | ||
|
|
a738553dc9 | ||
|
|
0862db49b8 | ||
|
|
05af59e819 | ||
|
|
5d6dfb9012 | ||
|
|
72518dcc1a | ||
|
|
a6027264d4 | ||
|
|
47ea774621 | ||
|
|
5ed0affc95 | ||
|
|
42e9733060 | ||
|
|
bae45287ad | ||
|
|
458257b7a3 | ||
|
|
09452b0964 | ||
|
|
c7baa057a6 | ||
|
|
c57af25d36 | ||
|
|
320ded493a |
@@ -1,11 +1,11 @@
|
|||||||
cmake_minimum_required(VERSION 3.29)
|
cmake_minimum_required(VERSION 3.29)
|
||||||
|
|
||||||
set(KF_VERSION "6.29.0") # handled by release scripts
|
set(KF_VERSION "6.31.0") # handled by release scripts
|
||||||
set(KF_DEP_VERSION "6.29.0") # handled by release scripts
|
set(KF_DEP_VERSION "6.30.0") # handled by release scripts
|
||||||
project(KImageFormats VERSION ${KF_VERSION})
|
project(KImageFormats VERSION ${KF_VERSION})
|
||||||
|
|
||||||
include(FeatureSummary)
|
include(FeatureSummary)
|
||||||
find_package(ECM 6.29.0 NO_MODULE)
|
find_package(ECM 6.30.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)
|
||||||
|
|
||||||
|
|||||||
@@ -392,6 +392,22 @@ The plugin can set the following additional metadata:
|
|||||||
- `EXRLayerName`: A string containing the name of the EXR layer used to decode
|
- `EXRLayerName`: A string containing the name of the EXR layer used to decode
|
||||||
the image.
|
the image.
|
||||||
|
|
||||||
|
When writing, it is possible to specify compression ratio and quality. The
|
||||||
|
values you can set depend on the version of the OpenEXR library you're using.
|
||||||
|
Please check the [exr_p.h](./src/imageformats/exr_p.h) for more
|
||||||
|
info.
|
||||||
|
|
||||||
|
When writing, it is also possible to specify a subtype:
|
||||||
|
- `RGB` (default): preserves full-resolution color data (R, G, B) without
|
||||||
|
subsampling or loss of chromatic detail.
|
||||||
|
- `YC`: converts RGB data into luminance (Y) and 2x2 subsampled chroma
|
||||||
|
(RY, BY), cutting raw color data by ~50% for significantly smaller file
|
||||||
|
sizes. **If the image does not have a height and width that are multiples
|
||||||
|
of 2, `RGB` is used.**
|
||||||
|
|
||||||
|
> [!note]
|
||||||
|
> When writing grayscale images, the subtype makes no difference.
|
||||||
|
|
||||||
### The EPS plugin
|
### The EPS plugin
|
||||||
|
|
||||||
The plugin uses `Ghostscript` to convert the raster image. When reading it
|
The plugin uses `Ghostscript` to convert the raster image. When reading it
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ cd $SRC/aom
|
|||||||
mkdir build.libavif
|
mkdir build.libavif
|
||||||
cd build.libavif
|
cd build.libavif
|
||||||
extra_libaom_flags='-DAOM_MAX_ALLOCABLE_MEMORY=536870912 -DDO_RANGE_CHECK_CLAMP=1'
|
extra_libaom_flags='-DAOM_MAX_ALLOCABLE_MEMORY=536870912 -DDO_RANGE_CHECK_CLAMP=1'
|
||||||
cmake -G Ninja -DBUILD_SHARED_LIBS=0 -DENABLE_DOCS=0 -DENABLE_EXAMPLES=0 -DENABLE_TESTDATA=0 -DENABLE_TESTS=0 -DENABLE_TOOLS=0 -DCONFIG_PIC=1 -DAOM_TARGET_CPU=generic -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=12288 -DDECODE_WIDTH_LIMIT=12288 -DAOM_EXTRA_C_FLAGS="${extra_libaom_flags}" -DAOM_EXTRA_CXX_FLAGS="${extra_libaom_flags}" ..
|
cmake -G Ninja -DBUILD_SHARED_LIBS=0 -DENABLE_APPS=0 -DENABLE_DOCS=0 -DENABLE_EXAMPLES=0 -DENABLE_TESTDATA=0 -DENABLE_TESTS=0 -DENABLE_TOOLS=0 -DCONFIG_PIC=1 -DAOM_TARGET_CPU=generic -DCONFIG_SIZE_LIMIT=1 -DDECODE_HEIGHT_LIMIT=12288 -DDECODE_WIDTH_LIMIT=12288 -DAOM_EXTRA_C_FLAGS="${extra_libaom_flags}" -DAOM_EXTRA_CXX_FLAGS="${extra_libaom_flags}" ..
|
||||||
ninja install
|
ninja install
|
||||||
|
|
||||||
# Build libavif
|
# Build libavif
|
||||||
@@ -186,7 +186,7 @@ echo "$HANDLER_TYPES" | while read format; do
|
|||||||
/usr/libexec/moc $SRC/kimageformats/src/imageformats/$format.cpp -o $format.moc
|
/usr/libexec/moc $SRC/kimageformats/src/imageformats/$format.cpp -o $format.moc
|
||||||
header=`ls $SRC/kimageformats/src/imageformats/$format*.h`
|
header=`ls $SRC/kimageformats/src/imageformats/$format*.h`
|
||||||
/usr/libexec/moc $header -o moc_`basename $header .h`.cpp
|
/usr/libexec/moc $header -o moc_`basename $header .h`.cpp
|
||||||
$CXX $CXXFLAGS $LDFLAGS -fPIC -DKIMG_FUZZER_${format}=1 -std=c++17 autotests/ossfuzz/kimgio_fuzzer.cc $SRC/kimageformats/src/imageformats/$format.cpp $SRC/kimageformats/src/imageformats/scanlineconverter.cpp $SRC/kimageformats/src/imageformats/microexif.cpp $SRC/kimageformats/src/imageformats/chunks.cpp -o $OUT/$fuzz_target_name -DJXL_STATIC_DEFINE -DJXL_THREADS_STATIC_DEFINE -DJXL_CMS_STATIC_DEFINE -D__ANSI__ -I $SRC/kimageformats/src/imageformats/ -I $SRC/libavif/include/ -I $SRC/libjxl/build/lib/include/ -I $SRC/libjxl/lib/include/ -I /usr/local/include/OpenEXR/ -I /usr/local/include/KF6/KArchive/ -I /usr/local/include/openjpeg-2.5 -I /usr/local/include/Imath -I $SRC/jxrlib/common/include -I $SRC/jxrlib/jxrgluelib -I $SRC/jxrlib/image/sys -I /usr/include/QtCore/ -I /usr/include/QtGui/ -I . $SRC/libavif/build/libavif.a /usr/local/lib/libheif.a /usr/local/lib/libde265.a /usr/local/lib/libopenh264.a $SRC/aom/build.libavif/libaom.a $SRC/libjxl/build/lib/libjxl_threads.a $SRC/libjxl/build/lib/libjxl.a $SRC/libjxl/build/lib/libjxl_cms.a $SRC/libjxl/build/third_party/highway/libhwy.a $SRC/libjxl/build/third_party/brotli/libbrotlidec.a $SRC/libjxl/build/third_party/brotli/libbrotlienc.a $SRC/libjxl/build/third_party/brotli/libbrotlicommon.a -lQt6Gui -lQt6Core -lQt6BundledLibpng -lQt6BundledHarfbuzz -lm -lQt6BundledPcre2 -ldl -lpthread $LIB_FUZZING_ENGINE /usr/local/lib/libz.a /usr/local/lib/x86_64-linux-gnu/libKF6Archive.a /usr/local/lib/libz.a /usr/local/lib/libraw.a /usr/local/lib/libOpenEXR-3_4.a /usr/local/lib/libIex-3_4.a /usr/local/lib/libImath-3_2.a /usr/local/lib/libIlmThread-3_4.a /usr/local/lib/libOpenEXRCore-3_4.a /usr/local/lib/libOpenEXRUtil-3_4.a /usr/local/lib/libopenjp2.a /usr/local/lib/libzstd.a $SRC/jxrlib/build/libjxrglue.a $SRC/jxrlib/build/libjpegxr.a /usr/local/lib/liblzma.a /usr/local/lib/libbz2.a -lclang_rt.builtins
|
$CXX $CXXFLAGS $LDFLAGS -fPIC -DKIMG_FUZZER_${format}=1 -std=c++17 autotests/ossfuzz/kimgio_fuzzer.cc $SRC/kimageformats/src/imageformats/$format.cpp $SRC/kimageformats/src/imageformats/scanlineconverter.cpp $SRC/kimageformats/src/imageformats/photoshop.cpp $SRC/kimageformats/src/imageformats/microexif.cpp $SRC/kimageformats/src/imageformats/chunks.cpp -o $OUT/$fuzz_target_name -DJXL_STATIC_DEFINE -DJXL_THREADS_STATIC_DEFINE -DJXL_CMS_STATIC_DEFINE -D__ANSI__ -I $SRC/kimageformats/src/imageformats/ -I $SRC/libavif/include/ -I $SRC/libjxl/build/lib/include/ -I $SRC/libjxl/lib/include/ -I /usr/local/include/OpenEXR/ -I /usr/local/include/KF6/KArchive/ -I /usr/local/include/openjpeg-2.5 -I /usr/local/include/Imath -I $SRC/jxrlib/common/include -I $SRC/jxrlib/jxrgluelib -I $SRC/jxrlib/image/sys -I /usr/include/QtCore/ -I /usr/include/QtGui/ -I . $SRC/libavif/build/libavif.a /usr/local/lib/libheif.a /usr/local/lib/libde265.a /usr/local/lib/libopenh264.a $SRC/aom/build.libavif/libaom.a $SRC/libjxl/build/lib/libjxl_threads.a $SRC/libjxl/build/lib/libjxl.a $SRC/libjxl/build/lib/libjxl_cms.a $SRC/libjxl/build/third_party/highway/libhwy.a $SRC/libjxl/build/third_party/brotli/libbrotlidec.a $SRC/libjxl/build/third_party/brotli/libbrotlienc.a $SRC/libjxl/build/third_party/brotli/libbrotlicommon.a -lQt6Gui -lQt6Core -lQt6BundledLibpng -lQt6BundledHarfbuzz -lm -lQt6BundledPcre2 -ldl -lpthread $LIB_FUZZING_ENGINE /usr/local/lib/libz.a /usr/local/lib/x86_64-linux-gnu/libKF6Archive.a /usr/local/lib/libz.a /usr/local/lib/libraw.a /usr/local/lib/libOpenEXR-3_4.a /usr/local/lib/libIex-3_4.a /usr/local/lib/libImath-3_2.a /usr/local/lib/libIlmThread-3_4.a /usr/local/lib/libOpenEXRCore-3_4.a /usr/local/lib/libOpenEXRUtil-3_4.a /usr/local/lib/libopenjp2.a /usr/local/lib/libzstd.a $SRC/jxrlib/build/libjxrglue.a $SRC/jxrlib/build/libjpegxr.a /usr/local/lib/liblzma.a /usr/local/lib/libbz2.a -lclang_rt.builtins
|
||||||
|
|
||||||
# -lclang_rt.builtins in the previous line is a temporary workaround to avoid a linker error "undefined reference to __truncsfhf2". Investigate why this is needed here, but not anywhere else, and possibly remove it.
|
# -lclang_rt.builtins in the previous line is a temporary workaround to avoid a linker error "undefined reference to __truncsfhf2". Investigate why this is needed here, but not anywhere else, and possibly remove it.
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,27 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
|||||||
b.open(QIODevice::ReadOnly);
|
b.open(QIODevice::ReadOnly);
|
||||||
handler->setDevice(&b);
|
handler->setDevice(&b);
|
||||||
handler->canRead();
|
handler->canRead();
|
||||||
handler->read(&i);
|
|
||||||
|
bool is_animated = false;
|
||||||
|
if (handler->supportsOption(QImageIOHandler::Animation)) {
|
||||||
|
is_animated = handler->option(QImageIOHandler::Animation).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_animated) { // animated image
|
||||||
|
if (handler->supportsOption(QImageIOHandler::Size)) {
|
||||||
|
handler->option(QImageIOHandler::Size).toSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
handler->imageCount();
|
||||||
|
handler->loopCount();
|
||||||
|
handler->read(&i);
|
||||||
|
handler->nextImageDelay();
|
||||||
|
handler->currentImageNumber();
|
||||||
|
handler->jumpToImage(0);
|
||||||
|
handler->jumpToNextImage();
|
||||||
|
} else { // static image
|
||||||
|
handler->read(&i);
|
||||||
|
}
|
||||||
|
|
||||||
delete handler;
|
delete handler;
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ git clone --depth 1 -b master https://invent.kde.org/frameworks/extra-cmake-modu
|
|||||||
git clone --depth 1 --branch=dev git://code.qt.io/qt/qtbase.git
|
git clone --depth 1 --branch=dev git://code.qt.io/qt/qtbase.git
|
||||||
git clone --depth 1 --branch=dev git://code.qt.io/qt/qttools.git
|
git clone --depth 1 --branch=dev git://code.qt.io/qt/qttools.git
|
||||||
git clone --depth 1 -b master https://invent.kde.org/frameworks/karchive.git
|
git clone --depth 1 -b master https://invent.kde.org/frameworks/karchive.git
|
||||||
git clone --depth 1 -b v3.14.1 https://aomedia.googlesource.com/aom
|
git clone --depth 1 -b v3.15.0 https://aomedia.googlesource.com/aom
|
||||||
git clone --depth 1 -b v1.4.2 https://github.com/AOMediaCodec/libavif.git
|
git clone --depth 1 -b v1.4.2 https://github.com/AOMediaCodec/libavif.git
|
||||||
git clone --depth 1 https://github.com/strukturag/libde265.git
|
git clone --depth 1 https://github.com/strukturag/libde265.git
|
||||||
git clone --depth 1 -b v2.5.4 https://github.com/uclouvain/openjpeg.git
|
git clone --depth 1 -b v2.5.4 https://github.com/uclouvain/openjpeg.git
|
||||||
|
|||||||
|
After Width: | Height: | Size: 255 KiB |
@@ -0,0 +1,7 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName": "grid.png",
|
||||||
|
"fuzziness": 2,
|
||||||
|
"description": "Minimum fuzziness value to pass the test on all architectures."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
After Width: | Height: | Size: 277 KiB |
@@ -0,0 +1,33 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "alphatest_rgba.tif",
|
||||||
|
"colorSpace" : {
|
||||||
|
"description" : "sRGB build-in (Profilo RGB lineare)",
|
||||||
|
"primaries" : "SRgb",
|
||||||
|
"transferFunction" : "Linear",
|
||||||
|
"gamma" : 1
|
||||||
|
},
|
||||||
|
"metadata" : [
|
||||||
|
{
|
||||||
|
"key" : "ModificationDate",
|
||||||
|
"value" : "2026-09-04T11:45:06"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Software" ,
|
||||||
|
"value" : "Adobe Photoshop 27.10 (Windows)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Author",
|
||||||
|
"value" : "Mirco Miranda"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Description",
|
||||||
|
"value" : "This test is designed to detect problems with alpha encoding. Specifically, it detects incorrect use of associated values (premultiplied alpha) and unassociated values (normal alpha)."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"resolution" : {
|
||||||
|
"dotsPerMeterX" : 11811,
|
||||||
|
"dotsPerMeterY" : 11811
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -7,9 +7,19 @@
|
|||||||
"transferFunction" : "Linear",
|
"transferFunction" : "Linear",
|
||||||
"gamma" : 1
|
"gamma" : 1
|
||||||
},
|
},
|
||||||
|
"metadata" : [
|
||||||
|
{
|
||||||
|
"key" : "ModificationDate",
|
||||||
|
"value" : "2026-04-15T10:48:48"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Software" ,
|
||||||
|
"value" : "Adobe Photoshop 27.5 (Windows)"
|
||||||
|
}
|
||||||
|
],
|
||||||
"resolution" : {
|
"resolution" : {
|
||||||
"dotsPerMeterX" : 3937,
|
"dotsPerMeterX" : 11811,
|
||||||
"dotsPerMeterY" : 3937
|
"dotsPerMeterY" : 11811
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
|
After Width: | Height: | Size: 199 B |
|
After Width: | Height: | Size: 242 B |
|
After Width: | Height: | Size: 234 B |
|
After Width: | Height: | Size: 232 B |
|
After Width: | Height: | Size: 221 B |
|
After Width: | Height: | Size: 222 B |
|
After Width: | Height: | Size: 243 B |
|
After Width: | Height: | Size: 228 B |
|
After Width: | Height: | Size: 244 B |
@@ -0,0 +1,7 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName": "konqi.jpg",
|
||||||
|
"fuzziness": 72,
|
||||||
|
"description": "Minimum fuzziness value to pass the test on all architectures."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
After Width: | Height: | Size: 362 KiB |
@@ -57,7 +57,7 @@ endif()
|
|||||||
##################################
|
##################################
|
||||||
|
|
||||||
if(OpenEXR_FOUND)
|
if(OpenEXR_FOUND)
|
||||||
kimageformats_add_plugin(kimg_exr SOURCES exr.cpp scanlineconverter.cpp)
|
kimageformats_add_plugin(kimg_exr SOURCES exr.cpp microexif.cpp photoshop.cpp scanlineconverter.cpp)
|
||||||
if(TARGET OpenEXR::OpenEXR)
|
if(TARGET OpenEXR::OpenEXR)
|
||||||
target_link_libraries(kimg_exr PRIVATE OpenEXR::OpenEXR)
|
target_link_libraries(kimg_exr PRIVATE OpenEXR::OpenEXR)
|
||||||
else()
|
else()
|
||||||
@@ -117,7 +117,7 @@ kimageformats_add_plugin(kimg_pfm SOURCES pfm.cpp)
|
|||||||
|
|
||||||
##################################
|
##################################
|
||||||
|
|
||||||
kimageformats_add_plugin(kimg_psd SOURCES psd.cpp microexif.cpp scanlineconverter.cpp)
|
kimageformats_add_plugin(kimg_psd SOURCES psd.cpp microexif.cpp photoshop.cpp scanlineconverter.cpp)
|
||||||
|
|
||||||
##################################
|
##################################
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <QScopeGuard>
|
#include <QScopeGuard>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QtEndian>
|
#include <QtEndian>
|
||||||
|
#include <QtNumeric>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
@@ -267,7 +268,17 @@ int ANIHandler::nextImageDelay() const
|
|||||||
rate = m_displayRates.at(previousImage);
|
rate = m_displayRates.at(previousImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rate * 1000 / 60;
|
int delayNumerator;
|
||||||
|
if (qMulOverflow(rate, 1000, &delayNumerator)) {
|
||||||
|
int delay;
|
||||||
|
// We're losing a bit of precision by dividing first and multiplying later
|
||||||
|
// but this is most probably a broken file so not much of an issue
|
||||||
|
if (qMulOverflow(rate / 60, 1000, &delay)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return delay;
|
||||||
|
}
|
||||||
|
return delayNumerator / 60;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ANIHandler::supportsOption(ImageOption option) const
|
bool ANIHandler::supportsOption(ImageOption option) const
|
||||||
|
|||||||
@@ -1160,25 +1160,19 @@ bool QAVIFHandler::jumpToNextImage()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
avifResult decodeResult;
|
|
||||||
|
|
||||||
if (m_decoder->imageIndex >= 0) {
|
if (m_decoder->imageIndex >= 0) {
|
||||||
if (m_decoder->imageCount < 2) {
|
if (m_decoder->imageCount < 2) {
|
||||||
m_parseState = ParseAvifSuccess;
|
// single image, there is no next image
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_decoder->imageIndex >= m_decoder->imageCount - 1) { // start from beginning
|
if (m_decoder->imageIndex >= m_decoder->imageCount - 1) {
|
||||||
decodeResult = avifDecoderReset(m_decoder);
|
// do not start from beginning when at the end already
|
||||||
if (decodeResult != AVIF_RESULT_OK) {
|
return false;
|
||||||
qCWarning(LOG_AVIFPLUGIN, "ERROR in avifDecoderReset: %s", avifResultToString(decodeResult));
|
|
||||||
m_parseState = ParseAvifError;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decodeResult = avifDecoderNextImage(m_decoder);
|
avifResult decodeResult = avifDecoderNextImage(m_decoder);
|
||||||
|
|
||||||
if (decodeResult != AVIF_RESULT_OK) {
|
if (decodeResult != AVIF_RESULT_OK) {
|
||||||
qCWarning(LOG_AVIFPLUGIN, "ERROR: Failed to decode Next image in sequence: %s", avifResultToString(decodeResult));
|
qCWarning(LOG_AVIFPLUGIN, "ERROR: Failed to decode Next image in sequence: %s", avifResultToString(decodeResult));
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "exr_p.h"
|
#include "exr_p.h"
|
||||||
|
#include "microexif_p.h"
|
||||||
|
#include "photoshop_p.h"
|
||||||
#include "scanlineconverter_p.h"
|
#include "scanlineconverter_p.h"
|
||||||
#include "util_p.h"
|
#include "util_p.h"
|
||||||
|
|
||||||
@@ -200,12 +202,17 @@ void K_OStream::seekg(Imf::Int64 pos)
|
|||||||
m_dev->seek(pos);
|
m_dev->seek(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define EXR_SUBFORMAT_RGB QByteArray("RGB")
|
||||||
|
|
||||||
|
#define EXR_SUBFORMAT_YC QByteArray("YC")
|
||||||
|
|
||||||
EXRHandler::EXRHandler()
|
EXRHandler::EXRHandler()
|
||||||
: m_compressionRatio(-1)
|
: m_compressionRatio(-1)
|
||||||
, m_quality(-1)
|
, m_quality(-1)
|
||||||
, m_imageNumber(0)
|
, m_imageNumber(0)
|
||||||
, m_imageCount(0)
|
, m_imageCount(0)
|
||||||
, m_startPos(-1)
|
, m_startPos(-1)
|
||||||
|
, m_subType(EXR_SUBFORMAT_RGB)
|
||||||
{
|
{
|
||||||
// Set the number of threads to use (0 is allowed)
|
// Set the number of threads to use (0 is allowed)
|
||||||
Imf::setGlobalThreadCount(QThread::idealThreadCount() / 2);
|
Imf::setGlobalThreadCount(QThread::idealThreadCount() / 2);
|
||||||
@@ -225,7 +232,7 @@ bool EXRHandler::canRead() const
|
|||||||
static QImage::Format imageFormat(const Imf::RgbaInputFile &file)
|
static QImage::Format imageFormat(const Imf::RgbaInputFile &file)
|
||||||
{
|
{
|
||||||
auto isRgba = file.channels() & Imf::RgbaChannels::WRITE_A;
|
auto isRgba = file.channels() & Imf::RgbaChannels::WRITE_A;
|
||||||
return (isRgba ? QImage::Format_RGBA16FPx4 : QImage::Format_RGBX16FPx4);
|
return (isRgba ? QImage::Format_RGBA16FPx4_Premultiplied : QImage::Format_RGBX16FPx4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -368,7 +375,8 @@ static void readMetadata(const Imf::Header &header, QImage &image)
|
|||||||
|
|
||||||
// shot metadata
|
// shot metadata
|
||||||
if (auto isoSpeed = header.findTypedAttribute<Imf::FloatAttribute>("isoSpeed")) {
|
if (auto isoSpeed = header.findTypedAttribute<Imf::FloatAttribute>("isoSpeed")) {
|
||||||
image.setText(QStringLiteral(META_KEY_ISOSPEEDRATINGS), QLocale::c().toString(qRound(isoSpeed->value())));
|
if (auto v = qRoundOrZero(isoSpeed->value()))
|
||||||
|
image.setText(QStringLiteral(META_KEY_ISOSPEEDRATINGS), QLocale::c().toString(v));
|
||||||
}
|
}
|
||||||
if (auto expTime = header.findTypedAttribute<Imf::FloatAttribute>("expTime")) {
|
if (auto expTime = header.findTypedAttribute<Imf::FloatAttribute>("expTime")) {
|
||||||
image.setText(QStringLiteral(META_KEY_EXPOSURETIME), QLocale::c().toString(expTime->value()));
|
image.setText(QStringLiteral(META_KEY_EXPOSURETIME), QLocale::c().toString(expTime->value()));
|
||||||
@@ -379,6 +387,34 @@ static void readMetadata(const Imf::Header &header, QImage &image)
|
|||||||
if (auto focalLen = header.findTypedAttribute<Imf::FloatAttribute>("effectiveFocalLength")) {
|
if (auto focalLen = header.findTypedAttribute<Imf::FloatAttribute>("effectiveFocalLength")) {
|
||||||
image.setText(QStringLiteral(META_KEY_FOCALLENGTH), QLocale::c().toString(focalLen->value()));
|
image.setText(QStringLiteral(META_KEY_FOCALLENGTH), QLocale::c().toString(focalLen->value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Photoshop image resource section
|
||||||
|
if (auto adobe = header.findTypedAttribute<Imf::OpaqueAttribute>("adobe_rsrc")) {
|
||||||
|
auto &&data = adobe->data();
|
||||||
|
auto ba = QByteArray(data, data.size());
|
||||||
|
ba.prepend((data.size()) & 0xFF);
|
||||||
|
ba.prepend((data.size() >> 8) & 0xFF);
|
||||||
|
ba.prepend((data.size() >> 16) & 0xFF);
|
||||||
|
ba.prepend((data.size() >> 24) & 0xFF);
|
||||||
|
QDataStream ds(ba);
|
||||||
|
ds.setByteOrder(QDataStream::BigEndian);
|
||||||
|
auto ok = false;
|
||||||
|
auto irs = readImageResourceSection(ds, &ok);
|
||||||
|
if (ok) {
|
||||||
|
if (irs.contains(IRI_EXIFDATA1)) {
|
||||||
|
auto exif = MicroExif::fromByteArray(irs.value(IRI_EXIFDATA1).data);
|
||||||
|
exif.updateImageMetadata(image);
|
||||||
|
exif.updateImageResolution(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (irs.contains(IRI_XMPMETADATA)) {
|
||||||
|
auto irb = irs.value(IRI_XMPMETADATA);
|
||||||
|
auto xmp = QString::fromUtf8(irb.data);
|
||||||
|
if (!xmp.isEmpty())
|
||||||
|
image.setText(QStringLiteral(META_KEY_XMP_ADOBE), xmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -513,9 +549,9 @@ bool makePreview(const QImage &image, Imf::Array2D<Imf::PreviewRgba> &pixels)
|
|||||||
|
|
||||||
QImage preview;
|
QImage preview;
|
||||||
if (w > h) {
|
if (w > h) {
|
||||||
preview = image.scaledToWidth(256).convertToFormat(QImage::Format_ARGB32);
|
preview = image.scaledToWidth(256).convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||||
} else {
|
} else {
|
||||||
preview = image.scaledToHeight(256).convertToFormat(QImage::Format_ARGB32);
|
preview = image.scaledToHeight(256).convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||||
}
|
}
|
||||||
if (preview.isNull()) {
|
if (preview.isNull()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -687,6 +723,10 @@ bool EXRHandler::write(const QImage &image)
|
|||||||
// write the EXR
|
// write the EXR
|
||||||
K_OStream ostr(device());
|
K_OStream ostr(device());
|
||||||
auto channelsType = image.hasAlphaChannel() ? Imf::RgbaChannels::WRITE_RGBA : Imf::RgbaChannels::WRITE_RGB;
|
auto channelsType = image.hasAlphaChannel() ? Imf::RgbaChannels::WRITE_RGBA : Imf::RgbaChannels::WRITE_RGB;
|
||||||
|
if (m_subType == EXR_SUBFORMAT_YC && !(width % 2) && !(height % 2)) {
|
||||||
|
// Works only with images with height and width that are multiples of 2.
|
||||||
|
channelsType = channelsType == Imf::RgbaChannels::WRITE_RGBA ? Imf::RgbaChannels::WRITE_YCA : Imf::RgbaChannels::WRITE_YC;
|
||||||
|
}
|
||||||
if (image.format() == QImage::Format_Mono ||
|
if (image.format() == QImage::Format_Mono ||
|
||||||
image.format() == QImage::Format_MonoLSB ||
|
image.format() == QImage::Format_MonoLSB ||
|
||||||
image.format() == QImage::Format_Grayscale16 ||
|
image.format() == QImage::Format_Grayscale16 ||
|
||||||
@@ -698,7 +738,7 @@ bool EXRHandler::write(const QImage &image)
|
|||||||
pixels.resizeErase(EXR_LINES_PER_BLOCK, width);
|
pixels.resizeErase(EXR_LINES_PER_BLOCK, width);
|
||||||
|
|
||||||
// convert the image and write into the stream
|
// convert the image and write into the stream
|
||||||
auto convFormat = image.hasAlphaChannel() ? QImage::Format_RGBA32FPx4 : QImage::Format_RGBX32FPx4;
|
auto convFormat = image.hasAlphaChannel() ? QImage::Format_RGBA32FPx4_Premultiplied : QImage::Format_RGBX32FPx4;
|
||||||
ScanLineConverter slc(convFormat);
|
ScanLineConverter slc(convFormat);
|
||||||
slc.setDefaultSourceColorSpace(QColorSpace(QColorSpace::SRgb));
|
slc.setDefaultSourceColorSpace(QColorSpace(QColorSpace::SRgb));
|
||||||
slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgbLinear));
|
slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgbLinear));
|
||||||
@@ -742,6 +782,15 @@ void EXRHandler::setOption(ImageOption option, const QVariant &value)
|
|||||||
m_quality = q;
|
m_quality = q;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (option == QImageIOHandler::SubType) {
|
||||||
|
auto subType = value.toByteArray();
|
||||||
|
auto list = EXRHandler::option(QImageIOHandler::SupportedSubTypes).value<QList<QByteArray>>();
|
||||||
|
if (list.contains(subType)) {
|
||||||
|
m_subType = subType;
|
||||||
|
} else {
|
||||||
|
m_subType = EXR_SUBFORMAT_RGB;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EXRHandler::supportsOption(ImageOption option) const
|
bool EXRHandler::supportsOption(ImageOption option) const
|
||||||
@@ -760,6 +809,12 @@ bool EXRHandler::supportsOption(ImageOption option) const
|
|||||||
if (option == QImageIOHandler::Quality) {
|
if (option == QImageIOHandler::Quality) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (option == QImageIOHandler::SubType) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (option == QImageIOHandler::SupportedSubTypes) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -814,6 +869,14 @@ QVariant EXRHandler::option(ImageOption option) const
|
|||||||
v = QVariant(m_quality);
|
v = QVariant(m_quality);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (option == QImageIOHandler::SupportedSubTypes) {
|
||||||
|
return QVariant::fromValue(QList<QByteArray>() << EXR_SUBFORMAT_RGB << EXR_SUBFORMAT_YC);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option == QImageIOHandler::SubType) {
|
||||||
|
return QVariant::fromValue(m_subType);
|
||||||
|
}
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,6 +118,14 @@ private:
|
|||||||
* The initial device position to allow multi image load (cache value).
|
* The initial device position to allow multi image load (cache value).
|
||||||
*/
|
*/
|
||||||
qint64 m_startPos;
|
qint64 m_startPos;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief m_subType
|
||||||
|
* The EXR subtype to use when writing:
|
||||||
|
* - RGB: preserves full-resolution color data (R, G, B) without subsampling or loss of chromatic detail (default).
|
||||||
|
* - YC: converts RGB data into luminance (Y) and 2x2 subsampled chroma (RY, BY), cutting raw color data by ~50% for significantly smaller file sizes.
|
||||||
|
*/
|
||||||
|
QByteArray m_subType;
|
||||||
};
|
};
|
||||||
|
|
||||||
class EXRPlugin : public QImageIOPlugin
|
class EXRPlugin : public QImageIOPlugin
|
||||||
|
|||||||
@@ -642,10 +642,31 @@ bool HEIFHandler::read_crop(void *heif_handle, const void *heif_ctx, const QSize
|
|||||||
if (heif_item_get_properties_of_type(ctx, item_id, heif_item_property_type_transform_crop, &crop_id, 1) > 0) {
|
if (heif_item_get_properties_of_type(ctx, item_id, heif_item_property_type_transform_crop, &crop_id, 1) > 0) {
|
||||||
int l = 0, t = 0, r = 0, b = 0;
|
int l = 0, t = 0, r = 0, b = 0;
|
||||||
heif_item_get_property_transform_crop_borders(ctx, item_id, crop_id, size.width(), size.height(), &l, &t, &r, &b);
|
heif_item_get_property_transform_crop_borders(ctx, item_id, crop_id, size.width(), size.height(), &l, &t, &r, &b);
|
||||||
crop = QRect(QPoint(t, l), size - QSize(b + t, r + l));
|
|
||||||
|
if (l >= size.width() || r >= size.width() || t >= size.height() || b >= size.height()) {
|
||||||
|
qCWarning(LOG_HEIFPLUGIN) << "Invalid crop values: left=" << l << "top=" << t << "right=" << r << "bottom=" << b;
|
||||||
|
}
|
||||||
|
if (l < 0) {
|
||||||
|
l = 0;
|
||||||
|
}
|
||||||
|
if (t < 0) {
|
||||||
|
t = 0;
|
||||||
|
}
|
||||||
|
if (r < 0) {
|
||||||
|
r = 0;
|
||||||
|
}
|
||||||
|
if (b < 0) {
|
||||||
|
b = 0;
|
||||||
|
}
|
||||||
|
if (l == 0 && t == 0 && r == 0 && b == 0) {
|
||||||
|
// no cropping needed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
crop = QRect(QPoint(l, t), size - QSize(l + r, t + b));
|
||||||
|
return crop.isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
return crop.isValid();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HEIFHandler::isSupportedBMFFType(const QByteArray &header)
|
bool HEIFHandler::isSupportedBMFFType(const QByteArray &header)
|
||||||
@@ -930,8 +951,8 @@ bool HEIFHandler::ensureDecoder()
|
|||||||
struct heif_image *img = nullptr;
|
struct heif_image *img = nullptr;
|
||||||
err = heif_decode_image(handle, &img, heif_colorspace_RGB, chroma, decoder_option);
|
err = heif_decode_image(handle, &img, heif_colorspace_RGB, chroma, decoder_option);
|
||||||
|
|
||||||
if (err.code == heif_error_Invalid_input && err.subcode == heif_suberror_Unknown_NCLX_matrix_coefficients && img == nullptr && buffer.contains("Xiaomi")) {
|
if (err.code == heif_error_Invalid_input && err.subcode == heif_suberror_Unknown_NCLX_matrix_coefficients && img == nullptr) {
|
||||||
qCWarning(LOG_HEIFPLUGIN) << "Non-standard HEIF image with invalid matrix_coefficients, probably made by a Xiaomi device!";
|
qCWarning(LOG_HEIFPLUGIN) << "Non-standard HEIF image with invalid matrix_coefficients!";
|
||||||
|
|
||||||
// second try to decode with strict decoding disabled
|
// second try to decode with strict decoding disabled
|
||||||
decoder_option->strict_decoding = 0;
|
decoder_option->strict_decoding = 0;
|
||||||
|
|||||||
@@ -622,21 +622,20 @@ public:
|
|||||||
m_cparameters.tcp_rates[0] = 100.0 - (m_quality < 10 ? m_quality : 10 + (std::log10(m_quality) - 1) * 90);
|
m_cparameters.tcp_rates[0] = 100.0 - (m_quality < 10 ? m_quality : 10 + (std::log10(m_quality) - 1) * 90);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<opj_image_cmptparm_t> cmptparm(new opj_image_cmptparm_t[ncomp]);
|
std::vector<opj_image_cmptparm_t> cmptparm(ncomp);
|
||||||
for (int i = 0; i < ncomp; ++i) {
|
for (opj_image_cmptparm_t &p : cmptparm) {
|
||||||
auto &&p = cmptparm.get() + i;
|
memset(&p, 0, sizeof(opj_image_cmptparm_t));
|
||||||
memset(p, 0, sizeof(opj_image_cmptparm_t));
|
p.dx = m_cparameters.subsampling_dx;
|
||||||
p->dx = m_cparameters.subsampling_dx;
|
p.dy = m_cparameters.subsampling_dy;
|
||||||
p->dy = m_cparameters.subsampling_dy;
|
p.w = image.width();
|
||||||
p->w = image.width();
|
p.h = image.height();
|
||||||
p->h = image.height();
|
p.x0 = 0;
|
||||||
p->x0 = 0;
|
p.y0 = 0;
|
||||||
p->y0 = 0;
|
p.prec = prec;
|
||||||
p->prec = prec;
|
p.sgnd = 0;
|
||||||
p->sgnd = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_jp2_image = opj_image_create(ncomp, cmptparm.get(), cs);
|
m_jp2_image = opj_image_create(ncomp, cmptparm.data(), cs);
|
||||||
if (m_jp2_image == nullptr) {
|
if (m_jp2_image == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ Q_LOGGING_CATEGORY(LOG_JXLPLUGIN, "kf.imageformats.plugins.jxl", QtWarningMsg)
|
|||||||
void *QtJXLMemoryManagerAlloc(void *opaque, size_t size)
|
void *QtJXLMemoryManagerAlloc(void *opaque, size_t size)
|
||||||
{
|
{
|
||||||
if (opaque) {
|
if (opaque) {
|
||||||
size_t maxBytes = *(size_t*)opaque;
|
size_t maxBytes = *(size_t *)opaque;
|
||||||
if (maxBytes && size > maxBytes)
|
if (maxBytes && size > maxBytes)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -75,8 +75,7 @@ void QtJXLMemoryManagerFree(void *, void *address)
|
|||||||
QJpegXLHandler::QJpegXLHandler()
|
QJpegXLHandler::QJpegXLHandler()
|
||||||
: m_parseState(ParseJpegXLNotParsed)
|
: m_parseState(ParseJpegXLNotParsed)
|
||||||
, m_quality(90)
|
, m_quality(90)
|
||||||
, m_currentimage_index(0)
|
, m_currentimage_index(-1)
|
||||||
, m_previousimage_index(-1)
|
|
||||||
, m_transformations(QImageIOHandler::TransformationNone)
|
, m_transformations(QImageIOHandler::TransformationNone)
|
||||||
, m_decoder(nullptr)
|
, m_decoder(nullptr)
|
||||||
, m_runner(nullptr)
|
, m_runner(nullptr)
|
||||||
@@ -183,11 +182,7 @@ bool QJpegXLHandler::ensureDecoder()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Creating a simple memory manager
|
// Creating a simple memory manager
|
||||||
JxlMemoryManager memory_manager = {
|
JxlMemoryManager memory_manager = {.opaque = &m_maxBytes, .alloc = QtJXLMemoryManagerAlloc, .free = QtJXLMemoryManagerFree};
|
||||||
.opaque = &m_maxBytes,
|
|
||||||
.alloc = QtJXLMemoryManagerAlloc,
|
|
||||||
.free = QtJXLMemoryManagerFree
|
|
||||||
};
|
|
||||||
// Creating the decoder (it makes a deep copy of memory manager)
|
// Creating the decoder (it makes a deep copy of memory manager)
|
||||||
m_decoder = JxlDecoderCreate(&memory_manager);
|
m_decoder = JxlDecoderCreate(&memory_manager);
|
||||||
if (!m_decoder) {
|
if (!m_decoder) {
|
||||||
@@ -817,17 +812,11 @@ bool QJpegXLHandler::decode_one_frame()
|
|||||||
exif.updateImageMetadata(m_current_image);
|
exif.updateImageMetadata(m_current_image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_currentimage_index++;
|
||||||
m_next_image_delay = m_framedelays[m_currentimage_index];
|
m_next_image_delay = m_framedelays[m_currentimage_index];
|
||||||
m_previousimage_index = m_currentimage_index;
|
|
||||||
|
|
||||||
if (m_framedelays.count() > 1) {
|
if (m_framedelays.count() > 1) {
|
||||||
m_currentimage_index++;
|
if (m_currentimage_index >= m_framedelays.count() - 1) {
|
||||||
|
|
||||||
if (m_currentimage_index >= m_framedelays.count()) {
|
|
||||||
if (!rewind()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// all frames in animation have been read
|
// all frames in animation have been read
|
||||||
m_parseState = ParseJpegXLFinished;
|
m_parseState = ParseJpegXLFinished;
|
||||||
} else {
|
} else {
|
||||||
@@ -847,9 +836,9 @@ bool QJpegXLHandler::read(QImage *image)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_currentimage_index == m_previousimage_index) {
|
if (m_parseState == ParseJpegXLFinished) {
|
||||||
*image = m_current_image;
|
// at the end already
|
||||||
return jumpToNextImage();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decode_one_frame()) {
|
if (decode_one_frame()) {
|
||||||
@@ -1757,19 +1746,26 @@ bool QJpegXLHandler::jumpToNextImage()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_framedelays.count() > 1) {
|
if (m_framedelays.count() > 1) {
|
||||||
m_currentimage_index++;
|
if (m_currentimage_index >= (m_framedelays.count() - 1)) {
|
||||||
|
// we are already at the last frame
|
||||||
if (m_currentimage_index >= m_framedelays.count()) {
|
return false;
|
||||||
if (!rewind()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
m_currentimage_index++;
|
||||||
|
m_next_image_delay = m_framedelays[m_currentimage_index];
|
||||||
JxlDecoderSkipFrames(m_decoder, 1);
|
JxlDecoderSkipFrames(m_decoder, 1);
|
||||||
|
if (m_currentimage_index >= (m_framedelays.count() - 1)) {
|
||||||
|
// last frame reached but no more reading is possible
|
||||||
|
m_parseState = ParseJpegXLFinished;
|
||||||
|
} else {
|
||||||
|
m_parseState = ParseJpegXLSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_parseState = ParseJpegXLSuccess;
|
// static image, there is no next frame
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QJpegXLHandler::jumpToImage(int imageNumber)
|
bool QJpegXLHandler::jumpToImage(int imageNumber)
|
||||||
@@ -1782,14 +1778,17 @@ bool QJpegXLHandler::jumpToImage(int imageNumber)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imageNumber == m_currentimage_index) {
|
const int next_image_index = m_currentimage_index + 1;
|
||||||
|
|
||||||
|
if (imageNumber == next_image_index) {
|
||||||
|
// This the already the next image to read, no action
|
||||||
m_parseState = ParseJpegXLSuccess;
|
m_parseState = ParseJpegXLSuccess;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imageNumber > m_currentimage_index) {
|
if (imageNumber > next_image_index) {
|
||||||
JxlDecoderSkipFrames(m_decoder, imageNumber - m_currentimage_index);
|
JxlDecoderSkipFrames(m_decoder, imageNumber - next_image_index);
|
||||||
m_currentimage_index = imageNumber;
|
m_currentimage_index = imageNumber - 1;
|
||||||
m_parseState = ParseJpegXLSuccess;
|
m_parseState = ParseJpegXLSuccess;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1801,7 +1800,7 @@ bool QJpegXLHandler::jumpToImage(int imageNumber)
|
|||||||
if (imageNumber > 0) {
|
if (imageNumber > 0) {
|
||||||
JxlDecoderSkipFrames(m_decoder, imageNumber);
|
JxlDecoderSkipFrames(m_decoder, imageNumber);
|
||||||
}
|
}
|
||||||
m_currentimage_index = imageNumber;
|
m_currentimage_index = imageNumber - 1;
|
||||||
m_parseState = ParseJpegXLSuccess;
|
m_parseState = ParseJpegXLSuccess;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1834,7 +1833,7 @@ int QJpegXLHandler::loopCount() const
|
|||||||
|
|
||||||
bool QJpegXLHandler::rewind()
|
bool QJpegXLHandler::rewind()
|
||||||
{
|
{
|
||||||
m_currentimage_index = 0;
|
m_currentimage_index = -1;
|
||||||
|
|
||||||
JxlDecoderReleaseInput(m_decoder);
|
JxlDecoderReleaseInput(m_decoder);
|
||||||
JxlDecoderRewind(m_decoder);
|
JxlDecoderRewind(m_decoder);
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ private:
|
|||||||
ParseJpegXLState m_parseState;
|
ParseJpegXLState m_parseState;
|
||||||
int m_quality;
|
int m_quality;
|
||||||
int m_currentimage_index;
|
int m_currentimage_index;
|
||||||
int m_previousimage_index;
|
|
||||||
QImageIOHandler::Transformations m_transformations;
|
QImageIOHandler::Transformations m_transformations;
|
||||||
|
|
||||||
QByteArray m_rawData;
|
QByteArray m_rawData;
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
This file is part of the KDE project
|
||||||
|
SPDX-FileCopyrightText: 2026 Mirco Miranda <mircomir@outlook.com>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
#include "photoshop_p.h"
|
||||||
|
#include "util_p.h"
|
||||||
|
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
#ifdef QT_DEBUG
|
||||||
|
Q_LOGGING_CATEGORY(LOG_PSDSHARED, "kf.imageformats.plugins.psdshared", QtDebugMsg)
|
||||||
|
#else
|
||||||
|
Q_LOGGING_CATEGORY(LOG_PSDSHARED, "kf.imageformats.plugins.psdshared", QtWarningMsg)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QString readPascalString(QDataStream &s, qint32 alignBytes, qint32 *size)
|
||||||
|
{
|
||||||
|
qint32 tmp = 0;
|
||||||
|
if (size == nullptr)
|
||||||
|
size = &tmp;
|
||||||
|
|
||||||
|
quint8 stringSize;
|
||||||
|
s >> stringSize;
|
||||||
|
*size = sizeof(stringSize);
|
||||||
|
|
||||||
|
QString str;
|
||||||
|
if (stringSize > 0) {
|
||||||
|
QByteArray ba;
|
||||||
|
ba.resize(stringSize);
|
||||||
|
auto read = s.readRawData(ba.data(), ba.size());
|
||||||
|
if (read > 0) {
|
||||||
|
*size += read;
|
||||||
|
str = QString::fromLatin1(ba);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// align
|
||||||
|
if (alignBytes > 1)
|
||||||
|
if (auto pad = *size % alignBytes)
|
||||||
|
*size += s.skipRawData(alignBytes - pad);
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok)
|
||||||
|
{
|
||||||
|
PSDImageResourceSection irs;
|
||||||
|
|
||||||
|
bool tmp = true;
|
||||||
|
if (ok == nullptr)
|
||||||
|
ok = &tmp;
|
||||||
|
*ok = true;
|
||||||
|
|
||||||
|
// Section size
|
||||||
|
quint32 tmpSize;
|
||||||
|
s >> tmpSize;
|
||||||
|
qint64 sectioSize = tmpSize;
|
||||||
|
|
||||||
|
// Reading Image resource block
|
||||||
|
for (auto size = sectioSize; size > 0;) {
|
||||||
|
|
||||||
|
#define DEC_SIZE(value) \
|
||||||
|
if ((size -= qint64(value)) < 0) { \
|
||||||
|
*ok = false; \
|
||||||
|
break; }
|
||||||
|
|
||||||
|
// Length Description
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// 4 Signature: '8BIM'
|
||||||
|
// 2 Unique identifier for the resource. Image resource IDs
|
||||||
|
// contains a list of resource IDs used by Photoshop.
|
||||||
|
// Variable Name: Pascal string, padded to make the size even
|
||||||
|
// (a null name consists of two bytes of 0)
|
||||||
|
// 4 Actual size of resource data that follows
|
||||||
|
// Variable The resource data, described in the sections on the
|
||||||
|
// individual resource types. It is padded to make the size
|
||||||
|
// even.
|
||||||
|
|
||||||
|
quint32 signature;
|
||||||
|
s >> signature;
|
||||||
|
DEC_SIZE(sizeof(signature))
|
||||||
|
// NOTE: MeSa signature is not documented but found in some old PSD take from Photoshop 7.0 CD.
|
||||||
|
if (signature != S_8BIM && signature != S_MeSa) { // 8BIM and MeSa
|
||||||
|
qCDebug(LOG_PSDSHARED) << "Invalid Image Resource Block Signature!";
|
||||||
|
*ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// id
|
||||||
|
quint16 id;
|
||||||
|
s >> id;
|
||||||
|
DEC_SIZE(sizeof(id))
|
||||||
|
|
||||||
|
// getting data
|
||||||
|
PSDImageResourceBlock irb;
|
||||||
|
|
||||||
|
// name
|
||||||
|
qint32 bytes = 0;
|
||||||
|
irb.name = readPascalString(s, 2, &bytes);
|
||||||
|
DEC_SIZE(bytes)
|
||||||
|
|
||||||
|
// data read
|
||||||
|
quint32 dataSize;
|
||||||
|
s >> dataSize;
|
||||||
|
DEC_SIZE(sizeof(dataSize))
|
||||||
|
if (auto dev = s.device()) {
|
||||||
|
if (dataSize > size) {
|
||||||
|
qCDebug(LOG_PSDSHARED) << "Invalid Image Resource Block Data Size!";
|
||||||
|
*ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
irb.data = deviceRead(dev, dataSize);
|
||||||
|
}
|
||||||
|
auto read = irb.data.size();
|
||||||
|
if (read > 0) {
|
||||||
|
DEC_SIZE(read)
|
||||||
|
}
|
||||||
|
if (read != qint64(dataSize)) {
|
||||||
|
qCDebug(LOG_PSDSHARED) << "Image Resource Block Read Error!";
|
||||||
|
*ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto pad = dataSize % 2) {
|
||||||
|
auto skipped = s.skipRawData(pad);
|
||||||
|
if (skipped > 0) {
|
||||||
|
DEC_SIZE(skipped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert IRB
|
||||||
|
irs.insert(ImageResourceId(id), irb);
|
||||||
|
|
||||||
|
#undef DEC_SIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
return irs;
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
This file is part of the KDE project
|
||||||
|
SPDX-FileCopyrightText: 2026 Mirco Miranda <mircomir@outlook.com>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
#ifndef PHOTOSHOP_P_H
|
||||||
|
#define PHOTOSHOP_P_H
|
||||||
|
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
enum Signature : quint32 {
|
||||||
|
S_8BIM = 0x3842494D, // '8BIM'
|
||||||
|
S_8B64 = 0x38423634, // '8B64'
|
||||||
|
|
||||||
|
S_MeSa = 0x4D655361 // 'MeSa'
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ColorMode : quint16 {
|
||||||
|
CM_BITMAP = 0,
|
||||||
|
CM_GRAYSCALE = 1,
|
||||||
|
CM_INDEXED = 2,
|
||||||
|
CM_RGB = 3,
|
||||||
|
CM_CMYK = 4,
|
||||||
|
CM_MULTICHANNEL = 7,
|
||||||
|
CM_DUOTONE = 8,
|
||||||
|
CM_LABCOLOR = 9,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ImageResourceId : quint16 {
|
||||||
|
IRI_RESOLUTIONINFO = 0x03ED,
|
||||||
|
IRI_ICCPROFILE = 0x040F,
|
||||||
|
IRI_TRANSPARENCYINDEX = 0x0417,
|
||||||
|
IRI_ALPHAIDENTIFIERS = 0x041D,
|
||||||
|
IRI_VERSIONINFO = 0x0421,
|
||||||
|
IRI_EXIFDATA1 = 0x0422,
|
||||||
|
IRI_EXIFDATA3 = 0x0423, // never seen
|
||||||
|
IRI_XMPMETADATA = 0x0424
|
||||||
|
};
|
||||||
|
|
||||||
|
enum LayerId : quint32 {
|
||||||
|
LI_MT16 = 0x4D743136, // 'Mt16',
|
||||||
|
LI_MT32 = 0x4D743332, // 'Mt32',
|
||||||
|
LI_MTRN = 0x4D74726E // 'Mtrn'
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The PSDImageResourceBlock class
|
||||||
|
* Raw data of a PSD image resource block
|
||||||
|
*/
|
||||||
|
struct PSDImageResourceBlock {
|
||||||
|
QString name;
|
||||||
|
QByteArray data;
|
||||||
|
};
|
||||||
|
|
||||||
|
using PSDImageResourceSection = QHash<ImageResourceId, PSDImageResourceBlock>;
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief readPascalString
|
||||||
|
* Reads the Pascal string as defined in the PSD specification.
|
||||||
|
* \param s The stream.
|
||||||
|
* \param alignBytes Alignment of the string.
|
||||||
|
* \param size Number of stream bytes used.
|
||||||
|
* \return The string read.
|
||||||
|
*/
|
||||||
|
QString readPascalString(QDataStream &s, qint32 alignBytes = 1, qint32 *size = nullptr);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief readImageResourceSection
|
||||||
|
* Reads the image resource section.
|
||||||
|
* \param s The stream.
|
||||||
|
* \param ok Pointer to the operation result variable.
|
||||||
|
* \return The image resource section raw data.
|
||||||
|
*/
|
||||||
|
PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok = nullptr);
|
||||||
|
|
||||||
|
#endif // PHOTOSHOP_P_H
|
||||||
@@ -29,12 +29,12 @@
|
|||||||
#include "fastmath_p.h"
|
#include "fastmath_p.h"
|
||||||
#include "microexif_p.h"
|
#include "microexif_p.h"
|
||||||
#include "packbits_p.h"
|
#include "packbits_p.h"
|
||||||
|
#include "photoshop_p.h"
|
||||||
#include "psd_p.h"
|
#include "psd_p.h"
|
||||||
#include "scanlineconverter_p.h"
|
#include "scanlineconverter_p.h"
|
||||||
#include "util_p.h"
|
#include "util_p.h"
|
||||||
|
|
||||||
#include <QColorSpace>
|
#include <QColorSpace>
|
||||||
#include <QDataStream>
|
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
@@ -99,41 +99,6 @@ namespace // Private.
|
|||||||
|
|
||||||
#define NATIVE_CMYK (CMYK_FORMAT != QImage::Format_Invalid)
|
#define NATIVE_CMYK (CMYK_FORMAT != QImage::Format_Invalid)
|
||||||
|
|
||||||
enum Signature : quint32 {
|
|
||||||
S_8BIM = 0x3842494D, // '8BIM'
|
|
||||||
S_8B64 = 0x38423634, // '8B64'
|
|
||||||
|
|
||||||
S_MeSa = 0x4D655361 // 'MeSa'
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ColorMode : quint16 {
|
|
||||||
CM_BITMAP = 0,
|
|
||||||
CM_GRAYSCALE = 1,
|
|
||||||
CM_INDEXED = 2,
|
|
||||||
CM_RGB = 3,
|
|
||||||
CM_CMYK = 4,
|
|
||||||
CM_MULTICHANNEL = 7,
|
|
||||||
CM_DUOTONE = 8,
|
|
||||||
CM_LABCOLOR = 9,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ImageResourceId : quint16 {
|
|
||||||
IRI_RESOLUTIONINFO = 0x03ED,
|
|
||||||
IRI_ICCPROFILE = 0x040F,
|
|
||||||
IRI_TRANSPARENCYINDEX = 0x0417,
|
|
||||||
IRI_ALPHAIDENTIFIERS = 0x041D,
|
|
||||||
IRI_VERSIONINFO = 0x0421,
|
|
||||||
IRI_EXIFDATA1 = 0x0422,
|
|
||||||
IRI_EXIFDATA3 = 0x0423, // never seen
|
|
||||||
IRI_XMPMETADATA = 0x0424
|
|
||||||
};
|
|
||||||
|
|
||||||
enum LayerId : quint32 {
|
|
||||||
LI_MT16 = 0x4D743136, // 'Mt16',
|
|
||||||
LI_MT32 = 0x4D743332, // 'Mt32',
|
|
||||||
LI_MTRN = 0x4D74726E // 'Mtrn'
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PSDHeader {
|
struct PSDHeader {
|
||||||
PSDHeader() {
|
PSDHeader() {
|
||||||
memset(this, 0, sizeof(PSDHeader));
|
memset(this, 0, sizeof(PSDHeader));
|
||||||
@@ -149,11 +114,6 @@ struct PSDHeader {
|
|||||||
ushort color_mode;
|
ushort color_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PSDImageResourceBlock {
|
|
||||||
QString name;
|
|
||||||
QByteArray data;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief The PSDDuotoneOptions struct
|
* \brief The PSDDuotoneOptions struct
|
||||||
* \note You can decode the duotone data using the "Duotone Options"
|
* \note You can decode the duotone data using the "Duotone Options"
|
||||||
@@ -172,7 +132,6 @@ struct PSDColorModeDataSection {
|
|||||||
QList<QRgb> palette;
|
QList<QRgb> palette;
|
||||||
};
|
};
|
||||||
|
|
||||||
using PSDImageResourceSection = QHash<quint16, PSDImageResourceBlock>;
|
|
||||||
|
|
||||||
struct PSDLayerInfo {
|
struct PSDLayerInfo {
|
||||||
qint64 size = -1;
|
qint64 size = -1;
|
||||||
@@ -273,145 +232,6 @@ static bool skip_section(QDataStream &s, bool psb = false)
|
|||||||
return skip_data(s, section_length);
|
return skip_data(s, section_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief readPascalString
|
|
||||||
* Reads the Pascal string as defined in the PSD specification.
|
|
||||||
* \param s The stream.
|
|
||||||
* \param alignBytes Alignment of the string.
|
|
||||||
* \param size Number of stream bytes used.
|
|
||||||
* \return The string read.
|
|
||||||
*/
|
|
||||||
static QString readPascalString(QDataStream &s, qint32 alignBytes = 1, qint32 *size = nullptr)
|
|
||||||
{
|
|
||||||
qint32 tmp = 0;
|
|
||||||
if (size == nullptr)
|
|
||||||
size = &tmp;
|
|
||||||
|
|
||||||
quint8 stringSize;
|
|
||||||
s >> stringSize;
|
|
||||||
*size = sizeof(stringSize);
|
|
||||||
|
|
||||||
QString str;
|
|
||||||
if (stringSize > 0) {
|
|
||||||
QByteArray ba;
|
|
||||||
ba.resize(stringSize);
|
|
||||||
auto read = s.readRawData(ba.data(), ba.size());
|
|
||||||
if (read > 0) {
|
|
||||||
*size += read;
|
|
||||||
str = QString::fromLatin1(ba);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// align
|
|
||||||
if (alignBytes > 1)
|
|
||||||
if (auto pad = *size % alignBytes)
|
|
||||||
*size += s.skipRawData(alignBytes - pad);
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief readImageResourceSection
|
|
||||||
* Reads the image resource section.
|
|
||||||
* \param s The stream.
|
|
||||||
* \param ok Pointer to the operation result variable.
|
|
||||||
* \return The image resource section raw data.
|
|
||||||
*/
|
|
||||||
static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok = nullptr)
|
|
||||||
{
|
|
||||||
PSDImageResourceSection irs;
|
|
||||||
|
|
||||||
bool tmp = true;
|
|
||||||
if (ok == nullptr)
|
|
||||||
ok = &tmp;
|
|
||||||
*ok = true;
|
|
||||||
|
|
||||||
// Section size
|
|
||||||
quint32 tmpSize;
|
|
||||||
s >> tmpSize;
|
|
||||||
qint64 sectioSize = tmpSize;
|
|
||||||
|
|
||||||
// Reading Image resource block
|
|
||||||
for (auto size = sectioSize; size > 0;) {
|
|
||||||
|
|
||||||
#define DEC_SIZE(value) \
|
|
||||||
if ((size -= qint64(value)) < 0) { \
|
|
||||||
*ok = false; \
|
|
||||||
break; }
|
|
||||||
|
|
||||||
// Length Description
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
// 4 Signature: '8BIM'
|
|
||||||
// 2 Unique identifier for the resource. Image resource IDs
|
|
||||||
// contains a list of resource IDs used by Photoshop.
|
|
||||||
// Variable Name: Pascal string, padded to make the size even
|
|
||||||
// (a null name consists of two bytes of 0)
|
|
||||||
// 4 Actual size of resource data that follows
|
|
||||||
// Variable The resource data, described in the sections on the
|
|
||||||
// individual resource types. It is padded to make the size
|
|
||||||
// even.
|
|
||||||
|
|
||||||
quint32 signature;
|
|
||||||
s >> signature;
|
|
||||||
DEC_SIZE(sizeof(signature))
|
|
||||||
// NOTE: MeSa signature is not documented but found in some old PSD take from Photoshop 7.0 CD.
|
|
||||||
if (signature != S_8BIM && signature != S_MeSa) { // 8BIM and MeSa
|
|
||||||
qCDebug(LOG_PSDPLUGIN) << "Invalid Image Resource Block Signature!";
|
|
||||||
*ok = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// id
|
|
||||||
quint16 id;
|
|
||||||
s >> id;
|
|
||||||
DEC_SIZE(sizeof(id))
|
|
||||||
|
|
||||||
// getting data
|
|
||||||
PSDImageResourceBlock irb;
|
|
||||||
|
|
||||||
// name
|
|
||||||
qint32 bytes = 0;
|
|
||||||
irb.name = readPascalString(s, 2, &bytes);
|
|
||||||
DEC_SIZE(bytes)
|
|
||||||
|
|
||||||
// data read
|
|
||||||
quint32 dataSize;
|
|
||||||
s >> dataSize;
|
|
||||||
DEC_SIZE(sizeof(dataSize))
|
|
||||||
if (auto dev = s.device()) {
|
|
||||||
if (dataSize > size) {
|
|
||||||
qCDebug(LOG_PSDPLUGIN) << "Invalid Image Resource Block Data Size!";
|
|
||||||
*ok = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
irb.data = deviceRead(dev, dataSize);
|
|
||||||
}
|
|
||||||
auto read = irb.data.size();
|
|
||||||
if (read > 0) {
|
|
||||||
DEC_SIZE(read)
|
|
||||||
}
|
|
||||||
if (read != qint64(dataSize)) {
|
|
||||||
qCDebug(LOG_PSDPLUGIN) << "Image Resource Block Read Error!";
|
|
||||||
*ok = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto pad = dataSize % 2) {
|
|
||||||
auto skipped = s.skipRawData(pad);
|
|
||||||
if (skipped > 0) {
|
|
||||||
DEC_SIZE(skipped);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert IRB
|
|
||||||
irs.insert(id, irb);
|
|
||||||
|
|
||||||
#undef DEC_SIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
return irs;
|
|
||||||
}
|
|
||||||
|
|
||||||
PSDAdditionalLayerInfo readAdditionalLayer(QDataStream &s, bool *ok = nullptr)
|
PSDAdditionalLayerInfo readAdditionalLayer(QDataStream &s, bool *ok = nullptr)
|
||||||
{
|
{
|
||||||
PSDAdditionalLayerInfo li;
|
PSDAdditionalLayerInfo li;
|
||||||
|
|||||||
@@ -314,6 +314,8 @@ bool SGIImagePrivate::readImage(QImage &img)
|
|||||||
_numrows = _ysize * _zsize;
|
_numrows = _ysize * _zsize;
|
||||||
|
|
||||||
if (_rle) {
|
if (_rle) {
|
||||||
|
// start table holds file offsets; raster data follows header + both tables
|
||||||
|
const quint32 dataOff = 512 + _numrows * 2 * sizeof(quint32);
|
||||||
uint l;
|
uint l;
|
||||||
_starttab = new (std::nothrow) quint32[_numrows];
|
_starttab = new (std::nothrow) quint32[_numrows];
|
||||||
if (_starttab == nullptr) {
|
if (_starttab == nullptr) {
|
||||||
@@ -321,7 +323,10 @@ bool SGIImagePrivate::readImage(QImage &img)
|
|||||||
}
|
}
|
||||||
for (l = 0; !_stream.atEnd() && l < _numrows; l++) {
|
for (l = 0; !_stream.atEnd() && l < _numrows; l++) {
|
||||||
_stream >> _starttab[l];
|
_stream >> _starttab[l];
|
||||||
_starttab[l] -= 512 + _numrows * 2 * sizeof(quint32);
|
if (_starttab[l] < dataOff) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_starttab[l] -= dataOff;
|
||||||
if (_stream.status() != QDataStream::Ok) {
|
if (_stream.status() != QDataStream::Ok) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -353,9 +358,10 @@ bool SGIImagePrivate::readImage(QImage &img)
|
|||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
if (_rle) {
|
if (_rle) {
|
||||||
|
const uint dataSize = uint(_data.size());
|
||||||
for (uint o = 0; o < _numrows; o++) {
|
for (uint o = 0; o < _numrows; o++) {
|
||||||
// don't change to greater-or-equal!
|
// don't add start+length: uint32 wrap would pass a corrupt file
|
||||||
if (_starttab[o] + _lengthtab[o] > (uint)_data.size()) {
|
if (_starttab[o] > dataSize || _lengthtab[o] > dataSize - _starttab[o]) {
|
||||||
// qCDebug(LOG_RGBPLUGIN) << "image corrupt (sanity check failed)";
|
// qCDebug(LOG_RGBPLUGIN) << "image corrupt (sanity check failed)";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,20 +165,27 @@ inline bool checkImageSize(const QSize& size, qint32 bytesPerPixel)
|
|||||||
return checkImageSize(size.width(), size.height(), bytesPerPixel);
|
return checkImageSize(size.width(), size.height(), bytesPerPixel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief qRoundOrZero_T
|
||||||
|
* In images, many float values can only be positive (e.g., resolution). This function calculates
|
||||||
|
* the roundness of the passed value, returning 0 if the value is negative or invalid.
|
||||||
|
* \return 0 when \a d is negative, NaN, Inf or std::numeric_limits<TI>::max(). Otherwise the qRound of \a d.
|
||||||
|
*/
|
||||||
template<class TI, class SF> // SF = source FP, TI = target INT
|
template<class TI, class SF> // SF = source FP, TI = target INT
|
||||||
TI qRoundOrZero_T(SF d, bool *ok = nullptr)
|
TI qRoundOrZero_T(SF d, bool *ok = nullptr)
|
||||||
{
|
{
|
||||||
|
bool tmp = false;
|
||||||
|
if (ok == nullptr) {
|
||||||
|
ok = &tmp;
|
||||||
|
}
|
||||||
|
|
||||||
// checks for undefined behavior
|
// checks for undefined behavior
|
||||||
if (qIsNaN(d) || qIsInf(d) || d < SF() || d > SF(std::numeric_limits<TI>::max())) {
|
if (qIsNaN(d) || qIsInf(d) || d < SF()) {
|
||||||
if (ok) {
|
*ok = false;
|
||||||
*ok = false;
|
} else {
|
||||||
}
|
*ok = d < SF(std::numeric_limits<TI>::max());
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
if (ok) {
|
return *ok ? qRound(d) : 0;
|
||||||
*ok = true;
|
|
||||||
}
|
|
||||||
return qRound(d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline qint32 qRoundOrZero(double d, bool *ok = nullptr)
|
inline qint32 qRoundOrZero(double d, bool *ok = nullptr)
|
||||||
|
|||||||