Compare commits

...

80 Commits

Author SHA1 Message Date
Nicolas Fella
eb896efea1 Update dependency version to 6.25.0 2026-04-03 18:34:25 +02:00
Mirco Miranda
142ec14c81 TIM: PlayStation graphics read only support 2026-03-24 08:38:08 +01:00
Albert Astals Cid
38b8b70304 Remove ifdef now that we depend on Qt >= 6.9.0 2026-03-23 22:57:17 +01:00
Albert Astals Cid
e28c48cfeb GIT_SILENT Upgrade CMake version requirement to 3.29.
See https://community.kde.org/Frameworks/Policies
2026-03-23 19:30:35 +01:00
Albert Astals Cid
3c08226aec GIT_SILENT Upgrade Qt6 version requirement to 6.9.0.
See https://community.kde.org/Frameworks/Policies
2026-03-23 19:13:47 +01:00
Mirco Miranda
7c86ccaefb IFF: fix Integer-overflow in IDATChunk::strideSize 2026-03-23 12:05:01 +01:00
Nicolas Fella
ec0610d5b0 Update version to 6.25.0 2026-03-07 22:30:10 +01:00
Nicolas Fella
ae279c55f4 Update dependency version to 6.24.0 2026-03-07 21:18:31 +01:00
Mirco Miranda
836e0a53bb JP2: fix possible Undefined-shift 2026-03-04 08:15:48 +01:00
Mirco Miranda
5eb09116b0 IFF: fix buffer read overflow 2026-02-24 08:57:45 +01:00
Mirco Miranda
92368ca58f Fix Heap-buffer-overflow WRITE 2026-02-22 18:27:34 +00:00
Mirco Miranda
a91c7ef72f Fixed excessively frequent warning messages 2026-02-22 14:56:21 +01:00
Daniel Novomeský
ea2a4aafab ossfuzz: update aom, libavif, openjpeg 2026-02-19 11:31:12 +01:00
Mirco Miranda
c254875780 ANI: fix possible QByteArray allocation exception 2026-02-16 10:12:05 +01:00
Daniel Novomeský
f3de2e77c1 jxl: adjust metadata size limits
Previously there was no limit for uncompressed metadata,
but when compressed metadata required buffer larger than 4MB,
image decoding stopped.

Now the plugin discards/skips metadata boxes larger than 8MB
without stopping image decoding.
If size of compressed box is above 8MB,
we do not attempt to decompress it.
File with compressed metadata could be rejected only in rare cases
when the decompression buffer grows above 32MB (four times 8M).
2026-02-13 15:44:39 +01:00
Mirco Miranda
1ef779f370 RGB: fix a possible exception on the new 2026-02-12 13:42:58 +01:00
Laurent Montel
169a874cba GIT_SILENT: Bump kf ecm_set_disabled_deprecation_versions. Make sure that it compiles fine without kf 6.23 deprecated methods 2026-02-10 07:52:45 +01:00
Mirco Miranda
ebf77ccdf5 TGA: fix Undefined-shift 2026-02-09 23:02:06 +00:00
Mirco Miranda
359cb039d2 PSD: improve conversion sanity checks 2026-02-09 22:50:51 +00:00
Mirco Miranda
f4b91d8a54 IFF: fix compilation warnings 2026-02-09 22:46:04 +00:00
Mirco Miranda
263b5a88e2 ANI: check for array allocation size 2026-02-09 08:41:40 +01:00
Nicolas Fella
8d07f7db1b Update version to 6.24.0 2026-02-06 13:31:01 +01:00
Nicolas Fella
1c2210c100 Update dependency version to 6.23.0 2026-02-06 13:01:56 +01:00
Azhar Momin
b7b438f903 Fix oss-fuzz AFL build (again) 2026-02-03 14:35:56 +05:30
Azhar Momin
336b8906aa Fix OSS-Fuzz AFL builds 2026-02-02 23:05:09 +00:00
Kai Uwe Broulik
2c8a1ad6ff GIT_SILENT anitest: Fix typo
text -> test
2026-01-30 17:36:12 +01:00
Mirco Miranda
e0f1ba640a IFF: add uncompressed RGFX support 2026-01-27 13:58:01 +01:00
Mirco Miranda
32773e5f0c IFF: add CD-i Rle7 support 2026-01-20 21:17:08 +00:00
Mirco Miranda
2410e45614 Decode Atari ST VDAT chunks 2026-01-20 10:20:32 +01:00
Mirco Miranda
99e4223393 IFF: add support for CD-i YUVS chunk (and minor code improvements) 2026-01-15 16:53:50 +01:00
Mirco Miranda
8224c0099d Add support for CD-I IFF images 2026-01-14 16:53:19 +01:00
Daniel Novomeský
8d7fb2c3fd Add JXL testfile which previously triggered crash 2026-01-11 15:25:03 +01:00
Daniel Novomeský
3353809906 jxl: fix crash on lossy gray images
There was a rare crash during decoding of some lossy gray images.
Problem was reported in https://github.com/libjxl/libjxl/issues/4549
This is a workaround which avoids JxlDecoderSetCms call.
2026-01-11 15:03:06 +01:00
Daniel Novomeský
abf4d32858 Add gray AVIF files with various transfer functions 2026-01-10 20:41:52 +00:00
Daniel Novomeský
6b1c52c55c avif: Improved color profiles support 2026-01-10 20:41:52 +00:00
Albert Astals Cid
e644ab997f GIT_SILENT Upgrade CMake version requirement to 3.27.
See https://community.kde.org/Frameworks/Policies
2026-01-10 00:13:59 +01:00
Nicolas Fella
1fb3363e7b Update version to 6.23.0 2026-01-02 18:59:54 +01:00
Nicolas Fella
19df8b03a8 Update dependency version to 6.22.0 2026-01-02 18:32:32 +01:00
Daniel Novomeský
3f1ee9f9d1 Add YCgCo-Re AVIF test
Disable AVIF read tests for libavif below 1.2.0,
where YCgCo-Re decoding is expected to fail.
2025-12-27 21:35:25 +01:00
Daniel Novomeský
8c9a7e88e5 avif: YCgCo-Re decoding fix 2025-12-26 00:47:46 +00:00
Mirco Miranda
30308f3541 HEIF tests skipped using kde-ci.yml 2025-12-24 17:56:44 +01:00
Laurent Montel
af4597f796 GIT_SILENT: Bump qt ecm_set_disabled_deprecation_versions. Make sure that it compiles fine without qt 6.11 deprecated methods 2025-12-24 06:57:32 +01:00
Laurent Montel
b8b9849268 GIT_SILENT: Bump kf ecm_set_disabled_deprecation_versions. Make sure that it compiles fine without kf 6.21 deprecated methods 2025-12-18 22:07:02 +01:00
Mirco Miranda
db1cb8de1f Add allocation limit test (0/256 MiB) 2025-12-09 15:55:35 +01:00
Albert Astals Cid
956b86c4de allocationLimit = 0 means no limit 2025-12-09 01:06:10 +01:00
Nicolas Fella
282c277204 Update version to 6.22.0 2025-12-05 15:14:46 +01:00
Nicolas Fella
237f25fd20 Update dependency version to 6.21.0 2025-12-05 14:07:10 +01:00
Kunda Ki
5140567853 Fix various typos in the codebase
Fixes mostly source documentation, comments, and some user-facing typos.
Found via `codespell -q 3 -S "*.eps,*.raw" -L ahd,siz,widthn`
2025-12-05 10:41:17 +00:00
Mirco Miranda
72a809bcfd XCF: initialize tiles and buffers 2025-11-22 12:45:46 +01:00
Mirco Miranda
731ac375bb HDR: fix uninitialized buffer 2025-11-22 09:41:25 +01:00
Mirco Miranda
8061500b79 PSD: add support for GrayA (8/16/32 bit) and Gray 32 bit 2025-11-21 06:24:53 +01:00
Mirco Miranda
472ff92b96 PSD: fix use of uninitialized tmp image 2025-11-21 01:21:18 +00:00
Mirco Miranda
847510f109 EXR: fix of uninitialized RGB read buffer 2025-11-20 07:36:49 +01:00
Mirco Miranda
a8ad7be1cd RGB: fix Use-of-uninitialized-value 2025-11-19 11:05:36 +00:00
Mirco Miranda
7202e77c74 HDR: readHeader() sanity checks 2025-11-18 16:56:46 +01:00
Mirco Miranda
379c3c1738 PSD: return error if a complete line is read/decompressed 2025-11-17 14:38:07 +01:00
Nicolas Fella
6519d2930e Update version to 6.21.0 2025-11-14 18:25:25 +01:00
Nicolas Fella
ffcfa49f13 Update dependency version to 6.20.0 2025-11-07 19:26:04 +01:00
Mirco Miranda
d3d4bea2f1 PSD: limit memory usage on corrupted files 2025-11-07 15:36:31 +01:00
Albert Astals Cid
cfbeb397a8 Fix assert on broken data
Change the isnan test to !isfinite. This way we include infinite numbers
too as not accepted
2025-11-05 23:16:03 +01:00
Mirco Miranda
1c6ce2437e PSD: minor fixes while reading Image resource Section 2025-11-03 08:27:17 +01:00
Laurent Montel
3d9f1c4e9e GIT_SILENT: Bump kf ecm_set_disabled_deprecation_versions. Make sure that it compiles fine without kf 6.19 deprecated methods 2025-11-02 16:07:02 +01:00
Mirco Miranda
e7d01c286f IFF: fix crash on malformed files 2025-10-31 09:07:40 +01:00
Albert Astals Cid
dfda863841 iff: Fix crash on malformed files
oss-fuzz/455510771
2025-10-28 00:50:06 +01:00
Mirco Miranda
3d4d74fc02 PSD: improve sections size checks 2025-10-24 08:39:58 +02:00
Mirco Miranda
326e56029a TGA: ignoring large metadata on dev area 2025-10-23 14:54:52 +02:00
Mirco Miranda
e8e5b091bc Check device before read 2025-10-23 12:18:59 +02:00
Mirco Miranda
aeb2988eec Fix possible infinite loop when reading a broken jp2 2025-10-23 11:59:39 +02:00
Mirco Miranda
abfda2ce69 On demand buffer allocation on PCHG decompression 2025-10-23 07:58:58 +00:00
Mirco Miranda
d6d67c7b8b Fix possible read overflow with malformed data 2025-10-23 08:23:57 +02:00
Mirco Miranda
54c94764d1 Fix wrong palette check 2025-10-21 19:33:12 +02:00
Mirco Miranda
7512874620 Use internal FP32 conversion instead of FP16 2025-10-18 05:43:08 +02:00
Mirco Miranda
146f637385 Fix read test failing on aarch64 2025-10-17 13:43:24 +02:00
Albert Astals Cid
436310b4f0 Remove Qt version checks now that 6.8.0 is the minimum required 2025-10-16 20:20:35 +02:00
Albert Astals Cid
d8a8bcb68e GIT_SILENT Upgrade Qt6 version requirement to 6.8.0.
See https://community.kde.org/Frameworks/Policies
2025-10-16 19:10:25 +02:00
Albert Astals Cid
2b80a0a55a Fix assert on broken data
The nan eventually ends up in qRound inside Qt code.
That asserts because it doesn't know what to do with a nan
2025-10-14 23:55:59 +02:00
Albert Astals Cid
8e164474b4 CI: Try newer libjxl for ossfuzz 2025-10-13 11:34:15 +02:00
Albert Astals Cid
4c52480b73 tga: Reduce Warning to Debug
It happens that apps have an unknown source and ask if they can read it,
we should not show a weird warning to the user
2025-10-12 15:14:41 +02:00
Mirco Miranda
19faa55a6d Fix crash on malformed files 2025-10-10 09:03:18 +02:00
Mirco Miranda
b9bfeedbc1 Fix crash on malformed files 2025-10-06 01:04:40 +02:00
116 changed files with 3173 additions and 410 deletions

View File

@@ -7,5 +7,5 @@ Dependencies:
Options:
test-before-installing: True
require-passing-tests-on: ['Linux', 'FreeBSD', 'Windows']
cmake-options: "-DKIMAGEFORMATS_DDS=ON -DKIMAGEFORMATS_JXR=ON -DKIMAGEFORMATS_HEIF=ON"
cmake-options: "-DKIMAGEFORMATS_JXR=ON -DKIMAGEFORMATS_HEIF=ON -DKIMAGEFORMATS_HEIF_TEST:STRING=OFF -DKIMAGEFORMATS_HEJ2_TEST:STRING=OFF -DKIMAGEFORMATS_AVCI_TEST:STRING=OFF"
per-test-timeout: 90

