mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2026-09-13 12:57:23 -04:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8658355701 |
+1
-1
@@ -7,5 +7,5 @@ Dependencies:
|
||||
Options:
|
||||
test-before-installing: True
|
||||
require-passing-tests-on: ['Linux', 'FreeBSD', 'Windows']
|
||||
cmake-options: "-DKIMAGEFORMATS_JXR=ON -DKIMAGEFORMATS_HEIF=ON -DKIMAGEFORMATS_HEIF_TEST:STRING=OFF -DKIMAGEFORMATS_HEJ2_TEST:STRING=OFF -DKIMAGEFORMATS_AVCI_TEST:STRING=OFF"
|
||||
cmake-options: "-DKIMAGEFORMATS_DDS=ON -DKIMAGEFORMATS_JXR=ON -DKIMAGEFORMATS_HEIF=ON"
|
||||
per-test-timeout: 90
|
||||
|
||||
+6
-12
@@ -1,11 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(KF_VERSION "6.22.0") # handled by release scripts
|
||||
set(KF_DEP_VERSION "6.22.0") # handled by release scripts
|
||||
set(KF_VERSION "6.20.0") # handled by release scripts
|
||||
set(KF_DEP_VERSION "6.19.0") # handled by release scripts
|
||||
project(KImageFormats VERSION ${KF_VERSION})
|
||||
|
||||
include(FeatureSummary)
|
||||
find_package(ECM 6.22.0 NO_MODULE)
|
||||
find_package(ECM 6.19.0 NO_MODULE)
|
||||
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules")
|
||||
feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
@@ -21,7 +21,7 @@ include(ECMDeprecationSettings)
|
||||
include(CheckIncludeFiles)
|
||||
include(FindPkgConfig)
|
||||
|
||||
set(REQUIRED_QT_VERSION 6.8.0)
|
||||
set(REQUIRED_QT_VERSION 6.7.0)
|
||||
find_package(Qt6Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
|
||||
|
||||
find_package(KF6Archive ${KF_DEP_VERSION})
|
||||
@@ -65,12 +65,6 @@ set_package_properties(libavif PROPERTIES
|
||||
option(KIMAGEFORMATS_DDS "Enable plugin for DDS format" ON)
|
||||
|
||||
option(KIMAGEFORMATS_HEIF "Enable plugin for HEIF format" OFF)
|
||||
set(KIMAGEFORMATS_HEIF_TEST "ALL" CACHE STRING "Enable HEIF tests: OFF, READ_ONLY, ALL")
|
||||
set_property(CACHE KIMAGEFORMATS_HEIF_TEST PROPERTY STRINGS "OFF" "READ_ONLY" "ALL")
|
||||
set(KIMAGEFORMATS_HEJ2_TEST "ALL" CACHE STRING "Enable HEJ2 tests: OFF, READ_ONLY, ALL")
|
||||
set_property(CACHE KIMAGEFORMATS_HEJ2_TEST PROPERTY STRINGS "OFF" "READ_ONLY" "ALL")
|
||||
set(KIMAGEFORMATS_AVCI_TEST "ALL" CACHE STRING "Enable AVCI tests: OFF, ALL")
|
||||
set_property(CACHE KIMAGEFORMATS_AVCI_TEST PROPERTY STRINGS "OFF" "ALL")
|
||||
if(KIMAGEFORMATS_HEIF)
|
||||
pkg_check_modules(LibHeif IMPORTED_TARGET libheif>=1.10.0)
|
||||
endif()
|
||||
@@ -105,8 +99,8 @@ endif()
|
||||
add_feature_info(LibJXR LibJXR_FOUND "required for the QImage plugin for JPEG XR images")
|
||||
|
||||
ecm_set_disabled_deprecation_versions(
|
||||
QT 6.11.0
|
||||
KF 6.21.0
|
||||
QT 6.10.0
|
||||
KF 6.18.0
|
||||
)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
@@ -290,7 +290,7 @@ in your cmake options.**
|
||||
|
||||
The following defines can be defined in cmake to modify the behavior of the
|
||||
plugin:
|
||||
- `DDS_DISABLE_STRIDE_ALIGNMENT`: disable the stride alignment based on DDS
|
||||
- `DDS_DISABLE_STRIDE_ALIGNMENT`: disable the stride aligment based on DDS
|
||||
pitch: it is known that some writers do not set it correctly.
|
||||
|
||||
### The HEIF plugin
|
||||
@@ -303,15 +303,6 @@ distributions. In particular, it is necessary that the HEIF library has
|
||||
support for HEVC codec. If HEVC codec is not available the plugin
|
||||
will compile but will fail the tests.
|
||||
|
||||
**If you are interested in compiling the plugin without running the tests,
|
||||
also use the following string options:**
|
||||
- `KIMAGEFORMATS_HEIF_TEST` to change the behaviour of HEIF tests. Set to
|
||||
`"OFF"` (no test at all) or `"READ_ONLY"` (run read tests only).
|
||||
- `KIMAGEFORMATS_HEJ2_TEST` to change the behaviour of HEJ2 tests. Set to
|
||||
`"OFF"` (no test at all) or `"READ_ONLY"` (run read tests only)..
|
||||
- `KIMAGEFORMATS_AVCI_TEST` to change the behaviour of AVCI tests. Set to
|
||||
`"OFF"` (no test at all).
|
||||
|
||||
### The EXR plugin
|
||||
|
||||
The following defines can be defined in cmake to modify the behavior of the
|
||||
@@ -391,7 +382,7 @@ The following defines can be defined in cmake to modify the behavior of the
|
||||
plugin:
|
||||
- `JXR_DENY_FLOAT_IMAGE`: disables the use of float images and consequently
|
||||
any HDR data will be lost.
|
||||
- `JXR_DISABLE_DEPTH_CONVERSION`: remove the needs of additional memory by
|
||||
- `JXR_DISABLE_DEPTH_CONVERSION`: remove the neeeds of additional memory by
|
||||
disabling the conversion between different color depths (e.g. RGBA64bpp to
|
||||
RGBA32bpp) at the cost of reduced compatibility.
|
||||
- `JXR_DISABLE_BGRA_HACK`: Windows displays and opens JXR files correctly out
|
||||
@@ -423,8 +414,6 @@ PSD support has the following limitations:
|
||||
- Multichannel images are treated as CMYK if they have 2 or more channels.
|
||||
- Multichannel images are treated as Grayscale if they have 1 channel.
|
||||
- Duotone images are treated as grayscale images.
|
||||
- Grayscale images with alpha channel or at 32 bit depth are converted to
|
||||
RGBA due to the lack of the appropriate Qt grayscale container.
|
||||
- Extra channels other than alpha are discarded.
|
||||
|
||||
The following defines can be defined in cmake to modify the behavior of the
|
||||
|
||||
+19
-35
@@ -102,52 +102,36 @@ if (KF6Archive_FOUND)
|
||||
endif()
|
||||
|
||||
if (TARGET avif)
|
||||
if(${libavif_VERSION} VERSION_GREATER_EQUAL "1.2.0")
|
||||
kimageformats_read_tests(
|
||||
avif
|
||||
)
|
||||
else()
|
||||
message(WARNING "libavif ${libavif_VERSION} doesn't support decoding new AVIF files!\n"
|
||||
"AVIF read tests are disabled, consider updating libavif.")
|
||||
endif()
|
||||
|
||||
kimageformats_read_tests(
|
||||
avif
|
||||
)
|
||||
kimageformats_write_tests(
|
||||
avif-nodatacheck-lossless
|
||||
)
|
||||
endif()
|
||||
|
||||
if (LibHeif_FOUND)
|
||||
if (KIMAGEFORMATS_HEIF_TEST STREQUAL "ALL" OR KIMAGEFORMATS_HEIF_TEST STREQUAL "READ_ONLY")
|
||||
kimageformats_read_tests(
|
||||
heif
|
||||
)
|
||||
endif()
|
||||
if (KIMAGEFORMATS_HEIF_TEST STREQUAL "ALL")
|
||||
# because the plug-ins use RGB->YUV conversion which sometimes results in 1 value difference.
|
||||
kimageformats_write_tests(FUZZ 1
|
||||
heif-nodatacheck-lossless
|
||||
)
|
||||
endif()
|
||||
kimageformats_read_tests(
|
||||
heif
|
||||
)
|
||||
# because the plug-ins use RGB->YUV conversion which sometimes results in 1 value difference.
|
||||
kimageformats_write_tests(FUZZ 1
|
||||
heif-nodatacheck-lossless
|
||||
)
|
||||
|
||||
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.17.0")
|
||||
if (KIMAGEFORMATS_HEJ2_TEST STREQUAL "ALL" OR KIMAGEFORMATS_HEJ2_TEST STREQUAL "READ_ONLY")
|
||||
kimageformats_read_tests(FUZZ 1
|
||||
hej2
|
||||
)
|
||||
endif()
|
||||
if (KIMAGEFORMATS_HEJ2_TEST STREQUAL "ALL")
|
||||
kimageformats_write_tests(FUZZ 1
|
||||
hej2-nodatacheck-lossless
|
||||
)
|
||||
endif()
|
||||
kimageformats_read_tests(FUZZ 1
|
||||
hej2
|
||||
)
|
||||
kimageformats_write_tests(FUZZ 1
|
||||
hej2-nodatacheck-lossless
|
||||
)
|
||||
endif()
|
||||
|
||||
if (LibHeif_VERSION VERSION_GREATER_EQUAL "1.19.6")
|
||||
if (KIMAGEFORMATS_AVCI_TEST STREQUAL "ALL")
|
||||
kimageformats_read_tests(FUZZ 4
|
||||
avci
|
||||
)
|
||||
endif()
|
||||
kimageformats_read_tests(FUZZ 4
|
||||
avci
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ git clone --depth 1 -b v1.2.1 https://github.com/AOMediaCodec/libavif.git
|
||||
git clone --depth 1 https://github.com/strukturag/libde265.git
|
||||
git clone --depth 1 -b v2.5.3 https://github.com/uclouvain/openjpeg.git
|
||||
git clone --depth 1 https://github.com/strukturag/libheif.git
|
||||
git clone --depth=1 --recursive --shallow-submodules https://github.com/libjxl/libjxl.git
|
||||
git clone --depth=1 --branch v0.11.x --recursive --shallow-submodules https://github.com/libjxl/libjxl.git
|
||||
git clone --depth 1 https://github.com/LibRaw/LibRaw
|
||||
git clone --depth 1 https://github.com/mircomir/jxrlib.git
|
||||
git clone --depth 1 -b v2.6.0 https://github.com/cisco/openh264.git
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 116 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 132 KiB |
@@ -2,8 +2,8 @@
|
||||
{
|
||||
"fileName" : "32bit_grayscale.png",
|
||||
"colorSpace" : {
|
||||
"description" : "RGB emulation of \"Linear Grayscale Profile\"",
|
||||
"colorModel" : "Rgb",
|
||||
"description" : "Linear Grayscale Profile",
|
||||
"colorModel" : "Gray",
|
||||
"primaries" : "Custom",
|
||||
"transferFunction" : "Linear",
|
||||
"gamma" : 1
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
Binary file not shown.
@@ -1,26 +0,0 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "testcard_graya16.png",
|
||||
"colorSpace" : {
|
||||
"description" : "RGB emulation of \"Gray Gamma 2.2\"",
|
||||
"colorModel" : "Rgb",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "Gamma",
|
||||
"gamma" : 2.19922
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "ModificationDate",
|
||||
"value" : "2025-11-17T07:27:47"
|
||||
},
|
||||
{
|
||||
"key" : "Software" ,
|
||||
"value" : "Adobe Photoshop 26.11 (Windows)"
|
||||
}
|
||||
],
|
||||
"resolution" : {
|
||||
"dotsPerMeterX" : 11811,
|
||||
"dotsPerMeterY" : 11811
|
||||
}
|
||||
}
|
||||
]
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
@@ -1,26 +0,0 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "testcard_graya32.png",
|
||||
"colorSpace" : {
|
||||
"description" : "RGB emulation of \"Profilo scala di grigio lineare\"",
|
||||
"colorModel" : "Rgb",
|
||||
"primaries" : "Custom",
|
||||
"transferFunction" : "Linear",
|
||||
"gamma" : 1
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "ModificationDate",
|
||||
"value" : "2025-11-17T07:29:19"
|
||||
},
|
||||
{
|
||||
"key" : "Software" ,
|
||||
"value" : "Adobe Photoshop 26.11 (Windows)"
|
||||
}
|
||||
],
|
||||
"resolution" : {
|
||||
"dotsPerMeterX" : 11811,
|
||||
"dotsPerMeterY" : 11811
|
||||
}
|
||||
}
|
||||
]
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
Binary file not shown.
@@ -1,28 +0,0 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "testcard_graya8.png",
|
||||
"fuzziness" : 1,
|
||||
"perceptiveFuzziness" : true,
|
||||
"colorSpace" : {
|
||||
"description" : "RGB emulation of \"Gray Gamma 2.2\"",
|
||||
"colorModel" : "Rgb",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "Gamma",
|
||||
"gamma" : 2.19922
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "ModificationDate",
|
||||
"value" : "2025-11-17T07:28:50"
|
||||
},
|
||||
{
|
||||
"key" : "Software" ,
|
||||
"value" : "Adobe Photoshop 26.11 (Windows)"
|
||||
}
|
||||
],
|
||||
"resolution" : {
|
||||
"dotsPerMeterX" : 11811,
|
||||
"dotsPerMeterY" : 11811
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,6 +0,0 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "fruktpilot.png",
|
||||
"fuzziness" : 1
|
||||
}
|
||||
]
|
||||
@@ -1,6 +0,0 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "fruktpilot16_icc.png",
|
||||
"fuzziness" : 1
|
||||
}
|
||||
]
|
||||
@@ -1,6 +0,0 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "fruktpilot32_icc.png",
|
||||
"fuzziness" : 1
|
||||
}
|
||||
]
|
||||
@@ -1,6 +0,0 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "fruktpilot32fplin_icc.png",
|
||||
"fuzziness" : 1
|
||||
}
|
||||
]
|
||||
@@ -1,6 +0,0 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "fruktpilot_icc.png",
|
||||
"fuzziness" : 1
|
||||
}
|
||||
]
|
||||
+15
-14
@@ -258,26 +258,22 @@ int main(int argc, char **argv)
|
||||
});
|
||||
QTextStream(stdout) << "QImageReader::supportedImageFormats: " << formatStrings.join(", ") << "\n";
|
||||
|
||||
// checks if the format has read capability
|
||||
if (!formats.contains(format)) {
|
||||
QTextStream(stdout) << "FAIL : current configuration is missing necessary decoder(s) for " << suffix << "!\n"
|
||||
<< "********* "
|
||||
<< "Finished basic read tests for " << suffix << " images *********\n";
|
||||
return 1;
|
||||
if (format == "avci" || format == "heif" || format == "hej2") {
|
||||
QTextStream(stdout) << "WARNING : " << suffix << " is not supported with current libheif configuration!\n"
|
||||
<< "********* "
|
||||
<< "Finished basic read tests for " << suffix << " images *********\n";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const QFileInfoList lstImgDir = imgdir.entryInfoList();
|
||||
// Launch 3 runs for each test:
|
||||
// - first run on a random access device with allocation limit set to 256 MiB.
|
||||
// - second run on a random access device with allocation limit set to 0 MiB.
|
||||
// - third run on a sequential access device.
|
||||
for (int run = 0; run < 3; ++run) {
|
||||
QImageReader::setAllocationLimit(run == 1 ? 0 : 256);
|
||||
bool seq = run == 2;
|
||||
// Launch 2 runs for each test: first run on a random access device, second run on a sequential access device
|
||||
for (int seq = 0; seq < 2; ++seq) {
|
||||
if (seq) {
|
||||
QTextStream(stdout) << "* Run on SEQUENTIAL ACCESS device\n";
|
||||
} else {
|
||||
QTextStream(stdout) << "* Run on RANDOM ACCESS device (allocation limit: " << QImageReader::allocationLimit() << " MiB)\n";
|
||||
QTextStream(stdout) << "* Run on RANDOM ACCESS device\n";
|
||||
}
|
||||
for (const QFileInfo &fi : lstImgDir) {
|
||||
TemplateImage timg(fi);
|
||||
@@ -342,7 +338,12 @@ int main(int argc, char **argv)
|
||||
OptionTest optionTest;
|
||||
if (!optionTest.store(&inputReader)) {
|
||||
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": error while reading options\n";
|
||||
++failed;
|
||||
if (format == "heif") {
|
||||
// libheif + ffmpeg decoder is unable to load all HEIF files.
|
||||
++skipped;
|
||||
} else {
|
||||
++failed;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -138,11 +138,13 @@ bool TemplateImage::checkOptionaInfo(const QImage& image, QString& error) const
|
||||
return false;
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
auto clmName = QString(QMetaEnum::fromType<QColorSpace::ColorModel>().valueToKey(quint64(cs.colorModel())));
|
||||
if (clmName != clm) {
|
||||
error = QStringLiteral("ColorSpace ColorModel mismatch (current: %1, expected: %2)!").arg(clmName, clm);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Test resolution
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
|
||||
/*!
|
||||
* \brief skipSequentialDeviceTest
|
||||
* \return True if the sequential test should be skipped.
|
||||
* \return tre it the sequential test should be skipped.
|
||||
*/
|
||||
bool skipSequentialDeviceTest() const;
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+22
-9
@@ -319,9 +319,11 @@ QImage formatSourceImage(const QImage::Format &format)
|
||||
image = QImage(QStringLiteral("%1/rgba16.png").arg(folder));
|
||||
break;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
case QImage::Format_CMYK8888:
|
||||
image = QImage(QStringLiteral("%1/cmyk8.tif").arg(folder));
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
@@ -380,7 +382,12 @@ int formatTest(const QString &suffix, bool createTemplates)
|
||||
QBuffer buffer(&ba);
|
||||
auto writtenImage = QImageReader(&buffer, suffix.toLatin1()).read();
|
||||
if (writtenImage.isNull()) {
|
||||
++failed;
|
||||
if (suffix.toLatin1() == "heif") {
|
||||
// libheif + ffmpeg decoder is unable to load all HEIF files.
|
||||
++skipped;
|
||||
} else {
|
||||
++failed;
|
||||
}
|
||||
QTextStream(stdout) << "FAIL : error while reading the image " << formatName << "\n";
|
||||
continue;
|
||||
}
|
||||
@@ -634,17 +641,23 @@ int main(int argc, char **argv)
|
||||
|
||||
auto suffix = args.at(0);
|
||||
|
||||
// skip test if configuration is obviously incomplete
|
||||
// skip test if libheif configuration is obviously incomplete
|
||||
QByteArray format = suffix.toLatin1();
|
||||
const QList<QByteArray> read_formats = QImageReader::supportedImageFormats();
|
||||
if (!read_formats.contains(format)) { // checks if the format has read capability
|
||||
QTextStream(stdout) << "FAIL : current configuration is missing necessary decoder(s) for " << format << "!\n";
|
||||
return 1;
|
||||
}
|
||||
const QList<QByteArray> write_formats = QImageWriter::supportedImageFormats();
|
||||
if (!write_formats.contains(format)) { // checks if the format has write capability
|
||||
QTextStream(stdout) << "FAIL : libraries configuration is missing necessary encoder(s) for " << format << "!\n";
|
||||
return 1;
|
||||
|
||||
if (!read_formats.contains(format)) {
|
||||
if (format == "heif" || format == "hej2") {
|
||||
QTextStream(stdout) << "WARNING : libheif configuration is missing necessary decoder(s)!\n";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!write_formats.contains(format)) {
|
||||
if (format == "heif" || format == "hej2") {
|
||||
QTextStream(stdout) << "WARNING : libheif configuration is missing necessary encoder(s)!\n";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// run test
|
||||
|
||||
@@ -265,15 +265,9 @@ bool QAVIFHandler::decode_one_frame()
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t resultdepth = m_decoder->image->depth;
|
||||
if (m_decoder->image->matrixCoefficients == 16 && m_decoder->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444 && resultdepth == 10) {
|
||||
// 10-bit YCgCo-Re AVIF must be decoded to 8bit
|
||||
resultdepth = 8;
|
||||
}
|
||||
|
||||
QImage::Format resultformat;
|
||||
|
||||
if (resultdepth > 8) {
|
||||
if (m_decoder->image->depth > 8) {
|
||||
if (loadalpha) {
|
||||
resultformat = QImage::Format_RGBA64;
|
||||
} else {
|
||||
@@ -299,7 +293,9 @@ bool QAVIFHandler::decode_one_frame()
|
||||
colorspace = QColorSpace::fromIccProfile(icc_data);
|
||||
if (!colorspace.isValid()) {
|
||||
qCWarning(LOG_AVIFPLUGIN, "AVIF image has Qt-unsupported or invalid ICC profile!");
|
||||
} else {
|
||||
}
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
|
||||
else {
|
||||
if (colorspace.colorModel() == QColorSpace::ColorModel::Cmyk) {
|
||||
qCWarning(LOG_AVIFPLUGIN, "CMYK ICC profile is not extected for AVIF, discarding the ICCprofile!");
|
||||
colorspace = QColorSpace();
|
||||
@@ -329,6 +325,7 @@ bool QAVIFHandler::decode_one_frame()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
float prim[8] = {0.64f, 0.33f, 0.3f, 0.6f, 0.15f, 0.06f, 0.3127f, 0.329f};
|
||||
// outPrimaries: rX, rY, gX, gY, bX, bY, wX, wY
|
||||
@@ -363,12 +360,14 @@ bool QAVIFHandler::decode_one_frame()
|
||||
case 13:
|
||||
q_trc = QColorSpace::TransferFunction::SRgb;
|
||||
break;
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
|
||||
case 16: /* AVIF_TRANSFER_CHARACTERISTICS_PQ */
|
||||
q_trc = QColorSpace::TransferFunction::St2084;
|
||||
break;
|
||||
case 18: /* AVIF_TRANSFER_CHARACTERISTICS_HLG */
|
||||
q_trc = QColorSpace::TransferFunction::Hlg;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
qCWarning(LOG_AVIFPLUGIN, "CICP colorPrimaries: %d, transferCharacteristics: %d\nThe colorspace is unsupported by this plug-in yet.",
|
||||
m_decoder->image->colorPrimaries,
|
||||
@@ -378,9 +377,11 @@ bool QAVIFHandler::decode_one_frame()
|
||||
}
|
||||
|
||||
if (q_trc != QColorSpace::TransferFunction::Custom) { // we create new colorspace using Qt
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
|
||||
if (loadgray) {
|
||||
colorspace = QColorSpace(whitePoint, q_trc, q_trc_gamma);
|
||||
} else {
|
||||
#endif
|
||||
switch (m_decoder->image->colorPrimaries) {
|
||||
/* AVIF_COLOR_PRIMARIES_BT709 */
|
||||
case 0:
|
||||
@@ -396,7 +397,9 @@ bool QAVIFHandler::decode_one_frame()
|
||||
colorspace = QColorSpace(whitePoint, redPoint, greenPoint, bluePoint, q_trc, q_trc_gamma);
|
||||
break;
|
||||
}
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!colorspace.isValid()) {
|
||||
@@ -411,7 +414,7 @@ bool QAVIFHandler::decode_one_frame()
|
||||
rgb.maxThreads = m_decoder->maxThreads;
|
||||
#endif
|
||||
|
||||
if (resultdepth > 8) {
|
||||
if (m_decoder->image->depth > 8) {
|
||||
rgb.depth = 16;
|
||||
rgb.format = AVIF_RGB_FORMAT_RGBA;
|
||||
|
||||
@@ -744,12 +747,14 @@ bool QAVIFHandler::write(const QImage &image)
|
||||
/* AVIF_TRANSFER_CHARACTERISTICS_SRGB */
|
||||
avif->transferCharacteristics = (avifTransferCharacteristics)13;
|
||||
break;
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
|
||||
case QColorSpace::TransferFunction::St2084:
|
||||
avif->transferCharacteristics = (avifTransferCharacteristics)16;
|
||||
break;
|
||||
case QColorSpace::TransferFunction::Hlg:
|
||||
avif->transferCharacteristics = (avifTransferCharacteristics)18;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
|
||||
break;
|
||||
@@ -794,6 +799,7 @@ bool QAVIFHandler::write(const QImage &image)
|
||||
}
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
QImage tmpcolorimage;
|
||||
auto cs = image.colorSpace();
|
||||
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) {
|
||||
@@ -808,6 +814,9 @@ bool QAVIFHandler::write(const QImage &image)
|
||||
} else {
|
||||
tmpcolorimage = image.convertToFormat(tmpformat);
|
||||
}
|
||||
#else
|
||||
QImage tmpcolorimage = image.convertToFormat(tmpformat);
|
||||
#endif
|
||||
|
||||
avifPixelFormat pixel_format = AVIF_PIXEL_FORMAT_YUV420;
|
||||
if (m_quality >= KIMG_AVIF_QUALITY_HIGH) {
|
||||
@@ -867,12 +876,14 @@ bool QAVIFHandler::write(const QImage &image)
|
||||
/* AVIF_TRANSFER_CHARACTERISTICS_SRGB */
|
||||
transfer_to_save = (avifTransferCharacteristics)13;
|
||||
break;
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
|
||||
case QColorSpace::TransferFunction::St2084:
|
||||
transfer_to_save = (avifTransferCharacteristics)16;
|
||||
break;
|
||||
case QColorSpace::TransferFunction::Hlg:
|
||||
transfer_to_save = (avifTransferCharacteristics)18;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED */
|
||||
transfer_to_save = (avifTransferCharacteristics)2;
|
||||
@@ -908,12 +919,14 @@ bool QAVIFHandler::write(const QImage &image)
|
||||
case 5: // AVIF_TRANSFER_CHARACTERISTICS_BT470BG
|
||||
tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, 2.8f));
|
||||
break;
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
|
||||
case 16:
|
||||
tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, QColorSpace::TransferFunction::St2084));
|
||||
break;
|
||||
case 18:
|
||||
tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, QColorSpace::TransferFunction::Hlg));
|
||||
break;
|
||||
#endif
|
||||
default: // AVIF_TRANSFER_CHARACTERISTICS_SRGB + any other
|
||||
tmpcolorimage.convertToColorSpace(QColorSpace(QColorSpace::Primaries::SRgb, QColorSpace::TransferFunction::SRgb));
|
||||
transfer_to_save = (avifTransferCharacteristics)13;
|
||||
|
||||
+48
-50
@@ -235,11 +235,6 @@ void IFFChunk::setRecursionCounter(qint32 cnt)
|
||||
_recursionCnt = cnt;
|
||||
}
|
||||
|
||||
quint32 IFFChunk::dataBytes() const
|
||||
{
|
||||
return std::min(bytes(), quint32(data().size()));
|
||||
}
|
||||
|
||||
IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *parent)
|
||||
{
|
||||
auto tmp = false;
|
||||
@@ -385,7 +380,7 @@ BMHDChunk::BMHDChunk() : IFFChunk()
|
||||
|
||||
bool BMHDChunk::isValid() const
|
||||
{
|
||||
if (dataBytes() < 20) {
|
||||
if (bytes() < 20) {
|
||||
return false;
|
||||
}
|
||||
return chunkId() == BMHDChunk::defaultChunkId();
|
||||
@@ -526,7 +521,7 @@ qint32 CMAPChunk::count() const
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return dataBytes() / 3;
|
||||
return bytes() / 3;
|
||||
}
|
||||
|
||||
QList<QRgb> CMAPChunk::palette(bool halfbride) const
|
||||
@@ -550,8 +545,12 @@ bool CMAPChunk::innerReadStructure(QIODevice *d)
|
||||
QList<QRgb> CMAPChunk::innerPalette() const
|
||||
{
|
||||
QList<QRgb> l;
|
||||
auto &&d = data();
|
||||
for (qint32 i = 0, n = count(); i < n; ++i) {
|
||||
const QByteArray &d = data();
|
||||
const qint32 n = count();
|
||||
if (n * 3 > d.size()) {
|
||||
return {};
|
||||
}
|
||||
for (qint32 i = 0; i < n; ++i) {
|
||||
auto i3 = i * 3;
|
||||
l << qRgb(d.at(i3), d.at(i3 + 1), d.at(i3 + 2));
|
||||
}
|
||||
@@ -583,7 +582,7 @@ qint32 CMYKChunk::count() const
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return dataBytes() / 4;
|
||||
return bytes() / 4;
|
||||
}
|
||||
|
||||
QList<QRgb> CMYKChunk::innerPalette() const
|
||||
@@ -617,7 +616,7 @@ CAMGChunk::CAMGChunk() : IFFChunk()
|
||||
|
||||
bool CAMGChunk::isValid() const
|
||||
{
|
||||
if (dataBytes() != 4) {
|
||||
if (bytes() != 4) {
|
||||
return false;
|
||||
}
|
||||
return chunkId() == CAMGChunk::defaultChunkId();
|
||||
@@ -659,7 +658,7 @@ bool DPIChunk::isValid() const
|
||||
|
||||
quint16 DPIChunk::dpiX() const
|
||||
{
|
||||
if (dataBytes() < 4) {
|
||||
if (bytes() < 4) {
|
||||
return 0;
|
||||
}
|
||||
return ui16(data().at(1), data().at(0));
|
||||
@@ -667,7 +666,7 @@ quint16 DPIChunk::dpiX() const
|
||||
|
||||
quint16 DPIChunk::dpiY() const
|
||||
{
|
||||
if (dataBytes() < 4) {
|
||||
if (bytes() < 4) {
|
||||
return 0;
|
||||
}
|
||||
return ui16(data().at(3), data().at(2));
|
||||
@@ -711,7 +710,7 @@ bool XBMIChunk::isValid() const
|
||||
|
||||
quint16 XBMIChunk::dpiX() const
|
||||
{
|
||||
if (dataBytes() < 6) {
|
||||
if (bytes() < 6) {
|
||||
return 0;
|
||||
}
|
||||
return ui16(data().at(3), data().at(2));
|
||||
@@ -719,7 +718,7 @@ quint16 XBMIChunk::dpiX() const
|
||||
|
||||
quint16 XBMIChunk::dpiY() const
|
||||
{
|
||||
if (dataBytes() < 6) {
|
||||
if (bytes() < 6) {
|
||||
return 0;
|
||||
}
|
||||
return ui16(data().at(5), data().at(4));
|
||||
@@ -727,7 +726,7 @@ quint16 XBMIChunk::dpiY() const
|
||||
|
||||
XBMIChunk::PictureType XBMIChunk::pictureType() const
|
||||
{
|
||||
if (dataBytes() < 6) {
|
||||
if (bytes() < 6) {
|
||||
return PictureType(-1);
|
||||
}
|
||||
return PictureType(i16(data().at(1), data().at(0)));
|
||||
@@ -1019,7 +1018,7 @@ QByteArray BODYChunk::deinterleave(const QByteArray &planes, qint32 y, const BMH
|
||||
auto pal = cmap->palette();
|
||||
if (ipal) {
|
||||
auto tmp = ipal->palette(y);
|
||||
if (!tmp.isEmpty())
|
||||
if (tmp.size() == pal.size())
|
||||
pal = tmp;
|
||||
}
|
||||
// HAM 6: 2 control bits+4 bits of data, 16-color palette
|
||||
@@ -1603,7 +1602,7 @@ TBHDChunk::TBHDChunk()
|
||||
|
||||
bool TBHDChunk::isValid() const
|
||||
{
|
||||
if (dataBytes() != 24 && dataBytes() != 32) {
|
||||
if (bytes() != 24 && bytes() != 32) {
|
||||
return false;
|
||||
}
|
||||
return chunkId() == TBHDChunk::defaultChunkId();
|
||||
@@ -1637,7 +1636,7 @@ QSize TBHDChunk::size() const
|
||||
|
||||
qint32 TBHDChunk::left() const
|
||||
{
|
||||
if (dataBytes() != 32) {
|
||||
if (bytes() != 32) {
|
||||
return 0;
|
||||
}
|
||||
return i32(data().at(27), data().at(26), data().at(25), data().at(24));
|
||||
@@ -1645,7 +1644,7 @@ qint32 TBHDChunk::left() const
|
||||
|
||||
qint32 TBHDChunk::top() const
|
||||
{
|
||||
if (dataBytes() != 32) {
|
||||
if (bytes() != 32) {
|
||||
return 0;
|
||||
}
|
||||
return i32(data().at(31), data().at(30), data().at(29), data().at(28));
|
||||
@@ -2419,12 +2418,10 @@ QList<QRgb> BEAMChunk::palette(qint32 y) const
|
||||
for (auto c = 0; c < col; ++c) {
|
||||
// 2 bytes per color (0x0R 0xGB)
|
||||
auto idx = bpp * y + c * 2;
|
||||
if (idx + 1 < dt.size()) {
|
||||
auto r = quint8(dt[idx] & 0x0F);
|
||||
auto g = quint8(dt[idx + 1] & 0xF0);
|
||||
auto b = quint8(dt[idx + 1] & 0x0F);
|
||||
pal << qRgb(r | (r << 4), (g >> 4) | g, b | (b << 4));
|
||||
}
|
||||
auto r = quint8(dt[idx] & 0x0F);
|
||||
auto g = quint8(dt[idx + 1] & 0xF0);
|
||||
auto b = quint8(dt[idx + 1] & 0x0F);
|
||||
pal << qRgb(r | (r << 4), (g >> 4) | g, b | (b << 4));
|
||||
}
|
||||
return pal;
|
||||
}
|
||||
@@ -2473,7 +2470,7 @@ SHAMChunk::SHAMChunk()
|
||||
|
||||
bool SHAMChunk::isValid() const
|
||||
{
|
||||
if (dataBytes() < 2) {
|
||||
if (bytes() < 2) {
|
||||
return false;
|
||||
}
|
||||
auto &&dt = data();
|
||||
@@ -2512,12 +2509,10 @@ QList<QRgb> SHAMChunk::palette(qint32 y) const
|
||||
for (auto c = 0, col = bpp / 2, idx0 = y / div * bpp + 2; c < col; ++c) {
|
||||
// 2 bytes per color (0x0R 0xGB)
|
||||
auto idx = idx0 + c * 2;
|
||||
if (idx + 1 < dt.size()) {
|
||||
auto r = quint8(dt[idx] & 0x0F);
|
||||
auto g = quint8(dt[idx + 1] & 0xF0);
|
||||
auto b = quint8(dt[idx + 1] & 0x0F);
|
||||
pal << qRgb(r | (r << 4), (g >> 4) | g, b | (b << 4));
|
||||
}
|
||||
auto r = quint8(dt[idx] & 0x0F);
|
||||
auto g = quint8(dt[idx + 1] & 0xF0);
|
||||
auto b = quint8(dt[idx + 1] & 0x0F);
|
||||
pal << qRgb(r | (r << 4), (g >> 4) | g, b | (b << 4));
|
||||
}
|
||||
return pal;
|
||||
}
|
||||
@@ -2574,18 +2569,16 @@ QList<QRgb> RASTChunk::palette(qint32 y) const
|
||||
QList<QRgb> pal;
|
||||
for (auto c = 0; c < col; ++c) {
|
||||
auto idx = bpp * y + 2 + c * 2;
|
||||
if (idx + 1 < dt.size()) {
|
||||
// The Atari ST uses 3 bits per color (512 colors) while the Atari STE
|
||||
// uses 4 bits per color (4096 colors). This strange encoding with the
|
||||
// least significant bit set as MSB is, I believe, to ensure hardware
|
||||
// compatibility between the two machines.
|
||||
#define H1L(a) ((quint8(a) & 0x7) << 1) | ((quint8(a) >> 3) & 1)
|
||||
auto r = H1L(dt[idx]);
|
||||
auto g = H1L(dt[idx + 1] >> 4);
|
||||
auto b = H1L(dt[idx + 1]);
|
||||
#undef H1L
|
||||
pal << qRgb(r | (r << 4), (g << 4) | g, b | (b << 4));
|
||||
}
|
||||
// The Atari ST uses 3 bits per color (512 colors) while the Atari STE
|
||||
// uses 4 bits per color (4096 colors). This strange encoding with the
|
||||
// least significant bit set as MSB is, I believe, to ensure hardware
|
||||
// compatibility between the two machines.
|
||||
#define H1L(a) ((quint8(a) & 0x7) << 1) | ((quint8(a) >> 3) & 1)
|
||||
auto r = H1L(dt[idx]);
|
||||
auto g = H1L(dt[idx + 1] >> 4);
|
||||
auto b = H1L(dt[idx + 1]);
|
||||
#undef H1L
|
||||
pal << qRgb(r | (r << 4), (g << 4) | g, b | (b << 4));
|
||||
}
|
||||
return pal;
|
||||
}
|
||||
@@ -2693,7 +2686,7 @@ bool PCHGChunk::hasAlpha() const
|
||||
|
||||
bool PCHGChunk::isValid() const
|
||||
{
|
||||
if (dataBytes() < 20) {
|
||||
if (bytes() < 20) {
|
||||
return false;
|
||||
}
|
||||
return chunkId() == PCHGChunk::defaultChunkId();
|
||||
@@ -2795,6 +2788,10 @@ static QByteArray pchgFastDecomp(const QByteArray& input, int treeSize, int orig
|
||||
return {};
|
||||
}
|
||||
|
||||
QByteArray out;
|
||||
out.resize(originalSize);
|
||||
char* outPtr = out.data();
|
||||
|
||||
// Emulate a3 pointer to words:
|
||||
// a2 points to the *last word* => word index (0..treeWords-1)
|
||||
auto resetA3 = [&]() {
|
||||
@@ -2819,9 +2816,10 @@ static QByteArray pchgFastDecomp(const QByteArray& input, int treeSize, int orig
|
||||
return true;
|
||||
};
|
||||
|
||||
int produced = 0;
|
||||
|
||||
// Main decode loop: produce exactly originalSize bytes
|
||||
QByteArray out;
|
||||
while (out.size() < qsizetype(originalSize)) {
|
||||
while (produced < originalSize) {
|
||||
if (bits == 0) {
|
||||
if (!refill()) {
|
||||
// Not enough bits to complete output
|
||||
@@ -2857,7 +2855,7 @@ static QByteArray pchgFastDecomp(const QByteArray& input, int treeSize, int orig
|
||||
a3_word = next;
|
||||
} else {
|
||||
// Leaf: emit low 8 bits, reset a3
|
||||
out.append(static_cast<char>(w & 0xFF));
|
||||
outPtr[produced++] = static_cast<char>(w & 0xFF);
|
||||
a3_word = resetA3();
|
||||
}
|
||||
} else {
|
||||
@@ -2877,7 +2875,7 @@ static QByteArray pchgFastDecomp(const QByteArray& input, int treeSize, int orig
|
||||
|
||||
// Non-negative: check bit #8; if set -> leaf
|
||||
if ((w & 0x0100) != 0) {
|
||||
out.append(static_cast<char>(w & 0xFF));
|
||||
outPtr[produced++] = static_cast<char>(w & 0xFF);
|
||||
a3_word = resetA3();
|
||||
} else {
|
||||
// Not a leaf: continue scanning
|
||||
|
||||
@@ -36,7 +36,7 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
|
||||
#define LIST_CHUNK QByteArray("LIST")
|
||||
#define PROP_CHUNK QByteArray("PROP")
|
||||
|
||||
// Main chunks (Maya)
|
||||
// Main chuncks (Maya)
|
||||
#define CAT4_CHUNK QByteArray("CAT4") // 4 byte alignment
|
||||
#define FOR4_CHUNK QByteArray("FOR4")
|
||||
#define LIS4_CHUNK QByteArray("LIS4")
|
||||
@@ -145,7 +145,7 @@ public:
|
||||
|
||||
/*!
|
||||
* \brief bytes
|
||||
* \return The size (in bytes) of the chunk data.
|
||||
* \return The size (in bytes) of the chunck data.
|
||||
*/
|
||||
|
||||
quint32 bytes() const;
|
||||
@@ -350,12 +350,6 @@ protected:
|
||||
|
||||
static ChunkList innerFromDevice(QIODevice *d, bool *ok, IFFChunk *parent = nullptr);
|
||||
|
||||
/*!
|
||||
* \brief dataBytes
|
||||
* \return Maximum usable cache data size.
|
||||
*/
|
||||
quint32 dataBytes() const;
|
||||
|
||||
private:
|
||||
char _chunkId[4];
|
||||
|
||||
@@ -870,7 +864,7 @@ public:
|
||||
/*!
|
||||
* \brief transformation
|
||||
* \return The image transformation.
|
||||
* \note The Default implementation returns the transformation of EXIF chunk (if any).
|
||||
* \note The Default implentation returns the trasformation of EXIF chunk (if any).
|
||||
*/
|
||||
virtual QImageIOHandler::Transformation transformation() const;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <cmath>
|
||||
|
||||
#ifndef DDS_DISABLE_STRIDE_ALIGNMENT
|
||||
// Disable the stride alignment based on DDS pitch: it is known that some writers do not set it correctly
|
||||
// Disable the stride aligment based on DDS pitch: it is known that some writers do not set it correctly
|
||||
// #define DDS_DISABLE_STRIDE_ALIGNMENT
|
||||
#endif
|
||||
|
||||
@@ -2120,8 +2120,10 @@ bool writeL8(const QImage &outImage, QDataStream &s)
|
||||
}
|
||||
|
||||
ScanLineConverter slc(QImage::Format_Grayscale8);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
if(outImage.colorSpace().isValid())
|
||||
slc.setTargetColorSpace(QColorSpace(QPointF(0.3127, 0.3291), QColorSpace::TransferFunction::SRgb));
|
||||
#endif
|
||||
|
||||
for (int y = 0, h = outImage.height(); y < h; ++y) {
|
||||
const quint8 *scanLine = reinterpret_cast<const quint8*>(slc.convertedScanLine(outImage, y));
|
||||
|
||||
@@ -179,7 +179,7 @@ bool EPSHandler::read(QImage *image)
|
||||
|
||||
const QString gsExec = QStandardPaths::findExecutable(QStringLiteral("gs"));
|
||||
if (gsExec.isEmpty()) {
|
||||
qCWarning(EPSPLUGIN) << "Couldn't find gs executable (from GhostScript) in PATH.";
|
||||
qCWarning(EPSPLUGIN) << "Couldn't find gs exectuable (from GhostScript) in PATH.";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -404,7 +404,6 @@ bool EXRHandler::read(QImage *outImage)
|
||||
|
||||
Imf::Array2D<Imf::Rgba> pixels;
|
||||
pixels.resizeErase(EXR_LINES_PER_BLOCK, width);
|
||||
std::memset(pixels[0], 0, sizeof(Imf::Rgba) * EXR_LINES_PER_BLOCK * width);
|
||||
bool isRgba = image.hasAlphaChannel();
|
||||
|
||||
for (int y = 0, n = 0; y < height; y += n) {
|
||||
@@ -609,13 +608,13 @@ bool EXRHandler::write(const QImage &image)
|
||||
pixels.resizeErase(EXR_LINES_PER_BLOCK, width);
|
||||
|
||||
// convert the image and write into the stream
|
||||
auto convFormat = image.hasAlphaChannel() ? QImage::Format_RGBA32FPx4 : QImage::Format_RGBX32FPx4;
|
||||
auto convFormat = image.hasAlphaChannel() ? QImage::Format_RGBA16FPx4 : QImage::Format_RGBX16FPx4;
|
||||
ScanLineConverter slc(convFormat);
|
||||
slc.setDefaultSourceColorSpace(QColorSpace(QColorSpace::SRgb));
|
||||
slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgbLinear));
|
||||
for (int y = 0, n = 0; y < height; y += n) {
|
||||
for (n = 0; n < std::min(EXR_LINES_PER_BLOCK, height - y); ++n) {
|
||||
auto scanLine = reinterpret_cast<const float *>(slc.convertedScanLine(image, y + n));
|
||||
auto scanLine = reinterpret_cast<const qfloat16 *>(slc.convertedScanLine(image, y + n));
|
||||
if (scanLine == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -125,16 +125,14 @@ public:
|
||||
Header h;
|
||||
|
||||
int cnt = 0;
|
||||
int len = 0;
|
||||
QByteArray line(MAXLINE, char());
|
||||
int len;
|
||||
QByteArray line(MAXLINE + 1, Qt::Uninitialized);
|
||||
QByteArray format;
|
||||
|
||||
// Parse header
|
||||
do {
|
||||
len = device->readLine(line.data(), line.size());
|
||||
if (len < 0) {
|
||||
break;
|
||||
}
|
||||
len = device->readLine(line.data(), MAXLINE);
|
||||
|
||||
if (line.startsWith("FORMAT=")) {
|
||||
format = line.mid(7, len - 7).trimmed();
|
||||
}
|
||||
@@ -175,11 +173,7 @@ public:
|
||||
return h;
|
||||
}
|
||||
|
||||
len = device->readLine(line.data(), line.size());
|
||||
if (len < 0) {
|
||||
qCDebug(HDRPLUGIN) << "Invalid HDR file, error while reading the first line after the header";
|
||||
return h;
|
||||
}
|
||||
len = device->readLine(line.data(), MAXLINE);
|
||||
line.resize(len);
|
||||
|
||||
/*
|
||||
@@ -328,7 +322,8 @@ static bool LoadHDR(QDataStream &s, const Header& h, QImage &img)
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray lineArray(4 * width, char());
|
||||
QByteArray lineArray;
|
||||
lineArray.resize(4 * width);
|
||||
uchar *image = reinterpret_cast<uchar *>(lineArray.data());
|
||||
|
||||
for (int cline = 0; cline < height; cline++) {
|
||||
@@ -514,7 +509,7 @@ bool HDRHandler::canRead(QIODevice *device)
|
||||
return true;
|
||||
}
|
||||
|
||||
// allow to load official test cases: https://radsite.lbl.gov/radiance/framed.html
|
||||
// allow to load offical test cases: https://radsite.lbl.gov/radiance/framed.html
|
||||
device->startTransaction();
|
||||
auto h = HDRHandlerPrivate::readHeader(device);
|
||||
device->rollbackTransaction();
|
||||
|
||||
@@ -189,6 +189,7 @@ bool HEIFHandler::write_helper(const QImage &image)
|
||||
}
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
QImage tmpimage;
|
||||
auto cs = image.colorSpace();
|
||||
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) {
|
||||
@@ -204,6 +205,9 @@ bool HEIFHandler::write_helper(const QImage &image)
|
||||
} else {
|
||||
tmpimage = image.convertToFormat(tmpformat);
|
||||
}
|
||||
#else
|
||||
QImage tmpimage = image.convertToFormat(tmpformat);
|
||||
#endif
|
||||
|
||||
struct heif_context *context = heif_context_alloc();
|
||||
struct heif_error err;
|
||||
@@ -864,7 +868,9 @@ bool HEIFHandler::ensureDecoder()
|
||||
QColorSpace colorspace = QColorSpace::fromIccProfile(ba);
|
||||
if (!colorspace.isValid()) {
|
||||
qCWarning(LOG_HEIFPLUGIN) << "HEIC image has Qt-unsupported or invalid ICC profile!";
|
||||
} else if (colorspace.colorModel() == QColorSpace::ColorModel::Cmyk) {
|
||||
}
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
|
||||
else if (colorspace.colorModel() == QColorSpace::ColorModel::Cmyk) {
|
||||
qCWarning(LOG_HEIFPLUGIN) << "CMYK ICC profile is not expected for HEIF, discarding the ICCprofile!";
|
||||
colorspace = QColorSpace();
|
||||
} else if (colorspace.colorModel() == QColorSpace::ColorModel::Gray) {
|
||||
@@ -891,6 +897,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
m_current_image.convertTo(bit_depth > 8 ? QImage::Format_Grayscale16 : QImage::Format_Grayscale8);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
m_current_image.setColorSpace(colorspace);
|
||||
}
|
||||
} else {
|
||||
@@ -927,12 +934,14 @@ bool HEIFHandler::ensureDecoder()
|
||||
case 13:
|
||||
q_trc = QColorSpace::TransferFunction::SRgb;
|
||||
break;
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
|
||||
case 16:
|
||||
q_trc = QColorSpace::TransferFunction::St2084;
|
||||
break;
|
||||
case 18:
|
||||
q_trc = QColorSpace::TransferFunction::Hlg;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
qCWarning(LOG_HEIFPLUGIN) << "CICP color_primaries: %d, transfer_characteristics: %d\nThe colorspace is unsupported by this plug-in yet."
|
||||
<< nclx->color_primaries
|
||||
|
||||
+12
-10
@@ -73,7 +73,7 @@ static void info_callback(const char *msg, void *client_data)
|
||||
static OPJ_SIZE_T jp2_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data)
|
||||
{
|
||||
auto dev = (QIODevice*)p_user_data;
|
||||
if (dev == nullptr || dev->atEnd()) {
|
||||
if (dev == nullptr) {
|
||||
return OPJ_SIZE_T(-1);
|
||||
}
|
||||
return OPJ_SIZE_T(dev->read((char*)p_buffer, (qint64)p_nb_bytes));
|
||||
@@ -352,15 +352,11 @@ public:
|
||||
}
|
||||
|
||||
// OpenJPEG uses a shadow copy @32-bit/channel so we need to do a check
|
||||
const int allocationLimit = QImageReader::allocationLimit();
|
||||
if (allocationLimit > 0) {
|
||||
auto maxBytes = qint64(allocationLimit) * 1024 * 1024;
|
||||
auto neededBytes = qint64(width) * height * nchannels * 4;
|
||||
if (maxBytes > 0 && neededBytes > maxBytes) {
|
||||
qCCritical(LOG_JP2PLUGIN) << "Allocation limit set to" << (maxBytes / 1024 / 1024) << "MiB but" << (neededBytes / 1024 / 1024)
|
||||
<< "MiB are needed!";
|
||||
return false;
|
||||
}
|
||||
auto maxBytes = qint64(QImageReader::allocationLimit()) * 1024 * 1024;
|
||||
auto neededBytes = qint64(width) * height * nchannels * 4;
|
||||
if (maxBytes > 0 && neededBytes > maxBytes) {
|
||||
qCCritical(LOG_JP2PLUGIN) << "Allocation limit set to" << (maxBytes / 1024 / 1024) << "MiB but" << (neededBytes / 1024 / 1024) << "MiB are needed!";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -395,6 +391,7 @@ public:
|
||||
prec = 0;
|
||||
}
|
||||
auto jp2cs = m_jp2_image->color_space;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
if (jp2cs == OPJ_CLRSPC_UNKNOWN || jp2cs == OPJ_CLRSPC_UNSPECIFIED) {
|
||||
auto cs = colorSpace();
|
||||
if (cs.colorModel() == QColorSpace::ColorModel::Cmyk)
|
||||
@@ -404,6 +401,7 @@ public:
|
||||
else if (cs.colorModel() == QColorSpace::ColorModel::Gray)
|
||||
jp2cs = OPJ_CLRSPC_GRAY;
|
||||
}
|
||||
#endif
|
||||
if (jp2cs == OPJ_CLRSPC_UNKNOWN || jp2cs == OPJ_CLRSPC_UNSPECIFIED) {
|
||||
if (m_jp2_image->numcomps == 1)
|
||||
jp2cs = OPJ_CLRSPC_GRAY;
|
||||
@@ -433,8 +431,10 @@ public:
|
||||
}
|
||||
} else if (jp2cs == OPJ_CLRSPC_CMYK) {
|
||||
if (m_jp2_image->numcomps == 4) {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
if (prec == 8 || prec == 16)
|
||||
fmt = QImage::Format_CMYK8888;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -579,6 +579,7 @@ public:
|
||||
prec = 16;
|
||||
convFormat = QImage::Format_RGBA64;
|
||||
break;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
case QImage::Format_CMYK8888: // requires OpenJPEG 2.5.3+
|
||||
if (opjVersion() >= QT_VERSION_CHECK(2, 5, 3)) {
|
||||
ncomp = 4;
|
||||
@@ -587,6 +588,7 @@ public:
|
||||
} else {
|
||||
Q_FALLTHROUGH();
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
if (image.depth() > 32) {
|
||||
qCWarning(LOG_JP2PLUGIN) << "The image is saved losing precision!";
|
||||
|
||||
@@ -26,9 +26,13 @@ Q_LOGGING_CATEGORY(LOG_JXLPLUGIN, "kf.imageformats.plugins.jxl", QtDebugMsg)
|
||||
Q_LOGGING_CATEGORY(LOG_JXLPLUGIN, "kf.imageformats.plugins.jxl", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
|
||||
// Avoid rotation on buggy Qts (see also https://bugreports.qt.io/browse/QTBUG-126575)
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 3)
|
||||
#ifndef JXL_QT_AUTOTRANSFORM
|
||||
#define JXL_QT_AUTOTRANSFORM
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef JXL_HDR_PRESERVATION_DISABLED
|
||||
// Define JXL_HDR_PRESERVATION_DISABLED to disable HDR preservation
|
||||
@@ -180,7 +184,7 @@ bool QJpegXLHandler::ensureDecoder()
|
||||
int num_worker_threads = QThread::idealThreadCount();
|
||||
if (!m_runner && num_worker_threads >= 4) {
|
||||
/* use half of the threads because plug-in is usually used in environment
|
||||
* where application performs other tasks in the background (pre-load other images) */
|
||||
* where application performs another tasks in backround (pre-load other images) */
|
||||
num_worker_threads = num_worker_threads / 2;
|
||||
num_worker_threads = qBound(2, num_worker_threads, 64);
|
||||
m_runner = JxlThreadParallelRunnerCreate(nullptr, num_worker_threads);
|
||||
@@ -410,6 +414,7 @@ bool QJpegXLHandler::countALLFrames()
|
||||
m_framedelays[0] = 0;
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
// CMYK detection
|
||||
if ((m_basicinfo.uses_original_profile == JXL_TRUE) && (m_basicinfo.num_color_channels == 3) && (m_colorspace.isValid())) {
|
||||
bool alpha_found = false;
|
||||
@@ -466,6 +471,7 @@ bool QJpegXLHandler::countALLFrames()
|
||||
qCWarning(LOG_JXLPLUGIN, "JXL has CMYK colorspace but BLACK channel was not found!");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef JXL_DECODE_BOXES_DISABLED
|
||||
if (!decodeContainer()) {
|
||||
@@ -492,6 +498,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
}
|
||||
|
||||
if (m_isCMYK) { // CMYK decoding
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
uchar *pixels_cmy = nullptr;
|
||||
uchar *pixels_black = nullptr;
|
||||
|
||||
@@ -732,6 +739,11 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
free(pixels_cmy);
|
||||
pixels_cmy = nullptr;
|
||||
}
|
||||
#else
|
||||
// CMYK not supported in older Qt
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
#endif
|
||||
} else { // RGB or GRAY
|
||||
m_current_image = imageAlloc(m_basicinfo.xsize, m_basicinfo.ysize, m_input_image_format);
|
||||
if (m_current_image.isNull()) {
|
||||
@@ -909,9 +921,11 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
}
|
||||
|
||||
bool save_cmyk = false;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
if (image.format() == QImage::Format_CMYK8888 && image.colorSpace().isValid() && image.colorSpace().colorModel() == QColorSpace::ColorModel::Cmyk) {
|
||||
save_cmyk = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
JxlEncoderStatus status;
|
||||
JxlPixelFormat pixel_format;
|
||||
@@ -922,6 +936,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
auto xmp_data = image.text(QStringLiteral(META_KEY_XMP_ADOBE)).toUtf8();
|
||||
|
||||
if (save_cmyk) { // CMYK is always lossless
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
output_info.uses_original_profile = JXL_TRUE;
|
||||
output_info.xsize = image.width();
|
||||
output_info.ysize = image.height();
|
||||
@@ -1094,6 +1109,13 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
JxlEncoderDestroy(encoder);
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
JxlEncoderDestroy(encoder);
|
||||
return false;
|
||||
#endif
|
||||
} else { // RGB or GRAY saving
|
||||
int save_depth = 8; // 8 / 16 / 32
|
||||
bool save_fp = false;
|
||||
@@ -1132,7 +1154,9 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
case QImage::Format_RGBX8888:
|
||||
case QImage::Format_RGBA8888:
|
||||
case QImage::Format_RGBA8888_Premultiplied:
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
case QImage::Format_CMYK8888:
|
||||
#endif
|
||||
save_depth = 8;
|
||||
break;
|
||||
case QImage::Format_Grayscale16:
|
||||
@@ -1227,6 +1251,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
}
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
QImage tmpimage;
|
||||
if (image.colorSpace().isValid()) {
|
||||
if (is_gray && image.colorSpace().colorModel() != QColorSpace::ColorModel::Gray) {
|
||||
@@ -1279,6 +1304,9 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
} else { // no ColorSpace or invalid
|
||||
tmpimage = image.convertToFormat(tmpformat);
|
||||
}
|
||||
#else
|
||||
QImage tmpimage = image.convertToFormat(tmpformat);
|
||||
#endif
|
||||
|
||||
output_info.xsize = tmpimage.width();
|
||||
output_info.ysize = tmpimage.height();
|
||||
@@ -1304,7 +1332,10 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
output_info.uses_original_profile = JXL_FALSE;
|
||||
|
||||
if (tmpimage.colorSpace().isValid()) {
|
||||
const QPointF whiteP = image.colorSpace().whitePoint();
|
||||
QPointF whiteP(0.3127f, 0.329f);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
whiteP = image.colorSpace().whitePoint();
|
||||
#endif
|
||||
|
||||
switch (tmpimage.colorSpace().primaries()) {
|
||||
case QColorSpace::Primaries::SRgb:
|
||||
@@ -1333,6 +1364,9 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
break;
|
||||
case QColorSpace::Primaries::ProPhotoRgb:
|
||||
color_profile.white_point = JXL_WHITE_POINT_CUSTOM;
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0)
|
||||
whiteP = QPointF(0.3457f, 0.3585f);
|
||||
#endif
|
||||
color_profile.white_point_xy[0] = whiteP.x();
|
||||
color_profile.white_point_xy[1] = whiteP.y();
|
||||
color_profile.primaries = JXL_PRIMARIES_CUSTOM;
|
||||
@@ -1343,6 +1377,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
color_profile.primaries_blue_xy[0] = 0.0366;
|
||||
color_profile.primaries_blue_xy[1] = 0.0001;
|
||||
break;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
case QColorSpace::Primaries::Bt2020:
|
||||
color_profile.white_point = JXL_WHITE_POINT_D65;
|
||||
color_profile.primaries = JXL_PRIMARIES_2100;
|
||||
@@ -1353,6 +1388,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
color_profile.primaries_blue_xy[0] = 0.131;
|
||||
color_profile.primaries_blue_xy[1] = 0.046;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
if (is_gray && !whiteP.isNull()) {
|
||||
color_profile.white_point = JXL_WHITE_POINT_CUSTOM;
|
||||
|
||||
@@ -45,7 +45,7 @@ Q_LOGGING_CATEGORY(LOG_JXRPLUGIN, "kf.imageformats.plugins.jxr", QtWarningMsg)
|
||||
// #define JXR_DENY_FLOAT_IMAGE
|
||||
|
||||
/*!
|
||||
* Remove the needs of additional memory by disabling the conversion between
|
||||
* Remove the neeeds of additional memory by disabling the conversion between
|
||||
* different color depths (e.g. RGBA64bpp to RGBA32bpp).
|
||||
*
|
||||
* NOTE: Leaving deptch conversion enabled (default) ensures maximum read compatibility.
|
||||
@@ -579,7 +579,9 @@ public:
|
||||
// IMPORTANT: these values must be in exactMatchingFormat()
|
||||
// clang-format off
|
||||
auto valid = QSet<QImage::Format>()
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
<< QImage::Format_CMYK8888
|
||||
#endif
|
||||
#ifndef JXR_DENY_FLOAT_IMAGE
|
||||
<< QImage::Format_RGBA16FPx4
|
||||
<< QImage::Format_RGBX16FPx4
|
||||
@@ -669,8 +671,10 @@ public:
|
||||
wmiSCP->bVerbose = FALSE;
|
||||
if (fmt == QImage::Format_Grayscale8 || fmt == QImage::Format_Grayscale16 || fmt == QImage::Format_Mono)
|
||||
wmiSCP->cfColorFormat = Y_ONLY;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
else if (fmt == QImage::Format_CMYK8888)
|
||||
wmiSCP->cfColorFormat = CMYK;
|
||||
#endif
|
||||
else
|
||||
wmiSCP->cfColorFormat = YUV_444;
|
||||
wmiSCP->bdBitDepth = BD_LONG;
|
||||
@@ -808,8 +812,10 @@ private:
|
||||
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_RGBA32FPx4_Premultiplied, GUID_PKPixelFormat128bppPRGBAFloat)
|
||||
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_RGBX32FPx4, GUID_PKPixelFormat128bppRGBFloat)
|
||||
#endif // JXR_DENY_FLOAT_IMAGE
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_CMYK8888, GUID_PKPixelFormat32bppCMYK)
|
||||
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_CMYK8888, GUID_PKPixelFormat32bppCMYKDIRECT)
|
||||
#endif
|
||||
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_Mono, GUID_PKPixelFormatBlackWhite)
|
||||
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_Grayscale8, GUID_PKPixelFormat8bppGray)
|
||||
<< std::pair<QImage::Format, PKPixelFormatGUID>(QImage::Format_Grayscale16, GUID_PKPixelFormat16bppGray)
|
||||
|
||||
@@ -256,7 +256,7 @@ static bool checkHeader(QDataStream &ds)
|
||||
|
||||
/*!
|
||||
* \brief updatePos
|
||||
* Write the current stream position in \a pos position as uint32.
|
||||
* Write the current stram position in \a pos position as uint32.
|
||||
* \return True on success, otherwise false;
|
||||
*/
|
||||
static bool updatePos(QDataStream &ds, quint32 pos)
|
||||
@@ -510,7 +510,7 @@ static QByteArray readBytes(QDataStream &ds, quint32 count, bool asciiz)
|
||||
* \param pos The position of the IFD.
|
||||
* \param knownTags List of known and supported tags.
|
||||
* \param nextIfd The position of next IFD (0 if none).
|
||||
* \return True on success, otherwise false.
|
||||
* \return True on succes, otherwise false.
|
||||
*/
|
||||
static bool readIfd(QDataStream &ds, MicroExif::Tags &tags, quint32 pos = 0, const KnownTags &knownTags = staticTagTypes, quint32 *nextIfd = nullptr)
|
||||
{
|
||||
|
||||
@@ -125,7 +125,7 @@ public:
|
||||
|
||||
/*!
|
||||
* \brief transformation
|
||||
* \return The orientation converted in the equivalent Qt transformation.
|
||||
* \return The orientation converted in the equvalent Qt transformation.
|
||||
* \sa orientation
|
||||
*/
|
||||
QImageIOHandler::Transformation transformation() const;
|
||||
|
||||
@@ -781,11 +781,13 @@ static bool writeImage24(const QImage &image, QDataStream &s, PCXHEADER &header)
|
||||
|
||||
auto tcs = QColorSpace();
|
||||
auto tfmt = image.format();
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
auto cs = image.colorSpace();
|
||||
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) {
|
||||
tcs = QColorSpace(QColorSpace::SRgb);
|
||||
tfmt = QImage::Format_RGB32;
|
||||
}
|
||||
#endif
|
||||
if (tfmt != QImage::Format_ARGB32 && tfmt != QImage::Format_RGB32) {
|
||||
tfmt = hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32;
|
||||
}
|
||||
|
||||
@@ -275,11 +275,13 @@ bool SoftimagePICHandler::write(const QImage &image)
|
||||
bool alpha = image.hasAlphaChannel();
|
||||
auto tcs = QColorSpace();
|
||||
auto tfmt = image.format();
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
auto cs = image.colorSpace();
|
||||
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) {
|
||||
tcs = QColorSpace(QColorSpace::SRgb);
|
||||
tfmt = QImage::Format_RGB32;
|
||||
}
|
||||
#endif
|
||||
if (tfmt != QImage::Format_ARGB32 && tfmt != QImage::Format_RGB32) {
|
||||
tfmt = alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32;
|
||||
}
|
||||
|
||||
+28
-97
@@ -91,7 +91,7 @@ typedef quint8 uchar;
|
||||
namespace // Private.
|
||||
{
|
||||
|
||||
#if defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED)
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0) || defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED)
|
||||
# define CMYK_FORMAT QImage::Format_Invalid
|
||||
#else
|
||||
# define CMYK_FORMAT QImage::Format_CMYK8888
|
||||
@@ -327,18 +327,11 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
|
||||
*ok = true;
|
||||
|
||||
// Section size
|
||||
quint32 tmpSize;
|
||||
s >> tmpSize;
|
||||
qint64 sectioSize = tmpSize;
|
||||
qint32 sectioSize;
|
||||
s >> sectioSize;
|
||||
|
||||
// Reading Image resource block
|
||||
for (auto size = sectioSize; size > 0;) {
|
||||
|
||||
#define DEC_SIZE(value) \
|
||||
if ((size -= qint64(value)) < 0) { \
|
||||
*ok = false; \
|
||||
break; }
|
||||
|
||||
// Length Description
|
||||
// -------------------------------------------------------------------
|
||||
// 4 Signature: '8BIM'
|
||||
@@ -353,7 +346,7 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
|
||||
|
||||
quint32 signature;
|
||||
s >> signature;
|
||||
DEC_SIZE(sizeof(signature))
|
||||
size -= sizeof(signature);
|
||||
// NOTE: MeSa signature is not documented but found in some old PSD take from Photoshop 7.0 CD.
|
||||
if (signature != S_8BIM && signature != S_MeSa) { // 8BIM and MeSa
|
||||
qCDebug(LOG_PSDPLUGIN) << "Invalid Image Resource Block Signature!";
|
||||
@@ -364,7 +357,7 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
|
||||
// id
|
||||
quint16 id;
|
||||
s >> id;
|
||||
DEC_SIZE(sizeof(id))
|
||||
size -= sizeof(id);
|
||||
|
||||
// getting data
|
||||
PSDImageResourceBlock irb;
|
||||
@@ -372,25 +365,20 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
|
||||
// name
|
||||
qint32 bytes = 0;
|
||||
irb.name = readPascalString(s, 2, &bytes);
|
||||
DEC_SIZE(bytes)
|
||||
size -= bytes;
|
||||
|
||||
// data read
|
||||
quint32 dataSize;
|
||||
s >> dataSize;
|
||||
DEC_SIZE(sizeof(dataSize))
|
||||
if (auto dev = s.device()) {
|
||||
if (dataSize > size) {
|
||||
qCDebug(LOG_PSDPLUGIN) << "Invalid Image Resource Block Data Size!";
|
||||
*ok = false;
|
||||
break;
|
||||
}
|
||||
irb.data = deviceRead(dev, dataSize);
|
||||
}
|
||||
size -= sizeof(dataSize);
|
||||
// NOTE: Qt device::read() and QDataStream::readRawData() could read less data than specified.
|
||||
// The read code should be improved.
|
||||
if (auto dev = s.device())
|
||||
irb.data = dev->read(dataSize);
|
||||
auto read = irb.data.size();
|
||||
if (read > 0) {
|
||||
DEC_SIZE(read)
|
||||
}
|
||||
if (read != qint64(dataSize)) {
|
||||
if (read > 0)
|
||||
size -= read;
|
||||
if (quint32(read) != dataSize) {
|
||||
qCDebug(LOG_PSDPLUGIN) << "Image Resource Block Read Error!";
|
||||
*ok = false;
|
||||
break;
|
||||
@@ -398,15 +386,12 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
|
||||
|
||||
if (auto pad = dataSize % 2) {
|
||||
auto skipped = s.skipRawData(pad);
|
||||
if (skipped > 0) {
|
||||
DEC_SIZE(skipped);
|
||||
}
|
||||
if (skipped > 0)
|
||||
size -= skipped;
|
||||
}
|
||||
|
||||
// insert IRB
|
||||
irs.insert(id, irb);
|
||||
|
||||
#undef DEC_SIZE
|
||||
}
|
||||
|
||||
return irs;
|
||||
@@ -504,13 +489,7 @@ PSDColorModeDataSection readColorModeDataSection(QDataStream &s, bool *ok = null
|
||||
|
||||
qint32 size;
|
||||
s >> size;
|
||||
if (size < 0) {
|
||||
*ok = false;
|
||||
} else if (size > 8 * 1024 * 1024) {
|
||||
// The known color sections are all in the order of a few hundred bytes.
|
||||
// I skip the ones that are too big, I don't know what to do with them.
|
||||
*ok = s.skipRawData(size) == size;
|
||||
} else if (size != 768) { // read the duotone data (524 bytes)
|
||||
if (size != 768) { // read the duotone data (524 bytes)
|
||||
// NOTE: A RGB/Gray float image has a 112 bytes ColorModeData that could be
|
||||
// the "32-bit Toning Options" of Photoshop (starts with 'hdrt').
|
||||
// Official Adobe specification tells "Only indexed color and duotone
|
||||
@@ -546,21 +525,8 @@ static bool setColorSpace(QImage &img, const PSDImageResourceSection &irs)
|
||||
auto cs = QColorSpace::fromIccProfile(irb.data);
|
||||
if (!cs.isValid())
|
||||
return false;
|
||||
|
||||
if (cs.colorModel() == QColorSpace::ColorModel::Gray && img.pixelFormat().colorModel() != QPixelFormat::Grayscale) {
|
||||
// I created an RGB from a grayscale without using color profile conversion (fast).
|
||||
// I'll try to create an RGB profile that looks the same.
|
||||
if (cs.transferFunction() != QColorSpace::TransferFunction::Custom) {
|
||||
auto tmp = QColorSpace(QColorSpace::Primaries::SRgb, cs.transferFunction(), cs.gamma());
|
||||
tmp.setWhitePoint(cs.whitePoint());
|
||||
tmp.setDescription(QStringLiteral("RGB emulation of \"%1\"").arg(cs.description()));
|
||||
if (tmp.isValid())
|
||||
cs = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
img.setColorSpace(cs);
|
||||
return img.colorSpace().isValid();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -640,7 +606,7 @@ static bool setResolution(QImage &img, const PSDImageResourceSection &irs)
|
||||
|
||||
s.skipRawData(4); // Display data (not used here)
|
||||
|
||||
s >> i32; // Vertical resolution in pixels per inch.
|
||||
s >> i32; // Vertial resolution in pixels per inch.
|
||||
if (i32 <= 0)
|
||||
return false;
|
||||
auto vres = dpi2ppm(fixedPointToDouble(i32));
|
||||
@@ -732,7 +698,7 @@ static bool IsValid(const PSDHeader &header)
|
||||
qCDebug(LOG_PSDPLUGIN) << "PSD header: invalid number of channels" << header.channel_count;
|
||||
return false;
|
||||
}
|
||||
if (header.width > uint(std::min(300000, PSD_MAX_IMAGE_WIDTH)) || header.height > uint(std::min(300000, PSD_MAX_IMAGE_HEIGHT))) {
|
||||
if (header.width > std::min(300000, PSD_MAX_IMAGE_WIDTH) || header.height > std::min(300000, PSD_MAX_IMAGE_HEIGHT)) {
|
||||
qCDebug(LOG_PSDPLUGIN) << "PSD header: invalid image size" << header.width << "x" << header.height;
|
||||
return false;
|
||||
}
|
||||
@@ -813,14 +779,6 @@ static QImage::Format imageFormat(const PSDHeader &header, bool alpha)
|
||||
}
|
||||
break;
|
||||
case CM_GRAYSCALE:
|
||||
if (header.depth == 32) {
|
||||
format = !alpha ? QImage::Format_RGBX32FPx4 : QImage::Format_RGBA32FPx4_Premultiplied;
|
||||
} else if (header.depth == 16) {
|
||||
format = !alpha ? QImage::Format_Grayscale16 : QImage::Format_RGBA64_Premultiplied;
|
||||
} else {
|
||||
format = !alpha ? QImage::Format_Grayscale8 : QImage::Format_RGBA8888_Premultiplied;
|
||||
}
|
||||
break;
|
||||
case CM_DUOTONE:
|
||||
format = header.depth == 8 ? QImage::Format_Grayscale8 : QImage::Format_Grayscale16;
|
||||
break;
|
||||
@@ -1123,7 +1081,7 @@ bool readChannel(QByteArray &target, QDataStream &stream, quint32 compressedSize
|
||||
if (stream.readRawData(tmp.data(), tmp.size()) != tmp.size()) {
|
||||
return false;
|
||||
}
|
||||
if (packbitsDecompress(tmp.data(), tmp.size(), target.data(), target.size()) != target.size()) {
|
||||
if (packbitsDecompress(tmp.data(), tmp.size(), target.data(), target.size()) < 0) {
|
||||
return false;
|
||||
}
|
||||
} else if (stream.readRawData(target.data(), target.size()) != target.size()) {
|
||||
@@ -1330,6 +1288,7 @@ bool PSDHandler::read(QImage *image)
|
||||
}
|
||||
|
||||
auto imgChannels = imageChannels(img.format());
|
||||
auto channel_num = std::min(qint32(header.channel_count), imgChannels);
|
||||
auto raw_count = qsizetype(header.width * header.depth + 7) / 8;
|
||||
auto native_cmyk = img.format() == CMYK_FORMAT;
|
||||
|
||||
@@ -1377,13 +1336,11 @@ bool PSDHandler::read(QImage *image)
|
||||
// CMYK with spots (e.g. CMYKA) ICC conversion to RGBA/RGBX
|
||||
QImage tmpCmyk;
|
||||
ScanLineConverter iccConv(img.format());
|
||||
#if !defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED)
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) && !defined(PSD_NATIVE_CMYK_SUPPORT_DISABLED)
|
||||
if (header.color_mode == CM_CMYK && img.format() != QImage::Format_CMYK8888) {
|
||||
auto tmpi = QImage(header.width, 1, QImage::Format_CMYK8888);
|
||||
if (setColorSpace(tmpi, irs)) {
|
||||
tmpi.fill(0);
|
||||
if (setColorSpace(tmpi, irs))
|
||||
tmpCmyk = tmpi;
|
||||
}
|
||||
iccConv.setTargetColorSpace(QColorSpace(QColorSpace::SRgb));
|
||||
}
|
||||
#endif
|
||||
@@ -1437,14 +1394,6 @@ bool PSDHandler::read(QImage *image)
|
||||
else if (header.depth == 32)
|
||||
premulConversion<float>(scanLine, header.width, 3, header.channel_count, PremulConversion::PS2P);
|
||||
}
|
||||
if (header.color_mode == CM_GRAYSCALE) {
|
||||
if (header.depth == 8)
|
||||
premulConversion<quint8>(scanLine, header.width, 1, header.channel_count, PremulConversion::PS2P);
|
||||
else if (header.depth == 16)
|
||||
premulConversion<quint16>(scanLine, header.width, 1, header.channel_count, PremulConversion::PS2P);
|
||||
else if (header.depth == 32)
|
||||
premulConversion<float>(scanLine, header.width, 1, header.channel_count, PremulConversion::PS2P);
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion to RGB
|
||||
@@ -1482,21 +1431,9 @@ bool PSDHandler::read(QImage *image)
|
||||
else if (header.depth == 32)
|
||||
rawChannelsCopy<float>(img.scanLine(y), imgChannels, psdScanline.data(), header.channel_count, header.width);
|
||||
}
|
||||
if (header.color_mode == CM_GRAYSCALE) {
|
||||
for (auto c = 0; c < imgChannels; ++c) { // GRAYA to RGBA
|
||||
auto sc = qBound(0, c - 2, int(header.channel_count));
|
||||
if (header.depth == 8)
|
||||
rawChannelCopy<quint8>(img.scanLine(y), imgChannels, c, psdScanline.data(), header.channel_count, sc, header.width);
|
||||
else if (header.depth == 16)
|
||||
rawChannelCopy<quint16>(img.scanLine(y), imgChannels, c, psdScanline.data(), header.channel_count, sc, header.width);
|
||||
else if (header.depth == 32)
|
||||
rawChannelCopy<float>(img.scanLine(y), imgChannels, c, psdScanline.data(), header.channel_count, sc, header.width);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Linear read (no position jumps): optimized code usable only for the colorspaces supported by QImage
|
||||
auto channel_num = std::min(qint32(header.channel_count), header.color_mode == CM_GRAYSCALE ? 1 : imgChannels);
|
||||
for (qint32 c = 0; c < channel_num; ++c) {
|
||||
for (qint32 y = 0, h = header.height; y < h; ++y) {
|
||||
auto&& strideSize = strides.at(c * qsizetype(h) + y);
|
||||
@@ -1525,13 +1462,8 @@ bool PSDHandler::read(QImage *image)
|
||||
// 32-bits float images: RGB/RGBA
|
||||
planarToChunchy<float>(scanLine, rawStride.data(), header.width, c, imgChannels);
|
||||
} else if (header.depth == 32 && header.color_mode == CM_GRAYSCALE) {
|
||||
if (imgChannels >= 3) { // GRAY to RGB
|
||||
planarToChunchy<float>(scanLine, rawStride.data(), header.width, 0, imgChannels);
|
||||
planarToChunchy<float>(scanLine, rawStride.data(), header.width, 1, imgChannels);
|
||||
planarToChunchy<float>(scanLine, rawStride.data(), header.width, 2, imgChannels);
|
||||
} else { // 32-bits float images: Grayscale (converted to equivalent integer 16-bits)
|
||||
planarToChunchyFloatToUInt16<float>(scanLine, rawStride.data(), header.width, c, imgChannels);
|
||||
}
|
||||
// 32-bits float images: Grayscale (coverted to equivalent integer 16-bits)
|
||||
planarToChunchyFloatToUInt16<float>(scanLine, rawStride.data(), header.width, c, imgChannels);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1556,11 +1488,13 @@ bool PSDHandler::read(QImage *image)
|
||||
if (header.color_mode == CM_RGB && header.depth == 32) {
|
||||
img.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
if (header.color_mode == CM_GRAYSCALE && header.depth == 32) {
|
||||
auto qs = QColorSpace(QPointF(0.3127, 0.3291), QColorSpace::TransferFunction::Linear);
|
||||
qs.setDescription(QStringLiteral("Linear grayscale"));
|
||||
img.setColorSpace(qs);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// XMP data
|
||||
@@ -1667,9 +1601,6 @@ bool PSDHandler::canRead(QIODevice *device)
|
||||
if (header.color_mode == CM_RGB && header.channel_count > 3) {
|
||||
return false; // supposing extra channel as alpha
|
||||
}
|
||||
if (header.color_mode == CM_GRAYSCALE && (header.channel_count > 1 || header.depth == 32)) {
|
||||
return false; // supposing extra channel as alpha
|
||||
}
|
||||
}
|
||||
|
||||
return IsSupported(header);
|
||||
|
||||
@@ -160,7 +160,7 @@ static bool LoadQOI(QIODevice *device, const QoiHeader &qoi, QImage &img)
|
||||
// Handle the byte stream
|
||||
QByteArray ba;
|
||||
for (quint32 y = 0, run = 0; y < qoi.Height; ++y) {
|
||||
if (quint64(ba.size()) < px_len && !device->atEnd()) {
|
||||
if (quint64(ba.size()) < px_len) {
|
||||
ba.append(device->read(px_len));
|
||||
}
|
||||
|
||||
|
||||
@@ -610,7 +610,7 @@ void setParams(QImageIOHandler *handler, LibRaw *rawProcessor)
|
||||
* \brief use_auto_wb
|
||||
* Average the whole image for white balance (0 - off, 1 - on)
|
||||
*
|
||||
* This is useful if no camera white balance is available.
|
||||
* This is usefull if no camera white balance is available.
|
||||
*/
|
||||
params.use_auto_wb = T_AW(quality);
|
||||
|
||||
@@ -841,9 +841,11 @@ bool LoadRAW(QImageIOHandler *handler, QImage &img)
|
||||
if (params.output_color == 7) {
|
||||
img.setColorSpace(QColorSpace(QColorSpace::DisplayP3));
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
if (params.output_color == 8) {
|
||||
img.setColorSpace(QColorSpace(QColorSpace::Bt2020));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// *** Set the metadata
|
||||
|
||||
@@ -49,7 +49,7 @@ private:
|
||||
* _: reserved (should be zero)
|
||||
* I: interpolation quality (0 - linear, 1 - VNG, 2 - PPG, 3 - AHD, 4 - DCB, 11 - DHT, 12 - AAHD)
|
||||
* C: output colorspace (0 - raw, 1 - sRGB, 2 - Adobe, 3 - Wide, 4 - ProPhoto, 5 - XYZ, 6 - ACES, 7 - DCI-P3, 8 - Rec2020)
|
||||
* W: use camera white balance (0 - off, 1 - on)
|
||||
* W: use camera white balace (0 - off, 1 - on)
|
||||
* A: use auto white balance (0 - off, 1 - on)
|
||||
* B: output bit per sample (0 - 8-bits, 1 - 16-bits)
|
||||
* H: half size image (0 - off, 1 - on)
|
||||
|
||||
@@ -338,9 +338,6 @@ bool SGIImagePrivate::readImage(QImage &img)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (; l < _numrows; l++) {
|
||||
_lengthtab[l] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (_stream.status() != QDataStream::Ok) {
|
||||
@@ -769,11 +766,15 @@ bool SGIImagePrivate::writeImage(const QImage &image)
|
||||
|
||||
auto tcs = QColorSpace();
|
||||
auto tfmt = image.format();
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
auto cs = image.colorSpace();
|
||||
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) {
|
||||
tcs = QColorSpace(QColorSpace::SRgb);
|
||||
tfmt = QImage::Format_RGB32;
|
||||
} else if (hasAlpha && tfmt != QImage::Format_ARGB32) {
|
||||
#else
|
||||
if (hasAlpha && tfmt != QImage::Format_ARGB32) {
|
||||
#endif
|
||||
tfmt = QImage::Format_ARGB32;
|
||||
} else if (!hasAlpha && tfmt != QImage::Format_RGB32) {
|
||||
tfmt = QImage::Format_RGB32;
|
||||
|
||||
@@ -72,12 +72,18 @@ const uchar *ScanLineConverter::convertedScanLine(const QImage &image, qint32 y)
|
||||
if (!cs.isValid()) {
|
||||
cs = _defaultColorSpace;
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
if (tmp.depth() < 8 && cs.colorModel() == QColorSpace::ColorModel::Gray) {
|
||||
tmp.convertTo(QImage::Format_Grayscale8);
|
||||
}
|
||||
else if (tmp.depth() < 24 && cs.colorModel() == QColorSpace::ColorModel::Rgb) {
|
||||
tmp.convertTo(tmp.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32);
|
||||
}
|
||||
#else
|
||||
if (tmp.depth() < 24) {
|
||||
tmp.convertTo(tmp.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32);
|
||||
}
|
||||
#endif
|
||||
tmp.setColorSpace(cs);
|
||||
tmp.convertToColorSpace(_colorSpace);
|
||||
}
|
||||
|
||||
@@ -177,10 +177,12 @@ public:
|
||||
QImage::Format format() const
|
||||
{
|
||||
auto format = QImage::Format_Invalid;
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
if (m_pb.colorCount() == 4) {
|
||||
if (m_pb.bitMask() == 15)
|
||||
format = QImage::Format_CMYK8888;
|
||||
}
|
||||
#endif
|
||||
if (m_pb.colorCount() == 3) {
|
||||
if (m_pb.bitMask() == 7)
|
||||
format = QImage::Format_RGB888;
|
||||
|
||||
@@ -82,13 +82,6 @@ enum TGAType {
|
||||
#define TGA_XMPP_TAGID 0x7002 // Xmp packet preceded by "xMPP" string
|
||||
#define TGA_ICCP_TAGID 0x7003 // Icc profile preceded by "iCCP" string
|
||||
|
||||
/*
|
||||
* Maximum size of a tag in the developer area.
|
||||
*
|
||||
* TGA is a 32-bit format so, a metadata should not be greater than some MB.
|
||||
*/
|
||||
#define DEV_TAG_MAX_SIZE (32 * 1024 * 1024)
|
||||
|
||||
/** Tga Header. */
|
||||
struct TgaHeader {
|
||||
uchar id_length = 0;
|
||||
@@ -617,7 +610,11 @@ static bool LoadTGA(QIODevice *dev, const TgaHeader &tga, QImage &img)
|
||||
// Read palette.
|
||||
if (info.pal) {
|
||||
QList<QRgb> colorTable;
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0)
|
||||
colorTable.resize(tga.colormap_length);
|
||||
#else
|
||||
colorTable.resizeForOverwrite(tga.colormap_length);
|
||||
#endif
|
||||
|
||||
if (tga.colormap_size == 32) {
|
||||
char data[4]; // BGRA
|
||||
@@ -992,11 +989,15 @@ bool TGAHandler::writeRGBA(const QImage &image)
|
||||
auto format = image.format();
|
||||
const bool hasAlpha = image.hasAlphaChannel();
|
||||
auto tcs = QColorSpace();
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
auto cs = image.colorSpace();
|
||||
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) {
|
||||
format = QImage::Format_RGB32;
|
||||
tcs = QColorSpace(QColorSpace::SRgb);
|
||||
} else if (hasAlpha && image.format() != QImage::Format_ARGB32) {
|
||||
#else
|
||||
if (hasAlpha && image.format() != QImage::Format_ARGB32) {
|
||||
#endif
|
||||
format = QImage::Format_ARGB32;
|
||||
} else if (!hasAlpha && image.format() != QImage::Format_RGB32) {
|
||||
format = QImage::Format_RGB32;
|
||||
@@ -1239,10 +1240,6 @@ bool TGAHandler::readMetadata(QImage &image)
|
||||
if (!dev->seek(f.offset)) {
|
||||
return false;
|
||||
}
|
||||
if (f.size > DEV_TAG_MAX_SIZE) {
|
||||
qCWarning(LOG_TGAPLUGIN) << "readMetadata: the size of TAG" << f.tagId << "is larger than" << (DEV_TAG_MAX_SIZE/1024/1024) << "MiB, so it will be ignored";
|
||||
continue;
|
||||
}
|
||||
if (f.tagId == TGA_EXIF_TAGID) {
|
||||
auto ba = dev->read(f.size);
|
||||
if (ba.startsWith(QByteArray("eXif"))) {
|
||||
@@ -1347,7 +1344,7 @@ bool TGAHandler::canRead(QIODevice *device)
|
||||
|
||||
TgaHeader tga;
|
||||
if (!peekHeader(device, tga)) {
|
||||
qCDebug(LOG_TGAPLUGIN) << "TGAHandler::canRead() error while reading the header";
|
||||
qCWarning(LOG_TGAPLUGIN) << "TGAHandler::canRead() error while reading the header";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
#include <QImage>
|
||||
#include <QImageIOHandler>
|
||||
#include <QIODevice>
|
||||
|
||||
// Default maximum width and height for the large image plugins.
|
||||
#ifndef KIF_LARGE_IMAGE_PIXEL_LIMIT
|
||||
@@ -133,48 +132,4 @@ inline float fppm2dpi(qint32 ppm, bool *ok = nullptr)
|
||||
return ppm2dpi_T<float>(ppm, ok);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief deviceRead
|
||||
* A function for reading from devices.
|
||||
*
|
||||
* Similar to QIODevice::read(qint64) but limits the initial memory allocation. Useful for reading corrupted streams.
|
||||
* \param d The device.
|
||||
* \param maxSize The maximum size to read.
|
||||
* \return The byte array read.
|
||||
*/
|
||||
static QByteArray deviceRead(QIODevice *d, qint64 maxSize)
|
||||
{
|
||||
if (d == nullptr) {
|
||||
return{};
|
||||
}
|
||||
|
||||
const qint64 blockSize = 32 * 1024 * 1024;
|
||||
auto devSize = d->isSequential() ? qint64() : d->size();
|
||||
|
||||
if (devSize > 0) {
|
||||
// random access device
|
||||
maxSize = std::min(maxSize, devSize - d->pos());
|
||||
return d->read(maxSize);
|
||||
} else if (maxSize < blockSize) {
|
||||
// small read
|
||||
return d->read(maxSize);
|
||||
}
|
||||
|
||||
// sequential device
|
||||
QByteArray ba;
|
||||
while (ba.size() < maxSize) {
|
||||
auto toRead = std::min(blockSize, maxSize - ba.size());
|
||||
if (toRead + ba.size() > QByteArray::maxSize()) {
|
||||
break;
|
||||
}
|
||||
auto tmp = d->read(toRead);
|
||||
if (tmp.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
ba.append(tmp);
|
||||
}
|
||||
|
||||
return ba;
|
||||
}
|
||||
|
||||
#endif // UTIL_P_H
|
||||
|
||||
+12
-42
@@ -1379,23 +1379,20 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
|
||||
// SANITY CHECK: Avoid to load XCF with a layer grater than 10 times the final image
|
||||
if (qint64(layer.width) * layer.height / 10 > qint64(xcf_image.header.width) * xcf_image.header.height) {
|
||||
if (qint64(layer.width) * layer.height > 16384 * 16384) { // large layers only
|
||||
qCWarning(XCFPLUGIN) << "Heuristic sanity check: the image may be corrupted!";
|
||||
qCWarning(XCFPLUGIN) << "Euristic sanity check: the image may be corrupted!";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef XCF_QT5_SUPPORT
|
||||
// Qt 6 image allocation limit calculation: we have to check the limit here because the image is split in
|
||||
// Qt 6 image allocation limit calculation: we have to check the limit here because the image is splitted in
|
||||
// tiles of 64x64 pixels. The required memory to build the image is at least doubled because tiles are loaded
|
||||
// and then the final image is created by copying the tiles inside it.
|
||||
// NOTE: on Windows to open a 10GiB image the plugin uses 28GiB of RAM
|
||||
const qint64 channels = 1 + (layer.type == RGB_GIMAGE ? 2 : 0) + (layer.type == RGBA_GIMAGE ? 3 : 0);
|
||||
const int allocationLimit = QImageReader::allocationLimit();
|
||||
if (allocationLimit > 0) {
|
||||
if (qint64(layer.width) * qint64(layer.height) * channels * 2ll / 1024ll / 1024ll > allocationLimit) {
|
||||
qCDebug(XCFPLUGIN) << "Rejecting image as it exceeds the current allocation limit of" << allocationLimit << "megabytes";
|
||||
return false;
|
||||
}
|
||||
qint64 channels = 1 + (layer.type == RGB_GIMAGE ? 2 : 0) + (layer.type == RGBA_GIMAGE ? 3 : 0);
|
||||
if (qint64(layer.width) * qint64(layer.height) * channels * 2ll / 1024ll / 1024ll > QImageReader::allocationLimit()) {
|
||||
qCDebug(XCFPLUGIN) << "Rejecting image as it exceeds the current allocation limit of" << QImageReader::allocationLimit() << "megabytes";
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1440,7 +1437,6 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
|
||||
return false;
|
||||
}
|
||||
layer.image_tiles[j][i].setColorCount(0);
|
||||
layer.image_tiles[j][i].fill(0);
|
||||
break;
|
||||
|
||||
case GRAY_GIMAGE:
|
||||
@@ -1449,17 +1445,15 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
|
||||
return false;
|
||||
}
|
||||
layer.image_tiles[j][i].setColorCount(256);
|
||||
layer.image_tiles[j][i].fill(0);
|
||||
setGrayPalette(layer.image_tiles[j][i]);
|
||||
break;
|
||||
|
||||
case GRAYA_GIMAGE:
|
||||
layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
|
||||
layer.image_tiles[j][i].setColorCount(256);
|
||||
if (layer.image_tiles[j][i].isNull()) {
|
||||
return false;
|
||||
}
|
||||
layer.image_tiles[j][i].setColorCount(256);
|
||||
layer.image_tiles[j][i].fill(0);
|
||||
setGrayPalette(layer.image_tiles[j][i]);
|
||||
|
||||
layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
|
||||
@@ -1467,17 +1461,15 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
|
||||
return false;
|
||||
}
|
||||
layer.alpha_tiles[j][i].setColorCount(256);
|
||||
layer.alpha_tiles[j][i].fill(0);
|
||||
setGrayPalette(layer.alpha_tiles[j][i]);
|
||||
break;
|
||||
|
||||
case INDEXED_GIMAGE:
|
||||
layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
|
||||
layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
|
||||
if (layer.image_tiles[j][i].isNull()) {
|
||||
return false;
|
||||
}
|
||||
layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
|
||||
layer.image_tiles[j][i].fill(0);
|
||||
setPalette(xcf_image, layer.image_tiles[j][i]);
|
||||
break;
|
||||
|
||||
@@ -1487,7 +1479,6 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
|
||||
return false;
|
||||
}
|
||||
layer.image_tiles[j][i].setColorCount(xcf_image.num_colors);
|
||||
layer.image_tiles[j][i].fill(0);
|
||||
setPalette(xcf_image, layer.image_tiles[j][i]);
|
||||
|
||||
layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
|
||||
@@ -1495,7 +1486,6 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
|
||||
return false;
|
||||
}
|
||||
layer.alpha_tiles[j][i].setColorCount(256);
|
||||
layer.alpha_tiles[j][i].fill(0);
|
||||
setGrayPalette(layer.alpha_tiles[j][i]);
|
||||
}
|
||||
if (layer.type != GRAYA_GIMAGE && layer.image_tiles[j][i].format() != format) {
|
||||
@@ -1533,11 +1523,10 @@ bool XCFImageFormat::composeTiles(XCFImage &xcf_image)
|
||||
#endif
|
||||
if (layer.mask_offset != 0) {
|
||||
layer.mask_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
|
||||
layer.mask_tiles[j][i].setColorCount(256);
|
||||
if (layer.mask_tiles[j][i].isNull()) {
|
||||
return false;
|
||||
}
|
||||
layer.mask_tiles[j][i].setColorCount(256);
|
||||
layer.mask_tiles[j][i].fill(0);
|
||||
setGrayPalette(layer.mask_tiles[j][i]);
|
||||
}
|
||||
}
|
||||
@@ -1812,25 +1801,14 @@ bool XCFImageFormat::assignImageBytes(Layer &layer, uint i, uint j, const GimpPr
|
||||
dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
|
||||
dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
|
||||
dataPtr[x + 3] = qfloat16(1);
|
||||
if (!dataPtr[x + 0].isFinite() || !dataPtr[x + 1].isFinite() || !dataPtr[x + 2].isFinite()) {
|
||||
if (dataPtr[x + 0].isNaN() || dataPtr[x + 1].isNaN() || dataPtr[x + 2].isNaN()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case QImage::Format_RGBA16FPx4:
|
||||
for (int y = 0; y < height; y++) {
|
||||
const size_t bpl = width * sizeof(QRgbaFloat16);
|
||||
qFromBigEndian<qint16>(tile + y * bpl, width * 4, image.scanLine(y));
|
||||
|
||||
const qfloat16 *dataPtr = reinterpret_cast<qfloat16 *>(image.scanLine(y));
|
||||
for (int x = 0; x < width * 4; ++x) {
|
||||
if (!dataPtr[x].isFinite()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
static_assert(sizeof(QRgbaFloat16) == sizeof(QRgba64), "Different sizes for float and int 16 bit pixels");
|
||||
#endif
|
||||
case QImage::Format_RGBA64:
|
||||
for (int y = 0; y < height; y++) {
|
||||
@@ -1843,13 +1821,6 @@ bool XCFImageFormat::assignImageBytes(Layer &layer, uint i, uint j, const GimpPr
|
||||
for (int y = 0; y < height; y++) {
|
||||
const size_t bpl = width * sizeof(QRgbaFloat32);
|
||||
qFromBigEndian<qint32>(tile + y * bpl, width * 4, image.scanLine(y));
|
||||
|
||||
const float *dataPtr = reinterpret_cast<float *>(image.scanLine(y));
|
||||
for (int x = 0; x < width * 4; ++x) {
|
||||
if (!std::isfinite(dataPtr[x])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case QImage::Format_RGBX32FPx4:
|
||||
@@ -1861,7 +1832,7 @@ bool XCFImageFormat::assignImageBytes(Layer &layer, uint i, uint j, const GimpPr
|
||||
dataPtr[x + 1] = qFromBigEndian(src[x + 1]);
|
||||
dataPtr[x + 2] = qFromBigEndian(src[x + 2]);
|
||||
dataPtr[x + 3] = 1.f;
|
||||
if (!std::isfinite(dataPtr[x + 0]) || !std::isfinite(dataPtr[x + 1]) || !std::isfinite(dataPtr[x + 2])) {
|
||||
if (std::isnan(dataPtr[x + 0]) || std::isnan(dataPtr[x + 1]) || std::isnan(dataPtr[x + 2])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2074,7 +2045,6 @@ bool XCFImageFormat::loadLevel(QDataStream &xcf_io, Layer &layer, qint32 bpp, co
|
||||
QList<uchar> buffer;
|
||||
if (needConvert) {
|
||||
buffer.resize(blockSize * (bpp == 2 ? 2 : 1));
|
||||
buffer.fill(uchar());
|
||||
}
|
||||
for (uint j = 0; j < layer.nrows; j++) {
|
||||
for (uint i = 0; i < layer.ncols; i++) {
|
||||
|
||||
Reference in New Issue
Block a user