Compare commits

..

1 Commits

Author SHA1 Message Date
Kai Uwe Broulik
f5494d9acf iff: Support ILBM (PBM)
It's another variant of IFF where the pixel data is stored
contiguously rather than interleaved.
2025-07-06 16:24:55 +02:00
86 changed files with 717 additions and 4209 deletions

View File

@@ -15,7 +15,7 @@ include:
image_json_validate:
stage: validate
image: invent-registry.kde.org/sysadmin/ci-images/suse-qt69:latest
image: invent-registry.kde.org/sysadmin/ci-images/suse-qt68:latest
tags:
- Linux
script:

View File

@@ -1,11 +1,11 @@
cmake_minimum_required(VERSION 3.16)
set(KF_VERSION "6.18.0") # handled by release scripts
set(KF_DEP_VERSION "6.18.0") # handled by release scripts
set(KF_VERSION "6.17.0") # handled by release scripts
set(KF_DEP_VERSION "6.16.0") # handled by release scripts
project(KImageFormats VERSION ${KF_VERSION})
include(FeatureSummary)
find_package(ECM 6.18.0 NO_MODULE)
find_package(ECM 6.16.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)
@@ -99,8 +99,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.17.0
QT 6.9.0
KF 6.13.0
)
add_subdirectory(src)

View File

