Compare commits
33 Commits
Frameworks
...
v6.5.0
Author | SHA1 | Date | |
---|---|---|---|
8c23e74ef6 | |||
219d9cb2c2 | |||
51921e8ee5 | |||
23e9fec869 | |||
4478bc8d2b | |||
acd6b3970c | |||
638fdfcbdd | |||
a497ab789b | |||
3590a43fc5 | |||
f5a6de7280 | |||
4c0f49295b | |||
e9da5edb9a | |||
e10f5aa9a5 | |||
14020a23d5 | |||
bb17f7bf84 | |||
b849e48ef4 | |||
81b7263d73 | |||
63e21ee5f3 | |||
06f097046c | |||
950ed43623 | |||
863c424390 | |||
bd083ff354 | |||
99663607b2 | |||
7499e3b8d4 | |||
cb5ca7fc48 | |||
4f61e3912c | |||
b8a9c75c80 | |||
4995c9cd15 | |||
a54c5e876c | |||
6c1a7ad339 | |||
c721fa481b | |||
ea15fed399 | |||
c2fabef501 |
12
.gitattributes
vendored
@ -1 +1,13 @@
|
|||||||
autotests/read/raw/RAW_KODAK_C330_FORMAT_NONE_YRGB.raw binary
|
autotests/read/raw/RAW_KODAK_C330_FORMAT_NONE_YRGB.raw binary
|
||||||
|
autotests/read/hdr/orientation1.hdr binary
|
||||||
|
autotests/read/hdr/orientation2.hdr binary
|
||||||
|
autotests/read/hdr/orientation3.hdr binary
|
||||||
|
autotests/read/hdr/orientation4.hdr binary
|
||||||
|
autotests/read/hdr/orientation5.hdr binary
|
||||||
|
autotests/read/hdr/orientation6.hdr binary
|
||||||
|
autotests/read/hdr/orientation7.hdr binary
|
||||||
|
autotests/read/hdr/orientation8.hdr binary
|
||||||
|
autotests/read/hdr/fake_earth.hdr binary
|
||||||
|
autotests/read/hdr/rgb.hdr binary
|
||||||
|
autotests/read/hdr/rgb-landscape.hdr binary
|
||||||
|
autotests/read/hdr/rgb-portrait.hdr binary
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
Dependencies:
|
Dependencies:
|
||||||
- 'on': ['Linux', 'FreeBSD', 'macOS', 'Windows']
|
- 'on': ['Linux', 'FreeBSD', 'macOS', 'Windows', 'Android']
|
||||||
'require':
|
'require':
|
||||||
'frameworks/extra-cmake-modules': '@same'
|
'frameworks/extra-cmake-modules': '@same'
|
||||||
'frameworks/karchive' : '@same'
|
'frameworks/karchive' : '@same'
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
set(KF_VERSION "6.2.0") # handled by release scripts
|
set(KF_VERSION "6.5.0") # handled by release scripts
|
||||||
set(KF_DEP_VERSION "6.2.0") # handled by release scripts
|
set(KF_DEP_VERSION "6.5.0") # handled by release scripts
|
||||||
project(KImageFormats VERSION ${KF_VERSION})
|
project(KImageFormats VERSION ${KF_VERSION})
|
||||||
|
|
||||||
include(FeatureSummary)
|
include(FeatureSummary)
|
||||||
find_package(ECM 6.2.0 NO_MODULE)
|
find_package(ECM 6.5.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)
|
||||||
|
|
||||||
@ -75,13 +75,18 @@ if(KIMAGEFORMATS_JXL)
|
|||||||
endif()
|
endif()
|
||||||
add_feature_info(LibJXL LibJXL_FOUND "required for the QImage plugin for JPEG XL images")
|
add_feature_info(LibJXL LibJXL_FOUND "required for the QImage plugin for JPEG XL images")
|
||||||
|
|
||||||
# note: module FindLibRaw missing from https://invent.kde.org/frameworks/extra-cmake-modules
|
|
||||||
find_package(LibRaw 0.20.2)
|
find_package(LibRaw 0.20.2)
|
||||||
set_package_properties(LibRaw PROPERTIES
|
set_package_properties(LibRaw PROPERTIES
|
||||||
TYPE OPTIONAL
|
TYPE OPTIONAL
|
||||||
PURPOSE "Required for the QImage plugin for RAW images"
|
PURPOSE "Required for the QImage plugin for RAW images"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
option(KIMAGEFORMATS_JXR "Enable plugin for JPEG XR format" OFF)
|
||||||
|
if(KIMAGEFORMATS_JXR)
|
||||||
|
find_package(LibJXR)
|
||||||
|
endif()
|
||||||
|
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.5
|
QT 6.5
|
||||||
KF 5.102
|
KF 5.102
|
||||||
|
@ -18,16 +18,19 @@ The following image formats have read-only support:
|
|||||||
- Gimp (xcf)
|
- Gimp (xcf)
|
||||||
- Krita (kra)
|
- Krita (kra)
|
||||||
- OpenRaster (ora)
|
- OpenRaster (ora)
|
||||||
|
- Pixar raster (pxr)
|
||||||
|
- Portable FloatMap (pfm)
|
||||||
- Photoshop documents (psd, psb, pdd, psdt)
|
- Photoshop documents (psd, psb, pdd, psdt)
|
||||||
- Radiance HDR (hdr)
|
- Radiance HDR (hdr)
|
||||||
- Sun Raster (im1, im8, im24, im32, ras, sun)
|
- Sun Raster (im1, im8, im24, im32, ras, sun)
|
||||||
|
|
||||||
The following image formats have read and write support:
|
The following image formats have read and write support:
|
||||||
|
|
||||||
- AV1 Image File Format (AVIF)
|
- AV1 Image File Format (avif)
|
||||||
- Encapsulated PostScript (eps)
|
- Encapsulated PostScript (eps)
|
||||||
- High Efficiency Image File Format (heif). Can be enabled with the KIMAGEFORMATS_HEIF build option.
|
- High Efficiency Image File Format (heif). Can be enabled with the KIMAGEFORMATS_HEIF build option.
|
||||||
- JPEG XL (jxl)
|
- JPEG XL (jxl)
|
||||||
|
- JPEG XR (jxr). Can be enabled with the KIMAGEFORMATS_JXR build option.
|
||||||
- OpenEXR (exr)
|
- OpenEXR (exr)
|
||||||
- Personal Computer Exchange (pcx)
|
- Personal Computer Exchange (pcx)
|
||||||
- Quite OK Image format (qoi)
|
- Quite OK Image format (qoi)
|
||||||
|
@ -11,7 +11,7 @@ macro(kimageformats_read_tests)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NOT TARGET readtest)
|
if (NOT TARGET readtest)
|
||||||
add_executable(readtest readtest.cpp)
|
add_executable(readtest readtest.cpp templateimage.cpp)
|
||||||
target_link_libraries(readtest Qt6::Gui)
|
target_link_libraries(readtest Qt6::Gui)
|
||||||
target_compile_definitions(readtest
|
target_compile_definitions(readtest
|
||||||
PRIVATE IMAGEDIR="${CMAKE_CURRENT_SOURCE_DIR}/read")
|
PRIVATE IMAGEDIR="${CMAKE_CURRENT_SOURCE_DIR}/read")
|
||||||
@ -66,7 +66,9 @@ endmacro()
|
|||||||
kimageformats_read_tests(
|
kimageformats_read_tests(
|
||||||
hdr
|
hdr
|
||||||
pcx
|
pcx
|
||||||
|
pfm
|
||||||
psd
|
psd
|
||||||
|
pxr
|
||||||
qoi
|
qoi
|
||||||
ras
|
ras
|
||||||
rgb
|
rgb
|
||||||
@ -114,6 +116,15 @@ if (LibJXL_FOUND AND LibJXLThreads_FOUND)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (LibJXR_FOUND)
|
||||||
|
kimageformats_read_tests(
|
||||||
|
jxr
|
||||||
|
)
|
||||||
|
kimageformats_write_tests(
|
||||||
|
jxr-nodatacheck
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Allow some fuzziness when reading this formats, to allow for
|
# Allow some fuzziness when reading this formats, to allow for
|
||||||
# rounding errors (eg: in alpha blending).
|
# rounding errors (eg: in alpha blending).
|
||||||
kimageformats_read_tests(FUZZ 1
|
kimageformats_read_tests(FUZZ 1
|
||||||
|
47
autotests/read/hdr/orientation1.hdr
Normal file
5
autotests/read/hdr/orientation1.hdr.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "orientation_all.png"
|
||||||
|
}
|
||||||
|
]
|
47
autotests/read/hdr/orientation2.hdr
Normal file
5
autotests/read/hdr/orientation2.hdr.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "orientation_all.png"
|
||||||
|
}
|
||||||
|
]
|
47
autotests/read/hdr/orientation3.hdr
Normal file
5
autotests/read/hdr/orientation3.hdr.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "orientation_all.png"
|
||||||
|
}
|
||||||
|
]
|
47
autotests/read/hdr/orientation4.hdr
Normal file
5
autotests/read/hdr/orientation4.hdr.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "orientation_all.png"
|
||||||
|
}
|
||||||
|
]
|
47
autotests/read/hdr/orientation5.hdr
Normal file
5
autotests/read/hdr/orientation5.hdr.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "orientation_all.png"
|
||||||
|
}
|
||||||
|
]
|
47
autotests/read/hdr/orientation6.hdr
Normal file
5
autotests/read/hdr/orientation6.hdr.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "orientation_all.png"
|
||||||
|
}
|
||||||
|
]
|
47
autotests/read/hdr/orientation7.hdr
Normal file
5
autotests/read/hdr/orientation7.hdr.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "orientation_all.png"
|
||||||
|
}
|
||||||
|
]
|
47
autotests/read/hdr/orientation8.hdr
Normal file
5
autotests/read/hdr/orientation8.hdr.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"fileName" : "orientation_all.png"
|
||||||
|
}
|
||||||
|
]
|
BIN
autotests/read/hdr/orientation_all.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
autotests/read/jxl/orientation6_notranfs.jxl
Normal file
19
autotests/read/jxl/orientation6_notranfs.jxl.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.5.7",
|
||||||
|
"maxQtVersion" : "6.5.99",
|
||||||
|
"disableAutoTransform": true,
|
||||||
|
"fileName" : "orientation6_notranfs.png",
|
||||||
|
"comment" : "Test with automatic transformation disabled."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.7.3",
|
||||||
|
"disableAutoTransform": true,
|
||||||
|
"fileName" : "orientation6_notranfs.png",
|
||||||
|
"comment" : "Test with automatic transformation disabled."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unsupportedFormat" : true,
|
||||||
|
"comment" : "It is not possible to disable the transformation with the current version of the plugin."
|
||||||
|
}
|
||||||
|
]
|
BIN
autotests/read/jxl/orientation6_notranfs.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
autotests/read/jxr/abydos_bgra32.jxr
Normal file
BIN
autotests/read/jxr/abydos_bgra32.png
Normal file
After Width: | Height: | Size: 206 KiB |
BIN
autotests/read/jxr/testcard_bgra8.jxr
Normal file
BIN
autotests/read/jxr/testcard_bgra8.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
autotests/read/jxr/testcard_cmyk8.jxr
Normal file
11
autotests/read/jxr/testcard_cmyk8.jxr.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.8.0",
|
||||||
|
"fileName" : "testcard_cmyk8.tif"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"maxQtVersion" : "6.7.99",
|
||||||
|
"unsupportedFormat" : true,
|
||||||
|
"comment" : "Qt versions lower than 6.8 do not support CMYK format so this test should be skipped."
|
||||||
|
}
|
||||||
|
]
|
BIN
autotests/read/jxr/testcard_cmyk8.tif
Normal file
BIN
autotests/read/jxr/testcard_gray16.jxr
Normal file
BIN
autotests/read/jxr/testcard_gray16.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
autotests/read/jxr/testcard_gray8.jxr
Normal file
BIN
autotests/read/jxr/testcard_gray8.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
autotests/read/jxr/testcard_mono.jxr
Normal file
BIN
autotests/read/jxr/testcard_mono.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
autotests/read/jxr/testcard_rgb16.jxr
Normal file
BIN
autotests/read/jxr/testcard_rgb16.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
autotests/read/jxr/testcard_rgb8.jxr
Normal file
BIN
autotests/read/jxr/testcard_rgb8.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
autotests/read/jxr/testcard_rgba16.jxr
Normal file
BIN
autotests/read/jxr/testcard_rgba16.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
autotests/read/jxr/testcard_rgba8.jxr
Normal file
BIN
autotests/read/jxr/testcard_rgba8.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
autotests/read/pcx/16color.pcx
Normal file
BIN
autotests/read/pcx/16color.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
autotests/read/pcx/indexed4.pcx
Normal file
BIN
autotests/read/pcx/indexed4.png
Normal file
After Width: | Height: | Size: 593 B |
BIN
autotests/read/pfm/testcard_gray.pfm
Normal file
BIN
autotests/read/pfm/testcard_gray.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
autotests/read/pfm/testcard_rgb.pfm
Normal file
BIN
autotests/read/pfm/testcard_rgb.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
autotests/read/pfm/testcard_rgb_ps.pfm
Normal file
BIN
autotests/read/pfm/testcard_rgb_ps.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
autotests/read/psd/cmyk16_testcard.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
autotests/read/psd/cmyk16_testcard.psd
Normal file
11
autotests/read/psd/cmyk16_testcard.psd.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.8.0",
|
||||||
|
"fileName" : "cmyk16_testcard_qt6_8.tif"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.0.0",
|
||||||
|
"maxQtVersion" : "6.7.99",
|
||||||
|
"fileName" : "cmyk16_testcard.png"
|
||||||
|
}
|
||||||
|
]
|
BIN
autotests/read/psd/cmyk16_testcard_qt6_8.tif
Normal file
BIN
autotests/read/psd/cmyk8_testcard.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
autotests/read/psd/cmyk8_testcard.psd
Normal file
11
autotests/read/psd/cmyk8_testcard.psd.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.8.0",
|
||||||
|
"fileName" : "cmyk8_testcard_qt6_8.tif"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.0.0",
|
||||||
|
"maxQtVersion" : "6.7.99",
|
||||||
|
"fileName" : "cmyk8_testcard.png"
|
||||||
|
}
|
||||||
|
]
|
BIN
autotests/read/psd/cmyk8_testcard_qt6_8.tif
Normal file
11
autotests/read/psd/cmyka-16bits.psd.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.8.0",
|
||||||
|
"fileName" : "cmyka-16bits_qt6_8.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.0.0",
|
||||||
|
"maxQtVersion" : "6.7.99",
|
||||||
|
"fileName" : "cmyka-16bits.png"
|
||||||
|
}
|
||||||
|
]
|
BIN
autotests/read/psd/cmyka-16bits_qt6_8.png
Normal file
After Width: | Height: | Size: 111 KiB |
11
autotests/read/psd/cmyka-8bits.psd.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.8.0",
|
||||||
|
"fileName" : "cmyka-8bits_qt6_8.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.0.0",
|
||||||
|
"maxQtVersion" : "6.7.99",
|
||||||
|
"fileName" : "cmyka-8bits.png"
|
||||||
|
}
|
||||||
|
]
|
BIN
autotests/read/psd/cmyka-8bits_qt6_8.png
Normal file
After Width: | Height: | Size: 77 KiB |
11
autotests/read/psd/mch-16bits.psd.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.8.0",
|
||||||
|
"fileName" : "mch-16bits_qt_6_8.tif"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.0.0",
|
||||||
|
"maxQtVersion" : "6.7.99",
|
||||||
|
"fileName" : "mch-16bits.png"
|
||||||
|
}
|
||||||
|
]
|
BIN
autotests/read/psd/mch-16bits_qt_6_8.tif
Normal file
11
autotests/read/psd/mch-8bits.psd.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.8.0",
|
||||||
|
"fileName" : "mch-8bits_qt_6.8.tif"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.0.0",
|
||||||
|
"maxQtVersion" : "6.7.99",
|
||||||
|
"fileName" : "mch-8bits.png"
|
||||||
|
}
|
||||||
|
]
|
BIN
autotests/read/psd/mch-8bits_qt_6.8.tif
Normal file
BIN
autotests/read/pxr/testcard_gray.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
autotests/read/pxr/testcard_gray.pxr
Normal file
BIN
autotests/read/pxr/testcard_rgb.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
autotests/read/pxr/testcard_rgb.pxr
Normal file
32
autotests/read/xcf/birthday16.xcf.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.7.0",
|
||||||
|
"fileName" : "birthday16.png",
|
||||||
|
"seeAlso" : "https://bugreports.qt.io/browse/QTBUG-120614"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.0.0",
|
||||||
|
"maxQtVersion" : "6.2.10",
|
||||||
|
"fileName" : "birthday16_alphabug.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.3.0",
|
||||||
|
"maxQtVersion" : "6.3.2",
|
||||||
|
"fileName" : "birthday32_alphabug.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.4.0",
|
||||||
|
"maxQtVersion" : "6.4.3",
|
||||||
|
"fileName" : "birthday32_alphabug.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.5.0",
|
||||||
|
"maxQtVersion" : "6.5.4",
|
||||||
|
"fileName" : "birthday16_alphabug.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.6.0",
|
||||||
|
"maxQtVersion" : "6.6.1",
|
||||||
|
"fileName" : "birthday16_alphabug.png"
|
||||||
|
}
|
||||||
|
]
|
BIN
autotests/read/xcf/birthday16_alphabug.png
Normal file
After Width: | Height: | Size: 91 KiB |
32
autotests/read/xcf/birthday32.xcf.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.7.0",
|
||||||
|
"fileName" : "birthday32.png",
|
||||||
|
"seeAlso" : "https://bugreports.qt.io/browse/QTBUG-120614"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.0.0",
|
||||||
|
"maxQtVersion" : "6.2.10",
|
||||||
|
"fileName" : "birthday32_alphabug.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.3.0",
|
||||||
|
"maxQtVersion" : "6.3.2",
|
||||||
|
"fileName" : "birthday32_alphabug.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.4.0",
|
||||||
|
"maxQtVersion" : "6.4.3",
|
||||||
|
"fileName" : "birthday32_alphabug.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.5.0",
|
||||||
|
"maxQtVersion" : "6.5.4",
|
||||||
|
"fileName" : "birthday32_alphabug.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"minQtVersion" : "6.6.0",
|
||||||
|
"maxQtVersion" : "6.6.1",
|
||||||
|
"fileName" : "birthday32_alphabug.png"
|
||||||
|
}
|
||||||
|
]
|
BIN
autotests/read/xcf/birthday32_alphabug.png
Normal file
After Width: | Height: | Size: 91 KiB |
BIN
autotests/read/xcf/bug_476755_gray_layers.png
Normal file
After Width: | Height: | Size: 234 KiB |
BIN
autotests/read/xcf/bug_476755_gray_layers.xcf
Normal file
BIN
autotests/read/xcf/bug_476755_rgb_layers.png
Normal file
After Width: | Height: | Size: 234 KiB |
BIN
autotests/read/xcf/bug_476755_rgb_layers.xcf
Normal file
@ -16,6 +16,7 @@
|
|||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
|
|
||||||
#include "../tests/format-enum.h"
|
#include "../tests/format-enum.h"
|
||||||
|
#include "templateimage.h"
|
||||||
|
|
||||||
#include "fuzzyeq.cpp"
|
#include "fuzzyeq.cpp"
|
||||||
|
|
||||||
@ -89,13 +90,115 @@ static QImage::Format preferredFormat(QImage::Format fmt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The OptionTest class
|
||||||
|
* Class for testing image options.
|
||||||
|
* Supports the most common options:
|
||||||
|
* - Size
|
||||||
|
* - ImageFormat
|
||||||
|
* - ImageTransformation (rotations)
|
||||||
|
* \todo Add missing options if needed.
|
||||||
|
*/
|
||||||
|
class OptionTest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OptionTest()
|
||||||
|
: m_size(QSize())
|
||||||
|
, m_format(QImage::Format_Invalid)
|
||||||
|
, m_transformations(QImageIOHandler::TransformationNone)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionTest(const OptionTest&) = default;
|
||||||
|
OptionTest& operator =(const OptionTest&) = default;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief store
|
||||||
|
* Stores the supported options of the reader.
|
||||||
|
* \param reader
|
||||||
|
* \return True on success, otherwise false.
|
||||||
|
*/
|
||||||
|
bool store(const QImageReader *reader = nullptr)
|
||||||
|
{
|
||||||
|
if (reader == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool ok = true;
|
||||||
|
if (reader->supportsOption(QImageIOHandler::Size)) {
|
||||||
|
m_size = reader->size();
|
||||||
|
if (m_size.isEmpty())
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
if (reader->supportsOption(QImageIOHandler::ImageFormat)) {
|
||||||
|
m_format = reader->imageFormat();
|
||||||
|
if (m_format == QImage::Format_Invalid)
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
if (reader->supportsOption(QImageIOHandler::ImageTransformation)) {
|
||||||
|
m_transformations = reader->transformation();
|
||||||
|
if (m_transformations < 0 || m_transformations > 7)
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief compare
|
||||||
|
* Compare the stored values with the ones read from the image reader.
|
||||||
|
* \param reader
|
||||||
|
* \return True on success, otherwise false.
|
||||||
|
*/
|
||||||
|
bool compare(const QImageReader *reader)
|
||||||
|
{
|
||||||
|
if (reader == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool ok = true;
|
||||||
|
if (reader->supportsOption(QImageIOHandler::Size)) {
|
||||||
|
ok = ok && (m_size == reader->size());
|
||||||
|
}
|
||||||
|
if (reader->supportsOption(QImageIOHandler::ImageFormat)) {
|
||||||
|
ok = ok && (m_format == reader->imageFormat());
|
||||||
|
}
|
||||||
|
if (reader->supportsOption(QImageIOHandler::ImageTransformation)) {
|
||||||
|
ok = ok && (m_transformations == reader->transformation());
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief compare
|
||||||
|
* Compare the image properties with the ones stored.
|
||||||
|
* \param image
|
||||||
|
* \return True on success, otherwise false.
|
||||||
|
*/
|
||||||
|
bool compare(const QImage& image)
|
||||||
|
{
|
||||||
|
bool ok = true;
|
||||||
|
if (!m_size.isEmpty()) {
|
||||||
|
// Size option return the size without transformation (tested with Qt TIFF plugin).
|
||||||
|
ok = ok && (m_size == image.size() || m_size == image.size().transposed());
|
||||||
|
}
|
||||||
|
if (m_format != QImage::Format_Invalid) {
|
||||||
|
ok = ok && (m_format == image.format());
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSize m_size;
|
||||||
|
QImage::Format m_format;
|
||||||
|
QImageIOHandler::Transformations m_transformations;
|
||||||
|
};
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
QCoreApplication app(argc, argv);
|
QCoreApplication app(argc, argv);
|
||||||
QCoreApplication::removeLibraryPath(QStringLiteral(PLUGIN_DIR));
|
QCoreApplication::removeLibraryPath(QStringLiteral(PLUGIN_DIR));
|
||||||
QCoreApplication::addLibraryPath(QStringLiteral(PLUGIN_DIR));
|
QCoreApplication::addLibraryPath(QStringLiteral(PLUGIN_DIR));
|
||||||
QCoreApplication::setApplicationName(QStringLiteral("readtest"));
|
QCoreApplication::setApplicationName(QStringLiteral("readtest"));
|
||||||
QCoreApplication::setApplicationVersion(QStringLiteral("1.1.0"));
|
QCoreApplication::setApplicationVersion(QStringLiteral("1.2.0"));
|
||||||
|
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.setApplicationDescription(QStringLiteral("Performs basic image conversion checking."));
|
parser.setApplicationDescription(QStringLiteral("Performs basic image conversion checking."));
|
||||||
@ -159,28 +262,39 @@ int main(int argc, char **argv)
|
|||||||
QTextStream(stdout) << "* Run on RANDOM ACCESS device\n";
|
QTextStream(stdout) << "* Run on RANDOM ACCESS device\n";
|
||||||
}
|
}
|
||||||
for (const QFileInfo &fi : lstImgDir) {
|
for (const QFileInfo &fi : lstImgDir) {
|
||||||
if (!fi.suffix().compare("png", Qt::CaseInsensitive) || !fi.suffix().compare("tif", Qt::CaseInsensitive)) {
|
TemplateImage timg(fi);
|
||||||
|
if (timg.isTemplate()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int suffixPos = fi.filePath().size() - suffix.size();
|
|
||||||
QString inputfile = fi.filePath();
|
|
||||||
QString fmt = QStringLiteral("png");
|
|
||||||
QString expfile = fi.filePath().replace(suffixPos, suffix.size(), fmt);
|
|
||||||
if (!QFile::exists(expfile)) { // try with tiff
|
|
||||||
fmt = QStringLiteral("tif");
|
|
||||||
expfile = fi.filePath().replace(suffixPos, suffix.size(), fmt);
|
|
||||||
}
|
|
||||||
QString expfilename = QFileInfo(expfile).fileName();
|
|
||||||
|
|
||||||
std::unique_ptr<QIODevice> inputDevice(seq ? new SequentialFile(inputfile) : new QFile(inputfile));
|
TemplateImage::TestFlags flags = TemplateImage::None;
|
||||||
|
QString comment;
|
||||||
|
QFileInfo expFileInfo = timg.compareImage(flags, comment);
|
||||||
|
if ((flags & TemplateImage::SkipTest) == TemplateImage::SkipTest) {
|
||||||
|
if(comment.isEmpty())
|
||||||
|
comment = QStringLiteral("image format not supported by current Qt version!");
|
||||||
|
QTextStream(stdout) << "SKIP : " << fi.fileName() << QStringLiteral(": %1\n").arg(comment);
|
||||||
|
++skipped;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!formatStrings.contains(expFileInfo.suffix(), Qt::CaseInsensitive)) {
|
||||||
|
// Work Around for CCBUG: 468288
|
||||||
|
QTextStream(stdout) << "SKIP : " << fi.fileName() << ": comparison image " << expFileInfo.fileName() << " cannot be loaded due to the lack of "
|
||||||
|
<< expFileInfo.suffix().toUpper() << " plugin!\n";
|
||||||
|
++skipped;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QString expfilename = expFileInfo.fileName();
|
||||||
|
|
||||||
|
std::unique_ptr<QIODevice> inputDevice(seq ? new SequentialFile(fi.filePath()) : new QFile(fi.filePath()));
|
||||||
QImageReader inputReader(inputDevice.get(), format);
|
QImageReader inputReader(inputDevice.get(), format);
|
||||||
QImageReader expReader(expfile, fmt.toLatin1());
|
QImageReader expReader(expFileInfo.filePath());
|
||||||
|
|
||||||
QImage inputImage;
|
QImage inputImage;
|
||||||
QImage expImage;
|
QImage expImage;
|
||||||
|
|
||||||
// inputImage is auto-rotated to final orientation
|
// inputImage is auto-rotated to final orientation
|
||||||
inputReader.setAutoTransform(true);
|
inputReader.setAutoTransform((flags & TemplateImage::DisableAutotransform) != TemplateImage::DisableAutotransform);
|
||||||
|
|
||||||
if (!expReader.read(&expImage)) {
|
if (!expReader.read(&expImage)) {
|
||||||
QTextStream(stdout) << "ERROR: " << fi.fileName() << ": could not load " << expfilename << ": " << expReader.errorString() << "\n";
|
QTextStream(stdout) << "ERROR: " << fi.fileName() << ": could not load " << expfilename << ": " << expReader.errorString() << "\n";
|
||||||
@ -199,11 +313,32 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OptionTest optionTest;
|
||||||
|
if (!optionTest.store(&inputReader)) {
|
||||||
|
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": error while reading options\n";
|
||||||
|
++failed;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!inputReader.read(&inputImage)) {
|
if (!inputReader.read(&inputImage)) {
|
||||||
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": failed to load: " << inputReader.errorString() << "\n";
|
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": failed to load: " << inputReader.errorString() << "\n";
|
||||||
++failed;
|
++failed;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!optionTest.compare(&inputReader)) {
|
||||||
|
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": error while comparing options\n";
|
||||||
|
++failed;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!optionTest.compare(inputImage)) {
|
||||||
|
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": error while comparing the image properties with options\n";
|
||||||
|
++failed;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (expImage.width() != inputImage.width()) {
|
if (expImage.width() != inputImage.width()) {
|
||||||
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": width was " << inputImage.width() << " but " << expfilename << " width was "
|
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": width was " << inputImage.width() << " but " << expfilename << " width was "
|
||||||
<< expImage.width() << "\n";
|
<< expImage.width() << "\n";
|
||||||
|
111
autotests/templateimage.cpp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2024 Mirco Miranda <mircomir@outlook.com>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "templateimage.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QVersionNumber>
|
||||||
|
|
||||||
|
TemplateImage::TemplateImage(const QFileInfo &fi) :
|
||||||
|
m_fi(fi)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TemplateImage::isTemplate() const
|
||||||
|
{
|
||||||
|
auto list = suffixes();
|
||||||
|
for (auto&& suffix : list) {
|
||||||
|
if (!m_fi.suffix().compare(suffix, Qt::CaseInsensitive))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo TemplateImage::compareImage(TestFlags &flags, QString& comment) const
|
||||||
|
{
|
||||||
|
auto fi = jsonImage(flags, comment);
|
||||||
|
if ((flags & TestFlag::SkipTest) == TestFlag::SkipTest) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (fi.exists()) {
|
||||||
|
return fi;
|
||||||
|
}
|
||||||
|
return legacyImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QStringList TemplateImage::suffixes()
|
||||||
|
{
|
||||||
|
return QStringList({"png", "tif", "tiff", "json"});
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo TemplateImage::legacyImage() const
|
||||||
|
{
|
||||||
|
auto list = suffixes();
|
||||||
|
for (auto&& suffix : list) {
|
||||||
|
auto fi = QFileInfo(QStringLiteral("%1/%2.%3").arg(m_fi.path(), m_fi.completeBaseName(), suffix));
|
||||||
|
if (fi.exists()) {
|
||||||
|
return fi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo TemplateImage::jsonImage(TestFlags &flags, QString& comment) const
|
||||||
|
{
|
||||||
|
flags = TestFlag::None;
|
||||||
|
auto fi = QFileInfo(QStringLiteral("%1.json").arg(m_fi.filePath()));
|
||||||
|
if (!fi.exists()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile f(fi.filePath());
|
||||||
|
if (!f.open(QFile::ReadOnly)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonParseError err;
|
||||||
|
auto doc = QJsonDocument::fromJson(f.readAll(), &err);
|
||||||
|
if (err.error != QJsonParseError::NoError || !doc.isArray()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto currentQt = QVersionNumber::fromString(qVersion());
|
||||||
|
auto arr = doc.array();
|
||||||
|
for (auto val : arr) {
|
||||||
|
if (!val.isObject())
|
||||||
|
continue;
|
||||||
|
auto obj = val.toObject();
|
||||||
|
auto minQt = QVersionNumber::fromString(obj.value("minQtVersion").toString());
|
||||||
|
auto maxQt = QVersionNumber::fromString(obj.value("maxQtVersion").toString());
|
||||||
|
auto name = obj.value("fileName").toString();
|
||||||
|
auto unsupportedFormat = obj.value("unsupportedFormat").toBool();
|
||||||
|
comment = obj.value("comment").toString();
|
||||||
|
|
||||||
|
if(obj.value("disableAutoTransform").toBool())
|
||||||
|
flags |= TestFlag::DisableAutotransform;
|
||||||
|
|
||||||
|
// filter
|
||||||
|
if (name.isEmpty() && !unsupportedFormat)
|
||||||
|
continue;
|
||||||
|
if (!minQt.isNull() && currentQt < minQt)
|
||||||
|
continue;
|
||||||
|
if (!maxQt.isNull() && currentQt > maxQt)
|
||||||
|
continue;
|
||||||
|
if (unsupportedFormat) {
|
||||||
|
flags |= TestFlag::SkipTest;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return QFileInfo(QStringLiteral("%1/%2").arg(fi.path(), name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
83
autotests/templateimage.h
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2024 Mirco Miranda <mircomir@outlook.com>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TEMPLATEIMAGE_H
|
||||||
|
#define TEMPLATEIMAGE_H
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The TemplateImage class
|
||||||
|
* Given an image name, it decides the template image to compare it with.
|
||||||
|
*/
|
||||||
|
class TemplateImage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum TestFlag {
|
||||||
|
None = 0x0,
|
||||||
|
SkipTest = 0x1,
|
||||||
|
DisableAutotransform = 0x2
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(TestFlags, TestFlag)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief TemplateImage
|
||||||
|
* \param fi The image to test.
|
||||||
|
*/
|
||||||
|
TemplateImage(const QFileInfo& fi);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief TemplateImage
|
||||||
|
* Default copy constructor.
|
||||||
|
*/
|
||||||
|
TemplateImage(const TemplateImage& other) = default;
|
||||||
|
/*!
|
||||||
|
* \brief operator =
|
||||||
|
* Default copy operator
|
||||||
|
*/
|
||||||
|
TemplateImage& operator=(const TemplateImage& other) = default;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief isTemplate
|
||||||
|
* \return True if the image is a template, false otherwise.
|
||||||
|
* \sa suffixes
|
||||||
|
*/
|
||||||
|
bool isTemplate() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief compareImage
|
||||||
|
* \param flags Flags for modifying test behavior (e.g. image format not supported by current Qt version).
|
||||||
|
* \return The template image to use for the comparison.
|
||||||
|
*/
|
||||||
|
QFileInfo compareImage(TestFlags &flags, QString& comment) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief suffixes
|
||||||
|
* \return The list of suffixes considered templates.
|
||||||
|
*/
|
||||||
|
static QStringList suffixes();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*!
|
||||||
|
* \brief legacyImage
|
||||||
|
* \return The template image calculated from the source image name.
|
||||||
|
*/
|
||||||
|
QFileInfo legacyImage() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief jsonImage
|
||||||
|
* \param flags Flags for modifying test behavior.
|
||||||
|
* \return The template image read from the corresponding JSON.
|
||||||
|
*/
|
||||||
|
QFileInfo jsonImage(TestFlags &flags, QString& comment) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QFileInfo m_fi;
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_OPERATORS_FOR_FLAGS(TemplateImage::TestFlags)
|
||||||
|
|
||||||
|
#endif // TEMPLATEIMAGE_H
|
24
cmake/find-modules/FindLibJXR.cmake
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# - Find LibJXR
|
||||||
|
# Find the JXR library
|
||||||
|
# This module defines
|
||||||
|
# LIBJXR_INCLUDE_DIRS, where to find jxrlib/JXRGlue.h
|
||||||
|
# LIBJXR_LIBRARIES, the libraries needed to use JXR
|
||||||
|
#
|
||||||
|
# Based on cmake code found at https://github.com/microsoft/vcpkg/blob/master/ports/jxrlib/FindJXR.cmake
|
||||||
|
|
||||||
|
find_path(LIBJXR_INCLUDE_DIRS
|
||||||
|
NAMES JXRGlue.h
|
||||||
|
PATH_SUFFIXES jxrlib
|
||||||
|
)
|
||||||
|
mark_as_advanced(LIBJXR_INCLUDE_DIRS)
|
||||||
|
|
||||||
|
include(SelectLibraryConfigurations)
|
||||||
|
|
||||||
|
find_library(LIBJPEGXR_LIBRARY NAMES jpegxr)
|
||||||
|
find_library(LIBJXRGLUE_LIBRARY NAMES jxrglue)
|
||||||
|
|
||||||
|
set(LIBJXR_LIBRARIES ${LIBJPEGXR_LIBRARY} ${LIBJXRGLUE_LIBRARY})
|
||||||
|
mark_as_advanced(LIBJXR_LIBRARIES)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibJXR DEFAULT_MSG LIBJXR_INCLUDE_DIRS LIBJXR_LIBRARIES)
|
@ -1,4 +1,3 @@
|
|||||||
maintainer: alexmerry
|
|
||||||
description: Image format plugins for Qt
|
description: Image format plugins for Qt
|
||||||
tier: 2
|
tier: 2
|
||||||
type: functional
|
type: functional
|
||||||
|
@ -83,7 +83,15 @@ kimageformats_add_plugin(kimg_pic SOURCES pic.cpp)
|
|||||||
|
|
||||||
##################################
|
##################################
|
||||||
|
|
||||||
kimageformats_add_plugin(kimg_psd SOURCES psd.cpp)
|
kimageformats_add_plugin(kimg_pfm SOURCES pfm.cpp)
|
||||||
|
|
||||||
|
##################################
|
||||||
|
|
||||||
|
kimageformats_add_plugin(kimg_psd SOURCES psd.cpp scanlineconverter.cpp)
|
||||||
|
|
||||||
|
##################################
|
||||||
|
|
||||||
|
kimageformats_add_plugin(kimg_pxr SOURCES pxr.cpp)
|
||||||
|
|
||||||
##################################
|
##################################
|
||||||
|
|
||||||
@ -115,6 +123,20 @@ endif()
|
|||||||
|
|
||||||
##################################
|
##################################
|
||||||
|
|
||||||
|
if (LibJXR_FOUND)
|
||||||
|
kimageformats_add_plugin(kimg_jxr SOURCES jxr.cpp)
|
||||||
|
kde_enable_exceptions()
|
||||||
|
target_include_directories(kimg_jxr PRIVATE ${LIBJXR_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(kimg_jxr PRIVATE jpegxr jxrglue)
|
||||||
|
target_compile_definitions(kimg_jxr PRIVATE INITGUID)
|
||||||
|
if (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=undef")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error=undef")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
##################################
|
||||||
|
|
||||||
if (KF6Archive_FOUND)
|
if (KF6Archive_FOUND)
|
||||||
|
|
||||||
kimageformats_add_plugin(kimg_kra SOURCES kra.cpp)
|
kimageformats_add_plugin(kimg_kra SOURCES kra.cpp)
|
||||||
|
@ -619,7 +619,15 @@ bool QAVIFHandler::write(const QImage &image)
|
|||||||
QImage tmpgrayimage = image.convertToFormat(tmpformat);
|
QImage tmpgrayimage = image.convertToFormat(tmpformat);
|
||||||
|
|
||||||
avif = avifImageCreate(tmpgrayimage.width(), tmpgrayimage.height(), save_depth, AVIF_PIXEL_FORMAT_YUV400);
|
avif = avifImageCreate(tmpgrayimage.width(), tmpgrayimage.height(), save_depth, AVIF_PIXEL_FORMAT_YUV400);
|
||||||
|
#if AVIF_VERSION >= 110000
|
||||||
|
res = avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
|
||||||
|
if (res != AVIF_RESULT_OK) {
|
||||||
|
qWarning("ERROR in avifImageAllocatePlanes: %s", avifResultToString(res));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
|
avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (tmpgrayimage.colorSpace().isValid()) {
|
if (tmpgrayimage.colorSpace().isValid()) {
|
||||||
avif->colorPrimaries = (avifColorPrimaries)1;
|
avif->colorPrimaries = (avifColorPrimaries)1;
|
||||||
@ -806,7 +814,15 @@ bool QAVIFHandler::write(const QImage &image)
|
|||||||
avif->transferCharacteristics = transfer_to_save;
|
avif->transferCharacteristics = transfer_to_save;
|
||||||
|
|
||||||
if (iccprofile.size() > 0) {
|
if (iccprofile.size() > 0) {
|
||||||
|
#if AVIF_VERSION >= 1000000
|
||||||
|
res = avifImageSetProfileICC(avif, reinterpret_cast<const uint8_t *>(iccprofile.constData()), iccprofile.size());
|
||||||
|
if (res != AVIF_RESULT_OK) {
|
||||||
|
qWarning("ERROR in avifImageSetProfileICC: %s", avifResultToString(res));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
avifImageSetProfileICC(avif, reinterpret_cast<const uint8_t *>(iccprofile.constData()), iccprofile.size());
|
avifImageSetProfileICC(avif, reinterpret_cast<const uint8_t *>(iccprofile.constData()), iccprofile.size());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
avifRGBImage rgb;
|
avifRGBImage rgb;
|
||||||
@ -971,6 +987,8 @@ bool QAVIFHandler::jumpToNextImage()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
avifResult decodeResult;
|
||||||
|
|
||||||
if (m_decoder->imageIndex >= 0) {
|
if (m_decoder->imageIndex >= 0) {
|
||||||
if (m_decoder->imageCount < 2) {
|
if (m_decoder->imageCount < 2) {
|
||||||
m_parseState = ParseAvifSuccess;
|
m_parseState = ParseAvifSuccess;
|
||||||
@ -978,11 +996,16 @@ bool QAVIFHandler::jumpToNextImage()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_decoder->imageIndex >= m_decoder->imageCount - 1) { // start from beginning
|
if (m_decoder->imageIndex >= m_decoder->imageCount - 1) { // start from beginning
|
||||||
avifDecoderReset(m_decoder);
|
decodeResult = avifDecoderReset(m_decoder);
|
||||||
|
if (decodeResult != AVIF_RESULT_OK) {
|
||||||
|
qWarning("ERROR in avifDecoderReset: %s", avifResultToString(decodeResult));
|
||||||
|
m_parseState = ParseAvifError;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
avifResult decodeResult = avifDecoderNextImage(m_decoder);
|
decodeResult = avifDecoderNextImage(m_decoder);
|
||||||
|
|
||||||
if (decodeResult != AVIF_RESULT_OK) {
|
if (decodeResult != AVIF_RESULT_OK) {
|
||||||
qWarning("ERROR: Failed to decode Next image in sequence: %s", avifResultToString(decodeResult));
|
qWarning("ERROR: Failed to decode Next image in sequence: %s", avifResultToString(decodeResult));
|
||||||
|
@ -7,20 +7,11 @@
|
|||||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* *** EXR_USE_LEGACY_CONVERSIONS ***
|
|
||||||
* If defined, the result image is an 8-bit RGB(A) converted
|
|
||||||
* without icc profiles. Otherwise, a 16-bit images is generated.
|
|
||||||
* NOTE: The use of legacy conversions are discouraged due to
|
|
||||||
* imprecise image result.
|
|
||||||
*/
|
|
||||||
//#define EXR_USE_LEGACY_CONVERSIONS // default commented -> you should define it in your cmake file
|
|
||||||
|
|
||||||
/* *** EXR_CONVERT_TO_SRGB ***
|
/* *** EXR_CONVERT_TO_SRGB ***
|
||||||
* If defined, the linear data is converted to sRGB on read to accommodate
|
* If defined, the linear data is converted to sRGB on read to accommodate
|
||||||
* programs that do not support color profiles.
|
* programs that do not support color profiles.
|
||||||
* Otherwise the data are kept as is and it is the display program that
|
* Otherwise the data are kept as is and it is the display program that
|
||||||
* must convert to the monitor profile.
|
* must convert to the monitor profile.
|
||||||
* NOTE: If EXR_USE_LEGACY_CONVERSIONS is active, this is ignored.
|
|
||||||
*/
|
*/
|
||||||
//#define EXR_CONVERT_TO_SRGB // default: commented -> you should define it in your cmake file
|
//#define EXR_CONVERT_TO_SRGB // default: commented -> you should define it in your cmake file
|
||||||
|
|
||||||
@ -92,13 +83,6 @@
|
|||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QTimeZone>
|
#include <QTimeZone>
|
||||||
|
|
||||||
// Allow the code to works on all QT versions supported by KDE
|
|
||||||
// project (Qt 5.15 and Qt 6.x) to easy backports fixes.
|
|
||||||
#if !defined(EXR_USE_LEGACY_CONVERSIONS)
|
|
||||||
// If uncommented, the image is rendered in a float16 format, the result is very precise
|
|
||||||
#define EXR_USE_QT6_FLOAT_IMAGE // default uncommented
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class K_IStream : public Imf::IStream
|
class K_IStream : public Imf::IStream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -209,22 +193,6 @@ void K_OStream::seekg(Imf::Int64 pos)
|
|||||||
m_dev->seek(pos);
|
m_dev->seek(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef EXR_USE_LEGACY_CONVERSIONS
|
|
||||||
// source: https://openexr.com/en/latest/ReadingAndWritingImageFiles.html
|
|
||||||
inline unsigned char gamma(float x)
|
|
||||||
{
|
|
||||||
x = std::pow(5.5555f * std::max(0.f, x), 0.4545f) * 84.66f;
|
|
||||||
return (unsigned char)qBound(0.f, x, 255.f);
|
|
||||||
}
|
|
||||||
inline QRgb RgbaToQrgba(struct Imf::Rgba &imagePixel)
|
|
||||||
{
|
|
||||||
return qRgba(gamma(float(imagePixel.r)),
|
|
||||||
gamma(float(imagePixel.g)),
|
|
||||||
gamma(float(imagePixel.b)),
|
|
||||||
(unsigned char)(qBound(0.f, imagePixel.a * 255.f, 255.f) + 0.5f));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
EXRHandler::EXRHandler()
|
EXRHandler::EXRHandler()
|
||||||
: m_compressionRatio(-1)
|
: m_compressionRatio(-1)
|
||||||
, m_quality(-1)
|
, m_quality(-1)
|
||||||
@ -248,13 +216,7 @@ bool EXRHandler::canRead() const
|
|||||||
static QImage::Format imageFormat(const Imf::RgbaInputFile &file)
|
static QImage::Format imageFormat(const Imf::RgbaInputFile &file)
|
||||||
{
|
{
|
||||||
auto isRgba = file.channels() & Imf::RgbaChannels::WRITE_A;
|
auto isRgba = file.channels() & Imf::RgbaChannels::WRITE_A;
|
||||||
#if defined(EXR_USE_LEGACY_CONVERSIONS)
|
|
||||||
return (isRgba ? QImage::Format_ARGB32 : QImage::Format_RGB32);
|
|
||||||
#elif defined(EXR_USE_QT6_FLOAT_IMAGE)
|
|
||||||
return (isRgba ? QImage::Format_RGBA16FPx4 : QImage::Format_RGBX16FPx4);
|
return (isRgba ? QImage::Format_RGBA16FPx4 : QImage::Format_RGBX16FPx4);
|
||||||
#else
|
|
||||||
return (isRgba ? QImage::Format_RGBA64 : QImage::Format_RGBX64);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -290,23 +252,23 @@ static void readMetadata(const Imf::Header &header, QImage &image)
|
|||||||
{
|
{
|
||||||
// set some useful metadata
|
// set some useful metadata
|
||||||
if (auto comments = header.findTypedAttribute<Imf::StringAttribute>("comments")) {
|
if (auto comments = header.findTypedAttribute<Imf::StringAttribute>("comments")) {
|
||||||
image.setText(QStringLiteral("Comment"), QString::fromStdString(comments->value()));
|
image.setText(QStringLiteral(META_KEY_COMMENT), QString::fromStdString(comments->value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto owner = header.findTypedAttribute<Imf::StringAttribute>("owner")) {
|
if (auto owner = header.findTypedAttribute<Imf::StringAttribute>("owner")) {
|
||||||
image.setText(QStringLiteral("Owner"), QString::fromStdString(owner->value()));
|
image.setText(QStringLiteral(META_KEY_OWNER), QString::fromStdString(owner->value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto lat = header.findTypedAttribute<Imf::FloatAttribute>("latitude")) {
|
if (auto lat = header.findTypedAttribute<Imf::FloatAttribute>("latitude")) {
|
||||||
image.setText(QStringLiteral("Latitude"), QLocale::c().toString(lat->value()));
|
image.setText(QStringLiteral(META_KEY_LATITUDE), QLocale::c().toString(lat->value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto lon = header.findTypedAttribute<Imf::FloatAttribute>("longitude")) {
|
if (auto lon = header.findTypedAttribute<Imf::FloatAttribute>("longitude")) {
|
||||||
image.setText(QStringLiteral("Longitude"), QLocale::c().toString(lon->value()));
|
image.setText(QStringLiteral(META_KEY_LONGITUDE), QLocale::c().toString(lon->value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto alt = header.findTypedAttribute<Imf::FloatAttribute>("altitude")) {
|
if (auto alt = header.findTypedAttribute<Imf::FloatAttribute>("altitude")) {
|
||||||
image.setText(QStringLiteral("Altitude"), QLocale::c().toString(alt->value()));
|
image.setText(QStringLiteral(META_KEY_ALTITUDE), QLocale::c().toString(alt->value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto capDate = header.findTypedAttribute<Imf::StringAttribute>("capDate")) {
|
if (auto capDate = header.findTypedAttribute<Imf::StringAttribute>("capDate")) {
|
||||||
@ -317,7 +279,7 @@ static void readMetadata(const Imf::Header &header, QImage &image)
|
|||||||
auto dateTime = QDateTime::fromString(QString::fromStdString(capDate->value()), QStringLiteral("yyyy:MM:dd HH:mm:ss"));
|
auto dateTime = QDateTime::fromString(QString::fromStdString(capDate->value()), QStringLiteral("yyyy:MM:dd HH:mm:ss"));
|
||||||
if (dateTime.isValid()) {
|
if (dateTime.isValid()) {
|
||||||
dateTime.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(off));
|
dateTime.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(off));
|
||||||
image.setText(QStringLiteral("CreationDate"), dateTime.toString(Qt::ISODate));
|
image.setText(QStringLiteral(META_KEY_CREATIONDATE), dateTime.toString(Qt::ISODate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,26 +294,30 @@ static void readMetadata(const Imf::Header &header, QImage &image)
|
|||||||
|
|
||||||
// Non-standard attribute
|
// Non-standard attribute
|
||||||
if (auto xmp = header.findTypedAttribute<Imf::StringAttribute>("xmp")) {
|
if (auto xmp = header.findTypedAttribute<Imf::StringAttribute>("xmp")) {
|
||||||
image.setText(QStringLiteral("XML:com.adobe.xmp"), QString::fromStdString(xmp->value()));
|
image.setText(QStringLiteral(META_KEY_XMP_ADOBE), QString::fromStdString(xmp->value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: OpenEXR 3.2 metadata
|
// camera metadata
|
||||||
*
|
if (auto manufacturer = header.findTypedAttribute<Imf::StringAttribute>("cameraMake")) {
|
||||||
* New Optional Standard Attributes:
|
image.setText(QStringLiteral(META_KEY_MANUFACTURER), QString::fromStdString(manufacturer->value()));
|
||||||
* - Support automated editorial workflow:
|
}
|
||||||
* reelName, imageCounter, ascFramingDecisionList
|
if (auto model = header.findTypedAttribute<Imf::StringAttribute>("cameraModel")) {
|
||||||
*
|
image.setText(QStringLiteral(META_KEY_MODEL), QString::fromStdString(model->value()));
|
||||||
* - Support forensics (“which other shots used that camera and lens before the camera firmware was updated?”):
|
}
|
||||||
* cameraMake, cameraModel, cameraSerialNumber, cameraFirmware, cameraUuid, cameraLabel, lensMake, lensModel,
|
if (auto serial = header.findTypedAttribute<Imf::StringAttribute>("cameraSerialNumber")) {
|
||||||
* lensSerialNumber, lensFirmware, cameraColorBalance
|
image.setText(QStringLiteral(META_KEY_SERIALNUMBER), QString::fromStdString(serial->value()));
|
||||||
*
|
}
|
||||||
* -Support pickup shots (reproduce critical camera settings):
|
|
||||||
* shutterAngle, cameraCCTSetting, cameraTintSetting
|
// lens metadata
|
||||||
*
|
if (auto manufacturer = header.findTypedAttribute<Imf::StringAttribute>("lensMake")) {
|
||||||
* - Support metadata-driven match move:
|
image.setText(QStringLiteral(META_KEY_LENS_MANUFACTURER), QString::fromStdString(manufacturer->value()));
|
||||||
* sensorCenterOffset, sensorOverallDimensions, sensorPhotositePitch, sensorAcquisitionRectanglenominalFocalLength,
|
}
|
||||||
* effectiveFocalLength, pinholeFocalLength, entrancePupilOffset, tStop(complementing existing 'aperture')
|
if (auto model = header.findTypedAttribute<Imf::StringAttribute>("lensModel")) {
|
||||||
*/
|
image.setText(QStringLiteral(META_KEY_LENS_MODEL), QString::fromStdString(model->value()));
|
||||||
|
}
|
||||||
|
if (auto serial = header.findTypedAttribute<Imf::StringAttribute>("lensSerialNumber")) {
|
||||||
|
image.setText(QStringLiteral(META_KEY_LENS_SERIALNUMBER), QString::fromStdString(serial->value()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -361,8 +327,6 @@ static void readMetadata(const Imf::Header &header, QImage &image)
|
|||||||
static void readColorSpace(const Imf::Header &header, QImage &image)
|
static void readColorSpace(const Imf::Header &header, QImage &image)
|
||||||
{
|
{
|
||||||
// final color operations
|
// final color operations
|
||||||
#ifndef EXR_USE_LEGACY_CONVERSIONS
|
|
||||||
|
|
||||||
QColorSpace cs;
|
QColorSpace cs;
|
||||||
if (auto chroma = header.findTypedAttribute<Imf::ChromaticitiesAttribute>("chromaticities")) {
|
if (auto chroma = header.findTypedAttribute<Imf::ChromaticitiesAttribute>("chromaticities")) {
|
||||||
auto &&v = chroma->value();
|
auto &&v = chroma->value();
|
||||||
@ -380,8 +344,6 @@ static void readColorSpace(const Imf::Header &header, QImage &image)
|
|||||||
#ifdef EXR_CONVERT_TO_SRGB
|
#ifdef EXR_CONVERT_TO_SRGB
|
||||||
image.convertToColorSpace(QColorSpace(QColorSpace::SRgb));
|
image.convertToColorSpace(QColorSpace(QColorSpace::SRgb));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // !EXR_USE_LEGACY_CONVERSIONS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EXRHandler::read(QImage *outImage)
|
bool EXRHandler::read(QImage *outImage)
|
||||||
@ -432,7 +394,6 @@ bool EXRHandler::read(QImage *outImage)
|
|||||||
pixels.resizeErase(EXR_LINES_PER_BLOCK, width);
|
pixels.resizeErase(EXR_LINES_PER_BLOCK, width);
|
||||||
bool isRgba = image.hasAlphaChannel();
|
bool isRgba = image.hasAlphaChannel();
|
||||||
|
|
||||||
// somehow copy pixels into image
|
|
||||||
for (int y = 0, n = 0; y < height; y += n) {
|
for (int y = 0, n = 0; y < height; y += n) {
|
||||||
auto my = dw.min.y + y;
|
auto my = dw.min.y + y;
|
||||||
if (my > dw.max.y) { // paranoia check
|
if (my > dw.max.y) { // paranoia check
|
||||||
@ -443,30 +404,14 @@ bool EXRHandler::read(QImage *outImage)
|
|||||||
file.readPixels(my, std::min(my + EXR_LINES_PER_BLOCK - 1, dw.max.y));
|
file.readPixels(my, std::min(my + EXR_LINES_PER_BLOCK - 1, dw.max.y));
|
||||||
|
|
||||||
for (n = 0; n < std::min(EXR_LINES_PER_BLOCK, height - y); ++n) {
|
for (n = 0; n < std::min(EXR_LINES_PER_BLOCK, height - y); ++n) {
|
||||||
#if defined(EXR_USE_LEGACY_CONVERSIONS)
|
|
||||||
Q_UNUSED(isRgba)
|
|
||||||
auto scanLine = reinterpret_cast<QRgb *>(image.scanLine(y + n));
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
*(scanLine + x) = RgbaToQrgba(pixels[n][x]);
|
|
||||||
}
|
|
||||||
#elif defined(EXR_USE_QT6_FLOAT_IMAGE)
|
|
||||||
auto scanLine = reinterpret_cast<qfloat16 *>(image.scanLine(y + n));
|
auto scanLine = reinterpret_cast<qfloat16 *>(image.scanLine(y + n));
|
||||||
for (int x = 0; x < width; ++x) {
|
for (int x = 0; x < width; ++x) {
|
||||||
auto xcs = x * 4;
|
auto xcs = x * 4;
|
||||||
*(scanLine + xcs) = qfloat16(qBound(0.f, float(pixels[n][x].r), 1.f));
|
*(scanLine + xcs) = qfloat16(float(pixels[n][x].r));
|
||||||
*(scanLine + xcs + 1) = qfloat16(qBound(0.f, float(pixels[n][x].g), 1.f));
|
*(scanLine + xcs + 1) = qfloat16(float(pixels[n][x].g));
|
||||||
*(scanLine + xcs + 2) = qfloat16(qBound(0.f, float(pixels[n][x].b), 1.f));
|
*(scanLine + xcs + 2) = qfloat16(float(pixels[n][x].b));
|
||||||
*(scanLine + xcs + 3) = qfloat16(isRgba ? qBound(0.f, float(pixels[n][x].a), 1.f) : 1.f);
|
*(scanLine + xcs + 3) = qfloat16(isRgba ? std::clamp(float(pixels[n][x].a), 0.f, 1.f) : 1.f);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
auto scanLine = reinterpret_cast<QRgba64 *>(image.scanLine(y + n));
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
*(scanLine + x) = QRgba64::fromRgba64(quint16(qBound(0.f, float(pixels[n][x].r) * 65535.f + 0.5f, 65535.f)),
|
|
||||||
quint16(qBound(0.f, float(pixels[n][x].g) * 65535.f + 0.5f, 65535.f)),
|
|
||||||
quint16(qBound(0.f, float(pixels[n][x].b) * 65535.f + 0.5f, 65535.f)),
|
|
||||||
isRgba ? quint16(qBound(0.f, float(pixels[n][x].a) * 65535.f + 0.5f, 65535.f)) : quint16(65535));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -530,18 +475,18 @@ static void setMetadata(const QImage &image, Imf::Header &header)
|
|||||||
auto dateTime = QDateTime::currentDateTime();
|
auto dateTime = QDateTime::currentDateTime();
|
||||||
for (auto &&key : image.textKeys()) {
|
for (auto &&key : image.textKeys()) {
|
||||||
auto text = image.text(key);
|
auto text = image.text(key);
|
||||||
if (!key.compare(QStringLiteral("Comment"), Qt::CaseInsensitive)) {
|
if (!key.compare(QStringLiteral(META_KEY_COMMENT), Qt::CaseInsensitive)) {
|
||||||
header.insert("comments", Imf::StringAttribute(text.toStdString()));
|
header.insert("comments", Imf::StringAttribute(text.toStdString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!key.compare(QStringLiteral("Owner"), Qt::CaseInsensitive)) {
|
if (!key.compare(QStringLiteral(META_KEY_OWNER), Qt::CaseInsensitive)) {
|
||||||
header.insert("owner", Imf::StringAttribute(text.toStdString()));
|
header.insert("owner", Imf::StringAttribute(text.toStdString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
if (!key.compare(QStringLiteral("Latitude"), Qt::CaseInsensitive) ||
|
if (!key.compare(QStringLiteral(META_KEY_LATITUDE), Qt::CaseInsensitive) ||
|
||||||
!key.compare(QStringLiteral("Longitude"), Qt::CaseInsensitive) ||
|
!key.compare(QStringLiteral(META_KEY_LONGITUDE), Qt::CaseInsensitive) ||
|
||||||
!key.compare(QStringLiteral("Altitude"), Qt::CaseInsensitive)) {
|
!key.compare(QStringLiteral(META_KEY_ALTITUDE), Qt::CaseInsensitive)) {
|
||||||
// clang-format on
|
// clang-format on
|
||||||
auto ok = false;
|
auto ok = false;
|
||||||
auto value = QLocale::c().toFloat(text, &ok);
|
auto value = QLocale::c().toFloat(text, &ok);
|
||||||
@ -550,7 +495,7 @@ static void setMetadata(const QImage &image, Imf::Header &header)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!key.compare(QStringLiteral("CreationDate"), Qt::CaseInsensitive)) {
|
if (!key.compare(QStringLiteral(META_KEY_CREATIONDATE), Qt::CaseInsensitive)) {
|
||||||
auto dt = QDateTime::fromString(text, Qt::ISODate);
|
auto dt = QDateTime::fromString(text, Qt::ISODate);
|
||||||
if (dt.isValid()) {
|
if (dt.isValid()) {
|
||||||
dateTime = dt;
|
dateTime = dt;
|
||||||
@ -558,10 +503,30 @@ static void setMetadata(const QImage &image, Imf::Header &header)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef EXR_DISABLE_XMP_ATTRIBUTE // warning: Non-standard attribute!
|
#ifndef EXR_DISABLE_XMP_ATTRIBUTE // warning: Non-standard attribute!
|
||||||
if (!key.compare(QStringLiteral("XML:com.adobe.xmp"), Qt::CaseInsensitive)) {
|
if (!key.compare(QStringLiteral(META_KEY_XMP_ADOBE), Qt::CaseInsensitive)) {
|
||||||
header.insert("xmp", Imf::StringAttribute(text.toStdString()));
|
header.insert("xmp", Imf::StringAttribute(text.toStdString()));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!key.compare(QStringLiteral(META_KEY_MANUFACTURER), Qt::CaseInsensitive)) {
|
||||||
|
header.insert("cameraMake", Imf::StringAttribute(text.toStdString()));
|
||||||
|
}
|
||||||
|
if (!key.compare(QStringLiteral(META_KEY_MODEL), Qt::CaseInsensitive)) {
|
||||||
|
header.insert("cameraModel", Imf::StringAttribute(text.toStdString()));
|
||||||
|
}
|
||||||
|
if (!key.compare(QStringLiteral(META_KEY_SERIALNUMBER), Qt::CaseInsensitive)) {
|
||||||
|
header.insert("cameraSerialNumber", Imf::StringAttribute(text.toStdString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key.compare(QStringLiteral(META_KEY_LENS_MANUFACTURER), Qt::CaseInsensitive)) {
|
||||||
|
header.insert("lensMake", Imf::StringAttribute(text.toStdString()));
|
||||||
|
}
|
||||||
|
if (!key.compare(QStringLiteral(META_KEY_LENS_MODEL), Qt::CaseInsensitive)) {
|
||||||
|
header.insert("lensModel", Imf::StringAttribute(text.toStdString()));
|
||||||
|
}
|
||||||
|
if (!key.compare(QStringLiteral(META_KEY_LENS_SERIALNUMBER), Qt::CaseInsensitive)) {
|
||||||
|
header.insert("lensSerialNumber", Imf::StringAttribute(text.toStdString()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (dateTime.isValid()) {
|
if (dateTime.isValid()) {
|
||||||
header.insert("capDate", Imf::StringAttribute(dateTime.toString(QStringLiteral("yyyy:MM:dd HH:mm:ss")).toStdString()));
|
header.insert("capDate", Imf::StringAttribute(dateTime.toString(QStringLiteral("yyyy:MM:dd HH:mm:ss")).toStdString()));
|
||||||
@ -578,8 +543,6 @@ static void setMetadata(const QImage &image, Imf::Header &header)
|
|||||||
// If a file doesn’t have a chromaticities attribute, display software should assume that the
|
// If a file doesn’t have a chromaticities attribute, display software should assume that the
|
||||||
// file’s primaries and the white point match Rec. ITU-R BT.709-3.
|
// file’s primaries and the white point match Rec. ITU-R BT.709-3.
|
||||||
// header.insert("chromaticities", Imf::ChromaticitiesAttribute(Imf::Chromaticities()));
|
// header.insert("chromaticities", Imf::ChromaticitiesAttribute(Imf::Chromaticities()));
|
||||||
|
|
||||||
// TODO: EXR 3.2 attributes (see readMetadata())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EXRHandler::write(const QImage &image)
|
bool EXRHandler::write(const QImage &image)
|
||||||
@ -633,17 +596,12 @@ bool EXRHandler::write(const QImage &image)
|
|||||||
pixels.resizeErase(EXR_LINES_PER_BLOCK, width);
|
pixels.resizeErase(EXR_LINES_PER_BLOCK, width);
|
||||||
|
|
||||||
// convert the image and write into the stream
|
// convert the image and write into the stream
|
||||||
#if defined(EXR_USE_QT6_FLOAT_IMAGE)
|
|
||||||
auto convFormat = image.hasAlphaChannel() ? QImage::Format_RGBA16FPx4 : QImage::Format_RGBX16FPx4;
|
auto convFormat = image.hasAlphaChannel() ? QImage::Format_RGBA16FPx4 : QImage::Format_RGBX16FPx4;
|
||||||
#else
|
|
||||||
auto convFormat = image.hasAlphaChannel() ? QImage::Format_RGBA64 : QImage::Format_RGBX64;
|
|
||||||
#endif
|
|
||||||
ScanLineConverter slc(convFormat);
|
ScanLineConverter slc(convFormat);
|
||||||
slc.setDefaultSourceColorSpace(QColorSpace(QColorSpace::SRgb));
|
slc.setDefaultSourceColorSpace(QColorSpace(QColorSpace::SRgb));
|
||||||
slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgbLinear));
|
slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgbLinear));
|
||||||
for (int y = 0, n = 0; y < height; y += n) {
|
for (int y = 0, n = 0; y < height; y += n) {
|
||||||
for (n = 0; n < std::min(EXR_LINES_PER_BLOCK, height - y); ++n) {
|
for (n = 0; n < std::min(EXR_LINES_PER_BLOCK, height - y); ++n) {
|
||||||
#if defined(EXR_USE_QT6_FLOAT_IMAGE)
|
|
||||||
auto scanLine = reinterpret_cast<const qfloat16 *>(slc.convertedScanLine(image, y + n));
|
auto scanLine = reinterpret_cast<const qfloat16 *>(slc.convertedScanLine(image, y + n));
|
||||||
if (scanLine == nullptr) {
|
if (scanLine == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
@ -655,18 +613,6 @@ bool EXRHandler::write(const QImage &image)
|
|||||||
pixels[n][x].b = float(*(scanLine + xcs + 2));
|
pixels[n][x].b = float(*(scanLine + xcs + 2));
|
||||||
pixels[n][x].a = float(*(scanLine + xcs + 3));
|
pixels[n][x].a = float(*(scanLine + xcs + 3));
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
auto scanLine = reinterpret_cast<const QRgba64 *>(slc.convertedScanLine(image, y + n));
|
|
||||||
if (scanLine == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int x = 0; x < width; ++x) {
|
|
||||||
pixels[n][x].r = float((scanLine + x)->red() / 65535.f);
|
|
||||||
pixels[n][x].g = float((scanLine + x)->green() / 65535.f);
|
|
||||||
pixels[n][x].b = float((scanLine + x)->blue() / 65535.f);
|
|
||||||
pixels[n][x].a = float((scanLine + x)->alpha() / 65535.f);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
file.setFrameBuffer(&pixels[0][0] - qint64(y) * width, 1, width);
|
file.setFrameBuffer(&pixels[0][0] - qint64(y) * width, 1, width);
|
||||||
file.writePixels(n);
|
file.writePixels(n);
|
||||||
@ -699,10 +645,12 @@ void EXRHandler::setOption(ImageOption option, const QVariant &value)
|
|||||||
bool EXRHandler::supportsOption(ImageOption option) const
|
bool EXRHandler::supportsOption(ImageOption option) const
|
||||||
{
|
{
|
||||||
if (option == QImageIOHandler::Size) {
|
if (option == QImageIOHandler::Size) {
|
||||||
return true;
|
if (auto d = device())
|
||||||
|
return !d->isSequential();
|
||||||
}
|
}
|
||||||
if (option == QImageIOHandler::ImageFormat) {
|
if (option == QImageIOHandler::ImageFormat) {
|
||||||
return true;
|
if (auto d = device())
|
||||||
|
return !d->isSequential();
|
||||||
}
|
}
|
||||||
if (option == QImageIOHandler::CompressionRatio) {
|
if (option == QImageIOHandler::CompressionRatio) {
|
||||||
return true;
|
return true;
|
||||||
@ -721,6 +669,9 @@ QVariant EXRHandler::option(ImageOption option) const
|
|||||||
if (auto d = device()) {
|
if (auto d = device()) {
|
||||||
// transactions works on both random and sequential devices
|
// transactions works on both random and sequential devices
|
||||||
d->startTransaction();
|
d->startTransaction();
|
||||||
|
if (m_startPos > -1) {
|
||||||
|
d->seek(m_startPos);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
K_IStream istr(d, QByteArray());
|
K_IStream istr(d, QByteArray());
|
||||||
Imf::RgbaInputFile file(istr);
|
Imf::RgbaInputFile file(istr);
|
||||||
@ -743,6 +694,9 @@ QVariant EXRHandler::option(ImageOption option) const
|
|||||||
if (auto d = device()) {
|
if (auto d = device()) {
|
||||||
// transactions works on both random and sequential devices
|
// transactions works on both random and sequential devices
|
||||||
d->startTransaction();
|
d->startTransaction();
|
||||||
|
if (m_startPos > -1) {
|
||||||
|
d->seek(m_startPos);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
K_IStream istr(d, QByteArray());
|
K_IStream istr(d, QByteArray());
|
||||||
Imf::RgbaInputFile file(istr);
|
Imf::RgbaInputFile file(istr);
|
||||||
|
@ -27,12 +27,194 @@ typedef unsigned char uchar;
|
|||||||
|
|
||||||
Q_LOGGING_CATEGORY(HDRPLUGIN, "kf.imageformats.plugins.hdr", QtWarningMsg)
|
Q_LOGGING_CATEGORY(HDRPLUGIN, "kf.imageformats.plugins.hdr", QtWarningMsg)
|
||||||
|
|
||||||
namespace // Private.
|
|
||||||
{
|
|
||||||
#define MAXLINE 1024
|
#define MAXLINE 1024
|
||||||
#define MINELEN 8 // minimum scanline length for encoding
|
#define MINELEN 8 // minimum scanline length for encoding
|
||||||
#define MAXELEN 0x7fff // maximum scanline length for encoding
|
#define MAXELEN 0x7fff // maximum scanline length for encoding
|
||||||
|
|
||||||
|
class Header
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Header()
|
||||||
|
{
|
||||||
|
m_colorSpace = QColorSpace(QColorSpace::SRgbLinear);
|
||||||
|
m_transformation = QImageIOHandler::TransformationNone;
|
||||||
|
}
|
||||||
|
Header(const Header&) = default;
|
||||||
|
Header& operator=(const Header&) = default;
|
||||||
|
|
||||||
|
bool isValid() const { return width() > 0 && height() > 0; }
|
||||||
|
qint32 width() const { return(m_size.width()); }
|
||||||
|
qint32 height() const { return(m_size.height()); }
|
||||||
|
QString software() const { return(m_software); }
|
||||||
|
QImageIOHandler::Transformations transformation() const { return(m_transformation); }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief colorSpace
|
||||||
|
*
|
||||||
|
* The color space for the image.
|
||||||
|
*
|
||||||
|
* The CIE (x,y) chromaticity coordinates of the three (RGB)
|
||||||
|
* primaries and the white point used to standardize the picture's
|
||||||
|
* color system. This is used mainly by the ra_xyze program to
|
||||||
|
* convert between color systems. If no PRIMARIES line
|
||||||
|
* appears, we assume the standard primaries defined in
|
||||||
|
* src/common/color.h, namely "0.640 0.330 0.290
|
||||||
|
* 0.600 0.150 0.060 0.333 0.333" for red, green, blue
|
||||||
|
* and white, respectively.
|
||||||
|
*/
|
||||||
|
QColorSpace colorSpace() const { return(m_colorSpace); }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief exposure
|
||||||
|
*
|
||||||
|
* A single floating point number indicating a multiplier that has
|
||||||
|
* been applied to all the pixels in the file. EXPOSURE values are
|
||||||
|
* cumulative, so the original pixel values (i.e., radiances in
|
||||||
|
* watts/steradian/m^2) must be derived by taking the values in the
|
||||||
|
* file and dividing by all the EXPOSURE settings multiplied
|
||||||
|
* together. No EXPOSURE setting implies that no exposure
|
||||||
|
* changes have taken place.
|
||||||
|
*/
|
||||||
|
float exposure() const {
|
||||||
|
float mul = 1;
|
||||||
|
for (auto&& v : m_exposure)
|
||||||
|
mul *= v;
|
||||||
|
return mul;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImageIOHandler::Transformations m_transformation;
|
||||||
|
QColorSpace m_colorSpace;
|
||||||
|
QString m_software;
|
||||||
|
QSize m_size;
|
||||||
|
QList<float> m_exposure;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HDRHandlerPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HDRHandlerPrivate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~HDRHandlerPrivate()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const Header& header(QIODevice *device)
|
||||||
|
{
|
||||||
|
auto&& h = m_header;
|
||||||
|
if (h.isValid()) {
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
h = readHeader(device);
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Header readHeader(QIODevice *device)
|
||||||
|
{
|
||||||
|
Header h;
|
||||||
|
|
||||||
|
int len;
|
||||||
|
QByteArray line(MAXLINE + 1, Qt::Uninitialized);
|
||||||
|
QByteArray format;
|
||||||
|
|
||||||
|
// Parse header
|
||||||
|
do {
|
||||||
|
len = device->readLine(line.data(), MAXLINE);
|
||||||
|
|
||||||
|
if (line.startsWith("FORMAT=")) {
|
||||||
|
format = line.mid(7, len - 7).trimmed();
|
||||||
|
}
|
||||||
|
if (line.startsWith("SOFTWARE=")) {
|
||||||
|
h.m_software = QString::fromUtf8(line.mid(9, len - 9)).trimmed();
|
||||||
|
}
|
||||||
|
if (line.startsWith("EXPOSURE=")) {
|
||||||
|
auto ok = false;
|
||||||
|
auto ex = QLocale::c().toFloat(QString::fromLatin1(line.mid(9, len - 9)).trimmed(), &ok);
|
||||||
|
if (ok)
|
||||||
|
h.m_exposure << ex;
|
||||||
|
}
|
||||||
|
if (line.startsWith("PRIMARIES=")) {
|
||||||
|
auto list = line.mid(10, len - 10).trimmed().split(' ');
|
||||||
|
QList<double> primaries;
|
||||||
|
for (auto&& v : list) {
|
||||||
|
auto ok = false;
|
||||||
|
auto d = QLocale::c().toDouble(QString::fromLatin1(v), &ok);
|
||||||
|
if (ok)
|
||||||
|
primaries << d;
|
||||||
|
}
|
||||||
|
if (primaries.size() == 8) {
|
||||||
|
auto cs = QColorSpace(QPointF(primaries.at(6), primaries.at(7)),
|
||||||
|
QPointF(primaries.at(0), primaries.at(1)),
|
||||||
|
QPointF(primaries.at(2), primaries.at(3)),
|
||||||
|
QPointF(primaries.at(4), primaries.at(5)),
|
||||||
|
QColorSpace::TransferFunction::Linear);
|
||||||
|
cs.setDescription(QStringLiteral("Embedded RGB"));
|
||||||
|
if (cs.isValid())
|
||||||
|
h.m_colorSpace = cs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} while ((len > 0) && (line[0] != '\n'));
|
||||||
|
|
||||||
|
if (format != "32-bit_rle_rgbe") {
|
||||||
|
qCDebug(HDRPLUGIN) << "Unknown HDR format:" << format;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = device->readLine(line.data(), MAXLINE);
|
||||||
|
line.resize(len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle flipping and rotation, as per the spec below.
|
||||||
|
* The single resolution line consists of 4 values, a X and Y label each followed by a numerical
|
||||||
|
* integer value. The X and Y are immediately preceded by a sign which can be used to indicate
|
||||||
|
* flipping, the order of the X and Y indicate rotation. The standard coordinate system for
|
||||||
|
* Radiance images would have the following resolution string -Y N +X N. This indicates that the
|
||||||
|
* vertical axis runs down the file and the X axis is to the right (imagining the image as a
|
||||||
|
* rectangular block of data). A -X would indicate a horizontal flip of the image. A +Y would
|
||||||
|
* indicate a vertical flip. If the X value appears before the Y value then that indicates that
|
||||||
|
* the image is stored in column order rather than row order, that is, it is rotated by 90 degrees.
|
||||||
|
* The reader can convince themselves that the 8 combinations cover all the possible image orientations
|
||||||
|
* and rotations.
|
||||||
|
*/
|
||||||
|
QRegularExpression resolutionRegExp(QStringLiteral("([+\\-][XY])\\s+([0-9]+)\\s+([+\\-][XY])\\s+([0-9]+)\n"));
|
||||||
|
QRegularExpressionMatch match = resolutionRegExp.match(QString::fromLatin1(line));
|
||||||
|
if (!match.hasMatch()) {
|
||||||
|
qCDebug(HDRPLUGIN) << "Invalid HDR file, the first line after the header didn't have the expected format:" << line;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto c0 = match.captured(1);
|
||||||
|
auto c1 = match.captured(3);
|
||||||
|
if (c0.at(1) == u'Y') {
|
||||||
|
if (c0.at(0) == u'-' && c1.at(0) == u'+')
|
||||||
|
h.m_transformation = QImageIOHandler::TransformationNone;
|
||||||
|
if (c0.at(0) == u'-' && c1.at(0) == u'-')
|
||||||
|
h.m_transformation = QImageIOHandler::TransformationMirror;
|
||||||
|
if (c0.at(0) == u'+' && c1.at(0) == u'+')
|
||||||
|
h.m_transformation = QImageIOHandler::TransformationFlip;
|
||||||
|
if (c0.at(0) == u'+' && c1.at(0) == u'-')
|
||||||
|
h.m_transformation = QImageIOHandler::TransformationRotate180;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (c0.at(0) == u'-' && c1.at(0) == u'+')
|
||||||
|
h.m_transformation = QImageIOHandler::TransformationRotate90;
|
||||||
|
if (c0.at(0) == u'-' && c1.at(0) == u'-')
|
||||||
|
h.m_transformation = QImageIOHandler::TransformationMirrorAndRotate90;
|
||||||
|
if (c0.at(0) == u'+' && c1.at(0) == u'+')
|
||||||
|
h.m_transformation = QImageIOHandler::TransformationFlipAndRotate90;
|
||||||
|
if (c0.at(0) == u'+' && c1.at(0) == u'-')
|
||||||
|
h.m_transformation = QImageIOHandler::TransformationRotate270;
|
||||||
|
}
|
||||||
|
|
||||||
|
h.m_size = QSize(match.captured(4).toInt(), match.captured(2).toInt());
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Header m_header;
|
||||||
|
};
|
||||||
|
|
||||||
// read an old style line from the hdr image file
|
// read an old style line from the hdr image file
|
||||||
// if 'first' is true the first byte is already read
|
// if 'first' is true the first byte is already read
|
||||||
static bool Read_Old_Line(uchar *image, int width, QDataStream &s)
|
static bool Read_Old_Line(uchar *image, int width, QDataStream &s)
|
||||||
@ -76,9 +258,10 @@ static bool Read_Old_Line(uchar *image, int width, QDataStream &s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class float_T>
|
template<class float_T>
|
||||||
void RGBE_To_QRgbLine(uchar *image, float_T *scanline, int width)
|
void RGBE_To_QRgbLine(uchar *image, float_T *scanline, const Header& h)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < width; j++) {
|
auto exposure = h.exposure();
|
||||||
|
for (int j = 0, width = h.width(); j < width; j++) {
|
||||||
// v = ldexp(1.0, int(image[3]) - 128);
|
// v = ldexp(1.0, int(image[3]) - 128);
|
||||||
float v;
|
float v;
|
||||||
int e = qBound(-31, int(image[3]) - 128, 31);
|
int e = qBound(-31, int(image[3]) - 128, 31);
|
||||||
@ -90,9 +273,13 @@ void RGBE_To_QRgbLine(uchar *image, float_T *scanline, int width)
|
|||||||
|
|
||||||
auto j4 = j * 4;
|
auto j4 = j * 4;
|
||||||
auto vn = v / 255.0f;
|
auto vn = v / 255.0f;
|
||||||
scanline[j4] = float_T(std::min(float(image[0]) * vn, 1.0f));
|
if (exposure > 0) {
|
||||||
scanline[j4 + 1] = float_T(std::min(float(image[1]) * vn, 1.0f));
|
vn /= exposure;
|
||||||
scanline[j4 + 2] = float_T(std::min(float(image[2]) * vn, 1.0f));
|
}
|
||||||
|
|
||||||
|
scanline[j4] = float_T(float(image[0]) * vn);
|
||||||
|
scanline[j4 + 1] = float_T(float(image[1]) * vn);
|
||||||
|
scanline[j4 + 2] = float_T(float(image[2]) * vn);
|
||||||
scanline[j4 + 3] = float_T(1.0f);
|
scanline[j4 + 3] = float_T(1.0f);
|
||||||
image += 4;
|
image += 4;
|
||||||
}
|
}
|
||||||
@ -108,11 +295,14 @@ QImage::Format imageFormat()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load the HDR image.
|
// Load the HDR image.
|
||||||
static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &img)
|
static bool LoadHDR(QDataStream &s, const Header& h, QImage &img)
|
||||||
{
|
{
|
||||||
uchar val;
|
uchar val;
|
||||||
uchar code;
|
uchar code;
|
||||||
|
|
||||||
|
const int width = h.width();
|
||||||
|
const int height = h.height();
|
||||||
|
|
||||||
// Create dst image.
|
// Create dst image.
|
||||||
img = imageAlloc(width, height, imageFormat());
|
img = imageAlloc(width, height, imageFormat());
|
||||||
if (img.isNull()) {
|
if (img.isNull()) {
|
||||||
@ -134,7 +324,7 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
|||||||
// determine scanline type
|
// determine scanline type
|
||||||
if ((width < MINELEN) || (MAXELEN < width)) {
|
if ((width < MINELEN) || (MAXELEN < width)) {
|
||||||
Read_Old_Line(image, width, s);
|
Read_Old_Line(image, width, s);
|
||||||
RGBE_To_QRgbLine(image, scanline, width);
|
RGBE_To_QRgbLine(image, scanline, h);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +337,7 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
|||||||
if (val != 2) {
|
if (val != 2) {
|
||||||
s.device()->ungetChar(val);
|
s.device()->ungetChar(val);
|
||||||
Read_Old_Line(image, width, s);
|
Read_Old_Line(image, width, s);
|
||||||
RGBE_To_QRgbLine(image, scanline, width);
|
RGBE_To_QRgbLine(image, scanline, h);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +352,7 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
|||||||
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);
|
Read_Old_Line(image + 4, width - 1, s);
|
||||||
RGBE_To_QRgbLine(image, scanline, width);
|
RGBE_To_QRgbLine(image, scanline, h);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,84 +394,34 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RGBE_To_QRgbLine(image, scanline, h);
|
||||||
RGBE_To_QRgbLine(image, scanline, width);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QSize readHeaderSize(QIODevice *device)
|
|
||||||
{
|
|
||||||
int len;
|
|
||||||
QByteArray line(MAXLINE + 1, Qt::Uninitialized);
|
|
||||||
QByteArray format;
|
|
||||||
|
|
||||||
// Parse header
|
|
||||||
do {
|
|
||||||
len = device->readLine(line.data(), MAXLINE);
|
|
||||||
|
|
||||||
if (line.startsWith("FORMAT=")) {
|
|
||||||
format = line.mid(7, len - 7 - 1 /*\n*/);
|
|
||||||
}
|
|
||||||
|
|
||||||
} while ((len > 0) && (line[0] != '\n'));
|
|
||||||
|
|
||||||
if (format != "32-bit_rle_rgbe") {
|
|
||||||
qCDebug(HDRPLUGIN) << "Unknown HDR format:" << format;
|
|
||||||
return QSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
len = device->readLine(line.data(), MAXLINE);
|
|
||||||
line.resize(len);
|
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: handle flipping and rotation, as per the spec below
|
|
||||||
The single resolution line consists of 4 values, a X and Y label each followed by a numerical
|
|
||||||
integer value. The X and Y are immediately preceded by a sign which can be used to indicate
|
|
||||||
flipping, the order of the X and Y indicate rotation. The standard coordinate system for
|
|
||||||
Radiance images would have the following resolution string -Y N +X N. This indicates that the
|
|
||||||
vertical axis runs down the file and the X axis is to the right (imagining the image as a
|
|
||||||
rectangular block of data). A -X would indicate a horizontal flip of the image. A +Y would
|
|
||||||
indicate a vertical flip. If the X value appears before the Y value then that indicates that
|
|
||||||
the image is stored in column order rather than row order, that is, it is rotated by 90 degrees.
|
|
||||||
The reader can convince themselves that the 8 combinations cover all the possible image orientations
|
|
||||||
and rotations.
|
|
||||||
*/
|
|
||||||
QRegularExpression resolutionRegExp(QStringLiteral("([+\\-][XY]) ([0-9]+) ([+\\-][XY]) ([0-9]+)\n"));
|
|
||||||
QRegularExpressionMatch match = resolutionRegExp.match(QString::fromLatin1(line));
|
|
||||||
if (!match.hasMatch()) {
|
|
||||||
qCDebug(HDRPLUGIN) << "Invalid HDR file, the first line after the header didn't have the expected format:" << line;
|
|
||||||
return QSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((match.captured(1).at(1) != u'Y') || (match.captured(3).at(1) != u'X')) {
|
|
||||||
qCDebug(HDRPLUGIN) << "Unsupported image orientation in HDR file.";
|
|
||||||
return QSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
return QSize(match.captured(4).toInt(), match.captured(2).toInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
bool HDRHandler::read(QImage *outImage)
|
bool HDRHandler::read(QImage *outImage)
|
||||||
{
|
{
|
||||||
QDataStream s(device());
|
QDataStream s(device());
|
||||||
|
|
||||||
QSize size = readHeaderSize(s.device());
|
const Header& h = d->header(s.device());
|
||||||
if (!size.isValid()) {
|
if (!h.isValid()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage img;
|
QImage img;
|
||||||
if (!LoadHDR(s, size.width(), size.height(), img)) {
|
if (!LoadHDR(s, h, img)) {
|
||||||
// qDebug() << "Error loading HDR file.";
|
// qDebug() << "Error loading HDR file.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// The images read by Gimp and Photoshop (including those of the tests) are interpreted with linear color space.
|
|
||||||
// By setting the linear color space, programs that support profiles display HDR files as in GIMP and Photoshop.
|
// By setting the linear color space, programs that support profiles display HDR files as in GIMP and Photoshop.
|
||||||
img.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
|
img.setColorSpace(h.colorSpace());
|
||||||
|
|
||||||
|
// Metadata
|
||||||
|
if (!h.software().isEmpty()) {
|
||||||
|
img.setText(QStringLiteral(META_KEY_SOFTWARE), h.software());
|
||||||
|
}
|
||||||
|
|
||||||
*outImage = img;
|
*outImage = img;
|
||||||
return true;
|
return true;
|
||||||
@ -295,6 +435,9 @@ bool HDRHandler::supportsOption(ImageOption option) const
|
|||||||
if (option == QImageIOHandler::ImageFormat) {
|
if (option == QImageIOHandler::ImageFormat) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (option == QImageIOHandler::ImageTransformation) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,13 +446,10 @@ QVariant HDRHandler::option(ImageOption option) const
|
|||||||
QVariant v;
|
QVariant v;
|
||||||
|
|
||||||
if (option == QImageIOHandler::Size) {
|
if (option == QImageIOHandler::Size) {
|
||||||
if (auto d = device()) {
|
if (auto dev = device()) {
|
||||||
// transactions works on both random and sequential devices
|
auto&& h = d->header(dev);
|
||||||
d->startTransaction();
|
if (h.isValid()) {
|
||||||
auto size = readHeaderSize(d);
|
v = QVariant::fromValue(h.m_size);
|
||||||
d->rollbackTransaction();
|
|
||||||
if (size.isValid()) {
|
|
||||||
v = QVariant::fromValue(size);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -318,10 +458,21 @@ QVariant HDRHandler::option(ImageOption option) const
|
|||||||
v = QVariant::fromValue(imageFormat());
|
v = QVariant::fromValue(imageFormat());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (option == QImageIOHandler::ImageTransformation) {
|
||||||
|
if (auto dev = device()) {
|
||||||
|
auto&& h = d->header(dev);
|
||||||
|
if (h.isValid()) {
|
||||||
|
v = QVariant::fromValue(h.transformation());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
HDRHandler::HDRHandler()
|
HDRHandler::HDRHandler()
|
||||||
|
: QImageIOHandler()
|
||||||
|
, d(new HDRHandlerPrivate)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,9 +499,9 @@ bool HDRHandler::canRead(QIODevice *device)
|
|||||||
|
|
||||||
// allow to load offical test cases: https://radsite.lbl.gov/radiance/framed.html
|
// allow to load offical test cases: https://radsite.lbl.gov/radiance/framed.html
|
||||||
device->startTransaction();
|
device->startTransaction();
|
||||||
QSize size = readHeaderSize(device);
|
auto h = HDRHandlerPrivate::readHeader(device);
|
||||||
device->rollbackTransaction();
|
device->rollbackTransaction();
|
||||||
if (size.isValid()) {
|
if (h.isValid()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,9 @@
|
|||||||
#define KIMG_HDR_P_H
|
#define KIMG_HDR_P_H
|
||||||
|
|
||||||
#include <QImageIOPlugin>
|
#include <QImageIOPlugin>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
|
||||||
|
class HDRHandlerPrivate;
|
||||||
class HDRHandler : public QImageIOHandler
|
class HDRHandler : public QImageIOHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -22,6 +24,9 @@ public:
|
|||||||
QVariant option(QImageIOHandler::ImageOption option) const override;
|
QVariant option(QImageIOHandler::ImageOption option) const override;
|
||||||
|
|
||||||
static bool canRead(QIODevice *device);
|
static bool canRead(QIODevice *device);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QScopedPointer<HDRHandlerPrivate> d;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HDRPlugin : public QImageIOPlugin
|
class HDRPlugin : public QImageIOPlugin
|
||||||
|
@ -16,11 +16,19 @@
|
|||||||
#include <jxl/thread_parallel_runner.h>
|
#include <jxl/thread_parallel_runner.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
// Avoid rotation on buggy Qts (see also https://bugreports.qt.io/browse/QTBUG-126575)
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 7) && QT_VERSION < QT_VERSION_CHECK(6, 6, 0)) || (QT_VERSION >= QT_VERSION_CHECK(6, 7, 3))
|
||||||
|
#ifndef JXL_QT_AUTOTRANSFORM
|
||||||
|
#define JXL_QT_AUTOTRANSFORM
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
QJpegXLHandler::QJpegXLHandler()
|
QJpegXLHandler::QJpegXLHandler()
|
||||||
: m_parseState(ParseJpegXLNotParsed)
|
: m_parseState(ParseJpegXLNotParsed)
|
||||||
, m_quality(90)
|
, m_quality(90)
|
||||||
, m_currentimage_index(0)
|
, m_currentimage_index(0)
|
||||||
, m_previousimage_index(-1)
|
, m_previousimage_index(-1)
|
||||||
|
, m_transformations(QImageIOHandler::TransformationNone)
|
||||||
, m_decoder(nullptr)
|
, m_decoder(nullptr)
|
||||||
, m_runner(nullptr)
|
, m_runner(nullptr)
|
||||||
, m_next_image_delay(0)
|
, m_next_image_delay(0)
|
||||||
@ -129,6 +137,11 @@ bool QJpegXLHandler::ensureDecoder()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef JXL_QT_AUTOTRANSFORM
|
||||||
|
// Let Qt handle the orientation.
|
||||||
|
JxlDecoderSetKeepOrientation(m_decoder, true);
|
||||||
|
#endif
|
||||||
|
|
||||||
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
|
||||||
@ -568,10 +581,25 @@ bool QJpegXLHandler::write(const QImage &image)
|
|||||||
pixel_format.endianness = JXL_NATIVE_ENDIAN;
|
pixel_format.endianness = JXL_NATIVE_ENDIAN;
|
||||||
pixel_format.align = 0;
|
pixel_format.align = 0;
|
||||||
|
|
||||||
output_info.orientation = JXL_ORIENT_IDENTITY;
|
|
||||||
output_info.num_color_channels = 3;
|
output_info.num_color_channels = 3;
|
||||||
output_info.animation.tps_numerator = 10;
|
output_info.animation.tps_numerator = 10;
|
||||||
output_info.animation.tps_denominator = 1;
|
output_info.animation.tps_denominator = 1;
|
||||||
|
output_info.orientation = JXL_ORIENT_IDENTITY;
|
||||||
|
if (m_transformations == QImageIOHandler::TransformationMirror) {
|
||||||
|
output_info.orientation = JXL_ORIENT_FLIP_HORIZONTAL;
|
||||||
|
} else if (m_transformations == QImageIOHandler::TransformationRotate180) {
|
||||||
|
output_info.orientation = JXL_ORIENT_ROTATE_180;
|
||||||
|
} else if (m_transformations == QImageIOHandler::TransformationFlip) {
|
||||||
|
output_info.orientation = JXL_ORIENT_FLIP_VERTICAL;
|
||||||
|
} else if (m_transformations == QImageIOHandler::TransformationFlipAndRotate90) {
|
||||||
|
output_info.orientation = JXL_ORIENT_TRANSPOSE;
|
||||||
|
} else if (m_transformations == QImageIOHandler::TransformationRotate90) {
|
||||||
|
output_info.orientation = JXL_ORIENT_ROTATE_90_CW;
|
||||||
|
} else if (m_transformations == QImageIOHandler::TransformationMirrorAndRotate90) {
|
||||||
|
output_info.orientation = JXL_ORIENT_ANTI_TRANSPOSE;
|
||||||
|
} else if (m_transformations == QImageIOHandler::TransformationRotate270) {
|
||||||
|
output_info.orientation = JXL_ORIENT_ROTATE_90_CCW;
|
||||||
|
}
|
||||||
|
|
||||||
if (save_depth > 8) { // 16bit depth
|
if (save_depth > 8) { // 16bit depth
|
||||||
pixel_format.data_type = JXL_TYPE_UINT16;
|
pixel_format.data_type = JXL_TYPE_UINT16;
|
||||||
@ -777,14 +805,24 @@ bool QJpegXLHandler::write(const QImage &image)
|
|||||||
|
|
||||||
QVariant QJpegXLHandler::option(ImageOption option) const
|
QVariant QJpegXLHandler::option(ImageOption option) const
|
||||||
{
|
{
|
||||||
|
if (!supportsOption(option)) {
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
if (option == Quality) {
|
if (option == Quality) {
|
||||||
return m_quality;
|
return m_quality;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!supportsOption(option) || !ensureParsed()) {
|
if (!ensureParsed()) {
|
||||||
|
#ifdef JXL_QT_AUTOTRANSFORM
|
||||||
|
if (option == ImageTransformation) {
|
||||||
|
return int(m_transformations);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case Size:
|
case Size:
|
||||||
return QSize(m_basicinfo.xsize, m_basicinfo.ysize);
|
return QSize(m_basicinfo.xsize, m_basicinfo.ysize);
|
||||||
@ -794,9 +832,31 @@ QVariant QJpegXLHandler::option(ImageOption option) const
|
|||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#ifdef JXL_QT_AUTOTRANSFORM
|
||||||
|
case ImageTransformation:
|
||||||
|
if (m_basicinfo.orientation == JXL_ORIENT_IDENTITY) {
|
||||||
|
return int(QImageIOHandler::TransformationNone);
|
||||||
|
} else if (m_basicinfo.orientation == JXL_ORIENT_FLIP_HORIZONTAL) {
|
||||||
|
return int(QImageIOHandler::TransformationMirror);
|
||||||
|
} else if (m_basicinfo.orientation == JXL_ORIENT_ROTATE_180) {
|
||||||
|
return int(QImageIOHandler::TransformationRotate180);
|
||||||
|
} else if (m_basicinfo.orientation == JXL_ORIENT_FLIP_VERTICAL) {
|
||||||
|
return int(QImageIOHandler::TransformationFlip);
|
||||||
|
} else if (m_basicinfo.orientation == JXL_ORIENT_TRANSPOSE) {
|
||||||
|
return int(QImageIOHandler::TransformationFlipAndRotate90);
|
||||||
|
} else if (m_basicinfo.orientation == JXL_ORIENT_ROTATE_90_CW) {
|
||||||
|
return int(QImageIOHandler::TransformationRotate90);
|
||||||
|
} else if (m_basicinfo.orientation == JXL_ORIENT_ANTI_TRANSPOSE) {
|
||||||
|
return int(QImageIOHandler::TransformationMirrorAndRotate90);
|
||||||
|
} else if (m_basicinfo.orientation == JXL_ORIENT_ROTATE_90_CCW) {
|
||||||
|
return int(QImageIOHandler::TransformationRotate270);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QJpegXLHandler::setOption(ImageOption option, const QVariant &value)
|
void QJpegXLHandler::setOption(ImageOption option, const QVariant &value)
|
||||||
@ -810,6 +870,14 @@ void QJpegXLHandler::setOption(ImageOption option, const QVariant &value)
|
|||||||
m_quality = 90;
|
m_quality = 90;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
#ifdef JXL_QT_AUTOTRANSFORM
|
||||||
|
case ImageTransformation:
|
||||||
|
if (auto t = value.toInt()) {
|
||||||
|
if (t > 0 && t < 8)
|
||||||
|
m_transformations = QImageIOHandler::Transformations(t);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -818,7 +886,11 @@ void QJpegXLHandler::setOption(ImageOption option, const QVariant &value)
|
|||||||
|
|
||||||
bool QJpegXLHandler::supportsOption(ImageOption option) const
|
bool QJpegXLHandler::supportsOption(ImageOption option) const
|
||||||
{
|
{
|
||||||
return option == Quality || option == Size || option == Animation;
|
auto supported = option == Quality || option == Size || option == Animation;
|
||||||
|
#ifdef JXL_QT_AUTOTRANSFORM
|
||||||
|
supported = supported || option == ImageTransformation;
|
||||||
|
#endif
|
||||||
|
return supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
int QJpegXLHandler::imageCount() const
|
int QJpegXLHandler::imageCount() const
|
||||||
|
@ -64,6 +64,7 @@ private:
|
|||||||
int m_quality;
|
int m_quality;
|
||||||
int m_currentimage_index;
|
int m_currentimage_index;
|
||||||
int m_previousimage_index;
|
int m_previousimage_index;
|
||||||
|
QImageIOHandler::Transformations m_transformations;
|
||||||
|
|
||||||
QByteArray m_rawData;
|
QByteArray m_rawData;
|
||||||
|
|
||||||
|
1110
src/imageformats/jxr.cpp
Normal file
4
src/imageformats/jxr.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"Keys": [ "jxr", "wdp", "hdp" ],
|
||||||
|
"MimeTypes": [ "image/jxr", "image/vnd.ms-photo", "image/vnd.ms-photo" ]
|
||||||
|
}
|
47
src/imageformats/jxr_p.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
This file is part of the KDE project
|
||||||
|
SPDX-FileCopyrightText: 2024 Mirco Miranda <mircomir@outlook.com>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KIMG_JXR_P_H
|
||||||
|
#define KIMG_JXR_P_H
|
||||||
|
|
||||||
|
#include <QImageIOPlugin>
|
||||||
|
#include <QSharedDataPointer>
|
||||||
|
|
||||||
|
class JXRHandlerPrivate;
|
||||||
|
|
||||||
|
class JXRHandler : public QImageIOHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JXRHandler();
|
||||||
|
|
||||||
|
bool canRead() const override;
|
||||||
|
bool read(QImage *outImage) override;
|
||||||
|
bool write(const QImage &image) override;
|
||||||
|
|
||||||
|
void setOption(ImageOption option, const QVariant &value) override;
|
||||||
|
bool supportsOption(QImageIOHandler::ImageOption option) const override;
|
||||||
|
QVariant option(QImageIOHandler::ImageOption option) const override;
|
||||||
|
|
||||||
|
static bool canRead(QIODevice *device);
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable QSharedDataPointer<JXRHandlerPrivate> d;
|
||||||
|
|
||||||
|
qint32 m_quality;
|
||||||
|
};
|
||||||
|
|
||||||
|
class JXRPlugin : public QImageIOPlugin
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "jxr.json")
|
||||||
|
|
||||||
|
public:
|
||||||
|
Capabilities capabilities(QIODevice *device, const QByteArray &format) const override;
|
||||||
|
QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KIMG_JXR_P_H
|
@ -308,6 +308,11 @@ static bool readImage4(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (header.BytesPerLine < (header.width() / 8)) {
|
||||||
|
qWarning() << "PCX image has invalid BytesPerLine value";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (int y = 0; y < header.height(); ++y) {
|
for (int y = 0; y < header.height(); ++y) {
|
||||||
if (s.atEnd()) {
|
if (s.atEnd()) {
|
||||||
return false;
|
return false;
|
||||||
@ -344,6 +349,47 @@ static bool readImage4(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool readImage4v2(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||||
|
{
|
||||||
|
QByteArray buf(header.BytesPerLine, 0);
|
||||||
|
|
||||||
|
img = imageAlloc(header.width(), header.height(), QImage::Format_Indexed8);
|
||||||
|
img.setColorCount(16);
|
||||||
|
|
||||||
|
if (img.isNull()) {
|
||||||
|
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = 0; y < header.height(); ++y) {
|
||||||
|
if (s.atEnd()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!readLine(s, buf, header)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uchar *p = img.scanLine(y);
|
||||||
|
if (!p) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned int bpl = std::min(header.BytesPerLine, static_cast<quint16>(header.width() / 2));
|
||||||
|
for (unsigned int x = 0; x < bpl; ++x) {
|
||||||
|
p[x * 2] = (buf[x] & 240) >> 4;
|
||||||
|
p[x * 2 + 1] = buf[x] & 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the palette
|
||||||
|
for (int i = 0; i < 16; ++i) {
|
||||||
|
img.setColor(i, header.ColorMap.color(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (s.status() == QDataStream::Ok);
|
||||||
|
}
|
||||||
|
|
||||||
static bool readImage8(QImage &img, QDataStream &s, const PCXHEADER &header)
|
static bool readImage8(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||||
{
|
{
|
||||||
QByteArray buf(header.BytesPerLine, 0);
|
QByteArray buf(header.BytesPerLine, 0);
|
||||||
@ -418,6 +464,8 @@ static bool readImage24(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const unsigned int bpl = std::min(header.BytesPerLine, static_cast<quint16>(header.width()));
|
||||||
|
|
||||||
for (int y = 0; y < header.height(); ++y) {
|
for (int y = 0; y < header.height(); ++y) {
|
||||||
if (s.atEnd()) {
|
if (s.atEnd()) {
|
||||||
return false;
|
return false;
|
||||||
@ -434,7 +482,8 @@ static bool readImage24(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint *p = (uint *)img.scanLine(y);
|
uint *p = (uint *)img.scanLine(y);
|
||||||
for (int x = 0; x < header.width(); ++x) {
|
|
||||||
|
for (unsigned int x = 0; x < bpl; ++x) {
|
||||||
p[x] = qRgb(r_buf[x], g_buf[x], b_buf[x]);
|
p[x] = qRgb(r_buf[x], g_buf[x], b_buf[x]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -672,6 +721,8 @@ bool PCXHandler::read(QImage *outImage)
|
|||||||
ok = readImage1(img, s, header);
|
ok = readImage1(img, s, header);
|
||||||
} else if (header.Bpp == 1 && header.NPlanes == 4) {
|
} else if (header.Bpp == 1 && header.NPlanes == 4) {
|
||||||
ok = readImage4(img, s, header);
|
ok = readImage4(img, s, header);
|
||||||
|
} else if (header.Bpp == 4 && header.NPlanes == 1) {
|
||||||
|
ok = readImage4v2(img, s, header);
|
||||||
} else if (header.Bpp == 8 && header.NPlanes == 1) {
|
} else if (header.Bpp == 8 && header.NPlanes == 1) {
|
||||||
ok = readImage8(img, s, header);
|
ok = readImage8(img, s, header);
|
||||||
} else if (header.Bpp == 8 && header.NPlanes == 3) {
|
} else if (header.Bpp == 8 && header.NPlanes == 3) {
|
||||||
|