Compare commits
70 Commits
v6.20.0-rc
...
v6.26.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84f28e8bc9 | ||
|
|
a936927ec1 | ||
|
|
191e5e6a69 | ||
|
|
51db11eefc | ||
|
|
bc398382ac | ||
|
|
d5e5012cfb | ||
|
|
1b3f32a332 | ||
|
|
7cf60da031 | ||
|
|
d160f268e7 | ||
|
|
276338199a | ||
|
|
742b5097f6 | ||
|
|
2d2ee68cc0 | ||
|
|
d15c3c679d | ||
|
|
eb896efea1 | ||
|
|
142ec14c81 | ||
|
|
38b8b70304 | ||
|
|
e28c48cfeb | ||
|
|
3c08226aec | ||
|
|
7c86ccaefb | ||
|
|
ec0610d5b0 | ||
|
|
ae279c55f4 | ||
|
|
836e0a53bb | ||
|
|
5eb09116b0 | ||
|
|
92368ca58f | ||
|
|
a91c7ef72f | ||
|
|
ea2a4aafab | ||
|
|
c254875780 | ||
|
|
f3de2e77c1 | ||
|
|
1ef779f370 | ||
|
|
169a874cba | ||
|
|
ebf77ccdf5 | ||
|
|
359cb039d2 | ||
|
|
f4b91d8a54 | ||
|
|
263b5a88e2 | ||
|
|
8d07f7db1b | ||
|
|
1c2210c100 | ||
|
|
b7b438f903 | ||
|
|
336b8906aa | ||
|
|
2c8a1ad6ff | ||
|
|
e0f1ba640a | ||
|
|
32773e5f0c | ||
|
|
2410e45614 | ||
|
|
99e4223393 | ||
|
|
8224c0099d | ||
|
|
8d7fb2c3fd | ||
|
|
3353809906 | ||
|
|
abf4d32858 | ||
|
|
6b1c52c55c | ||
|
|
e644ab997f | ||
|
|
1fb3363e7b | ||
|
|
19df8b03a8 | ||
|
|
3f1ee9f9d1 | ||
|
|
8c9a7e88e5 | ||
|
|
30308f3541 | ||
|
|
af4597f796 | ||
|
|
b8b9849268 | ||
|
|
db1cb8de1f | ||
|
|
956b86c4de | ||
|
|
282c277204 | ||
|
|
237f25fd20 | ||
|
|
5140567853 | ||
|
|
72a809bcfd | ||
|
|
731ac375bb | ||
|
|
8061500b79 | ||
|
|
472ff92b96 | ||
|
|
847510f109 | ||
|
|
a8ad7be1cd | ||
|
|
7202e77c74 | ||
|
|
379c3c1738 | ||
|
|
6519d2930e |
@@ -7,5 +7,5 @@ Dependencies:
|
|||||||
Options:
|
Options:
|
||||||
test-before-installing: True
|
test-before-installing: True
|
||||||
require-passing-tests-on: ['Linux', 'FreeBSD', 'Windows']
|
require-passing-tests-on: ['Linux', 'FreeBSD', 'Windows']
|
||||||
cmake-options: "-DKIMAGEFORMATS_DDS=ON -DKIMAGEFORMATS_JXR=ON -DKIMAGEFORMATS_HEIF=ON"
|
cmake-options: "-DKIMAGEFORMATS_WITH_KNOWN_CRASHES_JXR=ON -DKIMAGEFORMATS_HEIF=ON -DKIMAGEFORMATS_HEIF_TEST:STRING=OFF -DKIMAGEFORMATS_HEJ2_TEST:STRING=OFF -DKIMAGEFORMATS_AVCI_TEST:STRING=OFF"
|
||||||
per-test-timeout: 90
|
per-test-timeout: 90
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.29)
|
||||||
|
|
||||||
set(KF_VERSION "6.20.0") # handled by release scripts
|
set(KF_VERSION "6.26.0") # handled by release scripts
|
||||||
set(KF_DEP_VERSION "6.20.0") # handled by release scripts
|
set(KF_DEP_VERSION "6.26.0") # handled by release scripts
|
||||||
project(KImageFormats VERSION ${KF_VERSION})
|
project(KImageFormats VERSION ${KF_VERSION})
|
||||||
|
|
||||||
include(FeatureSummary)
|
include(FeatureSummary)
|
||||||
find_package(ECM 6.20.0 NO_MODULE)
|
find_package(ECM 6.26.0 NO_MODULE)
|
||||||
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules")
|
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules")
|
||||||
feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ include(ECMDeprecationSettings)
|
|||||||
include(CheckIncludeFiles)
|
include(CheckIncludeFiles)
|
||||||
include(FindPkgConfig)
|
include(FindPkgConfig)
|
||||||
|
|
||||||
set(REQUIRED_QT_VERSION 6.8.0)
|
set(REQUIRED_QT_VERSION 6.9.0)
|
||||||
find_package(Qt6Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
|
find_package(Qt6Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
|
||||||
|
|
||||||
find_package(KF6Archive ${KF_DEP_VERSION})
|
find_package(KF6Archive ${KF_DEP_VERSION})
|
||||||
@@ -65,6 +65,12 @@ set_package_properties(libavif PROPERTIES
|
|||||||
option(KIMAGEFORMATS_DDS "Enable plugin for DDS format" ON)
|
option(KIMAGEFORMATS_DDS "Enable plugin for DDS format" ON)
|
||||||
|
|
||||||
option(KIMAGEFORMATS_HEIF "Enable plugin for HEIF format" OFF)
|
option(KIMAGEFORMATS_HEIF "Enable plugin for HEIF format" OFF)
|
||||||
|
set(KIMAGEFORMATS_HEIF_TEST "ALL" CACHE STRING "Enable HEIF tests: OFF, READ_ONLY, ALL")
|
||||||
|
set_property(CACHE KIMAGEFORMATS_HEIF_TEST PROPERTY STRINGS "OFF" "READ_ONLY" "ALL")
|
||||||
|
set(KIMAGEFORMATS_HEJ2_TEST "ALL" CACHE STRING "Enable HEJ2 tests: OFF, READ_ONLY, ALL")
|
||||||
|
set_property(CACHE KIMAGEFORMATS_HEJ2_TEST PROPERTY STRINGS "OFF" "READ_ONLY" "ALL")
|
||||||
|
set(KIMAGEFORMATS_AVCI_TEST "ALL" CACHE STRING "Enable AVCI tests: OFF, ALL")
|
||||||
|
set_property(CACHE KIMAGEFORMATS_AVCI_TEST PROPERTY STRINGS "OFF" "ALL")
|
||||||
if(KIMAGEFORMATS_HEIF)
|
if(KIMAGEFORMATS_HEIF)
|
||||||
pkg_check_modules(LibHeif IMPORTED_TARGET libheif>=1.10.0)
|
pkg_check_modules(LibHeif IMPORTED_TARGET libheif>=1.10.0)
|
||||||
endif()
|
endif()
|
||||||
@@ -91,16 +97,17 @@ set_package_properties(LibRaw PROPERTIES
|
|||||||
PURPOSE "Required for the QImage plugin for RAW images"
|
PURPOSE "Required for the QImage plugin for RAW images"
|
||||||
)
|
)
|
||||||
|
|
||||||
# JXR plugin disabled by default due to security issues
|
# JXR plugin disabled by default due to security issues.
|
||||||
option(KIMAGEFORMATS_JXR "Enable plugin for JPEG XR format" OFF)
|
# You should not enable it unless you know what you are doing.
|
||||||
if(KIMAGEFORMATS_JXR)
|
option(KIMAGEFORMATS_WITH_KNOWN_CRASHES_JXR "Enable plugin for JPEG XR format" OFF)
|
||||||
|
if(KIMAGEFORMATS_WITH_KNOWN_CRASHES_JXR)
|
||||||
find_package(LibJXR)
|
find_package(LibJXR)
|
||||||
endif()
|
endif()
|
||||||
add_feature_info(LibJXR LibJXR_FOUND "required for the QImage plugin for JPEG XR images")
|
add_feature_info(LibJXR LibJXR_FOUND "required for the QImage plugin for JPEG XR images")
|
||||||
|
|
||||||
ecm_set_disabled_deprecation_versions(
|
ecm_set_disabled_deprecation_versions(
|
||||||
QT 6.10.0
|
QT 6.11.0
|
||||||
KF 6.19.0
|
KF 6.23.0
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|||||||
75
README.md
@@ -21,6 +21,7 @@ The following image formats have read-only support:
|
|||||||
- Krita (kra)
|
- Krita (kra)
|
||||||
- OpenRaster (ora)
|
- OpenRaster (ora)
|
||||||
- Pixar raster (pxr)
|
- Pixar raster (pxr)
|
||||||
|
- PlayStation graphics (tim)
|
||||||
- Portable FloatMap/HalfMap (pfm, phm)
|
- Portable FloatMap/HalfMap (pfm, phm)
|
||||||
- Photoshop documents (psd, psb, pdd, psdt)
|
- Photoshop documents (psd, psb, pdd, psdt)
|
||||||
- Radiance HDR (hdr)
|
- Radiance HDR (hdr)
|
||||||
@@ -154,8 +155,31 @@ About the image:
|
|||||||
- `Owner`: Name of the owner of the image.
|
- `Owner`: Name of the owner of the image.
|
||||||
- `Software`: Name and version number of the software package(s) used to
|
- `Software`: Name and version number of the software package(s) used to
|
||||||
create the image.
|
create the image.
|
||||||
|
- `Speed`: Floating-point number indicating the speed of GPS receiver
|
||||||
|
movement in Km/h (e.g. 30.2).
|
||||||
- `Title`: The title of the image.
|
- `Title`: The title of the image.
|
||||||
|
|
||||||
|
About the shot:
|
||||||
|
- `DigitalZoomRatio`: Floating-point number indicating the digital zoom ratio
|
||||||
|
when the image was shot.
|
||||||
|
- `ExposureMode`: Integer number indicating the exposure mode set when the
|
||||||
|
image was shot as reported in the EXIF specifications.
|
||||||
|
- `ExposureProgram`: Integer number indicating the class of the program used
|
||||||
|
by the camera to set exposure when the picture is taken as reported in the
|
||||||
|
EXIF specifications.
|
||||||
|
- `ExposureTime`: Floating-point number indicating the exposure time,
|
||||||
|
given in seconds (s).
|
||||||
|
- `Flash`: Integer number indicating the status of flash when the image
|
||||||
|
was shot as reported in the EXIF specifications.
|
||||||
|
- `FNumber`: Floating-point number indicating the F number.
|
||||||
|
- `FocalLength`: Floating-point number indicating the actual focal length
|
||||||
|
of the lens, in millimeters (mm).
|
||||||
|
- `ISOSpeedRatings`: Integer number indicating the sensitivity of the camera
|
||||||
|
or input device when the image was shot as reported in the EXIF
|
||||||
|
specifications.
|
||||||
|
- `WhiteBalance`: Integer number indicating the white balance mode set when
|
||||||
|
the image was shot as reported in the EXIF specifications.
|
||||||
|
|
||||||
About the camera:
|
About the camera:
|
||||||
- `Manufacturer`: The manufacturer of the recording equipment.
|
- `Manufacturer`: The manufacturer of the recording equipment.
|
||||||
- `Model`: The model name or model number of the recording equipment.
|
- `Model`: The model name or model number of the recording equipment.
|
||||||
@@ -250,6 +274,7 @@ limit depends on the format encoding).
|
|||||||
- RAW: 65,535 x 65,535 pixels
|
- RAW: 65,535 x 65,535 pixels
|
||||||
- RGB: 65,535 x 65,535 pixels
|
- RGB: 65,535 x 65,535 pixels
|
||||||
- SCT: 300,000 x 300,000 pixels
|
- SCT: 300,000 x 300,000 pixels
|
||||||
|
- TIM: 65,535 x 65,535 pixels
|
||||||
- TGA: 65,535 x 65,535 pixels
|
- TGA: 65,535 x 65,535 pixels
|
||||||
- XCF: 300,000 x 300,000 pixels
|
- XCF: 300,000 x 300,000 pixels
|
||||||
|
|
||||||
@@ -290,9 +315,14 @@ in your cmake options.**
|
|||||||
|
|
||||||
The following defines can be defined in cmake to modify the behavior of the
|
The following defines can be defined in cmake to modify the behavior of the
|
||||||
plugin:
|
plugin:
|
||||||
- `DDS_DISABLE_STRIDE_ALIGNMENT`: disable the stride aligment based on DDS
|
- `DDS_DISABLE_STRIDE_ALIGNMENT`: disable the stride alignment based on DDS
|
||||||
pitch: it is known that some writers do not set it correctly.
|
pitch: it is known that some writers do not set it correctly.
|
||||||
|
|
||||||
|
When writing, it is possible to set which pixel format to use by setting the
|
||||||
|
subtypes. The default is `Automatic` which chooses the most appropriate format
|
||||||
|
based on the image. For a complete list of subformats, please use the
|
||||||
|
appropriate [`QImageWriter`](https://doc.qt.io/qt-6/qimagewriter.html) APIs.
|
||||||
|
|
||||||
### The HEIF plugin
|
### The HEIF plugin
|
||||||
|
|
||||||
**This plugin is disabled by default. It can be enabled by settings
|
**This plugin is disabled by default. It can be enabled by settings
|
||||||
@@ -303,6 +333,15 @@ distributions. In particular, it is necessary that the HEIF library has
|
|||||||
support for HEVC codec. If HEVC codec is not available the plugin
|
support for HEVC codec. If HEVC codec is not available the plugin
|
||||||
will compile but will fail the tests.
|
will compile but will fail the tests.
|
||||||
|
|
||||||
|
**If you are interested in compiling the plugin without running the tests,
|
||||||
|
also use the following string options:**
|
||||||
|
- `KIMAGEFORMATS_HEIF_TEST` to change the behaviour of HEIF tests. Set to
|
||||||
|
`"OFF"` (no test at all) or `"READ_ONLY"` (run read tests only).
|
||||||
|
- `KIMAGEFORMATS_HEJ2_TEST` to change the behaviour of HEJ2 tests. Set to
|
||||||
|
`"OFF"` (no test at all) or `"READ_ONLY"` (run read tests only)..
|
||||||
|
- `KIMAGEFORMATS_AVCI_TEST` to change the behaviour of AVCI tests. Set to
|
||||||
|
`"OFF"` (no test at all).
|
||||||
|
|
||||||
### The EXR plugin
|
### The EXR plugin
|
||||||
|
|
||||||
The following defines can be defined in cmake to modify the behavior of the
|
The following defines can be defined in cmake to modify the behavior of the
|
||||||
@@ -313,6 +352,10 @@ plugin:
|
|||||||
attribute named "xmp". Note that Gimp reads the "xmp" attribute and Darktable
|
attribute named "xmp". Note that Gimp reads the "xmp" attribute and Darktable
|
||||||
writes it as well.
|
writes it as well.
|
||||||
|
|
||||||
|
The plugin can set the following additional metadata:
|
||||||
|
- `EXRLayerName`: A string containing the name of the EXR layer used to decode
|
||||||
|
the image.
|
||||||
|
|
||||||
### The EPS plugin
|
### The EPS plugin
|
||||||
|
|
||||||
The plugin uses `Ghostscript` to convert the raster image. When reading it
|
The plugin uses `Ghostscript` to convert the raster image. When reading it
|
||||||
@@ -348,9 +391,16 @@ The plugin supports the following image data:
|
|||||||
type 4.
|
type 4.
|
||||||
- FORM PBM: PBM is a chunky version of IFF pictures. It supports 8-bit images
|
- FORM PBM: PBM is a chunky version of IFF pictures. It supports 8-bit images
|
||||||
with color map only.
|
with color map only.
|
||||||
|
- FORM IMAG (Compact Disc-Interactive): It supports CLut4, CLut7, CLut8, Rle7
|
||||||
|
and DYuv formats.
|
||||||
|
- FORM RGFX: It supports uncompressed images only.
|
||||||
- FOR4 CIMG (Maya Image File Format): It supports 24/48-bit RGB and 32/64-bit
|
- FOR4 CIMG (Maya Image File Format): It supports 24/48-bit RGB and 32/64-bit
|
||||||
RGBA images.
|
RGBA images.
|
||||||
|
|
||||||
|
> [!note]
|
||||||
|
> The plugin only supports the IFF, ILBM, and LBM file extensions. You'll
|
||||||
|
> need to rename files with different extensions to open them.
|
||||||
|
|
||||||
### The JP2 plugin
|
### The JP2 plugin
|
||||||
|
|
||||||
**This plugin can be disabled by setting `KIMAGEFORMATS_JP2` to `OFF`
|
**This plugin can be disabled by setting `KIMAGEFORMATS_JP2` to `OFF`
|
||||||
@@ -361,6 +411,11 @@ JP2 plugin has the following limitations due to the lack of support by OpenJPEG:
|
|||||||
- Image resolution is not supported.
|
- Image resolution is not supported.
|
||||||
- To write ICC profiles you need OpenJPEG V2.5.4 or higher
|
- To write ICC profiles you need OpenJPEG V2.5.4 or higher
|
||||||
|
|
||||||
|
When writing, it is possible to set which format to use by setting the
|
||||||
|
following subtypes:
|
||||||
|
- `JP2` (default): Save data using the JP2 container.
|
||||||
|
- `J2K`: Save only the compressed codestream.
|
||||||
|
|
||||||
### The JXL plugin
|
### The JXL plugin
|
||||||
|
|
||||||
**The current version of the plugin limits the image size to 256 megapixels
|
**The current version of the plugin limits the image size to 256 megapixels
|
||||||
@@ -376,13 +431,18 @@ plugin:
|
|||||||
### The JXR plugin
|
### The JXR plugin
|
||||||
|
|
||||||
**This plugin is disabled by default. It can be enabled by settings
|
**This plugin is disabled by default. It can be enabled by settings
|
||||||
`KIMAGEFORMATS_JXR` to `ON` in your cmake options.**
|
`KIMAGEFORMATS_WITH_KNOWN_CRASHES_JXR` to `ON` in your cmake options.**
|
||||||
|
|
||||||
|
> [!caution]
|
||||||
|
> The plugin disabled by default due to security issues in [jxrlib](https://github.com/4creators/jxrlib):
|
||||||
|
> the upstream jxrlib is dead and there is no "hope" they will fix the issues.
|
||||||
|
> **You should not enable it unless you know what you are doing.**
|
||||||
|
|
||||||
The following defines can be defined in cmake to modify the behavior of the
|
The following defines can be defined in cmake to modify the behavior of the
|
||||||
plugin:
|
plugin:
|
||||||
- `JXR_DENY_FLOAT_IMAGE`: disables the use of float images and consequently
|
- `JXR_DENY_FLOAT_IMAGE`: disables the use of float images and consequently
|
||||||
any HDR data will be lost.
|
any HDR data will be lost.
|
||||||
- `JXR_DISABLE_DEPTH_CONVERSION`: remove the neeeds of additional memory by
|
- `JXR_DISABLE_DEPTH_CONVERSION`: remove the needs of additional memory by
|
||||||
disabling the conversion between different color depths (e.g. RGBA64bpp to
|
disabling the conversion between different color depths (e.g. RGBA64bpp to
|
||||||
RGBA32bpp) at the cost of reduced compatibility.
|
RGBA32bpp) at the cost of reduced compatibility.
|
||||||
- `JXR_DISABLE_BGRA_HACK`: Windows displays and opens JXR files correctly out
|
- `JXR_DISABLE_BGRA_HACK`: Windows displays and opens JXR files correctly out
|
||||||
@@ -414,6 +474,8 @@ PSD support has the following limitations:
|
|||||||
- Multichannel images are treated as CMYK if they have 2 or more channels.
|
- Multichannel images are treated as CMYK if they have 2 or more channels.
|
||||||
- Multichannel images are treated as Grayscale if they have 1 channel.
|
- Multichannel images are treated as Grayscale if they have 1 channel.
|
||||||
- Duotone images are treated as grayscale images.
|
- Duotone images are treated as grayscale images.
|
||||||
|
- Grayscale images with alpha channel or at 32 bit depth are converted to
|
||||||
|
RGBA due to the lack of the appropriate Qt grayscale container.
|
||||||
- Extra channels other than alpha are discarded.
|
- Extra channels other than alpha are discarded.
|
||||||
|
|
||||||
The following defines can be defined in cmake to modify the behavior of the
|
The following defines can be defined in cmake to modify the behavior of the
|
||||||
@@ -423,6 +485,13 @@ plugin:
|
|||||||
- `PSD_NATIVE_CMYK_SUPPORT_DISABLED`: disable native support for CMYK images
|
- `PSD_NATIVE_CMYK_SUPPORT_DISABLED`: disable native support for CMYK images
|
||||||
when compiled with Qt 6.8+
|
when compiled with Qt 6.8+
|
||||||
|
|
||||||
|
The plugin can set the following additional metadata:
|
||||||
|
- `PSDDuotoneOptions`: Byte array in hexadecimal format of color data of the
|
||||||
|
duotone specification (the format of which is not documented). From the PSD
|
||||||
|
specification: *"Other applications that read Photoshop files can treat a
|
||||||
|
duotone image as a gray image, and just preserve the contents of the duotone
|
||||||
|
information when reading and writing the file."*
|
||||||
|
|
||||||
### The RAW plugin
|
### The RAW plugin
|
||||||
|
|
||||||
Loading RAW images always requires a conversion. To allow the user to
|
Loading RAW images always requires a conversion. To allow the user to
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ kimageformats_read_tests(
|
|||||||
ras
|
ras
|
||||||
rgb
|
rgb
|
||||||
sct
|
sct
|
||||||
|
tim
|
||||||
tga
|
tga
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -102,36 +103,52 @@ if (KF6Archive_FOUND)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (TARGET avif)
|
if (TARGET avif)
|
||||||
kimageformats_read_tests(
|
if(${libavif_VERSION} VERSION_GREATER_EQUAL "1.2.0")
|
||||||
avif
|
kimageformats_read_tests(
|
||||||
)
|
avif
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
message(WARNING "libavif ${libavif_VERSION} doesn't support decoding new AVIF files!\n"
|
||||||
|
"AVIF read tests are disabled, consider updating libavif.")
|
||||||
|
endif()
|
||||||
|
|
||||||
kimageformats_write_tests(
|
kimageformats_write_tests(
|
||||||
avif-nodatacheck-lossless
|
avif-nodatacheck-lossless
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (LibHeif_FOUND)
|
if (LibHeif_FOUND)
|
||||||
kimageformats_read_tests(
|
if (KIMAGEFORMATS_HEIF_TEST STREQUAL "ALL" OR KIMAGEFORMATS_HEIF_TEST STREQUAL "READ_ONLY")
|
||||||
heif
|
kimageformats_read_tests(
|
||||||
)
|
heif
|
||||||
# because the plug-ins use RGB->YUV conversion which sometimes results in 1 value difference.
|
|
||||||
kimageformats_write_tests(FUZZ 1
|
|
||||||
heif-nodatacheck-lossless
|
|
||||||
)
|
|
||||||
|
|
||||||
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.17.0")
|
|
||||||
kimageformats_read_tests(FUZZ 1
|
|
||||||
hej2
|
|
||||||
)
|
)
|
||||||
|
endif()
|
||||||
|
if (KIMAGEFORMATS_HEIF_TEST STREQUAL "ALL")
|
||||||
|
# because the plug-ins use RGB->YUV conversion which sometimes results in 1 value difference.
|
||||||
kimageformats_write_tests(FUZZ 1
|
kimageformats_write_tests(FUZZ 1
|
||||||
hej2-nodatacheck-lossless
|
heif-nodatacheck-lossless
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.17.0")
|
||||||
|
if (KIMAGEFORMATS_HEJ2_TEST STREQUAL "ALL" OR KIMAGEFORMATS_HEJ2_TEST STREQUAL "READ_ONLY")
|
||||||
|
kimageformats_read_tests(FUZZ 1
|
||||||
|
hej2
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
if (KIMAGEFORMATS_HEJ2_TEST STREQUAL "ALL")
|
||||||
|
kimageformats_write_tests(FUZZ 1
|
||||||
|
hej2-nodatacheck-lossless
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.19.6")
|
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.19.6")
|
||||||
kimageformats_read_tests(FUZZ 4
|
if (KIMAGEFORMATS_AVCI_TEST STREQUAL "ALL")
|
||||||
avci
|
kimageformats_read_tests(FUZZ 4
|
||||||
)
|
avci
|
||||||
|
)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ Depending on the format, you can specify the following additional options.
|
|||||||
|
|
||||||
- `--help`: Displays help on commandline options.
|
- `--help`: Displays help on commandline options.
|
||||||
- `--fuzz <max>`: The fuzziness. Used to add some deviation in ARGB data
|
- `--fuzz <max>`: The fuzziness. Used to add some deviation in ARGB data
|
||||||
(nornally used on lossy codec).
|
(normally used on lossy codec).
|
||||||
- `--perceptive-fuzz`: Used to scale dynamically the fuzziness based on
|
- `--perceptive-fuzz`: Used to scale dynamically the fuzziness based on
|
||||||
the alpha channel value. This is useful on images with pre-multiplied and
|
the alpha channel value. This is useful on images with pre-multiplied and
|
||||||
small alphas. Qt can use different roundings based on optimizations resulting
|
small alphas. Qt can use different roundings based on optimizations resulting
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ private Q_SLOTS:
|
|||||||
QCOMPARE(reader.text(QStringLiteral("Author")), QStringLiteral("KDE Community"));
|
QCOMPARE(reader.text(QStringLiteral("Author")), QStringLiteral("KDE Community"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void textRead()
|
void testRead()
|
||||||
{
|
{
|
||||||
QImageReader reader(QFINDTESTDATA("ani/test.ani"));
|
QImageReader reader(QFINDTESTDATA("ani/test.ani"));
|
||||||
QVERIFY(reader.canRead());
|
QVERIFY(reader.canRead());
|
||||||
|
|||||||
@@ -18,6 +18,11 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
################################################################################
|
################################################################################
|
||||||
|
LDFLAGS=""
|
||||||
|
if [[ $FUZZING_ENGINE == "afl" ]]; then
|
||||||
|
LDFLAGS="-fuse-ld=lld"
|
||||||
|
fi
|
||||||
|
export LDFLAGS
|
||||||
|
|
||||||
# build zstd
|
# build zstd
|
||||||
cd $SRC/zstd
|
cd $SRC/zstd
|
||||||
@@ -175,6 +180,7 @@ HANDLER_TYPES="ANIHandler ani
|
|||||||
RAWHandler raw
|
RAWHandler raw
|
||||||
RGBHandler rgb
|
RGBHandler rgb
|
||||||
ScitexHandler sct
|
ScitexHandler sct
|
||||||
|
TIMHandler tim
|
||||||
TGAHandler tga
|
TGAHandler tga
|
||||||
XCFHandler xcf"
|
XCFHandler xcf"
|
||||||
|
|
||||||
@@ -185,7 +191,7 @@ echo "$HANDLER_TYPES" | while read class format; do
|
|||||||
/usr/libexec/moc $SRC/kimageformats/src/imageformats/$format.cpp -o $format.moc
|
/usr/libexec/moc $SRC/kimageformats/src/imageformats/$format.cpp -o $format.moc
|
||||||
header=`ls $SRC/kimageformats/src/imageformats/$format*.h`
|
header=`ls $SRC/kimageformats/src/imageformats/$format*.h`
|
||||||
/usr/libexec/moc $header -o moc_`basename $header .h`.cpp
|
/usr/libexec/moc $header -o moc_`basename $header .h`.cpp
|
||||||
$CXX $CXXFLAGS -fPIC -DHANDLER=$class -std=c++17 autotests/ossfuzz/kimgio_fuzzer.cc $SRC/kimageformats/src/imageformats/$format.cpp $SRC/kimageformats/src/imageformats/scanlineconverter.cpp $SRC/kimageformats/src/imageformats/microexif.cpp $SRC/kimageformats/src/imageformats/chunks.cpp -o $OUT/$fuzz_target_name -DJXL_STATIC_DEFINE -DJXL_THREADS_STATIC_DEFINE -DJXL_CMS_STATIC_DEFINE -DINITGUID -I $SRC/kimageformats/src/imageformats/ -I $SRC/libavif/include/ -I $SRC/libjxl/build/lib/include/ -I $SRC/libjxl/lib/include/ -I /usr/local/include/OpenEXR/ -I /usr/local/include/KF6/KArchive/ -I /usr/local/include/openjpeg-2.5 -I /usr/local/include/Imath -I $SRC/jxrlib/common/include -I $SRC/jxrlib/jxrgluelib -I $SRC/jxrlib/image/sys -I /usr/include/QtCore/ -I /usr/include/QtGui/ -I . $SRC/libavif/build/libavif.a /usr/local/lib/libheif.a /usr/local/lib/libde265.a /usr/local/lib/libopenh264.a $SRC/aom/build.libavif/libaom.a $SRC/libjxl/build/lib/libjxl_threads.a $SRC/libjxl/build/lib/libjxl.a $SRC/libjxl/build/lib/libjxl_cms.a $SRC/libjxl/build/third_party/highway/libhwy.a $SRC/libjxl/build/third_party/brotli/libbrotlidec.a $SRC/libjxl/build/third_party/brotli/libbrotlienc.a $SRC/libjxl/build/third_party/brotli/libbrotlicommon.a -lQt6Gui -lQt6Core -lQt6BundledLibpng -lQt6BundledHarfbuzz -lm -lQt6BundledPcre2 -ldl -lpthread $LIB_FUZZING_ENGINE /usr/local/lib/libz.a -lKF6Archive /usr/local/lib/libz.a /usr/local/lib/libraw.a /usr/local/lib/libOpenEXR-3_3.a /usr/local/lib/libIex-3_3.a /usr/local/lib/libImath-3_1.a /usr/local/lib/libIlmThread-3_3.a /usr/local/lib/libOpenEXRCore-3_3.a /usr/local/lib/libOpenEXRUtil-3_3.a /usr/local/lib/libopenjp2.a /usr/local/lib/libzstd.a $SRC/jxrlib/build/libjxrglue.a $SRC/jxrlib/build/libjpegxr.a -llzma /usr/local/lib/libbz2.a -lclang_rt.builtins
|
$CXX $CXXFLAGS $LDFLAGS -fPIC -DHANDLER=$class -std=c++17 autotests/ossfuzz/kimgio_fuzzer.cc $SRC/kimageformats/src/imageformats/$format.cpp $SRC/kimageformats/src/imageformats/scanlineconverter.cpp $SRC/kimageformats/src/imageformats/microexif.cpp $SRC/kimageformats/src/imageformats/chunks.cpp -o $OUT/$fuzz_target_name -DJXL_STATIC_DEFINE -DJXL_THREADS_STATIC_DEFINE -DJXL_CMS_STATIC_DEFINE -DINITGUID -I $SRC/kimageformats/src/imageformats/ -I $SRC/libavif/include/ -I $SRC/libjxl/build/lib/include/ -I $SRC/libjxl/lib/include/ -I /usr/local/include/OpenEXR/ -I /usr/local/include/KF6/KArchive/ -I /usr/local/include/openjpeg-2.5 -I /usr/local/include/Imath -I $SRC/jxrlib/common/include -I $SRC/jxrlib/jxrgluelib -I $SRC/jxrlib/image/sys -I /usr/include/QtCore/ -I /usr/include/QtGui/ -I . $SRC/libavif/build/libavif.a /usr/local/lib/libheif.a /usr/local/lib/libde265.a /usr/local/lib/libopenh264.a $SRC/aom/build.libavif/libaom.a $SRC/libjxl/build/lib/libjxl_threads.a $SRC/libjxl/build/lib/libjxl.a $SRC/libjxl/build/lib/libjxl_cms.a $SRC/libjxl/build/third_party/highway/libhwy.a $SRC/libjxl/build/third_party/brotli/libbrotlidec.a $SRC/libjxl/build/third_party/brotli/libbrotlienc.a $SRC/libjxl/build/third_party/brotli/libbrotlicommon.a -lQt6Gui -lQt6Core -lQt6BundledLibpng -lQt6BundledHarfbuzz -lm -lQt6BundledPcre2 -ldl -lpthread $LIB_FUZZING_ENGINE /usr/local/lib/libz.a /usr/local/lib/x86_64-linux-gnu/libKF6Archive.a /usr/local/lib/libz.a /usr/local/lib/libraw.a /usr/local/lib/libOpenEXR-3_3.a /usr/local/lib/libIex-3_3.a /usr/local/lib/libImath-3_1.a /usr/local/lib/libIlmThread-3_3.a /usr/local/lib/libOpenEXRCore-3_3.a /usr/local/lib/libOpenEXRUtil-3_3.a /usr/local/lib/libopenjp2.a /usr/local/lib/libzstd.a $SRC/jxrlib/build/libjxrglue.a $SRC/jxrlib/build/libjpegxr.a /usr/local/lib/liblzma.a /usr/local/lib/libbz2.a -lclang_rt.builtins
|
||||||
|
|
||||||
# -lclang_rt.builtins in the previous line is a temporary workaround to avoid a linker error "undefined reference to __truncsfhf2". Investigate why this is needed here, but not anywhere else, and possibly remove it.
|
# -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.
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
Usage:
|
Usage:
|
||||||
python infra/helper.py build_image kimageformats
|
python infra/helper.py build_image kimageformats
|
||||||
python infra/helper.py build_fuzzers --sanitizer undefined|address|memory kimageformats
|
python infra/helper.py build_fuzzers --sanitizer undefined|address|memory kimageformats
|
||||||
python infra/helper.py run_fuzzer kimageformats kimgio_[ani|avif|dds|exr|hdr|heif|iff|jp2|jxl|jxr|kra|ora|pcx|pfm|pic|psd|pxr|qoi|ras|raw|rgb|sct|tga|xcf]_fuzzer
|
python infra/helper.py run_fuzzer kimageformats kimgio_[ani|avif|dds|exr|hdr|heif|iff|jp2|jxl|jxr|kra|ora|pcx|pfm|pic|psd|pxr|qoi|ras|raw|rgb|sct|tim|tga|xcf]_fuzzer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
@@ -52,6 +52,7 @@
|
|||||||
#include "raw_p.h"
|
#include "raw_p.h"
|
||||||
#include "rgb_p.h"
|
#include "rgb_p.h"
|
||||||
#include "sct_p.h"
|
#include "sct_p.h"
|
||||||
|
#include "tim_p.h"
|
||||||
#include "tga_p.h"
|
#include "tga_p.h"
|
||||||
#include "xcf_p.h"
|
#include "xcf_p.h"
|
||||||
|
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ git clone --depth 1 -b master https://invent.kde.org/frameworks/extra-cmake-modu
|
|||||||
git clone --depth 1 --branch=dev git://code.qt.io/qt/qtbase.git
|
git clone --depth 1 --branch=dev git://code.qt.io/qt/qtbase.git
|
||||||
git clone --depth 1 --branch=dev git://code.qt.io/qt/qttools.git
|
git clone --depth 1 --branch=dev git://code.qt.io/qt/qttools.git
|
||||||
git clone --depth 1 -b master https://invent.kde.org/frameworks/karchive.git
|
git clone --depth 1 -b master https://invent.kde.org/frameworks/karchive.git
|
||||||
git clone --depth 1 -b v3.12.0 https://aomedia.googlesource.com/aom
|
git clone --depth 1 -b v3.13.1 https://aomedia.googlesource.com/aom
|
||||||
git clone --depth 1 -b v1.2.1 https://github.com/AOMediaCodec/libavif.git
|
git clone --depth 1 -b v1.3.0 https://github.com/AOMediaCodec/libavif.git
|
||||||
git clone --depth 1 https://github.com/strukturag/libde265.git
|
git clone --depth 1 https://github.com/strukturag/libde265.git
|
||||||
git clone --depth 1 -b v2.5.3 https://github.com/uclouvain/openjpeg.git
|
git clone --depth 1 -b v2.5.4 https://github.com/uclouvain/openjpeg.git
|
||||||
git clone --depth 1 https://github.com/strukturag/libheif.git
|
git clone --depth 1 https://github.com/strukturag/libheif.git
|
||||||
git clone --depth=1 --recursive --shallow-submodules https://github.com/libjxl/libjxl.git
|
git clone --depth=1 --recursive --shallow-submodules https://github.com/libjxl/libjxl.git
|
||||||
git clone --depth 1 https://github.com/LibRaw/LibRaw
|
git clone --depth 1 https://github.com/LibRaw/LibRaw
|
||||||
|
|||||||
BIN
autotests/read/avif/profile_gray.avif
Normal file
|
After Width: | Height: | Size: 267 KiB |
11
autotests/read/avif/profile_gray.avif.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "profile_gray.png",
|
||||||
|
"colorSpace" : {
|
||||||
|
"colorModel" : "Gray",
|
||||||
|
"primaries" : "Custom",
|
||||||
|
"transferFunction" : "SRgb",
|
||||||
|
"gamma" : 2.31
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
autotests/read/avif/profile_gray.png
Normal file
|
After Width: | Height: | Size: 267 KiB |
BIN
autotests/read/avif/profile_gray_gamma22.avif
Normal file
|
After Width: | Height: | Size: 261 KiB |
11
autotests/read/avif/profile_gray_gamma22.avif.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "profile_gray_gamma22.png",
|
||||||
|
"colorSpace" : {
|
||||||
|
"colorModel" : "Gray",
|
||||||
|
"primaries" : "Custom",
|
||||||
|
"transferFunction" : "Gamma",
|
||||||
|
"gamma" : 2.2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
autotests/read/avif/profile_gray_gamma22.png
Normal file
|
After Width: | Height: | Size: 263 KiB |
BIN
autotests/read/avif/profile_gray_gamma28.avif
Normal file
|
After Width: | Height: | Size: 263 KiB |
11
autotests/read/avif/profile_gray_gamma28.avif.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "profile_gray_gamma28.png",
|
||||||
|
"colorSpace" : {
|
||||||
|
"colorModel" : "Gray",
|
||||||
|
"primaries" : "Custom",
|
||||||
|
"transferFunction" : "Gamma",
|
||||||
|
"gamma" : 2.8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
autotests/read/avif/profile_gray_gamma28.png
Normal file
|
After Width: | Height: | Size: 265 KiB |
BIN
autotests/read/avif/profile_gray_linear.avif
Normal file
|
After Width: | Height: | Size: 216 KiB |
11
autotests/read/avif/profile_gray_linear.avif.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "profile_gray_linear.png",
|
||||||
|
"colorSpace" : {
|
||||||
|
"colorModel" : "Gray",
|
||||||
|
"primaries" : "Custom",
|
||||||
|
"transferFunction" : "Linear",
|
||||||
|
"gamma" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
autotests/read/avif/profile_gray_linear.png
Normal file
|
After Width: | Height: | Size: 224 KiB |
BIN
autotests/read/avif/profile_gray_prophoto.avif
Normal file
|
After Width: | Height: | Size: 255 KiB |
12
autotests/read/avif/profile_gray_prophoto.avif.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "profile_gray_prophoto.png",
|
||||||
|
"colorSpace" : {
|
||||||
|
"description" : "grayscale D50 with ProPhoto TRC",
|
||||||
|
"colorModel" : "Gray",
|
||||||
|
"primaries" : "Custom",
|
||||||
|
"transferFunction" : "Custom",
|
||||||
|
"gamma" : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
autotests/read/avif/profile_gray_prophoto.png
Normal file
|
After Width: | Height: | Size: 259 KiB |
BIN
autotests/read/avif/ycgco-re.avif
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
autotests/read/avif/ycgco-re.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
autotests/read/exr/ps2026_testcard_rgb.exr
Normal file
15
autotests/read/exr/ps2026_testcard_rgb.exr.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "ps2026_testcard_rgb.png",
|
||||||
|
"colorSpace" : {
|
||||||
|
"description" : "sRGB build-in (Profilo RGB lineare)",
|
||||||
|
"primaries" : "SRgb",
|
||||||
|
"transferFunction" : "Linear",
|
||||||
|
"gamma" : 1
|
||||||
|
},
|
||||||
|
"resolution" : {
|
||||||
|
"dotsPerMeterX" : 3937,
|
||||||
|
"dotsPerMeterY" : 3937
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
autotests/read/exr/ps2026_testcard_rgb.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
@@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
"fileName" : "rgb-gimp.png",
|
"fileName" : "rgb-gimp.png",
|
||||||
"colorSpace" : {
|
"colorSpace" : {
|
||||||
"description" : "",
|
"description" : "Embedded RGB (linear)",
|
||||||
"primaries" : "Custom",
|
"primaries" : "Custom",
|
||||||
"transferFunction" : "Linear",
|
"transferFunction" : "Linear",
|
||||||
"gamma" : 1
|
"gamma" : 1
|
||||||
|
|||||||
BIN
autotests/read/iff/cdi_cl7.iff
Normal file
BIN
autotests/read/iff/cdi_cl7.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
autotests/read/iff/cdi_dyuv_each.iff
Normal file
9
autotests/read/iff/cdi_dyuv_each.iff.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "cdi_dyuv_each.iff",
|
||||||
|
"resolution" : {
|
||||||
|
"dotsPerMeterX" : 3937,
|
||||||
|
"dotsPerMeterY" : 5249
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
autotests/read/iff/cdi_dyuv_each.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
autotests/read/iff/cdi_dyuv_one.iff
Normal file
BIN
autotests/read/iff/cdi_dyuv_one.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
autotests/read/iff/sv5_gray8_rgx.iff
Normal file
BIN
autotests/read/iff/sv5_gray8_rgx.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
autotests/read/iff/sv5_idx8_rgx.iff
Normal file
BIN
autotests/read/iff/sv5_idx8_rgx.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
autotests/read/iff/sv5_rgb16_rgx.iff
Normal file
BIN
autotests/read/iff/sv5_rgb16_rgx.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
autotests/read/iff/sv5_rgb32_rgx.iff
Normal file
BIN
autotests/read/iff/sv5_rgb32_rgx.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
autotests/read/iff/sv5_rgb8_rgx.iff
Normal file
BIN
autotests/read/iff/sv5_rgb8_rgx.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
autotests/read/iff/sv5_rgba16_rgx.iff
Normal file
BIN
autotests/read/iff/sv5_rgba16_rgx.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
autotests/read/iff/sv5_rgba32_rgx.iff
Normal file
37
autotests/read/iff/sv5_rgba32_rgx.iff.json
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "sv5_rgba32_rgx.png",
|
||||||
|
"colorSpace" : {
|
||||||
|
"description" : "TIFF ICC Profile",
|
||||||
|
"primaries" : "SRgb",
|
||||||
|
"transferFunction" : "SRgb",
|
||||||
|
"gamma" : 0
|
||||||
|
},
|
||||||
|
"metadata" : [
|
||||||
|
{
|
||||||
|
"key" : "Author",
|
||||||
|
"value" : "KDE Project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Copyright",
|
||||||
|
"value" : "@2025 KDE Project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "CreationDate",
|
||||||
|
"value" : "2025-01-14T10:34:51"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Description",
|
||||||
|
"value" : "TV broadcast test image."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Title",
|
||||||
|
"value" : "Test Card"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"resolution" : {
|
||||||
|
"dotsPerMeterX" : 2835,
|
||||||
|
"dotsPerMeterY" : 2835
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
autotests/read/iff/sv5_rgba32_rgx.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
autotests/read/iff/sv5_rgba8_rgx.iff
Normal file
BIN
autotests/read/iff/sv5_rgba8_rgx.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
autotests/read/jxl/gray_linear_lossy.jxl
Normal file
14
autotests/read/jxl/gray_linear_lossy.jxl.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "gray_linear_lossy.png",
|
||||||
|
"fuzziness" : 1,
|
||||||
|
"description" : "Minimum fuzziness value to pass the test on all architectures.",
|
||||||
|
"colorSpace" : {
|
||||||
|
"description" : "Gra_D65_Rel_SRG",
|
||||||
|
"primaries" : "Custom",
|
||||||
|
"transferFunction" : "SRgb",
|
||||||
|
"gamma" : 0,
|
||||||
|
"colorModel" : "Gray"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
autotests/read/jxl/gray_linear_lossy.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
@@ -14,7 +14,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key" : "Software" ,
|
"key" : "Software" ,
|
||||||
"value" : "LIFE Pro 2.18.10"
|
"value" : "LIFE Pro 2.20.35"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key" : "Altitude",
|
"key" : "Altitude",
|
||||||
@@ -32,6 +32,10 @@
|
|||||||
"key" : "Description",
|
"key" : "Description",
|
||||||
"value" : "TV broadcast test image."
|
"value" : "TV broadcast test image."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key" : "HostComputer",
|
||||||
|
"value" : "Windows 11 Enterprise (25H2)"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key" : "Latitude",
|
"key" : "Latitude",
|
||||||
"value" : "44.6478"
|
"value" : "44.6478"
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
{
|
{
|
||||||
"fileName" : "32bit_grayscale.png",
|
"fileName" : "32bit_grayscale.png",
|
||||||
"colorSpace" : {
|
"colorSpace" : {
|
||||||
"description" : "Linear Grayscale Profile",
|
"description" : "RGB emulation of \"Linear Grayscale Profile\"",
|
||||||
"colorModel" : "Gray",
|
"colorModel" : "Rgb",
|
||||||
"primaries" : "Custom",
|
"primaries" : "Custom",
|
||||||
"transferFunction" : "Linear",
|
"transferFunction" : "Linear",
|
||||||
"gamma" : 1
|
"gamma" : 1
|
||||||
|
|||||||
BIN
autotests/read/psd/testcard_graya16.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
autotests/read/psd/testcard_graya16.psd
Normal file
26
autotests/read/psd/testcard_graya16.psd.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "testcard_graya16.png",
|
||||||
|
"colorSpace" : {
|
||||||
|
"description" : "RGB emulation of \"Gray Gamma 2.2\"",
|
||||||
|
"colorModel" : "Rgb",
|
||||||
|
"primaries" : "SRgb",
|
||||||
|
"transferFunction" : "Gamma",
|
||||||
|
"gamma" : 2.19922
|
||||||
|
},
|
||||||
|
"metadata" : [
|
||||||
|
{
|
||||||
|
"key" : "ModificationDate",
|
||||||
|
"value" : "2025-11-17T07:27:47"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Software" ,
|
||||||
|
"value" : "Adobe Photoshop 26.11 (Windows)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"resolution" : {
|
||||||
|
"dotsPerMeterX" : 11811,
|
||||||
|
"dotsPerMeterY" : 11811
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
autotests/read/psd/testcard_graya32.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
autotests/read/psd/testcard_graya32.psd
Normal file
26
autotests/read/psd/testcard_graya32.psd.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "testcard_graya32.png",
|
||||||
|
"colorSpace" : {
|
||||||
|
"description" : "RGB emulation of \"Profilo scala di grigio lineare\"",
|
||||||
|
"colorModel" : "Rgb",
|
||||||
|
"primaries" : "Custom",
|
||||||
|
"transferFunction" : "Linear",
|
||||||
|
"gamma" : 1
|
||||||
|
},
|
||||||
|
"metadata" : [
|
||||||
|
{
|
||||||
|
"key" : "ModificationDate",
|
||||||
|
"value" : "2025-11-17T07:29:19"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Software" ,
|
||||||
|
"value" : "Adobe Photoshop 26.11 (Windows)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"resolution" : {
|
||||||
|
"dotsPerMeterX" : 11811,
|
||||||
|
"dotsPerMeterY" : 11811
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
autotests/read/psd/testcard_graya8.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
autotests/read/psd/testcard_graya8.psd
Normal file
28
autotests/read/psd/testcard_graya8.psd.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "testcard_graya8.png",
|
||||||
|
"fuzziness" : 1,
|
||||||
|
"perceptiveFuzziness" : true,
|
||||||
|
"colorSpace" : {
|
||||||
|
"description" : "RGB emulation of \"Gray Gamma 2.2\"",
|
||||||
|
"colorModel" : "Rgb",
|
||||||
|
"primaries" : "SRgb",
|
||||||
|
"transferFunction" : "Gamma",
|
||||||
|
"gamma" : 2.19922
|
||||||
|
},
|
||||||
|
"metadata" : [
|
||||||
|
{
|
||||||
|
"key" : "ModificationDate",
|
||||||
|
"value" : "2025-11-17T07:28:50"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Software" ,
|
||||||
|
"value" : "Adobe Photoshop 26.11 (Windows)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"resolution" : {
|
||||||
|
"dotsPerMeterX" : 11811,
|
||||||
|
"dotsPerMeterY" : 11811
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
autotests/read/tim/testcard_idx4.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
autotests/read/tim/testcard_idx4.tim
Normal file
BIN
autotests/read/tim/testcard_idx8.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
autotests/read/tim/testcard_idx8.tim
Normal file
BIN
autotests/read/tim/testcard_rgb16.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
autotests/read/tim/testcard_rgb16.tim
Normal file
BIN
autotests/read/tim/testcard_rgb24.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
autotests/read/tim/testcard_rgb24.tim
Normal file
@@ -258,22 +258,26 @@ int main(int argc, char **argv)
|
|||||||
});
|
});
|
||||||
QTextStream(stdout) << "QImageReader::supportedImageFormats: " << formatStrings.join(", ") << "\n";
|
QTextStream(stdout) << "QImageReader::supportedImageFormats: " << formatStrings.join(", ") << "\n";
|
||||||
|
|
||||||
|
// checks if the format has read capability
|
||||||
if (!formats.contains(format)) {
|
if (!formats.contains(format)) {
|
||||||
if (format == "avci" || format == "heif" || format == "hej2") {
|
QTextStream(stdout) << "FAIL : current configuration is missing necessary decoder(s) for " << suffix << "!\n"
|
||||||
QTextStream(stdout) << "WARNING : " << suffix << " is not supported with current libheif configuration!\n"
|
<< "********* "
|
||||||
<< "********* "
|
<< "Finished basic read tests for " << suffix << " images *********\n";
|
||||||
<< "Finished basic read tests for " << suffix << " images *********\n";
|
return 1;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QFileInfoList lstImgDir = imgdir.entryInfoList();
|
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
|
// Launch 3 runs for each test:
|
||||||
for (int seq = 0; seq < 2; ++seq) {
|
// - first run on a random access device with allocation limit set to 256 MiB.
|
||||||
|
// - second run on a random access device with allocation limit set to 0 MiB.
|
||||||
|
// - third run on a sequential access device.
|
||||||
|
for (int run = 0; run < 3; ++run) {
|
||||||
|
QImageReader::setAllocationLimit(run == 1 ? 0 : 256);
|
||||||
|
bool seq = run == 2;
|
||||||
if (seq) {
|
if (seq) {
|
||||||
QTextStream(stdout) << "* Run on SEQUENTIAL ACCESS device\n";
|
QTextStream(stdout) << "* Run on SEQUENTIAL ACCESS device\n";
|
||||||
} else {
|
} else {
|
||||||
QTextStream(stdout) << "* Run on RANDOM ACCESS device\n";
|
QTextStream(stdout) << "* Run on RANDOM ACCESS device (allocation limit: " << QImageReader::allocationLimit() << " MiB)\n";
|
||||||
}
|
}
|
||||||
for (const QFileInfo &fi : lstImgDir) {
|
for (const QFileInfo &fi : lstImgDir) {
|
||||||
TemplateImage timg(fi);
|
TemplateImage timg(fi);
|
||||||
@@ -338,12 +342,7 @@ int main(int argc, char **argv)
|
|||||||
OptionTest optionTest;
|
OptionTest optionTest;
|
||||||
if (!optionTest.store(&inputReader)) {
|
if (!optionTest.store(&inputReader)) {
|
||||||
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": error while reading options\n";
|
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": error while reading options\n";
|
||||||
if (format == "heif") {
|
++failed;
|
||||||
// libheif + ffmpeg decoder is unable to load all HEIF files.
|
|
||||||
++skipped;
|
|
||||||
} else {
|
|
||||||
++failed;
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public:
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief skipSequentialDeviceTest
|
* \brief skipSequentialDeviceTest
|
||||||
* \return tre it the sequential test should be skipped.
|
* \return True if the sequential test should be skipped.
|
||||||
*/
|
*/
|
||||||
bool skipSequentialDeviceTest() const;
|
bool skipSequentialDeviceTest() const;
|
||||||
|
|
||||||
|
|||||||
@@ -13,14 +13,18 @@
|
|||||||
"key" : "ModificationDate",
|
"key" : "ModificationDate",
|
||||||
"value" : "2025-02-14T15:58:44+01:00"
|
"value" : "2025-02-14T15:58:44+01:00"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"key" : "Software" ,
|
|
||||||
"value" : "Adobe Photoshop 26.2 (Windows)"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"key" : "Altitude",
|
"key" : "Altitude",
|
||||||
"value" : "34"
|
"value" : "34"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key" : "Title",
|
||||||
|
"value" : "A test"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Software",
|
||||||
|
"value" : "KImageFormats write test"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key" : "Author",
|
"key" : "Author",
|
||||||
"value" : "KDE Project"
|
"value" : "KDE Project"
|
||||||
@@ -45,6 +49,10 @@
|
|||||||
"key" : "LensModel",
|
"key" : "LensModel",
|
||||||
"value" : "A1234"
|
"value" : "A1234"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key" : "LensSerialNumber",
|
||||||
|
"value" : "S/N:1234567"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key" : "Longitude",
|
"key" : "Longitude",
|
||||||
"value" : "10.9254"
|
"value" : "10.9254"
|
||||||
@@ -56,6 +64,50 @@
|
|||||||
{
|
{
|
||||||
"key" : "Model",
|
"key" : "Model",
|
||||||
"value" : "KImageFormats"
|
"value" : "KImageFormats"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "SerialNumber",
|
||||||
|
"value" : "S/N:7654321"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Speed",
|
||||||
|
"value" : "13.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "DigitalZoomRatio",
|
||||||
|
"value" : "3.4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "ExposureMode",
|
||||||
|
"value" : "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "ExposureProgram",
|
||||||
|
"value" : "6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "ExposureTime",
|
||||||
|
"value" : "0.004"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Flash",
|
||||||
|
"value" : "16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "FNumber",
|
||||||
|
"value" : "1.6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "FocalLength",
|
||||||
|
"value" : "5.96"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "ISOSpeedRatings",
|
||||||
|
"value" : "50"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "WhiteBalance",
|
||||||
|
"value" : "1"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"resolution" : {
|
"resolution" : {
|
||||||
|
|||||||
@@ -41,6 +41,10 @@
|
|||||||
"key" : "LensModel",
|
"key" : "LensModel",
|
||||||
"value" : "A1234"
|
"value" : "A1234"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key" : "LensSerialNumber",
|
||||||
|
"value" : "S/N:1234567"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key" : "Longitude",
|
"key" : "Longitude",
|
||||||
"value" : "10.9254"
|
"value" : "10.9254"
|
||||||
@@ -52,6 +56,50 @@
|
|||||||
{
|
{
|
||||||
"key" : "Model",
|
"key" : "Model",
|
||||||
"value" : "KImageFormats"
|
"value" : "KImageFormats"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "SerialNumber",
|
||||||
|
"value" : "S/N:7654321"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Speed",
|
||||||
|
"value" : "13.2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "DigitalZoomRatio",
|
||||||
|
"value" : "3.4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "ExposureMode",
|
||||||
|
"value" : "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "ExposureProgram",
|
||||||
|
"value" : "6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "ExposureTime",
|
||||||
|
"value" : "0.004"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "Flash",
|
||||||
|
"value" : "16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "FNumber",
|
||||||
|
"value" : "1.6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "FocalLength",
|
||||||
|
"value" : "5.96"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "ISOSpeedRatings",
|
||||||
|
"value" : "50"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key" : "WhiteBalance",
|
||||||
|
"value" : "1"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"resolution" : {
|
"resolution" : {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ void setOptionalInfo(QImage &image, const QString &suffix)
|
|||||||
|
|
||||||
// Set metadata
|
// Set metadata
|
||||||
auto meta = obj.value("metadata").toArray();
|
auto meta = obj.value("metadata").toArray();
|
||||||
for (auto jv : meta) {
|
for (auto &&jv : meta) {
|
||||||
auto obj = jv.toObject();
|
auto obj = jv.toObject();
|
||||||
auto key = obj.value("key").toString();
|
auto key = obj.value("key").toString();
|
||||||
auto val = obj.value("value").toString();
|
auto val = obj.value("value").toString();
|
||||||
@@ -106,7 +106,7 @@ bool checkOptionalInfo(QImage &image, const QString &suffix)
|
|||||||
|
|
||||||
// Test metadata
|
// Test metadata
|
||||||
auto meta = obj.value("metadata").toArray();
|
auto meta = obj.value("metadata").toArray();
|
||||||
for (auto jv : meta) {
|
for (auto &&jv : meta) {
|
||||||
auto obj = jv.toObject();
|
auto obj = jv.toObject();
|
||||||
auto key = obj.value("key").toString();
|
auto key = obj.value("key").toString();
|
||||||
auto val = obj.value("value").toString();
|
auto val = obj.value("value").toString();
|
||||||
@@ -380,12 +380,7 @@ int formatTest(const QString &suffix, bool createTemplates)
|
|||||||
QBuffer buffer(&ba);
|
QBuffer buffer(&ba);
|
||||||
auto writtenImage = QImageReader(&buffer, suffix.toLatin1()).read();
|
auto writtenImage = QImageReader(&buffer, suffix.toLatin1()).read();
|
||||||
if (writtenImage.isNull()) {
|
if (writtenImage.isNull()) {
|
||||||
if (suffix.toLatin1() == "heif") {
|
++failed;
|
||||||
// libheif + ffmpeg decoder is unable to load all HEIF files.
|
|
||||||
++skipped;
|
|
||||||
} else {
|
|
||||||
++failed;
|
|
||||||
}
|
|
||||||
QTextStream(stdout) << "FAIL : error while reading the image " << formatName << "\n";
|
QTextStream(stdout) << "FAIL : error while reading the image " << formatName << "\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -639,23 +634,17 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
auto suffix = args.at(0);
|
auto suffix = args.at(0);
|
||||||
|
|
||||||
// skip test if libheif configuration is obviously incomplete
|
// skip test if configuration is obviously incomplete
|
||||||
QByteArray format = suffix.toLatin1();
|
QByteArray format = suffix.toLatin1();
|
||||||
const QList<QByteArray> read_formats = QImageReader::supportedImageFormats();
|
const QList<QByteArray> read_formats = QImageReader::supportedImageFormats();
|
||||||
const QList<QByteArray> write_formats = QImageWriter::supportedImageFormats();
|
if (!read_formats.contains(format)) { // checks if the format has read capability
|
||||||
|
QTextStream(stdout) << "FAIL : current configuration is missing necessary decoder(s) for " << format << "!\n";
|
||||||
if (!read_formats.contains(format)) {
|
return 1;
|
||||||
if (format == "heif" || format == "hej2") {
|
|
||||||
QTextStream(stdout) << "WARNING : libheif configuration is missing necessary decoder(s)!\n";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
const QList<QByteArray> write_formats = QImageWriter::supportedImageFormats();
|
||||||
if (!write_formats.contains(format)) {
|
if (!write_formats.contains(format)) { // checks if the format has write capability
|
||||||
if (format == "heif" || format == "hej2") {
|
QTextStream(stdout) << "FAIL : libraries configuration is missing necessary encoder(s) for " << format << "!\n";
|
||||||
QTextStream(stdout) << "WARNING : libheif configuration is missing necessary encoder(s)!\n";
|
return 1;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// run test
|
// run test
|
||||||
|
|||||||
@@ -137,6 +137,10 @@ kimageformats_add_plugin(kimg_sct SOURCES sct.cpp)
|
|||||||
|
|
||||||
##################################
|
##################################
|
||||||
|
|
||||||
|
kimageformats_add_plugin(kimg_tim SOURCES tim.cpp)
|
||||||
|
|
||||||
|
##################################
|
||||||
|
|
||||||
kimageformats_add_plugin(kimg_tga SOURCES tga.cpp microexif.cpp scanlineconverter.cpp)
|
kimageformats_add_plugin(kimg_tga SOURCES tga.cpp microexif.cpp scanlineconverter.cpp)
|
||||||
|
|
||||||
##################################
|
##################################
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ani_p.h"
|
#include "ani_p.h"
|
||||||
|
#include "util_p.h"
|
||||||
|
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
@@ -101,7 +102,7 @@ bool ANIHandler::read(QImage *outImage)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto frameSize = *(reinterpret_cast<const quint32_le *>(frameSizeData.data()));
|
const auto frameSize = *(reinterpret_cast<const quint32_le *>(frameSizeData.data()));
|
||||||
if (!frameSize) {
|
if (!frameSize || frameSize > quint32(kMaxQVectorSize)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,6 +418,9 @@ bool ANIHandler::ensureScanned() const
|
|||||||
// IART and INAM are technically inside LIST->INFO but "INFO" is supposedly optional
|
// IART and INAM are technically inside LIST->INFO but "INFO" is supposedly optional
|
||||||
// so just handle those two attributes wherever we encounter them
|
// so just handle those two attributes wherever we encounter them
|
||||||
} else if (chunkId == "INAM" || chunkId == "IART") {
|
} else if (chunkId == "INAM" || chunkId == "IART") {
|
||||||
|
if (chunkSize > kMaxQVectorSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const QByteArray value = device()->read(chunkSize);
|
const QByteArray value = device()->read(chunkSize);
|
||||||
|
|
||||||
if (static_cast<quint32_le>(value.size()) != chunkSize) {
|
if (static_cast<quint32_le>(value.size()) != chunkSize) {
|
||||||
|
|||||||
@@ -265,9 +265,15 @@ bool QAVIFHandler::decode_one_frame()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t resultdepth = m_decoder->image->depth;
|
||||||
|
if (m_decoder->image->matrixCoefficients == 16 && m_decoder->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444 && resultdepth == 10) {
|
||||||
|
// 10-bit YCgCo-Re AVIF must be decoded to 8bit
|
||||||
|
resultdepth = 8;
|
||||||
|
}
|
||||||
|
|
||||||
QImage::Format resultformat;
|
QImage::Format resultformat;
|
||||||
|
|
||||||
if (m_decoder->image->depth > 8) {
|
if (resultdepth > 8) {
|
||||||
if (loadalpha) {
|
if (loadalpha) {
|
||||||
resultformat = QImage::Format_RGBA64;
|
resultformat = QImage::Format_RGBA64;
|
||||||
} else {
|
} else {
|
||||||
@@ -382,6 +388,9 @@ bool QAVIFHandler::decode_one_frame()
|
|||||||
case 2: /* AVIF_COLOR_PRIMARIES_UNSPECIFIED */
|
case 2: /* AVIF_COLOR_PRIMARIES_UNSPECIFIED */
|
||||||
colorspace = QColorSpace(QColorSpace::Primaries::SRgb, q_trc, q_trc_gamma);
|
colorspace = QColorSpace(QColorSpace::Primaries::SRgb, q_trc, q_trc_gamma);
|
||||||
break;
|
break;
|
||||||
|
case 9:
|
||||||
|
colorspace = QColorSpace(QColorSpace::Primaries::Bt2020, q_trc, q_trc_gamma);
|
||||||
|
break;
|
||||||
/* AVIF_COLOR_PRIMARIES_SMPTE432 */
|
/* AVIF_COLOR_PRIMARIES_SMPTE432 */
|
||||||
case 12:
|
case 12:
|
||||||
colorspace = QColorSpace(QColorSpace::Primaries::DciP3D65, q_trc, q_trc_gamma);
|
colorspace = QColorSpace(QColorSpace::Primaries::DciP3D65, q_trc, q_trc_gamma);
|
||||||
@@ -405,7 +414,7 @@ bool QAVIFHandler::decode_one_frame()
|
|||||||
rgb.maxThreads = m_decoder->maxThreads;
|
rgb.maxThreads = m_decoder->maxThreads;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (m_decoder->image->depth > 8) {
|
if (resultdepth > 8) {
|
||||||
rgb.depth = 16;
|
rgb.depth = 16;
|
||||||
rgb.format = AVIF_RGB_FORMAT_RGBA;
|
rgb.format = AVIF_RGB_FORMAT_RGBA;
|
||||||
|
|
||||||
@@ -504,21 +513,12 @@ bool QAVIFHandler::decode_one_frame()
|
|||||||
#else
|
#else
|
||||||
switch (m_decoder->image->imir.axis) {
|
switch (m_decoder->image->imir.axis) {
|
||||||
#endif
|
#endif
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
|
|
||||||
case 0: // top-to-bottom
|
|
||||||
result = result.mirrored(false, true);
|
|
||||||
break;
|
|
||||||
case 1: // left-to-right
|
|
||||||
result = result.mirrored(true, false);
|
|
||||||
break;
|
|
||||||
#else
|
|
||||||
case 0: // top-to-bottom
|
case 0: // top-to-bottom
|
||||||
result = result.flipped(Qt::Vertical);
|
result = result.flipped(Qt::Vertical);
|
||||||
break;
|
break;
|
||||||
case 1: // left-to-right
|
case 1: // left-to-right
|
||||||
result = result.flipped(Qt::Horizontal);
|
result = result.flipped(Qt::Horizontal);
|
||||||
break;
|
break;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -734,6 +734,15 @@ bool QAVIFHandler::write(const QImage &image)
|
|||||||
/* AVIF_TRANSFER_CHARACTERISTICS_LINEAR */
|
/* AVIF_TRANSFER_CHARACTERISTICS_LINEAR */
|
||||||
avif->transferCharacteristics = (avifTransferCharacteristics)8;
|
avif->transferCharacteristics = (avifTransferCharacteristics)8;
|
||||||
break;
|
break;
|
||||||
|
case QColorSpace::TransferFunction::Gamma:
|
||||||
|
if (qAbs(tmpgrayimage.colorSpace().gamma() - 2.2f) < 0.1f) {
|
||||||
|
/* AVIF_TRANSFER_CHARACTERISTICS_BT470M */
|
||||||
|
avif->transferCharacteristics = (avifTransferCharacteristics)4;
|
||||||
|
} else if (qAbs(tmpgrayimage.colorSpace().gamma() - 2.8f) < 0.1f) {
|
||||||
|
/* AVIF_TRANSFER_CHARACTERISTICS_BT470BG */
|
||||||
|
avif->transferCharacteristics = (avifTransferCharacteristics)5;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case QColorSpace::TransferFunction::SRgb:
|
case QColorSpace::TransferFunction::SRgb:
|
||||||
/* AVIF_TRANSFER_CHARACTERISTICS_SRGB */
|
/* AVIF_TRANSFER_CHARACTERISTICS_SRGB */
|
||||||
avif->transferCharacteristics = (avifTransferCharacteristics)13;
|
avif->transferCharacteristics = (avifTransferCharacteristics)13;
|
||||||
@@ -748,6 +757,42 @@ bool QAVIFHandler::write(const QImage &image)
|
|||||||
/* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
|
/* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (avif->transferCharacteristics == 2) { // in case TransferFunction was not identified yet
|
||||||
|
if (tmpgrayimage.colorSpace().colorModel() == QColorSpace::ColorModel::Gray && lossless) {
|
||||||
|
avif->colorPrimaries = (avifColorPrimaries)2;
|
||||||
|
avif->matrixCoefficients = (avifMatrixCoefficients)6;
|
||||||
|
|
||||||
|
QByteArray iccprofile_gray = tmpgrayimage.colorSpace().iccProfile();
|
||||||
|
|
||||||
|
if (iccprofile_gray.size() > 0) {
|
||||||
|
#if AVIF_VERSION >= 1000000
|
||||||
|
res = avifImageSetProfileICC(avif, reinterpret_cast<const uint8_t *>(iccprofile_gray.constData()), iccprofile_gray.size());
|
||||||
|
if (res != AVIF_RESULT_OK) {
|
||||||
|
qCWarning(LOG_AVIFPLUGIN, "ERROR in avifImageSetProfileICC: %s", avifResultToString(res));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
avifImageSetProfileICC(avif, reinterpret_cast<const uint8_t *>(iccprofile_gray.constData()), iccprofile_gray.size());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} else { // convert to grayscale with SRgb
|
||||||
|
tmpgrayimage.convertToColorSpace(QColorSpace(QPointF(0.3127f, 0.329f), QColorSpace::TransferFunction::SRgb), QImage::Format_Grayscale16);
|
||||||
|
switch (tmpgrayimage.format()) {
|
||||||
|
case QImage::Format_Grayscale8:
|
||||||
|
save_depth = 8;
|
||||||
|
break;
|
||||||
|
case QImage::Format_Grayscale16:
|
||||||
|
save_depth = 10;
|
||||||
|
avif->transferCharacteristics = (avifTransferCharacteristics)13;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qCWarning(LOG_AVIFPLUGIN, "Error saving Gray image");
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (save_depth > 8) { // QImage::Format_Grayscale16
|
if (save_depth > 8) { // QImage::Format_Grayscale16
|
||||||
@@ -832,6 +877,10 @@ bool QAVIFHandler::write(const QImage &image)
|
|||||||
/* AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL */
|
/* AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL */
|
||||||
matrix_to_save = (avifMatrixCoefficients)12;
|
matrix_to_save = (avifMatrixCoefficients)12;
|
||||||
break;
|
break;
|
||||||
|
case QColorSpace::Primaries::Bt2020:
|
||||||
|
primaries_to_save = (avifColorPrimaries)9;
|
||||||
|
matrix_to_save = (avifMatrixCoefficients)12;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
/* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
|
/* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
|
||||||
primaries_to_save = (avifColorPrimaries)2;
|
primaries_to_save = (avifColorPrimaries)2;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of the KDE project
|
This file is part of the KDE project
|
||||||
SPDX-FileCopyrightText: 2025 Mirco Miranda <mircomir@outlook.com>
|
SPDX-FileCopyrightText: 2025-2026 Mirco Miranda <mircomir@outlook.com>
|
||||||
|
|
||||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
* - https://wiki.amigaos.net/wiki/IFF_FORM_and_Chunk_Registry
|
* - https://wiki.amigaos.net/wiki/IFF_FORM_and_Chunk_Registry
|
||||||
* - https://www.fileformat.info/format/iff/egff.htm
|
* - https://www.fileformat.info/format/iff/egff.htm
|
||||||
* - https://download.autodesk.com/us/maya/2010help/index.html (Developer resources -> File formats -> Maya IFF)
|
* - https://download.autodesk.com/us/maya/2010help/index.html (Developer resources -> File formats -> Maya IFF)
|
||||||
|
* - https://aminet.net/package/dev/misc/IFF-RGFX
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef KIMG_CHUNKS_P_H
|
#ifndef KIMG_CHUNKS_P_H
|
||||||
@@ -36,7 +37,7 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
|
|||||||
#define LIST_CHUNK QByteArray("LIST")
|
#define LIST_CHUNK QByteArray("LIST")
|
||||||
#define PROP_CHUNK QByteArray("PROP")
|
#define PROP_CHUNK QByteArray("PROP")
|
||||||
|
|
||||||
// Main chuncks (Maya)
|
// Main chunks (Maya)
|
||||||
#define CAT4_CHUNK QByteArray("CAT4") // 4 byte alignment
|
#define CAT4_CHUNK QByteArray("CAT4") // 4 byte alignment
|
||||||
#define FOR4_CHUNK QByteArray("FOR4")
|
#define FOR4_CHUNK QByteArray("FOR4")
|
||||||
#define LIS4_CHUNK QByteArray("LIS4")
|
#define LIS4_CHUNK QByteArray("LIS4")
|
||||||
@@ -55,7 +56,17 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
|
|||||||
#define CMAP_CHUNK QByteArray("CMAP")
|
#define CMAP_CHUNK QByteArray("CMAP")
|
||||||
#define CMYK_CHUNK QByteArray("CMYK") // https://wiki.amigaos.net/wiki/ILBM_IFF_Interleaved_Bitmap#ILBM.CMYK
|
#define CMYK_CHUNK QByteArray("CMYK") // https://wiki.amigaos.net/wiki/ILBM_IFF_Interleaved_Bitmap#ILBM.CMYK
|
||||||
#define DPI__CHUNK QByteArray("DPI ")
|
#define DPI__CHUNK QByteArray("DPI ")
|
||||||
|
#define IDAT_CHUNK QByteArray("IDAT")
|
||||||
|
#define IHDR_CHUNK QByteArray("IHDR")
|
||||||
|
#define IPAR_CHUNK QByteArray("IPAR")
|
||||||
|
#define PLTE_CHUNK QByteArray("PLTE")
|
||||||
|
#define RBOD_CHUNK QByteArray("RBOD")
|
||||||
|
#define RCOL_CHUNK QByteArray("RCOL")
|
||||||
|
#define RFLG_CHUNK QByteArray("RFLG")
|
||||||
|
#define RGHD_CHUNK QByteArray("RGHD")
|
||||||
|
#define RSCM_CHUNK QByteArray("RSCM")
|
||||||
#define XBMI_CHUNK QByteArray("XBMI")
|
#define XBMI_CHUNK QByteArray("XBMI")
|
||||||
|
#define YUVS_CHUNK QByteArray("YUVS")
|
||||||
|
|
||||||
// Different palette for scanline
|
// Different palette for scanline
|
||||||
#define BEAM_CHUNK QByteArray("BEAM")
|
#define BEAM_CHUNK QByteArray("BEAM")
|
||||||
@@ -79,14 +90,18 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
|
|||||||
#define FVER_CHUNK QByteArray("FVER")
|
#define FVER_CHUNK QByteArray("FVER")
|
||||||
#define HIST_CHUNK QByteArray("HIST")
|
#define HIST_CHUNK QByteArray("HIST")
|
||||||
#define NAME_CHUNK QByteArray("NAME")
|
#define NAME_CHUNK QByteArray("NAME")
|
||||||
|
#define VDAT_CHUNK QByteArray("VDAT")
|
||||||
#define VERS_CHUNK QByteArray("VERS")
|
#define VERS_CHUNK QByteArray("VERS")
|
||||||
#define XMP0_CHUNK QByteArray("XMP0") // https://aminet.net/package/docs/misc/IFF-metadata
|
#define XMP0_CHUNK QByteArray("XMP0") // https://aminet.net/package/docs/misc/IFF-metadata
|
||||||
|
|
||||||
|
// FORM types
|
||||||
#define ACBM_FORM_TYPE QByteArray("ACBM")
|
#define ACBM_FORM_TYPE QByteArray("ACBM")
|
||||||
#define ILBM_FORM_TYPE QByteArray("ILBM")
|
#define ILBM_FORM_TYPE QByteArray("ILBM")
|
||||||
|
#define IMAG_FORM_TYPE QByteArray("IMAG")
|
||||||
#define PBM__FORM_TYPE QByteArray("PBM ")
|
#define PBM__FORM_TYPE QByteArray("PBM ")
|
||||||
#define RGB8_FORM_TYPE QByteArray("RGB8")
|
#define RGB8_FORM_TYPE QByteArray("RGB8")
|
||||||
#define RGBN_FORM_TYPE QByteArray("RGBN")
|
#define RGBN_FORM_TYPE QByteArray("RGBN")
|
||||||
|
#define RGFX_FORM_TYPE QByteArray("RGFX")
|
||||||
|
|
||||||
#define CIMG_FOR4_TYPE QByteArray("CIMG")
|
#define CIMG_FOR4_TYPE QByteArray("CIMG")
|
||||||
#define TBMP_FOR4_TYPE QByteArray("TBMP")
|
#define TBMP_FOR4_TYPE QByteArray("TBMP")
|
||||||
@@ -145,7 +160,7 @@ public:
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief bytes
|
* \brief bytes
|
||||||
* \return The size (in bytes) of the chunck data.
|
* \return The size (in bytes) of the chunk data.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
quint32 bytes() const;
|
quint32 bytes() const;
|
||||||
@@ -424,7 +439,8 @@ public:
|
|||||||
enum Compression {
|
enum Compression {
|
||||||
Uncompressed = 0, /**< Image data are uncompressed. */
|
Uncompressed = 0, /**< Image data are uncompressed. */
|
||||||
Rle = 1, /**< Image data are RLE compressed. */
|
Rle = 1, /**< Image data are RLE compressed. */
|
||||||
RgbN8 = 4 /**< RGB8/RGBN compresson. */
|
Vdat = 2, /**< Image data are VDAT compressed. */
|
||||||
|
RgbN8 = 4 /**< Image data are RGB8/RGBN compressed. */
|
||||||
};
|
};
|
||||||
enum Masking {
|
enum Masking {
|
||||||
None = 0, /**< Designates an opaque rectangular image. */
|
None = 0, /**< Designates an opaque rectangular image. */
|
||||||
@@ -756,10 +772,11 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
* \brief readStride
|
* \brief readStride
|
||||||
* \param d The device.
|
* \param d The device.
|
||||||
* \param header The bitmap header.
|
|
||||||
* \param y The current scanline.
|
* \param y The current scanline.
|
||||||
|
* \param header The bitmap header.
|
||||||
* \param camg The CAMG chunk (optional)
|
* \param camg The CAMG chunk (optional)
|
||||||
* \param cmap The CMAP chunk (optional)
|
* \param cmap The CMAP chunk (optional)
|
||||||
|
* \param ipal The per-line palette chunk (optional)
|
||||||
* \param formType The type of the current form chunk.
|
* \param formType The type of the current form chunk.
|
||||||
* \return The scanline as requested for QImage.
|
* \return The scanline as requested for QImage.
|
||||||
* \warning Call resetStrideRead() once before this one.
|
* \warning Call resetStrideRead() once before this one.
|
||||||
@@ -776,8 +793,6 @@ public:
|
|||||||
* \brief resetStrideRead
|
* \brief resetStrideRead
|
||||||
* Reset the stride read set the position at the beginning of the data and reset all buffers.
|
* 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.
|
* \return True on success, otherwise false.
|
||||||
* \sa strideRead
|
* \sa strideRead
|
||||||
* \note Must be called once before strideRead().
|
* \note Must be called once before strideRead().
|
||||||
@@ -788,6 +803,7 @@ public:
|
|||||||
* \brief safeModeId
|
* \brief safeModeId
|
||||||
* \param header The header.
|
* \param header The header.
|
||||||
* \param camg The CAMG chunk.
|
* \param camg The CAMG chunk.
|
||||||
|
* \param cmap The CMAP chunk.
|
||||||
* \return The most likely ModeId if not explicitly specified.
|
* \return The most likely ModeId if not explicitly specified.
|
||||||
*/
|
*/
|
||||||
static CAMGChunk::ModeIds safeModeId(const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap = nullptr);
|
static CAMGChunk::ModeIds safeModeId(const BMHDChunk *header, const CAMGChunk *camg, const CMAPChunk *cmap = nullptr);
|
||||||
@@ -808,6 +824,8 @@ protected:
|
|||||||
|
|
||||||
QByteArray rgbN(const QByteArray &planes, qint32 y, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr, const IPALChunk *ipal = nullptr) const;
|
QByteArray rgbN(const QByteArray &planes, qint32 y, const BMHDChunk *header, const CAMGChunk *camg = nullptr, const CMAPChunk *cmap = nullptr, const IPALChunk *ipal = nullptr) const;
|
||||||
|
|
||||||
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable QByteArray _readBuffer;
|
mutable QByteArray _readBuffer;
|
||||||
};
|
};
|
||||||
@@ -870,7 +888,7 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
* \brief transformation
|
* \brief transformation
|
||||||
* \return The image transformation.
|
* \return The image transformation.
|
||||||
* \note The Default implentation returns the trasformation of EXIF chunk (if any).
|
* \note The Default implementation returns the transformation of EXIF chunk (if any).
|
||||||
*/
|
*/
|
||||||
virtual QImageIOHandler::Transformation transformation() const;
|
virtual QImageIOHandler::Transformation transformation() const;
|
||||||
|
|
||||||
@@ -921,6 +939,13 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool innerReadStructure(QIODevice *d) override;
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QImage::Format iffFormat() const;
|
||||||
|
|
||||||
|
QImage::Format cdiFormat() const;
|
||||||
|
|
||||||
|
QImage::Format rgfxFormat() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -1357,6 +1382,32 @@ protected:
|
|||||||
virtual bool innerReadStructure(QIODevice *d) override;
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The VDATChunk class
|
||||||
|
*/
|
||||||
|
class VDATChunk : public IFFChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~VDATChunk() override;
|
||||||
|
VDATChunk();
|
||||||
|
VDATChunk(const VDATChunk& other) = default;
|
||||||
|
VDATChunk& operator =(const VDATChunk& other) = default;
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(VDAT_CHUNK)
|
||||||
|
|
||||||
|
const QByteArray &uncompressedData(const BMHDChunk *header) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable QByteArray uncompressed;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief The VERSChunk class
|
* \brief The VERSChunk class
|
||||||
*/
|
*/
|
||||||
@@ -1400,6 +1451,500 @@ protected:
|
|||||||
virtual bool innerReadStructure(QIODevice *d) override;
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* *** I-CD IFF CHUNKS ***
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The IHDRChunk class
|
||||||
|
* Image Header
|
||||||
|
*/
|
||||||
|
class IHDRChunk: public IFFChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Model {
|
||||||
|
Invalid = 0, /**< Invalid model. */
|
||||||
|
Rgb888 = 1, /**< red, green, blue - 8 bits per color. */
|
||||||
|
Rgb555 = 2, /**< Green Book absolute RGB. */
|
||||||
|
DYuv = 3, /**< Green Book Delta YUV. */
|
||||||
|
CLut8 = 4, /**< Green Book 8 bit CLUT. */
|
||||||
|
CLut7 = 5, /**< Green Book 7 bit CLUT. */
|
||||||
|
CLut4 = 6, /**< Green Book 4 bit CLUT. */
|
||||||
|
CLut3 = 7, /**< Green Book 3 bit CLUT. */
|
||||||
|
Rle7 = 8, /**< Green Book runlength coded 7 bit CLUT. */
|
||||||
|
Rle3 = 9, /**< Green Book runlength coded 3 bit CLUT. */
|
||||||
|
PaletteOnly = 10 /**< color palette only. */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DYuvKind {
|
||||||
|
One = 0,
|
||||||
|
Each = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Yuv {
|
||||||
|
Yuv(quint8 y0 = 0, quint8 u0 = 0, quint8 v0 = 0) : y(y0), u(u0), v(v0) {}
|
||||||
|
quint8 y;
|
||||||
|
quint8 u;
|
||||||
|
quint8 v;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~IHDRChunk() override;
|
||||||
|
|
||||||
|
IHDRChunk();
|
||||||
|
IHDRChunk(const IHDRChunk& other) = default;
|
||||||
|
IHDRChunk& operator =(const IHDRChunk& other) = default;
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief width
|
||||||
|
* \return Width of the bitmap in pixels.
|
||||||
|
*/
|
||||||
|
qint32 width() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief height
|
||||||
|
* \return Height of the bitmap in pixels.
|
||||||
|
*/
|
||||||
|
qint32 height() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief size
|
||||||
|
* \return Size in pixels.
|
||||||
|
*/
|
||||||
|
QSize size() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief lineSize
|
||||||
|
* Physical width of image (number of bytes in each scan line, including any data required at
|
||||||
|
* the end of each scan line for padding [see description of each model’s IDAT chunk for padding
|
||||||
|
* rules]) This field is not used when model() = Rle7 or Rle3.
|
||||||
|
* When model() = Rgb555, this field defines the size of one scan line of the upper
|
||||||
|
* or lower portion of the pixel data, but not the size of them both together.
|
||||||
|
*/
|
||||||
|
qint32 lineSize() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief model
|
||||||
|
* Image model (coding method)
|
||||||
|
*/
|
||||||
|
Model model() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief depth
|
||||||
|
* Physical size of pixel (number of bits per pixel used in storing image data) When
|
||||||
|
* model() = Rle7 or Rle3, this value only represents the size of a
|
||||||
|
* single pixel; the size of a run of pixels is indeterminate.
|
||||||
|
*/
|
||||||
|
quint16 depth() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief yuvKind
|
||||||
|
* if model() = DYuv, indicates whether there is one DYUV start value for all
|
||||||
|
* scan lines (in yuvStart()), or whether each scan line has its own start value in the
|
||||||
|
* YUVS chunk which follows.
|
||||||
|
*/
|
||||||
|
DYuvKind yuvKind() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief yuvStart
|
||||||
|
* Start values for DYUV image if model() = DYuv and dYuvKind() = One
|
||||||
|
*/
|
||||||
|
Yuv yuvStart() const;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(IHDR_CHUNK)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The IHDRChunk class
|
||||||
|
*/
|
||||||
|
class IPARChunk: public IFFChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Rgb {
|
||||||
|
Rgb(quint8 r0 = 0, quint8 g0 = 0, quint8 b0 = 0) : r(r0), g(g0), b(b0) {}
|
||||||
|
quint8 r;
|
||||||
|
quint8 g;
|
||||||
|
quint8 b;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~IPARChunk() override;
|
||||||
|
|
||||||
|
IPARChunk();
|
||||||
|
IPARChunk(const IPARChunk& other) = default;
|
||||||
|
IPARChunk& operator =(const IPARChunk& other) = default;
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief xOffset
|
||||||
|
* X offset of origin in source image [0 < xOffset() < xPage()]
|
||||||
|
*/
|
||||||
|
qint32 xOffset() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief yOffset
|
||||||
|
* \returnX offset of origin in source image [0 < yOffset() < yPage()]
|
||||||
|
*/
|
||||||
|
qint32 yOffset() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief aspectRatio
|
||||||
|
* Aspect ratio of pixels in source image.
|
||||||
|
*/
|
||||||
|
double aspectRatio() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief xPage
|
||||||
|
* X size of source image.
|
||||||
|
*/
|
||||||
|
qint32 xPage() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief yPage
|
||||||
|
* Y size of source image.
|
||||||
|
*/
|
||||||
|
qint32 yPage() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief xGrub
|
||||||
|
* X location of hot spot within image.
|
||||||
|
*/
|
||||||
|
qint32 xGrub() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief yGrub
|
||||||
|
* Y location of hot spot within image.
|
||||||
|
*/
|
||||||
|
qint32 yGrub() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief transparency
|
||||||
|
* Transparent color.
|
||||||
|
*/
|
||||||
|
Rgb transparency() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief mask
|
||||||
|
* Mask color.
|
||||||
|
*/
|
||||||
|
Rgb mask() const;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(IPAR_CHUNK)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The PLTEChunk class
|
||||||
|
*/
|
||||||
|
class PLTEChunk : public CMAPChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~PLTEChunk() override;
|
||||||
|
PLTEChunk();
|
||||||
|
PLTEChunk(const PLTEChunk& other) = default;
|
||||||
|
PLTEChunk& operator =(const PLTEChunk& other) = default;
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief count
|
||||||
|
* \return The number of color in the palette.
|
||||||
|
*/
|
||||||
|
virtual qint32 count() const override;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(PLTE_CHUNK)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
qint32 offset() const;
|
||||||
|
|
||||||
|
qint32 total() const;
|
||||||
|
|
||||||
|
virtual QList<QRgb> innerPalette() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The YUVSChunk class
|
||||||
|
*/
|
||||||
|
class YUVSChunk : public IFFChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~YUVSChunk() override;
|
||||||
|
YUVSChunk();
|
||||||
|
YUVSChunk(const YUVSChunk& other) = default;
|
||||||
|
YUVSChunk& operator =(const YUVSChunk& other) = default;
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
qint32 count() const;
|
||||||
|
|
||||||
|
IHDRChunk::Yuv yuvStart(qint32 y) const;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(YUVS_CHUNK)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The IDATChunk class
|
||||||
|
*/
|
||||||
|
class IDATChunk : public IFFChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IDATChunk() override;
|
||||||
|
IDATChunk();
|
||||||
|
IDATChunk(const IDATChunk& other) = default;
|
||||||
|
IDATChunk& operator =(const IDATChunk& other) = default;
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(IDAT_CHUNK)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief readStride
|
||||||
|
* \param d The device.
|
||||||
|
* \param y The current scanline.
|
||||||
|
* \param header The bitmap header.
|
||||||
|
* \param params The additional parameters (optional)
|
||||||
|
* \return The scanline as requested for QImage.
|
||||||
|
* \warning Call resetStrideRead() once before this one.
|
||||||
|
*/
|
||||||
|
QByteArray strideRead(QIODevice *d,
|
||||||
|
qint32 y,
|
||||||
|
const IHDRChunk *header,
|
||||||
|
const IPARChunk *params = nullptr,
|
||||||
|
const YUVSChunk *yuvs = nullptr) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief resetStrideRead
|
||||||
|
* Reset the stride read set the position at the beginning of the data and reset all buffers.
|
||||||
|
* \param d The device.
|
||||||
|
* \return True on success, otherwise false.
|
||||||
|
* \sa strideRead
|
||||||
|
* \note Must be called once before strideRead().
|
||||||
|
*/
|
||||||
|
bool resetStrideRead(QIODevice *d) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
quint32 strideSize(const IHDRChunk *header) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* *** RGFX IFF CHUNKS ***
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The RGHDChunk class
|
||||||
|
*/
|
||||||
|
class RGHDChunk : public IFFChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Compression {
|
||||||
|
Uncompressed = 0,
|
||||||
|
Xpk = 1, /**< any XPK-packer */
|
||||||
|
Zip = 2 /**< libzip (LZ77/ZIP) compression */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum BitmapType {
|
||||||
|
Planar8 = 0x0000, /**< unaligned planar 8 bit bitmap */
|
||||||
|
Chunky8 = 0x0001, /**< unaligned chunky 8 bit bitmap */
|
||||||
|
Rgb24 = 0x0002, /**< 3-byte 24 bit RGB triples */
|
||||||
|
Rgb32 = 0x0004, /**< 4-byte 32 bit ARGB quadruples */
|
||||||
|
Rgb15 = 0x0010, /**< 2-byte 15 bit RGB (x+3x5 bit integer) */
|
||||||
|
Rgb16 = 0x0020, /**< 2-byte 16 bit ARGB (1+3x5 bit integer) */
|
||||||
|
Rgb48 = 0x0040, /**< 6-byte 48 bit RGB (3x 16 bit integer) */
|
||||||
|
Rgb64 = 0x0080, /**< 8-byte 64 bit ARGB (4x 16 bit integer) */
|
||||||
|
Rgb96 = 0x0100, /**< 12-byte 96 bit RGB (3x 32 bit float) */
|
||||||
|
Rgb128 = 0x0200, /**< 16-byte 128 bit ARGB (4x 32 bit float) */
|
||||||
|
|
||||||
|
HasAlpha = (1 << 30), /**< set if A is meaningful */
|
||||||
|
HasInvAlpha = (1 << 31) /**< set if A is meaningful but inversed (A = 255 - alpha) */
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(BitmapTypes, BitmapType)
|
||||||
|
|
||||||
|
virtual ~RGHDChunk() override;
|
||||||
|
RGHDChunk();
|
||||||
|
RGHDChunk(const RGHDChunk&) = default;
|
||||||
|
RGHDChunk& operator=(const RGHDChunk&) = default;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(RGHD_CHUNK)
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
QSize size() const;
|
||||||
|
|
||||||
|
qint32 leftEdge() const;
|
||||||
|
|
||||||
|
qint32 topEdge() const;
|
||||||
|
|
||||||
|
qint32 width() const;
|
||||||
|
|
||||||
|
qint32 height() const;
|
||||||
|
|
||||||
|
qint32 pageWidth() const;
|
||||||
|
|
||||||
|
qint32 pageHeight() const;
|
||||||
|
|
||||||
|
quint32 depth() const;
|
||||||
|
|
||||||
|
quint32 pixelBits() const;
|
||||||
|
|
||||||
|
quint32 bytesPerLine() const;
|
||||||
|
|
||||||
|
Compression compression() const;
|
||||||
|
|
||||||
|
quint32 xAspect() const;
|
||||||
|
|
||||||
|
quint32 yAspect() const;
|
||||||
|
|
||||||
|
BitmapTypes bitmapType() const;
|
||||||
|
|
||||||
|
double aspectRatio() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The RCOLChunk class
|
||||||
|
*/
|
||||||
|
class RCOLChunk : public CMAPChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~RCOLChunk() override;
|
||||||
|
RCOLChunk();
|
||||||
|
RCOLChunk(const RCOLChunk& other) = default;
|
||||||
|
RCOLChunk& operator =(const RCOLChunk& other) = default;
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
virtual qint32 count() const override;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(RCOL_CHUNK)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual QList<QRgb> innerPalette() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The RFLGChunk class
|
||||||
|
*/
|
||||||
|
class RFLGChunk : public IFFChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Flag : quint32 {
|
||||||
|
FromGray = 0x08, /**< created from 8/16 bit gray source so R==G==B */
|
||||||
|
From8Bit = 0x10, /**< created from 8 bit source, so (R,G,B)&0xFF00 == ... & 0x00FF */
|
||||||
|
From4Bit = 0x20, /**< created from 4 bit source, so (R,G,B)&0xF0 == ... & 0x0F */
|
||||||
|
From8BitAlpha = 0x40, /**< 16/32 bit alpha created from 8 bit alpha source */
|
||||||
|
From16BitAlpha = 0x80 /**< 32 bit alpha created from 16 bit alpha source */
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(Flags, Flag)
|
||||||
|
|
||||||
|
virtual ~RFLGChunk() override;
|
||||||
|
RFLGChunk();
|
||||||
|
RFLGChunk(const RFLGChunk&) = default;
|
||||||
|
RFLGChunk& operator=(const RFLGChunk&) = default;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(RFLG_CHUNK)
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
Flags flags() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The RSCMChunk class
|
||||||
|
*/
|
||||||
|
class RSCMChunk : public IFFChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~RSCMChunk() override;
|
||||||
|
RSCMChunk();
|
||||||
|
RSCMChunk(const RSCMChunk&) = default;
|
||||||
|
RSCMChunk& operator=(const RSCMChunk&) = default;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(RSCM_CHUNK)
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief viewMode Default screenmode
|
||||||
|
*
|
||||||
|
* Since HAM modes only can be identified by their ID (or DIPF) you have to make sure,
|
||||||
|
* that rcsm_ViewMode is OR'ed with HAM_KEY for these (same for EHB and EXTRAHALFBRITE_KEY).
|
||||||
|
*
|
||||||
|
* Specific RTG ViewModes will lose their meaning, as soon as graphics are transferred between
|
||||||
|
* different systems, which is why the two LocalVM entries are considered obsolete.
|
||||||
|
*
|
||||||
|
* Always set the obsolete entries to 0xFFFFFFFF and avoid interpreting them.
|
||||||
|
* \return default screenmode
|
||||||
|
*/
|
||||||
|
quint32 viewMode() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief localVM0
|
||||||
|
* \obsolete obsolete local RTG
|
||||||
|
*/
|
||||||
|
quint32 localVM0() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief localVM1
|
||||||
|
* \obsolete obsolete local RTG
|
||||||
|
*/
|
||||||
|
quint32 localVM1() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool innerReadStructure(QIODevice *d) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The RBODChunk class
|
||||||
|
*/
|
||||||
|
class RBODChunk : public IFFChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~RBODChunk() override;
|
||||||
|
RBODChunk();
|
||||||
|
RBODChunk(const RBODChunk&) = default;
|
||||||
|
RBODChunk& operator=(const RBODChunk&) = default;
|
||||||
|
|
||||||
|
CHUNKID_DEFINE(RBOD_CHUNK)
|
||||||
|
|
||||||
|
virtual bool isValid() const override;
|
||||||
|
|
||||||
|
QByteArray strideRead(QIODevice *d,
|
||||||
|
qint32 y,
|
||||||
|
const RGHDChunk *header,
|
||||||
|
const RSCMChunk *rcsm = nullptr,
|
||||||
|
const RCOLChunk *rcol = nullptr) const;
|
||||||
|
|
||||||
|
bool resetStrideRead(QIODevice *d) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QByteArray deinterleave(const QByteArray &planes, qint32 y, const RGHDChunk *header, const RSCMChunk *rcsm = nullptr, const RCOLChunk *rcol = nullptr) const;
|
||||||
|
|
||||||
|
quint32 strideSize(const RGHDChunk *header) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* *** UNDOCUMENTED CHUNKS ***
|
* *** UNDOCUMENTED CHUNKS ***
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#ifndef DDS_DISABLE_STRIDE_ALIGNMENT
|
#ifndef DDS_DISABLE_STRIDE_ALIGNMENT
|
||||||
// Disable the stride aligment based on DDS pitch: it is known that some writers do not set it correctly
|
// Disable the stride alignment based on DDS pitch: it is known that some writers do not set it correctly
|
||||||
// #define DDS_DISABLE_STRIDE_ALIGNMENT
|
// #define DDS_DISABLE_STRIDE_ALIGNMENT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ bool EPSHandler::read(QImage *image)
|
|||||||
|
|
||||||
const QString gsExec = QStandardPaths::findExecutable(QStringLiteral("gs"));
|
const QString gsExec = QStandardPaths::findExecutable(QStringLiteral("gs"));
|
||||||
if (gsExec.isEmpty()) {
|
if (gsExec.isEmpty()) {
|
||||||
qCWarning(EPSPLUGIN) << "Couldn't find gs exectuable (from GhostScript) in PATH.";
|
qCWarning(EPSPLUGIN) << "Couldn't find gs executable (from GhostScript) in PATH.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
#include <ImathBox.h>
|
#include <ImathBox.h>
|
||||||
#include <ImfArray.h>
|
#include <ImfArray.h>
|
||||||
#include <ImfBoxAttribute.h>
|
#include <ImfBoxAttribute.h>
|
||||||
|
#include <ImfOpaqueAttribute.h>
|
||||||
#include <ImfChannelListAttribute.h>
|
#include <ImfChannelListAttribute.h>
|
||||||
#include <ImfCompressionAttribute.h>
|
#include <ImfCompressionAttribute.h>
|
||||||
#include <ImfConvert.h>
|
#include <ImfConvert.h>
|
||||||
@@ -227,25 +228,57 @@ static QImage::Format imageFormat(const Imf::RgbaInputFile &file)
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief viewList
|
* \brief viewList
|
||||||
* \param header
|
* \param header The image header.
|
||||||
* \return The list of available views.
|
* \return The list of available views.
|
||||||
|
* \note This plugin does not support compositing layers which are returned as single images.
|
||||||
*/
|
*/
|
||||||
static QStringList viewList(const Imf::Header &h)
|
static QStringList viewList(const Imf::Header &h)
|
||||||
{
|
{
|
||||||
QStringList l;
|
QStringList l;
|
||||||
if (auto views = h.findTypedAttribute<Imf::StringVectorAttribute>("multiView")) {
|
if (auto views = h.findTypedAttribute<Imf::StringVectorAttribute>("multiView")) {
|
||||||
|
// Internally OpenEXR first checks if the multiView attribute is present:
|
||||||
|
// if present, I have no other layers.
|
||||||
for (auto &&v : views->value()) {
|
for (auto &&v : views->value()) {
|
||||||
l << QString::fromStdString(v);
|
l << QString::fromStdString(v);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Recent versions of Photoshop save images by setting the layer.
|
||||||
|
// Channels are named Layer 1.A, Layer 1.B, etc., so I have to set
|
||||||
|
// the layer or the images will appear black.
|
||||||
|
auto channels = h.channels();
|
||||||
|
for (auto i = channels.begin(); i != channels.end(); ++i) {
|
||||||
|
auto name = QString::fromLatin1(i.name(), -1);
|
||||||
|
auto idx = name.indexOf(QChar(u'.'));
|
||||||
|
if (idx > -1)
|
||||||
|
l << name.left(idx);
|
||||||
|
}
|
||||||
|
l.removeDuplicates();
|
||||||
}
|
}
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QString setLayerName(Imf::RgbaInputFile &file, qint32 imageNumber = -1)
|
||||||
|
{
|
||||||
|
// set the image to load
|
||||||
|
QString layerName;
|
||||||
|
auto &&header = file.header();
|
||||||
|
if (imageNumber > -1) {
|
||||||
|
auto views = viewList(header);
|
||||||
|
if (imageNumber < views.count())
|
||||||
|
layerName = views.at(imageNumber);
|
||||||
|
}
|
||||||
|
// set the layer name
|
||||||
|
if (!layerName.isEmpty()) {
|
||||||
|
file.setLayerName(layerName.toStdString());
|
||||||
|
}
|
||||||
|
return layerName;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef QT_DEBUG
|
#ifdef QT_DEBUG
|
||||||
static void printAttributes(const Imf::Header &h)
|
static void printAttributes(const Imf::Header &h)
|
||||||
{
|
{
|
||||||
for (auto i = h.begin(); i != h.end(); ++i) {
|
for (auto i = h.begin(); i != h.end(); ++i) {
|
||||||
qCDebug(LOG_EXRPLUGIN) << i.name();
|
qCDebug(LOG_EXRPLUGIN) << i.name() << i.attribute().typeName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -340,15 +373,29 @@ static void readColorSpace(const Imf::Header &header, QImage &image)
|
|||||||
{
|
{
|
||||||
// final color operations
|
// final color operations
|
||||||
QColorSpace cs;
|
QColorSpace cs;
|
||||||
if (auto chroma = header.findTypedAttribute<Imf::ChromaticitiesAttribute>("chromaticities")) {
|
|
||||||
auto &&v = chroma->value();
|
// Photoshop 2026 allow to save the ICC profile as "iccProfile" attribute
|
||||||
cs = QColorSpace(QPointF(v.white.x, v.white.y),
|
if (auto iccProfile = header.findTypedAttribute<Imf::OpaqueAttribute>("iccProfile")) {
|
||||||
QPointF(v.red.x, v.red.y),
|
auto &&v = iccProfile->data();
|
||||||
QPointF(v.green.x, v.green.y),
|
cs = QColorSpace::fromIccProfile(QByteArray::fromRawData(v, v.size()));
|
||||||
QPointF(v.blue.x, v.blue.y),
|
|
||||||
QColorSpace::TransferFunction::Linear);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cs.isValid()) {
|
if (!cs.isValid()) {
|
||||||
|
// Creating the ICC profile from Chromaticities
|
||||||
|
if (auto chroma = header.findTypedAttribute<Imf::ChromaticitiesAttribute>("chromaticities")) {
|
||||||
|
auto &&v = chroma->value();
|
||||||
|
cs = QColorSpace(QPointF(v.white.x, v.white.y),
|
||||||
|
QPointF(v.red.x, v.red.y),
|
||||||
|
QPointF(v.green.x, v.green.y),
|
||||||
|
QPointF(v.blue.x, v.blue.y),
|
||||||
|
QColorSpace::TransferFunction::Linear);
|
||||||
|
if (cs.isValid())
|
||||||
|
cs.setDescription(QStringLiteral("Embedded RGB (linear)"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cs.isValid()) {
|
||||||
|
// Use a linear profile
|
||||||
cs = QColorSpace(QColorSpace::SRgbLinear);
|
cs = QColorSpace(QColorSpace::SRgbLinear);
|
||||||
}
|
}
|
||||||
image.setColorSpace(cs);
|
image.setColorSpace(cs);
|
||||||
@@ -377,12 +424,7 @@ bool EXRHandler::read(QImage *outImage)
|
|||||||
auto &&header = file.header();
|
auto &&header = file.header();
|
||||||
|
|
||||||
// set the image to load
|
// set the image to load
|
||||||
if (m_imageNumber > -1) {
|
auto layerName = setLayerName(file, m_imageNumber);
|
||||||
auto views = viewList(header);
|
|
||||||
if (m_imageNumber < views.count()) {
|
|
||||||
file.setLayerName(views.at(m_imageNumber).toStdString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get image info
|
// get image info
|
||||||
Imath::Box2i dw = file.dataWindow();
|
Imath::Box2i dw = file.dataWindow();
|
||||||
@@ -401,9 +443,13 @@ bool EXRHandler::read(QImage *outImage)
|
|||||||
qCWarning(LOG_EXRPLUGIN) << "Failed to allocate image, invalid size?" << QSize(width, height);
|
qCWarning(LOG_EXRPLUGIN) << "Failed to allocate image, invalid size?" << QSize(width, height);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!layerName.isEmpty()) {
|
||||||
|
image.setText(QStringLiteral("EXRLayerName"), layerName);
|
||||||
|
}
|
||||||
|
|
||||||
Imf::Array2D<Imf::Rgba> pixels;
|
Imf::Array2D<Imf::Rgba> pixels;
|
||||||
pixels.resizeErase(EXR_LINES_PER_BLOCK, width);
|
pixels.resizeErase(EXR_LINES_PER_BLOCK, width);
|
||||||
|
std::memset(pixels[0], 0, sizeof(Imf::Rgba) * EXR_LINES_PER_BLOCK * width);
|
||||||
bool isRgba = image.hasAlphaChannel();
|
bool isRgba = image.hasAlphaChannel();
|
||||||
|
|
||||||
for (int y = 0, n = 0; y < height; y += n) {
|
for (int y = 0, n = 0; y < height; y += n) {
|
||||||
@@ -687,12 +733,7 @@ QVariant EXRHandler::option(ImageOption option) const
|
|||||||
try {
|
try {
|
||||||
K_IStream istr(d);
|
K_IStream istr(d);
|
||||||
Imf::RgbaInputFile file(istr);
|
Imf::RgbaInputFile file(istr);
|
||||||
if (m_imageNumber > -1) { // set the image to read
|
setLayerName(file, m_imageNumber);
|
||||||
auto views = viewList(file.header());
|
|
||||||
if (m_imageNumber < views.count()) {
|
|
||||||
file.setLayerName(views.at(m_imageNumber).toStdString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Imath::Box2i dw = file.dataWindow();
|
Imath::Box2i dw = file.dataWindow();
|
||||||
v = QVariant(QSize(dw.max.x - dw.min.x + 1, dw.max.y - dw.min.y + 1));
|
v = QVariant(QSize(dw.max.x - dw.min.x + 1, dw.max.y - dw.min.y + 1));
|
||||||
} catch (const std::exception &) {
|
} catch (const std::exception &) {
|
||||||
@@ -712,6 +753,7 @@ QVariant EXRHandler::option(ImageOption option) const
|
|||||||
try {
|
try {
|
||||||
K_IStream istr(d);
|
K_IStream istr(d);
|
||||||
Imf::RgbaInputFile file(istr);
|
Imf::RgbaInputFile file(istr);
|
||||||
|
setLayerName(file, m_imageNumber);
|
||||||
v = QVariant::fromValue(imageFormat(file));
|
v = QVariant::fromValue(imageFormat(file));
|
||||||
} catch (const std::exception &) {
|
} catch (const std::exception &) {
|
||||||
// broken file or unsupported version
|
// broken file or unsupported version
|
||||||
@@ -786,12 +828,9 @@ bool EXRHandler::canRead(QIODevice *device)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if OPENEXR_VERSION_MAJOR == 3 && OPENEXR_VERSION_MINOR > 2
|
|
||||||
// openexpr >= 3.3 uses seek and tell extensively
|
|
||||||
if (device->isSequential()) {
|
if (device->isSequential()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
const QByteArray head = device->peek(4);
|
const QByteArray head = device->peek(4);
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ private:
|
|||||||
* - 7: lossy 4-by-4 pixel block compression, fields are compressed more
|
* - 7: lossy 4-by-4 pixel block compression, fields are compressed more
|
||||||
* - 8: lossy DCT based compression, in blocks of 32 scanlines. More efficient for partial buffer access.
|
* - 8: lossy DCT based compression, in blocks of 32 scanlines. More efficient for partial buffer access.
|
||||||
* - 9: lossy DCT based compression, in blocks of 256 scanlines. More efficient space wise and faster to decode full frames than DWAA_COMPRESSION.
|
* - 9: lossy DCT based compression, in blocks of 256 scanlines. More efficient space wise and faster to decode full frames than DWAA_COMPRESSION.
|
||||||
|
* - 10: High-Throughput JPEG2000 (HTJ2K), 256 scanlines (requires OpenEXR 3.4+).
|
||||||
|
* - 11: High-Throughput JPEG2000 (HTJ2K), 32 scanlines (requires OpenEXR 3.4+).
|
||||||
*/
|
*/
|
||||||
qint32 m_compressionRatio;
|
qint32 m_compressionRatio;
|
||||||
|
|
||||||
|
|||||||
@@ -54,10 +54,10 @@ public:
|
|||||||
{
|
{
|
||||||
return width() > 0 && height() > 0 && width() <= HDR_MAX_IMAGE_WIDTH && height() <= HDR_MAX_IMAGE_HEIGHT;
|
return width() > 0 && height() > 0 && width() <= HDR_MAX_IMAGE_WIDTH && height() <= HDR_MAX_IMAGE_HEIGHT;
|
||||||
}
|
}
|
||||||
qint32 width() const { return(m_size.width()); }
|
qint32 width() const { return m_size.width(); }
|
||||||
qint32 height() const { return(m_size.height()); }
|
qint32 height() const { return m_size.height(); }
|
||||||
QString software() const { return(m_software); }
|
QString software() const { return m_software; }
|
||||||
QImageIOHandler::Transformations transformation() const { return(m_transformation); }
|
QImageIOHandler::Transformations transformation() const { return m_transformation; }
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief colorSpace
|
* \brief colorSpace
|
||||||
@@ -73,7 +73,7 @@ public:
|
|||||||
* 0.600 0.150 0.060 0.333 0.333" for red, green, blue
|
* 0.600 0.150 0.060 0.333 0.333" for red, green, blue
|
||||||
* and white, respectively.
|
* and white, respectively.
|
||||||
*/
|
*/
|
||||||
QColorSpace colorSpace() const { return(m_colorSpace); }
|
QColorSpace colorSpace() const { return m_colorSpace; }
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief exposure
|
* \brief exposure
|
||||||
@@ -125,14 +125,16 @@ public:
|
|||||||
Header h;
|
Header h;
|
||||||
|
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
int len;
|
int len = 0;
|
||||||
QByteArray line(MAXLINE + 1, Qt::Uninitialized);
|
QByteArray line(MAXLINE, char());
|
||||||
QByteArray format;
|
QByteArray format;
|
||||||
|
|
||||||
// Parse header
|
// Parse header
|
||||||
do {
|
do {
|
||||||
len = device->readLine(line.data(), MAXLINE);
|
len = device->readLine(line.data(), line.size());
|
||||||
|
if (len < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (line.startsWith("FORMAT=")) {
|
if (line.startsWith("FORMAT=")) {
|
||||||
format = line.mid(7, len - 7).trimmed();
|
format = line.mid(7, len - 7).trimmed();
|
||||||
}
|
}
|
||||||
@@ -173,7 +175,11 @@ public:
|
|||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = device->readLine(line.data(), MAXLINE);
|
len = device->readLine(line.data(), line.size());
|
||||||
|
if (len < 0) {
|
||||||
|
qCDebug(HDRPLUGIN) << "Invalid HDR file, error while reading the first line after the header";
|
||||||
|
return h;
|
||||||
|
}
|
||||||
line.resize(len);
|
line.resize(len);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -241,7 +247,7 @@ static bool Read_Old_Line(uchar *image, int width, QDataStream &s)
|
|||||||
s >> image[2];
|
s >> image[2];
|
||||||
s >> image[3];
|
s >> image[3];
|
||||||
|
|
||||||
if (s.atEnd()) {
|
if (s.status() != QDataStream::Ok) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,8 +328,7 @@ static bool LoadHDR(QDataStream &s, const Header& h, QImage &img)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray lineArray;
|
QByteArray lineArray(4 * width, char());
|
||||||
lineArray.resize(4 * width);
|
|
||||||
uchar *image = reinterpret_cast<uchar *>(lineArray.data());
|
uchar *image = reinterpret_cast<uchar *>(lineArray.data());
|
||||||
|
|
||||||
for (int cline = 0; cline < height; cline++) {
|
for (int cline = 0; cline < height; cline++) {
|
||||||
@@ -335,20 +340,24 @@ static bool LoadHDR(QDataStream &s, const Header& h, QImage &img)
|
|||||||
|
|
||||||
// determine scanline type
|
// determine scanline type
|
||||||
if ((width < MINELEN) || (MAXELEN < width)) {
|
if ((width < MINELEN) || (MAXELEN < width)) {
|
||||||
Read_Old_Line(image, width, s);
|
if (!Read_Old_Line(image, width, s)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
RGBE_To_QRgbLine(image, scanline, h);
|
RGBE_To_QRgbLine(image, scanline, h);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
s >> val;
|
s >> val;
|
||||||
|
|
||||||
if (s.atEnd()) {
|
if (s.status() != QDataStream::Ok) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (val != 2) {
|
if (val != 2) {
|
||||||
s.device()->ungetChar(val);
|
s.device()->ungetChar(val);
|
||||||
Read_Old_Line(image, width, s);
|
if (!Read_Old_Line(image, width, s)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
RGBE_To_QRgbLine(image, scanline, h);
|
RGBE_To_QRgbLine(image, scanline, h);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -357,13 +366,15 @@ static bool LoadHDR(QDataStream &s, const Header& h, QImage &img)
|
|||||||
s >> image[2];
|
s >> image[2];
|
||||||
s >> image[3];
|
s >> image[3];
|
||||||
|
|
||||||
if (s.atEnd()) {
|
if (s.status() != QDataStream::Ok) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((image[1] != 2) || (image[2] & 128)) {
|
if ((image[1] != 2) || (image[2] & 128)) {
|
||||||
image[0] = 2;
|
image[0] = 2;
|
||||||
Read_Old_Line(image + 4, width - 1, s);
|
if (!Read_Old_Line(image + 4, width - 1, s)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
RGBE_To_QRgbLine(image, scanline, h);
|
RGBE_To_QRgbLine(image, scanline, h);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -377,7 +388,7 @@ static bool LoadHDR(QDataStream &s, const Header& h, QImage &img)
|
|||||||
for (int i = 0, len = int(lineArray.size()); i < 4; i++) {
|
for (int i = 0, len = int(lineArray.size()); i < 4; i++) {
|
||||||
for (int j = 0; j < width;) {
|
for (int j = 0; j < width;) {
|
||||||
s >> code;
|
s >> code;
|
||||||
if (s.atEnd()) {
|
if (s.status() != QDataStream::Ok) {
|
||||||
qCDebug(HDRPLUGIN) << "Truncated HDR file";
|
qCDebug(HDRPLUGIN) << "Truncated HDR file";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -505,11 +516,11 @@ bool HDRHandler::canRead(QIODevice *device)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the .pic taken from official test cases does not start with this string but can be loaded.
|
// the .pic taken from official test cases does not start with this string but can be loaded.
|
||||||
if(device->peek(11) == "#?RADIANCE\n" || device->peek(7) == "#?RGBE\n") {
|
if (device->peek(11) == "#?RADIANCE\n" || device->peek(7) == "#?RGBE\n") {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// allow to load offical test cases: https://radsite.lbl.gov/radiance/framed.html
|
// allow to load official test cases: https://radsite.lbl.gov/radiance/framed.html
|
||||||
device->startTransaction();
|
device->startTransaction();
|
||||||
auto h = HDRHandlerPrivate::readHeader(device);
|
auto h = HDRHandlerPrivate::readHeader(device);
|
||||||
device->rollbackTransaction();
|
device->rollbackTransaction();
|
||||||
|
|||||||
@@ -262,14 +262,26 @@ static void addMetadata(QImage &img, const IFOR_Chunk *form)
|
|||||||
|
|
||||||
// if no explicit resolution was found, apply the aspect ratio to the default one
|
// if no explicit resolution was found, apply the aspect ratio to the default one
|
||||||
if (!resChanged) {
|
if (!resChanged) {
|
||||||
auto headers = IFFChunk::searchT<BMHDChunk>(form);
|
if (form->formType() == IMAG_FORM_TYPE) {
|
||||||
if (!headers.isEmpty()) {
|
auto params = IFFChunk::searchT<IPARChunk>(form);
|
||||||
auto xr = headers.first()->xAspectRatio();
|
if (!params.isEmpty()) {
|
||||||
auto yr = headers.first()->yAspectRatio();
|
img.setDotsPerMeterY(img.dotsPerMeterY() * params.first()->aspectRatio());
|
||||||
if (xr > 0 && yr > 0 && xr > yr) {
|
}
|
||||||
img.setDotsPerMeterX(img.dotsPerMeterX() * yr / xr);
|
} else if (form->formType() == RGFX_FORM_TYPE) {
|
||||||
} else if (xr > 0 && yr > 0 && xr < yr) {
|
auto headers = IFFChunk::searchT<RGHDChunk>(form);
|
||||||
img.setDotsPerMeterY(img.dotsPerMeterY() * xr / yr);
|
if (!headers.isEmpty()) {
|
||||||
|
img.setDotsPerMeterY(img.dotsPerMeterY() * headers.first()->aspectRatio());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto headers = IFFChunk::searchT<BMHDChunk>(form);
|
||||||
|
if (!headers.isEmpty()) {
|
||||||
|
auto xr = headers.first()->xAspectRatio();
|
||||||
|
auto yr = headers.first()->yAspectRatio();
|
||||||
|
if (xr > 0 && yr > 0 && xr > yr) {
|
||||||
|
img.setDotsPerMeterX(img.dotsPerMeterX() * yr / xr);
|
||||||
|
} else if (xr > 0 && yr > 0 && xr < yr) {
|
||||||
|
img.setDotsPerMeterY(img.dotsPerMeterY() * xr / yr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,7 +339,6 @@ bool IFFHandler::readStandardImage(QImage *image)
|
|||||||
// show the first one (I don't have a sample with many images)
|
// show the first one (I don't have a sample with many images)
|
||||||
auto headers = IFFChunk::searchT<BMHDChunk>(form);
|
auto headers = IFFChunk::searchT<BMHDChunk>(form);
|
||||||
if (headers.isEmpty()) {
|
if (headers.isEmpty()) {
|
||||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage(): no supported image found";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,7 +437,6 @@ bool IFFHandler::readMayaImage(QImage *image)
|
|||||||
// show the first one (I don't have a sample with many images)
|
// show the first one (I don't have a sample with many images)
|
||||||
auto headers = IFFChunk::searchT<TBHDChunk>(form);
|
auto headers = IFFChunk::searchT<TBHDChunk>(form);
|
||||||
if (headers.isEmpty()) {
|
if (headers.isEmpty()) {
|
||||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readMayaImage(): no supported image found";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,11 +474,133 @@ bool IFFHandler::readMayaImage(QImage *image)
|
|||||||
painter.setCompositionMode(QPainter::CompositionMode_Source);
|
painter.setCompositionMode(QPainter::CompositionMode_Source);
|
||||||
painter.drawImage(tp, ti);
|
painter.drawImage(tp, ti);
|
||||||
}
|
}
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
|
|
||||||
img.mirror(false, true);
|
|
||||||
#else
|
|
||||||
img.flip(Qt::Orientation::Vertical);
|
img.flip(Qt::Orientation::Vertical);
|
||||||
#endif
|
addMetadata(img, form);
|
||||||
|
|
||||||
|
*image = img;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IFFHandler::readCDIImage(QImage *image)
|
||||||
|
{
|
||||||
|
auto forms = d->searchForms<FORMChunk>();
|
||||||
|
if (forms.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto cin = qBound(0, currentImageNumber(), int(forms.size() - 1));
|
||||||
|
auto &&form = forms.at(cin);
|
||||||
|
|
||||||
|
// show the first one (I don't have a sample with many images)
|
||||||
|
auto headers = IFFChunk::searchT<IHDRChunk>(form);
|
||||||
|
if (headers.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the image
|
||||||
|
auto &&header = headers.first();
|
||||||
|
auto img = imageAlloc(header->size(), form->format());
|
||||||
|
if (img.isNull()) {
|
||||||
|
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readCDIImage(): error while allocating the image";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the palette
|
||||||
|
if (img.format() == QImage::Format_Indexed8) {
|
||||||
|
auto pltes = IFFChunk::searchT<PLTEChunk>(form);
|
||||||
|
if (!pltes.isEmpty()) {
|
||||||
|
img.setColorTable(pltes.first()->palette());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// decoding the image
|
||||||
|
auto bodies = IFFChunk::searchT<IDATChunk>(form);
|
||||||
|
if (bodies.isEmpty()) {
|
||||||
|
img.fill(0);
|
||||||
|
} else {
|
||||||
|
auto &&body = bodies.first();
|
||||||
|
if (!body->resetStrideRead(device())) {
|
||||||
|
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readCDIImage(): error while reading image data";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto pars = IFFChunk::searchT<IPARChunk>(form);
|
||||||
|
auto yuvs = IFFChunk::searchT<YUVSChunk>(form);
|
||||||
|
for (auto y = 0, h = img.height(); y < h; ++y) {
|
||||||
|
auto line = reinterpret_cast<char*>(img.scanLine(y));
|
||||||
|
auto ba = body->strideRead(device(), y, header,
|
||||||
|
pars.isEmpty() ? nullptr : pars.first(),
|
||||||
|
yuvs.isEmpty() ? nullptr : yuvs.first());
|
||||||
|
if (ba.isEmpty()) {
|
||||||
|
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readCDIImage(): error while reading image scanline";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(line, ba.constData(), std::min(img.bytesPerLine(), ba.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set metadata (including image resolution)
|
||||||
|
addMetadata(img, form);
|
||||||
|
|
||||||
|
*image = img;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IFFHandler::readRGFXImage(QImage *image)
|
||||||
|
{
|
||||||
|
auto forms = d->searchForms<FORMChunk>();
|
||||||
|
if (forms.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto cin = qBound(0, currentImageNumber(), int(forms.size() - 1));
|
||||||
|
auto &&form = forms.at(cin);
|
||||||
|
|
||||||
|
// show the first one (I don't have a sample with many images)
|
||||||
|
auto headers = IFFChunk::searchT<RGHDChunk>(form);
|
||||||
|
if (headers.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the image
|
||||||
|
auto &&header = headers.first();
|
||||||
|
auto img = imageAlloc(header->size(), form->format());
|
||||||
|
if (img.isNull()) {
|
||||||
|
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readRGFXImage(): error while allocating the image";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the palette
|
||||||
|
if (img.format() == QImage::Format_Indexed8) {
|
||||||
|
auto pltes = IFFChunk::searchT<RCOLChunk>(form);
|
||||||
|
if (!pltes.isEmpty()) {
|
||||||
|
img.setColorTable(pltes.first()->palette());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// decoding the image
|
||||||
|
auto bodies = IFFChunk::searchT<RBODChunk>(form);
|
||||||
|
if (bodies.isEmpty()) {
|
||||||
|
img.fill(0);
|
||||||
|
} else {
|
||||||
|
auto &&body = bodies.first();
|
||||||
|
if (!body->resetStrideRead(device())) {
|
||||||
|
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readRGFXImage(): error while reading image data";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto rcsms = IFFChunk::searchT<RSCMChunk>(form);
|
||||||
|
auto rcols = IFFChunk::searchT<RCOLChunk>(form);
|
||||||
|
for (auto y = 0, h = img.height(); y < h; ++y) {
|
||||||
|
auto line = reinterpret_cast<char*>(img.scanLine(y));
|
||||||
|
auto ba = body->strideRead(device(), y, header,
|
||||||
|
rcsms.isEmpty() ? nullptr : rcsms.first(),
|
||||||
|
rcols.isEmpty() ? nullptr : rcols.first());
|
||||||
|
if (ba.isEmpty()) {
|
||||||
|
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readRGFXImage(): error while reading image scanline";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(line, ba.constData(), std::min(img.bytesPerLine(), ba.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set metadata (including image resolution)
|
||||||
addMetadata(img, form);
|
addMetadata(img, form);
|
||||||
|
|
||||||
*image = img;
|
*image = img;
|
||||||
@@ -490,6 +622,14 @@ bool IFFHandler::read(QImage *image)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (readCDIImage(image)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readRGFXImage(image)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::read(): no supported image found";
|
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::read(): no supported image found";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ private:
|
|||||||
|
|
||||||
bool readMayaImage(QImage *image);
|
bool readMayaImage(QImage *image);
|
||||||
|
|
||||||
|
bool readCDIImage(QImage *image);
|
||||||
|
|
||||||
|
bool readRGFXImage(QImage *image);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QScopedPointer<IFFHandlerPrivate> d;
|
const QScopedPointer<IFFHandlerPrivate> d;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -253,20 +253,27 @@ public:
|
|||||||
bool jp2ToImage(QImage *img) const
|
bool jp2ToImage(QImage *img) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(img->depth() == 8 * sizeof(T) || img->depth() == 32 * sizeof(T));
|
Q_ASSERT(img->depth() == 8 * sizeof(T) || img->depth() == 32 * sizeof(T));
|
||||||
for (qint32 c = 0, cc = m_jp2_image->numcomps; c < cc; ++c) {
|
if (img->width() < 1 || img->height() < 1) {
|
||||||
auto cs = cc == 1 ? 1 : 4;
|
return false;
|
||||||
|
}
|
||||||
|
auto maxChannels = qint32(img->bytesPerLine() / sizeof(T) / img->width());
|
||||||
|
for (qint32 c = 0, cc = std::min(qint32(m_jp2_image->numcomps), maxChannels); c < cc; ++c) {
|
||||||
|
auto cs = std::min(cc == 1 ? 1 : 4, maxChannels);
|
||||||
auto &&jc = m_jp2_image->comps[c];
|
auto &&jc = m_jp2_image->comps[c];
|
||||||
if (jc.data == nullptr)
|
if (jc.data == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
if (qint32(jc.w) != img->width() || qint32(jc.h) != img->height())
|
}
|
||||||
|
if (qint32(jc.w) != img->width() || qint32(jc.h) != img->height()) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// discriminate between int and float (avoid complicating things by creating classes with template specializations)
|
// discriminate between int and float (avoid complicating things by creating classes with template specializations)
|
||||||
if (std::numeric_limits<T>::is_integer) {
|
if (std::numeric_limits<T>::is_integer) {
|
||||||
auto divisor = 1;
|
auto divisor = 1ull;
|
||||||
if (jc.prec > sizeof(T) * 8) {
|
auto prec = std::min(size_t(jc.prec), sizeof(*jc.data) * 8);
|
||||||
|
if (prec > sizeof(T) * 8 && prec < 64) {
|
||||||
// convert to the wanted precision (e.g. 16-bit -> 8-bit: divisor = 65535 / 255 = 257)
|
// convert to the wanted precision (e.g. 16-bit -> 8-bit: divisor = 65535 / 255 = 257)
|
||||||
divisor = std::max(1, int(((1ll << jc.prec) - 1) / ((1ll << (sizeof(T) * 8)) - 1)));
|
divisor = std::max(1ull, (((1ull << prec) - 1) / ((1ull << (sizeof(T) * 8)) - 1)));
|
||||||
}
|
}
|
||||||
for (qint32 y = 0, h = img->height(); y < h; ++y) {
|
for (qint32 y = 0, h = img->height(); y < h; ++y) {
|
||||||
auto ptr = reinterpret_cast<T *>(img->scanLine(y));
|
auto ptr = reinterpret_cast<T *>(img->scanLine(y));
|
||||||
@@ -352,11 +359,15 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OpenJPEG uses a shadow copy @32-bit/channel so we need to do a check
|
// OpenJPEG uses a shadow copy @32-bit/channel so we need to do a check
|
||||||
auto maxBytes = qint64(QImageReader::allocationLimit()) * 1024 * 1024;
|
const int allocationLimit = QImageReader::allocationLimit();
|
||||||
auto neededBytes = qint64(width) * height * nchannels * 4;
|
if (allocationLimit > 0) {
|
||||||
if (maxBytes > 0 && neededBytes > maxBytes) {
|
auto maxBytes = qint64(allocationLimit) * 1024 * 1024;
|
||||||
qCCritical(LOG_JP2PLUGIN) << "Allocation limit set to" << (maxBytes / 1024 / 1024) << "MiB but" << (neededBytes / 1024 / 1024) << "MiB are needed!";
|
auto neededBytes = qint64(width) * height * nchannels * 4;
|
||||||
return false;
|
if (maxBytes > 0 && neededBytes > maxBytes) {
|
||||||
|
qCCritical(LOG_JP2PLUGIN) << "Allocation limit set to" << (maxBytes / 1024 / 1024) << "MiB but" << (neededBytes / 1024 / 1024)
|
||||||
|
<< "MiB are needed!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ bool QJpegXLHandler::ensureDecoder()
|
|||||||
int num_worker_threads = QThread::idealThreadCount();
|
int num_worker_threads = QThread::idealThreadCount();
|
||||||
if (!m_runner && num_worker_threads >= 4) {
|
if (!m_runner && num_worker_threads >= 4) {
|
||||||
/* use half of the threads because plug-in is usually used in environment
|
/* use half of the threads because plug-in is usually used in environment
|
||||||
* where application performs another tasks in backround (pre-load other images) */
|
* where application performs other tasks in the background (pre-load other images) */
|
||||||
num_worker_threads = num_worker_threads / 2;
|
num_worker_threads = num_worker_threads / 2;
|
||||||
num_worker_threads = qBound(2, num_worker_threads, 64);
|
num_worker_threads = qBound(2, num_worker_threads, 64);
|
||||||
m_runner = JxlThreadParallelRunnerCreate(nullptr, num_worker_threads);
|
m_runner = JxlThreadParallelRunnerCreate(nullptr, num_worker_threads);
|
||||||
@@ -258,14 +258,16 @@ bool QJpegXLHandler::countALLFrames()
|
|||||||
bool is_gray = m_basicinfo.num_color_channels == 1 && m_basicinfo.alpha_bits == 0;
|
bool is_gray = m_basicinfo.num_color_channels == 1 && m_basicinfo.alpha_bits == 0;
|
||||||
JxlColorEncoding color_encoding;
|
JxlColorEncoding color_encoding;
|
||||||
if (m_basicinfo.uses_original_profile == JXL_FALSE && m_basicinfo.have_animation == JXL_FALSE) {
|
if (m_basicinfo.uses_original_profile == JXL_FALSE && m_basicinfo.have_animation == JXL_FALSE) {
|
||||||
const JxlCmsInterface *jxlcms = JxlGetDefaultCms();
|
if (!is_gray) {
|
||||||
if (jxlcms) {
|
const JxlCmsInterface *jxlcms = JxlGetDefaultCms();
|
||||||
status = JxlDecoderSetCms(m_decoder, *jxlcms);
|
if (jxlcms) {
|
||||||
if (status != JXL_DEC_SUCCESS) {
|
status = JxlDecoderSetCms(m_decoder, *jxlcms);
|
||||||
qCWarning(LOG_JXLPLUGIN, "JxlDecoderSetCms ERROR");
|
if (status != JXL_DEC_SUCCESS) {
|
||||||
|
qCWarning(LOG_JXLPLUGIN, "JxlDecoderSetCms ERROR");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCWarning(LOG_JXLPLUGIN, "No JPEG XL CMS Interface");
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
qCWarning(LOG_JXLPLUGIN, "No JPEG XL CMS Interface");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JxlColorEncodingSetToSRGB(&color_encoding, is_gray ? JXL_TRUE : JXL_FALSE);
|
JxlColorEncodingSetToSRGB(&color_encoding, is_gray ? JXL_TRUE : JXL_FALSE);
|
||||||
@@ -853,7 +855,11 @@ bool QJpegXLHandler::write(const QImage &image)
|
|||||||
|
|
||||||
size_t pixel_count = size_t(image.width()) * image.height();
|
size_t pixel_count = size_t(image.width()) * image.height();
|
||||||
if (MAX_IMAGE_PIXELS && pixel_count > MAX_IMAGE_PIXELS) {
|
if (MAX_IMAGE_PIXELS && pixel_count > MAX_IMAGE_PIXELS) {
|
||||||
qCWarning(LOG_JXLPLUGIN, "Image (%dx%d) will not be saved because it has more than %d megapixels!", image.width(), image.height(), MAX_IMAGE_PIXELS / 1024 / 1024);
|
qCWarning(LOG_JXLPLUGIN,
|
||||||
|
"Image (%dx%d) will not be saved because it has more than %d megapixels!",
|
||||||
|
image.width(),
|
||||||
|
image.height(),
|
||||||
|
MAX_IMAGE_PIXELS / 1024 / 1024);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1838,17 +1844,20 @@ bool QJpegXLHandler::rewind()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const JxlCmsInterface *jxlcms = JxlGetDefaultCms();
|
bool is_gray = m_basicinfo.num_color_channels == 1 && m_basicinfo.alpha_bits == 0;
|
||||||
if (jxlcms) {
|
|
||||||
status = JxlDecoderSetCms(m_decoder, *jxlcms);
|
if (!is_gray) {
|
||||||
if (status != JXL_DEC_SUCCESS) {
|
const JxlCmsInterface *jxlcms = JxlGetDefaultCms();
|
||||||
qCWarning(LOG_JXLPLUGIN, "JxlDecoderSetCms ERROR");
|
if (jxlcms) {
|
||||||
|
status = JxlDecoderSetCms(m_decoder, *jxlcms);
|
||||||
|
if (status != JXL_DEC_SUCCESS) {
|
||||||
|
qCWarning(LOG_JXLPLUGIN, "JxlDecoderSetCms ERROR");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCWarning(LOG_JXLPLUGIN, "No JPEG XL CMS Interface");
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
qCWarning(LOG_JXLPLUGIN, "No JPEG XL CMS Interface");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_gray = m_basicinfo.num_color_channels == 1 && m_basicinfo.alpha_bits == 0;
|
|
||||||
JxlColorEncoding color_encoding;
|
JxlColorEncoding color_encoding;
|
||||||
JxlColorEncodingSetToSRGB(&color_encoding, is_gray ? JXL_TRUE : JXL_FALSE);
|
JxlColorEncodingSetToSRGB(&color_encoding, is_gray ? JXL_TRUE : JXL_FALSE);
|
||||||
JxlDecoderSetPreferredColorProfile(m_decoder, &color_encoding);
|
JxlDecoderSetPreferredColorProfile(m_decoder, &color_encoding);
|
||||||
@@ -1999,6 +2008,11 @@ bool QJpegXLHandler::extractBox(QByteArray &output, size_t container_size)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rawboxsize > 8388608) { // 8MB limit
|
||||||
|
qCWarning(LOG_JXLPLUGIN, "Skipped decoding of big JXL metadata box");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
output.resize(rawboxsize);
|
output.resize(rawboxsize);
|
||||||
status = JxlDecoderSetBoxBuffer(m_decoder, reinterpret_cast<uint8_t *>(output.data()), output.size());
|
status = JxlDecoderSetBoxBuffer(m_decoder, reinterpret_cast<uint8_t *>(output.data()), output.size());
|
||||||
if (status != JXL_DEC_SUCCESS) {
|
if (status != JXL_DEC_SUCCESS) {
|
||||||
@@ -2012,7 +2026,7 @@ bool QJpegXLHandler::extractBox(QByteArray &output, size_t container_size)
|
|||||||
if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) {
|
if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) {
|
||||||
size_t bytes_remains = JxlDecoderReleaseBoxBuffer(m_decoder);
|
size_t bytes_remains = JxlDecoderReleaseBoxBuffer(m_decoder);
|
||||||
|
|
||||||
if (output.size() > 4194304) { // approx. 4MB limit for decompressed metadata box
|
if (output.size() > 33554432) { // approx. 32MB (4*8) limit for decompressed metadata box
|
||||||
qCWarning(LOG_JXLPLUGIN, "JXL metadata box is too large");
|
qCWarning(LOG_JXLPLUGIN, "JXL metadata box is too large");
|
||||||
m_parseState = ParseJpegXLError;
|
m_parseState = ParseJpegXLError;
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -35,17 +35,22 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
Q_DECLARE_LOGGING_CATEGORY(LOG_JXRPLUGIN)
|
Q_DECLARE_LOGGING_CATEGORY(LOG_JXRPLUGIN)
|
||||||
|
|
||||||
|
#ifdef QT_DEBUG
|
||||||
|
Q_LOGGING_CATEGORY(LOG_JXRPLUGIN, "kf.imageformats.plugins.jxr", QtDebugMsg)
|
||||||
|
#else
|
||||||
Q_LOGGING_CATEGORY(LOG_JXRPLUGIN, "kf.imageformats.plugins.jxr", QtWarningMsg)
|
Q_LOGGING_CATEGORY(LOG_JXRPLUGIN, "kf.imageformats.plugins.jxr", QtWarningMsg)
|
||||||
|
#endif
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Support for float images
|
* Support for float images
|
||||||
*
|
*
|
||||||
* NOTE: Float images have values greater than 1 so they need an additional in place conversion.
|
* NOTE: Float images have values greater than 1 so they need an additional in place conversion.
|
||||||
*/
|
*/
|
||||||
// #define JXR_DENY_FLOAT_IMAGE
|
// #define JXR_DENY_FLOAT_IMAGE // default commented
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Remove the neeeds of additional memory by disabling the conversion between
|
* Remove the needs of additional memory by disabling the conversion between
|
||||||
* different color depths (e.g. RGBA64bpp to RGBA32bpp).
|
* different color depths (e.g. RGBA64bpp to RGBA32bpp).
|
||||||
*
|
*
|
||||||
* NOTE: Leaving deptch conversion enabled (default) ensures maximum read compatibility.
|
* NOTE: Leaving deptch conversion enabled (default) ensures maximum read compatibility.
|
||||||
@@ -112,28 +117,35 @@ public:
|
|||||||
, m_transformations(QImageIOHandler::TransformationNone)
|
, m_transformations(QImageIOHandler::TransformationNone)
|
||||||
{
|
{
|
||||||
m_tempDir = QSharedPointer<QTemporaryDir>(new QTemporaryDir);
|
m_tempDir = QSharedPointer<QTemporaryDir>(new QTemporaryDir);
|
||||||
if (PKCreateFactory(&pFactory, PK_SDK_VERSION) == WMP_errSuccess) {
|
if (auto err = PKCreateFactory(&pFactory, PK_SDK_VERSION)) {
|
||||||
PKCreateCodecFactory(&pCodecFactory, WMP_SDK_VERSION);
|
qCCritical(LOG_JXRPLUGIN) << "JXRHandlerPrivate::JXRHandlerPrivate() error while initializing the JXR factory:" << err;
|
||||||
}
|
} else if (auto err = PKCreateCodecFactory(&pCodecFactory, WMP_SDK_VERSION)) {
|
||||||
if (pFactory == nullptr || pCodecFactory == nullptr) {
|
qCCritical(LOG_JXRPLUGIN) << "JXRHandlerPrivate::JXRHandlerPrivate() error while initializing the JXR codec factory:" << err;
|
||||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandlerPrivate::JXRHandlerPrivate() initialization error of JXR library!";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JXRHandlerPrivate(const JXRHandlerPrivate &other) = default;
|
JXRHandlerPrivate(const JXRHandlerPrivate &other) = default;
|
||||||
|
|
||||||
~JXRHandlerPrivate()
|
~JXRHandlerPrivate()
|
||||||
{
|
{
|
||||||
if (pCodecFactory) {
|
|
||||||
PKCreateCodecFactory_Release(&pCodecFactory);
|
|
||||||
}
|
|
||||||
if (pFactory) {
|
|
||||||
PKCreateFactory_Release(&pFactory);
|
|
||||||
}
|
|
||||||
if (pDecoder) {
|
if (pDecoder) {
|
||||||
PKImageDecode_Release(&pDecoder);
|
if (auto err = pDecoder->Release(&pDecoder)) {
|
||||||
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandlerPrivate::JXRHandlerPrivate() error while releasing the decoder:" << err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (pEncoder) {
|
if (pEncoder) {
|
||||||
PKImageEncode_Release(&pEncoder);
|
if (auto err = pEncoder->Release(&pEncoder)) {
|
||||||
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandlerPrivate::JXRHandlerPrivate() error while releasing the encoder:" << err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pCodecFactory) {
|
||||||
|
if (auto err = pCodecFactory->Release(&pCodecFactory)) {
|
||||||
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandlerPrivate::JXRHandlerPrivate() error while releasing the codec factory:" << err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pFactory) {
|
||||||
|
if (auto err = pFactory->Release(&pFactory)) {
|
||||||
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandlerPrivate::JXRHandlerPrivate() error while releasing the factory:" << err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,8 +290,12 @@ public:
|
|||||||
PKPixelFormatGUID jxrFormat() const
|
PKPixelFormatGUID jxrFormat() const
|
||||||
{
|
{
|
||||||
PKPixelFormatGUID pixelFormatGUID = GUID_PKPixelFormatUndefined;
|
PKPixelFormatGUID pixelFormatGUID = GUID_PKPixelFormatUndefined;
|
||||||
if (pDecoder) {
|
if (pDecoder == nullptr) {
|
||||||
pDecoder->GetPixelFormat(pDecoder, &pixelFormatGUID);
|
return pixelFormatGUID;
|
||||||
|
}
|
||||||
|
if (auto err = pDecoder->GetPixelFormat(pDecoder, &pixelFormatGUID)) {
|
||||||
|
qCCritical(LOG_JXRPLUGIN) << "JXRHandlerPrivate::jxrFormat() error while getting pixel format:" << err;
|
||||||
|
return GUID_PKPixelFormatUndefined;
|
||||||
}
|
}
|
||||||
return pixelFormatGUID;
|
return pixelFormatGUID;
|
||||||
}
|
}
|
||||||
@@ -303,6 +319,12 @@ public:
|
|||||||
return qtFormat;
|
return qtFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// *** MCH could be RGB, CMYK ***
|
||||||
|
qtFormat = multichannelFormat(jxrfmt, colorSpace());
|
||||||
|
if (qtFormat != QImage::Format_Invalid) {
|
||||||
|
return qtFormat;
|
||||||
|
}
|
||||||
|
|
||||||
// *** CONVERSION WITH THE SAME DEPTH ***
|
// *** CONVERSION WITH THE SAME DEPTH ***
|
||||||
// IMPORTANT: For supported conversions see JXRGluePFC.c
|
// IMPORTANT: For supported conversions see JXRGluePFC.c
|
||||||
|
|
||||||
@@ -393,17 +415,20 @@ public:
|
|||||||
*/
|
*/
|
||||||
QSize imageSize() const
|
QSize imageSize() const
|
||||||
{
|
{
|
||||||
if (pDecoder) {
|
if (pDecoder == nullptr) {
|
||||||
qint32 w, h;
|
return {};
|
||||||
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 {};
|
qint32 w = 0, h = 0;
|
||||||
|
if (auto err = pDecoder->GetSize(pDecoder, &w, &h)) {
|
||||||
|
qCCritical(LOG_JXRPLUGIN) << "JXRHandlerPrivate::imageSize() error while getting the image size:" << err;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -416,8 +441,8 @@ public:
|
|||||||
if (pDecoder == nullptr) {
|
if (pDecoder == nullptr) {
|
||||||
return cs;
|
return cs;
|
||||||
}
|
}
|
||||||
quint32 size;
|
quint32 size = 0;
|
||||||
if (!pDecoder->GetColorContext(pDecoder, nullptr, &size) && size) {
|
if (!pDecoder->GetColorContext(pDecoder, nullptr, &size) && size > 0 && size < kMaxQVectorSize) {
|
||||||
QByteArray ba(size, 0);
|
QByteArray ba(size, 0);
|
||||||
if (!pDecoder->GetColorContext(pDecoder, reinterpret_cast<quint8 *>(ba.data()), &size)) {
|
if (!pDecoder->GetColorContext(pDecoder, reinterpret_cast<quint8 *>(ba.data()), &size)) {
|
||||||
cs = QColorSpace::fromIccProfile(ba);
|
cs = QColorSpace::fromIccProfile(ba);
|
||||||
@@ -437,7 +462,7 @@ public:
|
|||||||
return xmp;
|
return xmp;
|
||||||
}
|
}
|
||||||
#ifdef JXR_ENABLE_ADVANCED_METADATA
|
#ifdef JXR_ENABLE_ADVANCED_METADATA
|
||||||
quint32 size;
|
quint32 size = 0;
|
||||||
if (!PKImageDecode_GetXMPMetadata_WMP(pDecoder, nullptr, &size) && size > 0 && size < JXR_MAX_METADATA_SIZE) {
|
if (!PKImageDecode_GetXMPMetadata_WMP(pDecoder, nullptr, &size) && size > 0 && size < JXR_MAX_METADATA_SIZE) {
|
||||||
QByteArray ba(size, 0);
|
QByteArray ba(size, 0);
|
||||||
if (!PKImageDecode_GetXMPMetadata_WMP(pDecoder, reinterpret_cast<quint8 *>(ba.data()), &size)) {
|
if (!PKImageDecode_GetXMPMetadata_WMP(pDecoder, reinterpret_cast<quint8 *>(ba.data()), &size)) {
|
||||||
@@ -498,7 +523,7 @@ public:
|
|||||||
}
|
}
|
||||||
auto host = hostComputer();
|
auto host = hostComputer();
|
||||||
if (!host.isEmpty()) {
|
if (!host.isEmpty()) {
|
||||||
image.setText(QStringLiteral(META_KEY_HOSTCOMPUTER), capt);
|
image.setText(QStringLiteral(META_KEY_HOSTCOMPUTER), host);
|
||||||
}
|
}
|
||||||
auto docn = documentName();
|
auto docn = documentName();
|
||||||
if (!docn.isEmpty()) {
|
if (!docn.isEmpty()) {
|
||||||
@@ -556,7 +581,11 @@ public:
|
|||||||
if (device == nullptr || pEncoder == nullptr) {
|
if (device == nullptr || pEncoder == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (auto err = PKImageEncode_Release(&pEncoder)) {
|
if (auto err = pEncoder->Terminate(pEncoder)) {
|
||||||
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandlerPrivate::finalizeWriting() error while terminating the encoder:" << err;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (auto err = pEncoder->Release(&pEncoder)) {
|
||||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandlerPrivate::finalizeWriting() error while releasing the encoder:" << err;
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandlerPrivate::finalizeWriting() error while releasing the encoder:" << err;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -796,6 +825,47 @@ public:
|
|||||||
return GUID_PKPixelFormatUndefined;
|
return GUID_PKPixelFormatUndefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief multichannelFormat
|
||||||
|
* I can only decide how to interpret multichannels by checking the color profile.
|
||||||
|
* If it's not present, I assume CMYK for 4 channels and RGB for 3 channels (like
|
||||||
|
* Windows does).
|
||||||
|
* \param jxrFormat Format to be converted.
|
||||||
|
* \param cs The color space of the image.
|
||||||
|
* \return A valid Qt format or QImage::Format_Invalid if there is no match
|
||||||
|
*/
|
||||||
|
static QImage::Format multichannelFormat(const PKPixelFormatGUID &jxrFormat, const QColorSpace& cs)
|
||||||
|
{
|
||||||
|
auto model = QColorSpace::ColorModel::Undefined;
|
||||||
|
if (cs.isValid()) {
|
||||||
|
model = cs.colorModel();
|
||||||
|
} else if (!cs.iccProfile().isEmpty()) {
|
||||||
|
model = QColorSpace::ColorModel::Gray; // means invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsEqualGUID(GUID_PKPixelFormat24bpp3Channels, jxrFormat)) {
|
||||||
|
if (model == QColorSpace::ColorModel::Rgb || model == QColorSpace::ColorModel::Undefined)
|
||||||
|
return QImage::Format_RGB888;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsEqualGUID(GUID_PKPixelFormat32bpp4Channels, jxrFormat)) {
|
||||||
|
if (model == QColorSpace::ColorModel::Cmyk || model == QColorSpace::ColorModel::Undefined)
|
||||||
|
return QImage::Format_CMYK8888;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsEqualGUID(GUID_PKPixelFormat32bpp3ChannelsAlpha, jxrFormat)) {
|
||||||
|
if (model == QColorSpace::ColorModel::Rgb || model == QColorSpace::ColorModel::Undefined)
|
||||||
|
return QImage::Format_RGBA8888;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsEqualGUID(GUID_PKPixelFormat64bpp3ChannelsAlpha, jxrFormat)) {
|
||||||
|
if (model == QColorSpace::ColorModel::Rgb || model == QColorSpace::ColorModel::Undefined)
|
||||||
|
return QImage::Format_RGBA64;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QImage::Format_Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static QList<std::pair<QImage::Format, PKPixelFormatGUID>> exactMatchingFormats()
|
static QList<std::pair<QImage::Format, PKPixelFormatGUID>> exactMatchingFormats()
|
||||||
{
|
{
|
||||||
@@ -967,7 +1037,7 @@ bool JXRHandler::read(QImage *outImage)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// resolution
|
// resolution
|
||||||
float hres, vres;
|
float hres = 0, vres = 0;
|
||||||
if (auto err = d->pDecoder->GetResolution(d->pDecoder, &hres, &vres)) {
|
if (auto err = d->pDecoder->GetResolution(d->pDecoder, &hres, &vres)) {
|
||||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() error while reading resolution:" << err;
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() error while reading resolution:" << err;
|
||||||
} else {
|
} else {
|
||||||
@@ -999,14 +1069,18 @@ bool JXRHandler::read(QImage *outImage)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (auto err = pConverter->Initialize(pConverter, d->pDecoder, nullptr, convFmt)) {
|
if (auto err = pConverter->Initialize(pConverter, d->pDecoder, nullptr, convFmt)) {
|
||||||
PKFormatConverter_Release(&pConverter);
|
|
||||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() unable to initialize the converter:" << err;
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() unable to initialize the converter:" << err;
|
||||||
|
if (auto err = pConverter->Release(&pConverter)) {
|
||||||
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() error while releasing the converter:" << err;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (d->pDecoder->WMP.wmiI.cBitsPerUnit == size_t(img.depth())) { // in place conversion
|
if (d->pDecoder->WMP.wmiI.cBitsPerUnit == size_t(img.depth())) { // in place conversion
|
||||||
if (auto err = pConverter->Copy(pConverter, &rect, img.bits(), img.bytesPerLine())) {
|
if (auto err = pConverter->Copy(pConverter, &rect, img.bits(), img.bytesPerLine())) {
|
||||||
PKFormatConverter_Release(&pConverter);
|
|
||||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() unable to copy converted data:" << err;
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() unable to copy converted data:" << err;
|
||||||
|
if (auto err = pConverter->Release(&pConverter)) {
|
||||||
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() error while releasing the converter:" << err;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else { // additional buffer needed
|
} else { // additional buffer needed
|
||||||
@@ -1015,19 +1089,26 @@ bool JXRHandler::read(QImage *outImage)
|
|||||||
qint64 limit = QImageReader::allocationLimit();
|
qint64 limit = QImageReader::allocationLimit();
|
||||||
if (limit && (buffSize + img.sizeInBytes()) > limit * 1024 * 1024) {
|
if (limit && (buffSize + img.sizeInBytes()) > limit * 1024 * 1024) {
|
||||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() unable to covert due to allocation limit set:" << limit << "MiB";
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() unable to covert due to allocation limit set:" << limit << "MiB";
|
||||||
|
if (auto err = pConverter->Release(&pConverter)) {
|
||||||
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() error while releasing the converter:" << err;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
QVector<quint8> ba(buffSize);
|
QVector<quint8> ba(buffSize);
|
||||||
if (auto err = pConverter->Copy(pConverter, &rect, ba.data(), convStrideSize)) {
|
if (auto err = pConverter->Copy(pConverter, &rect, ba.data(), convStrideSize)) {
|
||||||
PKFormatConverter_Release(&pConverter);
|
|
||||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() unable to copy converted data:" << err;
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() unable to copy converted data:" << err;
|
||||||
|
if (auto err = pConverter->Release(&pConverter)) {
|
||||||
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() error while releasing the converter:" << err;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (qint32 y = 0, h = img.height(); y < h; ++y) {
|
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, qint64(img.bytesPerLine())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PKFormatConverter_Release(&pConverter);
|
if (auto err = pConverter->Release(&pConverter)) {
|
||||||
|
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() error while releasing the converter:" << err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata (e.g.: icc profile, description, etc...)
|
// Metadata (e.g.: icc profile, description, etc...)
|
||||||
|
|||||||
@@ -36,17 +36,26 @@
|
|||||||
#define TIFF_VAL_URES_CENTIMETER 3
|
#define TIFF_VAL_URES_CENTIMETER 3
|
||||||
|
|
||||||
// EXIF 3 specs
|
// EXIF 3 specs
|
||||||
|
#define EXIF_EXPOSURETIME 0x829A
|
||||||
|
#define EXIF_FNUMBER 0x829D
|
||||||
#define EXIF_EXIFIFD 0x8769
|
#define EXIF_EXIFIFD 0x8769
|
||||||
|
#define EXIF_EXPOSUREPROGRAM 0x8822
|
||||||
#define EXIF_GPSIFD 0x8825
|
#define EXIF_GPSIFD 0x8825
|
||||||
|
#define EXIF_ISOSPEEDRATINGS 0x8827
|
||||||
#define EXIF_EXIFVERSION 0x9000
|
#define EXIF_EXIFVERSION 0x9000
|
||||||
#define EXIF_DATETIMEORIGINAL 0x9003
|
#define EXIF_DATETIMEORIGINAL 0x9003
|
||||||
#define EXIF_DATETIMEDIGITIZED 0x9004
|
#define EXIF_DATETIMEDIGITIZED 0x9004
|
||||||
#define EXIF_OFFSETTIME 0x9010
|
#define EXIF_OFFSETTIME 0x9010
|
||||||
#define EXIF_OFFSETTIMEORIGINAL 0x9011
|
#define EXIF_OFFSETTIMEORIGINAL 0x9011
|
||||||
#define EXIF_OFFSETTIMEDIGITIZED 0x9012
|
#define EXIF_OFFSETTIMEDIGITIZED 0x9012
|
||||||
|
#define EXIF_FLASH 0x9209
|
||||||
|
#define EXIF_FOCALLENGTH 0x920A
|
||||||
#define EXIF_COLORSPACE 0xA001
|
#define EXIF_COLORSPACE 0xA001
|
||||||
#define EXIF_PIXELXDIM 0xA002
|
#define EXIF_PIXELXDIM 0xA002
|
||||||
#define EXIF_PIXELYDIM 0xA003
|
#define EXIF_PIXELYDIM 0xA003
|
||||||
|
#define EXIF_EXPOSUREMODE 0xA402
|
||||||
|
#define EXIF_WHITEBALANCE 0xA403
|
||||||
|
#define EXIF_DIGITALZOOMRATIO 0xA404
|
||||||
#define EXIF_IMAGEUNIQUEID 0xA420
|
#define EXIF_IMAGEUNIQUEID 0xA420
|
||||||
#define EXIF_BODYSERIALNUMBER 0xA431
|
#define EXIF_BODYSERIALNUMBER 0xA431
|
||||||
#define EXIF_LENSMAKE 0xA433
|
#define EXIF_LENSMAKE 0xA433
|
||||||
@@ -64,6 +73,8 @@
|
|||||||
#define GPS_LONGITUDE 4
|
#define GPS_LONGITUDE 4
|
||||||
#define GPS_ALTITUDEREF 5
|
#define GPS_ALTITUDEREF 5
|
||||||
#define GPS_ALTITUDE 6
|
#define GPS_ALTITUDE 6
|
||||||
|
#define GPS_SPEEDREF 12
|
||||||
|
#define GPS_SPEED 13
|
||||||
#define GPS_IMGDIRECTIONREF 16
|
#define GPS_IMGDIRECTIONREF 16
|
||||||
#define GPS_IMGDIRECTION 17
|
#define GPS_IMGDIRECTION 17
|
||||||
#define EXIF_TAG_VALUE(n, byteSize) (((n) << 6) | ((byteSize) & 0x3F))
|
#define EXIF_TAG_VALUE(n, byteSize) (((n) << 6) | ((byteSize) & 0x3F))
|
||||||
@@ -123,16 +134,25 @@ static const KnownTags staticTagTypes = {
|
|||||||
TagInfo(TIFF_ARTIST, ExifTagType::Utf8),
|
TagInfo(TIFF_ARTIST, ExifTagType::Utf8),
|
||||||
TagInfo(TIFF_DATETIME, ExifTagType::Ascii),
|
TagInfo(TIFF_DATETIME, ExifTagType::Ascii),
|
||||||
TagInfo(TIFF_COPYRIGHT, ExifTagType::Utf8),
|
TagInfo(TIFF_COPYRIGHT, ExifTagType::Utf8),
|
||||||
|
TagInfo(EXIF_EXPOSURETIME, ExifTagType::Rational),
|
||||||
|
TagInfo(EXIF_FNUMBER, ExifTagType::Rational),
|
||||||
TagInfo(EXIF_EXIFIFD, ExifTagType::Long),
|
TagInfo(EXIF_EXIFIFD, ExifTagType::Long),
|
||||||
|
TagInfo(EXIF_EXPOSUREPROGRAM, ExifTagType::Short),
|
||||||
TagInfo(EXIF_GPSIFD, ExifTagType::Long),
|
TagInfo(EXIF_GPSIFD, ExifTagType::Long),
|
||||||
|
TagInfo(EXIF_ISOSPEEDRATINGS, ExifTagType::Short),
|
||||||
TagInfo(EXIF_DATETIMEORIGINAL, ExifTagType::Ascii),
|
TagInfo(EXIF_DATETIMEORIGINAL, ExifTagType::Ascii),
|
||||||
TagInfo(EXIF_OFFSETTIMEDIGITIZED, ExifTagType::Ascii),
|
TagInfo(EXIF_DATETIMEDIGITIZED, ExifTagType::Ascii),
|
||||||
TagInfo(EXIF_OFFSETTIME, ExifTagType::Ascii),
|
TagInfo(EXIF_OFFSETTIME, ExifTagType::Ascii),
|
||||||
TagInfo(EXIF_OFFSETTIMEORIGINAL, ExifTagType::Ascii),
|
TagInfo(EXIF_OFFSETTIMEORIGINAL, ExifTagType::Ascii),
|
||||||
TagInfo(EXIF_OFFSETTIMEDIGITIZED, ExifTagType::Ascii),
|
TagInfo(EXIF_OFFSETTIMEDIGITIZED, ExifTagType::Ascii),
|
||||||
|
TagInfo(EXIF_FLASH, ExifTagType::Short),
|
||||||
|
TagInfo(EXIF_FOCALLENGTH, ExifTagType::Rational),
|
||||||
TagInfo(EXIF_COLORSPACE, ExifTagType::Short),
|
TagInfo(EXIF_COLORSPACE, ExifTagType::Short),
|
||||||
TagInfo(EXIF_PIXELXDIM, ExifTagType::Long),
|
TagInfo(EXIF_PIXELXDIM, ExifTagType::Long),
|
||||||
TagInfo(EXIF_PIXELYDIM, ExifTagType::Long),
|
TagInfo(EXIF_PIXELYDIM, ExifTagType::Long),
|
||||||
|
TagInfo(EXIF_EXPOSUREMODE, ExifTagType::Short),
|
||||||
|
TagInfo(EXIF_WHITEBALANCE, ExifTagType::Short),
|
||||||
|
TagInfo(EXIF_DIGITALZOOMRATIO, ExifTagType::Rational),
|
||||||
TagInfo(EXIF_IMAGEUNIQUEID, ExifTagType::Ascii),
|
TagInfo(EXIF_IMAGEUNIQUEID, ExifTagType::Ascii),
|
||||||
TagInfo(EXIF_BODYSERIALNUMBER, ExifTagType::Ascii),
|
TagInfo(EXIF_BODYSERIALNUMBER, ExifTagType::Ascii),
|
||||||
TagInfo(EXIF_LENSMAKE, ExifTagType::Utf8),
|
TagInfo(EXIF_LENSMAKE, ExifTagType::Utf8),
|
||||||
@@ -155,6 +175,8 @@ static const KnownTags staticGpsTagTypes = {
|
|||||||
TagInfo(GPS_LONGITUDE, ExifTagType::Rational),
|
TagInfo(GPS_LONGITUDE, ExifTagType::Rational),
|
||||||
TagInfo(GPS_ALTITUDEREF, ExifTagType::Byte),
|
TagInfo(GPS_ALTITUDEREF, ExifTagType::Byte),
|
||||||
TagInfo(GPS_ALTITUDE, ExifTagType::Rational),
|
TagInfo(GPS_ALTITUDE, ExifTagType::Rational),
|
||||||
|
TagInfo(GPS_SPEEDREF, ExifTagType::Ascii),
|
||||||
|
TagInfo(GPS_SPEED, ExifTagType::Rational),
|
||||||
TagInfo(GPS_IMGDIRECTIONREF, ExifTagType::Ascii),
|
TagInfo(GPS_IMGDIRECTIONREF, ExifTagType::Ascii),
|
||||||
TagInfo(GPS_IMGDIRECTION, ExifTagType::Rational)
|
TagInfo(GPS_IMGDIRECTION, ExifTagType::Rational)
|
||||||
};
|
};
|
||||||
@@ -256,7 +278,7 @@ static bool checkHeader(QDataStream &ds)
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief updatePos
|
* \brief updatePos
|
||||||
* Write the current stram position in \a pos position as uint32.
|
* Write the current stream position in \a pos position as uint32.
|
||||||
* \return True on success, otherwise false;
|
* \return True on success, otherwise false;
|
||||||
*/
|
*/
|
||||||
static bool updatePos(QDataStream &ds, quint32 pos)
|
static bool updatePos(QDataStream &ds, quint32 pos)
|
||||||
@@ -317,7 +339,7 @@ static void writeList(QDataStream &ds, const QVariant &value)
|
|||||||
inline qint32 rationalPrecision(double v)
|
inline qint32 rationalPrecision(double v)
|
||||||
{
|
{
|
||||||
v = qAbs(v);
|
v = qAbs(v);
|
||||||
return 8 - qBound(0, v < 1 ? 8 : int(std::log10(v)), 8);
|
return v < 1 ? 8 : 8 - qBound(0, int(std::log10(v)), 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
@@ -510,7 +532,7 @@ static QByteArray readBytes(QDataStream &ds, quint32 count, bool asciiz)
|
|||||||
* \param pos The position of the IFD.
|
* \param pos The position of the IFD.
|
||||||
* \param knownTags List of known and supported tags.
|
* \param knownTags List of known and supported tags.
|
||||||
* \param nextIfd The position of next IFD (0 if none).
|
* \param nextIfd The position of next IFD (0 if none).
|
||||||
* \return True on succes, otherwise false.
|
* \return True on success, otherwise false.
|
||||||
*/
|
*/
|
||||||
static bool readIfd(QDataStream &ds, MicroExif::Tags &tags, quint32 pos = 0, const KnownTags &knownTags = staticTagTypes, quint32 *nextIfd = nullptr)
|
static bool readIfd(QDataStream &ds, MicroExif::Tags &tags, quint32 pos = 0, const KnownTags &knownTags = staticTagTypes, quint32 *nextIfd = nullptr)
|
||||||
{
|
{
|
||||||
@@ -884,7 +906,7 @@ QDateTime MicroExif::dateTime() const
|
|||||||
auto ofTag = exifString(EXIF_OFFSETTIME);
|
auto ofTag = exifString(EXIF_OFFSETTIME);
|
||||||
if (dt.isValid() && !ofTag.isEmpty())
|
if (dt.isValid() && !ofTag.isEmpty())
|
||||||
dt.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(timeOffset(ofTag) * 60));
|
dt.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(timeOffset(ofTag) * 60));
|
||||||
return(dt);
|
return dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MicroExif::setDateTime(const QDateTime &dt)
|
void MicroExif::setDateTime(const QDateTime &dt)
|
||||||
@@ -904,7 +926,7 @@ QDateTime MicroExif::dateTimeOriginal() const
|
|||||||
auto ofTag = exifString(EXIF_OFFSETTIMEORIGINAL);
|
auto ofTag = exifString(EXIF_OFFSETTIMEORIGINAL);
|
||||||
if (dt.isValid() && !ofTag.isEmpty())
|
if (dt.isValid() && !ofTag.isEmpty())
|
||||||
dt.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(timeOffset(ofTag) * 60));
|
dt.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(timeOffset(ofTag) * 60));
|
||||||
return(dt);
|
return dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MicroExif::setDateTimeOriginal(const QDateTime &dt)
|
void MicroExif::setDateTimeOriginal(const QDateTime &dt)
|
||||||
@@ -924,7 +946,7 @@ QDateTime MicroExif::dateTimeDigitized() const
|
|||||||
auto ofTag = exifString(EXIF_OFFSETTIMEDIGITIZED);
|
auto ofTag = exifString(EXIF_OFFSETTIMEDIGITIZED);
|
||||||
if (dt.isValid() && !ofTag.isEmpty())
|
if (dt.isValid() && !ofTag.isEmpty())
|
||||||
dt.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(timeOffset(ofTag) * 60));
|
dt.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(timeOffset(ofTag) * 60));
|
||||||
return(dt);
|
return dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MicroExif::setDateTimeDigitized(const QDateTime &dt)
|
void MicroExif::setDateTimeDigitized(const QDateTime &dt)
|
||||||
@@ -966,6 +988,138 @@ void MicroExif::setUniqueId(const QUuid &uuid)
|
|||||||
setExifString(EXIF_IMAGEUNIQUEID, uuid.toString(QUuid::WithoutBraces).replace(QStringLiteral("-"), QString()));
|
setExifString(EXIF_IMAGEUNIQUEID, uuid.toString(QUuid::WithoutBraces).replace(QStringLiteral("-"), QString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double MicroExif::digitalZoomRatio() const
|
||||||
|
{
|
||||||
|
if (!m_exifTags.contains(EXIF_DIGITALZOOMRATIO))
|
||||||
|
return qQNaN();
|
||||||
|
return m_exifTags.value(EXIF_DIGITALZOOMRATIO).toDouble();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MicroExif::setDigitalZoomRatio(double zoom)
|
||||||
|
{
|
||||||
|
if (qIsNaN(zoom))
|
||||||
|
m_exifTags.remove(EXIF_DIGITALZOOMRATIO);
|
||||||
|
else
|
||||||
|
m_exifTags.insert(EXIF_DIGITALZOOMRATIO, zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
quint16 MicroExif::isoSpeedRatings() const
|
||||||
|
{
|
||||||
|
return quint16(m_exifTags.value(EXIF_ISOSPEEDRATINGS).toUInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MicroExif::setIsoSpeedRatings(quint16 iso)
|
||||||
|
{
|
||||||
|
if (iso == 0)
|
||||||
|
m_exifTags.remove(EXIF_ISOSPEEDRATINGS);
|
||||||
|
else
|
||||||
|
m_exifTags.insert(EXIF_ISOSPEEDRATINGS, iso);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExposureMode MicroExif::exposureMode() const
|
||||||
|
{
|
||||||
|
auto ok = false;
|
||||||
|
auto v = m_exifTags.value(EXIF_EXPOSUREMODE).toUInt(&ok);
|
||||||
|
return ok ? ExposureMode(v) : ExposureMode::NotSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MicroExif::setExposureMode(const ExposureMode &em)
|
||||||
|
{
|
||||||
|
if (em == ExposureMode::NotSet)
|
||||||
|
m_exifTags.remove(EXIF_EXPOSUREMODE);
|
||||||
|
else
|
||||||
|
m_exifTags.insert(EXIF_EXPOSUREMODE, quint16(em));
|
||||||
|
}
|
||||||
|
|
||||||
|
ExposureProgram MicroExif::exposureProgram() const
|
||||||
|
{
|
||||||
|
auto ok = false;
|
||||||
|
auto v = m_exifTags.value(EXIF_EXPOSUREPROGRAM).toUInt(&ok);
|
||||||
|
return ok ? ExposureProgram(v) : ExposureProgram::NotSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MicroExif::setExposureProgram(const ExposureProgram &ep)
|
||||||
|
{
|
||||||
|
if (ep == ExposureProgram::NotSet)
|
||||||
|
m_exifTags.remove(EXIF_EXPOSUREPROGRAM);
|
||||||
|
else
|
||||||
|
m_exifTags.insert(EXIF_EXPOSUREPROGRAM, quint16(ep));
|
||||||
|
}
|
||||||
|
|
||||||
|
double MicroExif::exposureTime() const
|
||||||
|
{
|
||||||
|
if (!m_exifTags.contains(EXIF_EXPOSURETIME))
|
||||||
|
return qQNaN();
|
||||||
|
return m_exifTags.value(EXIF_EXPOSURETIME).toDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MicroExif::setExposureTime(double et)
|
||||||
|
{
|
||||||
|
if (qIsNaN(et))
|
||||||
|
m_exifTags.remove(EXIF_EXPOSURETIME);
|
||||||
|
else
|
||||||
|
m_exifTags.insert(EXIF_EXPOSURETIME, et);
|
||||||
|
}
|
||||||
|
|
||||||
|
double MicroExif::fNumber() const
|
||||||
|
{
|
||||||
|
if (!m_exifTags.contains(EXIF_FNUMBER))
|
||||||
|
return qQNaN();
|
||||||
|
return m_exifTags.value(EXIF_FNUMBER).toDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MicroExif::setFNumber(double f)
|
||||||
|
{
|
||||||
|
if (qIsNaN(f))
|
||||||
|
m_exifTags.remove(EXIF_FNUMBER);
|
||||||
|
else
|
||||||
|
m_exifTags.insert(EXIF_FNUMBER, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
double MicroExif::focalLength() const
|
||||||
|
{
|
||||||
|
if (!m_exifTags.contains(EXIF_FOCALLENGTH))
|
||||||
|
return qQNaN();
|
||||||
|
return m_exifTags.value(EXIF_FOCALLENGTH).toDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MicroExif::setFocalLength(double fl)
|
||||||
|
{
|
||||||
|
if (qIsNaN(fl))
|
||||||
|
m_exifTags.remove(EXIF_FOCALLENGTH);
|
||||||
|
else
|
||||||
|
m_exifTags.insert(EXIF_FOCALLENGTH, fl);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlashFlags MicroExif::flash() const
|
||||||
|
{
|
||||||
|
return FlashFlags(m_exifTags.value(EXIF_FLASH).toUInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MicroExif::setFlash(const FlashFlags &flash)
|
||||||
|
{
|
||||||
|
if (flash == Flash::NotSet)
|
||||||
|
m_exifTags.remove(EXIF_FLASH);
|
||||||
|
else
|
||||||
|
m_exifTags.insert(EXIF_FLASH, quint16(flash));
|
||||||
|
}
|
||||||
|
|
||||||
|
WhiteBalance MicroExif::whiteBalance() const
|
||||||
|
{
|
||||||
|
auto ok = false;
|
||||||
|
auto v = m_exifTags.value(EXIF_WHITEBALANCE).toUInt(&ok);
|
||||||
|
return ok ? WhiteBalance(v) : WhiteBalance::NotSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MicroExif::setWhiteBalance(const WhiteBalance &wb)
|
||||||
|
{
|
||||||
|
if (wb == WhiteBalance::NotSet)
|
||||||
|
m_exifTags.remove(EXIF_WHITEBALANCE);
|
||||||
|
else
|
||||||
|
m_exifTags.insert(EXIF_WHITEBALANCE, quint16(wb));
|
||||||
|
}
|
||||||
|
|
||||||
double MicroExif::latitude() const
|
double MicroExif::latitude() const
|
||||||
{
|
{
|
||||||
auto ref = gpsString(GPS_LATITUDEREF).toUpper();
|
auto ref = gpsString(GPS_LATITUDEREF).toUpper();
|
||||||
@@ -1045,6 +1199,30 @@ void MicroExif::setAltitude(double meters)
|
|||||||
m_gpsTags.insert(GPS_ALTITUDE, meters);
|
m_gpsTags.insert(GPS_ALTITUDE, meters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double MicroExif::imageSpeed() const
|
||||||
|
{
|
||||||
|
if (!m_gpsTags.contains(GPS_SPEED))
|
||||||
|
return qQNaN();
|
||||||
|
auto ref = gpsString(GPS_SPEEDREF).toUpper();
|
||||||
|
auto speed = m_gpsTags.value(GPS_SPEED).toDouble();
|
||||||
|
if (ref == QStringLiteral("M"))
|
||||||
|
speed *= 1.60934;
|
||||||
|
else if (ref == QStringLiteral("N"))
|
||||||
|
speed *= 1.852;
|
||||||
|
return speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MicroExif::setImageSpeed(double kmh)
|
||||||
|
{
|
||||||
|
if (qIsNaN(kmh)) {
|
||||||
|
m_gpsTags.remove(GPS_SPEEDREF);
|
||||||
|
m_gpsTags.remove(GPS_SPEED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_gpsTags.insert(GPS_SPEEDREF, QStringLiteral("K"));
|
||||||
|
m_gpsTags.insert(GPS_SPEED, kmh);
|
||||||
|
}
|
||||||
|
|
||||||
double MicroExif::imageDirection(bool *isMagnetic) const
|
double MicroExif::imageDirection(bool *isMagnetic) const
|
||||||
{
|
{
|
||||||
auto tmp = false;
|
auto tmp = false;
|
||||||
@@ -1191,6 +1369,58 @@ void MicroExif::updateImageMetadata(QImage &targetImage, bool replaceExisting) c
|
|||||||
if (!qIsNaN(v))
|
if (!qIsNaN(v))
|
||||||
targetImage.setText(QStringLiteral(META_KEY_DIRECTION), QStringLiteral("%1").arg(v, 0, 'g', 9));
|
targetImage.setText(QStringLiteral(META_KEY_DIRECTION), QStringLiteral("%1").arg(v, 0, 'g', 9));
|
||||||
}
|
}
|
||||||
|
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_SPEED)).isEmpty()) {
|
||||||
|
auto v = imageSpeed();
|
||||||
|
if (!qIsNaN(v))
|
||||||
|
targetImage.setText(QStringLiteral(META_KEY_SPEED), QStringLiteral("%1").arg(v, 0, 'g', 9));
|
||||||
|
}
|
||||||
|
|
||||||
|
// shot info
|
||||||
|
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_DIGITALZOOMRATIO)).isEmpty()) {
|
||||||
|
auto v = digitalZoomRatio();
|
||||||
|
if (!qIsNaN(v))
|
||||||
|
targetImage.setText(QStringLiteral(META_KEY_DIGITALZOOMRATIO), QStringLiteral("%1").arg(v, 0, 'g', 9));
|
||||||
|
}
|
||||||
|
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_EXPOSUREMODE)).isEmpty()) {
|
||||||
|
auto v = exposureMode();
|
||||||
|
if (v != ExposureMode::NotSet)
|
||||||
|
targetImage.setText(QStringLiteral(META_KEY_EXPOSUREMODE), QStringLiteral("%1").arg(quint16(v)));
|
||||||
|
}
|
||||||
|
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_EXPOSUREPROGRAM)).isEmpty()) {
|
||||||
|
auto v = exposureProgram();
|
||||||
|
if (v != ExposureProgram::NotSet)
|
||||||
|
targetImage.setText(QStringLiteral(META_KEY_EXPOSUREPROGRAM), QStringLiteral("%1").arg(quint16(v)));
|
||||||
|
}
|
||||||
|
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_EXPOSURETIME)).isEmpty()) {
|
||||||
|
auto v = exposureTime();
|
||||||
|
if (!qIsNaN(v))
|
||||||
|
targetImage.setText(QStringLiteral(META_KEY_EXPOSURETIME), QStringLiteral("%1").arg(v, 0, 'g', 9));
|
||||||
|
}
|
||||||
|
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_FLASH)).isEmpty()) {
|
||||||
|
auto v = flash();
|
||||||
|
if (v != Flash::NotSet)
|
||||||
|
targetImage.setText(QStringLiteral(META_KEY_FLASH), QStringLiteral("%1").arg(quint16(v)));
|
||||||
|
}
|
||||||
|
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_FNUMBER)).isEmpty()) {
|
||||||
|
auto v = fNumber();
|
||||||
|
if (!qIsNaN(v))
|
||||||
|
targetImage.setText(QStringLiteral(META_KEY_FNUMBER), QStringLiteral("%1").arg(v, 0, 'g', 9));
|
||||||
|
}
|
||||||
|
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_FOCALLENGTH)).isEmpty()) {
|
||||||
|
auto v = focalLength();
|
||||||
|
if (!qIsNaN(v))
|
||||||
|
targetImage.setText(QStringLiteral(META_KEY_FOCALLENGTH), QStringLiteral("%1").arg(v, 0, 'g', 9));
|
||||||
|
}
|
||||||
|
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_ISOSPEEDRATINGS)).isEmpty()) {
|
||||||
|
auto v = isoSpeedRatings();
|
||||||
|
if (v != 0)
|
||||||
|
targetImage.setText(QStringLiteral(META_KEY_ISOSPEEDRATINGS), QStringLiteral("%1").arg(v));
|
||||||
|
}
|
||||||
|
if (replaceExisting || targetImage.text(QStringLiteral(META_KEY_WHITEBALANCE)).isEmpty()) {
|
||||||
|
auto v = whiteBalance();
|
||||||
|
if (v != WhiteBalance::NotSet)
|
||||||
|
targetImage.setText(QStringLiteral(META_KEY_WHITEBALANCE), QStringLiteral("%1").arg(quint16(v)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MicroExif::updateImageResolution(QImage &targetImage)
|
bool MicroExif::updateImageResolution(QImage &targetImage)
|
||||||
@@ -1217,7 +1447,7 @@ MicroExif MicroExif::fromByteArray(const QByteArray &ba, bool searchHeader)
|
|||||||
idx = std::min(idxLE, idxBE);
|
idx = std::min(idxLE, idxBE);
|
||||||
else
|
else
|
||||||
idx = idxLE > -1 ? idxLE : idxBE;
|
idx = idxLE > -1 ? idxLE : idxBE;
|
||||||
if(idx > 0)
|
if (idx > 0)
|
||||||
ba0 = ba0.mid(idx);
|
ba0 = ba0.mid(idx);
|
||||||
}
|
}
|
||||||
QBuffer buf;
|
QBuffer buf;
|
||||||
@@ -1308,7 +1538,7 @@ MicroExif MicroExif::fromImage(const QImage &image)
|
|||||||
dt = QDateTime::currentDateTime();
|
dt = QDateTime::currentDateTime();
|
||||||
exif.setDateTimeOriginal(dt);
|
exif.setDateTimeOriginal(dt);
|
||||||
|
|
||||||
// GPS Info
|
// GPS info
|
||||||
auto ok = false;
|
auto ok = false;
|
||||||
auto alt = image.text(QStringLiteral(META_KEY_ALTITUDE)).toDouble(&ok);
|
auto alt = image.text(QStringLiteral(META_KEY_ALTITUDE)).toDouble(&ok);
|
||||||
if (ok)
|
if (ok)
|
||||||
@@ -1322,6 +1552,38 @@ MicroExif MicroExif::fromImage(const QImage &image)
|
|||||||
auto dir = image.text(QStringLiteral(META_KEY_DIRECTION)).toDouble(&ok);
|
auto dir = image.text(QStringLiteral(META_KEY_DIRECTION)).toDouble(&ok);
|
||||||
if (ok)
|
if (ok)
|
||||||
exif.setImageDirection(dir);
|
exif.setImageDirection(dir);
|
||||||
|
auto spd = image.text(QStringLiteral(META_KEY_SPEED)).toDouble(&ok);
|
||||||
|
if (ok)
|
||||||
|
exif.setImageSpeed(spd);
|
||||||
|
|
||||||
|
// EXIF shot info
|
||||||
|
auto zoom = image.text(QStringLiteral(META_KEY_DIGITALZOOMRATIO)).toDouble(&ok);
|
||||||
|
if (ok)
|
||||||
|
exif.setDigitalZoomRatio(zoom);
|
||||||
|
auto expm = image.text(QStringLiteral(META_KEY_EXPOSUREMODE)).toUShort(&ok);
|
||||||
|
if (ok)
|
||||||
|
exif.setExposureMode(ExposureMode(expm));
|
||||||
|
auto expp = image.text(QStringLiteral(META_KEY_EXPOSUREPROGRAM)).toUShort(&ok);
|
||||||
|
if (ok)
|
||||||
|
exif.setExposureProgram(ExposureProgram(expp));
|
||||||
|
auto expt = image.text(QStringLiteral(META_KEY_EXPOSURETIME)).toDouble(&ok);
|
||||||
|
if (ok)
|
||||||
|
exif.setExposureTime(expt);
|
||||||
|
auto flsh = image.text(QStringLiteral(META_KEY_FLASH)).toUShort(&ok);
|
||||||
|
if (ok)
|
||||||
|
exif.setFlash(FlashFlags(flsh));
|
||||||
|
auto fnum = image.text(QStringLiteral(META_KEY_FNUMBER)).toDouble(&ok);
|
||||||
|
if (ok)
|
||||||
|
exif.setFNumber(fnum);
|
||||||
|
auto flen = image.text(QStringLiteral(META_KEY_FOCALLENGTH)).toDouble(&ok);
|
||||||
|
if (ok)
|
||||||
|
exif.setFocalLength(flen);
|
||||||
|
auto isos = image.text(QStringLiteral(META_KEY_ISOSPEEDRATINGS)).toUShort(&ok);
|
||||||
|
if (ok)
|
||||||
|
exif.setIsoSpeedRatings(isos);
|
||||||
|
auto whtb = image.text(QStringLiteral(META_KEY_WHITEBALANCE)).toUShort(&ok);
|
||||||
|
if (ok)
|
||||||
|
exif.setWhiteBalance(WhiteBalance(whtb));
|
||||||
|
|
||||||
return exif;
|
return exif;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,85 @@
|
|||||||
#define EXIF_DEFAULT_BYTEORDER QDataStream::BigEndian
|
#define EXIF_DEFAULT_BYTEORDER QDataStream::BigEndian
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The Flash enum
|
||||||
|
*/
|
||||||
|
enum class Flash : quint16 {
|
||||||
|
NotSet = 0,
|
||||||
|
|
||||||
|
// Values for bit 0 indicating whether the flash fired.
|
||||||
|
// 0b = Flash did not fire.
|
||||||
|
// 1b = Flash fired.
|
||||||
|
Fired = 1,
|
||||||
|
|
||||||
|
// Values for bits 1 and 2 indicating the status of returned light.
|
||||||
|
// 00b = No strobe return detection function
|
||||||
|
// 01b = reserved
|
||||||
|
// 10b = Strobe return light not detected.
|
||||||
|
// 11b = Strobe return light detected.
|
||||||
|
ReturnLightNotDetected = 2 << 1,
|
||||||
|
ReturnLightDetected = 3 << 1,
|
||||||
|
|
||||||
|
// Values for bits 3 and 4 indicating the camera's flash mode.
|
||||||
|
// 00b = unknown
|
||||||
|
// 01b = Compulsory flash firing
|
||||||
|
// 10b = Compulsory flash suppression
|
||||||
|
// 11b = Auto mode
|
||||||
|
CompulsoryFiring = 1 << 3,
|
||||||
|
CompulsorySuppression = 2 << 3,
|
||||||
|
AutoMode = 3 << 3,
|
||||||
|
|
||||||
|
// Values for bit 5 indicating the presence of a flash function.
|
||||||
|
// 0b = Flash function present
|
||||||
|
// 1b = No flash function
|
||||||
|
FlashNotAvailable = 1 << 5,
|
||||||
|
|
||||||
|
// Values for bit 6 indicating the camera's red-eye mode.
|
||||||
|
// 0b = No red-eye reduction mode or unknown
|
||||||
|
// 1b = Red-eye reduction supported
|
||||||
|
RedEyeReductionSupported = 1 << 6,
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(FlashFlags, Flash)
|
||||||
|
Q_DECLARE_OPERATORS_FOR_FLAGS(FlashFlags)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The ExposureMode enum
|
||||||
|
*/
|
||||||
|
enum class ExposureMode : quint16 {
|
||||||
|
Auto,
|
||||||
|
Manual,
|
||||||
|
AutoBracket,
|
||||||
|
|
||||||
|
NotSet = 65535
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The ExposureProgram enum
|
||||||
|
*/
|
||||||
|
enum class ExposureProgram : quint16 {
|
||||||
|
NotDefined,
|
||||||
|
Manual,
|
||||||
|
Normal,
|
||||||
|
AperturePriority,
|
||||||
|
ShutterPriority,
|
||||||
|
Creative,
|
||||||
|
Action,
|
||||||
|
PortraitMode,
|
||||||
|
LandscapeMode,
|
||||||
|
|
||||||
|
NotSet = 65535
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The WhiteBalance enum
|
||||||
|
*/
|
||||||
|
enum class WhiteBalance : quint16 {
|
||||||
|
Auto,
|
||||||
|
Manual,
|
||||||
|
|
||||||
|
NotSet = 65535
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief The MicroExif class
|
* \brief The MicroExif class
|
||||||
* Class to extract / write minimal EXIF data (e.g. resolution, rotation,
|
* Class to extract / write minimal EXIF data (e.g. resolution, rotation,
|
||||||
@@ -125,7 +204,7 @@ public:
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief transformation
|
* \brief transformation
|
||||||
* \return The orientation converted in the equvalent Qt transformation.
|
* \return The orientation converted in the equivalent Qt transformation.
|
||||||
* \sa orientation
|
* \sa orientation
|
||||||
*/
|
*/
|
||||||
QImageIOHandler::Transformation transformation() const;
|
QImageIOHandler::Transformation transformation() const;
|
||||||
@@ -236,6 +315,69 @@ public:
|
|||||||
QUuid uniqueId() const;
|
QUuid uniqueId() const;
|
||||||
void setUniqueId(const QUuid &uuid);
|
void setUniqueId(const QUuid &uuid);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief digitalZoomRatio
|
||||||
|
* \return The digital zoom ratio when the image was shot or NaN if not set.
|
||||||
|
*/
|
||||||
|
double digitalZoomRatio() const;
|
||||||
|
void setDigitalZoomRatio(double zoom);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief exposureMode
|
||||||
|
* \return The exposure mode set when the image was shot. In auto-bracketing mode, the camera shoots a series of frames of the same scene at different exposure settings.
|
||||||
|
*/
|
||||||
|
ExposureMode exposureMode() const;
|
||||||
|
void setExposureMode(const ExposureMode& em);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief exposureProgram
|
||||||
|
* \return The class of the program used by the camera to set exposure when the picture is taken.
|
||||||
|
*/
|
||||||
|
ExposureProgram exposureProgram() const;
|
||||||
|
void setExposureProgram(const ExposureProgram& ep);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief exposureTime
|
||||||
|
* \return Exposure time, given in seconds (sec) or NaN if not set.
|
||||||
|
*/
|
||||||
|
double exposureTime() const;
|
||||||
|
void setExposureTime(double et);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief fNumber
|
||||||
|
* \return The F number or NaN if not set.
|
||||||
|
*/
|
||||||
|
double fNumber() const;
|
||||||
|
void setFNumber(double f);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief focalLength
|
||||||
|
* \return The actual focal length of the lens, in mm.
|
||||||
|
*/
|
||||||
|
double focalLength() const;
|
||||||
|
void setFocalLength(double fl);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief flash
|
||||||
|
* \return The status of flash when the image was shot.
|
||||||
|
*/
|
||||||
|
FlashFlags flash() const;
|
||||||
|
void setFlash(const FlashFlags& flash);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief isoSpeedRatings
|
||||||
|
* \return The sensitivity of the camera or input device when the image was shot.
|
||||||
|
*/
|
||||||
|
quint16 isoSpeedRatings() const;
|
||||||
|
void setIsoSpeedRatings(quint16 iso);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief whiteBalance
|
||||||
|
* \return The white balance mode set when the image was shot.
|
||||||
|
*/
|
||||||
|
WhiteBalance whiteBalance() const;
|
||||||
|
void setWhiteBalance(const WhiteBalance& wb);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief latitude
|
* \brief latitude
|
||||||
* \return Floating-point number indicating the latitude in degrees north of the equator (e.g. 27.717) or NaN if not set.
|
* \return Floating-point number indicating the latitude in degrees north of the equator (e.g. 27.717) or NaN if not set.
|
||||||
@@ -258,6 +400,13 @@ public:
|
|||||||
double altitude() const;
|
double altitude() const;
|
||||||
void setAltitude(double meters);
|
void setAltitude(double meters);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief imageSpeed
|
||||||
|
* \return The speed in Km/h or NaN if not set.
|
||||||
|
*/
|
||||||
|
double imageSpeed() const;
|
||||||
|
void setImageSpeed(double kmh);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief imageDirection
|
* \brief imageDirection
|
||||||
* \param isMagnetic Set to true if the direction is relative to magnetic north, false if it is relative to true north. Leave nullptr if is not of interest.
|
* \param isMagnetic Set to true if the direction is relative to magnetic north, false if it is relative to true north. Leave nullptr if is not of interest.
|
||||||
|
|||||||
@@ -546,8 +546,21 @@ static bool setColorSpace(QImage &img, const PSDImageResourceSection &irs)
|
|||||||
auto cs = QColorSpace::fromIccProfile(irb.data);
|
auto cs = QColorSpace::fromIccProfile(irb.data);
|
||||||
if (!cs.isValid())
|
if (!cs.isValid())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (cs.colorModel() == QColorSpace::ColorModel::Gray && img.pixelFormat().colorModel() != QPixelFormat::Grayscale) {
|
||||||
|
// I created an RGB from a grayscale without using color profile conversion (fast).
|
||||||
|
// I'll try to create an RGB profile that looks the same.
|
||||||
|
if (cs.transferFunction() != QColorSpace::TransferFunction::Custom) {
|
||||||
|
auto tmp = QColorSpace(QColorSpace::Primaries::SRgb, cs.transferFunction(), cs.gamma());
|
||||||
|
tmp.setWhitePoint(cs.whitePoint());
|
||||||
|
tmp.setDescription(QStringLiteral("RGB emulation of \"%1\"").arg(cs.description()));
|
||||||
|
if (tmp.isValid())
|
||||||
|
cs = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
img.setColorSpace(cs);
|
img.setColorSpace(cs);
|
||||||
return true;
|
return img.colorSpace().isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -627,7 +640,7 @@ static bool setResolution(QImage &img, const PSDImageResourceSection &irs)
|
|||||||
|
|
||||||
s.skipRawData(4); // Display data (not used here)
|
s.skipRawData(4); // Display data (not used here)
|
||||||
|
|
||||||
s >> i32; // Vertial resolution in pixels per inch.
|
s >> i32; // Vertical resolution in pixels per inch.
|
||||||
if (i32 <= 0)
|
if (i32 <= 0)
|
||||||
return false;
|
return false;
|
||||||
auto vres = dpi2ppm(fixedPointToDouble(i32));
|
auto vres = dpi2ppm(fixedPointToDouble(i32));
|
||||||
@@ -800,6 +813,14 @@ static QImage::Format imageFormat(const PSDHeader &header, bool alpha)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CM_GRAYSCALE:
|
case CM_GRAYSCALE:
|
||||||
|
if (header.depth == 32) {
|
||||||
|
format = !alpha ? QImage::Format_RGBX32FPx4 : QImage::Format_RGBA32FPx4_Premultiplied;
|
||||||
|
} else if (header.depth == 16) {
|
||||||
|
format = !alpha ? QImage::Format_Grayscale16 : QImage::Format_RGBA64_Premultiplied;
|
||||||
|
} else {
|
||||||
|
format = !alpha ? QImage::Format_Grayscale8 : QImage::Format_RGBA8888_Premultiplied;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case CM_DUOTONE:
|
case CM_DUOTONE:
|
||||||
format = header.depth == 8 ? QImage::Format_Grayscale8 : QImage::Format_Grayscale16;
|
format = header.depth == 8 ? QImage::Format_Grayscale8 : QImage::Format_Grayscale16;
|
||||||
break;
|
break;
|
||||||
@@ -996,7 +1017,7 @@ inline void rawChannelCopy(uchar *target, qint32 targetChannels, qint32 targetCh
|
|||||||
|
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
inline void cmykToRgb(uchar *target, qint32 targetChannels, const char *source, qint32 sourceChannels, qint32 width, bool alpha = false)
|
inline bool cmykToRgb(uchar *target, qint32 targetChannels, const char *source, qint32 sourceChannels, qint32 width, bool alpha = false)
|
||||||
{
|
{
|
||||||
auto s = reinterpret_cast<const T*>(source);
|
auto s = reinterpret_cast<const T*>(source);
|
||||||
auto t = reinterpret_cast<T*>(target);
|
auto t = reinterpret_cast<T*>(target);
|
||||||
@@ -1005,7 +1026,7 @@ inline void cmykToRgb(uchar *target, qint32 targetChannels, const char *source,
|
|||||||
|
|
||||||
if (sourceChannels < 2) {
|
if (sourceChannels < 2) {
|
||||||
qCDebug(LOG_PSDPLUGIN) << "cmykToRgb: image is not a valid MCH/CMYK!";
|
qCDebug(LOG_PSDPLUGIN) << "cmykToRgb: image is not a valid MCH/CMYK!";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (qint32 w = 0; w < width; ++w) {
|
for (qint32 w = 0; w < width; ++w) {
|
||||||
@@ -1026,6 +1047,7 @@ inline void cmykToRgb(uchar *target, qint32 targetChannels, const char *source,
|
|||||||
*(pt + 3) = std::numeric_limits<T>::max();
|
*(pt + 3) = std::numeric_limits<T>::max();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double finv(double v)
|
inline double finv(double v)
|
||||||
@@ -1045,7 +1067,7 @@ inline double gammaCorrection(double linear)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
inline void labToRgb(uchar *target, qint32 targetChannels, const char *source, qint32 sourceChannels, qint32 width, bool alpha = false)
|
inline bool labToRgb(uchar *target, qint32 targetChannels, const char *source, qint32 sourceChannels, qint32 width, bool alpha = false)
|
||||||
{
|
{
|
||||||
auto s = reinterpret_cast<const T*>(source);
|
auto s = reinterpret_cast<const T*>(source);
|
||||||
auto t = reinterpret_cast<T*>(target);
|
auto t = reinterpret_cast<T*>(target);
|
||||||
@@ -1054,7 +1076,7 @@ inline void labToRgb(uchar *target, qint32 targetChannels, const char *source, q
|
|||||||
|
|
||||||
if (sourceChannels < 3) {
|
if (sourceChannels < 3) {
|
||||||
qCDebug(LOG_PSDPLUGIN) << "labToRgb: image is not a valid LAB!";
|
qCDebug(LOG_PSDPLUGIN) << "labToRgb: image is not a valid LAB!";
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (qint32 w = 0; w < width; ++w) {
|
for (qint32 w = 0; w < width; ++w) {
|
||||||
@@ -1089,6 +1111,7 @@ inline void labToRgb(uchar *target, qint32 targetChannels, const char *source, q
|
|||||||
*(pt + 3) = std::numeric_limits<T>::max();
|
*(pt + 3) = std::numeric_limits<T>::max();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool readChannel(QByteArray &target, QDataStream &stream, quint32 compressedSize, quint16 compression)
|
bool readChannel(QByteArray &target, QDataStream &stream, quint32 compressedSize, quint16 compression)
|
||||||
@@ -1102,7 +1125,7 @@ bool readChannel(QByteArray &target, QDataStream &stream, quint32 compressedSize
|
|||||||
if (stream.readRawData(tmp.data(), tmp.size()) != tmp.size()) {
|
if (stream.readRawData(tmp.data(), tmp.size()) != tmp.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (packbitsDecompress(tmp.data(), tmp.size(), target.data(), target.size()) < 0) {
|
if (packbitsDecompress(tmp.data(), tmp.size(), target.data(), target.size()) != target.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (stream.readRawData(target.data(), target.size()) != target.size()) {
|
} else if (stream.readRawData(target.data(), target.size()) != target.size()) {
|
||||||
@@ -1309,7 +1332,6 @@ bool PSDHandler::read(QImage *image)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto imgChannels = imageChannels(img.format());
|
auto imgChannels = imageChannels(img.format());
|
||||||
auto channel_num = std::min(qint32(header.channel_count), imgChannels);
|
|
||||||
auto raw_count = qsizetype(header.width * header.depth + 7) / 8;
|
auto raw_count = qsizetype(header.width * header.depth + 7) / 8;
|
||||||
auto native_cmyk = img.format() == CMYK_FORMAT;
|
auto native_cmyk = img.format() == CMYK_FORMAT;
|
||||||
|
|
||||||
@@ -1360,8 +1382,10 @@ bool PSDHandler::read(QImage *image)
|
|||||||
#if !defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED)
|
#if !defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED)
|
||||||
if (header.color_mode == CM_CMYK && img.format() != QImage::Format_CMYK8888) {
|
if (header.color_mode == CM_CMYK && img.format() != QImage::Format_CMYK8888) {
|
||||||
auto tmpi = QImage(header.width, 1, QImage::Format_CMYK8888);
|
auto tmpi = QImage(header.width, 1, QImage::Format_CMYK8888);
|
||||||
if (setColorSpace(tmpi, irs))
|
if (setColorSpace(tmpi, irs)) {
|
||||||
|
tmpi.fill(0);
|
||||||
tmpCmyk = tmpi;
|
tmpCmyk = tmpi;
|
||||||
|
}
|
||||||
iccConv.setTargetColorSpace(QColorSpace(QColorSpace::SRgb));
|
iccConv.setTargetColorSpace(QColorSpace(QColorSpace::SRgb));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1415,15 +1439,26 @@ bool PSDHandler::read(QImage *image)
|
|||||||
else if (header.depth == 32)
|
else if (header.depth == 32)
|
||||||
premulConversion<float>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
|
premulConversion<float>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
|
||||||
}
|
}
|
||||||
|
if (header.color_mode == CM_GRAYSCALE) {
|
||||||
|
if (header.depth == 8)
|
||||||
|
premulConversion<quint8>(scanLine, header.width, 1, header.channel_count, PremulConversion::PS2P);
|
||||||
|
else if (header.depth == 16)
|
||||||
|
premulConversion<quint16>(scanLine, header.width, 1, header.channel_count, PremulConversion::PS2P);
|
||||||
|
else if (header.depth == 32)
|
||||||
|
premulConversion<float>(scanLine, header.width, 1, header.channel_count, PremulConversion::PS2P);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conversion to RGB
|
// Conversion to RGB
|
||||||
if (header.color_mode == CM_CMYK || header.color_mode == CM_MULTICHANNEL) {
|
if (header.color_mode == CM_CMYK || header.color_mode == CM_MULTICHANNEL) {
|
||||||
if (tmpCmyk.isNull()) {
|
if (tmpCmyk.isNull()) {
|
||||||
if (header.depth == 8)
|
if (header.depth == 8) {
|
||||||
cmykToRgb<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
|
if (!cmykToRgb<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha))
|
||||||
else if (header.depth == 16)
|
return false;
|
||||||
cmykToRgb<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
|
} else if (header.depth == 16) {
|
||||||
|
if (!cmykToRgb<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else if (header.depth == 8) {
|
} else if (header.depth == 8) {
|
||||||
rawChannelsCopyToCMYK<quint8>(tmpCmyk.bits(), 4, psdScanline.data(), header.channel_count, header.width);
|
rawChannelsCopyToCMYK<quint8>(tmpCmyk.bits(), 4, psdScanline.data(), header.channel_count, header.width);
|
||||||
if (auto rgbPtr = iccConv.convertedScanLine(tmpCmyk, 0))
|
if (auto rgbPtr = iccConv.convertedScanLine(tmpCmyk, 0))
|
||||||
@@ -1439,10 +1474,13 @@ bool PSDHandler::read(QImage *image)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (header.color_mode == CM_LABCOLOR) {
|
if (header.color_mode == CM_LABCOLOR) {
|
||||||
if (header.depth == 8)
|
if (header.depth == 8) {
|
||||||
labToRgb<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
|
if (!labToRgb<quint8>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha))
|
||||||
else if (header.depth == 16)
|
return false;
|
||||||
labToRgb<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha);
|
} else if (header.depth == 16) {
|
||||||
|
if (!labToRgb<quint16>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width, alpha))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (header.color_mode == CM_RGB) {
|
if (header.color_mode == CM_RGB) {
|
||||||
if (header.depth == 8)
|
if (header.depth == 8)
|
||||||
@@ -1452,9 +1490,21 @@ bool PSDHandler::read(QImage *image)
|
|||||||
else if (header.depth == 32)
|
else if (header.depth == 32)
|
||||||
rawChannelsCopy<float>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width);
|
rawChannelsCopy<float>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width);
|
||||||
}
|
}
|
||||||
|
if (header.color_mode == CM_GRAYSCALE) {
|
||||||
|
for (auto c = 0; c < imgChannels; ++c) { // GRAYA to RGBA
|
||||||
|
auto sc = qBound(0, c - 2, int(header.channel_count));
|
||||||
|
if (header.depth == 8)
|
||||||
|
rawChannelCopy<quint8>(img.scanLine(y), imgChannels, c, psdScanline.data(), header.channel_count, sc, header.width);
|
||||||
|
else if (header.depth == 16)
|
||||||
|
rawChannelCopy<quint16>(img.scanLine(y), imgChannels, c, psdScanline.data(), header.channel_count, sc, header.width);
|
||||||
|
else if (header.depth == 32)
|
||||||
|
rawChannelCopy<float>(img.scanLine(y), imgChannels, c, psdScanline.data(), header.channel_count, sc, header.width);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Linear read (no position jumps): optimized code usable only for the colorspaces supported by QImage
|
// Linear read (no position jumps): optimized code usable only for the colorspaces supported by QImage
|
||||||
|
auto channel_num = std::min(qint32(header.channel_count), header.color_mode == CM_GRAYSCALE ? 1 : imgChannels);
|
||||||
for (qint32 c = 0; c < channel_num; ++c) {
|
for (qint32 c = 0; c < channel_num; ++c) {
|
||||||
for (qint32 y = 0, h = header.height; y < h; ++y) {
|
for (qint32 y = 0, h = header.height; y < h; ++y) {
|
||||||
auto&& strideSize = strides.at(c * qsizetype(h) + y);
|
auto&& strideSize = strides.at(c * qsizetype(h) + y);
|
||||||
@@ -1483,8 +1533,13 @@ bool PSDHandler::read(QImage *image)
|
|||||||
// 32-bits float images: RGB/RGBA
|
// 32-bits float images: RGB/RGBA
|
||||||
planarToChunchy<float>(scanLine, rawStride.data(), header.width, c, imgChannels);
|
planarToChunchy<float>(scanLine, rawStride.data(), header.width, c, imgChannels);
|
||||||
} else if (header.depth == 32 && header.color_mode == CM_GRAYSCALE) {
|
} else if (header.depth == 32 && header.color_mode == CM_GRAYSCALE) {
|
||||||
// 32-bits float images: Grayscale (coverted to equivalent integer 16-bits)
|
if (imgChannels >= 3) { // GRAY to RGB
|
||||||
planarToChunchyFloatToUInt16<float>(scanLine, rawStride.data(), header.width, c, imgChannels);
|
planarToChunchy<float>(scanLine, rawStride.data(), header.width, 0, imgChannels);
|
||||||
|
planarToChunchy<float>(scanLine, rawStride.data(), header.width, 1, imgChannels);
|
||||||
|
planarToChunchy<float>(scanLine, rawStride.data(), header.width, 2, imgChannels);
|
||||||
|
} else { // 32-bits float images: Grayscale (converted to equivalent integer 16-bits)
|
||||||
|
planarToChunchyFloatToUInt16<float>(scanLine, rawStride.data(), header.width, c, imgChannels);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1620,6 +1675,9 @@ bool PSDHandler::canRead(QIODevice *device)
|
|||||||
if (header.color_mode == CM_RGB && header.channel_count > 3) {
|
if (header.color_mode == CM_RGB && header.channel_count > 3) {
|
||||||
return false; // supposing extra channel as alpha
|
return false; // supposing extra channel as alpha
|
||||||
}
|
}
|
||||||
|
if (header.color_mode == CM_GRAYSCALE && (header.channel_count > 1 || header.depth == 32)) {
|
||||||
|
return false; // supposing extra channel as alpha
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return IsSupported(header);
|
return IsSupported(header);
|
||||||
|
|||||||