mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-06-03 17:08:08 -04:00
Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a33446f86a |
@ -7,5 +7,5 @@ Dependencies:
|
||||
Options:
|
||||
test-before-installing: True
|
||||
require-passing-tests-on: ['Linux', 'FreeBSD', 'Windows']
|
||||
cmake-options: "-DKIMAGEFORMATS_DDS=ON -DKIMAGEFORMATS_JXR=ON -DKIMAGEFORMATS_HEIF=ON"
|
||||
cmake-options: "-DKIMAGEFORMATS_DDS=ON -DKIMAGEFORMATS_JXR=ON"
|
||||
per-test-timeout: 90
|
||||
|
@ -1,11 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(KF_VERSION "6.15.0") # handled by release scripts
|
||||
set(KF_DEP_VERSION "6.14.0") # handled by release scripts
|
||||
set(KF_VERSION "6.12.0") # handled by release scripts
|
||||
set(KF_DEP_VERSION "6.12.0") # handled by release scripts
|
||||
project(KImageFormats VERSION ${KF_VERSION})
|
||||
|
||||
include(FeatureSummary)
|
||||
find_package(ECM 6.14.0 NO_MODULE)
|
||||
find_package(ECM 6.12.0 NO_MODULE)
|
||||
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules")
|
||||
feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
@ -21,7 +21,7 @@ include(ECMDeprecationSettings)
|
||||
include(CheckIncludeFiles)
|
||||
include(FindPkgConfig)
|
||||
|
||||
set(REQUIRED_QT_VERSION 6.7.0)
|
||||
set(REQUIRED_QT_VERSION 6.6.0)
|
||||
find_package(Qt6Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
|
||||
|
||||
find_package(KF6Archive ${KF_DEP_VERSION})
|
||||
@ -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.9.0
|
||||
KF 6.13.0
|
||||
QT 6.8.0
|
||||
KF 6.11.0
|
||||
)
|
||||
|
||||
add_subdirectory(src)
|
||||
@ -109,28 +109,6 @@ if (BUILD_TESTING)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF6ImageFormats")
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/KF6ImageFormatsConfigVersion.cmake"
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
configure_package_config_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/KF6ImageFormatsConfig.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/KF6ImageFormatsConfig.cmake"
|
||||
INSTALL_DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
|
||||
)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/KF6ImageFormatsConfig.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/KF6ImageFormatsConfigVersion.cmake"
|
||||
DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
|
||||
COMPONENT Devel
|
||||
)
|
||||
|
||||
include(ECMFeatureSummary)
|
||||
ecm_feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
|
@ -1,5 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2025 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
@PACKAGE_INIT@
|
||||
# empty, because this is plugins for Qt instead of a library to link against
|
@ -53,10 +53,8 @@ willing to sign the Qt Project contributor agreement, it may be better to
|
||||
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.
|
||||
|
||||
For more info about tests, see also [Autotests README](autotests/README.md).
|
||||
- Contain the test images needed to verify that the changes work correctly
|
||||
- Pass the tests successfully
|
||||
|
||||
## Duplicated Plugins
|
||||
|
||||
|
@ -122,12 +122,9 @@ if (LibHeif_FOUND)
|
||||
kimageformats_read_tests(FUZZ 1
|
||||
hej2
|
||||
)
|
||||
kimageformats_write_tests(FUZZ 1
|
||||
hej2-nodatacheck-lossless
|
||||
)
|
||||
endif()
|
||||
|
||||
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.19.6")
|
||||
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.19.0")
|
||||
kimageformats_read_tests(FUZZ 4
|
||||
avci
|
||||
)
|
||||
|
@ -1,253 +0,0 @@
|
||||
# Autotests
|
||||
|
||||
Automated testing for plugins to allow
|
||||
[`QImage`](https://doc.qt.io/qt-6/qimage.html) to support
|
||||
extra file formats.
|
||||
|
||||
## Introduction
|
||||
|
||||
The testing part is essential for the correct functioning of the plugins.
|
||||
There are generic read/write tests and specific tests for the plugins that
|
||||
require them.
|
||||
|
||||
## Read tests
|
||||
|
||||
The generic reading tests are contained in the `read` folder. Inside the
|
||||
`read` folder, there are one or more folders for each plugin to be tested.
|
||||
A plugin can support multiple image file types, and if you need different
|
||||
parameters for each type, you need to create a folder for each type supported
|
||||
by the plugin (see e.g. HEIF plugin). If all formats supported by the plugin
|
||||
do not require different parameters, a single folder approach is simpler (see
|
||||
e.g. PSD plugin).
|
||||
|
||||
The reading tests are mainly based on comparing the image read by the plugin
|
||||
with a template in a known and working format. For this reason, the template
|
||||
formats chosen are those distributed by the Qt project: PNG in the first
|
||||
instance and TIFF for image formats not supported by PNG (e.g. CMYK images).
|
||||
|
||||
Some image options such as `QImageIOHandler::Size`,
|
||||
`QImageIOHandler::ImageFormat` and `QImageIOHandler::ImageTransformation` are
|
||||
also checked. If supported by the plugin, the resulting image is checked to
|
||||
see if it is compatible with the option's specification.
|
||||
|
||||
Optionally, for each image, you can also create a JSON file to modify the test
|
||||
behavior and/or verify data other than image pixels, such as metadata.
|
||||
|
||||
Finally, two tests are run for each test case:
|
||||
- On a random access device: this test must not fail.
|
||||
- On a sequential access device: a plugin may not support sequential operation.
|
||||
In this case, the test is skipped. However, if an image is returned, the test
|
||||
must succeed.
|
||||
|
||||
### The readtest command
|
||||
|
||||
To start a test, run the `readtest` command with the format to test as an
|
||||
argument. The format is one of those supported by plugins and a folder with
|
||||
the name of the format must be present inside the `read` folder.
|
||||
Depending on the format, you can specify the following additional options.
|
||||
|
||||
- `--help`: Displays help on commandline options.
|
||||
- `--fuzz <max>`: The fuzziness. Used to add some deviation in ARGB data
|
||||
(nornally used on lossy codec).
|
||||
- `--perceptive-fuzz`: Used to scale dynamically the fuzziness based on
|
||||
the alpha channel value. This is useful on images with pre-multiplied and
|
||||
small alphas. Qt can use different roundings based on optimizations resulting
|
||||
in very different RGB values. Since the alpha is small visually there is no
|
||||
difference (so it is not considered an error).
|
||||
- `--skip-optional-tests`: Used to skip the optional test such as metadata
|
||||
and resolution tests.
|
||||
|
||||
Note that some tests may fail if the correct options are not used. The correct
|
||||
options for each test are defined in [CMakeLists.txt](CMakeLists.txt).
|
||||
See also [Add a test to CMakeLists.txt](#add-a-test-to-cmakeliststxt).
|
||||
|
||||
### Test image nomenclature
|
||||
|
||||
Each test consists of the image to test, the verification image(s) (template)
|
||||
and, optionally, the additional JSON file.
|
||||
To be a test, the names of these files must be the same (except for
|
||||
the extension). A test for a JXL image would be, for example, composed like
|
||||
this:
|
||||
- `testRGB.jxl`: The image to test.
|
||||
- `testRGB.jxl.json`: The test behavior modifier (note that it must contain
|
||||
the double extension).
|
||||
- `testRGB.png`: How the image should look (template). The template name
|
||||
can be different if specified in the JSON file.
|
||||
|
||||
Although there is no precise rule for the name of a test, it is good to have
|
||||
a name that is explanatory.
|
||||
|
||||
### JSON behavior file
|
||||
|
||||
The behavior file was initially introduced to solve compatibility issues
|
||||
between different versions of Qt supported by the framework. It was later
|
||||
extended to also check image metadata.
|
||||
|
||||
The JSON file consists of an array of JSON objects. The objects in the array
|
||||
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
|
||||
test.
|
||||
- `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.
|
||||
"testRGB_Qt_6_2.png".
|
||||
- `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
|
||||
with (if not set means all). E.g. "6.2.99".
|
||||
- `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
|
||||
with (if not set means all). E.g. "6.2.0".
|
||||
- `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
|
||||
`dotsPerMeterY` (integer) values of the image.
|
||||
- `seeAlso`: Type string. More info about the object. Normally used to point
|
||||
to bug reports. Not used by the test.
|
||||
- `unsupportedFormat`: Type `boolean`. When true, the test is skipped.
|
||||
|
||||
Some examples:
|
||||
- Example 1: [Runs only on Qt without alpha bug on float formats](read/jxl/testcard_rgba_fp16.jxl.json)
|
||||
- Example 2: [Rotation disabled](read/jxl/orientation6_notranfs.jxl.json)
|
||||
- Example 3: [Metadata](read/psd/metadata.psd.json)
|
||||
- Example 4: [Check Qt version, resolution and metadata](read/psd/mch-16bits.psd.json)
|
||||
- Example 5: [Fuzziness setting](read/xcf/birthday16.xcf.json)
|
||||
|
||||
These are just a few examples. More examples can be found in the test folders.
|
||||
|
||||
## Write tests
|
||||
|
||||
The generic writing tests are contained in the `write/basic` and
|
||||
`write/format` folders. Similar to the read tests, they verify the written
|
||||
(and then reread) image with a template.
|
||||
|
||||
The write test is composed of several phases:
|
||||
- Basic test: Uses the `write/basic` folder and checks the most common images
|
||||
and, optionally, metadata and resolution via a JSON properties file.
|
||||
- Format test: Uses the `write/format` folder and checks that all QImage image
|
||||
formats are written correctly.
|
||||
- Dimensional test: Uses the `write/format` folder and check images of
|
||||
different sizes (odd numbers, prime numbers, etc.) to verify internal
|
||||
alignments.
|
||||
- Null device test: Verify that there are no crashes if the device is null.
|
||||
|
||||
### The writetest command
|
||||
|
||||
To start a test, run the `writetest` command with the format to test as an
|
||||
argument. The format is one of those supported by plugins, a folder with
|
||||
the name of the format must be present inside the `write/format` folder and
|
||||
may need a template image in `write/basic`.
|
||||
Depending on the format, you can specify the following additional options.
|
||||
|
||||
- `--help`: Displays help on commandline options.
|
||||
- `--create-format-templates`: Create template images for all formats
|
||||
supported by the QImage in `write/format`. Command to simplify the creation of
|
||||
format test images when adding a new plugin or modifying an old one. This
|
||||
command is not intended to be used from the CMakeLists file as it must be used
|
||||
manually and the generated images must be verified one by one.
|
||||
- `--fuzz <max>`: The fuzziness. Used to add some deviation in ARGB data
|
||||
(nornally used on lossy codec).
|
||||
- `--lossless`: Check that reading back the data gives the same image.
|
||||
- `--no-data-check`: Don't check that write data is exactly the same.
|
||||
- `--skip-optional-tests`: Skip optional data tests (metadata, resolution,
|
||||
etc...).
|
||||
|
||||
Note that some tests may fail if the correct options are not used. The correct
|
||||
options for each test are defined in [CMakeLists.txt](CMakeLists.txt).
|
||||
See also [Add a test to CMakeLists.txt](#add-a-test-to-cmakeliststxt).
|
||||
|
||||
### JSON properties file
|
||||
|
||||
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)
|
||||
containing the image metadata as returned by `QImage::text`.
|
||||
- `resolution`: Type object. An object with the `dotsPerMeterX` and `
|
||||
dotsPerMeterY` (integer) values of the image.
|
||||
|
||||
[This is an example](write/basic/jxl.json) of property file.
|
||||
|
||||
|
||||
## Custom tests
|
||||
|
||||
If the generic read/write tests do not meet the requirements of a plugin,
|
||||
it is possible to write a custom test.
|
||||
In general it makes sense to write a dedicated test for a format if and
|
||||
only if you are testing unique features not present in other plugins.
|
||||
|
||||
### The PIC test
|
||||
|
||||
The PIC test is generated using Qt Test class. For more information
|
||||
see [Qt Test](https://doc.qt.io/qt-6/qttest-index.html).
|
||||
|
||||
### The ANI test
|
||||
|
||||
The ANI test is generated using Qt Test class. For more information
|
||||
see [Qt Test](https://doc.qt.io/qt-6/qttest-index.html).
|
||||
|
||||
|
||||
## Add a test to CMakeLists.txt
|
||||
|
||||
To add a test to CMake use the `kimageformats_read_tests` and
|
||||
`kimageformats_write_tests` functions. For example, to add the read
|
||||
tests for the PSD you just write `kimageformats_read_tests(psd)`.
|
||||
|
||||
It is also possible to pass command line arguments to the test by
|
||||
appropriately composing the string passed to the test functions.
|
||||
For boolean parameters you need to add a string starting with '-'
|
||||
after the image format. For example, to pass `--skip-optional-tests`
|
||||
to the PSD plugin write `kimageformats_read_tests(psd-skipoptional)`.
|
||||
|
||||
To add a fuzziness of 4, you must first set it as follows:
|
||||
`kimageformats_read_tests(FUZZ 4 psd-skipoptional)`.
|
||||
|
||||
The possible modifiers for `kimageformats_read_tests` are as follows:
|
||||
- `-skipoptional`: Add the `--skip-optional-tests` command line parameter.
|
||||
|
||||
The possible modifiers for `kimageformats_write_tests` are as follows:
|
||||
- `-lossless`: Add the `--lossless` command line parameter.
|
||||
- `-nodatacheck`: Add the `--no-data-check` command line parameter.
|
||||
- `-skipoptional`: Add the `--skip-optional-tests` command line parameter.
|
||||
|
||||
To set multiple parameters, you can enter multiple modifiers. For example:
|
||||
```
|
||||
kimageformats_write_tests(FUZZ 1
|
||||
hej2-nodatacheck-lossless
|
||||
)
|
||||
```
|
||||
|
||||
## OSS-Fuzz
|
||||
|
||||
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 [kimageformats
|
||||
project](https://github.com/google/oss-fuzz/tree/master/projects/kimageformats)
|
||||
on OSS-Fuzz.
|
||||
|
||||
## TODO
|
||||
|
||||
List of tests not implemented or only partially implemented.
|
||||
|
||||
### Color Profiles Test
|
||||
|
||||
Many plugins support color profiles via [`QColorSpace`](https://doc.qt.io/qt-6/qcolorspace.html).
|
||||
Checking for correct color management is increasingly necessary especially now
|
||||
that monitors are HDR and other than RGB color spaces have been added to Qt.
|
||||
|
||||
Furthermore, lossy plugins often have different color management behaviors
|
||||
depending on how the image is saved.
|
||||
|
||||
### Animations Test
|
||||
|
||||
Few plugins support animations. There are currently no plans for
|
||||
implementation.
|
@ -258,15 +258,6 @@ int main(int argc, char **argv)
|
||||
});
|
||||
QTextStream(stdout) << "QImageReader::supportedImageFormats: " << formatStrings.join(", ") << "\n";
|
||||
|
||||
if (!formats.contains(format)) {
|
||||
if (format == "avci" || format == "heif" || format == "hej2") {
|
||||
QTextStream(stdout) << "WARNING : " << suffix << " is not supported with current libheif configuration!\n"
|
||||
<< "********* "
|
||||
<< "Finished basic read tests for " << suffix << " images *********\n";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const QFileInfoList lstImgDir = imgdir.entryInfoList();
|
||||
// Launch 2 runs for each test: first run on a random access device, second run on a sequential access device
|
||||
for (int seq = 0; seq < 2; ++seq) {
|
||||
@ -332,12 +323,7 @@ int main(int argc, char **argv)
|
||||
OptionTest optionTest;
|
||||
if (!optionTest.store(&inputReader)) {
|
||||
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": error while reading options\n";
|
||||
if (format == "heif") {
|
||||
// libheif + ffmpeg decoder is unable to load all HEIF files.
|
||||
++skipped;
|
||||
} else {
|
||||
++failed;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
||||
},
|
||||
{
|
||||
"key" : "Description",
|
||||
"value" : "テレビ放送テスト映像。(TV broadcast test image.)"
|
||||
"value" : "TV broadcast test image."
|
||||
},
|
||||
{
|
||||
"key" : "Latitude",
|
||||
|
@ -31,7 +31,7 @@
|
||||
},
|
||||
{
|
||||
"key" : "Description",
|
||||
"value" : "テレビ放送テスト映像。(TV broadcast test image.)"
|
||||
"value" : "TV broadcast test image."
|
||||
},
|
||||
{
|
||||
"key" : "Latitude",
|
||||
|
@ -1,65 +0,0 @@
|
||||
{
|
||||
"format" : "hej2",
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "CreationDate",
|
||||
"value" : "2025-01-14T13:53:32+01:00"
|
||||
},
|
||||
{
|
||||
"key" : "Direction",
|
||||
"value" : "123.7"
|
||||
},
|
||||
{
|
||||
"key" : "ModificationDate",
|
||||
"value" : "2025-02-14T15:58:44+01:00"
|
||||
},
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@
|
||||
},
|
||||
{
|
||||
"key" : "Description",
|
||||
"value" : "テレビ放送テスト映像。(TV broadcast test image.)"
|
||||
"value" : "TV broadcast test image."
|
||||
},
|
||||
{
|
||||
"key" : "Latitude",
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -370,12 +370,7 @@ int formatTest(const QString &suffix, bool createTemplates)
|
||||
QBuffer buffer(&ba);
|
||||
auto writtenImage = QImageReader(&buffer, suffix.toLatin1()).read();
|
||||
if (writtenImage.isNull()) {
|
||||
if (suffix.toLatin1() == "heif") {
|
||||
// libheif + ffmpeg decoder is unable to load all HEIF files.
|
||||
++skipped;
|
||||
} else {
|
||||
++failed;
|
||||
}
|
||||
QTextStream(stdout) << "FAIL : error while reading the image " << formatName << "\n";
|
||||
continue;
|
||||
}
|
||||
@ -627,28 +622,8 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
auto suffix = args.at(0);
|
||||
|
||||
// skip test if libheif configuration is obviously incomplete
|
||||
QByteArray format = suffix.toLatin1();
|
||||
const QList<QByteArray> read_formats = QImageReader::supportedImageFormats();
|
||||
const QList<QByteArray> write_formats = QImageWriter::supportedImageFormats();
|
||||
|
||||
if (!read_formats.contains(format)) {
|
||||
if (format == "heif" || format == "hej2") {
|
||||
QTextStream(stdout) << "WARNING : libheif configuration is missing necessary decoder(s)!\n";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!write_formats.contains(format)) {
|
||||
if (format == "heif" || format == "hej2") {
|
||||
QTextStream(stdout) << "WARNING : libheif configuration is missing necessary encoder(s)!\n";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// run test
|
||||
auto suffix = args.at(0);
|
||||
auto ret = basicTest(suffix, parser.isSet(lossless), parser.isSet(ignoreDataCheck), parser.isSet(skipOptTest), fuzzarg);
|
||||
if (ret == 0) {
|
||||
ret = formatTest(suffix, parser.isSet(createFormatTempates));
|
||||
|
@ -15,16 +15,6 @@ function(kimageformats_add_plugin plugin)
|
||||
target_sources(${plugin} PRIVATE ${KIF_ADD_PLUGIN_SOURCES})
|
||||
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/imageformats)
|
||||
target_link_libraries(${plugin} PRIVATE Qt6::Gui)
|
||||
|
||||
if(ANDROID)
|
||||
# Plugins should be named with lib prefix on Android
|
||||
# Working name: libplugins_imageformats_kimg_avif_armeabi-v7a.so
|
||||
# Doesn't work: plugins_imageformats_kimg_avif_armeabi-v7a.so
|
||||
if(NOT ${CMAKE_SHARED_LIBRARY_PREFIX} STREQUAL "")
|
||||
set_target_properties(${plugin} PROPERTIES PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(TARGETS ${plugin} DESTINATION ${KDE_INSTALL_QTPLUGINDIR}/imageformats)
|
||||
endfunction()
|
||||
|
||||
|
@ -2557,5 +2557,3 @@ QImageIOHandler *QDDSPlugin::create(QIODevice *device, const QByteArray &format)
|
||||
handler->setFormat(format);
|
||||
return handler;
|
||||
}
|
||||
|
||||
#include "moc_dds_p.cpp"
|
||||
|
@ -16,8 +16,8 @@
|
||||
#include <QDebug>
|
||||
#include <QPointF>
|
||||
#include <QSysInfo>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef HEIF_MAX_METADATA_SIZE
|
||||
/*!
|
||||
@ -26,12 +26,12 @@
|
||||
#define HEIF_MAX_METADATA_SIZE (4 * 1024 * 1024)
|
||||
#endif
|
||||
|
||||
|
||||
size_t HEIFHandler::m_initialized_count = 0;
|
||||
bool HEIFHandler::m_plugins_queried = false;
|
||||
bool HEIFHandler::m_heif_decoder_available = false;
|
||||
bool HEIFHandler::m_heif_encoder_available = false;
|
||||
bool HEIFHandler::m_hej2_decoder_available = false;
|
||||
bool HEIFHandler::m_hej2_encoder_available = false;
|
||||
bool HEIFHandler::m_avci_decoder_available = false;
|
||||
|
||||
extern "C" {
|
||||
@ -155,14 +155,6 @@ bool HEIFHandler::write_helper(const QImage &image)
|
||||
break;
|
||||
}
|
||||
|
||||
heif_compression_format encoder_codec = heif_compression_HEVC;
|
||||
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
|
||||
if (format() == "hej2") {
|
||||
encoder_codec = heif_compression_JPEG2000;
|
||||
save_depth = 8; // for compatibility reasons
|
||||
}
|
||||
#endif
|
||||
|
||||
heif_chroma chroma;
|
||||
if (save_depth > 8) {
|
||||
if (save_alpha) {
|
||||
@ -295,7 +287,7 @@ bool HEIFHandler::write_helper(const QImage &image)
|
||||
}
|
||||
|
||||
struct heif_encoder *encoder = nullptr;
|
||||
err = heif_context_get_encoder_for_format(context, encoder_codec, &encoder);
|
||||
err = heif_context_get_encoder_for_format(context, heif_compression_HEVC, &encoder);
|
||||
if (err.code) {
|
||||
qWarning() << "Unable to get an encoder instance:" << err.message;
|
||||
heif_image_release(h_image);
|
||||
@ -562,7 +554,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
|
||||
QImage::Format target_image_format;
|
||||
|
||||
if (bit_depth == 10 || bit_depth == 12 || bit_depth == 16) {
|
||||
if (bit_depth == 10 || bit_depth == 12) {
|
||||
if (hasAlphaChannel) {
|
||||
chroma = (QSysInfo::ByteOrder == QSysInfo::LittleEndian) ? heif_chroma_interleaved_RRGGBBAA_LE : heif_chroma_interleaved_RRGGBBAA_BE;
|
||||
target_image_format = QImage::Format_RGBA64;
|
||||
@ -654,35 +646,6 @@ bool HEIFHandler::ensureDecoder()
|
||||
}
|
||||
|
||||
switch (bit_depth) {
|
||||
case 16:
|
||||
if (hasAlphaChannel) {
|
||||
for (int y = 0; y < imageHeight; y++) {
|
||||
memcpy(m_current_image.scanLine(y), src + (y * stride), 8 * size_t(imageWidth));
|
||||
}
|
||||
} else { // no alpha channel
|
||||
for (int y = 0; y < imageHeight; y++) {
|
||||
const uint16_t *src_word = reinterpret_cast<const uint16_t *>(src + (y * stride));
|
||||
uint16_t *dest_data = reinterpret_cast<uint16_t *>(m_current_image.scanLine(y));
|
||||
for (int x = 0; x < imageWidth; x++) {
|
||||
// R
|
||||
*dest_data = *src_word;
|
||||
src_word++;
|
||||
dest_data++;
|
||||
// G
|
||||
*dest_data = *src_word;
|
||||
src_word++;
|
||||
dest_data++;
|
||||
// B
|
||||
*dest_data = *src_word;
|
||||
src_word++;
|
||||
dest_data++;
|
||||
// X = 0xffff
|
||||
*dest_data = 0xffff;
|
||||
dest_data++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 12:
|
||||
if (hasAlphaChannel) {
|
||||
for (int y = 0; y < imageHeight; y++) {
|
||||
@ -1028,13 +991,6 @@ bool HEIFHandler::isHej2DecoderAvailable()
|
||||
return m_hej2_decoder_available;
|
||||
}
|
||||
|
||||
bool HEIFHandler::isHej2EncoderAvailable()
|
||||
{
|
||||
HEIFHandler::queryHeifLib();
|
||||
|
||||
return m_hej2_encoder_available;
|
||||
}
|
||||
|
||||
bool HEIFHandler::isAVCIDecoderAvailable()
|
||||
{
|
||||
HEIFHandler::queryHeifLib();
|
||||
@ -1057,9 +1013,8 @@ void HEIFHandler::queryHeifLib()
|
||||
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
|
||||
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
|
||||
m_hej2_decoder_available = heif_have_decoder_for_format(heif_compression_JPEG2000);
|
||||
m_hej2_encoder_available = heif_have_encoder_for_format(heif_compression_JPEG2000);
|
||||
#endif
|
||||
#if LIBHEIF_HAVE_VERSION(1, 19, 6)
|
||||
#if LIBHEIF_HAVE_VERSION(1, 19, 0)
|
||||
m_avci_decoder_available = heif_have_decoder_for_format(heif_compression_AVC);
|
||||
#endif
|
||||
m_plugins_queried = true;
|
||||
@ -1126,9 +1081,6 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q
|
||||
if (HEIFHandler::isHej2DecoderAvailable()) {
|
||||
format_cap |= CanRead;
|
||||
}
|
||||
if (HEIFHandler::isHej2EncoderAvailable()) {
|
||||
format_cap |= CanWrite;
|
||||
}
|
||||
return format_cap;
|
||||
}
|
||||
|
||||
@ -1158,7 +1110,7 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q
|
||||
}
|
||||
}
|
||||
|
||||
if (device->isWritable() && (HEIFHandler::isHeifEncoderAvailable() || HEIFHandler::isHej2EncoderAvailable())) {
|
||||
if (device->isWritable() && HEIFHandler::isHeifEncoderAvailable()) {
|
||||
cap |= CanWrite;
|
||||
}
|
||||
return cap;
|
||||
|
@ -31,7 +31,6 @@ public:
|
||||
static bool isHeifDecoderAvailable();
|
||||
static bool isHeifEncoderAvailable();
|
||||
static bool isHej2DecoderAvailable();
|
||||
static bool isHej2EncoderAvailable();
|
||||
static bool isAVCIDecoderAvailable();
|
||||
|
||||
static bool isSupportedBMFFType(const QByteArray &header);
|
||||
@ -63,7 +62,6 @@ private:
|
||||
static bool m_heif_decoder_available;
|
||||
static bool m_heif_encoder_available;
|
||||
static bool m_hej2_decoder_available;
|
||||
static bool m_hej2_encoder_available;
|
||||
static bool m_avci_decoder_available;
|
||||
|
||||
static QMutex &getHEIFHandlerMutex();
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include <string.h>
|
||||
|
||||
// Avoid rotation on buggy Qts (see also https://bugreports.qt.io/browse/QTBUG-126575)
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 3)
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 7) && QT_VERSION < QT_VERSION_CHECK(6, 6, 0)) || (QT_VERSION >= QT_VERSION_CHECK(6, 7, 3))
|
||||
#ifndef JXL_QT_AUTOTRANSFORM
|
||||
#define JXL_QT_AUTOTRANSFORM
|
||||
#endif
|
||||
|
@ -243,24 +243,6 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief initForReadingAndRollBack
|
||||
* Initialize the device for reading and rollback the device to start position.
|
||||
* \param device The source device.
|
||||
* \return True on success, otherwise false.
|
||||
*/
|
||||
bool initForReadingAndRollBack(QIODevice *device)
|
||||
{
|
||||
if (device) {
|
||||
device->startTransaction();
|
||||
}
|
||||
auto ok = initForReading(device);
|
||||
if (device) {
|
||||
device->rollbackTransaction();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief jxrFormat
|
||||
* \return The JXR format.
|
||||
@ -738,11 +720,11 @@ public:
|
||||
|
||||
auto exif = MicroExif::fromImage(image);
|
||||
if (!exif.isEmpty()) {
|
||||
auto exifIfd = exif.exifIfdByteArray(QDataStream::LittleEndian, MicroExif::V2);
|
||||
auto exifIfd = exif.exifIfdByteArray(QDataStream::LittleEndian);
|
||||
if (auto err = PKImageEncode_SetEXIFMetadata_WMP(pEncoder, reinterpret_cast<const quint8 *>(exifIfd.constData()), exifIfd.size())) {
|
||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::write() error while setting EXIF data:" << err;
|
||||
}
|
||||
auto gpsIfd = exif.gpsIfdByteArray(QDataStream::LittleEndian, MicroExif::V2);
|
||||
auto gpsIfd = exif.gpsIfdByteArray(QDataStream::LittleEndian);
|
||||
if (auto err = PKImageEncode_SetGPSInfoMetadata_WMP(pEncoder, reinterpret_cast<const quint8 *>(gpsIfd.constData()), gpsIfd.size())) {
|
||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::write() error while setting GPS data:" << err;
|
||||
}
|
||||
@ -1009,7 +991,7 @@ bool JXRHandler::read(QImage *outImage)
|
||||
return false;
|
||||
}
|
||||
for (qint32 y = 0, h = img.height(); y < h; ++y) {
|
||||
std::memcpy(img.scanLine(y), ba.data() + convStrideSize * y, (std::min)(convStrideSize, qint64(img.bytesPerLine())));
|
||||
std::memcpy(img.scanLine(y), ba.data() + convStrideSize * y, (std::min)(convStrideSize, img.bytesPerLine()));
|
||||
}
|
||||
}
|
||||
PKFormatConverter_Release(&pConverter);
|
||||
@ -1171,7 +1153,7 @@ QVariant JXRHandler::option(ImageOption option) const
|
||||
QVariant v;
|
||||
|
||||
if (option == QImageIOHandler::Size) {
|
||||
if (d->initForReadingAndRollBack(device())) {
|
||||
if (d->initForReading(device())) {
|
||||
auto size = d->imageSize();
|
||||
if (size.isValid()) {
|
||||
v = QVariant::fromValue(size);
|
||||
@ -1180,7 +1162,7 @@ QVariant JXRHandler::option(ImageOption option) const
|
||||
}
|
||||
|
||||
if (option == QImageIOHandler::ImageFormat) {
|
||||
if (d->initForReadingAndRollBack(device())) {
|
||||
if (d->initForReading(device())) {
|
||||
v = QVariant::fromValue(d->imageFormat());
|
||||
}
|
||||
}
|
||||
@ -1191,7 +1173,7 @@ QVariant JXRHandler::option(ImageOption option) const
|
||||
|
||||
if (option == QImageIOHandler::ImageTransformation) {
|
||||
// ignore result: I might want to read the value set in writing
|
||||
d->initForReadingAndRollBack(device());
|
||||
d->initForReading(device());
|
||||
v = int(d->transformation());
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QDataStream>
|
||||
#include <QHash>
|
||||
#include <QStringDecoder>
|
||||
#include <QTimeZone>
|
||||
|
||||
// TIFF 6 specs
|
||||
@ -112,17 +111,17 @@ static const KnownTags staticTagTypes = {
|
||||
TagInfo(TIFF_IMAGEWIDTH, ExifTagType::Long),
|
||||
TagInfo(TIFF_IMAGEHEIGHT, ExifTagType::Long),
|
||||
TagInfo(TIFF_BITSPERSAMPLE, ExifTagType::Short),
|
||||
TagInfo(TIFF_IMAGEDESCRIPTION, ExifTagType::Utf8),
|
||||
TagInfo(TIFF_MAKE, ExifTagType::Utf8),
|
||||
TagInfo(TIFF_MODEL, ExifTagType::Utf8),
|
||||
TagInfo(TIFF_IMAGEDESCRIPTION, ExifTagType::Ascii),
|
||||
TagInfo(TIFF_MAKE, ExifTagType::Ascii),
|
||||
TagInfo(TIFF_MODEL, ExifTagType::Ascii),
|
||||
TagInfo(TIFF_ORIENT, ExifTagType::Short),
|
||||
TagInfo(TIFF_XRES, ExifTagType::Rational),
|
||||
TagInfo(TIFF_YRES, ExifTagType::Rational),
|
||||
TagInfo(TIFF_URES, ExifTagType::Short),
|
||||
TagInfo(TIFF_SOFTWARE, ExifTagType::Utf8),
|
||||
TagInfo(TIFF_ARTIST, ExifTagType::Utf8),
|
||||
TagInfo(TIFF_SOFTWARE, ExifTagType::Ascii),
|
||||
TagInfo(TIFF_ARTIST, ExifTagType::Ascii),
|
||||
TagInfo(TIFF_DATETIME, ExifTagType::Ascii),
|
||||
TagInfo(TIFF_COPYRIGHT, ExifTagType::Utf8),
|
||||
TagInfo(TIFF_COPYRIGHT, ExifTagType::Ascii),
|
||||
TagInfo(EXIF_EXIFIFD, ExifTagType::Long),
|
||||
TagInfo(EXIF_GPSIFD, ExifTagType::Long),
|
||||
TagInfo(EXIF_DATETIMEORIGINAL, ExifTagType::Ascii),
|
||||
@ -135,10 +134,10 @@ static const KnownTags staticTagTypes = {
|
||||
TagInfo(EXIF_PIXELYDIM, ExifTagType::Long),
|
||||
TagInfo(EXIF_IMAGEUNIQUEID, ExifTagType::Ascii),
|
||||
TagInfo(EXIF_BODYSERIALNUMBER, ExifTagType::Ascii),
|
||||
TagInfo(EXIF_LENSMAKE, ExifTagType::Utf8),
|
||||
TagInfo(EXIF_LENSMODEL, ExifTagType::Utf8),
|
||||
TagInfo(EXIF_LENSMAKE, ExifTagType::Ascii),
|
||||
TagInfo(EXIF_LENSMODEL, ExifTagType::Ascii),
|
||||
TagInfo(EXIF_LENSSERIALNUMBER, ExifTagType::Ascii),
|
||||
TagInfo(EXIF_IMAGETITLE, ExifTagType::Utf8),
|
||||
TagInfo(EXIF_IMAGETITLE, ExifTagType::Ascii),
|
||||
TagInfo(EXIF_EXIFVERSION, ExifTagType::Undefined)
|
||||
};
|
||||
// clang-format on
|
||||
@ -368,28 +367,6 @@ static void writeData(QDataStream &ds, const QVariant &value, const ExifTagType&
|
||||
}
|
||||
}
|
||||
|
||||
static ExifTagType updateDataType(const ExifTagType &dataType, const QVariant &value, const MicroExif::Version &ver)
|
||||
{
|
||||
if (dataType != ExifTagType::Utf8)
|
||||
return dataType;
|
||||
|
||||
if (ver == MicroExif::V2)
|
||||
return ExifTagType::Ascii;
|
||||
|
||||
// Note that in EXIF specs, UTF-8 is backward compatible with ASCII: all UTF-8 tags can also be ASCII.
|
||||
// To maximize compatibility, I check if the string can be encoded in ASCII.
|
||||
auto txt = value.toString();
|
||||
|
||||
// Exif ASCII data type allow only values up to 127 (7-bit ASCII).
|
||||
auto u8 = txt.toUtf8();
|
||||
for (auto &&c : u8) {
|
||||
if (uchar(c) > 127)
|
||||
return dataType;
|
||||
}
|
||||
|
||||
return ExifTagType::Ascii;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief writeIfd
|
||||
* \param ds The stream.
|
||||
@ -398,12 +375,7 @@ static ExifTagType updateDataType(const ExifTagType &dataType, const QVariant &v
|
||||
* \param knownTags List of known and supported tags.
|
||||
* \return True on success, otherwise false.
|
||||
*/
|
||||
static bool writeIfd(QDataStream &ds,
|
||||
const MicroExif::Version &ver,
|
||||
const MicroExif::Tags &tags,
|
||||
TagPos &positions,
|
||||
quint32 pos = 0,
|
||||
const KnownTags &knownTags = staticTagTypes)
|
||||
static bool writeIfd(QDataStream &ds, const MicroExif::Tags &tags, TagPos &positions, quint32 pos = 0, const KnownTags &knownTags = staticTagTypes)
|
||||
{
|
||||
if (tags.isEmpty())
|
||||
return true;
|
||||
@ -418,7 +390,7 @@ static bool writeIfd(QDataStream &ds,
|
||||
continue;
|
||||
}
|
||||
auto value = tags.value(key);
|
||||
auto dataType = updateDataType(knownTags.value(key), value, ver);
|
||||
auto dataType = knownTags.value(key);
|
||||
auto count = countBytes(dataType, value);
|
||||
|
||||
ds << quint16(key);
|
||||
@ -440,8 +412,9 @@ static bool writeIfd(QDataStream &ds,
|
||||
if (!knownTags.contains(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto value = tags.value(key);
|
||||
auto dataType = updateDataType(knownTags.value(key), value, ver);
|
||||
auto dataType = knownTags.value(key);
|
||||
auto count = countBytes(dataType, value);
|
||||
auto valueSize = count * EXIF_TAG_SIZEOF(dataType);
|
||||
if (valueSize <= 4)
|
||||
@ -561,16 +534,8 @@ static bool readIfd(QDataStream &ds, MicroExif::Tags &tags, quint32 pos = 0, con
|
||||
|
||||
if (dataType == EXIF_TAG_DATATYPE(ExifTagType::Ascii) || dataType == EXIF_TAG_DATATYPE(ExifTagType::Utf8)) {
|
||||
auto l = readBytes(ds, count, true);
|
||||
if (!l.isEmpty()) {
|
||||
// It seems that converting to Latin 1 never detects errors so, using UTF-8.
|
||||
// Note that if the dataType is ASCII, by EXIF specification, it must use only the
|
||||
// first 128 values so the UTF-8 conversion is correct.
|
||||
auto dec = QStringDecoder(QStringDecoder::Utf8);
|
||||
// QStringDecoder raise an error only after converting to QString
|
||||
auto ut8 = QString(dec(l));
|
||||
// If there are errors in the conversion to UTF-8, then I try with latin1 (extended ASCII)
|
||||
tags.insert(tagId, dec.hasError() ? QString::fromLatin1(l) : ut8);
|
||||
}
|
||||
if (!l.isEmpty())
|
||||
tags.insert(tagId, dataType == EXIF_TAG_DATATYPE(ExifTagType::Utf8) ? QString::fromUtf8(l) : QString::fromLatin1(l));
|
||||
} else if (dataType == EXIF_TAG_DATATYPE(ExifTagType::Undefined)) {
|
||||
auto l = readBytes(ds, count, false);
|
||||
if (!l.isEmpty())
|
||||
@ -1067,7 +1032,7 @@ void MicroExif::setImageDirection(double degree, bool isMagnetic)
|
||||
m_gpsTags.insert(GPS_IMGDIRECTION, degree);
|
||||
}
|
||||
|
||||
QByteArray MicroExif::toByteArray(const QDataStream::ByteOrder &byteOrder, const Version &version) const
|
||||
QByteArray MicroExif::toByteArray(const QDataStream::ByteOrder &byteOrder) const
|
||||
{
|
||||
QByteArray ba;
|
||||
{
|
||||
@ -1078,16 +1043,16 @@ QByteArray MicroExif::toByteArray(const QDataStream::ByteOrder &byteOrder, const
|
||||
return ba;
|
||||
}
|
||||
|
||||
QByteArray MicroExif::exifIfdByteArray(const QDataStream::ByteOrder &byteOrder, const Version &version) const
|
||||
QByteArray MicroExif::exifIfdByteArray(const QDataStream::ByteOrder &byteOrder) const
|
||||
{
|
||||
QByteArray ba;
|
||||
{
|
||||
QDataStream ds(&ba, QIODevice::WriteOnly);
|
||||
ds.setByteOrder(byteOrder);
|
||||
auto exifTags = m_exifTags;
|
||||
exifTags.insert(EXIF_EXIFVERSION, version == Version::V3 ? QByteArray("0300") : QByteArray("0232"));
|
||||
exifTags.insert(EXIF_EXIFVERSION, QByteArray("0300"));
|
||||
TagPos positions;
|
||||
if (!writeIfd(ds, version, exifTags, positions))
|
||||
if (!writeIfd(ds, exifTags, positions))
|
||||
return {};
|
||||
}
|
||||
return ba;
|
||||
@ -1100,7 +1065,7 @@ bool MicroExif::setExifIfdByteArray(const QByteArray &ba, const QDataStream::Byt
|
||||
return readIfd(ds, m_exifTags, 0, staticTagTypes);
|
||||
}
|
||||
|
||||
QByteArray MicroExif::gpsIfdByteArray(const QDataStream::ByteOrder &byteOrder, const Version &version) const
|
||||
QByteArray MicroExif::gpsIfdByteArray(const QDataStream::ByteOrder &byteOrder) const
|
||||
{
|
||||
QByteArray ba;
|
||||
{
|
||||
@ -1109,7 +1074,7 @@ QByteArray MicroExif::gpsIfdByteArray(const QDataStream::ByteOrder &byteOrder, c
|
||||
auto gpsTags = m_gpsTags;
|
||||
gpsTags.insert(GPS_GPSVERSION, QByteArray("2400"));
|
||||
TagPos positions;
|
||||
if (!writeIfd(ds, version, gpsTags, positions, 0, staticGpsTagTypes))
|
||||
if (!writeIfd(ds, gpsTags, positions, 0, staticGpsTagTypes))
|
||||
return {};
|
||||
return ba;
|
||||
}
|
||||
@ -1122,7 +1087,7 @@ bool MicroExif::setGpsIfdByteArray(const QByteArray &ba, const QDataStream::Byte
|
||||
return readIfd(ds, m_gpsTags, 0, staticGpsTagTypes);
|
||||
}
|
||||
|
||||
bool MicroExif::write(QIODevice *device, const QDataStream::ByteOrder &byteOrder, const Version &version) const
|
||||
bool MicroExif::write(QIODevice *device, const QDataStream::ByteOrder &byteOrder) const
|
||||
{
|
||||
if (device == nullptr || device->isSequential() || isEmpty())
|
||||
return false;
|
||||
@ -1131,7 +1096,7 @@ bool MicroExif::write(QIODevice *device, const QDataStream::ByteOrder &byteOrder
|
||||
ds.setByteOrder(byteOrder);
|
||||
if (!writeHeader(ds))
|
||||
return false;
|
||||
if (!writeIfds(ds, version))
|
||||
if (!writeIfds(ds))
|
||||
return false;
|
||||
}
|
||||
device->close();
|
||||
@ -1362,30 +1327,30 @@ bool MicroExif::writeHeader(QDataStream &ds) const
|
||||
return ds.status() == QDataStream::Ok;
|
||||
}
|
||||
|
||||
bool MicroExif::writeIfds(QDataStream &ds, const Version &version) const
|
||||
bool MicroExif::writeIfds(QDataStream &ds) const
|
||||
{
|
||||
auto tiffTags = m_tiffTags;
|
||||
auto exifTags = m_exifTags;
|
||||
auto gpsTags = m_gpsTags;
|
||||
updateTags(tiffTags, exifTags, gpsTags, version);
|
||||
updateTags(tiffTags, exifTags, gpsTags);
|
||||
|
||||
TagPos positions;
|
||||
if (!writeIfd(ds, version, tiffTags, positions))
|
||||
if (!writeIfd(ds, tiffTags, positions))
|
||||
return false;
|
||||
if (!writeIfd(ds, version, exifTags, positions, positions.value(EXIF_EXIFIFD)))
|
||||
if (!writeIfd(ds, exifTags, positions, positions.value(EXIF_EXIFIFD)))
|
||||
return false;
|
||||
if (!writeIfd(ds, version, gpsTags, positions, positions.value(EXIF_GPSIFD), staticGpsTagTypes))
|
||||
if (!writeIfd(ds, gpsTags, positions, positions.value(EXIF_GPSIFD), staticGpsTagTypes))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MicroExif::updateTags(Tags &tiffTags, Tags &exifTags, Tags &gpsTags, const Version &version) const
|
||||
void MicroExif::updateTags(Tags &tiffTags, Tags &exifTags, Tags &gpsTags) const
|
||||
{
|
||||
if (exifTags.isEmpty()) {
|
||||
tiffTags.remove(EXIF_EXIFIFD);
|
||||
} else {
|
||||
tiffTags.insert(EXIF_EXIFIFD, quint32());
|
||||
exifTags.insert(EXIF_EXIFVERSION, version == Version::V3 ? QByteArray("0300") : QByteArray("0232"));
|
||||
exifTags.insert(EXIF_EXIFVERSION, QByteArray("0300"));
|
||||
}
|
||||
if (gpsTags.isEmpty()) {
|
||||
tiffTags.remove(EXIF_GPSIFD);
|
||||
|
@ -37,15 +37,6 @@ class MicroExif
|
||||
public:
|
||||
using Tags = QMap<quint16, QVariant>;
|
||||
|
||||
/*!
|
||||
* \brief The Version enum
|
||||
* Exif specs version used when writing.
|
||||
*/
|
||||
enum Version {
|
||||
V2, // V2.xx
|
||||
V3 // V3.xx, use of UTF-8 data type (default)
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief MicroExif
|
||||
* Constructs an empty class.
|
||||
@ -274,20 +265,18 @@ public:
|
||||
* - EXIF IFD
|
||||
* - GPS IFD
|
||||
* \param byteOrder Sets the serialization byte order for EXIF data.
|
||||
* \param version The EXIF specs version to use.
|
||||
* \return A byte array containing the serialized data.
|
||||
* \sa write
|
||||
*/
|
||||
QByteArray toByteArray(const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER, const Version &version = Version::V3) const;
|
||||
QByteArray toByteArray(const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER) const;
|
||||
|
||||
/*!
|
||||
* \brief exifIfdByteArray
|
||||
* Convert the EXIF IFD only to RAW data. Useful when you want to add EXIF data to an existing TIFF container.
|
||||
* \param byteOrder Sets the serialization byte order for the data.
|
||||
* \param version The EXIF specs version to use.
|
||||
* \return A byte array containing the serialized data.
|
||||
*/
|
||||
QByteArray exifIfdByteArray(const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER, const Version &version = Version::V3) const;
|
||||
QByteArray exifIfdByteArray(const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER) const;
|
||||
/*!
|
||||
* \brief setExifIfdByteArray
|
||||
* \param ba The RAW data of EXIF IFD.
|
||||
@ -300,10 +289,9 @@ public:
|
||||
* \brief gpsIfdByteArray
|
||||
* Convert the GPS IFD only to RAW data. Useful when you want to add GPS data to an existing TIFF container.
|
||||
* \param byteOrder Sets the serialization byte order for the data.
|
||||
* \param version The EXIF specs version to use.
|
||||
* \return A byte array containing the serialized data.
|
||||
*/
|
||||
QByteArray gpsIfdByteArray(const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER, const Version &version = Version::V3) const;
|
||||
QByteArray gpsIfdByteArray(const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER) const;
|
||||
/*!
|
||||
* \brief setGpsIfdByteArray
|
||||
* \param ba The RAW data of GPS IFD.
|
||||
@ -321,11 +309,10 @@ public:
|
||||
* - GPS IFD
|
||||
* \param device A random access device.
|
||||
* \param byteOrder Sets the serialization byte order for EXIF data.
|
||||
* \param version The EXIF specs version to use.
|
||||
* \return True on success, otherwise false.
|
||||
* \sa toByteArray
|
||||
*/
|
||||
bool write(QIODevice *device, const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER, const Version &version = Version::V3) const;
|
||||
bool write(QIODevice *device, const QDataStream::ByteOrder &byteOrder = EXIF_DEFAULT_BYTEORDER) const;
|
||||
|
||||
/*!
|
||||
* \brief updateImageMetadata
|
||||
@ -386,8 +373,8 @@ private:
|
||||
void setGpsString(quint16 tagId, const QString& s);
|
||||
QString gpsString(quint16 tagId) const;
|
||||
bool writeHeader(QDataStream &ds) const;
|
||||
bool writeIfds(QDataStream &ds, const Version &version) const;
|
||||
void updateTags(Tags &tiffTags, Tags &exifTags, Tags &gpsTags, const Version &version) const;
|
||||
bool writeIfds(QDataStream &ds) const;
|
||||
void updateTags(Tags &tiffTags, Tags &exifTags, Tags &gpsTags) const;
|
||||
|
||||
static void setString(Tags &tags, quint16 tagId, const QString &s);
|
||||
static QString string(const Tags &tags, quint16 tagId);
|
||||
|
@ -1504,18 +1504,7 @@ bool PSDHandler::read(QImage *image)
|
||||
img.setColorSpace(QColorSpace(QColorSpace::SRgb));
|
||||
#endif
|
||||
} else if (!setColorSpace(img, irs)) {
|
||||
// Float images are used by Photoshop as linear: if no color space
|
||||
// is present, a linear one should be chosen.
|
||||
if (header.color_mode == CM_RGB && header.depth == 32) {
|
||||
img.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
if (header.color_mode == CM_GRAYSCALE && header.depth == 32) {
|
||||
auto qs = QColorSpace(QPointF(0.3127, 0.3291), QColorSpace::TransferFunction::Linear);
|
||||
qs.setDescription(QStringLiteral("Linear grayscale"));
|
||||
img.setColorSpace(qs);
|
||||
}
|
||||
#endif
|
||||
// qDebug() << "No colorspace info set!";
|
||||
}
|
||||
|
||||
// XMP data
|
||||
|
@ -457,4 +457,3 @@ QImageIOHandler *ScitexPlugin::create(QIODevice *device, const QByteArray &forma
|
||||
return handler;
|
||||
}
|
||||
|
||||
#include "moc_sct_p.cpp"
|
||||
|
@ -962,7 +962,11 @@ bool XCFImageFormat::loadImageProperties(QDataStream &xcf_io, XCFImage &xcf_imag
|
||||
case PROP_PARASITES:
|
||||
while (!property.atEnd()) {
|
||||
char *tag;
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
|
||||
quint32 size;
|
||||
#else
|
||||
qint64 size;
|
||||
#endif
|
||||
|
||||
property.readBytes(tag, size);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user