@@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -17,7 +17,7 @@ The following image formats have read-only support:
- Animated Windows cursors (ani)
- Camera RAW images (arw, cr2, cr3, dcs, dng, ...)
- Gimp (xcf)
- Interchange Format Files (iff, ilbm, lbm)
- Interchange Format Files (IFF)
- Krita (kra)
- OpenRaster (ora)
- Pixar raster (pxr)
@@ -56,17 +56,11 @@ submit the plugin directly to the Qt Project.
To be accepted, contributions must:
- Contain the test images needed to verify that the changes work correctly.
- Pass the tests successfully.
- Use Qt logging categories for Debug messages.
For more info about tests, see also [Autotests README](autotests/README.md).
## Duplicated Plugins
> [!important]
> To ensure you are using the correct plugin, the unwanted one should be
renamed or deleted. If several plugins support the same capability, Qt will
select one arbitrarily.
### The TGA plugin
The TGA plugin supports more formats than Qt's own TGA plugin;
@@ -218,35 +212,30 @@ RGB.
Where possible, plugins support large images. By convention, many of the
large image plugins are limited to a maximum of 300,000 x 300,000 pixels.
Anyway, all plugins are also limited by the
`QImageIOReader::allocationLimit()`.
Anyway, all plugins are also limited by the
`QImageIOReader::allocationLimit()`. Below are the maximum sizes for each
plugin ('n/a' means no limit, i.e. the limit depends on the format encoding).
> [!note]
> You can change the maximum limit of 300000 pixels by setting the constant
> `KIF_LARGE_IMAGE_PIXEL_LIMIT` to the desired value in the cmake file.
Below are the maximum sizes for each plugin ('n/a' means no limit, i.e. the
limit depends on the format encoding).
- ANI: n/a
- AVIF: 32,768 x 32,768 pixels, in any case no larger than 256 megapixels
- DDS: n/a
- EXR: 300,000 x 300,000 pixels
- EPS: same size as Qt's JPG plugin
- HDR: 300,000 x 300,000 pixels
- EPS: n/a
- HDR: n/a (large image)
- HEIF: n/a
- IFF: 65,535 x 65,535 pixels
- JP2: 300,000 x 300,000 pixels, in any case no larger than 2 gigapixels
- JXL: 262,144 x 262,144 pixels, in any case no larger than 256 megapixels
- JXR: 300,000 x 300,000 pixels, in any case no larger than 4 GB
- JXR: n/a, in any case no larger than 4 GB
- KRA: same size as Qt's PNG plugin
- ORA: same size as Qt's PNG plugin
- PCX: 65,535 x 65,535 pixels
- PFM: 300,000 x 300,000 pixels
- PFM: n/a (large image)
- PIC: 65,535 x 65,535 pixels
- PSD: 300,000 x 300,000 pixels
- PXR: 65,535 x 65,535 pixels
- QOI: 300,000 x 300,000 pixels
- RAS: 300,000 x 300,000 pixels
- RAS: n/a (large image)
- RAW: n/a (depends on the RAW format loaded)
- RGB: 65,535 x 65,535 pixels
- SCT: 300,000 x 300,000 pixels
@@ -328,29 +317,6 @@ plugin:
- `HDR_HALF_QUALITY`: on read, a 16-bit float image is returned instead of a
32-bit float one.
### The IFF plugin
Interchange File Format is a chunk-based format. Since the original 1985
version, various extensions have been created over time.
The plugin supports the following image data:
- FORM ILBM (Interleaved Bitmap): Electronic Arts IFF standard for
Interchange File Format (EA IFF 1985). ILBM is a format to handle raster
images, specifically an InterLeaved bitplane BitMap image with color map.
It supports from 1 to 8-bit indexed images with HAM, Halfbride, and normal
encoding. It also supports interleaved 24-bit RGB and 32-bit RGBA
extension without color map.
- FORM ILBM 64: ILBM extension to support 48-bit RGB and 64-bit RGBA encoding.
- FORM ACBM (Amiga Contiguous BitMap): It supports uncompressed ACBMs by
converting them to ILBMs at runtime.
- FORM RGBN / RGB8: It supports 13-bit and 25-bit RGB images with compression
type 4.
- FORM PBM: PBM is a chunky version of IFF pictures. It supports 8-bit images
with color map only.
- FOR4 CIMG (Maya Image File Format): It supports 24/48-bit RGB and 32/64-bit
RGBA images.
### The JP2 plugin
**This plugin can be disabled by setting `KIMAGEFORMATS_JP2` to `OFF`
@@ -433,25 +399,6 @@ selectively change the conversion (see also [raw_p.h](./src/imageformats/raw_p.h
The default setting tries to balance quality and conversion speed.
### The TGA plugin
TGA plugin supports both version 1 and version 2 of TGA files. When writing,
it is possible to force which version to use by setting the following subtypes:
- `TGAv1`: force TGA v1.0. No metadata.
- `TGAv2` (default): force TGA v2.0 (strict). Adds the TGA Extension Area.
- `TGAv2E`: force TGA v2.0 (enhanced). Same as TGA v2.0 (strict) but with the
addition of the TGA v2.0 Developer Area with info like, for e.g., Exif data,
XMP packet and the ICC profile.
They are all TGA specs compliant. While for versions 1 and 2 (strict) it is
possible to decode all the information with the TGA specification alone, for
version 2 (enhanced) it is necessary to know how the additional data is
encoded.
The following defines can be defined in cmake to modify the behavior of the
plugin:
- `TGA_V2E_AS_DEFAULT`: change the default version of the plugin to `TGAv2E`.
### The XCF plugin
XCF support has the following limitations:

View File

@@ -189,7 +189,7 @@ kimageformats_write_tests(
pic-lossless
qoi-lossless
rgb-lossless
tga-nodatacheck
tga # fixme: the alpha images appear not to be written properly
)
# EPS read tests depend on the vagaries of GhostScript

View File

@@ -88,33 +88,30 @@ are iterated sequentially and the first object that matches the requirements
is used for testing (successes are ignored).
Supported values for JSON objects:
- `comment`: Type `string`. A string shown by the test when a condition occurs.
- `description`: Type `string`. A description of the object. Not used by the
- `comment`: Type string. A string shown by the test when a condition occurs.
- `description`: Type string. A description of the object. Not used by the
test.
- `disableAutoTransform`: Type `boolean`. By default, tests are run with
- `disableAutoTransform`: Type boolean. By default, tests are run with
autotransform enabled (i.e. rotation is applied if the plugin supports it).
Set to `true` to disable autotransform.
- `filename`: Type `string`. Name of the template file to use. E.g.
- `filename`: Type string. Name of the template file to use. E.g.
"testRGB_Qt_6_2.png".
- `fuzziness`: Type `integer`. Set the fuzziness only if not already set on the
- `fuzziness`: Type integer. Set the fuzziness only if not already set on the
command line. The value set on the command line wins over the one in the JSON
file.
- `maxQtVersion`: Type `string`. Maximum Qt version this object is compatible
- `maxQtVersion`: Type string. Maximum Qt version this object is compatible
with (if not set means all). E.g. "6.2.99".
- `metadata`: Type `array`. An array of key/value objects (string type)
- `metadata`: Type Array. An array of key/value objects (string type)
containing the image metadata as returned by `QImage::text`.
- `minQtVersion`: Type `string`. Minimum Qt version this object is compatible
- `minQtVersion`: Type string. Minimum Qt version this object is compatible
with (if not set means all). E.g. "6.2.0".
- `perceptiveFuzziness` Type `boolean`. Set the perceptive fuzziness only if not
- `perceptiveFuzziness` Type boolean. Set the perceptive fuzziness only if not
already set on the command line. The value set on the command line wins over
the one in the JSON file.
- `resolution`: Type `object`. An object with the `dotsPerMeterX` and
- `resolution`: Type object. An object with the `dotsPerMeterX` and
`dotsPerMeterY` (integer) values of the image.
- `seeAlso`: Type `string`. More info about the object. Normally used to point
- `seeAlso`: Type string. More info about the object. Normally used to point
to bug reports. Not used by the test.
- `skipSequential`: Type `boolean`. Skip the test on sequential access device.
Some plugins may have limited functionality on sequential devices (e.g.,
not reading metadata).
- `unsupportedFormat`: Type `boolean`. When true, the test is skipped.
Some examples:
@@ -172,12 +169,11 @@ See also [Add a test to CMakeLists.txt](#add-a-test-to-cmakeliststxt).
The properties file must be located in `write/basic` and must have the name
of the file format (e.g. jxl.json). It is a JSON object composed of the
following values:
- `format`: Type `string`. The format tested.
- `metadata`: Type `array`. An array of key/value objects (string type)
- `format`: Type string. The format tested.
- `metadata`: Type Array. An array of key/value objects (string type)
containing the image metadata as returned by `QImage::text`.
- `resolution`: Type `object`. An object with the `dotsPerMeterX` and `
- `resolution`: Type object. An object with the `dotsPerMeterX` and `
dotsPerMeterY` (integer) values of the image.
- `subType`: type `string`. The image writer subtype to set when testing.
[This is an example](write/basic/jxl.json) of property file.
@@ -234,7 +230,9 @@ kimageformats_write_tests(FUZZ 1
Plugins are also tested with [OSS-Fuzz](https://google.github.io/oss-fuzz/)
project to identify possible security issues. When adding a new plugin it is
also necessary to add it to the test in the [ossfuzz](ossfuzz) directory.
also necessary to add it to the test in the [kimageformats
project](https://github.com/google/oss-fuzz/tree/master/projects/kimageformats)
on OSS-Fuzz.
## TODO

View File

@@ -1,194 +0,0 @@
#!/bin/bash -eu
#
# SPDX-FileCopyrightText: 2020 Google LLC
# SPDX-License-Identifier: Apache-2.0
#
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################
# build zstd
cd $SRC/zstd
cmake -S build/cmake -DBUILD_SHARED_LIBS=OFF
make install -j$(nproc)
# Build zlib
cd $SRC/zlib
./configure --static
make install -j$(nproc)
# Build bzip2
# Inspired from ../bzip2/build
cd $SRC
tar xzf bzip2-*.tar.gz && rm -f bzip2-*.tar.gz
cd bzip2-*
SRCL=(blocksort.o huffman.o crctable.o randtable.o compress.o decompress.o bzlib.o)
for source in ${SRCL[@]}; do
name=$(basename $source .o)
$CC $CFLAGS -c ${name}.c
done
rm -f libbz2.a
ar cq libbz2.a ${SRCL[@]}
cp -f bzlib.h /usr/local/include
cp -f libbz2.a /usr/local/lib
# Build xz
export ORIG_CFLAGS="${CFLAGS}"
export ORIG_CXXFLAGS="${CXXFLAGS}"
unset CFLAGS
unset CXXFLAGS
cd $SRC/xz
./autogen.sh --no-po4a --no-doxygen
./configure --enable-static --disable-debug --disable-shared --disable-xz --disable-xzdec --disable-lzmainfo
make install -j$(nproc)
export CFLAGS="${ORIG_CFLAGS}"
export CXXFLAGS="${ORIG_CXXFLAGS}"
# Build qt
cd $SRC/qtbase
./configure -no-glib -qt-libpng -qt-pcre -opensource -confirm-license -static -no-opengl -no-icu -platform linux-clang-libc++ -debug -prefix /usr -no-feature-widgets -no-feature-sql -no-feature-network -no-feature-xml -no-feature-dbus -no-feature-printsupport
cmake --build . --parallel $(nproc)
cmake --install .
# Build extra-cmake-modules
cd $SRC/extra-cmake-modules
cmake . -DBUILD_TESTING=OFF
make install -j$(nproc)
cd $SRC/karchive
rm -rf poqm
cmake . -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=OFF -DCMAKE_INSTALL_PREFIX=/usr/local
make install -j$(nproc)
# Build JXRlib
cd $SRC/jxrlib
make -j$(nproc)
# Build LibRaw
cd $SRC/LibRaw
TMP_CFLAGS=$CFLAGS
TMP_CXXFLAGS=$CXXFLAGS
CFLAGS="$CFLAGS -fno-sanitize=function,vptr"
CXXFLAGS="$CXXFLAGS -fno-sanitize=function,vptr"
autoreconf --install
./configure --disable-examples
make -j$(nproc)
make install -j$(nproc)
CFLAGS=$TMP_CFLAGS
CXXFLAGS=$TMP_CXXFLAGS
# Build aom
cd $SRC/aom
mkdir build.libavif
cd build.libavif
extra_libaom_flags='-DAOM_MAX_ALLOCABLE_MEMORY=536870912 -DDO_RANGE_CHECK_CLAMP=1'
cmake -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}" ..
make -j$(nproc)
make install -j$(nproc)
# Build libavif
cd $SRC/libavif
ln -s "$SRC/aom" "$SRC/libavif/ext/"
mkdir build
cd build
CFLAGS="$CFLAGS -fPIC" cmake -DBUILD_SHARED_LIBS=OFF -DAVIF_ENABLE_WERROR=OFF -DAVIF_CODEC_AOM=LOCAL -DAVIF_LIBYUV=OFF ..
make -j$(nproc)
# Build libde265
cd $SRC/libde265
cmake -DBUILD_SHARED_LIBS=OFF -DDISABLE_SSE=ON .
make -j$(nproc)
make install -j$(nproc)
# Build openjpeg
cd $SRC/openjpeg
mkdir build
cd build
cmake -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON -DBUILD_CODEC=OFF ..
make -j$(nproc)
make install -j$(nproc)
# build openh264
cd $SRC/openh264
make USE_ASM=No BUILDTYPE=Debug install-static -j$(nproc)
# Build openexr
cd $SRC/openexr
mkdir _build
cd _build
cmake -DBUILD_SHARED_LIBS=OFF ..
make -j$(nproc)
make install -j$(nproc)
# Build libheif
cd $SRC/libheif
#Reduce max width and height to avoid allocating too much memory
sed -i "s/static const int MAX_IMAGE_WIDTH = 32768;/static const int MAX_IMAGE_WIDTH = 8192;/g" libheif/security_limits.h
sed -i "s/static const int MAX_IMAGE_HEIGHT = 32768;/static const int MAX_IMAGE_HEIGHT = 8192;/g" libheif/security_limits.h
mkdir build
cd build
cmake -DBUILD_SHARED_LIBS=OFF -DENABLE_PLUGIN_LOADING=OFF -DWITH_DAV1D=OFF -DWITH_EXAMPLES=OFF -DWITH_LIBDE265=ON -DWITH_RAV1E=OFF -DWITH_RAV1E_PLUGIN=OFF -DWITH_SvtEnc=OFF -DWITH_SvtEnc_PLUGIN=OFF -DWITH_X265=OFF -DWITH_OpenJPEG_DECODER=ON -DWITH_OpenH264_DECODER=ON ..
make -j$(nproc)
make install -j$(nproc)
# Build libjxl
cd $SRC/libjxl
mkdir build
cd build
CXXFLAGS="$CXXFLAGS -DHWY_COMPILE_ONLY_SCALAR" cmake -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=OFF -DJPEGXL_ENABLE_BENCHMARK=OFF -DJPEGXL_ENABLE_DOXYGEN=OFF -DJPEGXL_ENABLE_EXAMPLES=OFF -DJPEGXL_ENABLE_JNI=OFF -DJPEGXL_ENABLE_JPEGLI=OFF -DJPEGXL_ENABLE_JPEGLI_LIBJPEG=OFF -DJPEGXL_ENABLE_MANPAGES=OFF -DJPEGXL_ENABLE_OPENEXR=OFF -DJPEGXL_ENABLE_PLUGINS=OFF -DJPEGXL_ENABLE_SJPEG=OFF -DJPEGXL_ENABLE_SKCMS=ON -DJPEGXL_ENABLE_TCMALLOC=OFF -DJPEGXL_ENABLE_TOOLS=OFF -DJPEGXL_ENABLE_FUZZERS=OFF ..
make -j$(nproc) jxl jxl_cms jxl_threads
cd $SRC/kimageformats
HANDLER_TYPES="ANIHandler ani
QAVIFHandler avif
QDDSHandler dds
EXRHandler exr
HDRHandler hdr
HEIFHandler heif
IFFHandler iff
JP2Handler jp2
QJpegXLHandler jxl
JXRHandler jxr
KraHandler kra
OraHandler ora
PCXHandler pcx
PFMHandler pfm
SoftimagePICHandler pic
PSDHandler psd
PXRHandler pxr
QOIHandler qoi
RASHandler ras
RAWHandler raw
RGBHandler rgb
ScitexHandler sct
TGAHandler tga
XCFHandler xcf"
echo "$HANDLER_TYPES" | while read class format; do
(
fuzz_target_name=kimgio_${format}_fuzzer
/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
# -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.
find . -name "*.${format}" | zip -q $OUT/${fuzz_target_name}_seed_corpus.zip -@
)
done

View File

@@ -1,76 +0,0 @@
/*
# SPDX-FileCopyrightText: 2018 Google Inc.
# SPDX-License-Identifier: Apache-2.0
#
# Copyright 2018 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################
*/
/*
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
*/
#include <QBuffer>
#include <QCoreApplication>
#include <QImage>
#include "ani_p.h"
#include "avif_p.h"
#include "dds_p.h"
#include "exr_p.h"
#include "hdr_p.h"
#include "heif_p.h"
#include "iff_p.h"
#include "jp2_p.h"
#include "jxl_p.h"
#include "jxr_p.h"
#include "kra.h"
#include "ora.h"
#include "pcx_p.h"
#include "pfm_p.h"
#include "pic_p.h"
#include "psd_p.h"
#include "pxr_p.h"
#include "qoi_p.h"
#include "ras_p.h"
#include "raw_p.h"
#include "rgb_p.h"
#include "sct_p.h"
#include "tga_p.h"
#include "xcf_p.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
int argc = 0;
QCoreApplication a(argc, nullptr);
QImageIOHandler* handler = new HANDLER();
QImage i;
QBuffer b;
b.setData((const char *)data, size);
b.open(QIODevice::ReadOnly);
handler->setDevice(&b);
handler->canRead();
handler->read(&i);
delete handler;
return 0;
}

View File

@@ -1,44 +0,0 @@
#!/bin/bash -eu
#
# SPDX-FileCopyrightText: 2018 Google Inc.
# SPDX-License-Identifier: Apache-2.0
#
# Based on https://github.com/google/oss-fuzz/blob/33aab4a70dc4b5811143d214536584a8c8cb3924/projects/kimageformats/Dockerfile
# Copyright 2018 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################
apt-get update && \
apt-get install -y cmake make autoconf automake autopoint libtool \
wget po4a ninja-build pkgconf
git clone --depth 1 https://github.com/madler/zlib.git
git clone --depth 1 -b v1.5.7 https://github.com/facebook/zstd.git
wget https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz
git clone https://github.com/tukaani-project/xz.git
git clone --depth 1 --branch=RB-3.3 https://github.com/AcademySoftwareFoundation/openexr.git
git clone --depth 1 -b master https://invent.kde.org/frameworks/extra-cmake-modules.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 -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 https://github.com/strukturag/libde265.git
git clone --depth 1 -b v2.5.3 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 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.

View File

@@ -1,5 +0,0 @@
[
{
"fileName" : "ps_testcard_rgb_maya.png"
}
]

Binary file not shown.

View File

@@ -1,31 +0,0 @@
[
{
"fileName" : "metadata.png",
"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.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -1,5 +0,0 @@
[
{
"fileName" : "ps_testcard_rgb16_maya.png"
}
]

View File

@@ -1,5 +0,0 @@
[
{
"fileName" : "ps_testcard_rgba16_maya.png"
}
]

View File

@@ -1,5 +0,0 @@
[
{
"fileName" : "ps_testcard_rgb_maya.png"
}
]

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

View File

@@ -1,5 +0,0 @@
[
{
"fileName" : "orientation.png"
}
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

View File

@@ -1,5 +0,0 @@
[
{
"fileName" : "orientation.png"
}
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -1,56 +0,0 @@
[
{
"fileName" : "extarea.png",
"skipSequential" : true,
"metadata" : [
{
"key" : "Author",
"value" : "KDE Project"
},
{
"key" : "Comment",
"value" : "TV broadcast test image."
},
{
"key" : "Altitude",
"value" : "34"
},
{
"key" : "Copyright",
"value" : "@2025 KDE Project"
},
{
"key" : "Latitude",
"value" : "44.6478"
},
{
"key" : "LensManufacturer",
"value" : "KDE Glasses"
},
{
"key" : "LensModel",
"value" : "A1234"
},
{
"key" : "Longitude",
"value" : "10.9254"
},
{
"key" : "Manufacturer",
"value" : "KFramework"
},
{
"key" : "Model",
"value" : "KImageFormats"
},
{
"key" : "Software",
"value" : "LIFE Pro 2.18.30 (Linux)"
}
],
"resolution" : {
"dotsPerMeterX" : 11811,
"dotsPerMeterY" : 5906
}
}
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

View File

@@ -1,32 +0,0 @@
[
{
"fileName" : "extarea.png",
"skipSequential" : true,
"metadata" : [
{
"key" : "Title",
"value" : "Test Card"
},
{
"key" : "Author",
"value" : "KDE Project"
},
{
"key" : "ModificationDate",
"value" : "2025-08-21T07:32:45"
},
{
"key" : "Comment",
"value" : "TV broadcast test image."
},
{
"key" : "Software",
"value" : "LIFE Pro 2.18.31 (Linux)"
}
],
"resolution" : {
"dotsPerMeterX" : 3937,
"dotsPerMeterY" : 3937
}
}
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

View File

@@ -1,5 +0,0 @@
[
{
"fileName" : "orientation.png"
}
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

View File

@@ -1,5 +0,0 @@
[
{
"fileName" : "orientation.png"
}
]

View File

@@ -281,12 +281,6 @@ int main(int argc, char **argv)
continue;
}
if (seq && timg.skipSequentialDeviceTest()) {
QTextStream(stdout) << "SKIP : " << fi.fileName() << ": marked to be skipped on a sequential device (don't worry, it's ok)\n";
++skipped;
continue;
}
TemplateImage::TestFlags flags = TemplateImage::None;
QString comment;
QFileInfo expFileInfo = timg.compareImage(flags, comment);

View File

@@ -76,15 +76,6 @@ bool TemplateImage::isLicense() const
return !m_fi.suffix().compare(QStringLiteral("license"), Qt::CaseInsensitive);
}
bool TemplateImage::skipSequentialDeviceTest() const
{
auto obj = searchObject(m_fi);
if (obj.isEmpty()) {
return false;
}
return obj.value("skipSequential").toBool();
}
QFileInfo TemplateImage::compareImage(TestFlags &flags, QString& comment) const
{
auto fi = jsonImage(flags, comment);

View File

@@ -54,12 +54,6 @@ public:
*/
bool isLicense() const;
/*!
* \brief skipSequentialDeviceTest
* \return tre it the sequential test should be skipped.
*/
bool skipSequentialDeviceTest() const;
/*!
* \brief compareImage
* \param flags Flags for modifying test behavior (e.g. image format not supported by current Qt version).

View File

@@ -1,62 +0,0 @@
{
"format" : "tga",
"subType" : "TGAv2E",
"metadata" : [
{
"key" : "CreationDate",
"value" : "2025-01-14T13:53:32+01:00"
},
{
"key" : "Direction",
"value" : "123.7"
},
{
"key" : "Software" ,
"value" : "Adobe Photoshop 26.2 (Windows)"
},
{
"key" : "Altitude",
"value" : "34"
},
{
"key" : "Author",
"value" : "KDE Project"
},
{
"key" : "Copyright",
"value" : "@2025 KDE Project"
},
{
"key" : "Description",
"value" : "テレビ放送テスト映像。(TV broadcast test image.)"
},
{
"key" : "Latitude",
"value" : "44.6478"
},
{
"key" : "LensManufacturer",
"value" : "KDE Glasses"
},
{
"key" : "LensModel",
"value" : "A1234"
},
{
"key" : "Longitude",
"value" : "10.9254"
},
{
"key" : "Manufacturer",
"value" : "KFramework"
},
{
"key" : "Model",
"value" : "KImageFormats"
}
],
"resolution" : {
"dotsPerMeterX" : 11811,
"dotsPerMeterY" : 11812
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -70,15 +70,6 @@ void setOptionalInfo(QImage &image, const QString &suffix)
}
}
QByteArray readSubType(const QString &suffix)
{
auto obj = readOptionalInfo(suffix);
if (obj.isEmpty()) {
return {};
}
return obj.value("subType").toString().toLatin1();
}
bool checkOptionalInfo(QImage &image, const QString &suffix)
{
auto obj = readOptionalInfo(suffix);
@@ -166,9 +157,6 @@ int basicTest(const QString &suffix, bool lossless, bool ignoreDataCheck, bool s
{
QBuffer buffer(&writtenData);
QImageWriter imgWriter(&buffer, format.constData());
auto subType = readSubType(suffix);
if (!subType.isEmpty())
imgWriter.setSubType(subType);
if (lossless) {
imgWriter.setQuality(100);
}

View File

@@ -84,7 +84,7 @@ endif()
##################################
kimageformats_add_plugin(kimg_iff SOURCES iff.cpp chunks.cpp microexif.cpp)
kimageformats_add_plugin(kimg_iff SOURCES iff.cpp chunks.cpp)
##################################
@@ -101,11 +101,11 @@ endif()
##################################
kimageformats_add_plugin(kimg_pcx SOURCES pcx.cpp scanlineconverter.cpp)
kimageformats_add_plugin(kimg_pcx SOURCES pcx.cpp)
##################################
kimageformats_add_plugin(kimg_pic SOURCES pic.cpp scanlineconverter.cpp)
kimageformats_add_plugin(kimg_pic SOURCES pic.cpp)
##################################
@@ -129,7 +129,7 @@ kimageformats_add_plugin(kimg_ras SOURCES ras.cpp)
##################################
kimageformats_add_plugin(kimg_rgb SOURCES rgb.cpp scanlineconverter.cpp)
kimageformats_add_plugin(kimg_rgb SOURCES rgb.cpp)
##################################
@@ -137,7 +137,7 @@ kimageformats_add_plugin(kimg_sct SOURCES sct.cpp)
##################################
kimageformats_add_plugin(kimg_tga SOURCES tga.cpp microexif.cpp scanlineconverter.cpp)
kimageformats_add_plugin(kimg_tga SOURCES tga.cpp)
##################################

View File

@@ -11,12 +11,12 @@
#include <QByteArray>
#include <QImage>
#include <QImageIOHandler>
#include <QImageIOPlugin>
#include <QPointF>
#include <QSize>
#include <QVariant>
#include <avif/avif.h>
#include <qimageiohandler.h>
class QAVIFHandler : public QImageIOHandler
{

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,6 @@
* Format specifications:
* - 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)
*/
#ifndef KIMG_CHUNKS_P_H
@@ -19,15 +18,10 @@
#include <QDateTime>
#include <QImage>
#include <QIODevice>
#include <QLoggingCategory>
#include <QPoint>
#include <QSize>
#include <QSharedPointer>
#include "microexif_p.h"
Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
// Main chunks (Standard)
#define CAT__CHUNK QByteArray("CAT ")
#define FILL_CHUNK QByteArray(" ")
@@ -36,67 +30,29 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
#define PROP_CHUNK QByteArray("PROP")
// Main chuncks (Maya)
#define CAT4_CHUNK QByteArray("CAT4") // 4 byte alignment
#define FOR4_CHUNK QByteArray("FOR4")
#define LIS4_CHUNK QByteArray("LIS4")
#define PRO4_CHUNK QByteArray("PRO4")
#define CAT8_CHUNK QByteArray("CAT8") // 8 byte alignment (never seen)
#define FOR8_CHUNK QByteArray("FOR8")
#define LIS8_CHUNK QByteArray("LIS8")
#define PRO8_CHUNK QByteArray("PRO8")
// FORM ILBM IFF
#define ABIT_CHUNK QByteArray("ABIT")
#define BMHD_CHUNK QByteArray("BMHD")
#define BODY_CHUNK QByteArray("BODY")
#define CAMG_CHUNK QByteArray("CAMG")
#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 ")
// Different palette for scanline
#define BEAM_CHUNK QByteArray("BEAM")
#define CTBL_CHUNK QByteArray("CTBL") // same as BEAM
#define PCHG_CHUNK QByteArray("PCHG") // encoded in a unknown way (to be investigated)
#define RAST_CHUNK QByteArray("RAST") // Atari ST(E)
#define SHAM_CHUNK QByteArray("SHAM")
#define SHAM_CHUNK QByteArray("SHAM") // undocumented
// FOR4 CIMG IFF (Maya)
#define RGBA_CHUNK QByteArray("RGBA")
#define TBHD_CHUNK QByteArray("TBHD")
// FORx IFF (found on some IFF format specs)
#define ANNO_CHUNK QByteArray("ANNO")
#define AUTH_CHUNK QByteArray("AUTH")
#define COPY_CHUNK QByteArray("(c) ")
#define DATE_CHUNK QByteArray("DATE")
#define EXIF_CHUNK QByteArray("EXIF") // https://aminet.net/package/docs/misc/IFF-metadata
#define ICCN_CHUNK QByteArray("ICCN") // https://aminet.net/package/docs/misc/IFF-metadata
#define ICCP_CHUNK QByteArray("ICCP") // https://aminet.net/package/docs/misc/IFF-metadata
#define FVER_CHUNK QByteArray("FVER")
#define HIST_CHUNK QByteArray("HIST")
#define NAME_CHUNK QByteArray("NAME")
#define VERS_CHUNK QByteArray("VERS")
#define XMP0_CHUNK QByteArray("XMP0") // https://aminet.net/package/docs/misc/IFF-metadata
#define ACBM_FORM_TYPE QByteArray("ACBM")
#define ILBM_FORM_TYPE QByteArray("ILBM")
#define PBM__FORM_TYPE QByteArray("PBM ")
#define RGB8_FORM_TYPE QByteArray("RGB8")
#define RGBN_FORM_TYPE QByteArray("RGBN")
#define CIMG_FOR4_TYPE QByteArray("CIMG")
#define TBMP_FOR4_TYPE QByteArray("TBMP")
#define CHUNKID_DEFINE(a) static QByteArray defaultChunkId() { return a; }
// The 8-bit RGB format must be one. If you change it here, you have also to use the same
// when converting an image with BEAM/CTBL/SHAM chunks otherwise the option(QImageIOHandler::ImageFormat)
// could returns a wrong value.
// Warning: Changing it requires changing the algorithms. Se, don't touch! :)
#define FORMAT_RGB_8BIT QImage::Format_RGB888
/*!
* \brief The IFFChunk class
*/
@@ -239,18 +195,15 @@ public:
template <class T>
static QList<const T*> searchT(const IFFChunk *chunk) {
QList<const T*> list;
if (chunk == nullptr) {
if (chunk == nullptr)
return list;
}
auto cid = T::defaultChunkId();
if (chunk->chunkId() == cid) {
if (chunk->chunkId() == cid)
if (auto c = dynamic_cast<const T*>(chunk))
list << c;
}
auto tmp = chunk->chunks();
for (auto &&c : tmp) {
for (auto &&c : tmp)
list << searchT<T>(c.data());
}
return list;
}
@@ -263,9 +216,8 @@ public:
template <class T>
static QList<const T*> searchT(const ChunkList& chunks) {
QList<const T*> list;
for (auto &&chunk : chunks) {
for (auto &&chunk : chunks)
list << searchT<T>(chunk.data());
}
return list;
}
@@ -284,14 +236,11 @@ protected:
* \brief setAlignBytes
* \param bytes
*/
void setAlignBytes(qint32 bytes);
void setAlignBytes(qint32 bytes)
{
_align = bytes;
}
/*!
* \brief nextChunkPos
* Calculates the position of the next chunk. The position is already aligned.
* \return The position of the next chunk from the beginning of the stream.
*/
qint64 nextChunkPos() const;
/*!
* \brief cacheData
@@ -331,7 +280,7 @@ protected:
return qint32(ui32(c1, c2, c3, c4));
}
static ChunkList innerFromDevice(QIODevice *d, bool *ok, IFFChunk *parent = nullptr);
static ChunkList innerFromDevice(QIODevice *d, bool *ok, qint32 alignBytes, qint32 recursionCnt);
private:
char _chunkId[4];
@@ -347,45 +296,20 @@ private:
ChunkList _chunks;
qint32 _recursionCnt;
};
/*!
* \brief The IPALChunk class
* Interface for additional per-line palette.
*/
class IPALChunk : public IFFChunk
{
public:
virtual ~IPALChunk() override {}
IPALChunk() : IFFChunk() {}
virtual QList<QRgb> palette(qint32 y, qint32 height) const = 0;
};
/*!
* \brief The BMHDChunk class
* \brief The IffBMHD class
* Bitmap Header
*/
class BMHDChunk: public IFFChunk
{
public:
enum Compression {
Uncompressed = 0, /**< Image data are uncompressed. */
Rle = 1, /**< Image data are RLE compressed. */
RgbN8 = 4 /**< RGB8/RGBN compresson. */
};
enum Masking {
None = 0, /**< Designates an opaque rectangular image. */
HasMask = 1, /**< A mask plane is interleaved with the bitplanes in the BODY chunk. */
HasTransparentColor = 2, /**< Pixels in the source planes matching transparentColor
are to be considered “transparent”. (Actually, transparentColor
isnt a “color number” since its matched with numbers formed
by the source bitmap rather than the possibly deeper destination
bitmap. Note that having a transparent color implies ignoring
one of the color registers. */
Lasso = 3 /**< The reader may construct a mask by lassoing the image as in MacPaint.
To do this, put a 1 pixel border of transparentColor around the image rectangle.
Then do a seed fill from this border. Filled pixels are to be transparent. */
Uncompressed = 0,
Rle = 1
};
virtual ~BMHDChunk() override;
@@ -396,88 +320,34 @@ public:
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 left
* \return The left position of the image.
*/
qint32 left() const;
/*!
* \brief top
* \return The top position of the image.
*/
qint32 top() const;
/*!
* \brief bitplanes
* \return The number of bit planes.
*/
quint8 bitplanes() const;
/*!
* \brief masking
* \return Kind of masking is to be used for this image.
*/
Masking masking() const;
quint8 masking() const;
/*!
* \brief compression
* \return The type of compression used.
*/
Compression compression() const;
/*!
* \brief transparency
* \return Transparent "color number".
*/
quint8 padding() const;
qint16 transparency() const;
/*!
* \brief xAspectRatio
* \return X pixel aspect.
*/
quint8 xAspectRatio() const;
/*!
* \brief yAspectRatio
* \return Y pixel aspect.
*/
quint8 yAspectRatio() const;
/*!
* \brief pageWidth
* \return Source "page" width in pixels.
*/
quint16 pageWidth() const;
/*!
* \brief pageHeight
* \return Source "page" height in pixels.
*/
quint16 pageHeight() const;
/*!
* \brief rowLen
* \return The row len of a plane.
*/
quint32 rowLen() const;
CHUNKID_DEFINE(BMHD_CHUNK)
@@ -499,64 +369,14 @@ public:
virtual bool isValid() const override;
/*!
* \brief count
* \return The number of color in the palette.
*/
virtual qint32 count() const;
/*!
* \brief palette
* \param halfbride When True, the new palette values are appended using the halfbride method.
* \return The color palette.
* \note If \a halfbride is true, the returned palette size is count() * 2.
*/
QList<QRgb> palette(bool halfbride = false) const;
QList<QRgb> palette() const;
CHUNKID_DEFINE(CMAP_CHUNK)
protected:
virtual QList<QRgb> innerPalette() const;
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The CMYKChunk class
*
* This chunk would allow color specification in terms of Cyan,
* Magenta, Yellow, and Black as opposed to the current CMAP which uses RGB.
* The format would be the same as the CMAP chunk with the exception that this
* chunk uses four color components as opposed to three. The number of colors
* contained within would be chunk length/4. This chunk would be used in addition
* to the CMAP chunk.
*/
class CMYKChunk : public CMAPChunk
{
public:
virtual ~CMYKChunk() override;
CMYKChunk();
CMYKChunk(const CMYKChunk& other) = default;
CMYKChunk& operator =(const CMYKChunk& other) = default;
virtual bool isValid() const override;
/*!
* \brief count
* \return The number of color in the palette.
*/
virtual qint32 count() const override;
CHUNKID_DEFINE(CMYK_CHUNK)
protected:
/*!
* \brief palette
* \return The CMYK color palette converted to RGB one.
*/
virtual QList<QRgb> innerPalette() const override;
};
/*!
* \brief The CAMGChunk class
*/
@@ -631,6 +451,7 @@ protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
class FORMChunk;
/*!
* \brief The BODYChunk class
@@ -651,151 +472,42 @@ public:
* \brief readStride
* \param d The device.
* \param header The bitmap header.
* \param y The current scanline.
* \param camg The CAMG chunk (optional)
* \param cmap The CMAP 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.
*/
virtual QByteArray strideRead(QIODevice *d,
qint32 y,
const BMHDChunk *header,
const CAMGChunk *camg = nullptr,
const CMAPChunk *cmap = nullptr,
const IPALChunk *ipal = nullptr,
const QByteArray& formType = ILBM_FORM_TYPE) const;
QByteArray strideRead(QIODevice *d, const FORMChunk *form, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = 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.
* \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().
*/
virtual bool resetStrideRead(QIODevice *d) const;
/*!
* \brief safeModeId
* \param header The header.
* \param camg The CAMG chunk.
* \return The most likely ModeId if not explicitly specified.
*/
static CAMGChunk::ModeIds safeModeId(const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap = nullptr);
protected:
/*!
* \brief strideSize
* \param formType The type of the current form chunk.
* \return The size of data to have to decode an image row.
*/
quint32 strideSize(const BMHDChunk *header, const QByteArray& formType) const;
QByteArray deinterleave(const QByteArray &planes, qint32 y, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr, const IPALChunk *ipal = nullptr) const;
QByteArray pbm(const QByteArray &planes, qint32 y, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr, const IPALChunk *ipal = nullptr) const;
QByteArray rgb8(const QByteArray &planes, qint32 y, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr, const IPALChunk *ipal = nullptr) const;
QByteArray rgbN(const QByteArray &planes, qint32 y, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr, const IPALChunk *ipal = nullptr) const;
bool resetStrideRead(QIODevice *d) const;
private:
static QByteArray deinterleave(const QByteArray &planes, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr);
mutable QByteArray _readBuffer;
};
/*!
* \brief The ABITChunk class
*/
class ABITChunk : public BODYChunk
{
public:
virtual ~ABITChunk() override;
ABITChunk();
ABITChunk(const ABITChunk& other) = default;
ABITChunk& operator =(const ABITChunk& other) = default;
virtual bool isValid() const override;
CHUNKID_DEFINE(ABIT_CHUNK)
virtual QByteArray strideRead(QIODevice *d,
qint32 y,
const BMHDChunk *header,
const CAMGChunk *camg = nullptr,
const CMAPChunk *cmap = nullptr,
const IPALChunk *ipal = nullptr,
const QByteArray& formType = ACBM_FORM_TYPE) const override;
virtual bool resetStrideRead(QIODevice *d) const override;
};
/*!
* \brief The IFOR_Chunk class
* Interface for FORM chunks.
*/
class IFOR_Chunk : public IFFChunk
{
public:
virtual ~IFOR_Chunk() override;
IFOR_Chunk();
/*!
* \brief isSupported
* \return True if the form is supported by the plugin.
*/
virtual bool isSupported() const = 0;
/*!
* \brief formType
* \return The type of image data contained in the form.
*/
virtual QByteArray formType() const = 0;
/*!
* \brief format
* \return The Qt image format the form is converted to.
*/
virtual QImage::Format format() const = 0;
/*!
* \brief transformation
* \return The image transformation.
* \note The Default implentation returns the trasformation of EXIF chunk (if any).
*/
virtual QImageIOHandler::Transformation transformation() const;
/*!
* \brief size
* \return The image size in pixels.
*/
virtual QSize size() const = 0;
/*!
* \brief optionformat
* \return The format retuned by the plugin after all conversions.
*/
QImage::Format optionformat() const;
/*!
* \brief searchIPal
* Search the palett per line chunk.
* \return The per line palette (BEAM, CTBL, SHAM, etc....).
*/
const IPALChunk *searchIPal() const;
};
/*!
* \brief The FORMChunk class
*/
class FORMChunk : public IFOR_Chunk
class FORMChunk : public IFFChunk
{
QByteArray _type;
public:
enum class FormType {
Unknown,
Ilbm,
Pbm,
};
virtual ~FORMChunk() override;
FORMChunk();
FORMChunk(const FORMChunk& other) = default;
@@ -803,25 +515,27 @@ public:
virtual bool isValid() const override;
virtual bool isSupported() const override;
bool isSupported() const;
virtual QByteArray formType() const override;
FormType formType() const;
virtual QImage::Format format() const override;
QImage::Format format() const;
virtual QSize size() const override;
QSize size() const;
CHUNKID_DEFINE(FORM_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
private:
FormType _type = FormType::Unknown;
};
/*!
* \brief The FOR4Chunk class
*/
class FOR4Chunk : public IFOR_Chunk
class FOR4Chunk : public IFFChunk
{
QByteArray _type;
@@ -835,13 +549,13 @@ public:
virtual qint32 alignBytes() const override;
virtual bool isSupported() const override;
bool isSupported() const;
virtual QByteArray formType() const override;
QByteArray formType() const;
virtual QImage::Format format() const override;
QImage::Format format() const;
virtual QSize size() const override;
QSize size() const;
CHUNKID_DEFINE(FOR4_CHUNK)
@@ -849,29 +563,6 @@ protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The CATChunk class
*/
class CATChunk : public IFFChunk
{
QByteArray _type;
public:
virtual ~CATChunk() override;
CATChunk();
CATChunk(const CATChunk& other) = default;
CATChunk& operator =(const CATChunk& other) = default;
virtual bool isValid() const override;
QByteArray catType() const;
CHUNKID_DEFINE(CAT__CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The TBHDChunk class
*/
@@ -879,11 +570,12 @@ class TBHDChunk : public IFFChunk
{
public:
enum Flag {
Rgb = 0x01, /**< RGB image */
Alpha = 0x02, /**< Image contains alpha channel */
ZBuffer = 0x04, /**< If the image has a z-buffer, it is described by ZBUF blocks with the same structure as the RGBA blocks, RLE encoded. */
Rgb = 0x01,
Alpha = 0x02,
ZBuffer = 0x04,
Black = 0x10,
RgbA = Rgb | Alpha /**< RGBA image */
RgbA = Rgb | Alpha
};
Q_DECLARE_FLAGS(Flags, Flag)
@@ -1030,33 +722,13 @@ private:
QByteArray readStride(QIODevice *d, const TBHDChunk *header) const;
private:
QPoint _posPx;
QPoint _pos;
QSize _sizePx;
QSize _size;
mutable QByteArray _readBuffer;
};
/*!
* \brief The ANNOChunk class
*/
class ANNOChunk : public IFFChunk
{
public:
virtual ~ANNOChunk() override;
ANNOChunk();
ANNOChunk(const ANNOChunk& other) = default;
ANNOChunk& operator =(const ANNOChunk& other) = default;
virtual bool isValid() const override;
QString value() const;
CHUNKID_DEFINE(ANNO_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The AUTHChunk class
@@ -1079,27 +751,6 @@ protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The COPYChunk class
*/
class COPYChunk : public IFFChunk
{
public:
virtual ~COPYChunk() override;
COPYChunk();
COPYChunk(const COPYChunk& other) = default;
COPYChunk& operator =(const COPYChunk& other) = default;
virtual bool isValid() const override;
QString value() const;
CHUNKID_DEFINE(COPY_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The DATEChunk class
*/
@@ -1121,71 +772,6 @@ protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The EXIFChunk class
*/
class EXIFChunk : public IFFChunk
{
public:
virtual ~EXIFChunk() override;
EXIFChunk();
EXIFChunk(const EXIFChunk& other) = default;
EXIFChunk& operator =(const EXIFChunk& other) = default;
virtual bool isValid() const override;
MicroExif value() const;
CHUNKID_DEFINE(EXIF_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The NAMEChunk class
*/
class ICCNChunk : public IFFChunk
{
public:
virtual ~ICCNChunk() override;
ICCNChunk();
ICCNChunk(const ICCNChunk& other) = default;
ICCNChunk& operator =(const ICCNChunk& other) = default;
virtual bool isValid() const override;
QString value() const;
CHUNKID_DEFINE(ICCN_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The ICCPChunk class
*/
class ICCPChunk : public IFFChunk
{
public:
virtual ~ICCPChunk() override;
ICCPChunk();
ICCPChunk(const ICCPChunk& other) = default;
ICCPChunk& operator =(const ICCPChunk& other) = default;
virtual bool isValid() const override;
QColorSpace value() const;
CHUNKID_DEFINE(ICCP_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The FVERChunk class
*
@@ -1230,27 +816,6 @@ protected:
};
/*!
* \brief The NAMEChunk class
*/
class NAMEChunk : public IFFChunk
{
public:
virtual ~NAMEChunk() override;
NAMEChunk();
NAMEChunk(const NAMEChunk& other) = default;
NAMEChunk& operator =(const NAMEChunk& other) = default;
virtual bool isValid() const override;
QString value() const;
CHUNKID_DEFINE(NAME_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The VERSChunk class
*/
@@ -1272,114 +837,4 @@ protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The XMP0Chunk class
*/
class XMP0Chunk : public IFFChunk
{
public:
virtual ~XMP0Chunk() override;
XMP0Chunk();
XMP0Chunk(const XMP0Chunk& other) = default;
XMP0Chunk& operator =(const XMP0Chunk& other) = default;
virtual bool isValid() const override;
QString value() const;
CHUNKID_DEFINE(XMP0_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* *** UNDOCUMENTED CHUNKS ***
*/
/*!
* \brief The BEAMChunk class
*/
class BEAMChunk : public IPALChunk
{
public:
virtual ~BEAMChunk() override;
BEAMChunk();
BEAMChunk(const BEAMChunk& other) = default;
BEAMChunk& operator =(const BEAMChunk& other) = default;
virtual bool isValid() const override;
virtual QList<QRgb> palette(qint32 y, qint32 height) const override;
CHUNKID_DEFINE(BEAM_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The CTBLChunk class
*/
class CTBLChunk : public BEAMChunk
{
public:
virtual ~CTBLChunk() override;
CTBLChunk();
CTBLChunk(const CTBLChunk& other) = default;
CTBLChunk& operator =(const CTBLChunk& other) = default;
virtual bool isValid() const override;
CHUNKID_DEFINE(CTBL_CHUNK)
};
/*!
* \brief The SHAMChunk class
*/
class SHAMChunk : public IPALChunk
{
public:
virtual ~SHAMChunk() override;
SHAMChunk();
SHAMChunk(const SHAMChunk& other) = default;
SHAMChunk& operator =(const SHAMChunk& other) = default;
virtual bool isValid() const override;
virtual QList<QRgb> palette(qint32 y, qint32 height) const override;
CHUNKID_DEFINE(SHAM_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
/*!
* \brief The RASTChunk class
* \note I found an Atari STE image with the RAST chunk outside
* the form chunk (Fish.neo.iff). To support it the IFF parser
* should be changed so, this kind of IFFs are shown wrong.
*/
class RASTChunk : public IPALChunk
{
public:
virtual ~RASTChunk() override;
RASTChunk();
RASTChunk(const RASTChunk& other) = default;
RASTChunk& operator =(const RASTChunk& other) = default;
virtual bool isValid() const override;
virtual QList<QRgb> palette(qint32 y, qint32 height) const override;
CHUNKID_DEFINE(RAST_CHUNK)
protected:
virtual bool innerReadStructure(QIODevice *d) override;
};
#endif // KIMG_CHUNKS_P_H

View File

@@ -1126,11 +1126,7 @@ static QImage readR32F(QDataStream &s, const quint32 width, const quint32 height
for (quint32 y = 0; y < height; y++) {
float *line = reinterpret_cast<float *>(image.scanLine(y));
for (quint32 x = 0; x < width; x++) {
const float f = readFloat32(s);
if (std::isnan(f)) {
return {};
}
line[x * 4] = f;
line[x * 4] = readFloat32(s);
line[x * 4 + 1] = 0;
line[x * 4 + 2] = 0;
line[x * 4 + 3] = 1;

View File

@@ -7,10 +7,6 @@
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "exr_p.h"
#include "scanlineconverter_p.h"
#include "util_p.h"
/* *** EXR_CONVERT_TO_SRGB ***
* If defined, the linear data is converted to sRGB on read to accommodate
* programs that do not support color profiles.
@@ -32,7 +28,7 @@
* The maximum size in pixel allowed by the plugin.
*/
#ifndef EXR_MAX_IMAGE_WIDTH
#define EXR_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT
#define EXR_MAX_IMAGE_WIDTH 300000
#endif
#ifndef EXR_MAX_IMAGE_HEIGHT
#define EXR_MAX_IMAGE_HEIGHT EXR_MAX_IMAGE_WIDTH
@@ -54,6 +50,10 @@
#define EXR_LINES_PER_BLOCK 128
#endif
#include "exr_p.h"
#include "scanlineconverter_p.h"
#include "util_p.h"
#include <IexThrowErrnoExc.h>
#include <ImathBox.h>
#include <ImfArray.h>

View File

@@ -23,16 +23,6 @@
*/
//#define HDR_HALF_QUALITY // default commented -> you should define it in your cmake file
/* *** HDR_MAX_IMAGE_WIDTH and HDR_MAX_IMAGE_HEIGHT ***
* The maximum size in pixel allowed by the plugin.
*/
#ifndef HDR_MAX_IMAGE_WIDTH
#define HDR_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT
#endif
#ifndef HDR_MAX_IMAGE_HEIGHT
#define HDR_MAX_IMAGE_HEIGHT HDR_MAX_IMAGE_WIDTH
#endif
typedef unsigned char uchar;
Q_LOGGING_CATEGORY(HDRPLUGIN, "kf.imageformats.plugins.hdr", QtWarningMsg)
@@ -52,10 +42,7 @@ public:
Header(const Header&) = default;
Header& operator=(const Header&) = default;
bool isValid() const
{
return width() > 0 && height() > 0 && width() <= HDR_MAX_IMAGE_WIDTH && height() <= HDR_MAX_IMAGE_HEIGHT;
}
bool isValid() const { return width() > 0 && height() > 0; }
qint32 width() const { return(m_size.width()); }
qint32 height() const { return(m_size.height()); }
QString software() const { return(m_software); }

View File

@@ -11,43 +11,40 @@
#include <QIODevice>
#include <QImage>
#include <QLoggingCategory>
#include <QPainter>
#ifdef QT_DEBUG
Q_LOGGING_CATEGORY(LOG_IFFPLUGIN, "kf.imageformats.plugins.iff", QtInfoMsg)
#else
Q_LOGGING_CATEGORY(LOG_IFFPLUGIN, "kf.imageformats.plugins.iff", QtWarningMsg)
#endif
class IFFHandlerPrivate
{
public:
IFFHandlerPrivate()
: m_imageNumber(0)
, m_imageCount(0)
{
IFFHandlerPrivate() {}
~IFFHandlerPrivate() {}
}
~IFFHandlerPrivate()
{
}
bool readStructure(QIODevice *d)
{
bool readStructure(QIODevice *d) {
if (d == nullptr) {
return {};
}
if (!m_chunks.isEmpty()) {
if (!_chunks.isEmpty()) {
return true;
}
auto ok = false;
auto chunks = IFFChunk::fromDevice(d, &ok);
if (ok) {
m_chunks = chunks;
_chunks = chunks;
}
return ok;
}
template <class T>
static QList<const T*> searchForms(const IFFChunk::ChunkList &chunks, bool supportedOnly = true)
{
static QList<const T*> searchForms(const IFFChunk::ChunkList &chunks, bool supportedOnly = true) {
QList<const T*> list;
auto cid = T::defaultChunkId();
auto forms = IFFChunk::search(cid, chunks);
@@ -60,25 +57,11 @@ public:
}
template <class T>
QList<const T*> searchForms(bool supportedOnly = true)
{
return searchForms<T>(m_chunks, supportedOnly);
QList<const T*> searchForms(bool supportedOnly = true) {
return searchForms<T>(_chunks, supportedOnly);
}
IFFChunk::ChunkList m_chunks;
/*!
* \brief m_imageNumber
* Value set by QImageReader::jumpToImage() or QImageReader::jumpToNextImage().
* The number of view selected in a multiview image.
*/
qint32 m_imageNumber;
/*!
* \brief m_imageCount
* The total number of views (cache value)
*/
mutable qint32 m_imageCount;
IFFChunk::ChunkList _chunks;
};
@@ -86,13 +69,12 @@ IFFHandler::IFFHandler()
: QImageIOHandler()
, d(new IFFHandlerPrivate)
{
}
bool IFFHandler::canRead() const
{
if (canRead(device())) {
setFormat("iff");
setFormat("iff"); // TODO ilbm based on header?
return true;
}
return false;
@@ -109,17 +91,6 @@ bool IFFHandler::canRead(QIODevice *device)
return false;
}
// I avoid parsing obviously incorrect files
auto cid = device->peek(4);
if (cid != CAT__CHUNK &&
cid != FORM_CHUNK &&
cid != LIST_CHUNK &&
cid != CAT4_CHUNK &&
cid != FOR4_CHUNK &&
cid != LIS4_CHUNK) {
return false;
}
auto ok = false;
auto pos = device->pos();
auto chunks = IFFChunk::fromDevice(device, &ok);
@@ -134,14 +105,13 @@ bool IFFHandler::canRead(QIODevice *device)
return ok;
}
static void addMetadata(QImage &img, const IFOR_Chunk *form)
void addMetadata(QImage& img, const IFFChunk *form)
{
// standard IFF metadata
auto annos = IFFChunk::searchT<ANNOChunk>(form);
if (!annos.isEmpty()) {
auto anno = annos.first()->value();
if (!anno.isEmpty()) {
img.setText(QStringLiteral(META_KEY_DESCRIPTION), anno);
auto dates = IFFChunk::searchT<DATEChunk>(form);
if (!dates.isEmpty()) {
auto dt = dates.first()->value();
if (dt.isValid()) {
img.setText(QStringLiteral(META_KEY_CREATIONDATE), dt.toString(Qt::ISODate));
}
}
auto auths = IFFChunk::searchT<AUTHChunk>(form);
@@ -151,29 +121,6 @@ static void addMetadata(QImage &img, const IFOR_Chunk *form)
img.setText(QStringLiteral(META_KEY_AUTHOR), auth);
}
}
auto dates = IFFChunk::searchT<DATEChunk>(form);
if (!dates.isEmpty()) {
auto dt = dates.first()->value();
if (dt.isValid()) {
img.setText(QStringLiteral(META_KEY_CREATIONDATE), dt.toString(Qt::ISODate));
}
}
auto copys = IFFChunk::searchT<COPYChunk>(form);
if (!copys.isEmpty()) {
auto cp = copys.first()->value();
if (!cp.isEmpty()) {
img.setText(QStringLiteral(META_KEY_COPYRIGHT), cp);
}
}
auto names = IFFChunk::searchT<NAMEChunk>(form);
if (!names.isEmpty()) {
auto name = names.first()->value();
if (!name.isEmpty()) {
img.setText(QStringLiteral(META_KEY_TITLE), name);
}
}
// software info
auto vers = IFFChunk::searchT<VERSChunk>(form);
if (!vers.isEmpty()) {
auto ver = vers.first()->value();
@@ -181,98 +128,6 @@ static void addMetadata(QImage &img, const IFOR_Chunk *form)
img.setText(QStringLiteral(META_KEY_SOFTWARE), ver);
}
}
// SView5 metadata
auto resChanged = false;
auto exifs = IFFChunk::searchT<EXIFChunk>(form);
if (!exifs.isEmpty()) {
auto exif = exifs.first()->value();
exif.updateImageMetadata(img, false);
resChanged = exif.updateImageResolution(img);
}
auto xmp0s = IFFChunk::searchT<XMP0Chunk>(form);
if (!xmp0s.isEmpty()) {
auto xmp = xmp0s.first()->value();
if (!xmp.isEmpty()) {
img.setText(QStringLiteral(META_KEY_XMP_ADOBE), xmp);
}
}
auto iccps = IFFChunk::searchT<ICCPChunk>(form);
if (!iccps.isEmpty()) {
auto cs = iccps.first()->value();
if (cs.isValid()) {
auto iccns = IFFChunk::searchT<ICCNChunk>(form);
if (!iccns.isEmpty()) {
auto desc = iccns.first()->value();
if (!desc.isEmpty())
cs.setDescription(desc);
}
img.setColorSpace(cs);
}
}
// resolution -> leave after set of EXIF chunk
auto dpis = IFFChunk::searchT<DPIChunk>(form);
if (!dpis.isEmpty()) {
auto &&dpi = dpis.first();
if (dpi->isValid()) {
img.setDotsPerMeterX(dpi->dotsPerMeterX());
img.setDotsPerMeterY(dpi->dotsPerMeterY());
resChanged = true;
}
}
// 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);
}
}
}
}
/*!
* \brief convertIPAL
* \param img The source image.
* \param ipal The per line palette.
* \return The new image converted or \a img if no conversion is needed or possible.
*/
static QImage convertIPAL(const QImage& img, const IPALChunk *ipal)
{
if (img.format() != QImage::Format_Indexed8) {
qDebug(LOG_IFFPLUGIN) << "convertIPAL(): the image is not indexed!";
return img;
}
auto tmp = img.convertToFormat(FORMAT_RGB_8BIT);
if (tmp.isNull()) {
qCritical(LOG_IFFPLUGIN) << "convertIPAL(): error while converting the image!";
return img;
}
for (auto y = 0, h = img.height(); y < h; ++y) {
auto src = reinterpret_cast<const quint8 *>(img.constScanLine(y));
auto dst = tmp.scanLine(y);
auto lpal = ipal->palette(y, h);
for (auto x = 0, w = img.width(); x < w; ++x) {
if (src[x] < lpal.size()) {
auto x3 = x * 3;
dst[x3] = qRed(lpal.at(src[x]));
dst[x3 + 1] = qGreen(lpal.at(src[x]));
dst[x3 + 2] = qBlue(lpal.at(src[x]));
}
}
}
return tmp;
}
bool IFFHandler::readStandardImage(QImage *image)
@@ -281,8 +136,7 @@ bool IFFHandler::readStandardImage(QImage *image)
if (forms.isEmpty()) {
return false;
}
auto cin = qBound(0, currentImageNumber(), int(forms.size() - 1));
auto &&form = forms.at(cin);
auto &&form = forms.first();
// show the first one (I don't have a sample with many images)
auto headers = IFFChunk::searchT<BMHDChunk>(form);
@@ -299,41 +153,38 @@ bool IFFHandler::readStandardImage(QImage *image)
return false;
}
// set color table
const CAMGChunk *camg = nullptr;
auto camgs = IFFChunk::searchT<CAMGChunk>(form);
if (!camgs.isEmpty()) {
camg = camgs.first();
}
const CMAPChunk *cmap = nullptr;
auto cmaps = IFFChunk::searchT<CMAPChunk>(form);
if (cmaps.isEmpty()) {
auto cmyks = IFFChunk::searchT<CMYKChunk>(form);
for (auto &&cmyk : cmyks)
cmaps.append(cmyk);
}
if (!cmaps.isEmpty()) {
cmap = cmaps.first();
}
if (img.format() == QImage::Format_Indexed8) {
if (cmap) {
auto halfbride = BODYChunk::safeModeId(header, camg, cmap) & CAMGChunk::ModeId::HalfBrite ? true : false;
img.setColorTable(cmap->palette(halfbride));
// resolution
auto dpis = IFFChunk::searchT<DPIChunk>(form);
if (!dpis.isEmpty()) {
auto &&dpi = dpis.first();
if (dpi->isValid()) {
img.setDotsPerMeterX(dpi->dotsPerMeterX());
img.setDotsPerMeterY(dpi->dotsPerMeterY());
}
}
// reading image data
auto ipal = form->searchIPal();
auto bodies = IFFChunk::searchT<BODYChunk>(form);
if (bodies.isEmpty()) {
auto abits = IFFChunk::searchT<ABITChunk>(form);
for (auto &&abit : abits)
bodies.append(abit);
// set color table
auto cmaps = IFFChunk::searchT<CMAPChunk>(form);
if (img.format() == QImage::Format_Indexed8) {
if (!cmaps.isEmpty())
if (auto &&cmap = cmaps.first())
img.setColorTable(cmap->palette());
}
auto bodies = IFFChunk::searchT<BODYChunk>(form);
if (bodies.isEmpty()) {
img.fill(0);
} else {
const CAMGChunk *camg = nullptr;
auto camgs = IFFChunk::searchT<CAMGChunk>(form);
if (!camgs.isEmpty()) {
camg = camgs.first();
}
const CMAPChunk *cmap = nullptr;
if (!cmaps.isEmpty())
cmap = cmaps.first();
auto &&body = bodies.first();
if (!body->resetStrideRead(device())) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage() error while reading image data";
@@ -341,7 +192,7 @@ bool IFFHandler::readStandardImage(QImage *image)
}
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, camg, cmap, ipal, form->formType());
auto ba = body->strideRead(device(), form, header, camg, cmap);
if (ba.isEmpty()) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage() error while reading image scanline";
return false;
@@ -350,12 +201,6 @@ bool IFFHandler::readStandardImage(QImage *image)
}
}
// BEAM / CTBL conversion (if not already done)
if (ipal && img.format() == QImage::Format_Indexed8) {
img = convertIPAL(img, ipal);
}
// set metadata (including image resolution)
addMetadata(img, form);
*image = img;
@@ -368,8 +213,7 @@ bool IFFHandler::readMayaImage(QImage *image)
if (forms.isEmpty()) {
return false;
}
auto cin = qBound(0, currentImageNumber(), int(forms.size() - 1));
auto &&form = forms.at(cin);
auto &&form = forms.first();
// show the first one (I don't have a sample with many images)
auto headers = IFFChunk::searchT<TBHDChunk>(form);
@@ -450,93 +294,47 @@ bool IFFHandler::supportsOption(ImageOption option) const
if (option == QImageIOHandler::ImageFormat) {
return true;
}
if (option == QImageIOHandler::ImageTransformation) {
return true;
}
return false;
}
QVariant IFFHandler::option(ImageOption option) const
{
if (!supportsOption(option)) {
return {};
}
const IFOR_Chunk *form = nullptr;
if (d->readStructure(device())) {
auto forms = d->searchForms<FORMChunk>();
auto for4s = d->searchForms<FOR4Chunk>();
auto cin = currentImageNumber();
if (!forms.isEmpty())
form = cin < forms.size() ? forms.at(cin) : forms.first();
else if (!for4s.isEmpty())
form = cin < for4s.size() ? for4s.at(cin) : for4s.first();
}
if (form == nullptr) {
return {};
}
QVariant v;
if (option == QImageIOHandler::Size) {
return QVariant::fromValue(form->size());
if (d->readStructure(device())) {
auto forms = d->searchForms<FORMChunk>();
if (!forms.isEmpty())
if (auto &&form = forms.first())
v = QVariant::fromValue(form->size());
auto for4s = d->searchForms<FOR4Chunk>();
if (!for4s.isEmpty())
if (auto &&form = for4s.first())
v = QVariant::fromValue(form->size());
}
}
if (option == QImageIOHandler::ImageFormat) {
return QVariant::fromValue(form->optionformat());
if (d->readStructure(device())) {
auto forms = d->searchForms<FORMChunk>();
if (!forms.isEmpty())
if (auto &&form = forms.first())
v = QVariant::fromValue(form->format());
auto for4s = d->searchForms<FOR4Chunk>();
if (!for4s.isEmpty())
if (auto &&form = for4s.first())
v = QVariant::fromValue(form->format());
}
}
if (option == QImageIOHandler::ImageTransformation) {
return QVariant::fromValue(form->transformation());
}
return {};
}
bool IFFHandler::jumpToNextImage()
{
return jumpToImage(d->m_imageNumber + 1);
}
bool IFFHandler::jumpToImage(int imageNumber)
{
if (imageNumber < 0 || imageNumber >= imageCount()) {
return false;
}
d->m_imageNumber = imageNumber;
return true;
}
int IFFHandler::imageCount() const
{
// NOTE: image count is cached for performance reason
auto &&count = d->m_imageCount;
if (count > 0) {
return count;
}
count = QImageIOHandler::imageCount();
if (!d->readStructure(device())) {
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::imageCount() invalid IFF structure";
return count;
}
auto forms = d->searchForms<FORMChunk>();
auto for4s = d->searchForms<FOR4Chunk>();
if (!forms.isEmpty())
count = forms.size();
else if (!for4s.isEmpty())
count = for4s.size();
return count;
}
int IFFHandler::currentImageNumber() const
{
return d->m_imageNumber;
return v;
}
QImageIOPlugin::Capabilities IFFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
{
if (format == "iff" || format == "ilbm" || format == "lbm") {
if (format == "iff" || format == "ilbm") {
return Capabilities(CanRead);
}
if (!format.isEmpty()) {

View File

@@ -1,4 +1,4 @@
{
"Keys": [ "iff", "iff", "lbm", "ilbm" ],
"MimeTypes": [ "application/x-iff", "image/x-ilbm", "image/x-ilbm", "image/x-ilbm" ]
"Keys": [ "iff", "ilbm" ],
"MimeTypes": [ "application/x-iff", "application/x-ilbm" ]
}

View File

@@ -23,11 +23,6 @@ public:
bool supportsOption(QImageIOHandler::ImageOption option) const override;
QVariant option(QImageIOHandler::ImageOption option) const override;
bool jumpToNextImage() override;
bool jumpToImage(int imageNumber) override;
int imageCount() const override;
int currentImageNumber() const override;
static bool canRead(QIODevice *device);
private:

View File

@@ -21,7 +21,7 @@
* The maximum size in pixel allowed by the plugin.
*/
#ifndef JP2_MAX_IMAGE_WIDTH
#define JP2_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT
#define JP2_MAX_IMAGE_WIDTH 300000
#endif
#ifndef JP2_MAX_IMAGE_HEIGHT
#define JP2_MAX_IMAGE_HEIGHT JP2_MAX_IMAGE_WIDTH

View File

@@ -74,16 +74,6 @@ Q_LOGGING_CATEGORY(LOG_JXRPLUGIN, "kf.imageformats.plugins.jxr", QtWarningMsg)
// #define JXR_ENABLE_ADVANCED_METADATA
/* *** JXR_MAX_IMAGE_WIDTH and JXR_MAX_IMAGE_HEIGHT ***
* The maximum size in pixel allowed by the plugin.
*/
#ifndef JXR_MAX_IMAGE_WIDTH
#define JXR_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT
#endif
#ifndef JXR_MAX_IMAGE_HEIGHT
#define JXR_MAX_IMAGE_HEIGHT JXR_MAX_IMAGE_WIDTH
#endif
#ifndef JXR_MAX_METADATA_SIZE
/*!
* XMP and EXIF maximum size.
@@ -396,11 +386,6 @@ public:
if (pDecoder) {
qint32 w, h;
pDecoder->GetSize(pDecoder, &w, &h);
if (w > JXR_MAX_IMAGE_WIDTH || h > JXR_MAX_IMAGE_HEIGHT || w < 1 || h < 1) {
qCCritical(LOG_JXRPLUGIN) << "JXRHandlerPrivate::imageSize() Maximum image size is limited to" << JXR_MAX_IMAGE_WIDTH << "x"
<< JXR_MAX_IMAGE_HEIGHT << "pixels";
return {};
}
return QSize(w, h);
}
return {};
@@ -1068,10 +1053,6 @@ bool JXRHandler::write(const QImage &image)
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::write() image too large: the image cannot exceed 4GB.";
return false;
}
if (image.width() > JXR_MAX_IMAGE_WIDTH || image.height() > JXR_MAX_IMAGE_HEIGHT) {
qCCritical(LOG_JXRPLUGIN) << "JXRHandler::write() Maximum image size is limited to" << JXR_MAX_IMAGE_WIDTH << "x" << JXR_MAX_IMAGE_HEIGHT << "pixels";
return {};
}
if (!d->initForWriting()) {
return false;

View File

@@ -1072,7 +1072,7 @@ QByteArray MicroExif::toByteArray(const QDataStream::ByteOrder &byteOrder, const
QByteArray ba;
{
QBuffer buf(&ba);
if (!write(&buf, byteOrder, version))
if (!write(&buf, byteOrder))
return {};
}
return ba;
@@ -1193,13 +1193,12 @@ void MicroExif::updateImageMetadata(QImage &targetImage, bool replaceExisting) c
}
}
bool MicroExif::updateImageResolution(QImage &targetImage)
void MicroExif::updateImageResolution(QImage &targetImage)
{
if (horizontalResolution() > 0)
targetImage.setDotsPerMeterX(qRound(horizontalResolution() / 25.4 * 1000));
if (verticalResolution() > 0)
targetImage.setDotsPerMeterY(qRound(verticalResolution() / 25.4 * 1000));
return (horizontalResolution() > 0) || (verticalResolution() > 0);
}
MicroExif MicroExif::fromByteArray(const QByteArray &ba, bool searchHeader)

View File

@@ -339,9 +339,8 @@ public:
* \brief updateImageResolution
* Helper to set the EXIF resolution to the image. Resolution is set only if valid.
* \param targetImage The image to set resolution on.
* \return True if either the x-resolution or the y-resolution has been changed, otherwise false.
*/
bool updateImageResolution(QImage &targetImage);
void updateImageResolution(QImage &targetImage);
/*!
* \brief fromByteArray

View File

@@ -6,7 +6,6 @@
*/
#include "pcx_p.h"
#include "scanlineconverter_p.h"
#include "util_p.h"
#include <QColor>
@@ -622,38 +621,30 @@ static bool writeLine(QDataStream &s, QByteArray &buf)
return (s.status() == QDataStream::Ok);
}
static bool writeImage1(const QImage &image, QDataStream &s, PCXHEADER &header)
static bool writeImage1(QImage &img, QDataStream &s, PCXHEADER &header)
{
auto tfmt = image.format();
if (tfmt != QImage::Format_Mono)
tfmt = QImage::Format_Mono;
if (image.isNull() || image.colorCount() < 1) {
if (img.format() != QImage::Format_Mono) {
img.convertTo(QImage::Format_Mono);
}
if (img.isNull() || img.colorCount() < 1) {
return false;
}
ScanLineConverter scl(tfmt);
if (image.height() > 0) {
scl.convertedScanLine(image, 0); // required to calculate bytesPerLine
}
auto rgb = img.color(0);
auto minIsBlack = (qRed(rgb) + qGreen(rgb) + qBlue(rgb)) / 3 < 127;
header.Bpp = 1;
header.NPlanes = 1;
header.BytesPerLine = scl.bytesPerLine();
if (header.BytesPerLine == 0) {
header.BytesPerLine = image.bytesPerLine();
}
header.BytesPerLine = img.bytesPerLine();
if (header.BytesPerLine == 0) {
return false;
}
s << header;
auto rgb = image.color(0);
auto minIsBlack = (qRed(rgb) + qGreen(rgb) + qBlue(rgb)) / 3 < 127;
QByteArray buf(header.BytesPerLine, 0);
for (int y = 0; y < header.height(); ++y) {
auto p = scl.convertedScanLine(image, y);
auto p = img.constScanLine(y);
// Invert as QImage uses reverse palette for monochrome images?
for (int i = 0; i < header.BytesPerLine; ++i) {
@@ -667,7 +658,7 @@ static bool writeImage1(const QImage &image, QDataStream &s, PCXHEADER &header)
return true;
}
static bool writeImage4(const QImage &image, QDataStream &s, PCXHEADER &header)
static bool writeImage4(QImage &img, QDataStream &s, PCXHEADER &header)
{
header.Bpp = 1;
header.NPlanes = 4;
@@ -677,7 +668,7 @@ static bool writeImage4(const QImage &image, QDataStream &s, PCXHEADER &header)
}
for (int i = 0; i < 16; ++i) {
header.ColorMap.setColor(i, image.color(i));
header.ColorMap.setColor(i, img.color(i));
}
s << header;
@@ -689,7 +680,7 @@ static bool writeImage4(const QImage &image, QDataStream &s, PCXHEADER &header)
}
for (int y = 0; y < header.height(); ++y) {
auto p = image.constScanLine(y);
auto p = img.constScanLine(y);
for (int i = 0; i < 4; ++i) {
buf[i].fill(0);
@@ -712,23 +703,18 @@ static bool writeImage4(const QImage &image, QDataStream &s, PCXHEADER &header)
return true;
}
static bool writeImage8(const QImage &image, QDataStream &s, PCXHEADER &header)
static bool writeImage8(QImage &img, QDataStream &s, PCXHEADER &header)
{
auto tfmt = image.format();
if (tfmt == QImage::Format_Grayscale16)
tfmt = QImage::Format_Grayscale8;
ScanLineConverter scl(tfmt);
if (image.height() > 0) {
scl.convertedScanLine(image, 0); // required to calculate bytesPerLine
if (img.format() == QImage::Format_Grayscale16) {
img.convertTo(QImage::Format_Grayscale8);
}
if (img.isNull()) {
return false;
}
header.Bpp = 8;
header.NPlanes = 1;
header.BytesPerLine = scl.bytesPerLine();
if (header.BytesPerLine == 0) {
header.BytesPerLine = image.bytesPerLine();
}
header.BytesPerLine = img.bytesPerLine();
if (header.BytesPerLine == 0) {
return false;
}
@@ -738,7 +724,7 @@ static bool writeImage8(const QImage &image, QDataStream &s, PCXHEADER &header)
QByteArray buf(header.BytesPerLine, 0);
for (int y = 0; y < header.height(); ++y) {
auto p = scl.convertedScanLine(image, y);
auto p = img.constScanLine(y);
for (int i = 0; i < header.BytesPerLine; ++i) {
buf[i] = p[i];
@@ -755,18 +741,18 @@ static bool writeImage8(const QImage &image, QDataStream &s, PCXHEADER &header)
// Write palette
for (int i = 0; i < 256; ++i) {
if (tfmt != QImage::Format_Indexed8)
if (img.format() != QImage::Format_Indexed8)
s << RGB::from(qRgb(i, i, i));
else
s << RGB::from(image.color(i));
s << RGB::from(img.color(i));
}
return (s.status() == QDataStream::Ok);
}
static bool writeImage24(const QImage &image, QDataStream &s, PCXHEADER &header)
static bool writeImage24(QImage &img, QDataStream &s, PCXHEADER &header)
{
auto hasAlpha = image.hasAlphaChannel();
auto hasAlpha = img.hasAlphaChannel();
header.Bpp = 8;
header.NPlanes = hasAlpha ? 4 : 3;
header.BytesPerLine = header.width();
@@ -774,17 +760,11 @@ static bool writeImage24(const QImage &image, QDataStream &s, PCXHEADER &header)
return false;
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
auto cs = image.colorSpace();
auto tcs = QColorSpace();
auto tfmt = image.format();
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) {
tcs = QColorSpace(QColorSpace::SRgb);
tfmt = QImage::Format_RGB32;
if (img.format() != QImage::Format_ARGB32 && img.format() != QImage::Format_RGB32) {
img.convertTo(hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32);
}
#endif
if (tfmt != QImage::Format_ARGB32 && tfmt != QImage::Format_RGB32) {
tfmt = hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32;
if (img.isNull()) {
return false;
}
s << header;
@@ -794,10 +774,8 @@ static bool writeImage24(const QImage &image, QDataStream &s, PCXHEADER &header)
QByteArray b_buf(header.width(), 0);
QByteArray a_buf(header.width(), char(0xFF));
ScanLineConverter scl(tfmt);
scl.setTargetColorSpace(tcs);
for (int y = 0; y < header.height(); ++y) {
auto p = reinterpret_cast<const QRgb *>(scl.convertedScanLine(image, y));
auto p = reinterpret_cast<const QRgb *>(img.constScanLine(y));
for (int x = 0; x < header.width(); ++x) {
auto &&rgb = p[x];
@@ -899,8 +877,16 @@ bool PCXHandler::write(const QImage &image)
QDataStream s(device());
s.setByteOrder(QDataStream::LittleEndian);
const int w = image.width();
const int h = image.height();
QImage img = image;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
auto cs = image.colorSpace();
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) {
img = image.convertedToColorSpace(QColorSpace(QColorSpace::SRgb));
}
#endif
const int w = img.width();
const int h = img.height();
if (w > 65536 || h > 65536) {
return false;
@@ -921,14 +907,14 @@ bool PCXHandler::write(const QImage &image)
header.PaletteInfo = 1;
auto ok = false;
if (image.depth() == 1) {
ok = writeImage1(image, s, header);
} else if (image.format() == QImage::Format_Indexed8 && image.colorCount() <= 16) {
ok = writeImage4(image, s, header);
} else if (image.depth() == 8 || image.format() == QImage::Format_Grayscale16) {
ok = writeImage8(image, s, header);
} else if (image.depth() >= 16) {
ok = writeImage24(image, s, header);
if (img.depth() == 1) {
ok = writeImage1(img, s, header);
} else if (img.format() == QImage::Format_Indexed8 && img.colorCount() <= 16) {
ok = writeImage4(img, s, header);
} else if (img.depth() == 8 || img.format() == QImage::Format_Grayscale16) {
ok = writeImage8(img, s, header);
} else if (img.depth() >= 16) {
ok = writeImage24(img, s, header);
}
return ok;

View File

@@ -22,16 +22,6 @@
Q_DECLARE_LOGGING_CATEGORY(LOG_PFMPLUGIN)
Q_LOGGING_CATEGORY(LOG_PFMPLUGIN, "kf.imageformats.plugins.pfm", QtWarningMsg)
/* *** PFM_MAX_IMAGE_WIDTH and PFM_MAX_IMAGE_HEIGHT ***
* The maximum size in pixel allowed by the plugin.
*/
#ifndef PFM_MAX_IMAGE_WIDTH
#define PFM_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT
#endif
#ifndef PFM_MAX_IMAGE_HEIGHT
#define PFM_MAX_IMAGE_HEIGHT PFM_MAX_IMAGE_WIDTH
#endif
class PFMHeader
{
private:
@@ -90,7 +80,7 @@ public:
bool isValid() const
{
return (m_width > 0 && m_height > 0 && m_width <= PFM_MAX_IMAGE_WIDTH && m_height <= PFM_MAX_IMAGE_HEIGHT);
return (m_width > 0 && m_height > 0);
}
bool isBlackAndWhite() const

View File

@@ -15,7 +15,6 @@
#include "pic_p.h"
#include "rle_p.h"
#include "scanlineconverter_p.h"
#include "util_p.h"
#include <QColorSpace>
@@ -264,20 +263,18 @@ bool SoftimagePICHandler::read(QImage *image)
return true;
}
bool SoftimagePICHandler::write(const QImage &image)
bool SoftimagePICHandler::write(const QImage &_image)
{
bool alpha = image.hasAlphaChannel();
bool alpha = _image.hasAlphaChannel();
QImage image;
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
auto cs = image.colorSpace();
auto tfmt = image.format();
auto tcs = QColorSpace();
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) {
tcs = QColorSpace(QColorSpace::SRgb);
tfmt = QImage::Format_RGB32;
auto cs = _image.colorSpace();
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && _image.format() == QImage::Format_CMYK8888) {
image = _image.convertedToColorSpace(QColorSpace(QColorSpace::SRgb), QImage::Format_RGB32);
}
#endif
if (tfmt != QImage::Format_ARGB32 && tfmt != QImage::Format_RGB32) {
tfmt = alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32;
if (image.isNull()) {
image = _image.convertToFormat(alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32);
}
if (image.width() < 0 || image.height() < 0) {
@@ -302,10 +299,8 @@ bool SoftimagePICHandler::write(const QImage &image)
}
stream << channels;
ScanLineConverter scl(tfmt);
scl.setTargetColorSpace(tcs);
for (int r = 0; r < image.height(); r++) {
const QRgb *row = reinterpret_cast<const QRgb *>(scl.convertedScanLine(image, r));
const QRgb *row = reinterpret_cast<const QRgb *>(image.constScanLine(r));
/* Write the RGB part of the scanline */
auto rgbEqual = [](QRgb p1, QRgb p2) -> bool {

View File

@@ -72,16 +72,6 @@ typedef quint8 uchar;
*/
// #define PSD_FORCE_RGBA
/* *** PSD_MAX_IMAGE_WIDTH and PSD_MAX_IMAGE_HEIGHT ***
* The maximum size in pixel allowed by the plugin.
*/
#ifndef PSD_MAX_IMAGE_WIDTH
#define PSD_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT
#endif
#ifndef PSD_MAX_IMAGE_HEIGHT
#define PSD_MAX_IMAGE_HEIGHT PSD_MAX_IMAGE_WIDTH
#endif
namespace // Private.
{
@@ -688,7 +678,7 @@ static bool IsValid(const PSDHeader &header)
qDebug() << "PSD header: invalid number of channels" << header.channel_count;
return false;
}
if (header.width > std::min(300000, PSD_MAX_IMAGE_WIDTH) || header.height > std::min(300000, PSD_MAX_IMAGE_HEIGHT)) {
if (header.width > 300000 || header.height > 300000) {
qDebug() << "PSD header: invalid image size" << header.width << "x" << header.height;
return false;
}

View File

@@ -15,16 +15,6 @@
#include <QIODevice>
#include <QImage>
/* *** QOI_MAX_IMAGE_WIDTH and QOI_MAX_IMAGE_HEIGHT ***
* The maximum size in pixel allowed by the plugin.
*/
#ifndef QOI_MAX_IMAGE_WIDTH
#define QOI_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT
#endif
#ifndef QOI_MAX_IMAGE_HEIGHT
#define QOI_MAX_IMAGE_HEIGHT QOI_MAX_IMAGE_WIDTH
#endif
namespace // Private
{
@@ -102,7 +92,7 @@ static bool IsSupported(const QoiHeader &head)
return false;
}
// Set a reasonable upper limit
if (head.Width > QOI_MAX_IMAGE_WIDTH || head.Height > QOI_MAX_IMAGE_HEIGHT) {
if (head.Width > 300000 || head.Height > 300000) {
return false;
}
return true;

View File

@@ -15,16 +15,6 @@
#include <QDebug>
#include <QImage>
/* *** RAS_MAX_IMAGE_WIDTH and RAS_MAX_IMAGE_HEIGHT ***
* The maximum size in pixel allowed by the plugin.
*/
#ifndef RAS_MAX_IMAGE_WIDTH
#define RAS_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT
#endif
#ifndef RAS_MAX_IMAGE_HEIGHT
#define RAS_MAX_IMAGE_HEIGHT RAS_MAX_IMAGE_WIDTH
#endif
namespace // Private.
{
// format info from http://www.fileformat.info/format/sunraster/egff.htm
@@ -94,10 +84,9 @@ static bool IsSupported(const RasHeader &head)
if (head.Depth != 1 && head.Depth != 8 && head.Depth != 24 && head.Depth != 32) {
return false;
}
if (head.Width == 0 || head.Height == 0 || head.Width > RAS_MAX_IMAGE_WIDTH || head.Height > RAS_MAX_IMAGE_HEIGHT) {
if (head.Width == 0 || head.Height == 0) {
return false;
}
// the Type field adds support for RLE(BGR), RGB and other encodings
// we support Type 1: Normal(BGR), Type 2: RLE(BGR) and Type 3: Normal(RGB) ONLY!
// TODO: add support for Type 4,5: TIFF/IFF
@@ -399,7 +388,7 @@ bool RASHandler::read(QImage *outImage)
s >> ras;
if (ras.ColorMapLength > kMaxQVectorSize) {
qWarning() << "read() unsupported image color map length in file header" << ras.ColorMapLength;
qWarning() << "LoadRAS() unsupported image color map length in file header" << ras.ColorMapLength;
return false;
}

View File

@@ -11,6 +11,7 @@
#include <QColorSpace>
#include <QDateTime>
#include <QDebug>
#include <QImage>
#include <QSet>
#include <QTimeZone>
@@ -21,24 +22,12 @@
#include <libraw/libraw.h>
#ifndef RAW_DISABLE_BROKEN_STREAM_PROTECTION
/* The LibRaw_QIODevice stream interrupt the reading of the RAW file if a
* burst of read errors are detected.
* By defining RAW_IGNORE_BROKEN_STREAMS removes this protection, leaving
* LibRaw in complete control of error handling (which is mostly absent).
*/
// #define RAW_DISABLE_BROKEN_STREAM_PROTECTION
#endif
#ifdef QT_DEBUG
/* This should be used to exclude the local QIODevice wrapper of the
* LibRaw_abstract_datastream interface. If the result changes then the problem
* is in the local wrapper and must be corrected.
* WARNING: using the LibRaw's streams instead of the local wrapper from a Qt
* program falls in the LOCALE bug when the RAW file needs the
* scanf_one() function (e.g. *.MOS files). See also raw_scanf_one().
*/
// #define EXCLUDE_LibRaw_QIODevice // Uncomment this code if you think that the problem is LibRaw_QIODevice (default commented)
// This should be used to exclude the local QIODevice wrapper of the LibRaw_abstract_datastream interface.
// If the result changes then the problem is in the local wrapper and must be corrected.
// WARNING: using the LibRaw's streams instead of the local wrapper from a Qt program falls in the LOCALE
// bug when the RAW file needs the scanf_one() function (e.g. *.MOS files). See also raw_scanf_one().
//#define EXCLUDE_LibRaw_QIODevice // Uncomment this code if you think that the problem is LibRaw_QIODevice (default commented)
#endif
namespace // Private.
@@ -106,46 +95,10 @@ inline int raw_scanf_one(const QByteArray &ba, const char *fmt, void *val)
*/
class LibRaw_QIODevice : public LibRaw_abstract_datastream
{
inline void incError()
{
// Normally, when read errors occur, a plugin exits with an error.
// Here, given how LibRaw works, we need to be a little more flexible:
// for example, a seek might fail when the library tries to figure out
// what it's reading. We therefore use a dynamic error counting system
// where the error counts heavily, but subsequent successful
// operations can mitigate it.
// If the stream is obviously corrupt, the plugin exits.
m_errorCount += 3;
}
inline void decError()
{
m_errorCount = std::max(m_errorCount - 1, 0);
}
void checkErrors() {
// Internally, LibRaw often doesn't check the return values of various
// functions. It's not uncommon to find things like:
//
// fseek(ifp, oAtom, SEEK_SET);
// szAtom = get4();
//
// which can cause the read to go into an infinite loop on corrupted
// files. I've set the error count to high to avoid these issues.
if (m_errorCount > 10) {
#ifndef RAW_DISABLE_BROKEN_STREAM_PROTECTION
// LibRaw stream classes can throw exceptions that are handled by
// libraw itself (e.g. see implementation of
// LibRaw_bigfile_datastream).
// Every LibRaw function that works on the stream used in this
// plugin is protected by a Try...Catch.
IOERROR(); // defined in libraw_datastream.h
#endif
}
}
public:
explicit LibRaw_QIODevice(QIODevice *device)
: m_device(device)
, m_errorCount(0)
{
m_device = device;
}
virtual ~LibRaw_QIODevice() override
{
@@ -156,22 +109,19 @@ public:
}
virtual int read(void *ptr, size_t sz, size_t nmemb) override
{
checkErrors();
qint64 read = 0;
if (sz == 0) {
return 0;
}
auto data = reinterpret_cast<char*>(ptr);
for (qint64 r = 0, size = sz * nmemb; read < size; read += r) {
r = m_device->read(data + read, size - read);
if (r < 1) {
if (read == 0 || r < 0) {
incError();
}
if (m_device->atEnd()) {
break;
}
r = m_device->read(data + read, size - read);
if (r < 1) {
break;
}
decError();
}
return int(read / sz);
}
@@ -181,21 +131,17 @@ public:
}
virtual int seek(INT64 o, int whence) override
{
checkErrors();
auto pos = o;
auto siz = size();
auto size = m_device->size();
if (whence == SEEK_CUR) {
pos = tell() + o;
pos = m_device->pos() + o;
}
if (whence == SEEK_END) {
pos = siz + o;
pos = size + o;
}
if (pos < 0 || pos > siz || m_device->isSequential()) {
incError();
if (pos < 0 || m_device->isSequential()) {
return -1;
}
decError();
return m_device->seek(pos) ? 0 : -1;
}
virtual INT64 tell() override
@@ -208,24 +154,17 @@ public:
}
virtual char *gets(char *s, int sz) override
{
checkErrors();
if (m_device->readLine(s, sz) > 0) {
decError();
return s;
}
incError();
return nullptr;
}
virtual int scanf_one(const char *fmt, void *val) override
{
checkErrors();
QByteArray ba;
for (int xcnt = 0; xcnt < 24 && !m_device->atEnd(); ++xcnt) {
char c;
if (!m_device->getChar(&c)) {
incError();
return EOF;
}
if (ba.isEmpty() && (c == ' ' || c == '\t')) {
@@ -236,19 +175,14 @@ public:
}
ba.append(c);
}
decError();
return raw_scanf_one(ba, fmt, val);
}
virtual int get_char() override
{
checkErrors();
unsigned char c;
if (!m_device->getChar(reinterpret_cast<char *>(&c))) {
incError();
return EOF;
}
decError();
return int(c);
}
#if (LIBRAW_VERSION < LIBRAW_MAKE_VERSION(0, 21, 0)) || defined(LIBRAW_OLD_VIDEO_SUPPORT)
@@ -260,8 +194,6 @@ public:
private:
QIODevice *m_device;
qint32 m_errorCount;
};
bool addTag(const QString &tag, QStringList &lines)
@@ -659,8 +591,8 @@ bool LoadTHUMB(QImageIOHandler *handler, QImage &img)
return false;
}
#else
auto all = device->readAll();
if (rawProcessor->open_buffer(all.data(), all.size()) != LIBRAW_SUCCESS) {
auto ba = device->readAll();
if (rawProcessor->open_buffer(ba.data(), ba.size()) != LIBRAW_SUCCESS) {
return false;
}
#endif

View File

@@ -20,7 +20,6 @@
*/
#include "rgb_p.h"
#include "scanlineconverter_p.h"
#include "util_p.h"
#include <cstring>
@@ -134,8 +133,8 @@ private:
bool writeHeader();
bool writeRle();
bool writeVerbatim(const QImage &, const QImage::Format&, const QColorSpace&);
bool scanData(const QImage &, const QImage::Format&, const QColorSpace&);
bool writeVerbatim(const QImage &);
bool scanData(const QImage &);
uint compact(uchar *, uchar *);
uchar intensity(uchar);
};
@@ -458,7 +457,7 @@ uint SGIImagePrivate::compact(uchar *d, uchar *s)
return dest - d;
}
bool SGIImagePrivate::scanData(const QImage &img, const QImage::Format &tfmt, const QColorSpace &tcs)
bool SGIImagePrivate::scanData(const QImage &img)
{
quint32 *start = _starttab;
QByteArray lineguard(_xsize * 2, 0);
@@ -470,8 +469,6 @@ bool SGIImagePrivate::scanData(const QImage &img, const QImage::Format &tfmt, co
unsigned y;
uint len;
ScanLineConverter scl(tfmt);
scl.setTargetColorSpace(tcs);
for (y = 0; y < _ysize; y++) {
const int yPos = _ysize - y - 1; // scanline doesn't do any sanity checking
if (yPos >= img.height()) {
@@ -479,7 +476,7 @@ bool SGIImagePrivate::scanData(const QImage &img, const QImage::Format &tfmt, co
return false;
}
c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(img, yPos));
c = reinterpret_cast<const QRgb *>(img.scanLine(yPos));
for (x = 0; x < _xsize; x++) {
buf[x] = intensity(qRed(*c++));
@@ -500,7 +497,7 @@ bool SGIImagePrivate::scanData(const QImage &img, const QImage::Format &tfmt, co
return false;
}
c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(img, yPos));
c = reinterpret_cast<const QRgb *>(img.scanLine(yPos));
for (x = 0; x < _xsize; x++) {
buf[x] = intensity(qGreen(*c++));
}
@@ -515,7 +512,7 @@ bool SGIImagePrivate::scanData(const QImage &img, const QImage::Format &tfmt, co
return false;
}
c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(img, yPos));
c = reinterpret_cast<const QRgb *>(img.scanLine(yPos));
for (x = 0; x < _xsize; x++) {
buf[x] = intensity(qBlue(*c++));
}
@@ -535,7 +532,7 @@ bool SGIImagePrivate::scanData(const QImage &img, const QImage::Format &tfmt, co
return false;
}
c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(img, yPos));
c = reinterpret_cast<const QRgb *>(img.scanLine(yPos));
for (x = 0; x < _xsize; x++) {
buf[x] = intensity(qAlpha(*c++));
}
@@ -686,7 +683,7 @@ bool SGIImagePrivate::writeRle()
return _stream.status() == QDataStream::Ok;
}
bool SGIImagePrivate::writeVerbatim(const QImage &img, const QImage::Format &tfmt, const QColorSpace &tcs)
bool SGIImagePrivate::writeVerbatim(const QImage &img)
{
_rle = 0;
if (!writeHeader()) {
@@ -697,15 +694,11 @@ bool SGIImagePrivate::writeVerbatim(const QImage &img, const QImage::Format &tfm
unsigned x;
unsigned y;
ScanLineConverter scl(tfmt);
scl.setTargetColorSpace(tcs);
QByteArray ba(_xsize, char());
for (y = 0; y < _ysize; y++) {
c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(img, _ysize - y - 1));
c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1));
for (x = 0; x < _xsize; x++) {
ba[x] = char(qRed(*c++));
_stream << quint8(qRed(*c++));
}
_stream.writeRawData(ba.data(), ba.size());
}
if (_zsize == 1) {
@@ -714,19 +707,17 @@ bool SGIImagePrivate::writeVerbatim(const QImage &img, const QImage::Format &tfm
if (_zsize != 2) {
for (y = 0; y < _ysize; y++) {
c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(img, _ysize - y - 1));
c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1));
for (x = 0; x < _xsize; x++) {
ba[x] = char(qGreen(*c++));
_stream << quint8(qGreen(*c++));
}
_stream.writeRawData(ba.data(), ba.size());
}
for (y = 0; y < _ysize; y++) {
c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(img, _ysize - y - 1));
c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1));
for (x = 0; x < _xsize; x++) {
ba[x] = char(qBlue(*c++));
_stream << quint8(qBlue(*c++));
}
_stream.writeRawData(ba.data(), ba.size());
}
if (_zsize == 3) {
@@ -735,11 +726,10 @@ bool SGIImagePrivate::writeVerbatim(const QImage &img, const QImage::Format &tfm
}
for (y = 0; y < _ysize; y++) {
c = reinterpret_cast<const QRgb *>(scl.convertedScanLine(img, _ysize - y - 1));
c = reinterpret_cast<const QRgb *>(img.scanLine(_ysize - y - 1));
for (x = 0; x < _xsize; x++) {
ba[x] = char(qAlpha(*c++));
_stream << quint8(qAlpha(*c++));
}
_stream.writeRawData(ba.data(), ba.size());
}
return _stream.status() == QDataStream::Ok;
@@ -748,35 +738,37 @@ bool SGIImagePrivate::writeVerbatim(const QImage &img, const QImage::Format &tfm
bool SGIImagePrivate::writeImage(const QImage &image)
{
// qDebug() << "writing "; // TODO add filename
if (image.allGray()) {
QImage img = image;
if (img.allGray()) {
_dim = 2, _zsize = 1;
} else {
_dim = 3, _zsize = 3;
}
auto hasAlpha = image.hasAlphaChannel();
auto hasAlpha = img.hasAlphaChannel();
if (hasAlpha) {
_dim = 3, _zsize++;
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
auto cs = image.colorSpace();
auto tcs = QColorSpace();
auto tfmt = image.format();
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) {
tcs = QColorSpace(QColorSpace::SRgb);
tfmt = QImage::Format_RGB32;
} else if (hasAlpha && tfmt != QImage::Format_ARGB32) {
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) {
img = image.convertedToColorSpace(QColorSpace(QColorSpace::SRgb), QImage::Format_RGB32);
} else if (hasAlpha && img.format() != QImage::Format_ARGB32) {
#else
if (hasAlpha && tfmt != QImage::Format_ARGB32) {
if (hasAlpha && img.format() != QImage::Format_ARGB32) {
#endif
tfmt = QImage::Format_ARGB32;
} else if (!hasAlpha && tfmt != QImage::Format_RGB32) {
tfmt = QImage::Format_RGB32;
img = img.convertToFormat(QImage::Format_ARGB32);
} else if (!hasAlpha && img.format() != QImage::Format_RGB32) {
img = img.convertToFormat(QImage::Format_RGB32);
}
if (img.isNull()) {
// qDebug() << "can't convert image to depth 32";
return false;
}
const int w = image.width();
const int h = image.height();
const int w = img.width();
const int h = img.height();
if (w > 65535 || h > 65535) {
return false;
@@ -793,7 +785,7 @@ bool SGIImagePrivate::writeImage(const QImage &image)
_starttab = new quint32[_numrows];
_rlemap.setBaseOffset(512 + _numrows * 2 * sizeof(quint32));
if (!scanData(image, tfmt, tcs)) {
if (!scanData(img)) {
// qDebug() << "this can't happen";
return false;
}
@@ -807,7 +799,7 @@ bool SGIImagePrivate::writeImage(const QImage &image)
}
if (verbatim_size <= rle_size) {
return writeVerbatim(image, tfmt, tcs);
return writeVerbatim(img);
}
return writeRle();
}

View File

@@ -17,17 +17,8 @@
#include <QImage>
#include <QLoggingCategory>
Q_LOGGING_CATEGORY(LOG_SCTPLUGIN, "kf.imageformats.plugins.scitex", QtWarningMsg)
/* *** SCT_MAX_IMAGE_WIDTH and SCT_MAX_IMAGE_HEIGHT ***
* The maximum size in pixel allowed by the plugin.
*/
#ifndef SCT_MAX_IMAGE_WIDTH
#define SCT_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT
#endif
#ifndef SCT_MAX_IMAGE_HEIGHT
#define SCT_MAX_IMAGE_HEIGHT SCT_MAX_IMAGE_WIDTH
#endif
Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
Q_LOGGING_CATEGORY(LOG_IFFPLUGIN, "kf.imageformats.plugins.scitex", QtWarningMsg)
#define CTRLBLOCK_SIZE 256
#define PRMSBLOCK_SIZE_CT 256
@@ -149,7 +140,7 @@ public:
return false;
}
// Set a reasonable upper limit
if (width() > SCT_MAX_IMAGE_WIDTH || height() > SCT_MAX_IMAGE_HEIGHT) {
if (width() > 300000 || height() > 300000) {
return false;
}
return m_cb.fileType() == QStringLiteral(FILETYPE_CT) && format() != QImage::Format_Invalid;

File diff suppressed because it is too large Load Diff

View File

@@ -22,19 +22,14 @@ public:
bool write(const QImage &image) override;
bool supportsOption(QImageIOHandler::ImageOption option) const override;
void setOption(ImageOption option, const QVariant &value) override;
QVariant option(QImageIOHandler::ImageOption option) const override;
static bool canRead(QIODevice *device);
private:
bool writeIndexed(const QImage &image);
bool writeGrayscale(const QImage &image);
bool writeRGB555(const QImage &image);
bool writeRGBA(const QImage &image);
bool writeMetadata(const QImage &image);
bool readMetadata(QImage &image);
bool writeRGBA(const QImage &image);
const QScopedPointer<TGAHandlerPrivate> d;
};

View File

@@ -13,11 +13,6 @@
#include <QImage>
#include <QImageIOHandler>
// Default maximum width and height for the large image plugins.
#ifndef KIF_LARGE_IMAGE_PIXEL_LIMIT
#define KIF_LARGE_IMAGE_PIXEL_LIMIT 300000
#endif
// Image metadata keys to use in plugins (so they are consistent)
#define META_KEY_ALTITUDE "Altitude"
#define META_KEY_AUTHOR "Author"

View File

@@ -26,16 +26,12 @@
#define USE_FLOAT_IMAGES // default uncommented
// Let's set a "reasonable" maximum size
#ifndef XCF_MAX_IMAGE_WIDTH
#define XCF_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT
#endif
#ifndef XCF_MAX_IMAGE_HEIGHT
#define XCF_MAX_IMAGE_HEIGHT XCF_MAX_IMAGE_WIDTH
#endif
#define MAX_IMAGE_WIDTH 300000
#define MAX_IMAGE_HEIGHT 300000
#else
// While it is possible to have images larger than 32767 pixels, QPainter seems unable to go beyond this threshold using Qt 5.
#define XCF_MAX_IMAGE_WIDTH 32767
#define XCF_MAX_IMAGE_HEIGHT 32767
#define MAX_IMAGE_WIDTH 32767
#define MAX_IMAGE_HEIGHT 32767
#endif
#ifdef USE_FLOAT_IMAGES
@@ -848,8 +844,8 @@ bool XCFImageFormat::readXCFHeader(QDataStream &xcf_io, XCFImage::Header *header
return false;
}
if (header->width > XCF_MAX_IMAGE_WIDTH || header->height > XCF_MAX_IMAGE_HEIGHT) {
qCWarning(XCFPLUGIN) << "The maximum image size is limited to" << XCF_MAX_IMAGE_WIDTH << "x" << XCF_MAX_IMAGE_HEIGHT << "px";
if (header->width > MAX_IMAGE_WIDTH || header->height > MAX_IMAGE_HEIGHT) {
qCWarning(XCFPLUGIN) << "The maximum image size is limited to" << MAX_IMAGE_WIDTH << "x" << MAX_IMAGE_HEIGHT << "px";
return false;
}
@@ -1368,8 +1364,8 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
qCWarning(XCFPLUGIN) << "On 32-bits programs the maximum layer size is limited to" << 16384 << "x" << 16384 << "px";
return false;
}
if (layer.width > XCF_MAX_IMAGE_WIDTH || layer.height > XCF_MAX_IMAGE_HEIGHT) {
qCWarning(XCFPLUGIN) << "The maximum layer size is limited to" << XCF_MAX_IMAGE_WIDTH << "x" << XCF_MAX_IMAGE_HEIGHT << "px";
if (layer.width > MAX_IMAGE_WIDTH || layer.height > MAX_IMAGE_HEIGHT) {
qCWarning(XCFPLUGIN) << "The maximum layer size is limited to" << MAX_IMAGE_WIDTH << "x" << MAX_IMAGE_HEIGHT << "px";
return false;
}
@@ -2797,7 +2793,8 @@ void XCFImageFormat::copyLayerToImage(XCFImage &xcf_image)
QPainter painter(&image);
painter.setOpacity(layer.opacity / 255.0);
painter.setCompositionMode(QPainter::CompositionMode_Source);
if (x + layer.x_offset < XCF_MAX_IMAGE_WIDTH && y + layer.y_offset < XCF_MAX_IMAGE_HEIGHT) {
if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
y + layer.y_offset < MAX_IMAGE_HEIGHT) {
painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]);
}
continue;
@@ -3199,7 +3196,8 @@ void XCFImageFormat::mergeLayerIntoImage(XCFImage &xcf_image)
qint32 x = qint32(i * TILE_WIDTH);
QImage &tile = layer.image_tiles[j][i];
if (x + layer.x_offset < XCF_MAX_IMAGE_WIDTH && y + layer.y_offset < XCF_MAX_IMAGE_HEIGHT) {
if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
y + layer.y_offset < MAX_IMAGE_HEIGHT) {
painter.drawImage(x + layer.x_offset, y + layer.y_offset, tile);
}
}
@@ -3250,7 +3248,8 @@ void XCFImageFormat::mergeLayerIntoImage(XCFImage &xcf_image)
QPainter painter(&image);
painter.setOpacity(layer.opacity / 255.0);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
if (x + layer.x_offset < XCF_MAX_IMAGE_WIDTH && y + layer.y_offset < XCF_MAX_IMAGE_HEIGHT) {
if (x + layer.x_offset < MAX_IMAGE_WIDTH &&
y + layer.y_offset < MAX_IMAGE_HEIGHT) {
painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]);
}
continue;