View File

@@ -1,11 +1,11 @@
cmake_minimum_required(VERSION 3.16)
cmake_minimum_required(VERSION 3.29)
set(KF_VERSION "6.20.0") # handled by release scripts
set(KF_DEP_VERSION "6.19.0") # handled by release scripts
set(KF_VERSION "6.25.0") # handled by release scripts
set(KF_DEP_VERSION "6.25.0") # handled by release scripts
project(KImageFormats VERSION ${KF_VERSION})
include(FeatureSummary)
find_package(ECM 6.19.0 NO_MODULE)
find_package(ECM 6.25.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)
@@ -21,7 +21,7 @@ include(ECMDeprecationSettings)
include(CheckIncludeFiles)
include(FindPkgConfig)
set(REQUIRED_QT_VERSION 6.7.0)
set(REQUIRED_QT_VERSION 6.9.0)
find_package(Qt6Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
find_package(KF6Archive ${KF_DEP_VERSION})
@@ -65,6 +65,12 @@ set_package_properties(libavif PROPERTIES
option(KIMAGEFORMATS_DDS "Enable plugin for DDS format" ON)
option(KIMAGEFORMATS_HEIF "Enable plugin for HEIF format" OFF)
set(KIMAGEFORMATS_HEIF_TEST "ALL" CACHE STRING "Enable HEIF tests: OFF, READ_ONLY, ALL")
set_property(CACHE KIMAGEFORMATS_HEIF_TEST PROPERTY STRINGS "OFF" "READ_ONLY" "ALL")
set(KIMAGEFORMATS_HEJ2_TEST "ALL" CACHE STRING "Enable HEJ2 tests: OFF, READ_ONLY, ALL")
set_property(CACHE KIMAGEFORMATS_HEJ2_TEST PROPERTY STRINGS "OFF" "READ_ONLY" "ALL")
set(KIMAGEFORMATS_AVCI_TEST "ALL" CACHE STRING "Enable AVCI tests: OFF, ALL")
set_property(CACHE KIMAGEFORMATS_AVCI_TEST PROPERTY STRINGS "OFF" "ALL")
if(KIMAGEFORMATS_HEIF)
pkg_check_modules(LibHeif IMPORTED_TARGET libheif>=1.10.0)
endif()
@@ -99,8 +105,8 @@ endif()
add_feature_info(LibJXR LibJXR_FOUND "required for the QImage plugin for JPEG XR images")
ecm_set_disabled_deprecation_versions(
QT 6.10.0
KF 6.18.0
QT 6.11.0
KF 6.23.0
)
add_subdirectory(src)

View File

@@ -21,6 +21,7 @@ The following image formats have read-only support:
- Krita (kra)
- OpenRaster (ora)
- Pixar raster (pxr)
- PlayStation graphics (tim)
- Portable FloatMap/HalfMap (pfm, phm)
- Photoshop documents (psd, psb, pdd, psdt)
- Radiance HDR (hdr)
@@ -250,6 +251,7 @@ limit depends on the format encoding).
- RAW: 65,535 x 65,535 pixels
- RGB: 65,535 x 65,535 pixels
- SCT: 300,000 x 300,000 pixels
- TIM: 65,535 x 65,535 pixels
- TGA: 65,535 x 65,535 pixels
- XCF: 300,000 x 300,000 pixels
@@ -290,7 +292,7 @@ in your cmake options.**
The following defines can be defined in cmake to modify the behavior of the
plugin:
- `DDS_DISABLE_STRIDE_ALIGNMENT`: disable the stride aligment based on DDS
- `DDS_DISABLE_STRIDE_ALIGNMENT`: disable the stride alignment based on DDS
pitch: it is known that some writers do not set it correctly.
### The HEIF plugin
@@ -303,6 +305,15 @@ distributions. In particular, it is necessary that the HEIF library has
support for HEVC codec. If HEVC codec is not available the plugin
will compile but will fail the tests.
**If you are interested in compiling the plugin without running the tests,
also use the following string options:**
- `KIMAGEFORMATS_HEIF_TEST` to change the behaviour of HEIF tests. Set to
`"OFF"` (no test at all) or `"READ_ONLY"` (run read tests only).
- `KIMAGEFORMATS_HEJ2_TEST` to change the behaviour of HEJ2 tests. Set to
`"OFF"` (no test at all) or `"READ_ONLY"` (run read tests only)..
- `KIMAGEFORMATS_AVCI_TEST` to change the behaviour of AVCI tests. Set to
`"OFF"` (no test at all).
### The EXR plugin
The following defines can be defined in cmake to modify the behavior of the
@@ -348,9 +359,16 @@ The plugin supports the following image data:
type 4.
- FORM PBM: PBM is a chunky version of IFF pictures. It supports 8-bit images
with color map only.
- FORM IMAG (Compact Disc-Interactive): It supports CLut4, CLut7, CLut8, Rle7
and DYuv formats.
- FORM RGFX: It supports uncompressed images only.
- FOR4 CIMG (Maya Image File Format): It supports 24/48-bit RGB and 32/64-bit
RGBA images.
> [!note]
> The plugin only supports the IFF, ILBM, and LBM file extensions. You'll
> need to rename files with different extensions to open them.
### The JP2 plugin
**This plugin can be disabled by setting `KIMAGEFORMATS_JP2` to `OFF`
@@ -382,7 +400,7 @@ The following defines can be defined in cmake to modify the behavior of the
plugin:
- `JXR_DENY_FLOAT_IMAGE`: disables the use of float images and consequently
any HDR data will be lost.
- `JXR_DISABLE_DEPTH_CONVERSION`: remove the neeeds of additional memory by
- `JXR_DISABLE_DEPTH_CONVERSION`: remove the needs of additional memory by
disabling the conversion between different color depths (e.g. RGBA64bpp to
RGBA32bpp) at the cost of reduced compatibility.
- `JXR_DISABLE_BGRA_HACK`: Windows displays and opens JXR files correctly out
@@ -414,6 +432,8 @@ PSD support has the following limitations:
- Multichannel images are treated as CMYK if they have 2 or more channels.
- Multichannel images are treated as Grayscale if they have 1 channel.
- Duotone images are treated as grayscale images.
- Grayscale images with alpha channel or at 32 bit depth are converted to
RGBA due to the lack of the appropriate Qt grayscale container.
- Extra channels other than alpha are discarded.
The following defines can be defined in cmake to modify the behavior of the

View File

@@ -86,6 +86,7 @@ kimageformats_read_tests(
ras
rgb
sct
tim
tga
)
@@ -102,36 +103,52 @@ if (KF6Archive_FOUND)
endif()
if (TARGET avif)
kimageformats_read_tests(
avif
)
if(${libavif_VERSION} VERSION_GREATER_EQUAL "1.2.0")
kimageformats_read_tests(
avif
)
else()
message(WARNING "libavif ${libavif_VERSION} doesn't support decoding new AVIF files!\n"
"AVIF read tests are disabled, consider updating libavif.")
endif()
kimageformats_write_tests(
avif-nodatacheck-lossless
)
endif()
if (LibHeif_FOUND)
kimageformats_read_tests(
heif
)
# because the plug-ins use RGB->YUV conversion which sometimes results in 1 value difference.
kimageformats_write_tests(FUZZ 1
heif-nodatacheck-lossless
)
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.17.0")
kimageformats_read_tests(FUZZ 1
hej2
if (KIMAGEFORMATS_HEIF_TEST STREQUAL "ALL" OR KIMAGEFORMATS_HEIF_TEST STREQUAL "READ_ONLY")
kimageformats_read_tests(
heif
)
endif()
if (KIMAGEFORMATS_HEIF_TEST STREQUAL "ALL")
# because the plug-ins use RGB->YUV conversion which sometimes results in 1 value difference.
kimageformats_write_tests(FUZZ 1
hej2-nodatacheck-lossless
heif-nodatacheck-lossless
)
endif()
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.17.0")
if (KIMAGEFORMATS_HEJ2_TEST STREQUAL "ALL" OR KIMAGEFORMATS_HEJ2_TEST STREQUAL "READ_ONLY")
kimageformats_read_tests(FUZZ 1
hej2
)
endif()
if (KIMAGEFORMATS_HEJ2_TEST STREQUAL "ALL")
kimageformats_write_tests(FUZZ 1
hej2-nodatacheck-lossless
)
endif()
endif()
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.19.6")
kimageformats_read_tests(FUZZ 4
avci
)
if (KIMAGEFORMATS_AVCI_TEST STREQUAL "ALL")
kimageformats_read_tests(FUZZ 4
avci
)
endif()
endif()
endif()

View File

@@ -48,7 +48,7 @@ private Q_SLOTS:
QCOMPARE(reader.text(QStringLiteral("Author")), QStringLiteral("KDE Community"));
}
void textRead()
void testRead()
{
QImageReader reader(QFINDTESTDATA("ani/test.ani"));
QVERIFY(reader.canRead());

View File

@@ -18,6 +18,11 @@
# limitations under the License.
#
################################################################################
LDFLAGS=""
if [[ $FUZZING_ENGINE == "afl" ]]; then
LDFLAGS="-fuse-ld=lld"
fi
export LDFLAGS
# build zstd
cd $SRC/zstd
@@ -175,6 +180,7 @@ HANDLER_TYPES="ANIHandler ani
RAWHandler raw
RGBHandler rgb
ScitexHandler sct
TIMHandler tim
TGAHandler tga
XCFHandler xcf"
@@ -185,7 +191,7 @@ echo "$HANDLER_TYPES" | while read class format; do
/usr/libexec/moc $SRC/kimageformats/src/imageformats/$format.cpp -o $format.moc
header=`ls $SRC/kimageformats/src/imageformats/$format*.h`
/usr/libexec/moc $header -o moc_`basename $header .h`.cpp
$CXX $CXXFLAGS -fPIC -DHANDLER=$class -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 -DINITGUID -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 -lKF6Archive /usr/local/lib/libz.a /usr/local/lib/libraw.a /usr/local/lib/libOpenEXR-3_3.a /usr/local/lib/libIex-3_3.a /usr/local/lib/libImath-3_1.a /usr/local/lib/libIlmThread-3_3.a /usr/local/lib/libOpenEXRCore-3_3.a /usr/local/lib/libOpenEXRUtil-3_3.a /usr/local/lib/libopenjp2.a /usr/local/lib/libzstd.a $SRC/jxrlib/build/libjxrglue.a $SRC/jxrlib/build/libjpegxr.a -llzma /usr/local/lib/libbz2.a -lclang_rt.builtins
$CXX $CXXFLAGS $LDFLAGS -fPIC -DHANDLER=$class -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 -DINITGUID -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_3.a /usr/local/lib/libIex-3_3.a /usr/local/lib/libImath-3_1.a /usr/local/lib/libIlmThread-3_3.a /usr/local/lib/libOpenEXRCore-3_3.a /usr/local/lib/libOpenEXRUtil-3_3.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.

View File

@@ -23,7 +23,7 @@
Usage:
python infra/helper.py build_image kimageformats
python infra/helper.py build_fuzzers --sanitizer undefined|address|memory kimageformats
python infra/helper.py run_fuzzer kimageformats kimgio_[ani|avif|dds|exr|hdr|heif|iff|jp2|jxl|jxr|kra|ora|pcx|pfm|pic|psd|pxr|qoi|ras|raw|rgb|sct|tga|xcf]_fuzzer
python infra/helper.py run_fuzzer kimageformats kimgio_[ani|avif|dds|exr|hdr|heif|iff|jp2|jxl|jxr|kra|ora|pcx|pfm|pic|psd|pxr|qoi|ras|raw|rgb|sct|tim|tga|xcf]_fuzzer
*/
#include <QBuffer>
@@ -52,6 +52,7 @@
#include "raw_p.h"
#include "rgb_p.h"
#include "sct_p.h"
#include "tim_p.h"
#include "tga_p.h"
#include "xcf_p.h"

View File

@@ -33,12 +33,12 @@ 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/qttools.git
git clone --depth 1 -b master https://invent.kde.org/frameworks/karchive.git
git clone --depth 1 -b v3.12.0 https://aomedia.googlesource.com/aom
git clone --depth 1 -b v1.2.1 https://github.com/AOMediaCodec/libavif.git
git clone --depth 1 -b v3.13.1 https://aomedia.googlesource.com/aom
git clone --depth 1 -b v1.3.0 https://github.com/AOMediaCodec/libavif.git
git clone --depth 1 https://github.com/strukturag/libde265.git
git clone --depth 1 -b v2.5.3 https://github.com/uclouvain/openjpeg.git
git clone --depth 1 -b v2.5.4 https://github.com/uclouvain/openjpeg.git
git clone --depth 1 https://github.com/strukturag/libheif.git
git clone --depth=1 --branch v0.11.x --recursive --shallow-submodules https://github.com/libjxl/libjxl.git
git clone --depth=1 --recursive --shallow-submodules https://github.com/libjxl/libjxl.git
git clone --depth 1 https://github.com/LibRaw/LibRaw
git clone --depth 1 https://github.com/mircomir/jxrlib.git
git clone --depth 1 -b v2.6.0 https://github.com/cisco/openh264.git

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

View File

@@ -0,0 +1,11 @@
[
{
"fileName" : "profile_gray.png",
"colorSpace" : {
"colorModel" : "Gray",
"primaries" : "Custom",
"transferFunction" : "SRgb",
"gamma" : 2.31
}
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

View File

@@ -0,0 +1,11 @@
[
{
"fileName" : "profile_gray_gamma22.png",
"colorSpace" : {
"colorModel" : "Gray",
"primaries" : "Custom",
"transferFunction" : "Gamma",
"gamma" : 2.2
}
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

View File

@@ -0,0 +1,11 @@
[
{
"fileName" : "profile_gray_gamma28.png",
"colorSpace" : {
"colorModel" : "Gray",
"primaries" : "Custom",
"transferFunction" : "Gamma",
"gamma" : 2.8
}
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

View File

@@ -0,0 +1,11 @@
[
{
"fileName" : "profile_gray_linear.png",
"colorSpace" : {
"colorModel" : "Gray",
"primaries" : "Custom",
"transferFunction" : "Linear",
"gamma" : 1
}
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

View File

@@ -0,0 +1,12 @@
[
{
"fileName" : "profile_gray_prophoto.png",
"colorSpace" : {
"description" : "grayscale D50 with ProPhoto TRC",
"colorModel" : "Gray",
"primaries" : "Custom",
"transferFunction" : "Custom",
"gamma" : 0
}
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

View File

@@ -0,0 +1,9 @@
[
{
"fileName" : "cdi_dyuv_each.iff",
"resolution" : {
"dotsPerMeterX" : 3937,
"dotsPerMeterY" : 5249
}
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

View File

@@ -0,0 +1,37 @@
[
{
"fileName" : "sv5_rgba32_rgx.png",
"colorSpace" : {
"description" : "TIFF ICC Profile",
"primaries" : "SRgb",
"transferFunction" : "SRgb",
"gamma" : 0
},
"metadata" : [
{
"key" : "Author",
"value" : "KDE Project"
},
{
"key" : "Copyright",
"value" : "@2025 KDE Project"
},
{
"key" : "CreationDate",
"value" : "2025-01-14T10:34:51"
},
{
"key" : "Description",
"value" : "TV broadcast test image."
},
{
"key" : "Title",
"value" : "Test Card"
}
],
"resolution" : {
"dotsPerMeterX" : 2835,
"dotsPerMeterY" : 2835
}
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

View File

@@ -0,0 +1,14 @@
[
{
"fileName" : "gray_linear_lossy.png",
"fuzziness" : 1,
"description" : "Minimum fuzziness value to pass the test on all architectures.",
"colorSpace" : {
"description" : "Gra_D65_Rel_SRG",
"primaries" : "Custom",
"transferFunction" : "SRgb",
"gamma" : 0,
"colorModel" : "Gray"
}
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -2,8 +2,8 @@
{
"fileName" : "32bit_grayscale.png",
"colorSpace" : {
"description" : "Linear Grayscale Profile",
"colorModel" : "Gray",
"description" : "RGB emulation of \"Linear Grayscale Profile\"",
"colorModel" : "Rgb",
"primaries" : "Custom",
"transferFunction" : "Linear",
"gamma" : 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

View File

@@ -0,0 +1,26 @@
[
{
"fileName" : "testcard_graya16.png",
"colorSpace" : {
"description" : "RGB emulation of \"Gray Gamma 2.2\"",
"colorModel" : "Rgb",
"primaries" : "SRgb",
"transferFunction" : "Gamma",
"gamma" : 2.19922
},
"metadata" : [
{
"key" : "ModificationDate",
"value" : "2025-11-17T07:27:47"
},
{
"key" : "Software" ,
"value" : "Adobe Photoshop 26.11 (Windows)"
}
],
"resolution" : {
"dotsPerMeterX" : 11811,
"dotsPerMeterY" : 11811
}
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

View File

@@ -0,0 +1,26 @@
[
{
"fileName" : "testcard_graya32.png",
"colorSpace" : {
"description" : "RGB emulation of \"Profilo scala di grigio lineare\"",
"colorModel" : "Rgb",
"primaries" : "Custom",
"transferFunction" : "Linear",
"gamma" : 1
},
"metadata" : [
{
"key" : "ModificationDate",
"value" : "2025-11-17T07:29:19"
},
{
"key" : "Software" ,
"value" : "Adobe Photoshop 26.11 (Windows)"
}
],
"resolution" : {
"dotsPerMeterX" : 11811,
"dotsPerMeterY" : 11811
}
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

View File

@@ -0,0 +1,28 @@
[
{
"fileName" : "testcard_graya8.png",
"fuzziness" : 1,
"perceptiveFuzziness" : true,
"colorSpace" : {
"description" : "RGB emulation of \"Gray Gamma 2.2\"",
"colorModel" : "Rgb",
"primaries" : "SRgb",
"transferFunction" : "Gamma",
"gamma" : 2.19922
},
"metadata" : [
{
"key" : "ModificationDate",
"value" : "2025-11-17T07:28:50"
},
{
"key" : "Software" ,
"value" : "Adobe Photoshop 26.11 (Windows)"
}
],
"resolution" : {
"dotsPerMeterX" : 11811,
"dotsPerMeterY" : 11811
}
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

View File

@@ -0,0 +1,6 @@
[
{
"fileName" : "fruktpilot.png",
"fuzziness" : 1
}
]

View File

@@ -0,0 +1,6 @@
[
{
"fileName" : "fruktpilot16_icc.png",
"fuzziness" : 1
}
]

View File

@@ -0,0 +1,6 @@
[
{
"fileName" : "fruktpilot32_icc.png",
"fuzziness" : 1
}
]

View File

@@ -0,0 +1,6 @@
[
{
"fileName" : "fruktpilot32fplin_icc.png",
"fuzziness" : 1
}
]

View File

@@ -0,0 +1,6 @@
[
{
"fileName" : "fruktpilot_icc.png",
"fuzziness" : 1
}
]

View File

@@ -258,22 +258,26 @@ int main(int argc, char **argv)
});
QTextStream(stdout) << "QImageReader::supportedImageFormats: " << formatStrings.join(", ") << "\n";
// checks if the format has read capability
if (!formats.contains(format)) {
if (format == "avci" || format == "heif" || format == "hej2") {
QTextStream(stdout) << "WARNING : " << suffix << " is not supported with current libheif configuration!\n"
<< "********* "
<< "Finished basic read tests for " << suffix << " images *********\n";
return 0;
}
QTextStream(stdout) << "FAIL : current configuration is missing necessary decoder(s) for " << suffix << "!\n"
<< "********* "
<< "Finished basic read tests for " << suffix << " images *********\n";
return 1;
}
const QFileInfoList lstImgDir = imgdir.entryInfoList();
// Launch 2 runs for each test: first run on a random access device, second run on a sequential access device
for (int seq = 0; seq < 2; ++seq) {
// Launch 3 runs for each test:
// - first run on a random access device with allocation limit set to 256 MiB.
// - second run on a random access device with allocation limit set to 0 MiB.
// - third run on a sequential access device.
for (int run = 0; run < 3; ++run) {
QImageReader::setAllocationLimit(run == 1 ? 0 : 256);
bool seq = run == 2;
if (seq) {
QTextStream(stdout) << "* Run on SEQUENTIAL ACCESS device\n";
} else {
QTextStream(stdout) << "* Run on RANDOM ACCESS device\n";
QTextStream(stdout) << "* Run on RANDOM ACCESS device (allocation limit: " << QImageReader::allocationLimit() << " MiB)\n";
}
for (const QFileInfo &fi : lstImgDir) {
TemplateImage timg(fi);
@@ -338,12 +342,7 @@ int main(int argc, char **argv)
OptionTest optionTest;
if (!optionTest.store(&inputReader)) {
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": error while reading options\n";
if (format == "heif") {
// libheif + ffmpeg decoder is unable to load all HEIF files.
++skipped;
} else {
++failed;
}
++failed;
continue;
}

View File

@@ -138,13 +138,11 @@ bool TemplateImage::checkOptionaInfo(const QImage& image, QString& error) const
return false;
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
auto clmName = QString(QMetaEnum::fromType<QColorSpace::ColorModel>().valueToKey(quint64(cs.colorModel())));
if (clmName != clm) {
error = QStringLiteral("ColorSpace ColorModel mismatch (current: %1, expected: %2)!").arg(clmName, clm);
return false;
}
#endif
}
// Test resolution

View File

@@ -56,7 +56,7 @@ public:
/*!
* \brief skipSequentialDeviceTest
* \return tre it the sequential test should be skipped.
* \return True if the sequential test should be skipped.
*/
bool skipSequentialDeviceTest() const;

View File

@@ -319,11 +319,9 @@ QImage formatSourceImage(const QImage::Format &format)
image = QImage(QStringLiteral("%1/rgba16.png").arg(folder));
break;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
case QImage::Format_CMYK8888:
image = QImage(QStringLiteral("%1/cmyk8.tif").arg(folder));
break;
#endif
default:
break;
@@ -382,12 +380,7 @@ int formatTest(const QString &suffix, bool createTemplates)
QBuffer buffer(&ba);
auto writtenImage = QImageReader(&buffer, suffix.toLatin1()).read();
if (writtenImage.isNull()) {
if (suffix.toLatin1() == "heif") {
// libheif + ffmpeg decoder is unable to load all HEIF files.
++skipped;
} else {
++failed;
}
++failed;
QTextStream(stdout) << "FAIL : error while reading the image " << formatName << "\n";
continue;
}
@@ -641,23 +634,17 @@ int main(int argc, char **argv)
auto suffix = args.at(0);
// skip test if libheif configuration is obviously incomplete
// skip test if configuration is obviously incomplete
QByteArray format = suffix.toLatin1();
const QList<QByteArray> read_formats = QImageReader::supportedImageFormats();
const QList<QByteArray> write_formats = QImageWriter::supportedImageFormats();
if (!read_formats.contains(format)) {
if (format == "heif" || format == "hej2") {
QTextStream(stdout) << "WARNING : libheif configuration is missing necessary decoder(s)!\n";
return 0;
}
if (!read_formats.contains(format)) { // checks if the format has read capability
QTextStream(stdout) << "FAIL : current configuration is missing necessary decoder(s) for " << format << "!\n";
return 1;
}
if (!write_formats.contains(format)) {
if (format == "heif" || format == "hej2") {
QTextStream(stdout) << "WARNING : libheif configuration is missing necessary encoder(s)!\n";
return 0;
}
const QList<QByteArray> write_formats = QImageWriter::supportedImageFormats();
if (!write_formats.contains(format)) { // checks if the format has write capability
QTextStream(stdout) << "FAIL : libraries configuration is missing necessary encoder(s) for " << format << "!\n";
return 1;
}
// run test

View File

@@ -137,6 +137,10 @@ kimageformats_add_plugin(kimg_sct SOURCES sct.cpp)
##################################
kimageformats_add_plugin(kimg_tim SOURCES tim.cpp)
##################################
kimageformats_add_plugin(kimg_tga SOURCES tga.cpp microexif.cpp scanlineconverter.cpp)
##################################

View File

@@ -5,6 +5,7 @@
*/
#include "ani_p.h"
#include "util_p.h"
#include <QImage>
#include <QLoggingCategory>
@@ -101,7 +102,7 @@ bool ANIHandler::read(QImage *outImage)
}
const auto frameSize = *(reinterpret_cast<const quint32_le *>(frameSizeData.data()));
if (!frameSize) {
if (!frameSize || frameSize > quint32(kMaxQVectorSize)) {
return false;
}
@@ -417,6 +418,9 @@ bool ANIHandler::ensureScanned() const
// IART and INAM are technically inside LIST->INFO but "INFO" is supposedly optional
// so just handle those two attributes wherever we encounter them
} else if (chunkId == "INAM" || chunkId == "IART") {
if (chunkSize > kMaxQVectorSize) {
return false;
}
const QByteArray value = device()->read(chunkSize);
if (static_cast<quint32_le>(value.size()) != chunkSize) {

View File

@@ -265,9 +265,15 @@ bool QAVIFHandler::decode_one_frame()
}
}
uint32_t resultdepth = m_decoder->image->depth;
if (m_decoder->image->matrixCoefficients == 16 && m_decoder->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444 && resultdepth == 10) {
// 10-bit YCgCo-Re AVIF must be decoded to 8bit
resultdepth = 8;
}
QImage::Format resultformat;
if (m_decoder->image->depth > 8) {
if (resultdepth > 8) {
if (loadalpha) {
resultformat = QImage::Format_RGBA64;
} else {
@@ -293,9 +299,7 @@ bool QAVIFHandler::decode_one_frame()
colorspace = QColorSpace::fromIccProfile(icc_data);
if (!colorspace.isValid()) {
qCWarning(LOG_AVIFPLUGIN, "AVIF image has Qt-unsupported or invalid ICC profile!");
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
else {
} else {
if (colorspace.colorModel() == QColorSpace::ColorModel::Cmyk) {
qCWarning(LOG_AVIFPLUGIN, "CMYK ICC profile is not extected for AVIF, discarding the ICCprofile!");
colorspace = QColorSpace();
@@ -325,7 +329,6 @@ bool QAVIFHandler::decode_one_frame()
}
}
}
#endif
} else {
float prim[8] = {0.64f, 0.33f, 0.3f, 0.6f, 0.15f, 0.06f, 0.3127f, 0.329f};
// outPrimaries: rX, rY, gX, gY, bX, bY, wX, wY
@@ -360,14 +363,12 @@ bool QAVIFHandler::decode_one_frame()
case 13:
q_trc = QColorSpace::TransferFunction::SRgb;
break;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
case 16: /* AVIF_TRANSFER_CHARACTERISTICS_PQ */
q_trc = QColorSpace::TransferFunction::St2084;
break;
case 18: /* AVIF_TRANSFER_CHARACTERISTICS_HLG */
q_trc = QColorSpace::TransferFunction::Hlg;
break;
#endif
default:
qCWarning(LOG_AVIFPLUGIN, "CICP colorPrimaries: %d, transferCharacteristics: %d\nThe colorspace is unsupported by this plug-in yet.",
m_decoder->image->colorPrimaries,
@@ -377,11 +378,9 @@ bool QAVIFHandler::decode_one_frame()
}
if (q_trc != QColorSpace::TransferFunction::Custom) { // we create new colorspace using Qt
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
if (loadgray) {
colorspace = QColorSpace(whitePoint, q_trc, q_trc_gamma);
} else {
#endif
switch (m_decoder->image->colorPrimaries) {
/* AVIF_COLOR_PRIMARIES_BT709 */
case 0:
@@ -389,6 +388,9 @@ bool QAVIFHandler::decode_one_frame()
case 2: /* AVIF_COLOR_PRIMARIES_UNSPECIFIED */
colorspace = QColorSpace(QColorSpace::Primaries::SRgb, q_trc, q_trc_gamma);
break;
case 9:
colorspace = QColorSpace(QColorSpace::Primaries::Bt2020, q_trc, q_trc_gamma);
break;
/* AVIF_COLOR_PRIMARIES_SMPTE432 */
case 12:
colorspace = QColorSpace(QColorSpace::Primaries::DciP3D65, q_trc, q_trc_gamma);
@@ -397,9 +399,7 @@ bool QAVIFHandler::decode_one_frame()
colorspace = QColorSpace(whitePoint, redPoint, greenPoint, bluePoint, q_trc, q_trc_gamma);
break;
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
}
#endif
}
if (!colorspace.isValid()) {
@@ -414,7 +414,7 @@ bool QAVIFHandler::decode_one_frame()
rgb.maxThreads = m_decoder->maxThreads;
#endif
if (m_decoder->image->depth > 8) {
if (resultdepth > 8) {
rgb.depth = 16;
rgb.format = AVIF_RGB_FORMAT_RGBA;
@@ -513,21 +513,12 @@ bool QAVIFHandler::decode_one_frame()
#else
switch (m_decoder->image->imir.axis) {
#endif
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
case 0: // top-to-bottom
result = result.mirrored(false, true);
break;
case 1: // left-to-right
result = result.mirrored(true, false);
break;
#else
case 0: // top-to-bottom
result = result.flipped(Qt::Vertical);
break;
case 1: // left-to-right
result = result.flipped(Qt::Horizontal);
break;
#endif
}
}
@@ -743,22 +734,65 @@ bool QAVIFHandler::write(const QImage &image)
/* AVIF_TRANSFER_CHARACTERISTICS_LINEAR */
avif->transferCharacteristics = (avifTransferCharacteristics)8;
break;
case QColorSpace::TransferFunction::Gamma:
if (qAbs(tmpgrayimage.colorSpace().gamma() - 2.2f) < 0.1f) {
/* AVIF_TRANSFER_CHARACTERISTICS_BT470M */
avif->transferCharacteristics = (avifTransferCharacteristics)4;
} else if (qAbs(tmpgrayimage.colorSpace().gamma() - 2.8f) < 0.1f) {
/* AVIF_TRANSFER_CHARACTERISTICS_BT470BG */
avif->transferCharacteristics = (avifTransferCharacteristics)5;
}
break;
case QColorSpace::TransferFunction::SRgb:
/* AVIF_TRANSFER_CHARACTERISTICS_SRGB */
avif->transferCharacteristics = (avifTransferCharacteristics)13;
break;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
case QColorSpace::TransferFunction::St2084:
avif->transferCharacteristics = (avifTransferCharacteristics)16;
break;
case QColorSpace::TransferFunction::Hlg:
avif->transferCharacteristics = (avifTransferCharacteristics)18;
break;
#endif
default:
/* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
break;
}
if (avif->transferCharacteristics == 2) { // in case TransferFunction was not identified yet
if (tmpgrayimage.colorSpace().colorModel() == QColorSpace::ColorModel::Gray && lossless) {
avif->colorPrimaries = (avifColorPrimaries)2;
avif->matrixCoefficients = (avifMatrixCoefficients)6;
QByteArray iccprofile_gray = tmpgrayimage.colorSpace().iccProfile();
if (iccprofile_gray.size() > 0) {
#if AVIF_VERSION >= 1000000
res = avifImageSetProfileICC(avif, reinterpret_cast<const uint8_t *>(iccprofile_gray.constData()), iccprofile_gray.size());
if (res != AVIF_RESULT_OK) {
qCWarning(LOG_AVIFPLUGIN, "ERROR in avifImageSetProfileICC: %s", avifResultToString(res));
return false;
}
#else
avifImageSetProfileICC(avif, reinterpret_cast<const uint8_t *>(iccprofile_gray.constData()), iccprofile_gray.size());
#endif
}
} else { // convert to grayscale with SRgb
tmpgrayimage.convertToColorSpace(QColorSpace(QPointF(0.3127f, 0.329f), QColorSpace::TransferFunction::SRgb), QImage::Format_Grayscale16);
switch (tmpgrayimage.format()) {
case QImage::Format_Grayscale8:
save_depth = 8;
break;
case QImage::Format_Grayscale16:
save_depth = 10;
avif->transferCharacteristics = (avifTransferCharacteristics)13;
break;
default:
qCWarning(LOG_AVIFPLUGIN, "Error saving Gray image");
return false;
break;
}
}
}
}
if (save_depth > 8) { // QImage::Format_Grayscale16
@@ -799,7 +833,6 @@ bool QAVIFHandler::write(const QImage &image)
}
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
QImage tmpcolorimage;
auto cs = image.colorSpace();
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) {
@@ -814,9 +847,6 @@ bool QAVIFHandler::write(const QImage &image)
} else {
tmpcolorimage = image.convertToFormat(tmpformat);
}
#else
QImage tmpcolorimage = image.convertToFormat(tmpformat);
#endif
avifPixelFormat pixel_format = AVIF_PIXEL_FORMAT_YUV420;
if (m_quality >= KIMG_AVIF_QUALITY_HIGH) {
@@ -847,6 +877,10 @@ bool QAVIFHandler::write(const QImage &image)
/* AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL */
matrix_to_save = (avifMatrixCoefficients)12;
break;
case QColorSpace::Primaries::Bt2020:
primaries_to_save = (avifColorPrimaries)9;
matrix_to_save = (avifMatrixCoefficients)12;
break;
default:
/* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
primaries_to_save = (avifColorPrimaries)2;
@@ -876,14 +910,12 @@ bool QAVIFHandler::write(const QImage &image)
/* AVIF_TRANSFER_CHARACTERISTICS_SRGB */
transfer_to_save = (avifTransferCharacteristics)13;
break;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
case QColorSpace::TransferFunction::St2084:
transfer_to_save = (avifTransferCharacteristics)16;
break;
case QColorSpace::TransferFunction::Hlg:
transfer_to_save = (avifTransferCharacteristics)18;
break;
#endif
default:
/* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
transfer_to_save = (avifTransferCharacteristics)2;
@@ -919,14 +951,12 @@ bool QAVIFHandler::write(const QImage &image)
case 5: // AVIF_TRANSFER_CHARACTERISTICS_BT470BG
tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, 2.8f));
break;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
case 16:
tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, QColorSpace::TransferFunction::St2084));
break;
case 18:
tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, QColorSpace::TransferFunction::Hlg));
break;
#endif
default: // AVIF_TRANSFER_CHARACTERISTICS_SRGB + any other
tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, QColorSpace::TransferFunction::SRgb));
transfer_to_save = (avifTransferCharacteristics)13;

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/*
This file is part of the KDE project
SPDX-FileCopyrightText: 2025 Mirco Miranda <mircomir@outlook.com>
SPDX-FileCopyrightText: 2025-2026 Mirco Miranda <mircomir@outlook.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
@@ -10,6 +10,7 @@
* - https://wiki.amigaos.net/wiki/IFF_FORM_and_Chunk_Registry
* - https://www.fileformat.info/format/iff/egff.htm
* - https://download.autodesk.com/us/maya/2010help/index.html (Developer resources -> File formats -> Maya IFF)
* - https://aminet.net/package/dev/misc/IFF-RGFX
*/
#ifndef KIMG_CHUNKS_P_H
@@ -36,7 +37,7 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
#define LIST_CHUNK QByteArray("LIST")
#define PROP_CHUNK QByteArray("PROP")
// Main chuncks (Maya)
// Main chunks (Maya)
#define CAT4_CHUNK QByteArray("CAT4") // 4 byte alignment
#define FOR4_CHUNK QByteArray("FOR4")
#define LIS4_CHUNK QByteArray("LIS4")
@@ -55,7 +56,17 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
#define CMAP_CHUNK QByteArray("CMAP")
#define CMYK_CHUNK QByteArray("CMYK") // https://wiki.amigaos.net/wiki/ILBM_IFF_Interleaved_Bitmap#ILBM.CMYK
#define DPI__CHUNK QByteArray("DPI ")
#define IDAT_CHUNK QByteArray("IDAT")
#define IHDR_CHUNK QByteArray("IHDR")
#define IPAR_CHUNK QByteArray("IPAR")
#define PLTE_CHUNK QByteArray("PLTE")
#define RBOD_CHUNK QByteArray("RBOD")
#define RCOL_CHUNK QByteArray("RCOL")
#define RFLG_CHUNK QByteArray("RFLG")
#define RGHD_CHUNK QByteArray("RGHD")
#define RSCM_CHUNK QByteArray("RSCM")
#define XBMI_CHUNK QByteArray("XBMI")
#define YUVS_CHUNK QByteArray("YUVS")
// Different palette for scanline
#define BEAM_CHUNK QByteArray("BEAM")
@@ -79,14 +90,18 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
#define FVER_CHUNK QByteArray("FVER")
#define HIST_CHUNK QByteArray("HIST")
#define NAME_CHUNK QByteArray("NAME")
#define VDAT_CHUNK QByteArray("VDAT")
#define VERS_CHUNK QByteArray("VERS")
#define XMP0_CHUNK QByteArray("XMP0") // https://aminet.net/package/docs/misc/IFF-metadata
// FORM types
#define ACBM_FORM_TYPE QByteArray("ACBM")
#define ILBM_FORM_TYPE QByteArray("ILBM")
#define IMAG_FORM_TYPE QByteArray("IMAG")
#define PBM__FORM_TYPE QByteArray("PBM ")
#define RGB8_FORM_TYPE QByteArray("RGB8")
#define RGBN_FORM_TYPE QByteArray("RGBN")
#define RGFX_FORM_TYPE QByteArray("RGFX")
#define CIMG_FOR4_TYPE QByteArray("CIMG")
#define TBMP_FOR4_TYPE QByteArray("TBMP")
@@ -145,7 +160,7 @@ public:
/*!
* \brief bytes
* \return The size (in bytes) of the chunck data.
* \return The size (in bytes) of the chunk data.
*/
quint32 bytes() const;
@@ -350,6 +365,12 @@ protected:
static ChunkList innerFromDevice(QIODevice *d, bool *ok, IFFChunk *parent = nullptr);
/*!
* \brief dataBytes
* \return Maximum usable cache data size.
*/
quint32 dataBytes() const;
private:
char _chunkId[4];
@@ -418,7 +439,8 @@ public:
enum Compression {
Uncompressed = 0, /**< Image data are uncompressed. */
Rle = 1, /**< Image data are RLE compressed. */
RgbN8 = 4 /**< RGB8/RGBN compresson. */
Vdat = 2, /**< Image data are VDAT compressed. */
RgbN8 = 4 /**< Image data are RGB8/RGBN compressed. */
};
enum Masking {
None = 0, /**< Designates an opaque rectangular image. */
@@ -750,10 +772,11 @@ public:
/*!
* \brief readStride
* \param d The device.
* \param header The bitmap header.
* \param y The current scanline.
* \param header The bitmap header.
* \param camg The CAMG chunk (optional)
* \param cmap The CMAP chunk (optional)
* \param ipal The per-line palette chunk (optional)
* \param formType The type of the current form chunk.
* \return The scanline as requested for QImage.
* \warning Call resetStrideRead() once before this one.
@@ -770,8 +793,6 @@ public:
* \brief resetStrideRead
* Reset the stride read set the position at the beginning of the data and reset all buffers.
* \param d The device.
* \param header The BMHDChunk chunk (mandatory)
* \param camg The CAMG chunk (optional)
* \return True on success, otherwise false.
* \sa strideRead
* \note Must be called once before strideRead().
@@ -782,6 +803,7 @@ public:
* \brief safeModeId
* \param header The header.
* \param camg The CAMG chunk.
* \param cmap The CMAP chunk.
* \return The most likely ModeId if not explicitly specified.
*/
static CAMGChunk::ModeIds safeModeId(const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap = nullptr);
@@ -802,6 +824,8 @@ protected:
QByteArray rgbN(const QByteArray &planes, qint32 y, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr, const IPALChunk *ipal = nullptr) const;
virtual bool innerReadStructure(QIODevice *d) override;
private:
mutable QByteArray _readBuffer;
};
@@ -864,7 +888,7 @@ public:
/*!
* \brief transformation
* \return The image transformation.
* \note The Default implentation returns the trasformation of EXIF chunk (if any).
* \note The Default implementation returns the transformation of EXIF chunk (if any).
*/
virtual QImageIOHandler::Transformation transformation() const;
@@ -915,6 +939,13 @@ public:
protected:
virtual bool innerReadStructure(QIODevice *d) override;
private:
QImage::Format iffFormat() const;
QImage::Format cdiFormat() const;
QImage::Format rgfxFormat() const;
};
@@ -1351,6 +1382,32 @@ protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The VDATChunk class
*/
class VDATChunk : public IFFChunk
{
public:
virtual ~VDATChunk() override;
VDATChunk();
VDATChunk(const VDATChunk& other) = default;
VDATChunk& operator =(const VDATChunk& other) = default;
virtual bool isValid() const override;
CHUNKID_DEFINE(VDAT_CHUNK)
const QByteArray &uncompressedData(const BMHDChunk *header) const;
protected:
virtual bool innerReadStructure(QIODevice *d) override;
private:
mutable QByteArray uncompressed;
};
/*!
* \brief The VERSChunk class
*/
@@ -1394,6 +1451,500 @@ protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* *** I-CD IFF CHUNKS ***
*/
/*!
* \brief The IHDRChunk class
* Image Header
*/
class IHDRChunk: public IFFChunk
{
public:
enum Model {
Invalid = 0, /**< Invalid model. */
Rgb888 = 1, /**< red, green, blue - 8 bits per color. */
Rgb555 = 2, /**< Green Book absolute RGB. */
DYuv = 3, /**< Green Book Delta YUV. */
CLut8 = 4, /**< Green Book 8 bit CLUT. */
CLut7 = 5, /**< Green Book 7 bit CLUT. */
CLut4 = 6, /**< Green Book 4 bit CLUT. */
CLut3 = 7, /**< Green Book 3 bit CLUT. */
Rle7 = 8, /**< Green Book runlength coded 7 bit CLUT. */
Rle3 = 9, /**< Green Book runlength coded 3 bit CLUT. */
PaletteOnly = 10 /**< color palette only. */
};
enum DYuvKind {
One = 0,
Each = 1
};
struct Yuv {
Yuv(quint8 y0 = 0, quint8 u0 = 0, quint8 v0 = 0) : y(y0), u(u0), v(v0) {}
quint8 y;
quint8 u;
quint8 v;
};
virtual ~IHDRChunk() override;
IHDRChunk();
IHDRChunk(const IHDRChunk& other) = default;
IHDRChunk& operator =(const IHDRChunk& other) = default;
virtual bool isValid() const override;
/*!
* \brief width
* \return Width of the bitmap in pixels.
*/
qint32 width() const;
/*!
* \brief height
* \return Height of the bitmap in pixels.
*/
qint32 height() const;
/*!
* \brief size
* \return Size in pixels.
*/
QSize size() const;
/*!
* \brief lineSize
* Physical width of image (number of bytes in each scan line, including any data required at
* the end of each scan line for padding [see description of each models IDAT chunk for padding
* rules]) This field is not used when model() = Rle7 or Rle3.
* When model() = Rgb555, this field defines the size of one scan line of the upper
* or lower portion of the pixel data, but not the size of them both together.
*/
qint32 lineSize() const;
/*!
* \brief model
* Image model (coding method)
*/
Model model() const;
/*!
* \brief depth
* Physical size of pixel (number of bits per pixel used in storing image data) When
* model() = Rle7 or Rle3, this value only represents the size of a
* single pixel; the size of a run of pixels is indeterminate.
*/
quint16 depth() const;
/*!
* \brief yuvKind
* if model() = DYuv, indicates whether there is one DYUV start value for all
* scan lines (in yuvStart()), or whether each scan line has its own start value in the
* YUVS chunk which follows.
*/
DYuvKind yuvKind() const;
/*!
* \brief yuvStart
* Start values for DYUV image if model() = DYuv and dYuvKind() = One
*/
Yuv yuvStart() const;
CHUNKID_DEFINE(IHDR_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The IHDRChunk class
*/
class IPARChunk: public IFFChunk
{
public:
struct Rgb {
Rgb(quint8 r0 = 0, quint8 g0 = 0, quint8 b0 = 0) : r(r0), g(g0), b(b0) {}
quint8 r;
quint8 g;
quint8 b;
};
virtual ~IPARChunk() override;
IPARChunk();
IPARChunk(const IPARChunk& other) = default;
IPARChunk& operator =(const IPARChunk& other) = default;
virtual bool isValid() const override;
/*!
* \brief xOffset
* X offset of origin in source image [0 < xOffset() < xPage()]
*/
qint32 xOffset() const;
/*!
* \brief yOffset
* \returnX offset of origin in source image [0 < yOffset() < yPage()]
*/
qint32 yOffset() const;
/*!
* \brief aspectRatio
* Aspect ratio of pixels in source image.
*/
double aspectRatio() const;
/*!
* \brief xPage
* X size of source image.
*/
qint32 xPage() const;
/*!
* \brief yPage
* Y size of source image.
*/
qint32 yPage() const;
/*!
* \brief xGrub
* X location of hot spot within image.
*/
qint32 xGrub() const;
/*!
* \brief yGrub
* Y location of hot spot within image.
*/
qint32 yGrub() const;
/*!
* \brief transparency
* Transparent color.
*/
Rgb transparency() const;
/*!
* \brief mask
* Mask color.
*/
Rgb mask() const;
CHUNKID_DEFINE(IPAR_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The PLTEChunk class
*/
class PLTEChunk : public CMAPChunk
{
public:
virtual ~PLTEChunk() override;
PLTEChunk();
PLTEChunk(const PLTEChunk& other) = default;
PLTEChunk& operator =(const PLTEChunk& other) = default;
virtual bool isValid() const override;
/*!
* \brief count
* \return The number of color in the palette.
*/
virtual qint32 count() const override;
CHUNKID_DEFINE(PLTE_CHUNK)
protected:
qint32 offset() const;
qint32 total() const;
virtual QList<QRgb> innerPalette() const override;
};
/*!
* \brief The YUVSChunk class
*/
class YUVSChunk : public IFFChunk
{
public:
virtual ~YUVSChunk() override;
YUVSChunk();
YUVSChunk(const YUVSChunk& other) = default;
YUVSChunk& operator =(const YUVSChunk& other) = default;
virtual bool isValid() const override;
qint32 count() const;
IHDRChunk::Yuv yuvStart(qint32 y) const;
CHUNKID_DEFINE(YUVS_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The IDATChunk class
*/
class IDATChunk : public IFFChunk
{
public:
virtual ~IDATChunk() override;
IDATChunk();
IDATChunk(const IDATChunk& other) = default;
IDATChunk& operator =(const IDATChunk& other) = default;
virtual bool isValid() const override;
CHUNKID_DEFINE(IDAT_CHUNK)
/*!
* \brief readStride
* \param d The device.
* \param y The current scanline.
* \param header The bitmap header.
* \param params The additional parameters (optional)
* \return The scanline as requested for QImage.
* \warning Call resetStrideRead() once before this one.
*/
QByteArray strideRead(QIODevice *d,
qint32 y,
const IHDRChunk *header,
const IPARChunk *params = nullptr,
const YUVSChunk *yuvs = nullptr) const;
/*!
* \brief resetStrideRead
* Reset the stride read set the position at the beginning of the data and reset all buffers.
* \param d The device.
* \return True on success, otherwise false.
* \sa strideRead
* \note Must be called once before strideRead().
*/
bool resetStrideRead(QIODevice *d) const;
protected:
quint32 strideSize(const IHDRChunk *header) const;
};
/*!
* *** RGFX IFF CHUNKS ***
*/
/*!
* \brief The RGHDChunk class
*/
class RGHDChunk : public IFFChunk
{
public:
enum Compression {
Uncompressed = 0,
Xpk = 1, /**< any XPK-packer */
Zip = 2 /**< libzip (LZ77/ZIP) compression */
};
enum BitmapType {
Planar8 = 0x0000, /**< unaligned planar 8 bit bitmap */
Chunky8 = 0x0001, /**< unaligned chunky 8 bit bitmap */
Rgb24 = 0x0002, /**< 3-byte 24 bit RGB triples */
Rgb32 = 0x0004, /**< 4-byte 32 bit ARGB quadruples */
Rgb15 = 0x0010, /**< 2-byte 15 bit RGB (x+3x5 bit integer) */
Rgb16 = 0x0020, /**< 2-byte 16 bit ARGB (1+3x5 bit integer) */
Rgb48 = 0x0040, /**< 6-byte 48 bit RGB (3x 16 bit integer) */
Rgb64 = 0x0080, /**< 8-byte 64 bit ARGB (4x 16 bit integer) */
Rgb96 = 0x0100, /**< 12-byte 96 bit RGB (3x 32 bit float) */
Rgb128 = 0x0200, /**< 16-byte 128 bit ARGB (4x 32 bit float) */
HasAlpha = (1 << 30), /**< set if A is meaningful */
HasInvAlpha = (1 << 31) /**< set if A is meaningful but inversed (A = 255 - alpha) */
};
Q_DECLARE_FLAGS(BitmapTypes, BitmapType)
virtual ~RGHDChunk() override;
RGHDChunk();
RGHDChunk(const RGHDChunk&) = default;
RGHDChunk& operator=(const RGHDChunk&) = default;
CHUNKID_DEFINE(RGHD_CHUNK)
virtual bool isValid() const override;
QSize size() const;
qint32 leftEdge() const;
qint32 topEdge() const;
qint32 width() const;
qint32 height() const;
qint32 pageWidth() const;
qint32 pageHeight() const;
quint32 depth() const;
quint32 pixelBits() const;
quint32 bytesPerLine() const;
Compression compression() const;
quint32 xAspect() const;
quint32 yAspect() const;
BitmapTypes bitmapType() const;
double aspectRatio() const;
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The RCOLChunk class
*/
class RCOLChunk : public CMAPChunk
{
public:
virtual ~RCOLChunk() override;
RCOLChunk();
RCOLChunk(const RCOLChunk& other) = default;
RCOLChunk& operator =(const RCOLChunk& other) = default;
virtual bool isValid() const override;
virtual qint32 count() const override;
CHUNKID_DEFINE(RCOL_CHUNK)
protected:
virtual QList<QRgb> innerPalette() const override;
};
/*!
* \brief The RFLGChunk class
*/
class RFLGChunk : public IFFChunk
{
public:
enum class Flag : quint32 {
FromGray = 0x08, /**< created from 8/16 bit gray source so R==G==B */
From8Bit = 0x10, /**< created from 8 bit source, so (R,G,B)&0xFF00 == ... & 0x00FF */
From4Bit = 0x20, /**< created from 4 bit source, so (R,G,B)&0xF0 == ... & 0x0F */
From8BitAlpha = 0x40, /**< 16/32 bit alpha created from 8 bit alpha source */
From16BitAlpha = 0x80 /**< 32 bit alpha created from 16 bit alpha source */
};
Q_DECLARE_FLAGS(Flags, Flag)
virtual ~RFLGChunk() override;
RFLGChunk();
RFLGChunk(const RFLGChunk&) = default;
RFLGChunk& operator=(const RFLGChunk&) = default;
CHUNKID_DEFINE(RFLG_CHUNK)
virtual bool isValid() const override;
Flags flags() const;
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The RSCMChunk class
*/
class RSCMChunk : public IFFChunk
{
public:
virtual ~RSCMChunk() override;
RSCMChunk();
RSCMChunk(const RSCMChunk&) = default;
RSCMChunk& operator=(const RSCMChunk&) = default;
CHUNKID_DEFINE(RSCM_CHUNK)
virtual bool isValid() const override;
/*!
* \brief viewMode Default screenmode
*
* Since HAM modes only can be identified by their ID (or DIPF) you have to make sure,
* that rcsm_ViewMode is OR'ed with HAM_KEY for these (same for EHB and EXTRAHALFBRITE_KEY).
*
* Specific RTG ViewModes will lose their meaning, as soon as graphics are transferred between
* different systems, which is why the two LocalVM entries are considered obsolete.
*
* Always set the obsolete entries to 0xFFFFFFFF and avoid interpreting them.
* \return default screenmode
*/
quint32 viewMode() const;
/*!
* \brief localVM0
* \obsolete obsolete local RTG
*/
quint32 localVM0() const;
/*!
* \brief localVM1
* \obsolete obsolete local RTG
*/
quint32 localVM1() const;
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The RBODChunk class
*/
class RBODChunk : public IFFChunk
{
public:
virtual ~RBODChunk() override;
RBODChunk();
RBODChunk(const RBODChunk&) = default;
RBODChunk& operator=(const RBODChunk&) = default;
CHUNKID_DEFINE(RBOD_CHUNK)
virtual bool isValid() const override;
QByteArray strideRead(QIODevice *d,
qint32 y,
const RGHDChunk *header,
const RSCMChunk *rcsm = nullptr,
const RCOLChunk *rcol = nullptr) const;
bool resetStrideRead(QIODevice *d) const;
private:
QByteArray deinterleave(const QByteArray &planes, qint32 y, const RGHDChunk *header, const RSCMChunk *rcsm = nullptr, const RCOLChunk *rcol = nullptr) const;
quint32 strideSize(const RGHDChunk *header) const;
};
/*!
* *** UNDOCUMENTED CHUNKS ***

View File

@@ -20,7 +20,7 @@
#include <cmath>
#ifndef DDS_DISABLE_STRIDE_ALIGNMENT
// Disable the stride aligment based on DDS pitch: it is known that some writers do not set it correctly
// Disable the stride alignment based on DDS pitch: it is known that some writers do not set it correctly
// #define DDS_DISABLE_STRIDE_ALIGNMENT
#endif
@@ -2120,10 +2120,8 @@ bool writeL8(const QImage &outImage, QDataStream &s)
}
ScanLineConverter slc(QImage::Format_Grayscale8);
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
if(outImage.colorSpace().isValid())
slc.setTargetColorSpace(QColorSpace(QPointF(0.3127, 0.3291), QColorSpace::TransferFunction::SRgb));
#endif
for (int y = 0, h = outImage.height(); y < h; ++y) {
const quint8 *scanLine = reinterpret_cast<const quint8*>(slc.convertedScanLine(outImage, y));

View File

@@ -179,7 +179,7 @@ bool EPSHandler::read(QImage *image)
const QString gsExec = QStandardPaths::findExecutable(QStringLiteral("gs"));
if (gsExec.isEmpty()) {
qCWarning(EPSPLUGIN) << "Couldn't find gs exectuable (from GhostScript) in PATH.";
qCWarning(EPSPLUGIN) << "Couldn't find gs executable (from GhostScript) in PATH.";
return false;
}

View File

@@ -404,6 +404,7 @@ bool EXRHandler::read(QImage *outImage)
Imf::Array2D<Imf::Rgba> pixels;
pixels.resizeErase(EXR_LINES_PER_BLOCK, width);
std::memset(pixels[0], 0, sizeof(Imf::Rgba) * EXR_LINES_PER_BLOCK * width);
bool isRgba = image.hasAlphaChannel();
for (int y = 0, n = 0; y < height; y += n) {
@@ -608,13 +609,13 @@ bool EXRHandler::write(const QImage &image)
pixels.resizeErase(EXR_LINES_PER_BLOCK, width);
// convert the image and write into the stream
auto convFormat = image.hasAlphaChannel() ? QImage::Format_RGBA16FPx4 : QImage::Format_RGBX16FPx4;
auto convFormat = image.hasAlphaChannel() ? QImage::Format_RGBA32FPx4 : QImage::Format_RGBX32FPx4;
ScanLineConverter slc(convFormat);
slc.setDefaultSourceColorSpace(QColorSpace(QColorSpace::SRgb));
slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgbLinear));
for (int y = 0, n = 0; y < height; y += n) {
for (n = 0; n < std::min(EXR_LINES_PER_BLOCK, height - y); ++n) {
auto scanLine = reinterpret_cast<const qfloat16 *>(slc.convertedScanLine(image, y + n));
auto scanLine = reinterpret_cast<const float *>(slc.convertedScanLine(image, y + n));
if (scanLine == nullptr) {
return false;
}

View File

@@ -125,14 +125,16 @@ public:
Header h;
int cnt = 0;
int len;
QByteArray line(MAXLINE + 1, Qt::Uninitialized);
int len = 0;
QByteArray line(MAXLINE, char());
QByteArray format;
// Parse header
do {
len = device->readLine(line.data(), MAXLINE);
len = device->readLine(line.data(), line.size());
if (len < 0) {
break;
}
if (line.startsWith("FORMAT=")) {
format = line.mid(7, len - 7).trimmed();
}
@@ -173,7 +175,11 @@ public:
return h;
}
len = device->readLine(line.data(), MAXLINE);
len = device->readLine(line.data(), line.size());
if (len < 0) {
qCDebug(HDRPLUGIN) << "Invalid HDR file, error while reading the first line after the header";
return h;
}
line.resize(len);
/*
@@ -322,8 +328,7 @@ static bool LoadHDR(QDataStream &s, const Header& h, QImage &img)
return false;
}
QByteArray lineArray;
lineArray.resize(4 * width);
QByteArray lineArray(4 * width, char());
uchar *image = reinterpret_cast<uchar *>(lineArray.data());
for (int cline = 0; cline < height; cline++) {
@@ -509,7 +514,7 @@ bool HDRHandler::canRead(QIODevice *device)
return true;
}
// allow to load offical test cases: https://radsite.lbl.gov/radiance/framed.html
// allow to load official test cases: https://radsite.lbl.gov/radiance/framed.html
device->startTransaction();
auto h = HDRHandlerPrivate::readHeader(device);
device->rollbackTransaction();

View File

@@ -189,7 +189,6 @@ bool HEIFHandler::write_helper(const QImage &image)
}
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
QImage tmpimage;
auto cs = image.colorSpace();
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) {
@@ -205,9 +204,6 @@ bool HEIFHandler::write_helper(const QImage &image)
} else {
tmpimage = image.convertToFormat(tmpformat);
}
#else
QImage tmpimage = image.convertToFormat(tmpformat);
#endif
struct heif_context *context = heif_context_alloc();
struct heif_error err;
@@ -868,9 +864,7 @@ bool HEIFHandler::ensureDecoder()
QColorSpace colorspace = QColorSpace::fromIccProfile(ba);
if (!colorspace.isValid()) {
qCWarning(LOG_HEIFPLUGIN) << "HEIC image has Qt-unsupported or invalid ICC profile!";
}
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
else if (colorspace.colorModel() == QColorSpace::ColorModel::Cmyk) {
} else if (colorspace.colorModel() == QColorSpace::ColorModel::Cmyk) {
qCWarning(LOG_HEIFPLUGIN) << "CMYK ICC profile is not expected for HEIF, discarding the ICCprofile!";
colorspace = QColorSpace();
} else if (colorspace.colorModel() == QColorSpace::ColorModel::Gray) {
@@ -897,7 +891,6 @@ bool HEIFHandler::ensureDecoder()
m_current_image.convertTo(bit_depth > 8 ? QImage::Format_Grayscale16 : QImage::Format_Grayscale8);
}
}
#endif
m_current_image.setColorSpace(colorspace);
}
} else {
@@ -934,14 +927,12 @@ bool HEIFHandler::ensureDecoder()
case 13:
q_trc = QColorSpace::TransferFunction::SRgb;
break;
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
case 16:
q_trc = QColorSpace::TransferFunction::St2084;
break;
case 18:
q_trc = QColorSpace::TransferFunction::Hlg;
break;
#endif
default:
qCWarning(LOG_HEIFPLUGIN) << "CICP color_primaries: %d, transfer_characteristics: %d\nThe colorspace is unsupported by this plug-in yet."
<< nclx->color_primaries

View File

@@ -262,14 +262,26 @@ static void addMetadata(QImage &img, const IFOR_Chunk *form)
// if no explicit resolution was found, apply the aspect ratio to the default one
if (!resChanged) {
auto headers = IFFChunk::searchT<BMHDChunk>(form);
if (!headers.isEmpty()) {
auto xr = headers.first()->xAspectRatio();
auto yr = headers.first()->yAspectRatio();
if (xr > 0 && yr > 0 && xr > yr) {
img.setDotsPerMeterX(img.dotsPerMeterX() * yr / xr);
} else if (xr > 0 && yr > 0 && xr < yr) {
img.setDotsPerMeterY(img.dotsPerMeterY() * xr / yr);
if (form->formType() == IMAG_FORM_TYPE) {
auto params = IFFChunk::searchT<IPARChunk>(form);
if (!params.isEmpty()) {
img.setDotsPerMeterY(img.dotsPerMeterY() * params.first()->aspectRatio());
}
} else if (form->formType() == RGFX_FORM_TYPE) {
auto headers = IFFChunk::searchT<RGHDChunk>(form);
if (!headers.isEmpty()) {
img.setDotsPerMeterY(img.dotsPerMeterY() * headers.first()->aspectRatio());
}
} else {
auto headers = IFFChunk::searchT<BMHDChunk>(form);
if (!headers.isEmpty()) {
auto xr = headers.first()->xAspectRatio();
auto yr = headers.first()->yAspectRatio();
if (xr > 0 && yr > 0 && xr > yr) {
img.setDotsPerMeterX(img.dotsPerMeterX() * yr / xr);
} else if (xr > 0 && yr > 0 && xr < yr) {
img.setDotsPerMeterY(img.dotsPerMeterY() * xr / yr);
}
}
}
}
@@ -327,7 +339,6 @@ bool IFFHandler::readStandardImage(QImage *image)
// show the first one (I don't have a sample with many images)
auto headers = IFFChunk::searchT<BMHDChunk>(form);
if (headers.isEmpty()) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage(): no supported image found";
return false;
}
@@ -426,7 +437,6 @@ bool IFFHandler::readMayaImage(QImage *image)
// show the first one (I don't have a sample with many images)
auto headers = IFFChunk::searchT<TBHDChunk>(form);
if (headers.isEmpty()) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readMayaImage(): no supported image found";
return false;
}
@@ -464,11 +474,133 @@ bool IFFHandler::readMayaImage(QImage *image)
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.drawImage(tp, ti);
}
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
img.mirror(false, true);
#else
img.flip(Qt::Orientation::Vertical);
#endif
addMetadata(img, form);
*image = img;
return true;
}
bool IFFHandler::readCDIImage(QImage *image)
{
auto forms = d->searchForms<FORMChunk>();
if (forms.isEmpty()) {
return false;
}
auto cin = qBound(0, currentImageNumber(), int(forms.size() - 1));
auto &&form = forms.at(cin);
// show the first one (I don't have a sample with many images)
auto headers = IFFChunk::searchT<IHDRChunk>(form);
if (headers.isEmpty()) {
return false;
}
// create the image
auto &&header = headers.first();
auto img = imageAlloc(header->size(), form->format());
if (img.isNull()) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readCDIImage(): error while allocating the image";
return false;
}
// set the palette
if (img.format() == QImage::Format_Indexed8) {
auto pltes = IFFChunk::searchT<PLTEChunk>(form);
if (!pltes.isEmpty()) {
img.setColorTable(pltes.first()->palette());
}
}
// decoding the image
auto bodies = IFFChunk::searchT<IDATChunk>(form);
if (bodies.isEmpty()) {
img.fill(0);
} else {
auto &&body = bodies.first();
if (!body->resetStrideRead(device())) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readCDIImage(): error while reading image data";
return false;
}
auto pars = IFFChunk::searchT<IPARChunk>(form);
auto yuvs = IFFChunk::searchT<YUVSChunk>(form);
for (auto y = 0, h = img.height(); y < h; ++y) {
auto line = reinterpret_cast<char*>(img.scanLine(y));
auto ba = body->strideRead(device(), y, header,
pars.isEmpty() ? nullptr : pars.first(),
yuvs.isEmpty() ? nullptr : yuvs.first());
if (ba.isEmpty()) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readCDIImage(): error while reading image scanline";
return false;
}
memcpy(line, ba.constData(), std::min(img.bytesPerLine(), ba.size()));
}
}
// set metadata (including image resolution)
addMetadata(img, form);
*image = img;
return true;
}
bool IFFHandler::readRGFXImage(QImage *image)
{
auto forms = d->searchForms<FORMChunk>();
if (forms.isEmpty()) {
return false;
}
auto cin = qBound(0, currentImageNumber(), int(forms.size() - 1));
auto &&form = forms.at(cin);
// show the first one (I don't have a sample with many images)
auto headers = IFFChunk::searchT<RGHDChunk>(form);
if (headers.isEmpty()) {
return false;
}
// create the image
auto &&header = headers.first();
auto img = imageAlloc(header->size(), form->format());
if (img.isNull()) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readRGFXImage(): error while allocating the image";
return false;
}
// set the palette
if (img.format() == QImage::Format_Indexed8) {
auto pltes = IFFChunk::searchT<RCOLChunk>(form);
if (!pltes.isEmpty()) {
img.setColorTable(pltes.first()->palette());
}
}
// decoding the image
auto bodies = IFFChunk::searchT<RBODChunk>(form);
if (bodies.isEmpty()) {
img.fill(0);
} else {
auto &&body = bodies.first();
if (!body->resetStrideRead(device())) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readRGFXImage(): error while reading image data";
return false;
}
auto rcsms = IFFChunk::searchT<RSCMChunk>(form);
auto rcols = IFFChunk::searchT<RCOLChunk>(form);
for (auto y = 0, h = img.height(); y < h; ++y) {
auto line = reinterpret_cast<char*>(img.scanLine(y));
auto ba = body->strideRead(device(), y, header,
rcsms.isEmpty() ? nullptr : rcsms.first(),
rcols.isEmpty() ? nullptr : rcols.first());
if (ba.isEmpty()) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readRGFXImage(): error while reading image scanline";
return false;
}
memcpy(line, ba.constData(), std::min(img.bytesPerLine(), ba.size()));
}
}
// set metadata (including image resolution)
addMetadata(img, form);
*image = img;
@@ -490,6 +622,14 @@ bool IFFHandler::read(QImage *image)
return true;
}
if (readCDIImage(image)) {
return true;
}
if (readRGFXImage(image)) {
return true;
}
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::read(): no supported image found";
return false;
}

View File

@@ -35,6 +35,10 @@ private:
bool readMayaImage(QImage *image);
bool readCDIImage(QImage *image);
bool readRGFXImage(QImage *image);
private:
const QScopedPointer<IFFHandlerPrivate> d;
};

View File

@@ -73,7 +73,7 @@ static void info_callback(const char *msg, void *client_data)
static OPJ_SIZE_T jp2_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data)
{
auto dev = (QIODevice*)p_user_data;
if (dev == nullptr) {
if (dev == nullptr || dev->atEnd()) {
return OPJ_SIZE_T(-1);
}
return OPJ_SIZE_T(dev->read((char*)p_buffer, (qint64)p_nb_bytes));
@@ -253,20 +253,27 @@ public:
bool jp2ToImage(QImage *img) const
{
Q_ASSERT(img->depth() == 8 * sizeof(T) || img->depth() == 32 * sizeof(T));
for (qint32 c = 0, cc = m_jp2_image->numcomps; c < cc; ++c) {
auto cs = cc == 1 ? 1 : 4;
if (img->width() < 1 || img->height() < 1) {
return false;
}
auto maxChannels = qint32(img->bytesPerLine() / sizeof(T) / img->width());
for (qint32 c = 0, cc = std::min(qint32(m_jp2_image->numcomps), maxChannels); c < cc; ++c) {
auto cs = std::min(cc == 1 ? 1 : 4, maxChannels);
auto &&jc = m_jp2_image->comps[c];
if (jc.data == nullptr)
if (jc.data == nullptr) {
return false;
if (qint32(jc.w) != img->width() || qint32(jc.h) != img->height())
}
if (qint32(jc.w) != img->width() || qint32(jc.h) != img->height()) {
return false;
}
// discriminate between int and float (avoid complicating things by creating classes with template specializations)
if (std::numeric_limits<T>::is_integer) {
auto divisor = 1;
if (jc.prec > sizeof(T) * 8) {
auto divisor = 1ull;
auto prec = std::min(size_t(jc.prec), sizeof(*jc.data) * 8);
if (prec > sizeof(T) * 8 && prec < 64) {
// convert to the wanted precision (e.g. 16-bit -> 8-bit: divisor = 65535 / 255 = 257)
divisor = std::max(1, int(((1ll << jc.prec) - 1) / ((1ll << (sizeof(T) * 8)) - 1)));
divisor = std::max(1ull, (((1ull << prec) - 1) / ((1ull << (sizeof(T) * 8)) - 1)));
}
for (qint32 y = 0, h = img->height(); y < h; ++y) {
auto ptr = reinterpret_cast<T *>(img->scanLine(y));
@@ -352,11 +359,15 @@ public:
}
// OpenJPEG uses a shadow copy @32-bit/channel so we need to do a check
auto maxBytes = qint64(QImageReader::allocationLimit()) * 1024 * 1024;
auto neededBytes = qint64(width) * height * nchannels * 4;
if (maxBytes > 0 && neededBytes > maxBytes) {
qCCritical(LOG_JP2PLUGIN) << "Allocation limit set to" << (maxBytes / 1024 / 1024) << "MiB but" << (neededBytes / 1024 / 1024) << "MiB are needed!";
return false;
const int allocationLimit = QImageReader::allocationLimit();
if (allocationLimit > 0) {
auto maxBytes = qint64(allocationLimit) * 1024 * 1024;
auto neededBytes = qint64(width) * height * nchannels * 4;
if (maxBytes > 0 && neededBytes > maxBytes) {
qCCritical(LOG_JP2PLUGIN) << "Allocation limit set to" << (maxBytes / 1024 / 1024) << "MiB but" << (neededBytes / 1024 / 1024)
<< "MiB are needed!";
return false;
}
}
return true;
@@ -391,7 +402,6 @@ public:
prec = 0;
}
auto jp2cs = m_jp2_image->color_space;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
if (jp2cs == OPJ_CLRSPC_UNKNOWN || jp2cs == OPJ_CLRSPC_UNSPECIFIED) {
auto cs = colorSpace();
if (cs.colorModel() == QColorSpace::ColorModel::Cmyk)
@@ -401,7 +411,6 @@ public:
else if (cs.colorModel() == QColorSpace::ColorModel::Gray)
jp2cs = OPJ_CLRSPC_GRAY;
}
#endif
if (jp2cs == OPJ_CLRSPC_UNKNOWN || jp2cs == OPJ_CLRSPC_UNSPECIFIED) {
if (m_jp2_image->numcomps == 1)
jp2cs = OPJ_CLRSPC_GRAY;
@@ -431,10 +440,8 @@ public:
}
} else if (jp2cs == OPJ_CLRSPC_CMYK) {
if (m_jp2_image->numcomps == 4) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
if (prec == 8 || prec == 16)
fmt = QImage::Format_CMYK8888;
#endif
}
}
}
@@ -579,7 +586,6 @@ public:
prec = 16;
convFormat = QImage::Format_RGBA64;
break;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
case QImage::Format_CMYK8888: // requires OpenJPEG 2.5.3+
if (opjVersion() >= QT_VERSION_CHECK(2, 5, 3)) {
ncomp = 4;
@@ -588,7 +594,6 @@ public:
} else {
Q_FALLTHROUGH();
}
#endif
default:
if (image.depth() > 32) {
qCWarning(LOG_JP2PLUGIN) << "The image is saved losing precision!";

View File

@@ -26,13 +26,9 @@ Q_LOGGING_CATEGORY(LOG_JXLPLUGIN, "kf.imageformats.plugins.jxl", QtDebugMsg)
Q_LOGGING_CATEGORY(LOG_JXLPLUGIN, "kf.imageformats.plugins.jxl", QtWarningMsg)
#endif
// Avoid rotation on buggy Qts (see also https://bugreports.qt.io/browse/QTBUG-126575)
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 3)
#ifndef JXL_QT_AUTOTRANSFORM
#define JXL_QT_AUTOTRANSFORM
#endif
#endif
#ifndef JXL_HDR_PRESERVATION_DISABLED
// Define JXL_HDR_PRESERVATION_DISABLED to disable HDR preservation
@@ -184,7 +180,7 @@ bool QJpegXLHandler::ensureDecoder()
int num_worker_threads = QThread::idealThreadCount();
if (!m_runner && num_worker_threads >= 4) {
/* use half of the threads because plug-in is usually used in environment
* where application performs another tasks in backround (pre-load other images) */
* where application performs other tasks in the background (pre-load other images) */
num_worker_threads = num_worker_threads / 2;
num_worker_threads = qBound(2, num_worker_threads, 64);
m_runner = JxlThreadParallelRunnerCreate(nullptr, num_worker_threads);
@@ -262,14 +258,16 @@ bool QJpegXLHandler::countALLFrames()
bool is_gray = m_basicinfo.num_color_channels == 1 && m_basicinfo.alpha_bits == 0;
JxlColorEncoding color_encoding;
if (m_basicinfo.uses_original_profile == JXL_FALSE && m_basicinfo.have_animation == JXL_FALSE) {
const JxlCmsInterface *jxlcms = JxlGetDefaultCms();
if (jxlcms) {
status = JxlDecoderSetCms(m_decoder, *jxlcms);
if (status != JXL_DEC_SUCCESS) {
qCWarning(LOG_JXLPLUGIN, "JxlDecoderSetCms ERROR");
if (!is_gray) {
const JxlCmsInterface *jxlcms = JxlGetDefaultCms();
if (jxlcms) {
status = JxlDecoderSetCms(m_decoder, *jxlcms);
if (status != JXL_DEC_SUCCESS) {
qCWarning(LOG_JXLPLUGIN, "JxlDecoderSetCms ERROR");
}
} else {
qCWarning(LOG_JXLPLUGIN, "No JPEG XL CMS Interface");
}
} else {
qCWarning(LOG_JXLPLUGIN, "No JPEG XL CMS Interface");
}
JxlColorEncodingSetToSRGB(&color_encoding, is_gray ? JXL_TRUE : JXL_FALSE);
@@ -414,7 +412,6 @@ bool QJpegXLHandler::countALLFrames()
m_framedelays[0] = 0;
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
// CMYK detection
if ((m_basicinfo.uses_original_profile == JXL_TRUE) && (m_basicinfo.num_color_channels == 3) && (m_colorspace.isValid())) {
bool alpha_found = false;
@@ -471,7 +468,6 @@ bool QJpegXLHandler::countALLFrames()
qCWarning(LOG_JXLPLUGIN, "JXL has CMYK colorspace but BLACK channel was not found!");
}
}
#endif
#ifndef JXL_DECODE_BOXES_DISABLED
if (!decodeContainer()) {
@@ -498,7 +494,6 @@ bool QJpegXLHandler::decode_one_frame()
}
if (m_isCMYK) { // CMYK decoding
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
uchar *pixels_cmy = nullptr;
uchar *pixels_black = nullptr;
@@ -739,11 +734,6 @@ bool QJpegXLHandler::decode_one_frame()
free(pixels_cmy);
pixels_cmy = nullptr;
}
#else
// CMYK not supported in older Qt
m_parseState = ParseJpegXLError;
return false;
#endif
} else { // RGB or GRAY
m_current_image = imageAlloc(m_basicinfo.xsize, m_basicinfo.ysize, m_input_image_format);
if (m_current_image.isNull()) {
@@ -865,7 +855,11 @@ bool QJpegXLHandler::write(const QImage &image)
size_t pixel_count = size_t(image.width()) * image.height();
if (MAX_IMAGE_PIXELS && pixel_count > MAX_IMAGE_PIXELS) {
qCWarning(LOG_JXLPLUGIN, "Image (%dx%d) will not be saved because it has more than %d megapixels!", image.width(), image.height(), MAX_IMAGE_PIXELS / 1024 / 1024);
qCWarning(LOG_JXLPLUGIN,
"Image (%dx%d) will not be saved because it has more than %d megapixels!",
image.width(),
image.height(),
MAX_IMAGE_PIXELS / 1024 / 1024);
return false;
}
@@ -921,11 +915,9 @@ bool QJpegXLHandler::write(const QImage &image)
}
bool save_cmyk = false;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
if (image.format() == QImage::Format_CMYK8888 && image.colorSpace().isValid() && image.colorSpace().colorModel() == QColorSpace::ColorModel::Cmyk) {
save_cmyk = true;
}
#endif
JxlEncoderStatus status;
JxlPixelFormat pixel_format;
@@ -936,7 +928,6 @@ bool QJpegXLHandler::write(const QImage &image)
auto xmp_data = image.text(QStringLiteral(META_KEY_XMP_ADOBE)).toUtf8();
if (save_cmyk) { // CMYK is always lossless
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
output_info.uses_original_profile = JXL_TRUE;
output_info.xsize = image.width();
output_info.ysize = image.height();
@@ -1109,13 +1100,6 @@ bool QJpegXLHandler::write(const QImage &image)
JxlEncoderDestroy(encoder);
return false;
}
#else
if (runner) {
JxlThreadParallelRunnerDestroy(runner);
}
JxlEncoderDestroy(encoder);
return false;
#endif
} else { // RGB or GRAY saving
int save_depth = 8; // 8 / 16 / 32
bool save_fp = false;
@@ -1154,9 +1138,7 @@ bool QJpegXLHandler::write(const QImage &image)
case QImage::Format_RGBX8888:
case QImage::Format_RGBA8888:
case QImage::Format_RGBA8888_Premultiplied:
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
case QImage::Format_CMYK8888:
#endif
save_depth = 8;
break;
case QImage::Format_Grayscale16:
@@ -1251,7 +1233,6 @@ bool QJpegXLHandler::write(const QImage &image)
}
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
QImage tmpimage;
if (image.colorSpace().isValid()) {
if (is_gray && image.colorSpace().colorModel() != QColorSpace::ColorModel::Gray) {
@@ -1304,9 +1285,6 @@ bool QJpegXLHandler::write(const QImage &image)
} else { // no ColorSpace or invalid
tmpimage = image.convertToFormat(tmpformat);
}
#else
QImage tmpimage = image.convertToFormat(tmpformat);
#endif
output_info.xsize = tmpimage.width();
output_info.ysize = tmpimage.height();
@@ -1332,10 +1310,7 @@ bool QJpegXLHandler::write(const QImage &image)
output_info.uses_original_profile = JXL_FALSE;
if (tmpimage.colorSpace().isValid()) {
QPointF whiteP(0.3127f, 0.329f);
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
whiteP = image.colorSpace().whitePoint();
#endif
const QPointF whiteP = image.colorSpace().whitePoint();
switch (tmpimage.colorSpace().primaries()) {
case QColorSpace::Primaries::SRgb:
@@ -1364,9 +1339,6 @@ bool QJpegXLHandler::write(const QImage &image)
break;
case QColorSpace::Primaries::ProPhotoRgb:
color_profile.white_point = JXL_WHITE_POINT_CUSTOM;
#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0)
whiteP = QPointF(0.3457f, 0.3585f);
#endif
color_profile.white_point_xy[0] = whiteP.x();
color_profile.white_point_xy[1] = whiteP.y();
color_profile.primaries = JXL_PRIMARIES_CUSTOM;
@@ -1377,7 +1349,6 @@ bool QJpegXLHandler::write(const QImage &image)
color_profile.primaries_blue_xy[0] = 0.0366;
color_profile.primaries_blue_xy[1] = 0.0001;
break;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
case QColorSpace::Primaries::Bt2020:
color_profile.white_point = JXL_WHITE_POINT_D65;
color_profile.primaries = JXL_PRIMARIES_2100;
@@ -1388,7 +1359,6 @@ bool QJpegXLHandler::write(const QImage &image)
color_profile.primaries_blue_xy[0] = 0.131;
color_profile.primaries_blue_xy[1] = 0.046;
break;
#endif
default:
if (is_gray && !whiteP.isNull()) {
color_profile.white_point = JXL_WHITE_POINT_CUSTOM;
@@ -1874,17 +1844,20 @@ bool QJpegXLHandler::rewind()
return false;
}
const JxlCmsInterface *jxlcms = JxlGetDefaultCms();
if (jxlcms) {
status = JxlDecoderSetCms(m_decoder, *jxlcms);
if (status != JXL_DEC_SUCCESS) {
qCWarning(LOG_JXLPLUGIN, "JxlDecoderSetCms ERROR");
bool is_gray = m_basicinfo.num_color_channels == 1 && m_basicinfo.alpha_bits == 0;
if (!is_gray) {
const JxlCmsInterface *jxlcms = JxlGetDefaultCms();
if (jxlcms) {
status = JxlDecoderSetCms(m_decoder, *jxlcms);
if (status != JXL_DEC_SUCCESS) {
qCWarning(LOG_JXLPLUGIN, "JxlDecoderSetCms ERROR");
}
} else {
qCWarning(LOG_JXLPLUGIN, "No JPEG XL CMS Interface");
}
} else {
qCWarning(LOG_JXLPLUGIN, "No JPEG XL CMS Interface");
}
bool is_gray = m_basicinfo.num_color_channels == 1 && m_basicinfo.alpha_bits == 0;
JxlColorEncoding color_encoding;
JxlColorEncodingSetToSRGB(&color_encoding, is_gray ? JXL_TRUE : JXL_FALSE);
JxlDecoderSetPreferredColorProfile(m_decoder, &color_encoding);
@@ -2035,6 +2008,11 @@ bool QJpegXLHandler::extractBox(QByteArray &output, size_t container_size)
return false;
}
if (rawboxsize > 8388608) { // 8MB limit
qCWarning(LOG_JXLPLUGIN, "Skipped decoding of big JXL metadata box");
return true;
}
output.resize(rawboxsize);
status = JxlDecoderSetBoxBuffer(m_decoder, reinterpret_cast<uint8_t *>(output.data()), output.size());
if (status != JXL_DEC_SUCCESS) {
@@ -2048,7 +2026,7 @@ bool QJpegXLHandler::extractBox(QByteArray &output, size_t container_size)
if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) {
size_t bytes_remains = JxlDecoderReleaseBoxBuffer(m_decoder);
if (output.size() > 4194304) { // approx. 4MB limit for decompressed metadata box
if (output.size() > 33554432) { // approx. 32MB (4*8) limit for decompressed metadata box
qCWarning(LOG_JXLPLUGIN, "JXL metadata box is too large");
m_parseState = ParseJpegXLError;
return false;

View File

@@ -45,7 +45,7 @@ Q_LOGGING_CATEGORY(LOG_JXRPLUGIN, "kf.imageformats.plugins.jxr", QtWarningMsg)
// #define JXR_DENY_FLOAT_IMAGE
/*!
* Remove the neeeds of additional memory by disabling the conversion between
* Remove the needs of additional memory by disabling the conversion between
* different color depths (e.g. RGBA64bpp to RGBA32bpp).
*
* NOTE: Leaving deptch conversion enabled (default) ensures maximum read compatibility.
@@ -579,9 +579,7 @@ public:
// IMPORTANT: these values must be in exactMatchingFormat()
// clang-format off
auto valid = QSet<QImage::Format>()
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
<< QImage::Format_CMYK8888
#endif
#ifndef JXR_DENY_FLOAT_IMAGE
<< QImage::Format_RGBA16FPx4
<< QImage::Format_RGBX16FPx4
@@ -671,10 +669,8 @@ public:
wmiSCP->bVerbose = FALSE;
if (fmt == QImage::Format_Grayscale8 || fmt == QImage::Format_Grayscale16 || fmt == QImage::Format_Mono)
wmiSCP->cfColorFormat = Y_ONLY;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
else if (fmt == QImage::Format_CMYK8888)
wmiSCP->cfColorFormat = CMYK;
#endif
else
wmiSCP->cfColorFormat = YUV_444;
wmiSCP->bdBitDepth = BD_LONG;
@@ -812,10 +808,8 @@ private:
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_RGBA32FPx4_Premultiplied, GUID_PKPixelFormat128bppPRGBAFloat)
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_RGBX32FPx4, GUID_PKPixelFormat128bppRGBFloat)
#endif // JXR_DENY_FLOAT_IMAGE
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_CMYK8888, GUID_PKPixelFormat32bppCMYK)
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_CMYK8888, GUID_PKPixelFormat32bppCMYKDIRECT)
#endif
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_Mono, GUID_PKPixelFormatBlackWhite)
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_Grayscale8, GUID_PKPixelFormat8bppGray)
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_Grayscale16, GUID_PKPixelFormat16bppGray)

View File

@@ -256,7 +256,7 @@ static bool checkHeader(QDataStream &ds)
/*!
* \brief updatePos
* Write the current stram position in \a pos position as uint32.
* Write the current stream position in \a pos position as uint32.
* \return True on success, otherwise false;
*/
static bool updatePos(QDataStream &ds, quint32 pos)
@@ -510,7 +510,7 @@ static QByteArray readBytes(QDataStream &ds, quint32 count, bool asciiz)
* \param pos The position of the IFD.
* \param knownTags List of known and supported tags.
* \param nextIfd The position of next IFD (0 if none).
* \return True on succes, otherwise false.
* \return True on success, otherwise false.
*/
static bool readIfd(QDataStream &ds, MicroExif::Tags &tags, quint32 pos = 0, const KnownTags &knownTags = staticTagTypes, quint32 *nextIfd = nullptr)
{

Some files were not shown because too many files have changed in this diff Show More