Compare commits
54 Commits
Author | SHA1 | Date | |
---|---|---|---|
106279d32e | |||
f5962442ca | |||
7cc4cb8d0c | |||
2bf3a859fc | |||
3ce3ab2364 | |||
c09b88b7c6 | |||
8cc25c39b4 | |||
43c80793ac | |||
e90bca4924 | |||
bfc73ca260 | |||
f6bb59228e | |||
ee381958b2 | |||
478e49b8a6 | |||
c3daf86079 | |||
a981cefdd2 | |||
723f72930b | |||
99bb24803a | |||
63a9de758f | |||
240e28aac5 | |||
906ecce500 | |||
b2b677b8a5 | |||
bcec942cc9 | |||
66cb8c91d0 | |||
899a2df42d | |||
47920ed63c | |||
274f30e008 | |||
4348a09733 | |||
dd4576a472 | |||
9438540735 | |||
cf78907ff4 | |||
bcb5308545 | |||
c3a91c3bc6 | |||
034b8f331b | |||
ed6a3c520d | |||
bf1c7e8508 | |||
3cb6519dcc | |||
6cbf7529ee | |||
55227815d5 | |||
64d51ed610 | |||
2ca57c9c59 | |||
f7fd14d418 | |||
c9aa1ff629 | |||
91d3bd5227 | |||
bb66367bc8 | |||
14770318a3 | |||
9b1fafe29b | |||
fa673b5df8 | |||
e96b43aef5 | |||
64f3303ef0 | |||
63056c52f9 | |||
2997f7ae8d | |||
0b4741f4b7 | |||
bc52c03981 | |||
c1c57d9a11 |
@ -2,13 +2,11 @@
|
|||||||
# SPDX-License-Identifier: CC0-1.0
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux.yml
|
- project: sysadmin/ci-utilities
|
||||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-static.yml
|
file:
|
||||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android.yml
|
- /gitlab-templates/linux.yml
|
||||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd.yml
|
- /gitlab-templates/linux-static.yml
|
||||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/linux-qt6.yml
|
- /gitlab-templates/android.yml
|
||||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/android-qt6.yml
|
- /gitlab-templates/freebsd.yml
|
||||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows.yml
|
- /gitlab-templates/windows.yml
|
||||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows-static.yml
|
- /gitlab-templates/windows-static.yml
|
||||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/freebsd-qt6.yml
|
|
||||||
- https://invent.kde.org/sysadmin/ci-utilities/raw/master/gitlab-templates/windows-qt6.yml
|
|
||||||
|
@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.16)
|
|||||||
project(KImageFormats)
|
project(KImageFormats)
|
||||||
|
|
||||||
include(FeatureSummary)
|
include(FeatureSummary)
|
||||||
find_package(ECM 5.102.0 NO_MODULE)
|
find_package(ECM 5.116.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)
|
||||||
|
|
||||||
@ -51,7 +51,10 @@ set_package_properties(OpenEXR PROPERTIES
|
|||||||
PURPOSE "Required for the QImage plugin for OpenEXR images"
|
PURPOSE "Required for the QImage plugin for OpenEXR images"
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(libavif 0.8.2 CONFIG)
|
find_package(libavif 0.8.2 CONFIG QUIET)
|
||||||
|
if(NOT libavif_FOUND)
|
||||||
|
find_package(libavif 1 CONFIG)
|
||||||
|
endif()
|
||||||
set_package_properties(libavif PROPERTIES
|
set_package_properties(libavif PROPERTIES
|
||||||
TYPE OPTIONAL
|
TYPE OPTIONAL
|
||||||
PURPOSE "Required for the QImage plugin for AVIF images"
|
PURPOSE "Required for the QImage plugin for AVIF images"
|
||||||
@ -65,8 +68,8 @@ add_feature_info(LibHeif LibHeif_FOUND "required for the QImage plugin for HEIF/
|
|||||||
|
|
||||||
option(KIMAGEFORMATS_JXL "Enable plugin for JPEG XL format" ON)
|
option(KIMAGEFORMATS_JXL "Enable plugin for JPEG XL format" ON)
|
||||||
if(KIMAGEFORMATS_JXL)
|
if(KIMAGEFORMATS_JXL)
|
||||||
pkg_check_modules(LibJXL IMPORTED_TARGET libjxl>=0.6.1)
|
pkg_check_modules(LibJXL IMPORTED_TARGET libjxl>=0.7.0)
|
||||||
pkg_check_modules(LibJXLThreads IMPORTED_TARGET libjxl_threads>=0.6.1)
|
pkg_check_modules(LibJXLThreads IMPORTED_TARGET libjxl_threads>=0.7.0)
|
||||||
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")
|
||||||
|
|
||||||
|
@ -14,11 +14,12 @@ image formats.
|
|||||||
The following image formats have read-only support:
|
The following image formats have read-only support:
|
||||||
|
|
||||||
- Animated Windows cursors (ani)
|
- Animated Windows cursors (ani)
|
||||||
|
- Camera RAW images (arw, cr2, cr3, dcs, dng, ...)
|
||||||
- Gimp (xcf)
|
- Gimp (xcf)
|
||||||
- OpenEXR (exr)
|
- OpenEXR (exr)
|
||||||
- Photoshop documents (psd, psb, pdd, psdt)
|
- Photoshop documents (psd, psb, pdd, psdt)
|
||||||
|
- Radiance HDR (hdr)
|
||||||
- Sun Raster (ras)
|
- Sun Raster (ras)
|
||||||
- Camera RAW images (arw, cr2, cr3, dcs, dng, ...)
|
|
||||||
|
|
||||||
The following image formats have read and write support:
|
The following image formats have read and write support:
|
||||||
|
|
||||||
@ -26,6 +27,7 @@ The following image formats have read and write support:
|
|||||||
- Encapsulated PostScript (eps)
|
- Encapsulated PostScript (eps)
|
||||||
- JPEG XL (jxl)
|
- JPEG XL (jxl)
|
||||||
- Personal Computer Exchange (pcx)
|
- Personal Computer Exchange (pcx)
|
||||||
|
- Quite OK Image format (qoi)
|
||||||
- SGI images (rgb, rgba, sgi, bw)
|
- SGI images (rgb, rgba, sgi, bw)
|
||||||
- Softimage PIC (pic)
|
- Softimage PIC (pic)
|
||||||
- Targa (tga): supports more formats than Qt's version
|
- Targa (tga): supports more formats than Qt's version
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#find_package(Qt5Test ${REQUIRED_QT_VERSION} NO_MODULE)
|
#find_package(Qt5Test ${REQUIRED_QT_VERSION} NO_MODULE)
|
||||||
|
|
||||||
include(ECMMarkAsTest)
|
include(ECMMarkAsTest)
|
||||||
include(CMakeParseArguments)
|
|
||||||
|
|
||||||
add_definitions(-DPLUGIN_DIR="${CMAKE_CURRENT_BINARY_DIR}/../bin")
|
add_definitions(-DPLUGIN_DIR="${CMAKE_CURRENT_BINARY_DIR}/../bin")
|
||||||
remove_definitions(-DQT_NO_CAST_FROM_ASCII)
|
remove_definitions(-DQT_NO_CAST_FROM_ASCII)
|
||||||
@ -70,6 +69,7 @@ kimageformats_read_tests(
|
|||||||
hdr
|
hdr
|
||||||
pcx
|
pcx
|
||||||
psd
|
psd
|
||||||
|
qoi
|
||||||
ras
|
ras
|
||||||
rgb
|
rgb
|
||||||
tga
|
tga
|
||||||
@ -99,6 +99,12 @@ if (LibHeif_FOUND)
|
|||||||
kimageformats_write_tests(FUZZ 1
|
kimageformats_write_tests(FUZZ 1
|
||||||
heif-nodatacheck-lossless
|
heif-nodatacheck-lossless
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.17.0")
|
||||||
|
kimageformats_read_tests(FUZZ 1
|
||||||
|
hej2
|
||||||
|
)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (LibJXL_FOUND AND LibJXLThreads_FOUND)
|
if (LibJXL_FOUND AND LibJXLThreads_FOUND)
|
||||||
@ -125,6 +131,7 @@ kimageformats_read_tests(FUZZ 1
|
|||||||
kimageformats_write_tests(
|
kimageformats_write_tests(
|
||||||
pcx-lossless
|
pcx-lossless
|
||||||
pic-lossless
|
pic-lossless
|
||||||
|
qoi-lossless
|
||||||
rgb-lossless
|
rgb-lossless
|
||||||
tga # fixme: the alpha images appear not to be written properly
|
tga # fixme: the alpha images appear not to be written properly
|
||||||
)
|
)
|
||||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 2.7 KiB |
BIN
autotests/read/hej2/rgb_kcolorchooser.hej2
Normal file
BIN
autotests/read/hej2/rgb_kcolorchooser.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
autotests/read/hej2/rgba_kolourpaint.hej2
Normal file
BIN
autotests/read/hej2/rgba_kolourpaint.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
autotests/read/pcx/ccbug_463951.pcx
Normal file
BIN
autotests/read/pcx/ccbug_463951.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
autotests/read/pcx/mono.pcx
Normal file
BIN
autotests/read/pcx/mono.png
Normal file
After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 983 B After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 94 KiB |
BIN
autotests/read/psd/birthday.tif
Normal file
Before Width: | Height: | Size: 189 KiB After Width: | Height: | Size: 189 KiB |
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 114 KiB |
BIN
autotests/read/psd/mch3-16bits.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
autotests/read/psd/mch3-16bits.psb
Normal file
BIN
autotests/read/psd/mch3-8bits.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
autotests/read/psd/mch3-8bits.psb
Normal file
BIN
autotests/read/qoi/1px.png
Normal file
After Width: | Height: | Size: 551 B |
BIN
autotests/read/qoi/1px.qoi
Normal file
BIN
autotests/read/qoi/2px.png
Normal file
After Width: | Height: | Size: 561 B |
BIN
autotests/read/qoi/2px.qoi
Normal file
BIN
autotests/read/qoi/bnm_rgb.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
autotests/read/qoi/bnm_rgb.qoi
Normal file
BIN
autotests/read/qoi/bnm_rgba.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
autotests/read/qoi/bnm_rgba.qoi
Normal file
BIN
autotests/read/qoi/testcard.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
autotests/read/qoi/testcard.qoi
Normal file
BIN
autotests/read/qoi/testcard_rgba.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
autotests/read/qoi/testcard_rgba.qoi
Normal file
BIN
autotests/read/xcf/birthday.png
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
autotests/read/xcf/birthday.xcf
Normal file
BIN
autotests/read/xcf/birthday16.png
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
autotests/read/xcf/birthday16.xcf
Normal file
BIN
autotests/read/xcf/birthday16_gray.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
autotests/read/xcf/birthday16_gray.xcf
Normal file
BIN
autotests/read/xcf/birthday16_grayA.png
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
autotests/read/xcf/birthday16_grayA.xcf
Normal file
BIN
autotests/read/xcf/birthday16fp.png
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
autotests/read/xcf/birthday16fp.xcf
Normal file
BIN
autotests/read/xcf/birthday16fp_gray.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
autotests/read/xcf/birthday16fp_gray.xcf
Normal file
BIN
autotests/read/xcf/birthday16fp_grayA.png
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
autotests/read/xcf/birthday16fp_grayA.xcf
Normal file
BIN
autotests/read/xcf/birthday32.png
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
autotests/read/xcf/birthday32.xcf
Normal file
BIN
autotests/read/xcf/birthday32_gray.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
autotests/read/xcf/birthday32_gray.xcf
Normal file
BIN
autotests/read/xcf/birthday32_grayA.png
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
autotests/read/xcf/birthday32_grayA.xcf
Normal file
BIN
autotests/read/xcf/birthday32fp.png
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
autotests/read/xcf/birthday32fp.xcf
Normal file
BIN
autotests/read/xcf/birthday32fp_gray.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
autotests/read/xcf/birthday32fp_gray.xcf
Normal file
BIN
autotests/read/xcf/birthday32fp_grayA.png
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
autotests/read/xcf/birthday32fp_grayA.xcf
Normal file
BIN
autotests/read/xcf/birthday_grayA.png
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
autotests/read/xcf/birthday_grayA.xcf
Normal file
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 115 KiB |
BIN
autotests/read/xcf/fruktpilot16_icc.png
Normal file
After Width: | Height: | Size: 115 KiB |
BIN
autotests/read/xcf/fruktpilot16_icc.xcf
Normal file
BIN
autotests/read/xcf/fruktpilot16fplin_icc.png
Normal file
After Width: | Height: | Size: 112 KiB |
BIN
autotests/read/xcf/fruktpilot16fplin_icc.xcf
Normal file
BIN
autotests/read/xcf/fruktpilot32_icc.png
Normal file
After Width: | Height: | Size: 115 KiB |
BIN
autotests/read/xcf/fruktpilot32_icc.xcf
Normal file
BIN
autotests/read/xcf/fruktpilot32fplin_icc.png
Normal file
After Width: | Height: | Size: 112 KiB |
BIN
autotests/read/xcf/fruktpilot32fplin_icc.xcf
Normal file
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
@ -159,17 +159,22 @@ 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)) {
|
if (!fi.suffix().compare("png", Qt::CaseInsensitive) || !fi.suffix().compare("tif", Qt::CaseInsensitive)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int suffixPos = fi.filePath().count() - suffix.count();
|
int suffixPos = fi.filePath().count() - suffix.count();
|
||||||
QString inputfile = fi.filePath();
|
QString inputfile = fi.filePath();
|
||||||
QString expfile = fi.filePath().replace(suffixPos, suffix.count(), QStringLiteral("png"));
|
QString fmt = QStringLiteral("png");
|
||||||
|
QString expfile = fi.filePath().replace(suffixPos, suffix.count(), fmt);
|
||||||
|
if (!QFile::exists(expfile)) { // try with tiff
|
||||||
|
fmt = QStringLiteral("tif");
|
||||||
|
expfile = fi.filePath().replace(suffixPos, suffix.count(), fmt);
|
||||||
|
}
|
||||||
QString expfilename = QFileInfo(expfile).fileName();
|
QString expfilename = QFileInfo(expfile).fileName();
|
||||||
|
|
||||||
std::unique_ptr<QIODevice> inputDevice(seq ? new SequentialFile(inputfile) : new QFile(inputfile));
|
std::unique_ptr<QIODevice> inputDevice(seq ? new SequentialFile(inputfile) : new QFile(inputfile));
|
||||||
QImageReader inputReader(inputDevice.get(), format);
|
QImageReader inputReader(inputDevice.get(), format);
|
||||||
QImageReader expReader(expfile, "png");
|
QImageReader expReader(expfile, fmt.toLatin1());
|
||||||
|
|
||||||
QImage inputImage;
|
QImage inputImage;
|
||||||
QImage expImage;
|
QImage expImage;
|
||||||
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
BIN
autotests/write/rgb.qoi
Normal file
BIN
autotests/write/rgba.qoi
Normal file
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
@ -12,3 +12,4 @@ Ignacio Castaño <castano@ludicon.com> -- DDS and PDS format reader.
|
|||||||
Christoph Hormann <chris_hormann@gmx.de> -- HDR format read support.
|
Christoph Hormann <chris_hormann@gmx.de> -- HDR format read support.
|
||||||
Michael Ritzert <kde@ritzert.de> -- JPEG 2000 format read/write support
|
Michael Ritzert <kde@ritzert.de> -- JPEG 2000 format read/write support
|
||||||
Troy Unrau <troy@kde.org> -- Sun RASter read support
|
Troy Unrau <troy@kde.org> -- Sun RASter read support
|
||||||
|
Ernest Gupik <ernestgupik@wp.pl> -- QOI format read support
|
||||||
|
@ -104,9 +104,6 @@ endif()
|
|||||||
if (LibJXL_FOUND AND LibJXLThreads_FOUND)
|
if (LibJXL_FOUND AND LibJXLThreads_FOUND)
|
||||||
kimageformats_add_plugin(kimg_jxl SOURCES jxl.cpp)
|
kimageformats_add_plugin(kimg_jxl SOURCES jxl.cpp)
|
||||||
target_link_libraries(kimg_jxl PkgConfig::LibJXL PkgConfig::LibJXLThreads)
|
target_link_libraries(kimg_jxl PkgConfig::LibJXL PkgConfig::LibJXLThreads)
|
||||||
if (LibJXL_VERSION VERSION_GREATER_EQUAL "0.7.0")
|
|
||||||
target_compile_definitions(kimg_jxl PRIVATE KIMG_JXL_API_VERSION=70)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (QT_MAJOR_VERSION STREQUAL "5")
|
if (QT_MAJOR_VERSION STREQUAL "5")
|
||||||
install(FILES jxl.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
|
install(FILES jxl.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
|
||||||
@ -136,6 +133,13 @@ endif()
|
|||||||
|
|
||||||
##################################
|
##################################
|
||||||
|
|
||||||
|
kimageformats_add_plugin(kimg_qoi SOURCES qoi.cpp scanlineconverter.cpp)
|
||||||
|
if (QT_MAJOR_VERSION STREQUAL "5")
|
||||||
|
install(FILES qoi.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
##################################
|
||||||
|
|
||||||
kimageformats_add_plugin(kimg_ras SOURCES ras.cpp)
|
kimageformats_add_plugin(kimg_ras SOURCES ras.cpp)
|
||||||
if (QT_MAJOR_VERSION STREQUAL "5")
|
if (QT_MAJOR_VERSION STREQUAL "5")
|
||||||
install(FILES ras.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
|
install(FILES ras.desktop DESTINATION ${KDE_INSTALL_KSERVICESDIR}/qimageioplugins/)
|
||||||
|
@ -570,3 +570,5 @@ QImageIOHandler *ANIPlugin::create(QIODevice *device, const QByteArray &format)
|
|||||||
handler->setFormat(format);
|
handler->setFormat(format);
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "moc_ani_p.cpp"
|
||||||
|
@ -16,9 +16,34 @@
|
|||||||
|
|
||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Quality range - compression/subsampling
|
||||||
|
100 - lossless RGB compression
|
||||||
|
< KIMG_AVIF_QUALITY_BEST, 100 ) - YUV444 color subsampling
|
||||||
|
< KIMG_AVIF_QUALITY_HIGH, KIMG_AVIF_QUALITY_BEST ) - YUV422 color subsampling
|
||||||
|
< 0, KIMG_AVIF_QUALITY_HIGH ) - YUV420 color subsampling
|
||||||
|
< 0, KIMG_AVIF_QUALITY_LOW ) - lossy compression of alpha channel
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KIMG_AVIF_DEFAULT_QUALITY
|
||||||
|
#define KIMG_AVIF_DEFAULT_QUALITY 68
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef KIMG_AVIF_QUALITY_BEST
|
||||||
|
#define KIMG_AVIF_QUALITY_BEST 90
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef KIMG_AVIF_QUALITY_HIGH
|
||||||
|
#define KIMG_AVIF_QUALITY_HIGH 80
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef KIMG_AVIF_QUALITY_LOW
|
||||||
|
#define KIMG_AVIF_QUALITY_LOW 51
|
||||||
|
#endif
|
||||||
|
|
||||||
QAVIFHandler::QAVIFHandler()
|
QAVIFHandler::QAVIFHandler()
|
||||||
: m_parseState(ParseAvifNotParsed)
|
: m_parseState(ParseAvifNotParsed)
|
||||||
, m_quality(52)
|
, m_quality(KIMG_AVIF_DEFAULT_QUALITY)
|
||||||
, m_container_width(0)
|
, m_container_width(0)
|
||||||
, m_container_height(0)
|
, m_container_height(0)
|
||||||
, m_rawAvifData(AVIF_DATA_EMPTY)
|
, m_rawAvifData(AVIF_DATA_EMPTY)
|
||||||
@ -330,6 +355,10 @@ bool QAVIFHandler::decode_one_frame()
|
|||||||
avifRGBImage rgb;
|
avifRGBImage rgb;
|
||||||
avifRGBImageSetDefaults(&rgb, m_decoder->image);
|
avifRGBImageSetDefaults(&rgb, m_decoder->image);
|
||||||
|
|
||||||
|
#if AVIF_VERSION >= 1000000
|
||||||
|
rgb.maxThreads = m_decoder->maxThreads;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (m_decoder->image->depth > 8) {
|
if (m_decoder->image->depth > 8) {
|
||||||
rgb.depth = 16;
|
rgb.depth = 16;
|
||||||
rgb.format = AVIF_RGB_FORMAT_RGBA;
|
rgb.format = AVIF_RGB_FORMAT_RGBA;
|
||||||
@ -424,7 +453,7 @@ bool QAVIFHandler::decode_one_frame()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_decoder->image->transformFlags & AVIF_TRANSFORM_IMIR) {
|
if (m_decoder->image->transformFlags & AVIF_TRANSFORM_IMIR) {
|
||||||
#if AVIF_VERSION > 90100
|
#if AVIF_VERSION > 90100 && AVIF_VERSION < 1000000
|
||||||
switch (m_decoder->image->imir.mode) {
|
switch (m_decoder->image->imir.mode) {
|
||||||
#else
|
#else
|
||||||
switch (m_decoder->image->imir.axis) {
|
switch (m_decoder->image->imir.axis) {
|
||||||
@ -515,9 +544,17 @@ bool QAVIFHandler::write(const QImage &image)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_quality > 100) {
|
||||||
|
m_quality = 100;
|
||||||
|
} else if (m_quality < 0) {
|
||||||
|
m_quality = KIMG_AVIF_DEFAULT_QUALITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if AVIF_VERSION < 1000000
|
||||||
int maxQuantizer = AVIF_QUANTIZER_WORST_QUALITY * (100 - qBound(0, m_quality, 100)) / 100;
|
int maxQuantizer = AVIF_QUANTIZER_WORST_QUALITY * (100 - qBound(0, m_quality, 100)) / 100;
|
||||||
int minQuantizer = 0;
|
int minQuantizer = 0;
|
||||||
int maxQuantizerAlpha = 0;
|
int maxQuantizerAlpha = 0;
|
||||||
|
#endif
|
||||||
avifResult res;
|
avifResult res;
|
||||||
|
|
||||||
bool save_grayscale; // true - monochrome, false - colors
|
bool save_grayscale; // true - monochrome, false - colors
|
||||||
@ -563,13 +600,15 @@ bool QAVIFHandler::write(const QImage &image)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// quality settings
|
#if AVIF_VERSION < 1000000
|
||||||
|
// deprecated quality settings
|
||||||
if (maxQuantizer > 20) {
|
if (maxQuantizer > 20) {
|
||||||
minQuantizer = maxQuantizer - 20;
|
minQuantizer = maxQuantizer - 20;
|
||||||
if (maxQuantizer > 40) { // we decrease quality of alpha channel here
|
if (maxQuantizer > 40) { // we decrease quality of alpha channel here
|
||||||
maxQuantizerAlpha = maxQuantizer - 40;
|
maxQuantizerAlpha = maxQuantizer - 40;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (save_grayscale && !image.hasAlphaChannel()) { // we are going to save grayscale image without alpha channel
|
if (save_grayscale && !image.hasAlphaChannel()) { // we are going to save grayscale image without alpha channel
|
||||||
if (save_depth > 8) {
|
if (save_depth > 8) {
|
||||||
@ -642,8 +681,8 @@ bool QAVIFHandler::write(const QImage &image)
|
|||||||
QImage tmpcolorimage = image.convertToFormat(tmpformat);
|
QImage tmpcolorimage = image.convertToFormat(tmpformat);
|
||||||
|
|
||||||
avifPixelFormat pixel_format = AVIF_PIXEL_FORMAT_YUV420;
|
avifPixelFormat pixel_format = AVIF_PIXEL_FORMAT_YUV420;
|
||||||
if (maxQuantizer < 20) {
|
if (m_quality >= KIMG_AVIF_QUALITY_HIGH) {
|
||||||
if (maxQuantizer < 10) {
|
if (m_quality >= KIMG_AVIF_QUALITY_BEST) {
|
||||||
pixel_format = AVIF_PIXEL_FORMAT_YUV444; // best quality
|
pixel_format = AVIF_PIXEL_FORMAT_YUV444; // best quality
|
||||||
} else {
|
} else {
|
||||||
pixel_format = AVIF_PIXEL_FORMAT_YUV422; // high quality
|
pixel_format = AVIF_PIXEL_FORMAT_YUV422; // high quality
|
||||||
@ -714,9 +753,9 @@ bool QAVIFHandler::write(const QImage &image)
|
|||||||
if (save_depth == 8) {
|
if (save_depth == 8) {
|
||||||
save_depth = 10;
|
save_depth = 10;
|
||||||
if (tmpcolorimage.hasAlphaChannel()) {
|
if (tmpcolorimage.hasAlphaChannel()) {
|
||||||
tmpcolorimage = tmpcolorimage.convertToFormat(QImage::Format_RGBA64);
|
tmpcolorimage.convertTo(QImage::Format_RGBA64);
|
||||||
} else {
|
} else {
|
||||||
tmpcolorimage = tmpcolorimage.convertToFormat(QImage::Format_RGBX64);
|
tmpcolorimage.convertTo(QImage::Format_RGBX64);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -803,6 +842,8 @@ bool QAVIFHandler::write(const QImage &image)
|
|||||||
avifRWData raw = AVIF_DATA_EMPTY;
|
avifRWData raw = AVIF_DATA_EMPTY;
|
||||||
avifEncoder *encoder = avifEncoderCreate();
|
avifEncoder *encoder = avifEncoderCreate();
|
||||||
encoder->maxThreads = qBound(1, QThread::idealThreadCount(), 64);
|
encoder->maxThreads = qBound(1, QThread::idealThreadCount(), 64);
|
||||||
|
|
||||||
|
#if AVIF_VERSION < 1000000
|
||||||
encoder->minQuantizer = minQuantizer;
|
encoder->minQuantizer = minQuantizer;
|
||||||
encoder->maxQuantizer = maxQuantizer;
|
encoder->maxQuantizer = maxQuantizer;
|
||||||
|
|
||||||
@ -810,6 +851,17 @@ bool QAVIFHandler::write(const QImage &image)
|
|||||||
encoder->minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS;
|
encoder->minQuantizerAlpha = AVIF_QUANTIZER_LOSSLESS;
|
||||||
encoder->maxQuantizerAlpha = maxQuantizerAlpha;
|
encoder->maxQuantizerAlpha = maxQuantizerAlpha;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
encoder->quality = m_quality;
|
||||||
|
|
||||||
|
if (image.hasAlphaChannel()) {
|
||||||
|
if (m_quality >= KIMG_AVIF_QUALITY_LOW) {
|
||||||
|
encoder->qualityAlpha = 100;
|
||||||
|
} else {
|
||||||
|
encoder->qualityAlpha = 100 - (KIMG_AVIF_QUALITY_LOW - m_quality) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
encoder->speed = 6;
|
encoder->speed = 6;
|
||||||
|
|
||||||
@ -866,7 +918,7 @@ void QAVIFHandler::setOption(ImageOption option, const QVariant &value)
|
|||||||
if (m_quality > 100) {
|
if (m_quality > 100) {
|
||||||
m_quality = 100;
|
m_quality = 100;
|
||||||
} else if (m_quality < 0) {
|
} else if (m_quality < 0) {
|
||||||
m_quality = 52;
|
m_quality = KIMG_AVIF_DEFAULT_QUALITY;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
@ -1039,6 +1091,11 @@ int QAVIFHandler::loopCount() const
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if AVIF_VERSION >= 1000000
|
||||||
|
if (m_decoder->repetitionCount >= 0) {
|
||||||
|
return m_decoder->repetitionCount;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
// Endless loop to work around https://github.com/AOMediaCodec/libavif/issues/347
|
// Endless loop to work around https://github.com/AOMediaCodec/libavif/issues/347
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -1103,3 +1160,5 @@ QImageIOHandler *QAVIFPlugin::create(QIODevice *device, const QByteArray &format
|
|||||||
handler->setFormat(format);
|
handler->setFormat(format);
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "moc_avif_p.cpp"
|
||||||
|
@ -365,3 +365,5 @@ QImageIOHandler *EPSPlugin::create(QIODevice *device, const QByteArray &format)
|
|||||||
handler->setFormat(format);
|
handler->setFormat(format);
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "moc_eps_p.cpp"
|
||||||
|
@ -3,10 +3,27 @@
|
|||||||
in the high dynamic range EXR format.
|
in the high dynamic range EXR format.
|
||||||
|
|
||||||
SPDX-FileCopyrightText: 2003 Brad Hards <bradh@frogmouth.net>
|
SPDX-FileCopyrightText: 2003 Brad Hards <bradh@frogmouth.net>
|
||||||
|
SPDX-FileCopyrightText: 2023 Mirco Miranda <mircomir@outlook.com>
|
||||||
|
|
||||||
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_ALLOW_LINEAR_COLORSPACE ***
|
||||||
|
* If defined, the linear data is kept and it is the display program that
|
||||||
|
* must convert to the monitor profile. Otherwise the data is converted to sRGB
|
||||||
|
* to accommodate programs that do not support color profiles.
|
||||||
|
* NOTE: If EXR_USE_LEGACY_CONVERSIONS is active, this is ignored.
|
||||||
|
*/
|
||||||
|
//#define EXR_ALLOW_LINEAR_COLORSPACE // default: commented -> you should define it in your cmake file
|
||||||
|
|
||||||
#include "exr_p.h"
|
#include "exr_p.h"
|
||||||
#include "util_p.h"
|
#include "util_p.h"
|
||||||
|
|
||||||
@ -30,11 +47,24 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <QColorSpace>
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QFloat16>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QImageIOPlugin>
|
#include <QImageIOPlugin>
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
|
||||||
|
#include <QTimeZone>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 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 (QT_VERSION_MAJOR >= 6) && !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:
|
||||||
@ -94,77 +124,21 @@ void K_IStream::clear()
|
|||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this does a conversion from the ILM Half (equal to Nvidia Half)
|
#ifdef EXR_USE_LEGACY_CONVERSIONS
|
||||||
* format into the normal 32 bit pixel format. Process is from the
|
// source: https://openexr.com/en/latest/ReadingAndWritingImageFiles.html
|
||||||
* ILM code.
|
inline unsigned char gamma(float x)
|
||||||
*/
|
|
||||||
QRgb RgbaToQrgba(struct Imf::Rgba &imagePixel)
|
|
||||||
{
|
{
|
||||||
float r;
|
x = std::pow(5.5555f * std::max(0.f, x), 0.4545f) * 84.66f;
|
||||||
float g;
|
return (unsigned char)qBound(0.f, x, 255.f);
|
||||||
float b;
|
|
||||||
float a;
|
|
||||||
|
|
||||||
// 1) Compensate for fogging by subtracting defog
|
|
||||||
// from the raw pixel values.
|
|
||||||
// Response: We work with defog of 0.0, so this is a no-op
|
|
||||||
|
|
||||||
// 2) Multiply the defogged pixel values by
|
|
||||||
// 2^(exposure + 2.47393).
|
|
||||||
// Response: We work with exposure of 0.0.
|
|
||||||
// (2^2.47393) is 5.55555
|
|
||||||
r = imagePixel.r * 5.55555;
|
|
||||||
g = imagePixel.g * 5.55555;
|
|
||||||
b = imagePixel.b * 5.55555;
|
|
||||||
a = imagePixel.a * 5.55555;
|
|
||||||
|
|
||||||
// 3) Values, which are now 1.0, are called "middle gray".
|
|
||||||
// If defog and exposure are both set to 0.0, then
|
|
||||||
// middle gray corresponds to a raw pixel value of 0.18.
|
|
||||||
// In step 6, middle gray values will be mapped to an
|
|
||||||
// intensity 3.5 f-stops below the display's maximum
|
|
||||||
// intensity.
|
|
||||||
// Response: no apparent content.
|
|
||||||
|
|
||||||
// 4) Apply a knee function. The knee function has two
|
|
||||||
// parameters, kneeLow and kneeHigh. Pixel values
|
|
||||||
// below 2^kneeLow are not changed by the knee
|
|
||||||
// function. Pixel values above kneeLow are lowered
|
|
||||||
// according to a logarithmic curve, such that the
|
|
||||||
// value 2^kneeHigh is mapped to 2^3.5 (in step 6,
|
|
||||||
// this value will be mapped to the display's
|
|
||||||
// maximum intensity).
|
|
||||||
// Response: kneeLow = 0.0 (2^0.0 => 1); kneeHigh = 5.0 (2^5 =>32)
|
|
||||||
if (r > 1.0) {
|
|
||||||
r = 1.0 + std::log((r - 1.0) * 0.184874 + 1) / 0.184874;
|
|
||||||
}
|
|
||||||
if (g > 1.0) {
|
|
||||||
g = 1.0 + std::log((g - 1.0) * 0.184874 + 1) / 0.184874;
|
|
||||||
}
|
|
||||||
if (b > 1.0) {
|
|
||||||
b = 1.0 + std::log((b - 1.0) * 0.184874 + 1) / 0.184874;
|
|
||||||
}
|
|
||||||
if (a > 1.0) {
|
|
||||||
a = 1.0 + std::log((a - 1.0) * 0.184874 + 1) / 0.184874;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// 5) Gamma-correct the pixel values, assuming that the
|
|
||||||
// screen's gamma is 0.4545 (or 1/2.2).
|
|
||||||
r = std::pow(r, 0.4545);
|
|
||||||
g = std::pow(g, 0.4545);
|
|
||||||
b = std::pow(b, 0.4545);
|
|
||||||
a = std::pow(a, 0.4545);
|
|
||||||
|
|
||||||
// 6) Scale the values such that pixels middle gray
|
|
||||||
// pixels are mapped to 84.66 (or 3.5 f-stops below
|
|
||||||
// the display's maximum intensity).
|
|
||||||
//
|
|
||||||
// 7) Clamp the values to [0, 255].
|
|
||||||
return qRgba((unsigned char)(Imath::clamp(r * 84.66f, 0.f, 255.f)),
|
|
||||||
(unsigned char)(Imath::clamp(g * 84.66f, 0.f, 255.f)),
|
|
||||||
(unsigned char)(Imath::clamp(b * 84.66f, 0.f, 255.f)),
|
|
||||||
(unsigned char)(Imath::clamp(a * 84.66f, 0.f, 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()
|
||||||
{
|
{
|
||||||
@ -188,29 +162,98 @@ bool EXRHandler::read(QImage *outImage)
|
|||||||
K_IStream istr(device(), QByteArray());
|
K_IStream istr(device(), QByteArray());
|
||||||
Imf::RgbaInputFile file(istr);
|
Imf::RgbaInputFile file(istr);
|
||||||
Imath::Box2i dw = file.dataWindow();
|
Imath::Box2i dw = file.dataWindow();
|
||||||
|
bool isRgba = file.channels() & Imf::RgbaChannels::WRITE_A;
|
||||||
|
|
||||||
width = dw.max.x - dw.min.x + 1;
|
width = dw.max.x - dw.min.x + 1;
|
||||||
height = dw.max.y - dw.min.y + 1;
|
height = dw.max.y - dw.min.y + 1;
|
||||||
|
|
||||||
QImage image = imageAlloc(width, height, QImage::Format_RGB32);
|
#if defined(EXR_USE_LEGACY_CONVERSIONS)
|
||||||
|
QImage image = imageAlloc(width, height, isRgba ? QImage::Format_ARGB32 : QImage::Format_RGB32);
|
||||||
|
#elif defined(EXR_USE_QT6_FLOAT_IMAGE)
|
||||||
|
QImage image = imageAlloc(width, height, isRgba ? QImage::Format_RGBA16FPx4 : QImage::Format_RGBX16FPx4);
|
||||||
|
#else
|
||||||
|
QImage image = imageAlloc(width, height, isRgba ? QImage::Format_RGBA64 : QImage::Format_RGBX64);
|
||||||
|
#endif
|
||||||
if (image.isNull()) {
|
if (image.isNull()) {
|
||||||
qWarning() << "Failed to allocate image, invalid size?" << QSize(width, height);
|
qWarning() << "Failed to allocate image, invalid size?" << QSize(width, height);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Imf::Array2D<Imf::Rgba> pixels;
|
// set some useful metadata
|
||||||
pixels.resizeErase(height, width);
|
auto &&h = file.header();
|
||||||
|
if (auto comments = h.findTypedAttribute<Imf::StringAttribute>("comments")) {
|
||||||
file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y * width, 1, width);
|
image.setText(QStringLiteral("Comment"), QString::fromStdString(comments->value()));
|
||||||
file.readPixels(dw.min.y, dw.max.y);
|
}
|
||||||
|
if (auto owner = h.findTypedAttribute<Imf::StringAttribute>("owner")) {
|
||||||
// somehow copy pixels into image
|
image.setText(QStringLiteral("Owner"), QString::fromStdString(owner->value()));
|
||||||
for (int y = 0; y < height; y++) {
|
}
|
||||||
for (int x = 0; x < width; x++) {
|
if (auto capDate = h.findTypedAttribute<Imf::StringAttribute>("capDate")) {
|
||||||
// copy pixels(x,y) into image(x,y)
|
float off = 0;
|
||||||
image.setPixel(x, y, RgbaToQrgba(pixels[y][x]));
|
if (auto utcOffset = h.findTypedAttribute<Imf::FloatAttribute>("utcOffset")) {
|
||||||
|
off = utcOffset->value();
|
||||||
|
}
|
||||||
|
auto dateTime = QDateTime::fromString(QString::fromStdString(capDate->value()), QStringLiteral("yyyy:MM:dd HH:mm:ss"));
|
||||||
|
if (dateTime.isValid()) {
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
|
||||||
|
dateTime.setTimeZone(QTimeZone::fromSecondsAheadOfUtc(off));
|
||||||
|
#else
|
||||||
|
dateTime.setOffsetFromUtc(off);
|
||||||
|
#endif
|
||||||
|
image.setText(QStringLiteral("Date"), dateTime.toString(Qt::ISODate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (auto xDensity = h.findTypedAttribute<Imf::FloatAttribute>("xDensity")) {
|
||||||
|
float par = 1;
|
||||||
|
if (auto pixelAspectRatio = h.findTypedAttribute<Imf::FloatAttribute>("pixelAspectRatio")) {
|
||||||
|
par = pixelAspectRatio->value();
|
||||||
|
}
|
||||||
|
image.setDotsPerMeterX(qRound(xDensity->value() * 100.0 / 2.54));
|
||||||
|
image.setDotsPerMeterY(qRound(xDensity->value() * par * 100.0 / 2.54));
|
||||||
|
}
|
||||||
|
|
||||||
|
Imf::Array<Imf::Rgba> pixels;
|
||||||
|
pixels.resizeErase(width);
|
||||||
|
|
||||||
|
// somehow copy pixels into image
|
||||||
|
for (int y = 0; y < height; ++y) {
|
||||||
|
auto my = dw.min.y + y;
|
||||||
|
if (my <= dw.max.y) { // paranoia check
|
||||||
|
file.setFrameBuffer(&pixels[0] - dw.min.x - qint64(my) * width, 1, width);
|
||||||
|
file.readPixels(my, my);
|
||||||
|
|
||||||
|
#if defined(EXR_USE_LEGACY_CONVERSIONS)
|
||||||
|
auto scanLine = reinterpret_cast<QRgb *>(image.scanLine(y));
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
*(scanLine + x) = RgbaToQrgba(pixels[x]);
|
||||||
|
}
|
||||||
|
#elif defined(EXR_USE_QT6_FLOAT_IMAGE)
|
||||||
|
auto scanLine = reinterpret_cast<qfloat16 *>(image.scanLine(y));
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
auto xcs = x * 4;
|
||||||
|
*(scanLine + xcs) = qfloat16(qBound(0.f, float(pixels[x].r), 1.f));
|
||||||
|
*(scanLine + xcs + 1) = qfloat16(qBound(0.f, float(pixels[x].g), 1.f));
|
||||||
|
*(scanLine + xcs + 2) = qfloat16(qBound(0.f, float(pixels[x].b), 1.f));
|
||||||
|
*(scanLine + xcs + 3) = qfloat16(isRgba ? qBound(0.f, float(pixels[x].a), 1.f) : 1.f);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
auto scanLine = reinterpret_cast<QRgba64 *>(image.scanLine(y));
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
*(scanLine + x) = QRgba64::fromRgba64(quint16(qBound(0.f, float(pixels[x].r) * 65535.f + 0.5f, 65535.f)),
|
||||||
|
quint16(qBound(0.f, float(pixels[x].g) * 65535.f + 0.5f, 65535.f)),
|
||||||
|
quint16(qBound(0.f, float(pixels[x].b) * 65535.f + 0.5f, 65535.f)),
|
||||||
|
isRgba ? quint16(qBound(0.f, float(pixels[x].a) * 65535.f + 0.5f, 65535.f)) : quint16(65535));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// final color operations
|
||||||
|
#ifndef EXR_USE_LEGACY_CONVERSIONS
|
||||||
|
image.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
|
||||||
|
#ifndef EXR_ALLOW_LINEAR_COLORSPACE
|
||||||
|
image.convertToColorSpace(QColorSpace(QColorSpace::SRgb));
|
||||||
|
#endif // !EXR_ALLOW_LINEAR_COLORSPACE
|
||||||
|
#endif // !EXR_USE_LEGACY_CONVERSIONS
|
||||||
|
|
||||||
*outImage = image;
|
*outImage = image;
|
||||||
|
|
||||||
@ -259,3 +302,5 @@ QImageIOHandler *EXRPlugin::create(QIODevice *device, const QByteArray &format)
|
|||||||
handler->setFormat(format);
|
handler->setFormat(format);
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "moc_exr_p.cpp"
|
||||||
|
35
src/imageformats/fastmath_p.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
Approximated math functions used into conversions.
|
||||||
|
|
||||||
|
SPDX-FileCopyrightText: Edward Kmett
|
||||||
|
SPDX-FileCopyrightText: 2023 Mirco Miranda <mircomir@outlook.com>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
#ifndef FASTMATH_P_H
|
||||||
|
#define FASTMATH_P_H
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief fastPow
|
||||||
|
* Based on Edward Kmett code released into the public domain.
|
||||||
|
* See also: https://github.com/ekmett/approximate
|
||||||
|
*/
|
||||||
|
inline double fastPow(double x, double y)
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
double d;
|
||||||
|
qint32 i[2];
|
||||||
|
} u = {x};
|
||||||
|
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||||
|
u.i[1] = qint32(y * (u.i[1] - 1072632447) + 1072632447);
|
||||||
|
u.i[0] = 0;
|
||||||
|
#else // never tested
|
||||||
|
u.i[0] = qint32(y * (u.i[0] - 1072632447) + 1072632447);
|
||||||
|
u.i[1] = 0;
|
||||||
|
#endif
|
||||||
|
return u.d;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FASTMATH_P_H
|
@ -46,33 +46,6 @@ typedef enum {
|
|||||||
|
|
||||||
// From GIMP "libgimp/gimpenums.h" v2.4
|
// From GIMP "libgimp/gimpenums.h" v2.4
|
||||||
|
|
||||||
//! Effect to apply when layers are merged together.
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
NORMAL_MODE,
|
|
||||||
DISSOLVE_MODE,
|
|
||||||
BEHIND_MODE,
|
|
||||||
MULTIPLY_MODE,
|
|
||||||
SCREEN_MODE,
|
|
||||||
OVERLAY_MODE,
|
|
||||||
DIFFERENCE_MODE,
|
|
||||||
ADDITION_MODE,
|
|
||||||
SUBTRACT_MODE,
|
|
||||||
DARKEN_ONLY_MODE,
|
|
||||||
LIGHTEN_ONLY_MODE,
|
|
||||||
HUE_MODE,
|
|
||||||
SATURATION_MODE,
|
|
||||||
COLOR_MODE,
|
|
||||||
VALUE_MODE,
|
|
||||||
DIVIDE_MODE,
|
|
||||||
DODGE_MODE,
|
|
||||||
BURN_MODE,
|
|
||||||
HARDLIGHT_MODE,
|
|
||||||
SOFTLIGHT_MODE,
|
|
||||||
GRAIN_EXTRACT_MODE,
|
|
||||||
GRAIN_MERGE_MODE
|
|
||||||
} LayerModeEffects;
|
|
||||||
|
|
||||||
// From GIMP "paint_funcs.c" v1.2
|
// From GIMP "paint_funcs.c" v1.2
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "hdr_p.h"
|
#include "hdr_p.h"
|
||||||
#include "util_p.h"
|
#include "util_p.h"
|
||||||
|
|
||||||
|
#include <QColorSpace>
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
@ -28,11 +29,8 @@ namespace // Private.
|
|||||||
|
|
||||||
static inline uchar ClipToByte(float value)
|
static inline uchar ClipToByte(float value)
|
||||||
{
|
{
|
||||||
if (value > 255.0f) {
|
// we know value is positive.
|
||||||
return 255;
|
return uchar(std::min(value + 0.5f, 255.0f));
|
||||||
}
|
|
||||||
// else if (value < 0.0f) return 0; // we know value is positive.
|
|
||||||
return uchar(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// read an old style line from the hdr image file
|
// read an old style line from the hdr image file
|
||||||
@ -42,6 +40,7 @@ static bool Read_Old_Line(uchar *image, int width, QDataStream &s)
|
|||||||
int rshift = 0;
|
int rshift = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
uchar *start = image;
|
||||||
while (width > 0) {
|
while (width > 0) {
|
||||||
s >> image[0];
|
s >> image[0];
|
||||||
s >> image[1];
|
s >> image[1];
|
||||||
@ -53,7 +52,14 @@ static bool Read_Old_Line(uchar *image, int width, QDataStream &s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((image[0] == 1) && (image[1] == 1) && (image[2] == 1)) {
|
if ((image[0] == 1) && (image[1] == 1) && (image[2] == 1)) {
|
||||||
for (i = image[3] << rshift; i > 0; i--) {
|
// NOTE: we don't have an image sample that cover this code
|
||||||
|
if (rshift > 31) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (i = image[3] << rshift; i > 0 && width > 0; i--) {
|
||||||
|
if (image == start) {
|
||||||
|
return false; // you cannot be here at the first run
|
||||||
|
}
|
||||||
// memcpy(image, image-4, 4);
|
// memcpy(image, image-4, 4);
|
||||||
(uint &)image[0] = (uint &)image[0 - 4];
|
(uint &)image[0] = (uint &)image[0 - 4];
|
||||||
image += 4;
|
image += 4;
|
||||||
@ -74,7 +80,7 @@ static void RGBE_To_QRgbLine(uchar *image, QRgb *scanline, int width)
|
|||||||
for (int j = 0; j < width; j++) {
|
for (int j = 0; 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 = int(image[3]) - 128;
|
int e = qBound(-31, int(image[3]) - 128, 31);
|
||||||
if (e > 0) {
|
if (e > 0) {
|
||||||
v = float(1 << e);
|
v = float(1 << e);
|
||||||
} else {
|
} else {
|
||||||
@ -148,7 +154,7 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
|||||||
}
|
}
|
||||||
|
|
||||||
// read each component
|
// read each component
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0, len = int(lineArray.size()); i < 4; i++) {
|
||||||
for (int j = 0; j < width;) {
|
for (int j = 0; j < width;) {
|
||||||
s >> code;
|
s >> code;
|
||||||
if (s.atEnd()) {
|
if (s.atEnd()) {
|
||||||
@ -160,14 +166,20 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
|||||||
code &= 127;
|
code &= 127;
|
||||||
s >> val;
|
s >> val;
|
||||||
while (code != 0) {
|
while (code != 0) {
|
||||||
image[i + j * 4] = val;
|
auto idx = i + j * 4;
|
||||||
|
if (idx < len) {
|
||||||
|
image[idx] = val;
|
||||||
|
}
|
||||||
j++;
|
j++;
|
||||||
code--;
|
code--;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// non-run
|
// non-run
|
||||||
while (code != 0) {
|
while (code != 0) {
|
||||||
s >> image[i + j * 4];
|
auto idx = i + j * 4;
|
||||||
|
if (idx < len) {
|
||||||
|
s >> image[idx];
|
||||||
|
}
|
||||||
j++;
|
j++;
|
||||||
code--;
|
code--;
|
||||||
}
|
}
|
||||||
@ -242,6 +254,9 @@ bool HDRHandler::read(QImage *outImage)
|
|||||||
// 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.
|
||||||
|
img.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
|
||||||
|
|
||||||
*outImage = img;
|
*outImage = img;
|
||||||
return true;
|
return true;
|
||||||
@ -296,3 +311,5 @@ QImageIOHandler *HDRPlugin::create(QIODevice *device, const QByteArray &format)
|
|||||||
handler->setFormat(format);
|
handler->setFormat(format);
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "moc_hdr_p.cpp"
|
||||||
|
@ -22,6 +22,7 @@ size_t HEIFHandler::m_initialized_count = 0;
|
|||||||
bool HEIFHandler::m_plugins_queried = false;
|
bool HEIFHandler::m_plugins_queried = false;
|
||||||
bool HEIFHandler::m_heif_decoder_available = false;
|
bool HEIFHandler::m_heif_decoder_available = false;
|
||||||
bool HEIFHandler::m_heif_encoder_available = false;
|
bool HEIFHandler::m_heif_encoder_available = false;
|
||||||
|
bool HEIFHandler::m_hej2_decoder_available = false;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
static struct heif_error heifhandler_write_callback(struct heif_context * /* ctx */, const void *data, size_t size, void *userdata)
|
static struct heif_error heifhandler_write_callback(struct heif_context * /* ctx */, const void *data, size_t size, void *userdata)
|
||||||
@ -59,12 +60,25 @@ HEIFHandler::HEIFHandler()
|
|||||||
|
|
||||||
bool HEIFHandler::canRead() const
|
bool HEIFHandler::canRead() const
|
||||||
{
|
{
|
||||||
if (m_parseState == ParseHeicNotParsed && !canRead(device())) {
|
if (m_parseState == ParseHeicNotParsed) {
|
||||||
|
QIODevice *dev = device();
|
||||||
|
if (dev) {
|
||||||
|
const QByteArray header = dev->peek(28);
|
||||||
|
|
||||||
|
if (HEIFHandler::isSupportedBMFFType(header)) {
|
||||||
|
setFormat("heif");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HEIFHandler::isSupportedHEJ2(header)) {
|
||||||
|
setFormat("hej2");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_parseState != ParseHeicError) {
|
if (m_parseState != ParseHeicError) {
|
||||||
setFormat("heif");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -300,17 +314,6 @@ bool HEIFHandler::write_helper(const QImage &image)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HEIFHandler::canRead(QIODevice *device)
|
|
||||||
{
|
|
||||||
if (!device) {
|
|
||||||
qWarning("HEIFHandler::canRead() called with no device");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QByteArray header = device->peek(28);
|
|
||||||
return HEIFHandler::isSupportedBMFFType(header);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HEIFHandler::isSupportedBMFFType(const QByteArray &header)
|
bool HEIFHandler::isSupportedBMFFType(const QByteArray &header)
|
||||||
{
|
{
|
||||||
if (header.size() < 28) {
|
if (header.size() < 28) {
|
||||||
@ -350,6 +353,22 @@ bool HEIFHandler::isSupportedBMFFType(const QByteArray &header)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HEIFHandler::isSupportedHEJ2(const QByteArray &header)
|
||||||
|
{
|
||||||
|
if (header.size() < 28) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *buffer = header.constData();
|
||||||
|
if (qstrncmp(buffer + 4, "ftyp", 4) == 0) {
|
||||||
|
if (qstrncmp(buffer + 8, "j2ki", 4) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QVariant HEIFHandler::option(ImageOption option) const
|
QVariant HEIFHandler::option(ImageOption option) const
|
||||||
{
|
{
|
||||||
if (option == Quality) {
|
if (option == Quality) {
|
||||||
@ -425,7 +444,7 @@ bool HEIFHandler::ensureDecoder()
|
|||||||
}
|
}
|
||||||
|
|
||||||
const QByteArray buffer = device()->readAll();
|
const QByteArray buffer = device()->readAll();
|
||||||
if (!HEIFHandler::isSupportedBMFFType(buffer)) {
|
if (!HEIFHandler::isSupportedBMFFType(buffer) && !HEIFHandler::isSupportedHEJ2(buffer)) {
|
||||||
m_parseState = ParseHeicError;
|
m_parseState = ParseHeicError;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -449,6 +468,14 @@ bool HEIFHandler::ensureDecoder()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((heif_image_handle_get_width(handle) == 0) || (heif_image_handle_get_height(handle) == 0)) {
|
||||||
|
m_parseState = ParseHeicError;
|
||||||
|
heif_image_handle_release(handle);
|
||||||
|
heif_context_free(ctx);
|
||||||
|
qWarning() << "HEIC image has zero dimension";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const bool hasAlphaChannel = heif_image_handle_has_alpha_channel(handle);
|
const bool hasAlphaChannel = heif_image_handle_has_alpha_channel(handle);
|
||||||
const int bit_depth = heif_image_handle_get_luma_bits_per_pixel(handle);
|
const int bit_depth = heif_image_handle_get_luma_bits_per_pixel(handle);
|
||||||
heif_chroma chroma;
|
heif_chroma chroma;
|
||||||
@ -806,6 +833,9 @@ bool HEIFHandler::isHeifDecoderAvailable()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
|
||||||
|
m_hej2_decoder_available = heif_have_decoder_for_format(heif_compression_JPEG2000);
|
||||||
|
#endif
|
||||||
m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC);
|
m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC);
|
||||||
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
|
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
|
||||||
m_plugins_queried = true;
|
m_plugins_queried = true;
|
||||||
@ -831,6 +861,9 @@ bool HEIFHandler::isHeifEncoderAvailable()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
|
||||||
|
m_hej2_decoder_available = heif_have_decoder_for_format(heif_compression_JPEG2000);
|
||||||
|
#endif
|
||||||
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
|
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
|
||||||
m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC);
|
m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC);
|
||||||
m_plugins_queried = true;
|
m_plugins_queried = true;
|
||||||
@ -845,6 +878,34 @@ bool HEIFHandler::isHeifEncoderAvailable()
|
|||||||
return m_heif_encoder_available;
|
return m_heif_encoder_available;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HEIFHandler::isHej2DecoderAvailable()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&getHEIFHandlerMutex());
|
||||||
|
|
||||||
|
if (!m_plugins_queried) {
|
||||||
|
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
|
||||||
|
if (m_initialized_count == 0) {
|
||||||
|
heif_init(nullptr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_heif_encoder_available = heif_have_encoder_for_format(heif_compression_HEVC);
|
||||||
|
m_heif_decoder_available = heif_have_decoder_for_format(heif_compression_HEVC);
|
||||||
|
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
|
||||||
|
m_hej2_decoder_available = heif_have_decoder_for_format(heif_compression_JPEG2000);
|
||||||
|
#endif
|
||||||
|
m_plugins_queried = true;
|
||||||
|
|
||||||
|
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
|
||||||
|
if (m_initialized_count == 0) {
|
||||||
|
heif_deinit();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_hej2_decoder_available;
|
||||||
|
}
|
||||||
|
|
||||||
void HEIFHandler::startHeifLib()
|
void HEIFHandler::startHeifLib()
|
||||||
{
|
{
|
||||||
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
|
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
|
||||||
@ -893,6 +954,15 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q
|
|||||||
}
|
}
|
||||||
return format_cap;
|
return format_cap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (format == "hej2") {
|
||||||
|
Capabilities format_cap;
|
||||||
|
if (HEIFHandler::isHej2DecoderAvailable()) {
|
||||||
|
format_cap |= CanRead;
|
||||||
|
}
|
||||||
|
return format_cap;
|
||||||
|
}
|
||||||
|
|
||||||
if (!format.isEmpty()) {
|
if (!format.isEmpty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -901,8 +971,16 @@ QImageIOPlugin::Capabilities HEIFPlugin::capabilities(QIODevice *device, const Q
|
|||||||
}
|
}
|
||||||
|
|
||||||
Capabilities cap;
|
Capabilities cap;
|
||||||
if (device->isReadable() && HEIFHandler::canRead(device) && HEIFHandler::isHeifDecoderAvailable()) {
|
if (device->isReadable()) {
|
||||||
cap |= CanRead;
|
const QByteArray header = device->peek(28);
|
||||||
|
|
||||||
|
if (HEIFHandler::isSupportedBMFFType(header) && HEIFHandler::isHeifDecoderAvailable()) {
|
||||||
|
cap |= CanRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HEIFHandler::isSupportedHEJ2(header) && HEIFHandler::isHej2DecoderAvailable()) {
|
||||||
|
cap |= CanRead;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device->isWritable() && HEIFHandler::isHeifEncoderAvailable()) {
|
if (device->isWritable() && HEIFHandler::isHeifEncoderAvailable()) {
|
||||||
@ -918,3 +996,5 @@ QImageIOHandler *HEIFPlugin::create(QIODevice *device, const QByteArray &format)
|
|||||||
handler->setFormat(format);
|
handler->setFormat(format);
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "moc_heif_p.cpp"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"Keys": [ "heif", "heic" ],
|
"Keys": [ "heif", "heic", "hej2" ],
|
||||||
"MimeTypes": [ "image/heif", "image/heif" ]
|
"MimeTypes": [ "image/heif", "image/heif", "image/hej2k" ]
|
||||||
}
|
}
|
||||||
|
@ -24,17 +24,18 @@ public:
|
|||||||
bool read(QImage *image) override;
|
bool read(QImage *image) override;
|
||||||
bool write(const QImage &image) override;
|
bool write(const QImage &image) override;
|
||||||
|
|
||||||
static bool canRead(QIODevice *device);
|
|
||||||
|
|
||||||
QVariant option(ImageOption option) const override;
|
QVariant option(ImageOption option) const override;
|
||||||
void setOption(ImageOption option, const QVariant &value) override;
|
void setOption(ImageOption option, const QVariant &value) override;
|
||||||
bool supportsOption(ImageOption option) const override;
|
bool supportsOption(ImageOption option) const override;
|
||||||
|
|
||||||
static bool isHeifDecoderAvailable();
|
static bool isHeifDecoderAvailable();
|
||||||
static bool isHeifEncoderAvailable();
|
static bool isHeifEncoderAvailable();
|
||||||
|
static bool isHej2DecoderAvailable();
|
||||||
|
|
||||||
|
static bool isSupportedBMFFType(const QByteArray &header);
|
||||||
|
static bool isSupportedHEJ2(const QByteArray &header);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool isSupportedBMFFType(const QByteArray &header);
|
|
||||||
bool ensureParsed() const;
|
bool ensureParsed() const;
|
||||||
bool ensureDecoder();
|
bool ensureDecoder();
|
||||||
|
|
||||||
@ -57,6 +58,7 @@ private:
|
|||||||
static bool m_plugins_queried;
|
static bool m_plugins_queried;
|
||||||
static bool m_heif_decoder_available;
|
static bool m_heif_decoder_available;
|
||||||
static bool m_heif_encoder_available;
|
static bool m_heif_encoder_available;
|
||||||
|
static bool m_hej2_decoder_available;
|
||||||
|
|
||||||
static QMutex &getHEIFHandlerMutex();
|
static QMutex &getHEIFHandlerMutex();
|
||||||
};
|
};
|
||||||
|
@ -150,9 +150,7 @@ bool QJpegXLHandler::ensureDecoder()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef KIMG_JXL_API_VERSION
|
|
||||||
JxlDecoderCloseInput(m_decoder);
|
JxlDecoderCloseInput(m_decoder);
|
||||||
#endif
|
|
||||||
|
|
||||||
JxlDecoderStatus status = JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME);
|
JxlDecoderStatus status = JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME);
|
||||||
if (status == JXL_DEC_ERROR) {
|
if (status == JXL_DEC_ERROR) {
|
||||||
@ -269,18 +267,31 @@ bool QJpegXLHandler::countALLFrames()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
status = JxlDecoderGetColorAsEncodedProfile(m_decoder, &m_input_pixel_format, JXL_COLOR_PROFILE_TARGET_DATA, &color_encoding);
|
status = JxlDecoderGetColorAsEncodedProfile(m_decoder,
|
||||||
|
#if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0)
|
||||||
|
&m_input_pixel_format,
|
||||||
|
#endif
|
||||||
|
JXL_COLOR_PROFILE_TARGET_DATA,
|
||||||
|
&color_encoding);
|
||||||
|
|
||||||
if (status == JXL_DEC_SUCCESS && color_encoding.color_space == JXL_COLOR_SPACE_RGB && color_encoding.white_point == JXL_WHITE_POINT_D65
|
if (status == JXL_DEC_SUCCESS && color_encoding.color_space == JXL_COLOR_SPACE_RGB && color_encoding.white_point == JXL_WHITE_POINT_D65
|
||||||
&& color_encoding.primaries == JXL_PRIMARIES_SRGB && color_encoding.transfer_function == JXL_TRANSFER_FUNCTION_SRGB) {
|
&& color_encoding.primaries == JXL_PRIMARIES_SRGB && color_encoding.transfer_function == JXL_TRANSFER_FUNCTION_SRGB) {
|
||||||
m_colorspace = QColorSpace(QColorSpace::SRgb);
|
m_colorspace = QColorSpace(QColorSpace::SRgb);
|
||||||
} else {
|
} else {
|
||||||
size_t icc_size = 0;
|
size_t icc_size = 0;
|
||||||
if (JxlDecoderGetICCProfileSize(m_decoder, &m_input_pixel_format, JXL_COLOR_PROFILE_TARGET_DATA, &icc_size) == JXL_DEC_SUCCESS) {
|
if (JxlDecoderGetICCProfileSize(m_decoder,
|
||||||
|
#if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0)
|
||||||
|
&m_input_pixel_format,
|
||||||
|
#endif
|
||||||
|
JXL_COLOR_PROFILE_TARGET_DATA,
|
||||||
|
&icc_size)
|
||||||
|
== JXL_DEC_SUCCESS) {
|
||||||
if (icc_size > 0) {
|
if (icc_size > 0) {
|
||||||
QByteArray icc_data(icc_size, 0);
|
QByteArray icc_data(icc_size, 0);
|
||||||
if (JxlDecoderGetColorAsICCProfile(m_decoder,
|
if (JxlDecoderGetColorAsICCProfile(m_decoder,
|
||||||
|
#if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0)
|
||||||
&m_input_pixel_format,
|
&m_input_pixel_format,
|
||||||
|
#endif
|
||||||
JXL_COLOR_PROFILE_TARGET_DATA,
|
JXL_COLOR_PROFILE_TARGET_DATA,
|
||||||
reinterpret_cast<uint8_t *>(icc_data.data()),
|
reinterpret_cast<uint8_t *>(icc_data.data()),
|
||||||
icc_data.size())
|
icc_data.size())
|
||||||
@ -534,9 +545,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
|||||||
if (save_depth == 16 && (image.hasAlphaChannel() || output_info.uses_original_profile)) {
|
if (save_depth == 16 && (image.hasAlphaChannel() || output_info.uses_original_profile)) {
|
||||||
output_info.have_container = JXL_TRUE;
|
output_info.have_container = JXL_TRUE;
|
||||||
JxlEncoderUseContainer(encoder, JXL_TRUE);
|
JxlEncoderUseContainer(encoder, JXL_TRUE);
|
||||||
#ifdef KIMG_JXL_API_VERSION
|
|
||||||
JxlEncoderSetCodestreamLevel(encoder, 10);
|
JxlEncoderSetCodestreamLevel(encoder, 10);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *runner = nullptr;
|
void *runner = nullptr;
|
||||||
@ -650,19 +659,11 @@ bool QJpegXLHandler::write(const QImage &image)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef KIMG_JXL_API_VERSION
|
|
||||||
JxlEncoderFrameSettings *encoder_options = JxlEncoderFrameSettingsCreate(encoder, nullptr);
|
JxlEncoderFrameSettings *encoder_options = JxlEncoderFrameSettingsCreate(encoder, nullptr);
|
||||||
|
|
||||||
JxlEncoderSetFrameDistance(encoder_options, (100.0f - m_quality) / 10.0f);
|
JxlEncoderSetFrameDistance(encoder_options, (100.0f - m_quality) / 10.0f);
|
||||||
|
|
||||||
JxlEncoderSetFrameLossless(encoder_options, (m_quality == 100) ? JXL_TRUE : JXL_FALSE);
|
JxlEncoderSetFrameLossless(encoder_options, (m_quality == 100) ? JXL_TRUE : JXL_FALSE);
|
||||||
#else
|
|
||||||
JxlEncoderOptions *encoder_options = JxlEncoderOptionsCreate(encoder, nullptr);
|
|
||||||
|
|
||||||
JxlEncoderOptionsSetDistance(encoder_options, (100.0f - m_quality) / 10.0f);
|
|
||||||
|
|
||||||
JxlEncoderOptionsSetLossless(encoder_options, (m_quality == 100) ? JXL_TRUE : JXL_FALSE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (image.hasAlphaChannel() || ((save_depth == 8) && (xsize % 4 == 0))) {
|
if (image.hasAlphaChannel() || ((save_depth == 8) && (xsize % 4 == 0))) {
|
||||||
status = JxlEncoderAddImageFrame(encoder_options, &pixel_format, static_cast<const void *>(tmpimage.constBits()), buffer_size);
|
status = JxlEncoderAddImageFrame(encoder_options, &pixel_format, static_cast<const void *>(tmpimage.constBits()), buffer_size);
|
||||||
@ -957,9 +958,7 @@ bool QJpegXLHandler::rewind()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef KIMG_JXL_API_VERSION
|
|
||||||
JxlDecoderCloseInput(m_decoder);
|
JxlDecoderCloseInput(m_decoder);
|
||||||
#endif
|
|
||||||
|
|
||||||
if (m_basicinfo.uses_original_profile) {
|
if (m_basicinfo.uses_original_profile) {
|
||||||
if (JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_FULL_IMAGE) != JXL_DEC_SUCCESS) {
|
if (JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_FULL_IMAGE) != JXL_DEC_SUCCESS) {
|
||||||
@ -1021,3 +1020,5 @@ QImageIOHandler *QJpegXLPlugin::create(QIODevice *device, const QByteArray &form
|
|||||||
handler->setFormat(format);
|
handler->setFormat(format);
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "moc_jxl_p.cpp"
|
||||||
|
@ -95,3 +95,5 @@ QImageIOHandler *KraPlugin::create(QIODevice *device, const QByteArray &format)
|
|||||||
handler->setFormat(format);
|
handler->setFormat(format);
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "moc_kra.cpp"
|
||||||
|
@ -94,3 +94,5 @@ QImageIOHandler *OraPlugin::create(QIODevice *device, const QByteArray &format)
|
|||||||
handler->setFormat(format);
|
handler->setFormat(format);
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "moc_ora.cpp"
|
||||||
|
@ -230,7 +230,7 @@ PCXHEADER::PCXHEADER()
|
|||||||
s >> *this;
|
s >> *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void readLine(QDataStream &s, QByteArray &buf, const PCXHEADER &header)
|
static bool readLine(QDataStream &s, QByteArray &buf, const PCXHEADER &header)
|
||||||
{
|
{
|
||||||
quint32 i = 0;
|
quint32 i = 0;
|
||||||
quint32 size = buf.size();
|
quint32 size = buf.size();
|
||||||
@ -257,9 +257,11 @@ static void readLine(QDataStream &s, QByteArray &buf, const PCXHEADER &header)
|
|||||||
buf[i++] = byte;
|
buf[i++] = byte;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (s.status() == QDataStream::Ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void readImage1(QImage &img, QDataStream &s, const PCXHEADER &header)
|
static bool readImage1(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||||
{
|
{
|
||||||
QByteArray buf(header.BytesPerLine, 0);
|
QByteArray buf(header.BytesPerLine, 0);
|
||||||
|
|
||||||
@ -268,16 +270,18 @@ static void readImage1(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|||||||
|
|
||||||
if (img.isNull()) {
|
if (img.isNull()) {
|
||||||
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int y = 0; y < header.height(); ++y) {
|
for (int y = 0; y < header.height(); ++y) {
|
||||||
if (s.atEnd()) {
|
if (s.atEnd()) {
|
||||||
img = QImage();
|
return false;
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
if (!readLine(s, buf, header)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
readLine(s, buf, header);
|
|
||||||
uchar *p = img.scanLine(y);
|
uchar *p = img.scanLine(y);
|
||||||
unsigned int bpl = qMin((quint16)((header.width() + 7) / 8), header.BytesPerLine);
|
unsigned int bpl = qMin((quint16)((header.width() + 7) / 8), header.BytesPerLine);
|
||||||
for (unsigned int x = 0; x < bpl; ++x) {
|
for (unsigned int x = 0; x < bpl; ++x) {
|
||||||
@ -288,9 +292,11 @@ static void readImage1(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|||||||
// Set the color palette
|
// Set the color palette
|
||||||
img.setColor(0, qRgb(0, 0, 0));
|
img.setColor(0, qRgb(0, 0, 0));
|
||||||
img.setColor(1, qRgb(255, 255, 255));
|
img.setColor(1, qRgb(255, 255, 255));
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void readImage4(QImage &img, QDataStream &s, const PCXHEADER &header)
|
static bool readImage4(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||||
{
|
{
|
||||||
QByteArray buf(header.BytesPerLine * 4, 0);
|
QByteArray buf(header.BytesPerLine * 4, 0);
|
||||||
QByteArray pixbuf(header.width(), 0);
|
QByteArray pixbuf(header.width(), 0);
|
||||||
@ -299,17 +305,18 @@ static void readImage4(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|||||||
img.setColorCount(16);
|
img.setColorCount(16);
|
||||||
if (img.isNull()) {
|
if (img.isNull()) {
|
||||||
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int y = 0; y < header.height(); ++y) {
|
for (int y = 0; y < header.height(); ++y) {
|
||||||
if (s.atEnd()) {
|
if (s.atEnd()) {
|
||||||
img = QImage();
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pixbuf.fill(0);
|
pixbuf.fill(0);
|
||||||
readLine(s, buf, header);
|
if (!readLine(s, buf, header)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
quint32 offset = i * header.BytesPerLine;
|
quint32 offset = i * header.BytesPerLine;
|
||||||
@ -333,9 +340,11 @@ static void readImage4(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|||||||
for (int i = 0; i < 16; ++i) {
|
for (int i = 0; i < 16; ++i) {
|
||||||
img.setColor(i, header.ColorMap.color(i));
|
img.setColor(i, header.ColorMap.color(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void 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);
|
||||||
|
|
||||||
@ -344,21 +353,21 @@ static void readImage8(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|||||||
|
|
||||||
if (img.isNull()) {
|
if (img.isNull()) {
|
||||||
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int y = 0; y < header.height(); ++y) {
|
for (int y = 0; y < header.height(); ++y) {
|
||||||
if (s.atEnd()) {
|
if (s.atEnd()) {
|
||||||
img = QImage();
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
readLine(s, buf, header);
|
if (!readLine(s, buf, header)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uchar *p = img.scanLine(y);
|
uchar *p = img.scanLine(y);
|
||||||
|
|
||||||
if (!p) {
|
if (!p) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int bpl = qMin(header.BytesPerLine, (quint16)header.width());
|
unsigned int bpl = qMin(header.BytesPerLine, (quint16)header.width());
|
||||||
@ -367,10 +376,21 @@ static void readImage8(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
quint8 flag;
|
// by specification, the extended palette starts at file.size() - 769
|
||||||
s >> flag;
|
quint8 flag = 0;
|
||||||
// qDebug() << "Palette Flag: " << flag;
|
if (auto device = s.device()) {
|
||||||
|
if (device->isSequential()) {
|
||||||
|
while (flag != 12 && s.status() == QDataStream::Ok) {
|
||||||
|
s >> flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
device->seek(device->size() - 769);
|
||||||
|
s >> flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// qDebug() << "Palette Flag: " << flag;
|
||||||
if (flag == 12 && (header.Version == 5 || header.Version == 2)) {
|
if (flag == 12 && (header.Version == 5 || header.Version == 2)) {
|
||||||
// Read the palette
|
// Read the palette
|
||||||
quint8 r;
|
quint8 r;
|
||||||
@ -381,9 +401,11 @@ static void readImage8(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|||||||
img.setColor(i, qRgb(r, g, b));
|
img.setColor(i, qRgb(r, g, b));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (s.status() == QDataStream::Ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void readImage24(QImage &img, QDataStream &s, const PCXHEADER &header)
|
static bool readImage24(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||||
{
|
{
|
||||||
QByteArray r_buf(header.BytesPerLine, 0);
|
QByteArray r_buf(header.BytesPerLine, 0);
|
||||||
QByteArray g_buf(header.BytesPerLine, 0);
|
QByteArray g_buf(header.BytesPerLine, 0);
|
||||||
@ -393,27 +415,34 @@ static void readImage24(QImage &img, QDataStream &s, const PCXHEADER &header)
|
|||||||
|
|
||||||
if (img.isNull()) {
|
if (img.isNull()) {
|
||||||
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int y = 0; y < header.height(); ++y) {
|
for (int y = 0; y < header.height(); ++y) {
|
||||||
if (s.atEnd()) {
|
if (s.atEnd()) {
|
||||||
img = QImage();
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
readLine(s, r_buf, header);
|
if (!readLine(s, r_buf, header)) {
|
||||||
readLine(s, g_buf, header);
|
return false;
|
||||||
readLine(s, b_buf, header);
|
}
|
||||||
|
if (!readLine(s, g_buf, header)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!readLine(s, b_buf, header)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint *p = (uint *)img.scanLine(y);
|
uint *p = (uint *)img.scanLine(y);
|
||||||
for (int x = 0; x < header.width(); ++x) {
|
for (int x = 0; x < header.width(); ++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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeLine(QDataStream &s, QByteArray &buf)
|
static bool writeLine(QDataStream &s, QByteArray &buf)
|
||||||
{
|
{
|
||||||
quint32 i = 0;
|
quint32 i = 0;
|
||||||
quint32 size = buf.size();
|
quint32 size = buf.size();
|
||||||
@ -439,15 +468,26 @@ static void writeLine(QDataStream &s, QByteArray &buf)
|
|||||||
|
|
||||||
s << data;
|
s << data;
|
||||||
}
|
}
|
||||||
|
return (s.status() == QDataStream::Ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeImage1(QImage &img, QDataStream &s, PCXHEADER &header)
|
static bool writeImage1(QImage &img, QDataStream &s, PCXHEADER &header)
|
||||||
{
|
{
|
||||||
img = img.convertToFormat(QImage::Format_Mono);
|
if (img.format() != QImage::Format_Mono) {
|
||||||
|
img = img.convertToFormat(QImage::Format_Mono);
|
||||||
|
}
|
||||||
|
if (img.isNull() || img.colorCount() < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto rgb = img.color(0);
|
||||||
|
auto minIsBlack = (qRed(rgb) + qGreen(rgb) + qBlue(rgb)) / 3 < 127;
|
||||||
|
|
||||||
header.Bpp = 1;
|
header.Bpp = 1;
|
||||||
header.NPlanes = 1;
|
header.NPlanes = 1;
|
||||||
header.BytesPerLine = img.bytesPerLine();
|
header.BytesPerLine = img.bytesPerLine();
|
||||||
|
if (header.BytesPerLine == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
s << header;
|
s << header;
|
||||||
|
|
||||||
@ -458,18 +498,24 @@ static void writeImage1(QImage &img, QDataStream &s, PCXHEADER &header)
|
|||||||
|
|
||||||
// Invert as QImage uses reverse palette for monochrome images?
|
// Invert as QImage uses reverse palette for monochrome images?
|
||||||
for (int i = 0; i < header.BytesPerLine; ++i) {
|
for (int i = 0; i < header.BytesPerLine; ++i) {
|
||||||
buf[i] = ~p[i];
|
buf[i] = minIsBlack ? p[i] : ~p[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
writeLine(s, buf);
|
if (!writeLine(s, buf)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeImage4(QImage &img, QDataStream &s, PCXHEADER &header)
|
static bool writeImage4(QImage &img, QDataStream &s, PCXHEADER &header)
|
||||||
{
|
{
|
||||||
header.Bpp = 1;
|
header.Bpp = 1;
|
||||||
header.NPlanes = 4;
|
header.NPlanes = 4;
|
||||||
header.BytesPerLine = header.width() / 8;
|
header.BytesPerLine = header.width() / 8;
|
||||||
|
if (header.BytesPerLine == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 16; ++i) {
|
for (int i = 0; i < 16; ++i) {
|
||||||
header.ColorMap.setColor(i, img.color(i));
|
header.ColorMap.setColor(i, img.color(i));
|
||||||
@ -499,16 +545,22 @@ static void writeImage4(QImage &img, QDataStream &s, PCXHEADER &header)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
writeLine(s, buf[i]);
|
if (!writeLine(s, buf[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeImage8(QImage &img, QDataStream &s, PCXHEADER &header)
|
static bool writeImage8(QImage &img, QDataStream &s, PCXHEADER &header)
|
||||||
{
|
{
|
||||||
header.Bpp = 8;
|
header.Bpp = 8;
|
||||||
header.NPlanes = 1;
|
header.NPlanes = 1;
|
||||||
header.BytesPerLine = img.bytesPerLine();
|
header.BytesPerLine = img.bytesPerLine();
|
||||||
|
if (header.BytesPerLine == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
s << header;
|
s << header;
|
||||||
|
|
||||||
@ -521,7 +573,9 @@ static void writeImage8(QImage &img, QDataStream &s, PCXHEADER &header)
|
|||||||
buf[i] = p[i];
|
buf[i] = p[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
writeLine(s, buf);
|
if (!writeLine(s, buf)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write palette flag
|
// Write palette flag
|
||||||
@ -532,13 +586,25 @@ static void writeImage8(QImage &img, QDataStream &s, PCXHEADER &header)
|
|||||||
for (int i = 0; i < 256; ++i) {
|
for (int i = 0; i < 256; ++i) {
|
||||||
s << RGB::from(img.color(i));
|
s << RGB::from(img.color(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (s.status() == QDataStream::Ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeImage24(QImage &img, QDataStream &s, PCXHEADER &header)
|
static bool writeImage24(QImage &img, QDataStream &s, PCXHEADER &header)
|
||||||
{
|
{
|
||||||
header.Bpp = 8;
|
header.Bpp = 8;
|
||||||
header.NPlanes = 3;
|
header.NPlanes = 3;
|
||||||
header.BytesPerLine = header.width();
|
header.BytesPerLine = header.width();
|
||||||
|
if (header.BytesPerLine == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (img.format() != QImage::Format_ARGB32 && img.format() != QImage::Format_RGB32) {
|
||||||
|
img = img.convertToFormat(QImage::Format_RGB32);
|
||||||
|
}
|
||||||
|
if (img.isNull()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
s << header;
|
s << header;
|
||||||
|
|
||||||
@ -547,7 +613,7 @@ static void writeImage24(QImage &img, QDataStream &s, PCXHEADER &header)
|
|||||||
QByteArray b_buf(header.width(), 0);
|
QByteArray b_buf(header.width(), 0);
|
||||||
|
|
||||||
for (int y = 0; y < header.height(); ++y) {
|
for (int y = 0; y < header.height(); ++y) {
|
||||||
uint *p = (uint *)img.scanLine(y);
|
auto p = (QRgb*)img.scanLine(y);
|
||||||
|
|
||||||
for (int x = 0; x < header.width(); ++x) {
|
for (int x = 0; x < header.width(); ++x) {
|
||||||
QRgb rgb = *p++;
|
QRgb rgb = *p++;
|
||||||
@ -556,10 +622,18 @@ static void writeImage24(QImage &img, QDataStream &s, PCXHEADER &header)
|
|||||||
b_buf[x] = qBlue(rgb);
|
b_buf[x] = qBlue(rgb);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeLine(s, r_buf);
|
if (!writeLine(s, r_buf)) {
|
||||||
writeLine(s, g_buf);
|
return false;
|
||||||
writeLine(s, b_buf);
|
}
|
||||||
|
if (!writeLine(s, g_buf)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!writeLine(s, b_buf)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
PCXHandler::PCXHandler()
|
PCXHandler::PCXHandler()
|
||||||
@ -588,46 +662,30 @@ bool PCXHandler::read(QImage *outImage)
|
|||||||
|
|
||||||
s >> header;
|
s >> header;
|
||||||
|
|
||||||
if (header.Manufacturer != 10 || s.atEnd()) {
|
if (header.Manufacturer != 10 || header.BytesPerLine == 0 || s.atEnd()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// int w = header.width();
|
auto ok = false;
|
||||||
// int h = header.height();
|
|
||||||
|
|
||||||
// qDebug() << "Manufacturer: " << header.Manufacturer;
|
|
||||||
// qDebug() << "Version: " << header.Version;
|
|
||||||
// qDebug() << "Encoding: " << header.Encoding;
|
|
||||||
// qDebug() << "Bpp: " << header.Bpp;
|
|
||||||
// qDebug() << "Width: " << w;
|
|
||||||
// qDebug() << "Height: " << h;
|
|
||||||
// qDebug() << "Window: " << header.XMin << "," << header.XMax << ","
|
|
||||||
// << header.YMin << "," << header.YMax << endl;
|
|
||||||
// qDebug() << "BytesPerLine: " << header.BytesPerLine;
|
|
||||||
// qDebug() << "NPlanes: " << header.NPlanes;
|
|
||||||
|
|
||||||
QImage img;
|
QImage img;
|
||||||
|
|
||||||
if (header.Bpp == 1 && header.NPlanes == 1) {
|
if (header.Bpp == 1 && header.NPlanes == 1) {
|
||||||
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) {
|
||||||
readImage4(img, s, header);
|
ok = readImage4(img, s, header);
|
||||||
} else if (header.Bpp == 8 && header.NPlanes == 1) {
|
} else if (header.Bpp == 8 && header.NPlanes == 1) {
|
||||||
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) {
|
||||||
readImage24(img, s, header);
|
ok = readImage24(img, s, header);
|
||||||
}
|
}
|
||||||
|
|
||||||
// qDebug() << "Image Bytes: " << img.numBytes();
|
if (img.isNull() || !ok) {
|
||||||
// qDebug() << "Image Bytes Per Line: " << img.bytesPerLine();
|
|
||||||
// qDebug() << "Image Depth: " << img.depth();
|
|
||||||
|
|
||||||
if (!img.isNull()) {
|
|
||||||
*outImage = img;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img.setDotsPerMeterX(qRound(header.HDpi / 25.4 * 1000));
|
||||||
|
img.setDotsPerMeterY(qRound(header.YDpi / 25.4 * 1000));
|
||||||
|
*outImage = img;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PCXHandler::write(const QImage &image)
|
bool PCXHandler::write(const QImage &image)
|
||||||
@ -644,12 +702,6 @@ bool PCXHandler::write(const QImage &image)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// qDebug() << "Width: " << w;
|
|
||||||
// qDebug() << "Height: " << h;
|
|
||||||
// qDebug() << "Depth: " << img.depth();
|
|
||||||
// qDebug() << "BytesPerLine: " << img.bytesPerLine();
|
|
||||||
// qDebug() << "Color Count: " << img.colorCount();
|
|
||||||
|
|
||||||
PCXHEADER header;
|
PCXHEADER header;
|
||||||
|
|
||||||
header.Manufacturer = 10;
|
header.Manufacturer = 10;
|
||||||
@ -659,22 +711,23 @@ bool PCXHandler::write(const QImage &image)
|
|||||||
header.YMin = 0;
|
header.YMin = 0;
|
||||||
header.XMax = w - 1;
|
header.XMax = w - 1;
|
||||||
header.YMax = h - 1;
|
header.YMax = h - 1;
|
||||||
header.HDpi = 300;
|
header.HDpi = qRound(image.dotsPerMeterX() * 25.4 / 1000);
|
||||||
header.YDpi = 300;
|
header.YDpi = qRound(image.dotsPerMeterY() * 25.4 / 1000);
|
||||||
header.Reserved = 0;
|
header.Reserved = 0;
|
||||||
header.PaletteInfo = 1;
|
header.PaletteInfo = 1;
|
||||||
|
|
||||||
|
auto ok = false;
|
||||||
if (img.depth() == 1) {
|
if (img.depth() == 1) {
|
||||||
writeImage1(img, s, header);
|
ok = writeImage1(img, s, header);
|
||||||
} else if (img.depth() == 8 && img.colorCount() <= 16) {
|
} else if (img.depth() == 8 && img.colorCount() <= 16) {
|
||||||
writeImage4(img, s, header);
|
ok = writeImage4(img, s, header);
|
||||||
} else if (img.depth() == 8) {
|
} else if (img.depth() == 8) {
|
||||||
writeImage8(img, s, header);
|
ok = writeImage8(img, s, header);
|
||||||
} else if (img.depth() == 32) {
|
} else if (img.depth() >= 24) {
|
||||||
writeImage24(img, s, header);
|
ok = writeImage24(img, s, header);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PCXHandler::canRead(QIODevice *device)
|
bool PCXHandler::canRead(QIODevice *device)
|
||||||
@ -739,3 +792,5 @@ QImageIOHandler *PCXPlugin::create(QIODevice *device, const QByteArray &format)
|
|||||||
handler->setFormat(format);
|
handler->setFormat(format);
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "moc_pcx_p.cpp"
|
||||||
|
@ -452,3 +452,5 @@ QImageIOHandler *SoftimagePICPlugin::create(QIODevice *device, const QByteArray
|
|||||||
handler->setFormat(format);
|
handler->setFormat(format);
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "moc_pic_p.cpp"
|
||||||
|