mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2026-09-14 05:07:00 -04:00
Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8658355701 | ||
|
|
3c8539d53d | ||
|
|
d332ad717b | ||
|
|
1ca7baed98 | ||
|
|
f1b0c9f0ec | ||
|
|
b83f4c0231 | ||
|
|
a457e5ddcb | ||
|
|
f28cd98661 | ||
|
|
05c3a1afe6 | ||
|
|
fda751c641 | ||
|
|
a4e18734bd | ||
|
|
14286a6ab0 | ||
|
|
945fd6f0ce | ||
|
|
c36b4e2350 | ||
|
|
95f0d15e14 | ||
|
|
56c8bc7323 | ||
|
|
08e178f098 | ||
|
|
6881e3111b | ||
|
|
463da81fad | ||
|
|
8036b1d032 | ||
|
|
71cc137254 |
@@ -12,6 +12,7 @@ include:
|
||||
- /gitlab-templates/windows-qt6.yml
|
||||
- /gitlab-templates/xml-lint.yml
|
||||
- /gitlab-templates/yaml-lint.yml
|
||||
- /gitlab-templates/oss-fuzz.yml
|
||||
|
||||
image_json_validate:
|
||||
stage: validate
|
||||
|
||||
+4
-4
@@ -1,11 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(KF_VERSION "6.18.0") # handled by release scripts
|
||||
set(KF_DEP_VERSION "6.18.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.18.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)
|
||||
|
||||
@@ -100,7 +100,7 @@ add_feature_info(LibJXR LibJXR_FOUND "required for the QImage plugin for JPEG XR
|
||||
|
||||
ecm_set_disabled_deprecation_versions(
|
||||
QT 6.10.0
|
||||
KF 6.17.0
|
||||
KF 6.18.0
|
||||
)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
@@ -229,7 +229,7 @@ Below are the maximum sizes for each plugin ('n/a' means no limit, i.e. the
|
||||
limit depends on the format encoding).
|
||||
- ANI: n/a
|
||||
- AVIF: 32,768 x 32,768 pixels, in any case no larger than 256 megapixels
|
||||
- DDS: n/a
|
||||
- DDS: 300,000 x 300,000 pixels
|
||||
- EXR: 300,000 x 300,000 pixels
|
||||
- EPS: same size as Qt's JPG plugin
|
||||
- HDR: 300,000 x 300,000 pixels
|
||||
@@ -247,7 +247,7 @@ limit depends on the format encoding).
|
||||
- PXR: 65,535 x 65,535 pixels
|
||||
- QOI: 300,000 x 300,000 pixels
|
||||
- RAS: 300,000 x 300,000 pixels
|
||||
- RAW: n/a (depends on the RAW format loaded)
|
||||
- RAW: 65,535 x 65,535 pixels
|
||||
- RGB: 65,535 x 65,535 pixels
|
||||
- SCT: 300,000 x 300,000 pixels
|
||||
- TGA: 65,535 x 65,535 pixels
|
||||
|
||||
+8
-3
@@ -88,13 +88,17 @@ are iterated sequentially and the first object that matches the requirements
|
||||
is used for testing (successes are ignored).
|
||||
|
||||
Supported values for JSON objects:
|
||||
- `colorSpace` : Type `object`. An object with the `description` (type
|
||||
`string`), `primaries` (type `string`), `transferFunction` (type `string`),
|
||||
`colorModel` (type `string`) and `gamma` (type `double`) values of the
|
||||
image color space.
|
||||
- `comment`: Type `string`. A string shown by the test when a condition occurs.
|
||||
- `description`: Type `string`. A description of the object. Not used by the
|
||||
test.
|
||||
- `disableAutoTransform`: Type `boolean`. By default, tests are run with
|
||||
autotransform enabled (i.e. rotation is applied if the plugin supports it).
|
||||
Set to `true` to disable autotransform.
|
||||
- `filename`: Type `string`. Name of the template file to use. E.g.
|
||||
- `fileName`: Type `string`. Name of the template file to use. E.g.
|
||||
"testRGB_Qt_6_2.png".
|
||||
- `fuzziness`: Type `integer`. Set the fuzziness only if not already set on the
|
||||
command line. The value set on the command line wins over the one in the JSON
|
||||
@@ -108,8 +112,8 @@ with (if not set means all). E.g. "6.2.0".
|
||||
- `perceptiveFuzziness` Type `boolean`. Set the perceptive fuzziness only if not
|
||||
already set on the command line. The value set on the command line wins over
|
||||
the one in the JSON file.
|
||||
- `resolution`: Type `object`. An object with the `dotsPerMeterX` and
|
||||
`dotsPerMeterY` (integer) values of the image.
|
||||
- `resolution`: Type `object`. An object with the `dotsPerMeterX` (type `integer`)
|
||||
and `dotsPerMeterY` (type `integer`) values of the image.
|
||||
- `seeAlso`: Type `string`. More info about the object. Normally used to point
|
||||
to bug reports. Not used by the test.
|
||||
- `skipSequential`: Type `boolean`. Skip the test on sequential access device.
|
||||
@@ -123,6 +127,7 @@ Some examples:
|
||||
- Example 3: [Metadata](read/psd/metadata.psd.json)
|
||||
- Example 4: [Check Qt version, resolution and metadata](read/psd/mch-16bits.psd.json)
|
||||
- Example 5: [Fuzziness setting](read/xcf/birthday16.xcf.json)
|
||||
- Example 6: [Color space](read/dds/rgba_f16.dds.json)
|
||||
|
||||
These are just a few examples. More examples can be found in the test folders.
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "metadata.png",
|
||||
"colorSpace" : {
|
||||
"description" : "sRGB build-in",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "SRgb",
|
||||
"gamma" : 0
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "ModificationDate",
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
{
|
||||
"fileName" : "rgba_f16.png",
|
||||
"fuzziness" : 1,
|
||||
"description" : "Minimum fuzziness value to pass the test on all architectures."
|
||||
"description" : "Minimum fuzziness value to pass the test on all architectures.",
|
||||
"colorSpace" : {
|
||||
"description" : "Linear sRGB",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "Linear",
|
||||
"gamma" : 1
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "rgb-gimp.png",
|
||||
"colorSpace" : {
|
||||
"description" : "",
|
||||
"primaries" : "Custom",
|
||||
"transferFunction" : "Linear",
|
||||
"gamma" : 1
|
||||
},
|
||||
"resolution" : {
|
||||
"dotsPerMeterX" : 3937,
|
||||
"dotsPerMeterY" : 3937
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "orientation_all.png",
|
||||
"colorSpace" : {
|
||||
"description" : "Linear sRGB",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "Linear",
|
||||
"gamma" : 1
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "Software" ,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "orientation_all.png",
|
||||
"colorSpace" : {
|
||||
"description" : "Linear sRGB",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "Linear",
|
||||
"gamma" : 1
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "Software" ,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "orientation_all.png",
|
||||
"colorSpace" : {
|
||||
"description" : "Linear sRGB",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "Linear",
|
||||
"gamma" : 1
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "Software" ,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "orientation_all.png",
|
||||
"colorSpace" : {
|
||||
"description" : "Linear sRGB",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "Linear",
|
||||
"gamma" : 1
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "Software" ,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "orientation_all.png",
|
||||
"colorSpace" : {
|
||||
"description" : "Linear sRGB",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "Linear",
|
||||
"gamma" : 1
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "Software" ,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "orientation_all.png",
|
||||
"colorSpace" : {
|
||||
"description" : "Linear sRGB",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "Linear",
|
||||
"gamma" : 1
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "Software" ,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "orientation_all.png",
|
||||
"colorSpace" : {
|
||||
"description" : "Linear sRGB",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "Linear",
|
||||
"gamma" : 1
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "Software" ,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "orientation_all.png",
|
||||
"colorSpace" : {
|
||||
"description" : "Linear sRGB",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "Linear",
|
||||
"gamma" : 1
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "Software" ,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "metadata.png",
|
||||
"colorSpace" : {
|
||||
"description" : "sRGB build-in",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "SRgb",
|
||||
"gamma" : 0
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "ModificationDate",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "metadata.png",
|
||||
"colorSpace" : {
|
||||
"description" : "TICC ICC Profile",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "SRgb",
|
||||
"gamma" : 0
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "Author",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "gimp_exif.png",
|
||||
"colorSpace" : {
|
||||
"description" : "sRGB build-in",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "SRgb",
|
||||
"gamma" : 0
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "ModificationDate",
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
"minQtVersion" : "6.7.3",
|
||||
"disableAutoTransform": true,
|
||||
"fileName" : "orientation6_notranfs.png",
|
||||
"colorSpace" : {
|
||||
"description" : "GIMP built-in sRGB",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "SRgb",
|
||||
"gamma" : 0
|
||||
},
|
||||
"comment" : "Test with automatic transformation disabled."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
},
|
||||
{
|
||||
"minQtVersion" : "6.6.2",
|
||||
"colorSpace" : {
|
||||
"description" : "sRGB built-in",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "SRgb",
|
||||
"gamma" : 0
|
||||
},
|
||||
"fileName" : "testcard_rgba_fp16.png"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
},
|
||||
{
|
||||
"minQtVersion" : "6.6.2",
|
||||
"colorSpace" : {
|
||||
"description" : "sRGB built-in",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "SRgb",
|
||||
"gamma" : 0
|
||||
},
|
||||
"fileName" : "testcard_rgba_fp32.png"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "metadata.png",
|
||||
"colorSpace" : {
|
||||
"description" : "sRGB build-in",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "SRgb",
|
||||
"gamma" : 0
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "CreationDate",
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "orientation_all.png",
|
||||
"colorSpace" : {
|
||||
"description" : "GIMP built-in sRGB",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "SRgb",
|
||||
"gamma" : 0
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "Software" ,
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
{
|
||||
"minQtVersion" : "6.8.0",
|
||||
"fileName" : "testcard_cmyk8.tif",
|
||||
"colorSpace" : {
|
||||
"description" : "U.S. Web Coated (SWOP) v2",
|
||||
"colorModel" : "Cmyk",
|
||||
"primaries" : "Custom",
|
||||
"transferFunction" : "Custom",
|
||||
"gamma" : 0
|
||||
},
|
||||
"resolution" : {
|
||||
"dotsPerMeterX" : 3780,
|
||||
"dotsPerMeterY" : 3780
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "testcard_gray_half.png"
|
||||
"fileName" : "testcard_gray_half.png",
|
||||
"colorSpace" : {
|
||||
"description" : "Linear sRGB",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "Linear",
|
||||
"gamma" : 1
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "32bit_grayscale.png",
|
||||
"colorSpace" : {
|
||||
"description" : "Linear Grayscale Profile",
|
||||
"colorModel" : "Gray",
|
||||
"primaries" : "Custom",
|
||||
"transferFunction" : "Linear",
|
||||
"gamma" : 1
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "ModificationDate",
|
||||
"value" : "2022-03-28T12:46:04"
|
||||
},
|
||||
{
|
||||
"key" : "Software" ,
|
||||
"value" : "Adobe Photoshop 23.2 (Windows)"
|
||||
}
|
||||
],
|
||||
"resolution" : {
|
||||
"dotsPerMeterX" : 2232,
|
||||
"dotsPerMeterY" : 2232
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,7 +1,14 @@
|
||||
[
|
||||
{
|
||||
"minQtVersion" : "6.8.0",
|
||||
"fileName" : "cmyk16_testcard_qt6_8.tif"
|
||||
"fileName" : "cmyk16_testcard_qt6_8.tif",
|
||||
"colorSpace" : {
|
||||
"description" : "Coated FOGRA27 (ISO 12647-2:2004)",
|
||||
"colorModel" : "Cmyk",
|
||||
"primaries" : "Custom",
|
||||
"transferFunction" : "Custom",
|
||||
"gamma" : 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"minQtVersion" : "6.0.0",
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
[
|
||||
{
|
||||
"minQtVersion" : "6.8.0",
|
||||
"fileName" : "cmyk8_testcard_qt6_8.tif"
|
||||
"fileName" : "cmyk8_testcard_qt6_8.tif",
|
||||
"colorSpace" : {
|
||||
"description" : "Coated FOGRA27 (ISO 12647-2:2004)",
|
||||
"colorModel" : "Cmyk",
|
||||
"primaries" : "Custom",
|
||||
"transferFunction" : "Custom",
|
||||
"gamma" : 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"minQtVersion" : "6.0.0",
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "metadata.png",
|
||||
"colorSpace" : {
|
||||
"description" : "sRGB build-in",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "SRgb",
|
||||
"gamma" : 0
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "ModificationDate",
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "orientation_all.png",
|
||||
"colorSpace" : {
|
||||
"description" : "GIMP built-in sRGB",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "SRgb",
|
||||
"gamma" : 0
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "Software" ,
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "testcard_rgba.qoi",
|
||||
"colorSpace" : {
|
||||
"description" : "sRGB",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "SRgb",
|
||||
"gamma" : 2.31
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,6 +1,12 @@
|
||||
[
|
||||
{
|
||||
"fileName" : "RAW_KODAK_C330_FORMAT_NONE_YRGB.png",
|
||||
"colorSpace" : {
|
||||
"description" : "sRGB",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "SRgb",
|
||||
"gamma" : 2.31
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "Manufacturer",
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
{
|
||||
"fileName" : "extarea.png",
|
||||
"skipSequential" : true,
|
||||
"colorSpace" : {
|
||||
"description" : "sRGB build-in",
|
||||
"primaries" : "SRgb",
|
||||
"transferFunction" : "SRgb",
|
||||
"gamma" : 0
|
||||
},
|
||||
"metadata" : [
|
||||
{
|
||||
"key" : "Author",
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"seeAlso" : "https://bugreports.qt.io/browse/QTBUG-120614",
|
||||
"fuzziness" : 1,
|
||||
"resolution" : {
|
||||
"dotsPerMeterX" : 2834,
|
||||
"dotsPerMeterY" : 2834
|
||||
"dotsPerMeterX" : 2835,
|
||||
"dotsPerMeterY" : 2835
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
|
||||
#include "templateimage.h"
|
||||
|
||||
#include <QColorSpace>
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QMetaEnum>
|
||||
#include <QVersionNumber>
|
||||
|
||||
static QJsonObject searchObject(const QFileInfo& file)
|
||||
@@ -104,6 +106,47 @@ bool TemplateImage::checkOptionaInfo(const QImage& image, QString& error) const
|
||||
return true;
|
||||
}
|
||||
|
||||
// test color space (icc profile)
|
||||
auto color = obj.value("colorSpace").toObject();
|
||||
if (!color.isEmpty()) {
|
||||
auto dsc = color.value("description").toString();
|
||||
auto clm = color.value("colorModel").toString(QStringLiteral("Rgb"));
|
||||
auto prm = color.value("primaries").toString();
|
||||
auto trf = color.value("transferFunction").toString();
|
||||
auto gmm = color.value("gamma").toDouble();
|
||||
auto cs = image.colorSpace();
|
||||
|
||||
if (cs.description() != dsc) {
|
||||
error = QStringLiteral("ColorSpace Description mismatch (current: %1, expected: %2)!").arg(cs.description(), dsc);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto prmName = QString(QMetaEnum::fromType<QColorSpace::Primaries>().valueToKey(quint64(cs.primaries())));
|
||||
if (prmName != prm) {
|
||||
error = QStringLiteral("ColorSpace Primaries mismatch (current: %1, expected: %2)!").arg(prmName, prm);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto trfName = QString(QMetaEnum::fromType<QColorSpace::TransferFunction>().valueToKey(quint64(cs.transferFunction())));
|
||||
if (trfName != trf) {
|
||||
error = QStringLiteral("ColorSpace TransferFunction mismatch (current: %1, expected: %2)!").arg(trfName, trf);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (qAbs(cs.gamma() - gmm) > 0.01) {
|
||||
error = QStringLiteral("ColorSpace Gamma mismatch (current: %1, expected: %2)!").arg(cs.gamma()).arg(gmm);
|
||||
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
|
||||
auto res = obj.value("resolution").toObject();
|
||||
if (!res.isEmpty()) {
|
||||
|
||||
@@ -6,14 +6,20 @@
|
||||
|
||||
#include "ani_p.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
#include <QLoggingCategory>
|
||||
#include <QScopeGuard>
|
||||
#include <QVariant>
|
||||
#include <QtEndian>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_ANIPLUGIN, "kf.imageformats.plugins.ani", QtDebugMsg)
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(LOG_ANIPLUGIN, "kf.imageformats.plugins.ani", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
struct ChunkHeader {
|
||||
@@ -358,7 +364,7 @@ bool ANIHandler::ensureScanned() const
|
||||
|
||||
if (chunkId == "anih") {
|
||||
if (chunkSize != sizeof(AniHeader)) {
|
||||
qWarning() << "anih chunk size does not match ANIHEADER size";
|
||||
qCWarning(LOG_ANIPLUGIN) << "anih chunk size does not match ANIHEADER size";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -494,22 +500,22 @@ bool ANIHandler::ensureScanned() const
|
||||
}
|
||||
|
||||
if (m_imageCount != m_frameCount && m_imageSequence.isEmpty()) {
|
||||
qWarning("ANIHandler: 'nSteps' is not equal to 'nFrames' but no 'seq' entries were provided");
|
||||
qCWarning(LOG_ANIPLUGIN) << "ANIHandler: 'nSteps' is not equal to 'nFrames' but no 'seq' entries were provided";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_imageSequence.isEmpty() && m_imageSequence.count() != m_imageCount) {
|
||||
qWarning("ANIHandler: count of entries in 'seq' does not match 'nSteps' in anih");
|
||||
qCWarning(LOG_ANIPLUGIN) << "ANIHandler: count of entries in 'seq' does not match 'nSteps' in anih";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_displayRates.isEmpty() && m_displayRates.count() != m_imageCount) {
|
||||
qWarning("ANIHandler: count of entries in 'rate' does not match 'nSteps' in anih");
|
||||
qCWarning(LOG_ANIPLUGIN) << "ANIHandler: count of entries in 'rate' does not match 'nSteps' in anih";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_frameOffsets.isEmpty() && m_frameOffsets.count() - 1 != m_frameCount) {
|
||||
qWarning("ANIHandler: number of actual frames does not match 'nFrames' in anih");
|
||||
qCWarning(LOG_ANIPLUGIN) << "ANIHandler: number of actual frames does not match 'nFrames' in anih";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -520,7 +526,7 @@ bool ANIHandler::ensureScanned() const
|
||||
bool ANIHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qWarning("ANIHandler::canRead() called with no device");
|
||||
qCWarning(LOG_ANIPLUGIN) << "ANIHandler::canRead() called with no device";
|
||||
return false;
|
||||
}
|
||||
if (device->isSequential()) {
|
||||
|
||||
+39
-32
@@ -10,6 +10,7 @@
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <QColorSpace>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include "avif_p.h"
|
||||
#include "microexif_p.h"
|
||||
@@ -17,6 +18,12 @@
|
||||
|
||||
#include <cfloat>
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_AVIFPLUGIN, "kf.imageformats.plugins.avif", QtDebugMsg)
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(LOG_AVIFPLUGIN, "kf.imageformats.plugins.avif", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
/*
|
||||
Quality range - compression/subsampling
|
||||
100 - lossless RGB compression
|
||||
@@ -168,7 +175,7 @@ bool QAVIFHandler::ensureDecoder()
|
||||
|
||||
decodeResult = avifDecoderSetIOMemory(m_decoder, m_rawAvifData.data, m_rawAvifData.size);
|
||||
if (decodeResult != AVIF_RESULT_OK) {
|
||||
qWarning("ERROR: avifDecoderSetIOMemory failed: %s", avifResultToString(decodeResult));
|
||||
qCWarning(LOG_AVIFPLUGIN, "ERROR: avifDecoderSetIOMemory failed: %s", avifResultToString(decodeResult));
|
||||
|
||||
avifDecoderDestroy(m_decoder);
|
||||
m_decoder = nullptr;
|
||||
@@ -178,7 +185,7 @@ bool QAVIFHandler::ensureDecoder()
|
||||
|
||||
decodeResult = avifDecoderParse(m_decoder);
|
||||
if (decodeResult != AVIF_RESULT_OK) {
|
||||
qWarning("ERROR: Failed to parse input: %s", avifResultToString(decodeResult));
|
||||
qCWarning(LOG_AVIFPLUGIN, "ERROR: Failed to parse input: %s", avifResultToString(decodeResult));
|
||||
|
||||
avifDecoderDestroy(m_decoder);
|
||||
m_decoder = nullptr;
|
||||
@@ -190,19 +197,19 @@ bool QAVIFHandler::ensureDecoder()
|
||||
m_container_height = m_decoder->image->height;
|
||||
|
||||
if ((m_container_width > 65535) || (m_container_height > 65535)) {
|
||||
qWarning("AVIF image (%dx%d) is too large!", m_container_width, m_container_height);
|
||||
qCWarning(LOG_AVIFPLUGIN, "AVIF image (%dx%d) is too large!", m_container_width, m_container_height);
|
||||
m_parseState = ParseAvifError;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((m_container_width == 0) || (m_container_height == 0)) {
|
||||
qWarning("Empty image, nothing to decode");
|
||||
qCWarning(LOG_AVIFPLUGIN, "Empty image, nothing to decode");
|
||||
m_parseState = ParseAvifError;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_container_width > ((16384 * 16384) / m_container_height)) {
|
||||
qWarning("AVIF image (%dx%d) has more than 256 megapixels!", m_container_width, m_container_height);
|
||||
qCWarning(LOG_AVIFPLUGIN, "AVIF image (%dx%d) has more than 256 megapixels!", m_container_width, m_container_height);
|
||||
m_parseState = ParseAvifError;
|
||||
return false;
|
||||
}
|
||||
@@ -276,7 +283,7 @@ bool QAVIFHandler::decode_one_frame()
|
||||
|
||||
QImage result = imageAlloc(m_decoder->image->width, m_decoder->image->height, resultformat);
|
||||
if (result.isNull()) {
|
||||
qWarning("Memory cannot be allocated");
|
||||
qCWarning(LOG_AVIFPLUGIN, "Memory cannot be allocated");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -285,12 +292,12 @@ bool QAVIFHandler::decode_one_frame()
|
||||
const QByteArray icc_data(reinterpret_cast<const char *>(m_decoder->image->icc.data), m_decoder->image->icc.size);
|
||||
colorspace = QColorSpace::fromIccProfile(icc_data);
|
||||
if (!colorspace.isValid()) {
|
||||
qWarning("AVIF image has Qt-unsupported or invalid ICC profile!");
|
||||
qCWarning(LOG_AVIFPLUGIN, "AVIF image has Qt-unsupported or invalid ICC profile!");
|
||||
}
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
|
||||
else {
|
||||
if (colorspace.colorModel() == QColorSpace::ColorModel::Cmyk) {
|
||||
qWarning("CMYK ICC profile is not extected for AVIF, discarding the ICCprofile!");
|
||||
qCWarning(LOG_AVIFPLUGIN, "CMYK ICC profile is not extected for AVIF, discarding the ICCprofile!");
|
||||
colorspace = QColorSpace();
|
||||
} else if (colorspace.colorModel() == QColorSpace::ColorModel::Rgb && loadgray) {
|
||||
// Input is GRAY but ICC is RGB, we will return RGB image
|
||||
@@ -314,7 +321,7 @@ bool QAVIFHandler::decode_one_frame()
|
||||
}
|
||||
colorspace = QColorSpace(gray_whitePoint, redP, greenP, blueP, trc_new, gamma_new);
|
||||
if (!colorspace.isValid()) {
|
||||
qWarning("AVIF plugin created invalid QColorSpace!");
|
||||
qCWarning(LOG_AVIFPLUGIN, "AVIF plugin created invalid QColorSpace!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -362,7 +369,7 @@ bool QAVIFHandler::decode_one_frame()
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
qWarning("CICP colorPrimaries: %d, transferCharacteristics: %d\nThe colorspace is unsupported by this plug-in yet.",
|
||||
qCWarning(LOG_AVIFPLUGIN, "CICP colorPrimaries: %d, transferCharacteristics: %d\nThe colorspace is unsupported by this plug-in yet.",
|
||||
m_decoder->image->colorPrimaries,
|
||||
m_decoder->image->transferCharacteristics);
|
||||
q_trc = QColorSpace::TransferFunction::SRgb;
|
||||
@@ -396,7 +403,7 @@ bool QAVIFHandler::decode_one_frame()
|
||||
}
|
||||
|
||||
if (!colorspace.isValid()) {
|
||||
qWarning("AVIF plugin created invalid QColorSpace from NCLX/CICP!");
|
||||
qCWarning(LOG_AVIFPLUGIN, "AVIF plugin created invalid QColorSpace from NCLX/CICP!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,7 +446,7 @@ bool QAVIFHandler::decode_one_frame()
|
||||
|
||||
avifResult res = avifImageYUVToRGB(m_decoder->image, &rgb);
|
||||
if (res != AVIF_RESULT_OK) {
|
||||
qWarning("ERROR in avifImageYUVToRGB: %s", avifResultToString(res));
|
||||
qCWarning(LOG_AVIFPLUGIN, "ERROR in avifImageYUVToRGB: %s", avifResultToString(res));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -478,7 +485,7 @@ bool QAVIFHandler::decode_one_frame()
|
||||
}
|
||||
|
||||
else { // Zero values, we need to avoid 0 divide.
|
||||
qWarning("ERROR: Wrong values in avifCleanApertureBox");
|
||||
qCWarning(LOG_AVIFPLUGIN, "ERROR: Wrong values in avifCleanApertureBox");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -556,7 +563,7 @@ static void setMetadata(avifImage *avif, const QImage& image)
|
||||
#if AVIF_VERSION >= 1000000
|
||||
auto res = avifImageSetMetadataXMP(avif, reinterpret_cast<const uint8_t *>(xmp.constData()), xmp.size());
|
||||
if (res != AVIF_RESULT_OK) {
|
||||
qWarning("ERROR in avifImageSetMetadataXMP: %s", avifResultToString(res));
|
||||
qCWarning(LOG_AVIFPLUGIN, "ERROR in avifImageSetMetadataXMP: %s", avifResultToString(res));
|
||||
}
|
||||
#else
|
||||
avifImageSetMetadataXMP(avif, reinterpret_cast<const uint8_t *>(xmp.constData()), xmp.size());
|
||||
@@ -567,7 +574,7 @@ static void setMetadata(avifImage *avif, const QImage& image)
|
||||
#if AVIF_VERSION >= 1000000
|
||||
auto res = avifImageSetMetadataExif(avif, reinterpret_cast<const uint8_t *>(exif.constData()), exif.size());
|
||||
if (res != AVIF_RESULT_OK) {
|
||||
qWarning("ERROR in avifImageSetMetadataExif: %s", avifResultToString(res));
|
||||
qCWarning(LOG_AVIFPLUGIN, "ERROR in avifImageSetMetadataExif: %s", avifResultToString(res));
|
||||
}
|
||||
#else
|
||||
avifImageSetMetadataExif(avif, reinterpret_cast<const uint8_t *>(exif.constData()), exif.size());
|
||||
@@ -602,32 +609,32 @@ bool QAVIFHandler::read(QImage *image)
|
||||
bool QAVIFHandler::write(const QImage &image)
|
||||
{
|
||||
if (image.format() == QImage::Format_Invalid) {
|
||||
qWarning("No image data to save!");
|
||||
qCWarning(LOG_AVIFPLUGIN, "No image data to save!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
if ((image.width() > 65535) || (image.height() > 65535)) {
|
||||
qWarning("Image (%dx%d) is too large to save!", image.width(), image.height());
|
||||
qCWarning(LOG_AVIFPLUGIN, "Image (%dx%d) is too large to save!", image.width(), image.height());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (image.width() > ((16384 * 16384) / image.height())) {
|
||||
qWarning("Image (%dx%d) will not be saved because it has more than 256 megapixels!", image.width(), image.height());
|
||||
qCWarning(LOG_AVIFPLUGIN, "Image (%dx%d) will not be saved because it has more than 256 megapixels!", image.width(), image.height());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((image.width() > 32768) || (image.height() > 32768)) {
|
||||
qWarning("Image (%dx%d) has a dimension above 32768 pixels, saved AVIF may not work in other software!", image.width(), image.height());
|
||||
qCWarning(LOG_AVIFPLUGIN, "Image (%dx%d) has a dimension above 32768 pixels, saved AVIF may not work in other software!", image.width(), image.height());
|
||||
}
|
||||
} else {
|
||||
qWarning("Image has zero dimension!");
|
||||
qCWarning(LOG_AVIFPLUGIN, "Image has zero dimension!");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *encoder_name = avifCodecName(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_ENCODE);
|
||||
if (!encoder_name) {
|
||||
qWarning("Cannot save AVIF images because libavif was built without AV1 encoders!");
|
||||
qCWarning(LOG_AVIFPLUGIN, "Cannot save AVIF images because libavif was built without AV1 encoders!");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -636,7 +643,7 @@ bool QAVIFHandler::write(const QImage &image)
|
||||
if (avifCodecName(AVIF_CODEC_CHOICE_AOM, AVIF_CODEC_FLAG_CAN_ENCODE)) {
|
||||
lossless = true;
|
||||
} else {
|
||||
qWarning("You are using %s encoder. It is recommended to enable libAOM encoder in libavif to use lossless compression.", encoder_name);
|
||||
qCWarning(LOG_AVIFPLUGIN, "You are using %s encoder. It is recommended to enable libAOM encoder in libavif to use lossless compression.", encoder_name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -718,7 +725,7 @@ bool QAVIFHandler::write(const QImage &image)
|
||||
#if AVIF_VERSION >= 110000
|
||||
res = avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
|
||||
if (res != AVIF_RESULT_OK) {
|
||||
qWarning("ERROR in avifImageAllocatePlanes: %s", avifResultToString(res));
|
||||
qCWarning(LOG_AVIFPLUGIN, "ERROR in avifImageAllocatePlanes: %s", avifResultToString(res));
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
@@ -959,7 +966,7 @@ bool QAVIFHandler::write(const QImage &image)
|
||||
#if AVIF_VERSION >= 1000000
|
||||
res = avifImageSetProfileICC(avif, reinterpret_cast<const uint8_t *>(iccprofile.constData()), iccprofile.size());
|
||||
if (res != AVIF_RESULT_OK) {
|
||||
qWarning("ERROR in avifImageSetProfileICC: %s", avifResultToString(res));
|
||||
qCWarning(LOG_AVIFPLUGIN, "ERROR in avifImageSetProfileICC: %s", avifResultToString(res));
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
@@ -992,7 +999,7 @@ bool QAVIFHandler::write(const QImage &image)
|
||||
|
||||
res = avifImageRGBToYUV(avif, &rgb);
|
||||
if (res != AVIF_RESULT_OK) {
|
||||
qWarning("ERROR in avifImageRGBToYUV: %s", avifResultToString(res));
|
||||
qCWarning(LOG_AVIFPLUGIN, "ERROR in avifImageRGBToYUV: %s", avifResultToString(res));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1034,11 +1041,11 @@ bool QAVIFHandler::write(const QImage &image)
|
||||
if (status > 0) {
|
||||
return true;
|
||||
} else if (status == -1) {
|
||||
qWarning("Write error: %s", qUtf8Printable(device()->errorString()));
|
||||
qCWarning(LOG_AVIFPLUGIN, "Write error: %s", qUtf8Printable(device()->errorString()));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
qWarning("ERROR: Failed to encode: %s", avifResultToString(res));
|
||||
qCWarning(LOG_AVIFPLUGIN, "ERROR: Failed to encode: %s", avifResultToString(res));
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -1140,7 +1147,7 @@ bool QAVIFHandler::jumpToNextImage()
|
||||
if (m_decoder->imageIndex >= m_decoder->imageCount - 1) { // start from beginning
|
||||
decodeResult = avifDecoderReset(m_decoder);
|
||||
if (decodeResult != AVIF_RESULT_OK) {
|
||||
qWarning("ERROR in avifDecoderReset: %s", avifResultToString(decodeResult));
|
||||
qCWarning(LOG_AVIFPLUGIN, "ERROR in avifDecoderReset: %s", avifResultToString(decodeResult));
|
||||
m_parseState = ParseAvifError;
|
||||
return false;
|
||||
}
|
||||
@@ -1150,13 +1157,13 @@ bool QAVIFHandler::jumpToNextImage()
|
||||
decodeResult = avifDecoderNextImage(m_decoder);
|
||||
|
||||
if (decodeResult != AVIF_RESULT_OK) {
|
||||
qWarning("ERROR: Failed to decode Next image in sequence: %s", avifResultToString(decodeResult));
|
||||
qCWarning(LOG_AVIFPLUGIN, "ERROR: Failed to decode Next image in sequence: %s", avifResultToString(decodeResult));
|
||||
m_parseState = ParseAvifError;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((m_container_width != m_decoder->image->width) || (m_container_height != m_decoder->image->height)) {
|
||||
qWarning("Decoded image sequence size (%dx%d) do not match first image size (%dx%d)!",
|
||||
qCWarning(LOG_AVIFPLUGIN, "Decoded image sequence size (%dx%d) do not match first image size (%dx%d)!",
|
||||
m_decoder->image->width,
|
||||
m_decoder->image->height,
|
||||
m_container_width,
|
||||
@@ -1204,13 +1211,13 @@ bool QAVIFHandler::jumpToImage(int imageNumber)
|
||||
avifResult decodeResult = avifDecoderNthImage(m_decoder, imageNumber);
|
||||
|
||||
if (decodeResult != AVIF_RESULT_OK) {
|
||||
qWarning("ERROR: Failed to decode %d th Image in sequence: %s", imageNumber, avifResultToString(decodeResult));
|
||||
qCWarning(LOG_AVIFPLUGIN, "ERROR: Failed to decode %d th Image in sequence: %s", imageNumber, avifResultToString(decodeResult));
|
||||
m_parseState = ParseAvifError;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((m_container_width != m_decoder->image->width) || (m_container_height != m_decoder->image->height)) {
|
||||
qWarning("Decoded image sequence size (%dx%d) do not match declared container size (%dx%d)!",
|
||||
qCWarning(LOG_AVIFPLUGIN, "Decoded image sequence size (%dx%d) do not match declared container size (%dx%d)!",
|
||||
m_decoder->image->width,
|
||||
m_decoder->image->height,
|
||||
m_container_width,
|
||||
|
||||
+543
-31
@@ -7,9 +7,11 @@
|
||||
|
||||
#include "chunks_p.h"
|
||||
#include "packbits_p.h"
|
||||
#include "util_p.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QColor>
|
||||
#include <QDataStream>
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_IFFPLUGIN, "kf.imageformats.plugins.iff", QtDebugMsg)
|
||||
@@ -306,6 +308,8 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *
|
||||
chunk = QSharedPointer<IFFChunk>(new ICCPChunk());
|
||||
} else if (cid == NAME_CHUNK) {
|
||||
chunk = QSharedPointer<IFFChunk>(new NAMEChunk());
|
||||
} else if (cid == PCHG_CHUNK) {
|
||||
chunk = QSharedPointer<IFFChunk>(new PCHGChunk());
|
||||
} else if (cid == RAST_CHUNK) {
|
||||
chunk = QSharedPointer<IFFChunk>(new RASTChunk());
|
||||
} else if (cid == RGBA_CHUNK) {
|
||||
@@ -316,6 +320,8 @@ IFFChunk::ChunkList IFFChunk::innerFromDevice(QIODevice *d, bool *ok, IFFChunk *
|
||||
chunk = QSharedPointer<IFFChunk>(new TBHDChunk());
|
||||
} else if (cid == VERS_CHUNK) {
|
||||
chunk = QSharedPointer<IFFChunk>(new VERSChunk());
|
||||
} else if (cid == XBMI_CHUNK) {
|
||||
chunk = QSharedPointer<IFFChunk>(new XBMIChunk());
|
||||
} else if (cid == XMP0_CHUNK) {
|
||||
chunk = QSharedPointer<IFFChunk>(new XMP0Chunk());
|
||||
} else { // unknown chunk
|
||||
@@ -539,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));
|
||||
}
|
||||
@@ -651,7 +661,7 @@ quint16 DPIChunk::dpiX() const
|
||||
if (bytes() < 4) {
|
||||
return 0;
|
||||
}
|
||||
return i16(data().at(1), data().at(0));
|
||||
return ui16(data().at(1), data().at(0));
|
||||
}
|
||||
|
||||
quint16 DPIChunk::dpiY() const
|
||||
@@ -659,17 +669,17 @@ quint16 DPIChunk::dpiY() const
|
||||
if (bytes() < 4) {
|
||||
return 0;
|
||||
}
|
||||
return i16(data().at(3), data().at(2));
|
||||
return ui16(data().at(3), data().at(2));
|
||||
}
|
||||
|
||||
qint32 DPIChunk::dotsPerMeterX() const
|
||||
{
|
||||
return qRound(dpiX() / 25.4 * 1000);
|
||||
return dpi2ppm(dpiX());
|
||||
}
|
||||
|
||||
qint32 DPIChunk::dotsPerMeterY() const
|
||||
{
|
||||
return qRound(dpiY() / 25.4 * 1000);
|
||||
return dpi2ppm(dpiY());
|
||||
}
|
||||
|
||||
bool DPIChunk::innerReadStructure(QIODevice *d)
|
||||
@@ -677,6 +687,51 @@ bool DPIChunk::innerReadStructure(QIODevice *d)
|
||||
return cacheData(d);
|
||||
}
|
||||
|
||||
/* ******************
|
||||
* *** XBMI Chunk ***
|
||||
* ****************** */
|
||||
|
||||
XBMIChunk::~XBMIChunk()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
XBMIChunk::XBMIChunk() : DPIChunk()
|
||||
{
|
||||
}
|
||||
|
||||
bool XBMIChunk::isValid() const
|
||||
{
|
||||
if (dpiX() == 0 || dpiY() == 0) {
|
||||
return false;
|
||||
}
|
||||
return chunkId() == XBMIChunk::defaultChunkId();
|
||||
}
|
||||
|
||||
quint16 XBMIChunk::dpiX() const
|
||||
{
|
||||
if (bytes() < 6) {
|
||||
return 0;
|
||||
}
|
||||
return ui16(data().at(3), data().at(2));
|
||||
}
|
||||
|
||||
quint16 XBMIChunk::dpiY() const
|
||||
{
|
||||
if (bytes() < 6) {
|
||||
return 0;
|
||||
}
|
||||
return ui16(data().at(5), data().at(4));
|
||||
}
|
||||
|
||||
XBMIChunk::PictureType XBMIChunk::pictureType() const
|
||||
{
|
||||
if (bytes() < 6) {
|
||||
return PictureType(-1);
|
||||
}
|
||||
return PictureType(i16(data().at(1), data().at(0)));
|
||||
}
|
||||
|
||||
/* ******************
|
||||
* *** BODY Chunk ***
|
||||
* ****************** */
|
||||
@@ -884,7 +939,10 @@ quint32 BODYChunk::strideSize(const BMHDChunk *header, const QByteArray& formTyp
|
||||
}
|
||||
|
||||
// ILBM
|
||||
return header->rowLen() * header->bitplanes();
|
||||
auto sz = header->rowLen() * header->bitplanes();
|
||||
if (header->masking() == BMHDChunk::Masking::HasMask)
|
||||
sz += header->rowLen();
|
||||
return sz;
|
||||
}
|
||||
|
||||
QByteArray BODYChunk::pbm(const QByteArray &planes, qint32, const BMHDChunk *header, const CAMGChunk *, const CMAPChunk *, const IPALChunk *) const
|
||||
@@ -959,11 +1017,18 @@ QByteArray BODYChunk::deinterleave(const QByteArray &planes, qint32 y, const BMH
|
||||
ba = QByteArray(rowLen * 8 * 3, char());
|
||||
auto pal = cmap->palette();
|
||||
if (ipal) {
|
||||
auto tmp = ipal->palette(y, header->height());
|
||||
auto tmp = ipal->palette(y);
|
||||
if (tmp.size() == pal.size())
|
||||
pal = tmp;
|
||||
}
|
||||
auto max = (1 << (bitplanes - 2)) - 1;
|
||||
// HAM 6: 2 control bits+4 bits of data, 16-color palette
|
||||
//
|
||||
// HAM 8: 2 control bits+6 bits of data, 64-color palette
|
||||
//
|
||||
// HAM 5: 1 control bit (and 1 hardwired to zero)+4 bits of data
|
||||
// (red and green modify operations are unavailable)
|
||||
auto ctlbits = bitplanes > 5 ? 2 : 1;
|
||||
auto max = (1 << (bitplanes - ctlbits)) - 1;
|
||||
quint8 prev[3] = {};
|
||||
for (qint32 i = 0, cnt = 0; i < rowLen; ++i) {
|
||||
for (qint32 j = 0; j < 8; ++j, ++cnt) {
|
||||
@@ -971,11 +1036,14 @@ QByteArray BODYChunk::deinterleave(const QByteArray &planes, qint32 y, const BMH
|
||||
for (qint32 k = 0, msk = (1 << (7 - j)); k < bitplanes; ++k) {
|
||||
if ((planes.at(k * rowLen + i) & msk) == 0)
|
||||
continue;
|
||||
if (k < bitplanes - 2)
|
||||
if (k < bitplanes - ctlbits)
|
||||
idx |= 1 << k;
|
||||
else
|
||||
ctl |= 1 << (bitplanes - k - 1);
|
||||
}
|
||||
if (ctl && ctlbits == 1) {
|
||||
ctl <<= 1; // HAM 5 has only 1 control bit and the LSB is always 0
|
||||
}
|
||||
switch (ctl) {
|
||||
case 1: // red
|
||||
prev[0] = idx * 255 / max;
|
||||
@@ -1049,22 +1117,23 @@ QByteArray BODYChunk::deinterleave(const QByteArray &planes, qint32 y, const BMH
|
||||
for (qint32 i = 0; i < rowLen; ++i) {
|
||||
for (qint32 k = 0, i8 = i * 8; k < bitplanes; ++k) {
|
||||
auto v = planes.at(k * rowLen + i);
|
||||
auto msk = 1 << k;
|
||||
if (v & (1 << 7))
|
||||
ba[i8] |= 1 << k;
|
||||
ba[i8] |= msk;
|
||||
if (v & (1 << 6))
|
||||
ba[i8 + 1] |= 1 << k;
|
||||
ba[i8 + 1] |= msk;
|
||||
if (v & (1 << 5))
|
||||
ba[i8 + 2] |= 1 << k;
|
||||
ba[i8 + 2] |= msk;
|
||||
if (v & (1 << 4))
|
||||
ba[i8 + 3] |= 1 << k;
|
||||
ba[i8 + 3] |= msk;
|
||||
if (v & (1 << 3))
|
||||
ba[i8 + 4] |= 1 << k;
|
||||
ba[i8 + 4] |= msk;
|
||||
if (v & (1 << 2))
|
||||
ba[i8 + 5] |= 1 << k;
|
||||
ba[i8 + 5] |= msk;
|
||||
if (v & (1 << 1))
|
||||
ba[i8 + 6] |= 1 << k;
|
||||
ba[i8 + 6] |= msk;
|
||||
if (v & 1)
|
||||
ba[i8 + 7] |= 1 << k;
|
||||
ba[i8 + 7] |= msk;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1257,8 +1326,9 @@ QImage::Format IFOR_Chunk::optionformat() const
|
||||
{
|
||||
auto fmt = this->format();
|
||||
if (fmt == QImage::Format_Indexed8) {
|
||||
if (searchIPal())
|
||||
fmt = FORMAT_RGB_8BIT;
|
||||
if (auto ipal = searchIPal()) {
|
||||
fmt = ipal->hasAlpha() ? FORMAT_RGBA_8BIT : FORMAT_RGB_8BIT;
|
||||
}
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
@@ -1282,6 +1352,10 @@ const IPALChunk *IFOR_Chunk::searchIPal() const
|
||||
if (!rast.isEmpty()) {
|
||||
ipal = rast.first();
|
||||
}
|
||||
auto pchg = IFFChunk::searchT<PCHGChunk>(this);
|
||||
if (!pchg.isEmpty()) {
|
||||
ipal = pchg.first();
|
||||
}
|
||||
if (ipal && ipal->isValid()) {
|
||||
return ipal;
|
||||
}
|
||||
@@ -1372,11 +1446,6 @@ QImage::Format FORMChunk::format() const
|
||||
return QImage::Format_RGBA64;
|
||||
}
|
||||
if (h->bitplanes() >= 1 && h->bitplanes() <= 8) {
|
||||
if (!IFFChunk::search(PCHG_CHUNK, chunks()).isEmpty()) {
|
||||
qCDebug(LOG_IFFPLUGIN) << "FORMChunk::format(): PCHG chunk is not supported";
|
||||
return QImage::Format_Invalid;
|
||||
}
|
||||
|
||||
if (h->bitplanes() >= BITPLANES_HAM_MIN && h->bitplanes() <= BITPLANES_HAM_MAX) {
|
||||
if (modeId & CAMGChunk::ModeId::Ham)
|
||||
return FORMAT_RGB_8BIT;
|
||||
@@ -2310,7 +2379,9 @@ BEAMChunk::~BEAMChunk()
|
||||
|
||||
}
|
||||
|
||||
BEAMChunk::BEAMChunk() : IPALChunk()
|
||||
BEAMChunk::BEAMChunk()
|
||||
: IPALChunk()
|
||||
, _height()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -2320,8 +2391,20 @@ bool BEAMChunk::isValid() const
|
||||
return chunkId() == BEAMChunk::defaultChunkId();
|
||||
}
|
||||
|
||||
QList<QRgb> BEAMChunk::palette(qint32 y, qint32 height) const
|
||||
IPALChunk *BEAMChunk::clone() const
|
||||
{
|
||||
return new BEAMChunk(*this);
|
||||
}
|
||||
|
||||
bool BEAMChunk::initialize(const QList<QRgb> &, qint32 height)
|
||||
{
|
||||
_height = height;
|
||||
return true;
|
||||
}
|
||||
|
||||
QList<QRgb> BEAMChunk::palette(qint32 y) const
|
||||
{
|
||||
auto &&height = _height;
|
||||
if (height < 1) {
|
||||
return {};
|
||||
}
|
||||
@@ -2378,7 +2461,9 @@ SHAMChunk::~SHAMChunk()
|
||||
|
||||
}
|
||||
|
||||
SHAMChunk::SHAMChunk() : IPALChunk()
|
||||
SHAMChunk::SHAMChunk()
|
||||
: IPALChunk()
|
||||
, _height()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -2398,8 +2483,14 @@ bool SHAMChunk::isValid() const
|
||||
return chunkId() == SHAMChunk::defaultChunkId();
|
||||
}
|
||||
|
||||
QList<QRgb> SHAMChunk::palette(qint32 y, qint32 height) const
|
||||
IPALChunk *SHAMChunk::clone() const
|
||||
{
|
||||
return new SHAMChunk(*this);
|
||||
}
|
||||
|
||||
QList<QRgb> SHAMChunk::palette(qint32 y) const
|
||||
{
|
||||
auto && height = _height;
|
||||
if (height < 1) {
|
||||
return {};
|
||||
}
|
||||
@@ -2426,6 +2517,12 @@ QList<QRgb> SHAMChunk::palette(qint32 y, qint32 height) const
|
||||
return pal;
|
||||
}
|
||||
|
||||
bool SHAMChunk::initialize(const QList<QRgb> &, qint32 height)
|
||||
{
|
||||
_height = height;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SHAMChunk::innerReadStructure(QIODevice *d)
|
||||
{
|
||||
return cacheData(d);
|
||||
@@ -2440,7 +2537,9 @@ RASTChunk::~RASTChunk()
|
||||
|
||||
}
|
||||
|
||||
RASTChunk::RASTChunk() : IPALChunk()
|
||||
RASTChunk::RASTChunk()
|
||||
: IPALChunk()
|
||||
, _height()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -2450,8 +2549,14 @@ bool RASTChunk::isValid() const
|
||||
return chunkId() == RASTChunk::defaultChunkId();
|
||||
}
|
||||
|
||||
QList<QRgb> RASTChunk::palette(qint32 y, qint32 height) const
|
||||
IPALChunk *RASTChunk::clone() const
|
||||
{
|
||||
return new RASTChunk(*this);
|
||||
}
|
||||
|
||||
QList<QRgb> RASTChunk::palette(qint32 y) const
|
||||
{
|
||||
auto &&height = _height;
|
||||
if (height < 1) {
|
||||
return {};
|
||||
}
|
||||
@@ -2478,7 +2583,414 @@ QList<QRgb> RASTChunk::palette(qint32 y, qint32 height) const
|
||||
return pal;
|
||||
}
|
||||
|
||||
bool RASTChunk::initialize(const QList<QRgb> &, qint32 height)
|
||||
{
|
||||
_height = height;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RASTChunk::innerReadStructure(QIODevice *d)
|
||||
{
|
||||
return cacheData(d);
|
||||
}
|
||||
|
||||
/* ******************
|
||||
* *** PCHG Chunk ***
|
||||
* ****************** */
|
||||
|
||||
PCHGChunk::~PCHGChunk()
|
||||
{
|
||||
}
|
||||
|
||||
PCHGChunk::PCHGChunk() : IPALChunk()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PCHGChunk::Compression PCHGChunk::compression() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return Compression::Uncompressed;
|
||||
}
|
||||
return Compression(ui16(data(), 0));
|
||||
}
|
||||
|
||||
PCHGChunk::Flags PCHGChunk::flags() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return Flags(Flag::None);
|
||||
}
|
||||
return Flags(ui16(data(), 2));
|
||||
}
|
||||
|
||||
qint16 PCHGChunk::startLine() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return i16(data(), 4);
|
||||
}
|
||||
|
||||
quint16 PCHGChunk::lineCount() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return ui16(data(), 6);
|
||||
}
|
||||
|
||||
quint16 PCHGChunk::changedLines() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return ui16(data(), 8);
|
||||
}
|
||||
|
||||
quint16 PCHGChunk::minReg() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return ui16(data(), 10);
|
||||
}
|
||||
|
||||
quint16 PCHGChunk::maxReg() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return ui16(data(), 12);
|
||||
}
|
||||
|
||||
quint16 PCHGChunk::maxChanges() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return ui16(data(), 14);
|
||||
}
|
||||
|
||||
quint32 PCHGChunk::totalChanges() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return ui32(data(), 16);
|
||||
}
|
||||
|
||||
bool PCHGChunk::hasAlpha() const
|
||||
{
|
||||
return (flags() & PCHGChunk::Flag::UseAlpha) ? true : false;
|
||||
}
|
||||
|
||||
bool PCHGChunk::isValid() const
|
||||
{
|
||||
if (bytes() < 20) {
|
||||
return false;
|
||||
}
|
||||
return chunkId() == PCHGChunk::defaultChunkId();
|
||||
}
|
||||
|
||||
IPALChunk *PCHGChunk::clone() const
|
||||
{
|
||||
return new PCHGChunk(*this);
|
||||
}
|
||||
|
||||
QList<QRgb> PCHGChunk::palette(qint32 y) const
|
||||
{
|
||||
return _palettes.value(y);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// PCHG_FastDecomp reimplementation (Amiga 68k -> portable C++/Qt)
|
||||
// ----------------------------------------------------------------------------
|
||||
// This mirrors the original 68k routine semantics:
|
||||
// - The Huffman tree is stored as a sequence of signed 16-bit words (big-endian)
|
||||
// and TreeCode points to the *last word* of that sequence.
|
||||
// - Bits are consumed MSB-first from 32-bit big-endian longwords of the source.
|
||||
// - Navigation rules (matching the assembly):
|
||||
// bit=1: read w = *(a3). If w < 0 then a3 += w (byte-wise) and continue;
|
||||
// else emit (w & 0xFF) and reset a3 to TreeCode (last word).
|
||||
// bit=0: predecrement a3 by 2; read w = *a3. If w < 0: continue;
|
||||
// else if (w & 0x0100) emit (w & 0xFF) and reset a3; else continue.
|
||||
// - Stop after writing exactly OriginalSize bytes.
|
||||
//
|
||||
// This function expects a single QByteArray laid out as:
|
||||
// [ tree (treeSize bytes, even) | compressed bitstream (... bytes) ]
|
||||
//
|
||||
// On any error, logs with qCCritical(LOG_IFFPLUGIN) and returns {}.
|
||||
// Comments are in English as requested.
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// NOTE: Sebastiano Vigna, the author of the PCHG specification and the ASM
|
||||
// decompression code for the Motorola 68K, gave us permission to use his
|
||||
// code and recommended that we convert it with AI.
|
||||
|
||||
// Read a big-endian 16-bit signed word from a byte buffer
|
||||
static inline qint16 read_be16(const char* base, int byteIndex, int size)
|
||||
{
|
||||
if (byteIndex + 1 >= size)
|
||||
return 0; // caller must bounds-check; we keep silent here
|
||||
const quint8 b0 = static_cast<quint8>(base[byteIndex]);
|
||||
const quint8 b1 = static_cast<quint8>(base[byteIndex + 1]);
|
||||
return static_cast<qint16>((b0 << 8) | b1);
|
||||
}
|
||||
|
||||
// Read a big-endian 32-bit unsigned long from a byte buffer
|
||||
static inline quint32 read_be32(const char* base, int byteIndex, int size)
|
||||
{
|
||||
if (byteIndex + 3 >= size)
|
||||
return 0; // caller must bounds-check
|
||||
const quint8 b0 = static_cast<quint8>(base[byteIndex]);
|
||||
const quint8 b1 = static_cast<quint8>(base[byteIndex + 1]);
|
||||
const quint8 b2 = static_cast<quint8>(base[byteIndex + 2]);
|
||||
const quint8 b3 = static_cast<quint8>(base[byteIndex + 3]);
|
||||
return (static_cast<quint32>(b0) << 24) |
|
||||
(static_cast<quint32>(b1) << 16) |
|
||||
(static_cast<quint32>(b2) << 8) |
|
||||
static_cast<quint32>(b3);
|
||||
}
|
||||
|
||||
// Core decompressor (tree + compressed stream in one QByteArray)
|
||||
static QByteArray pchgFastDecomp(const QByteArray& input, int treeSize, int originalSize)
|
||||
{
|
||||
// Basic validation
|
||||
if (treeSize <= 0 || (treeSize & 1)) {
|
||||
qCCritical(LOG_IFFPLUGIN) << "Invalid treeSize (must be positive and even)" << treeSize;
|
||||
return {};
|
||||
}
|
||||
if (input.size() < treeSize) {
|
||||
qCCritical(LOG_IFFPLUGIN) << "Input too small for treeSize" << input.size() << treeSize;
|
||||
return {};
|
||||
}
|
||||
if (originalSize < 0) {
|
||||
qCCritical(LOG_IFFPLUGIN) << "Invalid originalSize" << originalSize;
|
||||
return {};
|
||||
}
|
||||
|
||||
const char* data = input.constData();
|
||||
const int totalSize = input.size();
|
||||
|
||||
// Tree view (big-endian words)
|
||||
const int treeBytes = treeSize;
|
||||
const int treeWords = treeBytes / 2;
|
||||
if (treeWords <= 0) {
|
||||
qCCritical(LOG_IFFPLUGIN) << "Tree has zero words";
|
||||
return {};
|
||||
}
|
||||
|
||||
// Compressed stream
|
||||
const int srcBase = treeBytes; // offset where bitstream starts
|
||||
const int srcSize = totalSize - srcBase;
|
||||
if (srcSize <= 0 && originalSize > 0) {
|
||||
qCCritical(LOG_IFFPLUGIN) << "No compressed payload present";
|
||||
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 = [&]() {
|
||||
return treeWords - 1; // last word index
|
||||
};
|
||||
int a3_word = resetA3();
|
||||
|
||||
// Bit reader: loads 32b big-endian and shifts MSB-first
|
||||
quint32 bitbuf = 0;
|
||||
int bits = 0; // remaining bits in bitbuf
|
||||
int srcPos = 0; // byte offset relative to srcBase
|
||||
|
||||
auto refill = [&]() -> bool {
|
||||
if (srcPos + 4 > srcSize) {
|
||||
qCCritical(LOG_IFFPLUGIN) << "Compressed stream underflow while refilling bit buffer"
|
||||
<< "srcPos=" << srcPos << "srcSize=" << srcSize;
|
||||
return false;
|
||||
}
|
||||
bitbuf = read_be32(data + srcBase, srcPos, srcSize);
|
||||
bits = 32;
|
||||
srcPos += 4;
|
||||
return true;
|
||||
};
|
||||
|
||||
int produced = 0;
|
||||
|
||||
// Main decode loop: produce exactly originalSize bytes
|
||||
while (produced < originalSize) {
|
||||
if (bits == 0) {
|
||||
if (!refill()) {
|
||||
// Not enough bits to complete output
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const bool bit1 = (bitbuf & 0x80000000u) != 0u; // MSB before shift
|
||||
bitbuf <<= 1;
|
||||
--bits;
|
||||
|
||||
if (bit1) {
|
||||
// Case bit == 1 --> w = *(a3)
|
||||
if (a3_word < 0 || a3_word >= treeWords) {
|
||||
qCCritical(LOG_IFFPLUGIN) << "a3 out of bounds (bit=1)" << a3_word;
|
||||
return {};
|
||||
}
|
||||
const int byteIndex = a3_word * 2;
|
||||
const qint16 w = read_be16(data, byteIndex, treeBytes);
|
||||
|
||||
if (w < 0) {
|
||||
// a3 += w (w is a signed byte offset, must be even)
|
||||
if (w & 1) {
|
||||
qCCritical(LOG_IFFPLUGIN) << "Misaligned tree offset (odd)" << w;
|
||||
return {};
|
||||
}
|
||||
const int deltaWords = w / 2; // arithmetic division, w is even in valid streams
|
||||
const int next = a3_word + deltaWords;
|
||||
if (next < 0 || next >= treeWords) {
|
||||
qCCritical(LOG_IFFPLUGIN) << "a3 out of bounds after offset" << next;
|
||||
return {};
|
||||
}
|
||||
a3_word = next;
|
||||
} else {
|
||||
// Leaf: emit low 8 bits, reset a3
|
||||
outPtr[produced++] = static_cast<char>(w & 0xFF);
|
||||
a3_word = resetA3();
|
||||
}
|
||||
} else {
|
||||
// Case bit == 0 --> w = *--a3 (predecrement)
|
||||
--a3_word;
|
||||
if (a3_word < 0) {
|
||||
qCCritical(LOG_IFFPLUGIN) << "a3 underflow on predecrement";
|
||||
return {};
|
||||
}
|
||||
const int byteIndex = a3_word * 2;
|
||||
const qint16 w = read_be16(data, byteIndex, treeBytes);
|
||||
|
||||
if (w < 0) {
|
||||
// Internal node: continue with current a3
|
||||
continue;
|
||||
}
|
||||
|
||||
// Non-negative: check bit #8; if set -> leaf
|
||||
if ((w & 0x0100) != 0) {
|
||||
outPtr[produced++] = static_cast<char>(w & 0xFF);
|
||||
a3_word = resetA3();
|
||||
} else {
|
||||
// Not a leaf: continue scanning
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// !Huffman decompression
|
||||
|
||||
bool PCHGChunk::initialize(const QList<QRgb> &cmapPalette, qint32 height)
|
||||
{
|
||||
Q_UNUSED(height)
|
||||
auto dt = data().mid(20);
|
||||
if (compression() == PCHGChunk::Compression::Huffman) {
|
||||
QDataStream ds(dt);
|
||||
ds.setByteOrder(QDataStream::BigEndian);
|
||||
|
||||
quint32 infoSize;
|
||||
ds >> infoSize;
|
||||
quint32 origSize;
|
||||
ds >> origSize;
|
||||
|
||||
dt = pchgFastDecomp(dt.mid(8), infoSize, origSize);
|
||||
}
|
||||
if (dt.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QDataStream ds(dt);
|
||||
ds.setByteOrder(QDataStream::BigEndian);
|
||||
|
||||
// read the masks
|
||||
auto lcnt = lineCount();
|
||||
auto nlw = (lcnt + 31) / 32; // number of LWORD containing the bit mask
|
||||
QList<quint32> masks;
|
||||
for (auto i = 0; i < nlw; ++i) {
|
||||
quint32 mask;
|
||||
ds >> mask;
|
||||
masks << mask;
|
||||
}
|
||||
if (ds.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// read the palettes
|
||||
auto changesLoaded = qint64();
|
||||
auto startY = startLine();
|
||||
auto last = cmapPalette;
|
||||
auto flgs = flags();
|
||||
for (auto i = 0; i < lcnt; ++i) {
|
||||
auto mask = masks.at(i / 32);
|
||||
if (((mask >> (31 - i % 32)) & 1) == 0) {
|
||||
_palettes.insert(i + startY, last);
|
||||
continue; // no palette change for this line
|
||||
}
|
||||
|
||||
QHash<quint16, QRgb> hash;
|
||||
if (flgs & PCHGChunk::Flag::F12Bit) {
|
||||
quint8 c16;
|
||||
ds >> c16;
|
||||
quint8 c32;
|
||||
ds >> c32;
|
||||
for (auto j = 0; j < int(c16); ++j) {
|
||||
quint16 tmp;
|
||||
ds >> tmp;
|
||||
hash.insert(((tmp >> 12) & 0xF), qRgb(((tmp >> 8) & 0xF) * 17, ((tmp >> 4) & 0xF) * 17, ((tmp & 0xF) * 17)));
|
||||
}
|
||||
for (auto j = 0; j < int(c32); ++j) {
|
||||
quint16 tmp;
|
||||
ds >> tmp;
|
||||
hash.insert((((tmp >> 12) & 0xF) + 16), qRgb(((tmp >> 8) & 0xF) * 17, ((tmp >> 4) & 0xF) * 17, ((tmp & 0xF) * 17)));
|
||||
}
|
||||
} else if (flgs & PCHGChunk::Flag::F32Bit) { // NOTE: missing test case (not tested)
|
||||
quint16 cnt;
|
||||
ds >> cnt;
|
||||
for (auto j = 0; j < int(cnt); ++j) {
|
||||
quint16 reg;
|
||||
ds >> reg;
|
||||
quint8 alpha;
|
||||
ds >> alpha;
|
||||
quint8 red;
|
||||
ds >> red;
|
||||
quint8 blue;
|
||||
ds >> blue;
|
||||
quint8 green;
|
||||
ds >> green;
|
||||
hash.insert(reg, qRgba(red, green, blue, flgs & PCHGChunk::Flag::UseAlpha ? alpha : 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
if (ds.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto i = qsizetype(), n = last.size(); i < n; ++i) {
|
||||
if (hash.contains(i))
|
||||
last[i] = hash.value(i);
|
||||
}
|
||||
|
||||
_palettes.insert(i + startY, last);
|
||||
changesLoaded += hash.size();
|
||||
}
|
||||
|
||||
if (changesLoaded != qint64(totalChanges())) {
|
||||
qCDebug(LOG_IFFPLUGIN) << "PCHGChunk::innerReadStructure(): palette changes count mismatch!";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PCHGChunk::innerReadStructure(QIODevice *d)
|
||||
{
|
||||
return cacheData(d);
|
||||
}
|
||||
|
||||
+193
-10
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDateTime>
|
||||
#include <QHash>
|
||||
#include <QImage>
|
||||
#include <QIODevice>
|
||||
#include <QLoggingCategory>
|
||||
@@ -54,6 +55,7 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
|
||||
#define CMAP_CHUNK QByteArray("CMAP")
|
||||
#define CMYK_CHUNK QByteArray("CMYK") // https://wiki.amigaos.net/wiki/ILBM_IFF_Interleaved_Bitmap#ILBM.CMYK
|
||||
#define DPI__CHUNK QByteArray("DPI ")
|
||||
#define XBMI_CHUNK QByteArray("XBMI")
|
||||
|
||||
// Different palette for scanline
|
||||
#define BEAM_CHUNK QByteArray("BEAM")
|
||||
@@ -91,17 +93,20 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_IFFPLUGIN)
|
||||
|
||||
#define CHUNKID_DEFINE(a) static QByteArray defaultChunkId() { return a; }
|
||||
|
||||
// The 8-bit RGB format must be one. If you change it here, you have also to use the same
|
||||
// The 8-bit RGB format must be consistent. If you change it here, you have also to use the same
|
||||
// when converting an image with BEAM/CTBL/SHAM chunks otherwise the option(QImageIOHandler::ImageFormat)
|
||||
// could returns a wrong value.
|
||||
// Warning: Changing it requires changing the algorithms. Se, don't touch! :)
|
||||
#define FORMAT_RGB_8BIT QImage::Format_RGB888
|
||||
#define FORMAT_RGB_8BIT QImage::Format_RGB888 // default one
|
||||
|
||||
#define FORMAT_RGBA_8BIT QImage::Format_RGBA8888 // used by PCHG chunk
|
||||
|
||||
/*!
|
||||
* \brief The IFFChunk class
|
||||
*/
|
||||
class IFFChunk
|
||||
{
|
||||
friend class IFFHandlerPrivate;
|
||||
public:
|
||||
using ChunkList = QList<QSharedPointer<IFFChunk>>;
|
||||
|
||||
@@ -318,18 +323,30 @@ protected:
|
||||
inline quint16 ui16(quint8 c1, quint8 c2) const {
|
||||
return (quint16(c2) << 8) | quint16(c1);
|
||||
}
|
||||
inline quint16 ui16(const QByteArray &data, qint32 pos) const {
|
||||
return ui16(data.at(pos + 1), data.at(pos));
|
||||
}
|
||||
|
||||
inline qint16 i16(quint8 c1, quint8 c2) const {
|
||||
return qint32(ui16(c1, c2));
|
||||
}
|
||||
inline qint16 i16(const QByteArray &data, qint32 pos) const {
|
||||
return i16(data.at(pos + 1), data.at(pos));
|
||||
}
|
||||
|
||||
inline quint32 ui32(quint8 c1, quint8 c2, quint8 c3, quint8 c4) const {
|
||||
return (quint32(c4) << 24) | (quint32(c3) << 16) | (quint32(c2) << 8) | quint32(c1);
|
||||
}
|
||||
inline quint32 ui32(const QByteArray &data, qint32 pos) const {
|
||||
return ui32(data.at(pos + 3), data.at(pos + 2), data.at(pos + 1), data.at(pos));
|
||||
}
|
||||
|
||||
inline qint32 i32(quint8 c1, quint8 c2, quint8 c3, quint8 c4) const {
|
||||
return qint32(ui32(c1, c2, c3, c4));
|
||||
}
|
||||
inline qint32 i32(const QByteArray &data, qint32 pos) const {
|
||||
return i32(data.at(pos + 3), data.at(pos + 2), data.at(pos + 1), data.at(pos));
|
||||
}
|
||||
|
||||
static ChunkList innerFromDevice(QIODevice *d, bool *ok, IFFChunk *parent = nullptr);
|
||||
|
||||
@@ -358,7 +375,36 @@ class IPALChunk : public IFFChunk
|
||||
public:
|
||||
virtual ~IPALChunk() override {}
|
||||
IPALChunk() : IFFChunk() {}
|
||||
virtual QList<QRgb> palette(qint32 y, qint32 height) const = 0;
|
||||
IPALChunk(const IPALChunk& other) = default;
|
||||
IPALChunk& operator =(const IPALChunk& other) = default;
|
||||
|
||||
/*!
|
||||
* \brief hasAlpha
|
||||
* \return True it the palette supports the alpha channel.
|
||||
*/
|
||||
virtual bool hasAlpha() const { return false; }
|
||||
|
||||
/*!
|
||||
* \brief clone
|
||||
* \return A new instance of the class with all data.
|
||||
*/
|
||||
virtual IPALChunk *clone() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief palette
|
||||
* \param y The scanline.
|
||||
* \return The modified palette.
|
||||
*/
|
||||
virtual QList<QRgb> palette(qint32 y) const = 0;
|
||||
|
||||
/*!
|
||||
* \brief initialize
|
||||
* Initialize the palette changer.
|
||||
* \param cmapPalette The palette as stored in the CMAP chunk.
|
||||
* \param height The image height.
|
||||
* \return True on success, otherwise false.
|
||||
*/
|
||||
virtual bool initialize(const QList<QRgb>& cmapPalette, qint32 height) = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -376,7 +422,17 @@ public:
|
||||
};
|
||||
enum Masking {
|
||||
None = 0, /**< Designates an opaque rectangular image. */
|
||||
HasMask = 1, /**< A mask plane is interleaved with the bitplanes in the BODY chunk. */
|
||||
HasMask = 1, /**< A "mask" is an optional "plane" of data the same size (w, h) as a bitplane.
|
||||
It tells how to "cut out" part of the image when painting it onto another
|
||||
image. "One" bits in the mask mean "copy the corresponding pixel to the
|
||||
destination". "Zero" mask bits mean "leave this destination pixel alone". In
|
||||
other words, "zero" bits designate transparent pixels.
|
||||
The rows of the different bitplanes and mask are interleaved in the file.
|
||||
This localizes all the information pertinent to each scan line. It
|
||||
makes it much easier to transform the data while reading it to adjust the
|
||||
image size or depth. It also makes it possible to scroll a big image by
|
||||
swapping rows directly from the file without the need for random-access to
|
||||
all the bitplanes. */
|
||||
HasTransparentColor = 2, /**< Pixels in the source planes matching transparentColor
|
||||
are to be considered “transparent”. (Actually, transparentColor
|
||||
isn’t a “color number” since it’s matched with numbers formed
|
||||
@@ -385,7 +441,7 @@ public:
|
||||
one of the color registers. */
|
||||
Lasso = 3 /**< The reader may construct a mask by lassoing the image as in MacPaint.
|
||||
To do this, put a 1 pixel border of transparentColor around the image rectangle.
|
||||
Then do a seed fill from this border. Filled pixels are to be transparent. */
|
||||
Then do a seed fill from this border. Filled pixels are to be transparent. */
|
||||
};
|
||||
|
||||
virtual ~BMHDChunk() override;
|
||||
@@ -605,13 +661,13 @@ public:
|
||||
* \brief dpiX
|
||||
* \return The horizontal resolution in DPI.
|
||||
*/
|
||||
quint16 dpiX() const;
|
||||
virtual quint16 dpiX() const;
|
||||
|
||||
/*!
|
||||
* \brief dpiY
|
||||
* \return The vertical resolution in DPI.
|
||||
*/
|
||||
quint16 dpiY() const;
|
||||
virtual quint16 dpiY() const;
|
||||
|
||||
/*!
|
||||
* \brief dotsPerMeterX
|
||||
@@ -631,6 +687,50 @@ protected:
|
||||
virtual bool innerReadStructure(QIODevice *d) override;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief The XBMIChunk class
|
||||
*/
|
||||
class XBMIChunk : public DPIChunk
|
||||
{
|
||||
public:
|
||||
enum PictureType : quint16 {
|
||||
Indexed = 0,
|
||||
Grayscale = 1,
|
||||
Rgb = 2,
|
||||
RgbA = 3,
|
||||
Cmyk = 4,
|
||||
CmykA = 5,
|
||||
Bitmap = 6
|
||||
};
|
||||
|
||||
virtual ~XBMIChunk() override;
|
||||
XBMIChunk();
|
||||
XBMIChunk(const XBMIChunk& other) = default;
|
||||
XBMIChunk& operator =(const XBMIChunk& other) = default;
|
||||
|
||||
virtual bool isValid() const override;
|
||||
|
||||
/*!
|
||||
* \brief dpiX
|
||||
* \return The horizontal resolution in DPI.
|
||||
*/
|
||||
virtual quint16 dpiX() const override;
|
||||
|
||||
/*!
|
||||
* \brief dpiY
|
||||
* \return The vertical resolution in DPI.
|
||||
*/
|
||||
virtual quint16 dpiY() const override;
|
||||
|
||||
/*!
|
||||
* \brief pictureType
|
||||
* \return The picture type
|
||||
*/
|
||||
PictureType pictureType() const;
|
||||
|
||||
CHUNKID_DEFINE(XBMI_CHUNK)
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* \brief The BODYChunk class
|
||||
@@ -1312,12 +1412,19 @@ public:
|
||||
|
||||
virtual bool isValid() const override;
|
||||
|
||||
virtual QList<QRgb> palette(qint32 y, qint32 height) const override;
|
||||
virtual IPALChunk *clone() const override;
|
||||
|
||||
virtual QList<QRgb> palette(qint32 y) const override;
|
||||
|
||||
virtual bool initialize(const QList<QRgb>& cmapPalette, qint32 height) override;
|
||||
|
||||
CHUNKID_DEFINE(BEAM_CHUNK)
|
||||
|
||||
protected:
|
||||
virtual bool innerReadStructure(QIODevice *d) override;
|
||||
|
||||
private:
|
||||
qint32 _height;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -1349,12 +1456,19 @@ public:
|
||||
|
||||
virtual bool isValid() const override;
|
||||
|
||||
virtual QList<QRgb> palette(qint32 y, qint32 height) const override;
|
||||
virtual IPALChunk *clone() const override;
|
||||
|
||||
virtual QList<QRgb> palette(qint32 y) const override;
|
||||
|
||||
virtual bool initialize(const QList<QRgb>& cmapPalette, qint32 height) override;
|
||||
|
||||
CHUNKID_DEFINE(SHAM_CHUNK)
|
||||
|
||||
protected:
|
||||
virtual bool innerReadStructure(QIODevice *d) override;
|
||||
|
||||
private:
|
||||
qint32 _height;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -1373,13 +1487,82 @@ public:
|
||||
|
||||
virtual bool isValid() const override;
|
||||
|
||||
virtual QList<QRgb> palette(qint32 y, qint32 height) const override;
|
||||
virtual IPALChunk *clone() const override;
|
||||
|
||||
virtual QList<QRgb> palette(qint32 y) const override;
|
||||
|
||||
virtual bool initialize(const QList<QRgb>& cmapPalette, qint32 height) override;
|
||||
|
||||
CHUNKID_DEFINE(RAST_CHUNK)
|
||||
|
||||
protected:
|
||||
virtual bool innerReadStructure(QIODevice *d) override;
|
||||
|
||||
private:
|
||||
qint32 _height;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief The PCHGChunk class
|
||||
*/
|
||||
class PCHGChunk : public IPALChunk
|
||||
{
|
||||
public:
|
||||
enum Compression {
|
||||
Uncompressed,
|
||||
Huffman
|
||||
};
|
||||
|
||||
enum Flag {
|
||||
None = 0x00,
|
||||
F12Bit = 0x01,
|
||||
F32Bit = 0x02,
|
||||
UseAlpha = 0x04
|
||||
};
|
||||
Q_DECLARE_FLAGS(Flags, Flag)
|
||||
|
||||
virtual ~PCHGChunk() override;
|
||||
PCHGChunk();
|
||||
PCHGChunk(const PCHGChunk& other) = default;
|
||||
PCHGChunk& operator =(const PCHGChunk& other) = default;
|
||||
|
||||
Compression compression() const;
|
||||
|
||||
Flags flags() const;
|
||||
|
||||
qint16 startLine() const;
|
||||
|
||||
quint16 lineCount() const;
|
||||
|
||||
quint16 changedLines() const;
|
||||
|
||||
quint16 minReg() const;
|
||||
|
||||
quint16 maxReg() const;
|
||||
|
||||
quint16 maxChanges() const;
|
||||
|
||||
quint32 totalChanges() const;
|
||||
|
||||
virtual bool hasAlpha() const override;
|
||||
|
||||
virtual bool isValid() const override;
|
||||
|
||||
virtual IPALChunk *clone() const override;
|
||||
|
||||
virtual QList<QRgb> palette(qint32 y) const override;
|
||||
|
||||
virtual bool initialize(const QList<QRgb>& cmapPalette, qint32 height) override;
|
||||
|
||||
CHUNKID_DEFINE(PCHG_CHUNK)
|
||||
|
||||
protected:
|
||||
virtual bool innerReadStructure(QIODevice *d) override;
|
||||
|
||||
private:
|
||||
QHash<qint32, QHash<quint16, QRgb>> _paletteChanges;
|
||||
|
||||
QHash<qint32, QList<QRgb>> _palettes;
|
||||
};
|
||||
|
||||
#endif // KIMG_CHUNKS_P_H
|
||||
|
||||
+40
-16
@@ -15,7 +15,7 @@
|
||||
|
||||
#include <QColorSpace>
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
@@ -24,6 +24,22 @@
|
||||
// #define DDS_DISABLE_STRIDE_ALIGNMENT
|
||||
#endif
|
||||
|
||||
/* *** DDS_MAX_IMAGE_WIDTH and DDS_MAX_IMAGE_HEIGHT ***
|
||||
* The maximum size in pixel allowed by the plugin.
|
||||
*/
|
||||
#ifndef DDS_MAX_IMAGE_WIDTH
|
||||
#define DDS_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT
|
||||
#endif
|
||||
#ifndef DDS_MAX_IMAGE_HEIGHT
|
||||
#define DDS_MAX_IMAGE_HEIGHT DDS_MAX_IMAGE_WIDTH
|
||||
#endif
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_DDSPLUGIN, "kf.imageformats.plugins.dds", QtDebugMsg)
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(LOG_DDSPLUGIN, "kf.imageformats.plugins.dds", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
enum Format {
|
||||
FormatUnknown = 0,
|
||||
|
||||
@@ -1030,20 +1046,32 @@ static QImage readUnsignedImage(QDataStream &s, const DDSHeader &dds, quint32 wi
|
||||
|
||||
static qfloat16 readFloat16(QDataStream &s)
|
||||
{
|
||||
qfloat16 f16;
|
||||
qfloat16 f16 = 0;
|
||||
if (s.status() != QDataStream::Ok) {
|
||||
return f16;
|
||||
}
|
||||
s >> f16;
|
||||
if (qIsNaN(f16)) {
|
||||
s.setStatus(QDataStream::ReadCorruptData);
|
||||
}
|
||||
return f16;
|
||||
}
|
||||
|
||||
static inline float readFloat32(QDataStream &s)
|
||||
{
|
||||
Q_ASSERT(sizeof(float) == 4);
|
||||
float value;
|
||||
float value = 0;
|
||||
if (s.status() != QDataStream::Ok) {
|
||||
return value;
|
||||
}
|
||||
// TODO: find better way to avoid setting precision each time
|
||||
QDataStream::FloatingPointPrecision precision = s.floatingPointPrecision();
|
||||
s.setFloatingPointPrecision(QDataStream::SinglePrecision);
|
||||
s >> value;
|
||||
s.setFloatingPointPrecision(precision);
|
||||
if (qIsNaN(value)) {
|
||||
s.setStatus(QDataStream::ReadCorruptData);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -1126,11 +1154,7 @@ static QImage readR32F(QDataStream &s, const quint32 width, const quint32 height
|
||||
for (quint32 y = 0; y < height; y++) {
|
||||
float *line = reinterpret_cast<float *>(image.scanLine(y));
|
||||
for (quint32 x = 0; x < width; x++) {
|
||||
const float f = readFloat32(s);
|
||||
if (std::isnan(f)) {
|
||||
return {};
|
||||
}
|
||||
line[x * 4] = f;
|
||||
line[x * 4] = readFloat32(s);
|
||||
line[x * 4 + 1] = 0;
|
||||
line[x * 4 + 2] = 0;
|
||||
line[x * 4 + 3] = 1;
|
||||
@@ -2381,7 +2405,7 @@ bool QDDSHandler::write(const QImage &outImage)
|
||||
return writeA32B32G32R32F(outImage, s);
|
||||
}
|
||||
|
||||
qWarning() << "Format" << formatName(format) << "is not supported";
|
||||
qCWarning(LOG_DDSPLUGIN) << "Format" << formatName(format) << "is not supported";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2454,7 +2478,7 @@ bool QDDSHandler::jumpToImage(int imageNumber)
|
||||
bool QDDSHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qWarning() << "DDSHandler::canRead() called with no device";
|
||||
qCWarning(LOG_DDSPLUGIN) << "DDSHandler::canRead() called with no device";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2479,7 +2503,7 @@ bool QDDSHandler::ensureScanned() const
|
||||
that->m_format = FormatUnknown;
|
||||
|
||||
if (device()->isSequential()) {
|
||||
qWarning() << "Sequential devices are not supported";
|
||||
qCWarning(LOG_DDSPLUGIN) << "Sequential devices are not supported";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2512,25 +2536,25 @@ bool QDDSHandler::verifyHeader(const DDSHeader &dds) const
|
||||
quint32 requiredFlags = DDSHeader::FlagCaps | DDSHeader::FlagHeight
|
||||
| DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat;
|
||||
if ((flags & requiredFlags) != requiredFlags) {
|
||||
qWarning() << "Wrong dds.flags - not all required flags present. "
|
||||
qCWarning(LOG_DDSPLUGIN) << "Wrong dds.flags - not all required flags present. "
|
||||
"Actual flags :" << flags;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dds.size != ddsSize) {
|
||||
qWarning() << "Wrong dds.size: actual =" << dds.size
|
||||
qCWarning(LOG_DDSPLUGIN) << "Wrong dds.size: actual =" << dds.size
|
||||
<< "expected =" << ddsSize;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dds.pixelFormat.size != pixelFormatSize) {
|
||||
qWarning() << "Wrong dds.pixelFormat.size: actual =" << dds.pixelFormat.size
|
||||
qCWarning(LOG_DDSPLUGIN) << "Wrong dds.pixelFormat.size: actual =" << dds.pixelFormat.size
|
||||
<< "expected =" << pixelFormatSize;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dds.width > INT_MAX || dds.height > INT_MAX) {
|
||||
qWarning() << "Can't read image with w/h bigger than INT_MAX";
|
||||
if (dds.width > DDS_MAX_IMAGE_WIDTH || dds.height > DDS_MAX_IMAGE_HEIGHT) {
|
||||
qCWarning(LOG_DDSPLUGIN) << "Can't read image with size bigger than" << DDS_MAX_IMAGE_WIDTH << "x" << DDS_MAX_IMAGE_HEIGHT << "pixels";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,14 +75,20 @@
|
||||
|
||||
#include <QColorSpace>
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QFloat16>
|
||||
#include <QImage>
|
||||
#include <QImageIOPlugin>
|
||||
#include <QLocale>
|
||||
#include <QLoggingCategory>
|
||||
#include <QThread>
|
||||
#include <QTimeZone>
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_EXRPLUGIN, "kf.imageformats.plugins.exr", QtDebugMsg)
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(LOG_EXRPLUGIN, "kf.imageformats.plugins.exr", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
class K_IStream : public Imf::IStream
|
||||
{
|
||||
public:
|
||||
@@ -236,10 +242,10 @@ static QStringList viewList(const Imf::Header &h)
|
||||
}
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
void printAttributes(const Imf::Header &h)
|
||||
static void printAttributes(const Imf::Header &h)
|
||||
{
|
||||
for (auto i = h.begin(); i != h.end(); ++i) {
|
||||
qDebug() << i.name();
|
||||
qCDebug(LOG_EXRPLUGIN) << i.name();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -288,8 +294,14 @@ static void readMetadata(const Imf::Header &header, QImage &image)
|
||||
if (auto pixelAspectRatio = header.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));
|
||||
auto hres = dpi2ppm(xDensity->value());
|
||||
if (hres > 0) {
|
||||
image.setDotsPerMeterX(hres);
|
||||
}
|
||||
auto vres = dpi2ppm(xDensity->value() * par);
|
||||
if (vres > 0) {
|
||||
image.setDotsPerMeterY(vres);
|
||||
}
|
||||
}
|
||||
|
||||
// Non-standard attribute
|
||||
@@ -379,14 +391,14 @@ bool EXRHandler::read(QImage *outImage)
|
||||
|
||||
// limiting the maximum image size on a reasonable size (as done in other plugins)
|
||||
if (width > EXR_MAX_IMAGE_WIDTH || height > EXR_MAX_IMAGE_HEIGHT) {
|
||||
qWarning() << "The maximum image size is limited to" << EXR_MAX_IMAGE_WIDTH << "x" << EXR_MAX_IMAGE_HEIGHT << "px";
|
||||
qCWarning(LOG_EXRPLUGIN) << "The maximum image size is limited to" << EXR_MAX_IMAGE_WIDTH << "x" << EXR_MAX_IMAGE_HEIGHT << "px";
|
||||
return false;
|
||||
}
|
||||
|
||||
// creating the image
|
||||
QImage image = imageAlloc(width, height, imageFormat(file));
|
||||
if (image.isNull()) {
|
||||
qWarning() << "Failed to allocate image, invalid size?" << QSize(width, height);
|
||||
qCWarning(LOG_EXRPLUGIN) << "Failed to allocate image, invalid size?" << QSize(width, height);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -554,7 +566,7 @@ bool EXRHandler::write(const QImage &image)
|
||||
|
||||
// limiting the maximum image size on a reasonable size (as done in other plugins)
|
||||
if (width > EXR_MAX_IMAGE_WIDTH || height > EXR_MAX_IMAGE_HEIGHT) {
|
||||
qWarning() << "The maximum image size is limited to" << EXR_MAX_IMAGE_WIDTH << "x" << EXR_MAX_IMAGE_HEIGHT << "px";
|
||||
qCWarning(LOG_EXRPLUGIN) << "The maximum image size is limited to" << EXR_MAX_IMAGE_WIDTH << "x" << EXR_MAX_IMAGE_HEIGHT << "px";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -770,7 +782,7 @@ int EXRHandler::currentImageNumber() const
|
||||
bool EXRHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qWarning("EXRHandler::canRead() called with no device");
|
||||
qCWarning(LOG_EXRPLUGIN) << "EXRHandler::canRead() called with no device";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
#include <QLoggingCategory>
|
||||
#include <QRegularExpressionMatch>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
/* *** HDR_HALF_QUALITY ***
|
||||
* If defined, a 16-bits float image is created, otherwise a 32-bits float ones (default).
|
||||
*/
|
||||
@@ -126,6 +124,7 @@ public:
|
||||
{
|
||||
Header h;
|
||||
|
||||
int cnt = 0;
|
||||
int len;
|
||||
QByteArray line(MAXLINE + 1, Qt::Uninitialized);
|
||||
QByteArray format;
|
||||
@@ -167,7 +166,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
} while ((len > 0) && (line[0] != '\n'));
|
||||
} while ((len > 0) && (line[0] != '\n') && (cnt++ < 128));
|
||||
|
||||
if (format != "32-bit_rle_rgbe") {
|
||||
qCDebug(HDRPLUGIN) << "Unknown HDR format:" << format;
|
||||
@@ -424,7 +423,7 @@ bool HDRHandler::read(QImage *outImage)
|
||||
|
||||
QImage img;
|
||||
if (!LoadHDR(s, h, img)) {
|
||||
// qDebug() << "Error loading HDR file.";
|
||||
// qCWarning(HDRPLUGIN) << "Error loading HDR file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -501,7 +500,7 @@ bool HDRHandler::canRead() const
|
||||
bool HDRHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qWarning("HDRHandler::canRead() called with no device");
|
||||
qCWarning(HDRPLUGIN) << "HDRHandler::canRead() called with no device";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+37
-30
@@ -13,12 +13,19 @@
|
||||
#include <libheif/heif.h>
|
||||
|
||||
#include <QColorSpace>
|
||||
#include <QDebug>
|
||||
#include <QLoggingCategory>
|
||||
#include <QPointF>
|
||||
#include <QSysInfo>
|
||||
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_HEIFPLUGIN, "kf.imageformats.plugins.heif", QtDebugMsg)
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(LOG_HEIFPLUGIN, "kf.imageformats.plugins.heif", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
#ifndef HEIF_MAX_METADATA_SIZE
|
||||
/*!
|
||||
* XMP and EXIF maximum size.
|
||||
@@ -112,7 +119,7 @@ bool HEIFHandler::read(QImage *outImage)
|
||||
bool HEIFHandler::write(const QImage &image)
|
||||
{
|
||||
if (image.format() == QImage::Format_Invalid || image.isNull()) {
|
||||
qWarning("No image data to save");
|
||||
qCWarning(LOG_HEIFPLUGIN) << "No image data to save";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -208,7 +215,7 @@ bool HEIFHandler::write_helper(const QImage &image)
|
||||
|
||||
err = heif_image_create(tmpimage.width(), tmpimage.height(), heif_colorspace_RGB, chroma, &h_image);
|
||||
if (err.code) {
|
||||
qWarning() << "heif_image_create error:" << err.message;
|
||||
qCWarning(LOG_HEIFPLUGIN) << "heif_image_create error:" << err.message;
|
||||
heif_context_free(context);
|
||||
return false;
|
||||
}
|
||||
@@ -287,7 +294,7 @@ bool HEIFHandler::write_helper(const QImage &image)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
qWarning() << "Unsupported depth:" << save_depth;
|
||||
qCWarning(LOG_HEIFPLUGIN) << "Unsupported depth:" << save_depth;
|
||||
heif_image_release(h_image);
|
||||
heif_context_free(context);
|
||||
return false;
|
||||
@@ -297,7 +304,7 @@ bool HEIFHandler::write_helper(const QImage &image)
|
||||
struct heif_encoder *encoder = nullptr;
|
||||
err = heif_context_get_encoder_for_format(context, encoder_codec, &encoder);
|
||||
if (err.code) {
|
||||
qWarning() << "Unable to get an encoder instance:" << err.message;
|
||||
qCWarning(LOG_HEIFPLUGIN) << "Unable to get an encoder instance:" << err.message;
|
||||
heif_image_release(h_image);
|
||||
heif_context_free(context);
|
||||
return false;
|
||||
@@ -315,7 +322,7 @@ bool HEIFHandler::write_helper(const QImage &image)
|
||||
encoder_options->save_alpha_channel = save_alpha;
|
||||
|
||||
if ((tmpimage.width() % 2 == 1) || (tmpimage.height() % 2 == 1)) {
|
||||
qWarning() << "Image has odd dimension!\nUse even-numbered dimension(s) for better compatibility with other HEIF implementations.";
|
||||
qCWarning(LOG_HEIFPLUGIN) << "Image has odd dimension!\nUse even-numbered dimension(s) for better compatibility with other HEIF implementations.";
|
||||
if (save_alpha) {
|
||||
// This helps to save alpha channel when image has odd dimension
|
||||
encoder_options->macOS_compatibility_workaround = 0;
|
||||
@@ -347,7 +354,7 @@ bool HEIFHandler::write_helper(const QImage &image)
|
||||
}
|
||||
|
||||
if (err.code) {
|
||||
qWarning() << "heif_context_encode_image failed:" << err.message;
|
||||
qCWarning(LOG_HEIFPLUGIN) << "heif_context_encode_image failed:" << err.message;
|
||||
heif_encoder_release(encoder);
|
||||
heif_image_release(h_image);
|
||||
heif_context_free(context);
|
||||
@@ -364,7 +371,7 @@ bool HEIFHandler::write_helper(const QImage &image)
|
||||
heif_image_release(h_image);
|
||||
|
||||
if (err.code) {
|
||||
qWarning() << "Writing HEIF image failed:" << err.message;
|
||||
qCWarning(LOG_HEIFPLUGIN) << "Writing HEIF image failed:" << err.message;
|
||||
heif_context_free(context);
|
||||
return false;
|
||||
}
|
||||
@@ -524,7 +531,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
struct heif_error err = heif_context_read_from_memory(ctx, static_cast<const void *>(buffer.constData()), buffer.size(), nullptr);
|
||||
|
||||
if (err.code) {
|
||||
qWarning() << "heif_context_read_from_memory error:" << err.message;
|
||||
qCWarning(LOG_HEIFPLUGIN) << "heif_context_read_from_memory error:" << err.message;
|
||||
heif_context_free(ctx);
|
||||
m_parseState = ParseHeicError;
|
||||
return false;
|
||||
@@ -533,7 +540,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
struct heif_image_handle *handle = nullptr;
|
||||
err = heif_context_get_primary_image_handle(ctx, &handle);
|
||||
if (err.code) {
|
||||
qWarning() << "heif_context_get_primary_image_handle error:" << err.message;
|
||||
qCWarning(LOG_HEIFPLUGIN) << "heif_context_get_primary_image_handle error:" << err.message;
|
||||
heif_context_free(ctx);
|
||||
m_parseState = ParseHeicError;
|
||||
return false;
|
||||
@@ -543,7 +550,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
m_parseState = ParseHeicError;
|
||||
heif_image_handle_release(handle);
|
||||
heif_context_free(ctx);
|
||||
qWarning() << "HEIC image has zero dimension";
|
||||
qCWarning(LOG_HEIFPLUGIN) << "HEIC image has zero dimension";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -553,7 +560,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
m_parseState = ParseHeicError;
|
||||
heif_image_handle_release(handle);
|
||||
heif_context_free(ctx);
|
||||
qWarning() << "HEIF image with undefined or unsupported bit depth.";
|
||||
qCWarning(LOG_HEIFPLUGIN) << "HEIF image with undefined or unsupported bit depth.";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -582,7 +589,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
m_parseState = ParseHeicError;
|
||||
heif_image_handle_release(handle);
|
||||
heif_context_free(ctx);
|
||||
qWarning() << "Unsupported bit depth:" << bit_depth;
|
||||
qCWarning(LOG_HEIFPLUGIN) << "Unsupported bit depth:" << bit_depth;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -597,7 +604,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
|
||||
#if LIBHEIF_HAVE_VERSION(1, 13, 0)
|
||||
if (err.code == heif_error_Invalid_input && err.subcode == heif_suberror_Unknown_NCLX_matrix_coefficients && img == nullptr && buffer.contains("Xiaomi")) {
|
||||
qWarning() << "Non-standard HEIF image with invalid matrix_coefficients, probably made by a Xiaomi device!";
|
||||
qCWarning(LOG_HEIFPLUGIN) << "Non-standard HEIF image with invalid matrix_coefficients, probably made by a Xiaomi device!";
|
||||
|
||||
// second try to decode with strict decoding disabled
|
||||
decoder_option->strict_decoding = 0;
|
||||
@@ -610,7 +617,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
}
|
||||
|
||||
if (err.code) {
|
||||
qWarning() << "heif_decode_image error:" << err.message;
|
||||
qCWarning(LOG_HEIFPLUGIN) << "heif_decode_image error:" << err.message;
|
||||
heif_image_handle_release(handle);
|
||||
heif_context_free(ctx);
|
||||
m_parseState = ParseHeicError;
|
||||
@@ -627,7 +634,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
heif_image_handle_release(handle);
|
||||
heif_context_free(ctx);
|
||||
m_parseState = ParseHeicError;
|
||||
qWarning() << "HEIC image size invalid:" << imageSize;
|
||||
qCWarning(LOG_HEIFPLUGIN) << "HEIC image size invalid:" << imageSize;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -639,7 +646,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
heif_image_handle_release(handle);
|
||||
heif_context_free(ctx);
|
||||
m_parseState = ParseHeicError;
|
||||
qWarning() << "HEIC data pixels information not valid!";
|
||||
qCWarning(LOG_HEIFPLUGIN) << "HEIC data pixels information not valid!";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -649,7 +656,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
heif_image_handle_release(handle);
|
||||
heif_context_free(ctx);
|
||||
m_parseState = ParseHeicError;
|
||||
qWarning() << "Unable to allocate memory!";
|
||||
qCWarning(LOG_HEIFPLUGIN) << "Unable to allocate memory!";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -844,7 +851,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
heif_image_handle_release(handle);
|
||||
heif_context_free(ctx);
|
||||
m_parseState = ParseHeicError;
|
||||
qWarning() << "Unsupported bit depth:" << bit_depth;
|
||||
qCWarning(LOG_HEIFPLUGIN) << "Unsupported bit depth:" << bit_depth;
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@@ -856,15 +863,15 @@ bool HEIFHandler::ensureDecoder()
|
||||
QByteArray ba(rawProfileSize, 0);
|
||||
err = heif_image_handle_get_raw_color_profile(handle, ba.data());
|
||||
if (err.code) {
|
||||
qWarning() << "icc profile loading failed";
|
||||
qCWarning(LOG_HEIFPLUGIN) << "icc profile loading failed";
|
||||
} else {
|
||||
QColorSpace colorspace = QColorSpace::fromIccProfile(ba);
|
||||
if (!colorspace.isValid()) {
|
||||
qWarning() << "HEIC image has Qt-unsupported or invalid ICC profile!";
|
||||
qCWarning(LOG_HEIFPLUGIN) << "HEIC image has Qt-unsupported or invalid ICC profile!";
|
||||
}
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
|
||||
else if (colorspace.colorModel() == QColorSpace::ColorModel::Cmyk) {
|
||||
qWarning("CMYK ICC profile is not expected for HEIF, discarding the ICCprofile!");
|
||||
qCWarning(LOG_HEIFPLUGIN) << "CMYK ICC profile is not expected for HEIF, discarding the ICCprofile!";
|
||||
colorspace = QColorSpace();
|
||||
} else if (colorspace.colorModel() == QColorSpace::ColorModel::Gray) {
|
||||
if (hasAlphaChannel) {
|
||||
@@ -884,7 +891,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
}
|
||||
colorspace = QColorSpace(gray_whitePoint, redP, greenP, blueP, trc_new, gamma_new);
|
||||
if (!colorspace.isValid()) {
|
||||
qWarning("HEIF plugin created invalid QColorSpace!");
|
||||
qCWarning(LOG_HEIFPLUGIN) << "HEIF plugin created invalid QColorSpace!";
|
||||
}
|
||||
} else { // no alpha channel
|
||||
m_current_image.convertTo(bit_depth > 8 ? QImage::Format_Grayscale16 : QImage::Format_Grayscale8);
|
||||
@@ -894,14 +901,14 @@ bool HEIFHandler::ensureDecoder()
|
||||
m_current_image.setColorSpace(colorspace);
|
||||
}
|
||||
} else {
|
||||
qWarning() << "icc profile is empty or above limits";
|
||||
qCWarning(LOG_HEIFPLUGIN) << "icc profile is empty or above limits";
|
||||
}
|
||||
|
||||
} else if (profileType == heif_color_profile_type_nclx) {
|
||||
struct heif_color_profile_nclx *nclx = nullptr;
|
||||
err = heif_image_handle_get_nclx_color_profile(handle, &nclx);
|
||||
if (err.code || !nclx) {
|
||||
qWarning() << "nclx profile loading failed";
|
||||
qCWarning(LOG_HEIFPLUGIN) << "nclx profile loading failed";
|
||||
} else {
|
||||
const QPointF redPoint(nclx->color_primary_red_x, nclx->color_primary_red_y);
|
||||
const QPointF greenPoint(nclx->color_primary_green_x, nclx->color_primary_green_y);
|
||||
@@ -936,9 +943,9 @@ bool HEIFHandler::ensureDecoder()
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
qWarning("CICP color_primaries: %d, transfer_characteristics: %d\nThe colorspace is unsupported by this plug-in yet.",
|
||||
nclx->color_primaries,
|
||||
nclx->transfer_characteristics);
|
||||
qCWarning(LOG_HEIFPLUGIN) << "CICP color_primaries: %d, transfer_characteristics: %d\nThe colorspace is unsupported by this plug-in yet."
|
||||
<< nclx->color_primaries
|
||||
<< nclx->transfer_characteristics;
|
||||
q_trc = QColorSpace::TransferFunction::SRgb;
|
||||
break;
|
||||
}
|
||||
@@ -960,7 +967,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
heif_nclx_color_profile_free(nclx);
|
||||
|
||||
if (!m_current_image.colorSpace().isValid()) {
|
||||
qWarning() << "HEIC plugin created invalid QColorSpace from NCLX!";
|
||||
qCWarning(LOG_HEIFPLUGIN) << "HEIC plugin created invalid QColorSpace from NCLX!";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -984,7 +991,7 @@ bool HEIFHandler::ensureDecoder()
|
||||
QByteArray ba(sz, char());
|
||||
auto err = heif_image_handle_get_metadata(handle, ids[n], ba.data());
|
||||
if (err.code != heif_error_Ok) {
|
||||
qWarning() << "Error while reading metadata" << err.message;
|
||||
qCWarning(LOG_HEIFPLUGIN) << "Error while reading metadata" << err.message;
|
||||
continue;
|
||||
}
|
||||
if (isXmp) {
|
||||
|
||||
+84
-32
@@ -27,6 +27,37 @@ public:
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief atariSTERast
|
||||
* On Atari STE images, the RAST chunk can be found outside
|
||||
* the FORM one so, I check if this is the case.
|
||||
* \param chunks The chunk list.
|
||||
*/
|
||||
void atariSTERast(QIODevice *d, IFFChunk::ChunkList &chunks)
|
||||
{
|
||||
if (chunks.size() != 1 || d->isSequential()) {
|
||||
return;
|
||||
}
|
||||
auto &&c = chunks.first();
|
||||
if (c->chunkId() != FORMChunk::defaultChunkId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The RAST chunk is not aligned so I have to temporary change the
|
||||
// position and the alignment to read it successfully.
|
||||
auto pos = d->pos();
|
||||
auto align = c->alignBytes();
|
||||
c->setAlignBytes(1);
|
||||
d->seek(c->nextChunkPos());
|
||||
c->setAlignBytes(align);
|
||||
if (d->peek(4) == RAST_CHUNK) {
|
||||
auto rast = QSharedPointer<IFFChunk>(new RASTChunk());
|
||||
if (rast->readStructure(d) && rast->isValid())
|
||||
chunks.first()->_chunks.append(rast);
|
||||
}
|
||||
d->seek(pos);
|
||||
}
|
||||
|
||||
bool readStructure(QIODevice *d)
|
||||
{
|
||||
if (d == nullptr) {
|
||||
@@ -40,6 +71,7 @@ public:
|
||||
auto ok = false;
|
||||
auto chunks = IFFChunk::fromDevice(d, &ok);
|
||||
if (ok) {
|
||||
atariSTERast(d, chunks);
|
||||
m_chunks = chunks;
|
||||
}
|
||||
return ok;
|
||||
@@ -101,7 +133,7 @@ bool IFFHandler::canRead() const
|
||||
bool IFFHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::canRead() called with no device";
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::canRead(): called with no device";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -124,7 +156,7 @@ bool IFFHandler::canRead(QIODevice *device)
|
||||
auto pos = device->pos();
|
||||
auto chunks = IFFChunk::fromDevice(device, &ok);
|
||||
if (!device->seek(pos)) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::canRead() unable to reset device position";
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::canRead(): unable to reset device position";
|
||||
}
|
||||
if (ok) {
|
||||
auto forms = IFFHandlerPrivate::searchForms<FORMChunk>(chunks, true);
|
||||
@@ -214,14 +246,18 @@ static void addMetadata(QImage &img, const IFOR_Chunk *form)
|
||||
}
|
||||
|
||||
// resolution -> leave after set of EXIF chunk
|
||||
const DPIChunk *dpi = nullptr;
|
||||
auto dpis = IFFChunk::searchT<DPIChunk>(form);
|
||||
auto xbmis = IFFChunk::searchT<XBMIChunk>(form);
|
||||
if (!dpis.isEmpty()) {
|
||||
auto &&dpi = dpis.first();
|
||||
if (dpi->isValid()) {
|
||||
img.setDotsPerMeterX(dpi->dotsPerMeterX());
|
||||
img.setDotsPerMeterY(dpi->dotsPerMeterY());
|
||||
resChanged = true;
|
||||
}
|
||||
dpi = dpis.first();
|
||||
} else if (!xbmis.isEmpty()) {
|
||||
dpi = xbmis.first(); // never seen
|
||||
}
|
||||
if (dpi && dpi->isValid()) {
|
||||
img.setDotsPerMeterX(dpi->dotsPerMeterX());
|
||||
img.setDotsPerMeterY(dpi->dotsPerMeterY());
|
||||
resChanged = true;
|
||||
}
|
||||
|
||||
// if no explicit resolution was found, apply the aspect ratio to the default one
|
||||
@@ -248,26 +284,30 @@ static void addMetadata(QImage &img, const IFOR_Chunk *form)
|
||||
static QImage convertIPAL(const QImage& img, const IPALChunk *ipal)
|
||||
{
|
||||
if (img.format() != QImage::Format_Indexed8) {
|
||||
qDebug(LOG_IFFPLUGIN) << "convertIPAL(): the image is not indexed!";
|
||||
qCDebug(LOG_IFFPLUGIN) << "convertIPAL(): the image is not indexed!";
|
||||
return img;
|
||||
}
|
||||
|
||||
auto tmp = img.convertToFormat(FORMAT_RGB_8BIT);
|
||||
auto tmp = img.convertToFormat(ipal->hasAlpha() ? FORMAT_RGBA_8BIT : FORMAT_RGB_8BIT);
|
||||
if (tmp.isNull()) {
|
||||
qCritical(LOG_IFFPLUGIN) << "convertIPAL(): error while converting the image!";
|
||||
return img;
|
||||
}
|
||||
|
||||
auto mul = tmp.hasAlphaChannel() ? 4 : 3;
|
||||
for (auto y = 0, h = img.height(); y < h; ++y) {
|
||||
auto src = reinterpret_cast<const quint8 *>(img.constScanLine(y));
|
||||
auto dst = tmp.scanLine(y);
|
||||
auto lpal = ipal->palette(y, h);
|
||||
auto lpal = ipal->palette(y);
|
||||
for (auto x = 0, w = img.width(); x < w; ++x) {
|
||||
if (src[x] < lpal.size()) {
|
||||
auto x3 = x * 3;
|
||||
dst[x3] = qRed(lpal.at(src[x]));
|
||||
dst[x3 + 1] = qGreen(lpal.at(src[x]));
|
||||
dst[x3 + 2] = qBlue(lpal.at(src[x]));
|
||||
auto xmul = x * mul;
|
||||
dst[xmul] = qRed(lpal.at(src[x]));
|
||||
dst[xmul + 1] = qGreen(lpal.at(src[x]));
|
||||
dst[xmul + 2] = qBlue(lpal.at(src[x]));
|
||||
if (mul == 4) {
|
||||
dst[xmul + 3] = qAlpha(lpal.at(src[x]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,7 +327,7 @@ bool IFFHandler::readStandardImage(QImage *image)
|
||||
// show the first one (I don't have a sample with many images)
|
||||
auto headers = IFFChunk::searchT<BMHDChunk>(form);
|
||||
if (headers.isEmpty()) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage() no supported image found";
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage(): no supported image found";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -295,7 +335,7 @@ bool IFFHandler::readStandardImage(QImage *image)
|
||||
auto &&header = headers.first();
|
||||
auto img = imageAlloc(header->size(), form->format());
|
||||
if (img.isNull()) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage() error while allocating the image";
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage(): error while allocating the image";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -324,7 +364,19 @@ bool IFFHandler::readStandardImage(QImage *image)
|
||||
}
|
||||
|
||||
// reading image data
|
||||
auto ipal = form->searchIPal();
|
||||
std::unique_ptr<IPALChunk> ipal;
|
||||
if (auto ptr = form->searchIPal()) {
|
||||
ipal = std::unique_ptr<IPALChunk>(ptr->clone());
|
||||
}
|
||||
if (ipal) {
|
||||
auto pal = img.colorTable();
|
||||
if (pal.isEmpty() && cmap)
|
||||
pal = cmap->palette();
|
||||
if (!ipal->initialize(pal, img.height())) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage(): unable to initialize palette changer";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
auto bodies = IFFChunk::searchT<BODYChunk>(form);
|
||||
if (bodies.isEmpty()) {
|
||||
auto abits = IFFChunk::searchT<ABITChunk>(form);
|
||||
@@ -336,23 +388,23 @@ bool IFFHandler::readStandardImage(QImage *image)
|
||||
} else {
|
||||
auto &&body = bodies.first();
|
||||
if (!body->resetStrideRead(device())) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage() error while reading image data";
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage(): error while reading image data";
|
||||
return false;
|
||||
}
|
||||
for (auto y = 0, h = img.height(); y < h; ++y) {
|
||||
auto line = reinterpret_cast<char*>(img.scanLine(y));
|
||||
auto ba = body->strideRead(device(), y, header, camg, cmap, ipal, form->formType());
|
||||
auto ba = body->strideRead(device(), y, header, camg, cmap, ipal.get(), form->formType());
|
||||
if (ba.isEmpty()) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage() error while reading image scanline";
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readStandardImage(): error while reading image scanline";
|
||||
return false;
|
||||
}
|
||||
memcpy(line, ba.constData(), std::min(img.bytesPerLine(), ba.size()));
|
||||
}
|
||||
}
|
||||
|
||||
// BEAM / CTBL conversion (if not already done)
|
||||
// BEAM / CTBL, SHAM, RAST, PCHG conversion (if not already done)
|
||||
if (ipal && img.format() == QImage::Format_Indexed8) {
|
||||
img = convertIPAL(img, ipal);
|
||||
img = convertIPAL(img, ipal.get());
|
||||
}
|
||||
|
||||
// set metadata (including image resolution)
|
||||
@@ -374,7 +426,7 @@ bool IFFHandler::readMayaImage(QImage *image)
|
||||
// show the first one (I don't have a sample with many images)
|
||||
auto headers = IFFChunk::searchT<TBHDChunk>(form);
|
||||
if (headers.isEmpty()) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readMayaImage() no supported image found";
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readMayaImage(): no supported image found";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -382,30 +434,30 @@ bool IFFHandler::readMayaImage(QImage *image)
|
||||
auto &&header = headers.first();
|
||||
auto img = imageAlloc(header->size(), form->format());
|
||||
if (img.isNull()) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readMayaImage() error while allocating the image";
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readMayaImage(): error while allocating the image";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto &&tiles = IFFChunk::searchT<RGBAChunk>(form);
|
||||
if ((tiles.size() & 0xFFFF) != header->tiles()) { // Photoshop, on large images saves more than 65535 tiles
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readMayaImage() tile number mismatch: found" << tiles.size() << "while expected" << header->tiles();
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readMayaImage(): tile number mismatch: found" << tiles.size() << "while expected" << header->tiles();
|
||||
return false;
|
||||
}
|
||||
for (auto &&tile : tiles) {
|
||||
auto tp = tile->pos();
|
||||
auto ts = tile->size();
|
||||
if (tp.x() < 0 || tp.x() + ts.width() > img.width()) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readMayaImage() wrong tile position or size";
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readMayaImage(): wrong tile position or size";
|
||||
return false;
|
||||
}
|
||||
if (tp.y() < 0 || tp.y() + ts.height() > img.height()) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readMayaImage() wrong tile position or size";
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readMayaImage(): wrong tile position or size";
|
||||
return false;
|
||||
}
|
||||
// For future releases: it might be a good idea not to use a QPainter
|
||||
auto ti = tile->tile(device(), header);
|
||||
if (ti.isNull()) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readMayaImage() error while decoding the tile";
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::readMayaImage(): error while decoding the tile";
|
||||
return false;
|
||||
}
|
||||
QPainter painter(&img);
|
||||
@@ -426,7 +478,7 @@ bool IFFHandler::readMayaImage(QImage *image)
|
||||
bool IFFHandler::read(QImage *image)
|
||||
{
|
||||
if (!d->readStructure(device())) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::read() invalid IFF structure";
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::read(): invalid IFF structure";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -438,7 +490,7 @@ bool IFFHandler::read(QImage *image)
|
||||
return true;
|
||||
}
|
||||
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::read() no supported image found";
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::read(): no supported image found";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -515,7 +567,7 @@ int IFFHandler::imageCount() const
|
||||
|
||||
count = QImageIOHandler::imageCount();
|
||||
if (!d->readStructure(device())) {
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::imageCount() invalid IFF structure";
|
||||
qCWarning(LOG_IFFPLUGIN) << "IFFHandler::imageCount(): invalid IFF structure";
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
+22
-15
@@ -13,10 +13,17 @@
|
||||
#include <QIODevice>
|
||||
#include <QImage>
|
||||
#include <QImageReader>
|
||||
#include <QLoggingCategory>
|
||||
#include <QThread>
|
||||
|
||||
#include <openjpeg.h>
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_JP2PLUGIN, "kf.imageformats.plugins.jp2", QtDebugMsg)
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(LOG_JP2PLUGIN, "kf.imageformats.plugins.jp2", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
/* *** JP2_MAX_IMAGE_WIDTH and JP2_MAX_IMAGE_HEIGHT ***
|
||||
* The maximum size in pixel allowed by the plugin.
|
||||
*/
|
||||
@@ -48,19 +55,19 @@
|
||||
static void error_callback(const char *msg, void *client_data)
|
||||
{
|
||||
Q_UNUSED(client_data)
|
||||
qCritical() << msg;
|
||||
qCCritical(LOG_JP2PLUGIN) << msg;
|
||||
}
|
||||
|
||||
static void warning_callback(const char *msg, void *client_data)
|
||||
{
|
||||
Q_UNUSED(client_data)
|
||||
qWarning() << msg;
|
||||
qCWarning(LOG_JP2PLUGIN) << msg;
|
||||
}
|
||||
|
||||
static void info_callback(const char *msg, void *client_data)
|
||||
{
|
||||
Q_UNUSED(client_data)
|
||||
qInfo() << msg;
|
||||
qCInfo(LOG_JP2PLUGIN) << msg;
|
||||
}
|
||||
|
||||
static OPJ_SIZE_T jp2_read(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data)
|
||||
@@ -186,9 +193,9 @@ public:
|
||||
void enableThreads(opj_codec_t *codec) const
|
||||
{
|
||||
if (!opj_has_thread_support()) {
|
||||
qInfo() << "OpenJPEG doesn't support multi-threading!";
|
||||
qCInfo(LOG_JP2PLUGIN) << "OpenJPEG doesn't support multi-threading!";
|
||||
} else if (!opj_codec_set_threads(codec, std::max(1, QThread::idealThreadCount() / 2))) {
|
||||
qWarning() << "Unable to enable multi-threading!";
|
||||
qCWarning(LOG_JP2PLUGIN) << "Unable to enable multi-threading!";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,12 +237,12 @@ public:
|
||||
|
||||
opj_set_default_decoder_parameters(&m_dparameters);
|
||||
if (!opj_setup_decoder(m_jp2_codec, &m_dparameters)) {
|
||||
qCritical() << "Failed to setup JP2 decoder!";
|
||||
qCCritical(LOG_JP2PLUGIN) << "Failed to setup JP2 decoder!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!opj_read_header(m_jp2_stream, m_jp2_codec, &m_jp2_image)) {
|
||||
qCritical() << "Failed to read JP2 header!";
|
||||
qCCritical(LOG_JP2PLUGIN) << "Failed to read JP2 header!";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -308,7 +315,7 @@ public:
|
||||
}
|
||||
|
||||
if (!opj_decode(m_jp2_codec, m_jp2_stream, m_jp2_image)) {
|
||||
qCritical() << "Failed to decoding JP2 image!";
|
||||
qCCritical(LOG_JP2PLUGIN) << "Failed to decoding JP2 image!";
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -335,12 +342,12 @@ public:
|
||||
bool checkSizeLimits(qint32 width, qint32 height, qint32 nchannels) const
|
||||
{
|
||||
if (width > JP2_MAX_IMAGE_WIDTH || height > JP2_MAX_IMAGE_HEIGHT || width < 1 || height < 1) {
|
||||
qCritical() << "Maximum image size is limited to" << JP2_MAX_IMAGE_WIDTH << "x" << JP2_MAX_IMAGE_HEIGHT << "pixels";
|
||||
qCCritical(LOG_JP2PLUGIN) << "Maximum image size is limited to" << JP2_MAX_IMAGE_WIDTH << "x" << JP2_MAX_IMAGE_HEIGHT << "pixels";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (qint64(width) * qint64(height) > JP2_MAX_IMAGE_PIXELS) {
|
||||
qCritical() << "Maximum image size is limited to" << JP2_MAX_IMAGE_PIXELS << "pixels";
|
||||
qCCritical(LOG_JP2PLUGIN) << "Maximum image size is limited to" << JP2_MAX_IMAGE_PIXELS << "pixels";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -348,7 +355,7 @@ public:
|
||||
auto maxBytes = qint64(QImageReader::allocationLimit()) * 1024 * 1024;
|
||||
auto neededBytes = qint64(width) * height * nchannels * 4;
|
||||
if (maxBytes > 0 && neededBytes > maxBytes) {
|
||||
qCritical() << "Allocation limit set to" << (maxBytes / 1024 / 1024) << "MiB but" << (neededBytes / 1024 / 1024) << "MiB are needed!";
|
||||
qCCritical(LOG_JP2PLUGIN) << "Allocation limit set to" << (maxBytes / 1024 / 1024) << "MiB but" << (neededBytes / 1024 / 1024) << "MiB are needed!";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -584,7 +591,7 @@ public:
|
||||
#endif
|
||||
default:
|
||||
if (image.depth() > 32) {
|
||||
qWarning() << "The image is saved losing precision!";
|
||||
qCWarning(LOG_JP2PLUGIN) << "The image is saved losing precision!";
|
||||
}
|
||||
convFormat = ncomp == 4 ? QImage::Format_RGBA8888 : QImage::Format_RGBX8888;
|
||||
break;
|
||||
@@ -677,13 +684,13 @@ public:
|
||||
bool writeImage(QIODevice *device, const QImage &image)
|
||||
{
|
||||
if (!imageToJp2(image)) {
|
||||
qCritical() << "Error while creating JP2 image!";
|
||||
qCCritical(LOG_JP2PLUGIN) << "Error while creating JP2 image!";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<opj_codec_t, std::function<void(opj_codec_t *)>> codec(opj_create_compress(encoderFormat()), opj_destroy_codec);
|
||||
if (codec == nullptr) {
|
||||
qCritical() << "Error while creating encoder!";
|
||||
qCCritical(LOG_JP2PLUGIN) << "Error while creating encoder!";
|
||||
return false;
|
||||
}
|
||||
enableThreads(codec.get());
|
||||
@@ -754,7 +761,7 @@ bool JP2Handler::canRead() const
|
||||
bool JP2Handler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qWarning("JP2Handler::canRead() called with no device");
|
||||
qCWarning(LOG_JP2PLUGIN) << "JP2Handler::canRead() called with no device";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+106
-98
@@ -6,6 +6,7 @@
|
||||
SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <QLoggingCategory>
|
||||
#include <QThread>
|
||||
#include <QtGlobal>
|
||||
|
||||
@@ -19,6 +20,13 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_JXLPLUGIN, "kf.imageformats.plugins.jxl", QtDebugMsg)
|
||||
#else
|
||||
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
|
||||
@@ -163,7 +171,7 @@ bool QJpegXLHandler::ensureDecoder()
|
||||
|
||||
m_decoder = JxlDecoderCreate(nullptr);
|
||||
if (!m_decoder) {
|
||||
qWarning("ERROR: JxlDecoderCreate failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderCreate failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -182,14 +190,14 @@ bool QJpegXLHandler::ensureDecoder()
|
||||
m_runner = JxlThreadParallelRunnerCreate(nullptr, num_worker_threads);
|
||||
|
||||
if (JxlDecoderSetParallelRunner(m_decoder, JxlThreadParallelRunner, m_runner) != JXL_DEC_SUCCESS) {
|
||||
qWarning("ERROR: JxlDecoderSetParallelRunner failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSetParallelRunner failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (JxlDecoderSetInput(m_decoder, reinterpret_cast<const uint8_t *>(m_rawData.constData()), m_rawData.size()) != JXL_DEC_SUCCESS) {
|
||||
qWarning("ERROR: JxlDecoderSetInput failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSetInput failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -198,38 +206,38 @@ bool QJpegXLHandler::ensureDecoder()
|
||||
|
||||
JxlDecoderStatus status = JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME);
|
||||
if (status == JXL_DEC_ERROR) {
|
||||
qWarning("ERROR: JxlDecoderSubscribeEvents failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSubscribeEvents failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
|
||||
status = JxlDecoderProcessInput(m_decoder);
|
||||
if (status == JXL_DEC_ERROR) {
|
||||
qWarning("ERROR: JXL decoding failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JXL decoding failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
if (status == JXL_DEC_NEED_MORE_INPUT) {
|
||||
qWarning("ERROR: JXL data incomplete");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JXL data incomplete");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
|
||||
status = JxlDecoderGetBasicInfo(m_decoder, &m_basicinfo);
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
qWarning("ERROR: JXL basic info not available");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JXL basic info not available");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_basicinfo.xsize == 0 || m_basicinfo.ysize == 0) {
|
||||
qWarning("ERROR: JXL image has zero dimensions");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JXL image has zero dimensions");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_basicinfo.xsize > MAX_IMAGE_WIDTH || m_basicinfo.ysize > MAX_IMAGE_HEIGHT) {
|
||||
qWarning("JXL image (%dx%d) is too large", m_basicinfo.xsize, m_basicinfo.ysize);
|
||||
qCWarning(LOG_JXLPLUGIN, "JXL image (%dx%d) is too large", m_basicinfo.xsize, m_basicinfo.ysize);
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -246,7 +254,7 @@ bool QJpegXLHandler::countALLFrames()
|
||||
|
||||
JxlDecoderStatus status = JxlDecoderProcessInput(m_decoder);
|
||||
if (status != JXL_DEC_COLOR_ENCODING) {
|
||||
qWarning("Unexpected event %d instead of JXL_DEC_COLOR_ENCODING", status);
|
||||
qCWarning(LOG_JXLPLUGIN, "Unexpected event %d instead of JXL_DEC_COLOR_ENCODING", status);
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -258,10 +266,10 @@ bool QJpegXLHandler::countALLFrames()
|
||||
if (jxlcms) {
|
||||
status = JxlDecoderSetCms(m_decoder, *jxlcms);
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
qWarning("JxlDecoderSetCms ERROR");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlDecoderSetCms ERROR");
|
||||
}
|
||||
} else {
|
||||
qWarning("No JPEG XL CMS Interface");
|
||||
qCWarning(LOG_JXLPLUGIN, "No JPEG XL CMS Interface");
|
||||
}
|
||||
|
||||
JxlColorEncodingSetToSRGB(&color_encoding, is_gray ? JXL_TRUE : JXL_FALSE);
|
||||
@@ -338,16 +346,16 @@ bool QJpegXLHandler::countALLFrames()
|
||||
m_colorspace = QColorSpace::fromIccProfile(icc_data);
|
||||
|
||||
if (!m_colorspace.isValid()) {
|
||||
qWarning("JXL image has Qt-unsupported or invalid ICC profile!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JXL image has Qt-unsupported or invalid ICC profile!");
|
||||
}
|
||||
} else {
|
||||
qWarning("Failed to obtain data from JPEG XL decoder");
|
||||
qCWarning(LOG_JXLPLUGIN, "Failed to obtain data from JPEG XL decoder");
|
||||
}
|
||||
} else {
|
||||
qWarning("Empty ICC data");
|
||||
qCWarning(LOG_JXLPLUGIN, "Empty ICC data");
|
||||
}
|
||||
} else {
|
||||
qWarning("no ICC, other color profile");
|
||||
qCWarning(LOG_JXLPLUGIN, "no ICC, other color profile");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,13 +367,13 @@ bool QJpegXLHandler::countALLFrames()
|
||||
if (status != JXL_DEC_FRAME) {
|
||||
switch (status) {
|
||||
case JXL_DEC_ERROR:
|
||||
qWarning("ERROR: JXL decoding failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JXL decoding failed");
|
||||
break;
|
||||
case JXL_DEC_NEED_MORE_INPUT:
|
||||
qWarning("ERROR: JXL data incomplete");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JXL data incomplete");
|
||||
break;
|
||||
default:
|
||||
qWarning("Unexpected event %d instead of JXL_DEC_FRAME", status);
|
||||
qCWarning(LOG_JXLPLUGIN, "Unexpected event %d instead of JXL_DEC_FRAME", status);
|
||||
break;
|
||||
}
|
||||
m_parseState = ParseJpegXLError;
|
||||
@@ -373,7 +381,7 @@ bool QJpegXLHandler::countALLFrames()
|
||||
}
|
||||
|
||||
if (JxlDecoderGetFrameHeader(m_decoder, &frame_header) != JXL_DEC_SUCCESS) {
|
||||
qWarning("ERROR: JxlDecoderGetFrameHeader failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderGetFrameHeader failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -392,13 +400,13 @@ bool QJpegXLHandler::countALLFrames()
|
||||
}
|
||||
|
||||
if (m_framedelays.isEmpty()) {
|
||||
qWarning("no frames loaded by the JXL plug-in");
|
||||
qCWarning(LOG_JXLPLUGIN, "no frames loaded by the JXL plug-in");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_framedelays.count() == 1) {
|
||||
qWarning("JXL file was marked as animation but it has only one frame.");
|
||||
qCWarning(LOG_JXLPLUGIN, "JXL file was marked as animation but it has only one frame.");
|
||||
m_basicinfo.have_animation = JXL_FALSE;
|
||||
}
|
||||
} else { // static picture
|
||||
@@ -414,7 +422,7 @@ bool QJpegXLHandler::countALLFrames()
|
||||
for (uint32_t index = 0; index < m_basicinfo.num_extra_channels; index++) {
|
||||
status = JxlDecoderGetExtraChannelInfo(m_decoder, index, &channel_info);
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
qWarning("JxlDecoderGetExtraChannelInfo for channel %d returned %d", index, status);
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlDecoderGetExtraChannelInfo for channel %d returned %d", index, status);
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -430,7 +438,7 @@ bool QJpegXLHandler::countALLFrames()
|
||||
for (uint32_t alpha_index = index + 1; alpha_index < m_basicinfo.num_extra_channels; alpha_index++) {
|
||||
status = JxlDecoderGetExtraChannelInfo(m_decoder, alpha_index, &channel_info);
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
qWarning("JxlDecoderGetExtraChannelInfo for channel %d returned %d", alpha_index, status);
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlDecoderGetExtraChannelInfo for channel %d returned %d", alpha_index, status);
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -443,14 +451,14 @@ bool QJpegXLHandler::countALLFrames()
|
||||
}
|
||||
|
||||
if (!alpha_found) {
|
||||
qWarning("JXL BasicInfo indicate Alpha channel but it was not found");
|
||||
qCWarning(LOG_JXLPLUGIN, "JXL BasicInfo indicate Alpha channel but it was not found");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qWarning("JXL has BLACK channel but colorspace is not CMYK!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JXL has BLACK channel but colorspace is not CMYK!");
|
||||
}
|
||||
break;
|
||||
} else if ((channel_info.type == JXL_CHANNEL_ALPHA) && !alpha_found) {
|
||||
@@ -460,7 +468,7 @@ bool QJpegXLHandler::countALLFrames()
|
||||
}
|
||||
|
||||
if (!m_isCMYK && (m_colorspace.colorModel() == QColorSpace::ColorModel::Cmyk)) {
|
||||
qWarning("JXL has CMYK colorspace but BLACK channel was not found!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JXL has CMYK colorspace but BLACK channel was not found!");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -484,7 +492,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
{
|
||||
JxlDecoderStatus status = JxlDecoderProcessInput(m_decoder);
|
||||
if (status != JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
|
||||
qWarning("Unexpected event %d instead of JXL_DEC_NEED_IMAGE_OUT_BUFFER", status);
|
||||
qCWarning(LOG_JXLPLUGIN, "Unexpected event %d instead of JXL_DEC_NEED_IMAGE_OUT_BUFFER", status);
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -512,7 +520,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
if (m_basicinfo.alpha_bits > 0) { // CMYK + alpha
|
||||
QImage tmp_cmyk_image = imageAlloc(m_basicinfo.xsize, m_basicinfo.ysize, QImage::Format_CMYK8888);
|
||||
if (tmp_cmyk_image.isNull()) {
|
||||
qWarning("Memory cannot be allocated");
|
||||
qCWarning(LOG_JXLPLUGIN, "Memory cannot be allocated");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -521,7 +529,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
|
||||
uchar *pixels_alpha = reinterpret_cast<uchar *>(malloc(extra_buffer_size));
|
||||
if (!pixels_alpha) {
|
||||
qWarning("Memory cannot be allocated for ALPHA channel");
|
||||
qCWarning(LOG_JXLPLUGIN, "Memory cannot be allocated for ALPHA channel");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -530,7 +538,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
if (!pixels_cmy) {
|
||||
free(pixels_alpha);
|
||||
pixels_alpha = nullptr;
|
||||
qWarning("Memory cannot be allocated for CMY buffer");
|
||||
qCWarning(LOG_JXLPLUGIN, "Memory cannot be allocated for CMY buffer");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -541,7 +549,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
pixels_cmy = nullptr;
|
||||
free(pixels_alpha);
|
||||
pixels_alpha = nullptr;
|
||||
qWarning("Memory cannot be allocated for BLACK buffer");
|
||||
qCWarning(LOG_JXLPLUGIN, "Memory cannot be allocated for BLACK buffer");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -553,7 +561,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
pixels_cmy = nullptr;
|
||||
free(pixels_alpha);
|
||||
pixels_alpha = nullptr;
|
||||
qWarning("ERROR: JxlDecoderSetImageOutBuffer failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSetImageOutBuffer failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -565,7 +573,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
pixels_cmy = nullptr;
|
||||
free(pixels_alpha);
|
||||
pixels_alpha = nullptr;
|
||||
qWarning("ERROR: JxlDecoderSetExtraChannelBuffer failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSetExtraChannelBuffer failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -577,7 +585,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
pixels_cmy = nullptr;
|
||||
free(pixels_alpha);
|
||||
pixels_alpha = nullptr;
|
||||
qWarning("ERROR: JxlDecoderSetExtraChannelBuffer failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSetExtraChannelBuffer failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -590,7 +598,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
pixels_cmy = nullptr;
|
||||
free(pixels_alpha);
|
||||
pixels_alpha = nullptr;
|
||||
qWarning("Unexpected event %d instead of JXL_DEC_FULL_IMAGE", status);
|
||||
qCWarning(LOG_JXLPLUGIN, "Unexpected event %d instead of JXL_DEC_FULL_IMAGE", status);
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -624,7 +632,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
if (m_current_image.isNull()) {
|
||||
free(pixels_alpha);
|
||||
pixels_alpha = nullptr;
|
||||
qWarning("ERROR: convertedToColorSpace returned empty image");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: convertedToColorSpace returned empty image");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -652,7 +660,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
} else { // CMYK (no alpha)
|
||||
m_current_image = imageAlloc(m_basicinfo.xsize, m_basicinfo.ysize, QImage::Format_CMYK8888);
|
||||
if (m_current_image.isNull()) {
|
||||
qWarning("Memory cannot be allocated");
|
||||
qCWarning(LOG_JXLPLUGIN, "Memory cannot be allocated");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -661,7 +669,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
|
||||
pixels_cmy = reinterpret_cast<uchar *>(malloc(cmy_buffer_size));
|
||||
if (!pixels_cmy) {
|
||||
qWarning("Memory cannot be allocated for CMY buffer");
|
||||
qCWarning(LOG_JXLPLUGIN, "Memory cannot be allocated for CMY buffer");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -670,7 +678,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
if (!pixels_black) {
|
||||
free(pixels_cmy);
|
||||
pixels_cmy = nullptr;
|
||||
qWarning("Memory cannot be allocated for BLACK buffer");
|
||||
qCWarning(LOG_JXLPLUGIN, "Memory cannot be allocated for BLACK buffer");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -680,7 +688,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
pixels_black = nullptr;
|
||||
free(pixels_cmy);
|
||||
pixels_cmy = nullptr;
|
||||
qWarning("ERROR: JxlDecoderSetImageOutBuffer failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSetImageOutBuffer failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -690,7 +698,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
pixels_black = nullptr;
|
||||
free(pixels_cmy);
|
||||
pixels_cmy = nullptr;
|
||||
qWarning("ERROR: JxlDecoderSetExtraChannelBuffer failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSetExtraChannelBuffer failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -701,7 +709,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
pixels_black = nullptr;
|
||||
free(pixels_cmy);
|
||||
pixels_cmy = nullptr;
|
||||
qWarning("Unexpected event %d instead of JXL_DEC_FULL_IMAGE", status);
|
||||
qCWarning(LOG_JXLPLUGIN, "Unexpected event %d instead of JXL_DEC_FULL_IMAGE", status);
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -739,7 +747,7 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
} else { // RGB or GRAY
|
||||
m_current_image = imageAlloc(m_basicinfo.xsize, m_basicinfo.ysize, m_input_image_format);
|
||||
if (m_current_image.isNull()) {
|
||||
qWarning("Memory cannot be allocated");
|
||||
qCWarning(LOG_JXLPLUGIN, "Memory cannot be allocated");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -761,21 +769,21 @@ bool QJpegXLHandler::decode_one_frame()
|
||||
rgb_buffer_size += 2 * size_t(m_input_pixel_format.num_channels) * size_t(m_current_image.width());
|
||||
break;
|
||||
default:
|
||||
qWarning("ERROR: unsupported data type");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: unsupported data type");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (JxlDecoderSetImageOutBuffer(m_decoder, &m_input_pixel_format, m_current_image.bits(), rgb_buffer_size) != JXL_DEC_SUCCESS) {
|
||||
qWarning("ERROR: JxlDecoderSetImageOutBuffer failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSetImageOutBuffer failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
|
||||
status = JxlDecoderProcessInput(m_decoder);
|
||||
if (status != JXL_DEC_FULL_IMAGE) {
|
||||
qWarning("Unexpected event %d instead of JXL_DEC_FULL_IMAGE", status);
|
||||
qCWarning(LOG_JXLPLUGIN, "Unexpected event %d instead of JXL_DEC_FULL_IMAGE", status);
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -841,29 +849,29 @@ bool QJpegXLHandler::read(QImage *image)
|
||||
bool QJpegXLHandler::write(const QImage &image)
|
||||
{
|
||||
if (image.format() == QImage::Format_Invalid) {
|
||||
qWarning("No image data to save");
|
||||
qCWarning(LOG_JXLPLUGIN, "No image data to save");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((image.width() == 0) || (image.height() == 0)) {
|
||||
qWarning("Image has zero dimension!");
|
||||
qCWarning(LOG_JXLPLUGIN, "Image has zero dimension!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((image.width() > MAX_IMAGE_WIDTH) || (image.height() > MAX_IMAGE_HEIGHT)) {
|
||||
qWarning("Image (%dx%d) is too large to save!", image.width(), image.height());
|
||||
qCWarning(LOG_JXLPLUGIN, "Image (%dx%d) is too large to save!", image.width(), image.height());
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t pixel_count = size_t(image.width()) * image.height();
|
||||
if (MAX_IMAGE_PIXELS && pixel_count > MAX_IMAGE_PIXELS) {
|
||||
qWarning("Image (%dx%d) will not be saved because it has more than %d megapixels!", image.width(), image.height(), MAX_IMAGE_PIXELS / 1024 / 1024);
|
||||
qCWarning(LOG_JXLPLUGIN, "Image (%dx%d) will not be saved because it has more than %d megapixels!", image.width(), image.height(), MAX_IMAGE_PIXELS / 1024 / 1024);
|
||||
return false;
|
||||
}
|
||||
|
||||
JxlEncoder *encoder = JxlEncoderCreate(nullptr);
|
||||
if (!encoder) {
|
||||
qWarning("Failed to create Jxl encoder");
|
||||
qCWarning(LOG_JXLPLUGIN, "Failed to create Jxl encoder");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -873,7 +881,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
if (num_worker_threads > 1) {
|
||||
runner = JxlThreadParallelRunnerCreate(nullptr, num_worker_threads);
|
||||
if (JxlEncoderSetParallelRunner(encoder, JxlThreadParallelRunner, runner) != JXL_ENC_SUCCESS) {
|
||||
qWarning("JxlEncoderSetParallelRunner failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlEncoderSetParallelRunner failed");
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
JxlEncoderDestroy(encoder);
|
||||
return false;
|
||||
@@ -953,7 +961,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
|
||||
const QByteArray cmyk_profile = image.colorSpace().iccProfile();
|
||||
if (cmyk_profile.isEmpty()) {
|
||||
qWarning("ERROR saving CMYK JXL: empty ICC profile");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR saving CMYK JXL: empty ICC profile");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -963,7 +971,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
|
||||
status = JxlEncoderSetBasicInfo(encoder, &output_info);
|
||||
if (status != JXL_ENC_SUCCESS) {
|
||||
qWarning("JxlEncoderSetBasicInfo for CMYK image failed!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlEncoderSetBasicInfo for CMYK image failed!");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -973,7 +981,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
|
||||
status = JxlEncoderSetExtraChannelInfo(encoder, 0, &extra_black_channel);
|
||||
if (status != JXL_ENC_SUCCESS) {
|
||||
qWarning("JxlEncoderSetExtraChannelInfo for CMYK image failed!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlEncoderSetExtraChannelInfo for CMYK image failed!");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -983,7 +991,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
|
||||
status = JxlEncoderSetICCProfile(encoder, reinterpret_cast<const uint8_t *>(cmyk_profile.constData()), cmyk_profile.size());
|
||||
if (status != JXL_ENC_SUCCESS) {
|
||||
qWarning("JxlEncoderSetICCProfile for CMYK image failed!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlEncoderSetICCProfile for CMYK image failed!");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -996,7 +1004,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
const char *box_type = "Exif";
|
||||
status = JxlEncoderAddBox(encoder, box_type, reinterpret_cast<const uint8_t *>(exif_data.constData()), exif_data.size(), JXL_FALSE);
|
||||
if (status != JXL_ENC_SUCCESS) {
|
||||
qWarning("JxlEncoderAddBox failed!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlEncoderAddBox failed!");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -1009,7 +1017,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
const char *box_type = "xml ";
|
||||
status = JxlEncoderAddBox(encoder, box_type, reinterpret_cast<const uint8_t *>(xmp_data.constData()), xmp_data.size(), JXL_FALSE);
|
||||
if (status != JXL_ENC_SUCCESS) {
|
||||
qWarning("JxlEncoderAddBox failed!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlEncoderAddBox failed!");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -1027,7 +1035,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
|
||||
pixels_cmy = reinterpret_cast<uchar *>(malloc(cmy_buffer_size));
|
||||
if (!pixels_cmy) {
|
||||
qWarning("Memory cannot be allocated for CMY buffer");
|
||||
qCWarning(LOG_JXLPLUGIN, "Memory cannot be allocated for CMY buffer");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -1037,7 +1045,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
|
||||
pixels_black = reinterpret_cast<uchar *>(malloc(extra_buffer_size));
|
||||
if (!pixels_black) {
|
||||
qWarning("Memory cannot be allocated for BLACK buffer");
|
||||
qCWarning(LOG_JXLPLUGIN, "Memory cannot be allocated for BLACK buffer");
|
||||
free(pixels_cmy);
|
||||
pixels_cmy = nullptr;
|
||||
|
||||
@@ -1074,7 +1082,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
|
||||
status = JxlEncoderAddImageFrame(frame_settings_lossless, &pixel_format, pixels_cmy, cmy_buffer_size);
|
||||
if (status == JXL_ENC_ERROR) {
|
||||
qWarning("JxlEncoderAddImageFrame failed!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlEncoderAddImageFrame failed!");
|
||||
free(pixels_black);
|
||||
pixels_black = nullptr;
|
||||
free(pixels_cmy);
|
||||
@@ -1094,7 +1102,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
pixels_cmy = nullptr;
|
||||
|
||||
if (status == JXL_ENC_ERROR) {
|
||||
qWarning("JxlEncoderSetExtraChannelBuffer failed!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlEncoderSetExtraChannelBuffer failed!");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -1263,7 +1271,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
if (gray_profile.isValid()) {
|
||||
tmpimage = image.convertedToColorSpace(gray_profile, tmpformat);
|
||||
} else {
|
||||
qWarning("JXL plugin created invalid grayscale QColorSpace!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JXL plugin created invalid grayscale QColorSpace!");
|
||||
tmpimage = image.convertToFormat(tmpformat);
|
||||
}
|
||||
} else if (!is_gray && image.colorSpace().colorModel() != QColorSpace::ColorModel::Rgb) {
|
||||
@@ -1287,7 +1295,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
if (rgb_profile.isValid()) {
|
||||
tmpimage = image.convertedToColorSpace(rgb_profile, tmpformat);
|
||||
} else {
|
||||
qWarning("JXL plugin created invalid RGB QColorSpace!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JXL plugin created invalid RGB QColorSpace!");
|
||||
tmpimage = image.convertToFormat(tmpformat);
|
||||
}
|
||||
} else { // ColorSpace matches the format
|
||||
@@ -1304,7 +1312,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
output_info.ysize = tmpimage.height();
|
||||
|
||||
if (output_info.xsize == 0 || output_info.ysize == 0 || tmpimage.isNull()) {
|
||||
qWarning("Unable to allocate memory for output image");
|
||||
qCWarning(LOG_JXLPLUGIN, "Unable to allocate memory for output image");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -1420,7 +1428,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
|
||||
status = JxlEncoderSetBasicInfo(encoder, &output_info);
|
||||
if (status != JXL_ENC_SUCCESS) {
|
||||
qWarning("JxlEncoderSetBasicInfo failed!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlEncoderSetBasicInfo failed!");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -1431,7 +1439,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
if (iccprofile.size() > 0) {
|
||||
status = JxlEncoderSetICCProfile(encoder, reinterpret_cast<const uint8_t *>(iccprofile.constData()), iccprofile.size());
|
||||
if (status != JXL_ENC_SUCCESS) {
|
||||
qWarning("JxlEncoderSetICCProfile failed!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlEncoderSetICCProfile failed!");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -1441,7 +1449,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
} else {
|
||||
status = JxlEncoderSetColorEncoding(encoder, &color_profile);
|
||||
if (status != JXL_ENC_SUCCESS) {
|
||||
qWarning("JxlEncoderSetColorEncoding failed!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlEncoderSetColorEncoding failed!");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -1455,7 +1463,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
const char *box_type = "Exif";
|
||||
status = JxlEncoderAddBox(encoder, box_type, reinterpret_cast<const uint8_t *>(exif_data.constData()), exif_data.size(), JXL_FALSE);
|
||||
if (status != JXL_ENC_SUCCESS) {
|
||||
qWarning("JxlEncoderAddBox failed!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlEncoderAddBox failed!");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -1468,7 +1476,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
const char *box_type = "xml ";
|
||||
status = JxlEncoderAddBox(encoder, box_type, reinterpret_cast<const uint8_t *>(xmp_data.constData()), xmp_data.size(), JXL_FALSE);
|
||||
if (status != JXL_ENC_SUCCESS) {
|
||||
qWarning("JxlEncoderAddBox failed!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlEncoderAddBox failed!");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -1494,7 +1502,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
|
||||
float *packed_pixels32 = reinterpret_cast<float *>(malloc(buffer_size));
|
||||
if (!packed_pixels32) {
|
||||
qWarning("ERROR: JXL plug-in failed to allocate memory");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JXL plug-in failed to allocate memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1522,7 +1530,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
|
||||
quint16 *packed_pixels16 = reinterpret_cast<quint16 *>(malloc(buffer_size));
|
||||
if (!packed_pixels16) {
|
||||
qWarning("ERROR: JXL plug-in failed to allocate memory");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JXL plug-in failed to allocate memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1560,7 +1568,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
buffer_size += 2 * size_t(pixel_format.num_channels) * size_t(tmpimage.width());
|
||||
break;
|
||||
default:
|
||||
qWarning("ERROR: unsupported data type");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: unsupported data type");
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@@ -1569,7 +1577,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
}
|
||||
|
||||
if (status == JXL_ENC_ERROR) {
|
||||
qWarning("JxlEncoderAddImageFrame failed!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlEncoderAddImageFrame failed!");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -1594,7 +1602,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
offset = next_out - compressed.data();
|
||||
compressed.resize(compressed.size() * 2);
|
||||
} else if (status == JXL_ENC_ERROR) {
|
||||
qWarning("JxlEncoderProcessOutput failed!");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlEncoderProcessOutput failed!");
|
||||
if (runner) {
|
||||
JxlThreadParallelRunnerDestroy(runner);
|
||||
}
|
||||
@@ -1616,7 +1624,7 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
if (write_status > 0) {
|
||||
return true;
|
||||
} else if (write_status == -1) {
|
||||
qWarning("Write error: %s\n", qUtf8Printable(device()->errorString()));
|
||||
qCWarning(LOG_JXLPLUGIN, "Write error: %s\n", qUtf8Printable(device()->errorString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1838,14 +1846,14 @@ bool QJpegXLHandler::rewind()
|
||||
JxlDecoderRewind(m_decoder);
|
||||
if (m_runner) {
|
||||
if (JxlDecoderSetParallelRunner(m_decoder, JxlThreadParallelRunner, m_runner) != JXL_DEC_SUCCESS) {
|
||||
qWarning("ERROR: JxlDecoderSetParallelRunner failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSetParallelRunner failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (JxlDecoderSetInput(m_decoder, reinterpret_cast<const uint8_t *>(m_rawData.constData()), m_rawData.size()) != JXL_DEC_SUCCESS) {
|
||||
qWarning("ERROR: JxlDecoderSetInput failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSetInput failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -1854,14 +1862,14 @@ bool QJpegXLHandler::rewind()
|
||||
|
||||
if (m_basicinfo.uses_original_profile == JXL_FALSE && m_basicinfo.have_animation == JXL_FALSE) {
|
||||
if (JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE) != JXL_DEC_SUCCESS) {
|
||||
qWarning("ERROR: JxlDecoderSubscribeEvents failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSubscribeEvents failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
|
||||
JxlDecoderStatus status = JxlDecoderProcessInput(m_decoder);
|
||||
if (status != JXL_DEC_COLOR_ENCODING) {
|
||||
qWarning("Unexpected event %d instead of JXL_DEC_COLOR_ENCODING", status);
|
||||
qCWarning(LOG_JXLPLUGIN, "Unexpected event %d instead of JXL_DEC_COLOR_ENCODING", status);
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -1870,10 +1878,10 @@ bool QJpegXLHandler::rewind()
|
||||
if (jxlcms) {
|
||||
status = JxlDecoderSetCms(m_decoder, *jxlcms);
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
qWarning("JxlDecoderSetCms ERROR");
|
||||
qCWarning(LOG_JXLPLUGIN, "JxlDecoderSetCms ERROR");
|
||||
}
|
||||
} else {
|
||||
qWarning("No JPEG XL CMS Interface");
|
||||
qCWarning(LOG_JXLPLUGIN, "No JPEG XL CMS Interface");
|
||||
}
|
||||
|
||||
bool is_gray = m_basicinfo.num_color_channels == 1 && m_basicinfo.alpha_bits == 0;
|
||||
@@ -1882,7 +1890,7 @@ bool QJpegXLHandler::rewind()
|
||||
JxlDecoderSetPreferredColorProfile(m_decoder, &color_encoding);
|
||||
} else {
|
||||
if (JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_FULL_IMAGE) != JXL_DEC_SUCCESS) {
|
||||
qWarning("ERROR: JxlDecoderSubscribeEvents failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSubscribeEvents failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -1913,7 +1921,7 @@ bool QJpegXLHandler::decodeContainer()
|
||||
JxlDecoderRewind(m_decoder);
|
||||
|
||||
if (JxlDecoderSetInput(m_decoder, buf, len) != JXL_DEC_SUCCESS) {
|
||||
qWarning("ERROR: JxlDecoderSetInput failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSetInput failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -1921,11 +1929,11 @@ bool QJpegXLHandler::decodeContainer()
|
||||
JxlDecoderCloseInput(m_decoder);
|
||||
|
||||
if (JxlDecoderSetDecompressBoxes(m_decoder, JXL_TRUE) != JXL_DEC_SUCCESS) {
|
||||
qWarning("WARNING: JxlDecoderSetDecompressBoxes failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "WARNING: JxlDecoderSetDecompressBoxes failed");
|
||||
}
|
||||
|
||||
if (JxlDecoderSubscribeEvents(m_decoder, JXL_DEC_BOX | JXL_DEC_BOX_COMPLETE) != JXL_DEC_SUCCESS) {
|
||||
qWarning("ERROR: JxlDecoderSubscribeEvents failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSubscribeEvents failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -1947,7 +1955,7 @@ bool QJpegXLHandler::decodeContainer()
|
||||
case JXL_DEC_BOX:
|
||||
status = JxlDecoderGetBoxType(m_decoder, box_type, JXL_TRUE);
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
qWarning("Error in JxlDecoderGetBoxType");
|
||||
qCWarning(LOG_JXLPLUGIN, "Error in JxlDecoderGetBoxType");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -1965,17 +1973,17 @@ bool QJpegXLHandler::decodeContainer()
|
||||
}
|
||||
break;
|
||||
case JXL_DEC_ERROR:
|
||||
qWarning("JXL Metadata decoding error");
|
||||
qCWarning(LOG_JXLPLUGIN, "JXL Metadata decoding error");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
break;
|
||||
case JXL_DEC_NEED_MORE_INPUT:
|
||||
qWarning("JXL metadata are probably incomplete");
|
||||
qCWarning(LOG_JXLPLUGIN, "JXL metadata are probably incomplete");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
qWarning("Unexpected event %d instead of JXL_DEC_BOX", status);
|
||||
qCWarning(LOG_JXLPLUGIN, "Unexpected event %d instead of JXL_DEC_BOX", status);
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
break;
|
||||
@@ -2003,7 +2011,7 @@ bool QJpegXLHandler::decodeContainer()
|
||||
} else if (headerindexBE != -1) {
|
||||
m_exif = exifBox.mid(headerindexBE);
|
||||
} else {
|
||||
qWarning("Exif box in JXL file doesn't have TIFF header");
|
||||
qCWarning(LOG_JXLPLUGIN, "Exif box in JXL file doesn't have TIFF header");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -2016,13 +2024,13 @@ bool QJpegXLHandler::extractBox(QByteArray &output, size_t container_size)
|
||||
uint64_t rawboxsize = 0;
|
||||
JxlDecoderStatus status = JxlDecoderGetBoxSizeRaw(m_decoder, &rawboxsize);
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
qWarning("ERROR: JxlDecoderGetBoxSizeRaw failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderGetBoxSizeRaw failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rawboxsize > container_size) {
|
||||
qWarning("JXL metadata box is incomplete");
|
||||
qCWarning(LOG_JXLPLUGIN, "JXL metadata box is incomplete");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -2030,7 +2038,7 @@ bool QJpegXLHandler::extractBox(QByteArray &output, size_t container_size)
|
||||
output.resize(rawboxsize);
|
||||
status = JxlDecoderSetBoxBuffer(m_decoder, reinterpret_cast<uint8_t *>(output.data()), output.size());
|
||||
if (status != JXL_DEC_SUCCESS) {
|
||||
qWarning("ERROR: JxlDecoderSetBoxBuffer failed");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSetBoxBuffer failed");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -2041,7 +2049,7 @@ bool QJpegXLHandler::extractBox(QByteArray &output, size_t container_size)
|
||||
size_t bytes_remains = JxlDecoderReleaseBoxBuffer(m_decoder);
|
||||
|
||||
if (output.size() > 4194304) { // approx. 4MB limit for decompressed metadata box
|
||||
qWarning("JXL metadata box is too large");
|
||||
qCWarning(LOG_JXLPLUGIN, "JXL metadata box is too large");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -2051,7 +2059,7 @@ bool QJpegXLHandler::extractBox(QByteArray &output, size_t container_size)
|
||||
uint8_t *extension_buffer = reinterpret_cast<uint8_t *>(output.data()) + (output.size() - extension_size);
|
||||
|
||||
if (JxlDecoderSetBoxBuffer(m_decoder, extension_buffer, extension_size) != JXL_DEC_SUCCESS) {
|
||||
qWarning("ERROR: JxlDecoderSetBoxBuffer failed after JXL_DEC_BOX_NEED_MORE_OUTPUT");
|
||||
qCWarning(LOG_JXLPLUGIN, "ERROR: JxlDecoderSetBoxBuffer failed after JXL_DEC_BOX_NEED_MORE_OUTPUT");
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
@@ -2059,7 +2067,7 @@ bool QJpegXLHandler::extractBox(QByteArray &output, size_t container_size)
|
||||
} while (status == JXL_DEC_BOX_NEED_MORE_OUTPUT);
|
||||
|
||||
if (status != JXL_DEC_BOX_COMPLETE) {
|
||||
qWarning("Unexpected event %d instead of JXL_DEC_BOX_COMPLETE", status);
|
||||
qCWarning(LOG_JXLPLUGIN, "Unexpected event %d instead of JXL_DEC_BOX_COMPLETE", status);
|
||||
m_parseState = ParseJpegXLError;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -977,8 +977,14 @@ bool JXRHandler::read(QImage *outImage)
|
||||
if (auto err = d->pDecoder->GetResolution(d->pDecoder, &hres, &vres)) {
|
||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::read() error while reading resolution:" << err;
|
||||
} else {
|
||||
img.setDotsPerMeterX(qRound(hres * 1000 / 25.4));
|
||||
img.setDotsPerMeterY(qRound(vres * 1000 / 25.4));
|
||||
const qint32 hdpm = dpi2ppm(hres);
|
||||
if (hdpm > 0) {
|
||||
img.setDotsPerMeterX(hdpm);
|
||||
}
|
||||
const qint32 vdpm = dpi2ppm(vres);
|
||||
if (vdpm > 0) {
|
||||
img.setDotsPerMeterY(vdpm);
|
||||
}
|
||||
}
|
||||
|
||||
// alpha copy mode
|
||||
@@ -1124,7 +1130,7 @@ bool JXRHandler::write(const QImage &image)
|
||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::write() error while setting the image size:" << err;
|
||||
return false;
|
||||
}
|
||||
if (auto err = d->pEncoder->SetResolution(d->pEncoder, qi.dotsPerMeterX() * 25.4 / 1000, qi.dotsPerMeterY() * 25.4 / 1000)) {
|
||||
if (auto err = d->pEncoder->SetResolution(d->pEncoder, dppm2dpi(qi.dotsPerMeterX()), dppm2dpi(qi.dotsPerMeterY()))) {
|
||||
qCWarning(LOG_JXRPLUGIN) << "JXRHandler::write() error while setting the image resolution:" << err;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1195,11 +1195,15 @@ void MicroExif::updateImageMetadata(QImage &targetImage, bool replaceExisting) c
|
||||
|
||||
bool MicroExif::updateImageResolution(QImage &targetImage)
|
||||
{
|
||||
if (horizontalResolution() > 0)
|
||||
targetImage.setDotsPerMeterX(qRound(horizontalResolution() / 25.4 * 1000));
|
||||
if (verticalResolution() > 0)
|
||||
targetImage.setDotsPerMeterY(qRound(verticalResolution() / 25.4 * 1000));
|
||||
return (horizontalResolution() > 0) || (verticalResolution() > 0);
|
||||
const auto hdpm = dpi2ppm(horizontalResolution());
|
||||
if (hdpm > 0) {
|
||||
targetImage.setDotsPerMeterX(hdpm);
|
||||
}
|
||||
const auto vdpm = dpi2ppm(verticalResolution());
|
||||
if (vdpm > 0) {
|
||||
targetImage.setDotsPerMeterY(vdpm);
|
||||
}
|
||||
return (hdpm > 0) || (vdpm > 0);
|
||||
}
|
||||
|
||||
MicroExif MicroExif::fromByteArray(const QByteArray &ba, bool searchHeader)
|
||||
@@ -1269,8 +1273,8 @@ MicroExif MicroExif::fromImage(const QImage &image)
|
||||
// Image properties
|
||||
exif.setWidth(image.width());
|
||||
exif.setHeight(image.height());
|
||||
exif.setHorizontalResolution(image.dotsPerMeterX() * 25.4 / 1000);
|
||||
exif.setVerticalResolution(image.dotsPerMeterY() * 25.4 / 1000);
|
||||
exif.setHorizontalResolution(dppm2dpi(image.dotsPerMeterX()));
|
||||
exif.setVerticalResolution(dppm2dpi(image.dotsPerMeterY()));
|
||||
exif.setColorSpace(image.colorSpace());
|
||||
|
||||
// TIFF strings
|
||||
|
||||
+28
-17
@@ -12,8 +12,14 @@
|
||||
#include <QColor>
|
||||
#include <QColorSpace>
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_PCXPLUGIN, "kf.imageformats.plugins.pcx", QtDebugMsg)
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(LOG_PCXPLUGIN, "kf.imageformats.plugins.pcx", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
#pragma pack(push, 1)
|
||||
class RGB
|
||||
@@ -325,7 +331,7 @@ static bool readImage1(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||
img.setColorCount(2);
|
||||
|
||||
if (img.isNull()) {
|
||||
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||
qCWarning(LOG_PCXPLUGIN) << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -360,12 +366,12 @@ static bool readImage4(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||
img = imageAlloc(header.width(), header.height(), header.format());
|
||||
img.setColorCount(16);
|
||||
if (img.isNull()) {
|
||||
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||
qCWarning(LOG_PCXPLUGIN) << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.BytesPerLine < (header.width() + 7) / 8) {
|
||||
qWarning() << "PCX image has invalid BytesPerLine value";
|
||||
qCWarning(LOG_PCXPLUGIN) << "PCX image has invalid BytesPerLine value";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -390,7 +396,7 @@ static bool readImage4(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||
|
||||
uchar *p = img.scanLine(y);
|
||||
if (!p) {
|
||||
qWarning() << "Failed to get scanline for" << y << "might be out of bounds";
|
||||
qCWarning(LOG_PCXPLUGIN) << "Failed to get scanline for" << y << "might be out of bounds";
|
||||
}
|
||||
for (int x = 0; x < header.width(); ++x) {
|
||||
p[x] = pixbuf[x];
|
||||
@@ -413,7 +419,7 @@ static bool readImage2(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||
img.setColorCount(4);
|
||||
|
||||
if (img.isNull()) {
|
||||
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||
qCWarning(LOG_PCXPLUGIN) << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -456,7 +462,7 @@ static bool readImage4v2(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||
img.setColorCount(16);
|
||||
|
||||
if (img.isNull()) {
|
||||
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||
qCWarning(LOG_PCXPLUGIN) << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -497,7 +503,7 @@ static bool readImage8(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||
img.setColorCount(256);
|
||||
|
||||
if (img.isNull()) {
|
||||
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||
qCWarning(LOG_PCXPLUGIN) << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -535,7 +541,6 @@ static bool readImage8(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||
}
|
||||
}
|
||||
|
||||
// qDebug() << "Palette Flag: " << flag;
|
||||
if (flag == 12 && (header.Version == 5 || header.Version == 2)) {
|
||||
// Read the palette
|
||||
quint8 r;
|
||||
@@ -560,7 +565,7 @@ static bool readImage24(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||
img = imageAlloc(header.width(), header.height(), header.format());
|
||||
|
||||
if (img.isNull()) {
|
||||
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||
qCWarning(LOG_PCXPLUGIN) << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -774,10 +779,10 @@ static bool writeImage24(const QImage &image, QDataStream &s, PCXHEADER &header)
|
||||
return false;
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
auto cs = image.colorSpace();
|
||||
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;
|
||||
@@ -888,8 +893,14 @@ bool PCXHandler::read(QImage *outImage)
|
||||
return false;
|
||||
}
|
||||
|
||||
img.setDotsPerMeterX(qRound(header.HDpi / 25.4 * 1000));
|
||||
img.setDotsPerMeterY(qRound(header.YDpi / 25.4 * 1000));
|
||||
auto hres = dpi2ppm(header.HDpi);
|
||||
if (hres > 0) {
|
||||
img.setDotsPerMeterX(hres);
|
||||
}
|
||||
auto vres = dpi2ppm(header.YDpi);
|
||||
if (vres > 0) {
|
||||
img.setDotsPerMeterY(vres);
|
||||
}
|
||||
*outImage = img;
|
||||
return true;
|
||||
}
|
||||
@@ -915,8 +926,8 @@ bool PCXHandler::write(const QImage &image)
|
||||
header.YMin = 0;
|
||||
header.XMax = w - 1;
|
||||
header.YMax = h - 1;
|
||||
header.HDpi = qRound(image.dotsPerMeterX() * 25.4 / 1000);
|
||||
header.YDpi = qRound(image.dotsPerMeterY() * 25.4 / 1000);
|
||||
header.HDpi = qRoundOrZero_T<quint16>(dppm2dpi(image.dotsPerMeterX()));
|
||||
header.YDpi = qRoundOrZero_T<quint16>(dppm2dpi(image.dotsPerMeterY()));
|
||||
header.Reserved = 0;
|
||||
header.PaletteInfo = 1;
|
||||
|
||||
@@ -977,7 +988,7 @@ QVariant PCXHandler::option(ImageOption option) const
|
||||
bool PCXHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qWarning("PCXHandler::canRead() called with no device");
|
||||
qCWarning(LOG_PCXPLUGIN) << "PCXHandler::canRead() called with no device";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+17
-11
@@ -20,8 +20,8 @@
|
||||
|
||||
#include <QColorSpace>
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
#include <QLoggingCategory>
|
||||
#include <QVariant>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -29,6 +29,12 @@
|
||||
#include <qendian.h>
|
||||
#include <utility>
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_PICPLUGIN, "kf.imageformats.plugins.pic", QtDebugMsg)
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(LOG_PICPLUGIN, "kf.imageformats.plugins.pic", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Reads a PIC file header from a data stream.
|
||||
*
|
||||
@@ -193,7 +199,7 @@ static bool readRow(QDataStream &stream, QRgb *row, quint16 width, const QList<P
|
||||
if (channel.encoding == MixedRLE) {
|
||||
bool success = decodeRLEData(RLEVariant::PIC, stream, row, width, readPixel, updatePixel);
|
||||
if (!success) {
|
||||
qDebug() << "decodeRLEData failed";
|
||||
qCDebug(LOG_PICPLUGIN) << "decodeRLEData failed";
|
||||
return false;
|
||||
}
|
||||
} else if (channel.encoding == Uncompressed) {
|
||||
@@ -203,12 +209,12 @@ static bool readRow(QDataStream &stream, QRgb *row, quint16 width, const QList<P
|
||||
}
|
||||
} else {
|
||||
// unknown encoding
|
||||
qDebug() << "Unknown encoding";
|
||||
qCDebug(LOG_PICPLUGIN) << "Unknown encoding";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
qDebug() << "DataStream status was" << stream.status();
|
||||
qCDebug(LOG_PICPLUGIN) << "DataStream status was" << stream.status();
|
||||
}
|
||||
return stream.status() == QDataStream::Ok;
|
||||
}
|
||||
@@ -232,7 +238,7 @@ bool SoftimagePICHandler::read(QImage *image)
|
||||
for (const PicChannel &channel : std::as_const(m_channels)) {
|
||||
if (channel.size != 8) {
|
||||
// we cannot read images that do not come in bytes
|
||||
qDebug() << "Channel size was" << channel.size;
|
||||
qCDebug(LOG_PICPLUGIN) << "Channel size was" << channel.size;
|
||||
m_state = Error;
|
||||
return false;
|
||||
}
|
||||
@@ -243,7 +249,7 @@ bool SoftimagePICHandler::read(QImage *image)
|
||||
|
||||
QImage img = imageAlloc(m_header.width, m_header.height, fmt);
|
||||
if (img.isNull()) {
|
||||
qDebug() << "Failed to allocate image, invalid dimensions?" << QSize(m_header.width, m_header.height) << fmt;
|
||||
qCDebug(LOG_PICPLUGIN) << "Failed to allocate image, invalid dimensions?" << QSize(m_header.width, m_header.height) << fmt;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -252,7 +258,7 @@ bool SoftimagePICHandler::read(QImage *image)
|
||||
for (int y = 0; y < m_header.height; y++) {
|
||||
QRgb *row = reinterpret_cast<QRgb *>(img.scanLine(y));
|
||||
if (!readRow(m_dataStream, row, m_header.width, m_channels)) {
|
||||
qDebug() << "readRow failed";
|
||||
qCDebug(LOG_PICPLUGIN) << "readRow failed";
|
||||
m_state = Error;
|
||||
return false;
|
||||
}
|
||||
@@ -267,10 +273,10 @@ bool SoftimagePICHandler::read(QImage *image)
|
||||
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();
|
||||
auto tfmt = image.format();
|
||||
auto tcs = QColorSpace();
|
||||
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && tfmt == QImage::Format_CMYK8888) {
|
||||
tcs = QColorSpace(QColorSpace::SRgb);
|
||||
tfmt = QImage::Format_RGB32;
|
||||
@@ -281,11 +287,11 @@ bool SoftimagePICHandler::write(const QImage &image)
|
||||
}
|
||||
|
||||
if (image.width() < 0 || image.height() < 0) {
|
||||
qDebug() << "Image size invalid:" << image.width() << image.height();
|
||||
qCDebug(LOG_PICPLUGIN) << "Image size invalid:" << image.width() << image.height();
|
||||
return false;
|
||||
}
|
||||
if (image.width() > 65535 || image.height() > 65535) {
|
||||
qDebug() << "Image too big:" << image.width() << image.height();
|
||||
qCDebug(LOG_PICPLUGIN) << "Image too big:" << image.width() << image.height();
|
||||
// there are only two bytes for each dimension
|
||||
return false;
|
||||
}
|
||||
|
||||
+44
-34
@@ -33,14 +33,20 @@
|
||||
#include "scanlineconverter_p.h"
|
||||
#include "util_p.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
#include <QColorSpace>
|
||||
#include <QDataStream>
|
||||
#include <QImage>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_PSDPLUGIN, "kf.imageformats.plugins.psd", QtDebugMsg)
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(LOG_PSDPLUGIN, "kf.imageformats.plugins.psd", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
typedef quint32 uint;
|
||||
typedef quint16 ushort;
|
||||
typedef quint8 uchar;
|
||||
@@ -343,7 +349,7 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
|
||||
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
|
||||
qDebug() << "Invalid Image Resource Block Signature!";
|
||||
qCDebug(LOG_PSDPLUGIN) << "Invalid Image Resource Block Signature!";
|
||||
*ok = false;
|
||||
break;
|
||||
}
|
||||
@@ -373,7 +379,7 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok
|
||||
if (read > 0)
|
||||
size -= read;
|
||||
if (quint32(read) != dataSize) {
|
||||
qDebug() << "Image Resource Block Read Error!";
|
||||
qCDebug(LOG_PSDPLUGIN) << "Image Resource Block Read Error!";
|
||||
*ok = false;
|
||||
break;
|
||||
}
|
||||
@@ -596,17 +602,21 @@ static bool setResolution(QImage &img, const PSDImageResourceSection &irs)
|
||||
s >> i32; // Horizontal resolution in pixels per inch.
|
||||
if (i32 <= 0)
|
||||
return false;
|
||||
auto hres = fixedPointToDouble(i32);
|
||||
auto hres = dpi2ppm(fixedPointToDouble(i32));
|
||||
|
||||
s.skipRawData(4); // Display data (not used here)
|
||||
|
||||
s >> i32; // Vertial resolution in pixels per inch.
|
||||
if (i32 <= 0)
|
||||
return false;
|
||||
auto vres = fixedPointToDouble(i32);
|
||||
auto vres = dpi2ppm(fixedPointToDouble(i32));
|
||||
|
||||
img.setDotsPerMeterX(hres * 1000 / 25.4);
|
||||
img.setDotsPerMeterY(vres * 1000 / 25.4);
|
||||
if (hres > 0) {
|
||||
img.setDotsPerMeterX(hres);
|
||||
}
|
||||
if (vres > 0) {
|
||||
img.setDotsPerMeterY(vres);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -657,18 +667,18 @@ static QDataStream &operator>>(QDataStream &s, PSDHeader &header)
|
||||
static bool IsValid(const PSDHeader &header)
|
||||
{
|
||||
if (header.signature != 0x38425053) { // '8BPS'
|
||||
// qDebug() << "PSD header: invalid signature" << header.signature;
|
||||
// qCDebug(LOG_PSDPLUGIN) << "PSD header: invalid signature" << header.signature;
|
||||
return false;
|
||||
}
|
||||
if (header.version != 1 && header.version != 2) {
|
||||
qDebug() << "PSD header: invalid version" << header.version;
|
||||
qCDebug(LOG_PSDPLUGIN) << "PSD header: invalid version" << header.version;
|
||||
return false;
|
||||
}
|
||||
if (header.depth != 8 &&
|
||||
header.depth != 16 &&
|
||||
header.depth != 32 &&
|
||||
header.depth != 1) {
|
||||
qDebug() << "PSD header: invalid depth" << header.depth;
|
||||
qCDebug(LOG_PSDPLUGIN) << "PSD header: invalid depth" << header.depth;
|
||||
return false;
|
||||
}
|
||||
if (header.color_mode != CM_RGB &&
|
||||
@@ -679,17 +689,17 @@ static bool IsValid(const PSDHeader &header)
|
||||
header.color_mode != CM_LABCOLOR &&
|
||||
header.color_mode != CM_MULTICHANNEL &&
|
||||
header.color_mode != CM_BITMAP) {
|
||||
qDebug() << "PSD header: invalid color mode" << header.color_mode;
|
||||
qCDebug(LOG_PSDPLUGIN) << "PSD header: invalid color mode" << header.color_mode;
|
||||
return false;
|
||||
}
|
||||
// Specs tells: "Supported range is 1 to 56" but when the alpha channel is present the limit is 57:
|
||||
// Photoshop does not make you add more (see also 53alphas.psd test case).
|
||||
if (header.channel_count < 1 || header.channel_count > 57) {
|
||||
qDebug() << "PSD header: invalid number of channels" << header.channel_count;
|
||||
qCDebug(LOG_PSDPLUGIN) << "PSD header: invalid number of channels" << header.channel_count;
|
||||
return false;
|
||||
}
|
||||
if (header.width > std::min(300000, PSD_MAX_IMAGE_WIDTH) || header.height > std::min(300000, PSD_MAX_IMAGE_HEIGHT)) {
|
||||
qDebug() << "PSD header: invalid image size" << header.width << "x" << header.height;
|
||||
qCDebug(LOG_PSDPLUGIN) << "PSD header: invalid image size" << header.width << "x" << header.height;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -973,7 +983,7 @@ inline void cmykToRgb(uchar *target, qint32 targetChannels, const char *source,
|
||||
auto invmax = 1.0 / max; // speed improvements by ~10%
|
||||
|
||||
if (sourceChannels < 2) {
|
||||
qDebug() << "cmykToRgb: image is not a valid MCH/CMYK!";
|
||||
qCDebug(LOG_PSDPLUGIN) << "cmykToRgb: image is not a valid MCH/CMYK!";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1022,7 +1032,7 @@ inline void labToRgb(uchar *target, qint32 targetChannels, const char *source, q
|
||||
auto invmax = 1.0 / max;
|
||||
|
||||
if (sourceChannels < 3) {
|
||||
qDebug() << "labToRgb: image is not a valid LAB!";
|
||||
qCDebug(LOG_PSDPLUGIN) << "labToRgb: image is not a valid LAB!";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1163,39 +1173,39 @@ public:
|
||||
|
||||
// Check image file format.
|
||||
if (stream.atEnd() || !IsValid(m_header)) {
|
||||
// qDebug() << "This PSD file is not valid.";
|
||||
// qCDebug(LOG_PSDPLUGIN) << "This PSD file is not valid.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if it's a supported format.
|
||||
if (!IsSupported(m_header)) {
|
||||
// qDebug() << "This PSD file is not supported.";
|
||||
// qCDebug(LOG_PSDPLUGIN) << "This PSD file is not supported.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Color Mode Data section
|
||||
m_cmds = readColorModeDataSection(stream, &ok);
|
||||
if (!ok) {
|
||||
qDebug() << "Error while skipping Color Mode Data section";
|
||||
qCDebug(LOG_PSDPLUGIN) << "Error while skipping Color Mode Data section";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Image Resources Section
|
||||
m_irs = readImageResourceSection(stream, &ok);
|
||||
if (!ok) {
|
||||
qDebug() << "Error while reading Image Resources Section";
|
||||
qCDebug(LOG_PSDPLUGIN) << "Error while reading Image Resources Section";
|
||||
return false;
|
||||
}
|
||||
// Checking for merged image (Photoshop compatibility data)
|
||||
if (!hasMergedData()) {
|
||||
qDebug() << "No merged data found";
|
||||
qCDebug(LOG_PSDPLUGIN) << "No merged data found";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Layer and Mask section
|
||||
m_lms = readLayerAndMaskSection(stream, isPsb(), &ok);
|
||||
if (!ok) {
|
||||
qDebug() << "Error while skipping Layer and Mask section";
|
||||
qCDebug(LOG_PSDPLUGIN) << "Error while skipping Layer and Mask section";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1256,19 +1266,19 @@ bool PSDHandler::read(QImage *image)
|
||||
quint16 compression;
|
||||
stream >> compression;
|
||||
if (compression > 1) {
|
||||
qDebug() << "Unknown compression type";
|
||||
qCDebug(LOG_PSDPLUGIN) << "Unknown compression type";
|
||||
return false;
|
||||
}
|
||||
|
||||
const QImage::Format format = d->format();
|
||||
if (format == QImage::Format_Invalid) {
|
||||
qWarning() << "Unsupported image format. color_mode:" << header.color_mode << "depth:" << header.depth << "channel_count:" << header.channel_count;
|
||||
qCWarning(LOG_PSDPLUGIN) << "Unsupported image format. color_mode:" << header.color_mode << "depth:" << header.depth << "channel_count:" << header.channel_count;
|
||||
return false;
|
||||
}
|
||||
|
||||
img = imageAlloc(d->size(), format);
|
||||
if (img.isNull()) {
|
||||
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width, header.height);
|
||||
qCWarning(LOG_PSDPLUGIN) << "Failed to allocate image, invalid dimensions?" << QSize(header.width, header.height);
|
||||
return false;
|
||||
}
|
||||
img.fill(qRgb(0, 0, 0));
|
||||
@@ -1283,7 +1293,7 @@ bool PSDHandler::read(QImage *image)
|
||||
auto native_cmyk = img.format() == CMYK_FORMAT;
|
||||
|
||||
if (header.height > kMaxQVectorSize / header.channel_count / sizeof(quint32)) {
|
||||
qWarning() << "LoadPSD() header height/channel_count too big" << header.height << header.channel_count;
|
||||
qCWarning(LOG_PSDPLUGIN) << "LoadPSD() header height/channel_count too big" << header.height << header.channel_count;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1342,12 +1352,12 @@ bool PSDHandler::read(QImage *image)
|
||||
for (qint32 c = 0; c < header.channel_count; ++c) {
|
||||
auto strideNumber = c * qsizetype(h) + y;
|
||||
if (!device->seek(stridePositions.at(strideNumber))) {
|
||||
qDebug() << "Error while seeking the stream of channel" << c << "line" << y;
|
||||
qCDebug(LOG_PSDPLUGIN) << "Error while seeking the stream of channel" << c << "line" << y;
|
||||
return false;
|
||||
}
|
||||
auto &&strideSize = strides.at(strideNumber);
|
||||
if (!readChannel(rawStride, stream, strideSize, compression)) {
|
||||
qDebug() << "Error while reading the stream of channel" << c << "line" << y;
|
||||
qCDebug(LOG_PSDPLUGIN) << "Error while reading the stream of channel" << c << "line" << y;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1428,7 +1438,7 @@ bool PSDHandler::read(QImage *image)
|
||||
for (qint32 y = 0, h = header.height; y < h; ++y) {
|
||||
auto&& strideSize = strides.at(c * qsizetype(h) + y);
|
||||
if (!readChannel(rawStride, stream, strideSize, compression)) {
|
||||
qDebug() << "Error while reading the stream of channel" << c << "line" << y;
|
||||
qCDebug(LOG_PSDPLUGIN) << "Error while reading the stream of channel" << c << "line" << y;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1461,7 +1471,7 @@ bool PSDHandler::read(QImage *image)
|
||||
|
||||
// Resolution info
|
||||
if (!setResolution(img, irs)) {
|
||||
// qDebug() << "No resolution info found!";
|
||||
// qCDebug(LOG_PSDPLUGIN) << "No resolution info found!";
|
||||
}
|
||||
|
||||
// ICC profile
|
||||
@@ -1489,12 +1499,12 @@ bool PSDHandler::read(QImage *image)
|
||||
|
||||
// XMP data
|
||||
if (!setXmpData(img, irs)) {
|
||||
// qDebug() << "No XMP data found!";
|
||||
// qCDebug(LOG_PSDPLUGIN) << "No XMP data found!";
|
||||
}
|
||||
|
||||
// EXIF data
|
||||
if (!setExifData(img, d->m_exif)) {
|
||||
// qDebug() << "No EXIF data found!";
|
||||
// qCDebug(LOG_PSDPLUGIN) << "No EXIF data found!";
|
||||
}
|
||||
|
||||
// Duotone images: color data contains the duotone specification (not documented).
|
||||
@@ -1565,7 +1575,7 @@ QVariant PSDHandler::option(ImageOption option) const
|
||||
bool PSDHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qWarning("PSDHandler::canRead() called with no device");
|
||||
qCWarning(LOG_PSDPLUGIN) << "PSDHandler::canRead() called with no device";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,13 @@
|
||||
#include <QFile>
|
||||
#include <QIODevice>
|
||||
#include <QImage>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_QOIPLUGIN, "kf.imageformats.plugins.qoi", QtDebugMsg)
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(LOG_QOIPLUGIN, "kf.imageformats.plugins.qoi", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
/* *** QOI_MAX_IMAGE_WIDTH and QOI_MAX_IMAGE_HEIGHT ***
|
||||
* The maximum size in pixel allowed by the plugin.
|
||||
@@ -347,7 +354,7 @@ bool QOIHandler::canRead() const
|
||||
bool QOIHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qWarning("QOIHandler::canRead() called with no device");
|
||||
qCWarning(LOG_QOIPLUGIN) << "QOIHandler::canRead() called with no device";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+23
-17
@@ -12,8 +12,14 @@
|
||||
#include "util_p.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_RASPLUGIN, "kf.imageformats.plugins.ras", QtDebugMsg)
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(LOG_RASPLUGIN, "kf.imageformats.plugins.ras", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
/* *** RAS_MAX_IMAGE_WIDTH and RAS_MAX_IMAGE_HEIGHT ***
|
||||
* The maximum size in pixel allowed by the plugin.
|
||||
@@ -73,14 +79,14 @@ static QDataStream &operator>>(QDataStream &s, RasHeader &head)
|
||||
s >> head.Type;
|
||||
s >> head.ColorMapType;
|
||||
s >> head.ColorMapLength;
|
||||
/*qDebug() << "MagicNumber: " << head.MagicNumber
|
||||
<< "Width: " << head.Width
|
||||
<< "Height: " << head.Height
|
||||
<< "Depth: " << head.Depth
|
||||
<< "Length: " << head.Length
|
||||
<< "Type: " << head.Type
|
||||
<< "ColorMapType: " << head.ColorMapType
|
||||
<< "ColorMapLength: " << head.ColorMapLength;*/
|
||||
// qCDebug(LOG_RASPLUGIN) << "MagicNumber: " << head.MagicNumber
|
||||
// << "Width: " << head.Width
|
||||
// << "Height: " << head.Height
|
||||
// << "Depth: " << head.Depth
|
||||
// << "Length: " << head.Length
|
||||
// << "Type: " << head.Type
|
||||
// << "ColorMapType: " << head.ColorMapType
|
||||
// << "ColorMapLength: " << head.ColorMapLength;
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -218,7 +224,7 @@ static bool LoadRAS(QDataStream &s, const RasHeader &ras, QImage &img)
|
||||
if (rasLineSize & 1)
|
||||
++rasLineSize;
|
||||
if (rasLineSize > kMaxQVectorSize) {
|
||||
qWarning() << "LoadRAS() unsupported line size" << rasLineSize;
|
||||
qCWarning(LOG_RASPLUGIN) << "LoadRAS() unsupported line size" << rasLineSize;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -256,7 +262,7 @@ static bool LoadRAS(QDataStream &s, const RasHeader &ras, QImage &img)
|
||||
for (quint32 y = 0; y < ras.Height; ++y) {
|
||||
auto rasLine = dec.readLine(rasLineSize);
|
||||
if (rasLine.size() != rasLineSize) {
|
||||
qWarning() << "LoadRAS() unable to read line" << y << ": the seems corrupted!";
|
||||
qCWarning(LOG_RASPLUGIN) << "LoadRAS() unable to read line" << y << ": the seems corrupted!";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -336,8 +342,8 @@ static bool LoadRAS(QDataStream &s, const RasHeader &ras, QImage &img)
|
||||
continue;
|
||||
}
|
||||
|
||||
qWarning() << "LoadRAS() unsupported format!"
|
||||
<< "ColorMapType:" << ras.ColorMapType << "Type:" << ras.Type << "Depth:" << ras.Depth;
|
||||
qCWarning(LOG_RASPLUGIN) << "LoadRAS() unsupported format!"
|
||||
<< "ColorMapType:" << ras.ColorMapType << "Type:" << ras.Type << "Depth:" << ras.Depth;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -373,7 +379,7 @@ bool RASHandler::canRead() const
|
||||
bool RASHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qWarning("RASHandler::canRead() called with no device");
|
||||
qCWarning(LOG_RASPLUGIN) << "RASHandler::canRead() called with no device";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -399,19 +405,19 @@ bool RASHandler::read(QImage *outImage)
|
||||
s >> ras;
|
||||
|
||||
if (ras.ColorMapLength > kMaxQVectorSize) {
|
||||
qWarning() << "read() unsupported image color map length in file header" << ras.ColorMapLength;
|
||||
qCWarning(LOG_RASPLUGIN) << "LoadRAS() unsupported image color map length in file header" << ras.ColorMapLength;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check supported file types.
|
||||
if (!IsSupported(ras)) {
|
||||
// qDebug() << "This RAS file is not supported.";
|
||||
// qCDebug(LOG_RASPLUGIN) << "This RAS file is not supported.";
|
||||
return false;
|
||||
}
|
||||
|
||||
QImage img;
|
||||
if (!LoadRAS(s, ras, img)) {
|
||||
// qDebug() << "Error loading RAS file.";
|
||||
// qCDebug(LOG_RASPLUGIN) << "Error loading RAS file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <QColorSpace>
|
||||
#include <QDateTime>
|
||||
#include <QImage>
|
||||
#include <QLoggingCategory>
|
||||
#include <QSet>
|
||||
#include <QTimeZone>
|
||||
|
||||
@@ -27,7 +28,17 @@
|
||||
* By defining RAW_IGNORE_BROKEN_STREAMS removes this protection, leaving
|
||||
* LibRaw in complete control of error handling (which is mostly absent).
|
||||
*/
|
||||
// #define RAW_DISABLE_BROKEN_STREAM_PROTECTION
|
||||
#define RAW_DISABLE_BROKEN_STREAM_PROTECTION
|
||||
#endif
|
||||
|
||||
/* *** RAW_MAX_IMAGE_WIDTH and RAW_MAX_IMAGE_HEIGHT ***
|
||||
* The maximum size in pixel allowed by the plugin.
|
||||
*/
|
||||
#ifndef RAW_MAX_IMAGE_WIDTH
|
||||
#define RAW_MAX_IMAGE_WIDTH std::min(65535, KIF_LARGE_IMAGE_PIXEL_LIMIT)
|
||||
#endif
|
||||
#ifndef RAW_MAX_IMAGE_HEIGHT
|
||||
#define RAW_MAX_IMAGE_HEIGHT RAW_MAX_IMAGE_WIDTH
|
||||
#endif
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
@@ -41,6 +52,12 @@
|
||||
// #define EXCLUDE_LibRaw_QIODevice // Uncomment this code if you think that the problem is LibRaw_QIODevice (default commented)
|
||||
#endif
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_RAWPLUGIN, "kf.imageformats.plugins.raw", QtDebugMsg)
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(LOG_RAWPLUGIN, "kf.imageformats.plugins.raw", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
namespace // Private.
|
||||
{
|
||||
|
||||
@@ -68,6 +85,18 @@ const auto supported_formats = QSet<QByteArray>{
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
/*!
|
||||
* \brief rawImageSize
|
||||
* \return The size in pixels of the RAW image.
|
||||
*/
|
||||
static QSize rawImageSize(LibRaw *rawProcessor)
|
||||
{
|
||||
auto w = libraw_get_iwidth(&rawProcessor->imgdata);
|
||||
auto h = libraw_get_iheight(&rawProcessor->imgdata);
|
||||
// flip & 4: taken from LibRaw code
|
||||
return (rawProcessor->imgdata.sizes.flip & 4) ? QSize(h, w) : QSize(w, h);
|
||||
}
|
||||
|
||||
inline int raw_scanf_one(const QByteArray &ba, const char *fmt, void *val)
|
||||
{
|
||||
// WARNING: Here it would be nice to use sscanf like LibRaw does but there
|
||||
@@ -728,6 +757,13 @@ bool LoadRAW(QImageIOHandler *handler, QImage &img)
|
||||
}
|
||||
#endif
|
||||
|
||||
// *** Limiting the maximum image size on a reasonable size
|
||||
auto size = rawImageSize(rawProcessor.get());
|
||||
if (size.width() > RAW_MAX_IMAGE_WIDTH || size.height() > RAW_MAX_IMAGE_HEIGHT) {
|
||||
qCWarning(LOG_RAWPLUGIN) << "The maximum image size is limited to" << RAW_MAX_IMAGE_WIDTH << "x" << RAW_MAX_IMAGE_HEIGHT << "px";
|
||||
return false;
|
||||
}
|
||||
|
||||
// *** Unpacking selected image
|
||||
if (rawProcessor->unpack() != LIBRAW_SUCCESS) {
|
||||
return false;
|
||||
@@ -946,10 +982,7 @@ QVariant RAWHandler::option(ImageOption option) const
|
||||
rawProcessor->imgdata.rawparams.shot_select = currentImageNumber();
|
||||
#endif
|
||||
if (rawProcessor->open_datastream(&stream) == LIBRAW_SUCCESS) {
|
||||
auto w = libraw_get_iwidth(&rawProcessor->imgdata);
|
||||
auto h = libraw_get_iheight(&rawProcessor->imgdata);
|
||||
// flip & 4: taken from LibRaw code
|
||||
v = (rawProcessor->imgdata.sizes.flip & 4) ? QSize(h, w) : QSize(w, h);
|
||||
v = rawImageSize(rawProcessor.get());
|
||||
}
|
||||
d->rollbackTransaction();
|
||||
}
|
||||
@@ -1010,7 +1043,7 @@ int RAWHandler::currentImageNumber() const
|
||||
bool RAWHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qWarning("RAWHandler::canRead() called with no device");
|
||||
qCWarning(LOG_RAWPLUGIN) << "RAWHandler::canRead() called with no device";
|
||||
return false;
|
||||
}
|
||||
if (device->isSequential()) {
|
||||
|
||||
+20
-15
@@ -26,11 +26,17 @@
|
||||
#include <cstring>
|
||||
|
||||
#include <QColorSpace>
|
||||
#include <QDebug>
|
||||
#include <QImage>
|
||||
#include <QList>
|
||||
#include <QLoggingCategory>
|
||||
#include <QMap>
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_RGBPLUGIN, "kf.imageformats.plugins.rgb", QtDebugMsg)
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(LOG_RGBPLUGIN, "kf.imageformats.plugins.rgb", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
class RLEData : public QList<uchar>
|
||||
{
|
||||
public:
|
||||
@@ -296,12 +302,12 @@ bool SGIImagePrivate::readImage(QImage &img)
|
||||
|
||||
img = imageAlloc(size(), format());
|
||||
if (img.isNull()) {
|
||||
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(_xsize, _ysize);
|
||||
qCWarning(LOG_RGBPLUGIN) << "Failed to allocate image, invalid dimensions?" << QSize(_xsize, _ysize);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_zsize > 4) {
|
||||
// qDebug() << "using first 4 of " << _zsize << " channels";
|
||||
// qCDebug(LOG_RGBPLUGIN) << "using first 4 of " << _zsize << " channels";
|
||||
// Only let this continue if it won't cause a int overflow later
|
||||
// this is most likely a broken file anyway
|
||||
if (_ysize > std::numeric_limits<int>::max() / _zsize) {
|
||||
@@ -345,14 +351,14 @@ bool SGIImagePrivate::readImage(QImage &img)
|
||||
for (uint o = 0; o < _numrows; o++) {
|
||||
// don't change to greater-or-equal!
|
||||
if (_starttab[o] + _lengthtab[o] > (uint)_data.size()) {
|
||||
// qDebug() << "image corrupt (sanity check failed)";
|
||||
// qCDebug(LOG_RGBPLUGIN) << "image corrupt (sanity check failed)";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!readData(img)) {
|
||||
// qDebug() << "image corrupt (incomplete scanline)";
|
||||
// qCDebug(LOG_RGBPLUGIN) << "image corrupt (incomplete scanline)";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -475,7 +481,7 @@ bool SGIImagePrivate::scanData(const QImage &img, const QImage::Format &tfmt, co
|
||||
for (y = 0; y < _ysize; y++) {
|
||||
const int yPos = _ysize - y - 1; // scanline doesn't do any sanity checking
|
||||
if (yPos >= img.height()) {
|
||||
qWarning() << "Failed to get scanline for" << yPos;
|
||||
qCWarning(LOG_RGBPLUGIN) << "Failed to get scanline for" << yPos;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -496,7 +502,7 @@ bool SGIImagePrivate::scanData(const QImage &img, const QImage::Format &tfmt, co
|
||||
for (y = 0; y < _ysize; y++) {
|
||||
const int yPos = _ysize - y - 1;
|
||||
if (yPos >= img.height()) {
|
||||
qWarning() << "Failed to get scanline for" << yPos;
|
||||
qCWarning(LOG_RGBPLUGIN) << "Failed to get scanline for" << yPos;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -511,7 +517,7 @@ bool SGIImagePrivate::scanData(const QImage &img, const QImage::Format &tfmt, co
|
||||
for (y = 0; y < _ysize; y++) {
|
||||
const int yPos = _ysize - y - 1;
|
||||
if (yPos >= img.height()) {
|
||||
qWarning() << "Failed to get scanline for" << yPos;
|
||||
qCWarning(LOG_RGBPLUGIN) << "Failed to get scanline for" << yPos;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -531,7 +537,7 @@ bool SGIImagePrivate::scanData(const QImage &img, const QImage::Format &tfmt, co
|
||||
for (y = 0; y < _ysize; y++) {
|
||||
const int yPos = _ysize - y - 1;
|
||||
if (yPos >= img.height()) {
|
||||
qWarning() << "Failed to get scanline for" << yPos;
|
||||
qCWarning(LOG_RGBPLUGIN) << "Failed to get scanline for" << yPos;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -661,7 +667,7 @@ bool SGIImagePrivate::writeHeader()
|
||||
bool SGIImagePrivate::writeRle()
|
||||
{
|
||||
_rle = 1;
|
||||
// qDebug() << "writing RLE data";
|
||||
// qCDebug(LOG_RGBPLUGIN) << "writing RLE data";
|
||||
if (!writeHeader()) {
|
||||
return false;
|
||||
}
|
||||
@@ -747,7 +753,6 @@ bool SGIImagePrivate::writeVerbatim(const QImage &img, const QImage::Format &tfm
|
||||
|
||||
bool SGIImagePrivate::writeImage(const QImage &image)
|
||||
{
|
||||
// qDebug() << "writing "; // TODO add filename
|
||||
if (image.allGray()) {
|
||||
_dim = 2, _zsize = 1;
|
||||
} else {
|
||||
@@ -759,10 +764,10 @@ bool SGIImagePrivate::writeImage(const QImage &image)
|
||||
_dim = 3, _zsize++;
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
|
||||
auto cs = image.colorSpace();
|
||||
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;
|
||||
@@ -794,7 +799,7 @@ bool SGIImagePrivate::writeImage(const QImage &image)
|
||||
_rlemap.setBaseOffset(512 + _numrows * 2 * sizeof(quint32));
|
||||
|
||||
if (!scanData(image, tfmt, tcs)) {
|
||||
// qDebug() << "this can't happen";
|
||||
// qCDebug(LOG_RGBPLUGIN) << "this can't happen";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -884,7 +889,7 @@ QVariant RGBHandler::option(ImageOption option) const
|
||||
bool RGBHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qWarning("RGBHandler::canRead() called with no device");
|
||||
qCWarning(LOG_RGBPLUGIN) << "RGBHandler::canRead() called with no device";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ public:
|
||||
auto v = QString::fromLatin1(pchar_t(res.data()), res.size()).toDouble(&ok);
|
||||
if (ok && v > 0) {
|
||||
if (m_pb._unitsOfMeasurement) { // Inches
|
||||
return qRoundOrZero(width() / v / 25.4 * 1000);
|
||||
return dpi2ppm(width() / v);
|
||||
}
|
||||
// Millimeters
|
||||
return qRoundOrZero(width() / v * 1000);
|
||||
@@ -230,7 +230,7 @@ public:
|
||||
auto v = QString::fromLatin1(pchar_t(res.data()), res.size()).toDouble(&ok);
|
||||
if (ok && v > 0) {
|
||||
if (m_pb._unitsOfMeasurement) { // Inches
|
||||
return qRoundOrZero(height() / v / 25.4 * 1000);
|
||||
return dpi2ppm(height() / v);
|
||||
}
|
||||
// Millimeters
|
||||
return qRoundOrZero(height() / v * 1000);
|
||||
@@ -330,7 +330,7 @@ bool ScitexHandler::canRead() const
|
||||
bool ScitexHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qWarning("ScitexHandler::canRead() called with no device");
|
||||
qCWarning(LOG_SCTPLUGIN) << "ScitexHandler::canRead() called with no device";
|
||||
return false;
|
||||
}
|
||||
ScitexHandlerPrivate hp;
|
||||
@@ -344,7 +344,7 @@ bool ScitexHandler::read(QImage *image)
|
||||
{
|
||||
auto dev = device();
|
||||
if (dev == nullptr) {
|
||||
qWarning("ScitexHandler::read() called with no device");
|
||||
qCWarning(LOG_SCTPLUGIN) << "ScitexHandler::read() called with no device";
|
||||
return false;
|
||||
}
|
||||
if (!d->loadHeader(dev)) {
|
||||
|
||||
@@ -988,9 +988,9 @@ 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();
|
||||
auto tcs = QColorSpace();
|
||||
if (cs.isValid() && cs.colorModel() == QColorSpace::ColorModel::Cmyk && image.format() == QImage::Format_CMYK8888) {
|
||||
format = QImage::Format_RGB32;
|
||||
tcs = QColorSpace(QColorSpace::SRgb);
|
||||
|
||||
@@ -66,13 +66,70 @@ inline QImage imageAlloc(qint32 width, qint32 height, const QImage::Format &form
|
||||
return imageAlloc(QSize(width, height), format);
|
||||
}
|
||||
|
||||
inline double qRoundOrZero(double d)
|
||||
template<class TI, class SF> // SF = source FP, TI = target INT
|
||||
TI qRoundOrZero_T(SF d, bool *ok = nullptr)
|
||||
{
|
||||
// If the value d is outside the range of int, the behavior is undefined.
|
||||
if (d > std::numeric_limits<int>::max()) {
|
||||
// checks for undefined behavior
|
||||
if (qIsNaN(d) || qIsInf(d) || d < SF() || d > SF(std::numeric_limits<TI>::max())) {
|
||||
if (ok) {
|
||||
*ok = false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (ok) {
|
||||
*ok = true;
|
||||
}
|
||||
return qRound(d);
|
||||
}
|
||||
|
||||
inline qint32 qRoundOrZero(double d, bool *ok = nullptr)
|
||||
{
|
||||
return qRoundOrZero_T<qint32>(d, ok);
|
||||
}
|
||||
inline qint32 qRoundOrZero(float d, bool *ok = nullptr)
|
||||
{
|
||||
return qRoundOrZero_T<qint32>(d, ok);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief dpi2ppm
|
||||
* Converts a value from DPI to PPM.
|
||||
* \return \a dpi converted to pixel per meter.
|
||||
*/
|
||||
inline qint32 dpi2ppm(double dpi, bool *ok = nullptr)
|
||||
{
|
||||
return qRoundOrZero(dpi / double(25.4) * double(1000), ok);
|
||||
}
|
||||
inline qint32 dpi2ppm(float dpi, bool *ok = nullptr)
|
||||
{
|
||||
return qRoundOrZero(dpi / float(25.4) * float(1000), ok);
|
||||
}
|
||||
inline qint32 dpi2ppm(quint16 dpi, bool *ok = nullptr)
|
||||
{
|
||||
return qRoundOrZero(dpi / double(25.4) * double(1000), ok);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief ppm2dpi
|
||||
* Converts a value from PPM to DPI.
|
||||
* \return \a ppm converted to dot per inch.
|
||||
*/
|
||||
template<class TF, class SI> // SI = source INT, TF = target FP
|
||||
TF ppm2dpi_T(SI ppm, bool *ok = nullptr)
|
||||
{
|
||||
if (ok) {
|
||||
*ok = ppm > 0;
|
||||
}
|
||||
return ppm > 0 ? ppm * TF(25.4) / TF(1000) : TF();
|
||||
}
|
||||
|
||||
inline double dppm2dpi(qint32 ppm, bool *ok = nullptr)
|
||||
{
|
||||
return ppm2dpi_T<double>(ppm, ok);
|
||||
}
|
||||
inline float fppm2dpi(qint32 ppm, bool *ok = nullptr)
|
||||
{
|
||||
return ppm2dpi_T<float>(ppm, ok);
|
||||
}
|
||||
|
||||
#endif // UTIL_P_H
|
||||
|
||||
+38
-24
@@ -10,7 +10,6 @@
|
||||
#include "xcf_p.h"
|
||||
|
||||
#include <QColorSpace>
|
||||
#include <QDebug>
|
||||
#include <QIODevice>
|
||||
#include <QImage>
|
||||
#include <QImageReader>
|
||||
@@ -55,8 +54,6 @@ Q_LOGGING_CATEGORY(XCFPLUGIN, "kf.imageformats.plugins.xcf", QtWarningMsg)
|
||||
#define DISABLE_TILE_PROFILE_CONV // default uncommented (comment to use the conversion as intended by Martin)
|
||||
#define DISABLE_IMAGE_PROFILE_CONV // default uncommented (comment to use the conversion as intended by Martin)
|
||||
|
||||
const float INCHESPERMETER = (100.0f / 2.54f);
|
||||
|
||||
namespace
|
||||
{
|
||||
struct RandomTable {
|
||||
@@ -1170,7 +1167,9 @@ bool XCFImageFormat::loadLayer(QDataStream &xcf_io, XCFImage &xcf_image)
|
||||
if (!composeTiles(xcf_image)) {
|
||||
return false;
|
||||
}
|
||||
xcf_io.device()->seek(layer.hierarchy_offset);
|
||||
if (!xcf_io.device()->seek(layer.hierarchy_offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// As tiles are loaded, they are copied into the layers tiles by
|
||||
// this routine. (loadMask(), below, uses a slightly different
|
||||
@@ -1188,7 +1187,9 @@ bool XCFImageFormat::loadLayer(QDataStream &xcf_io, XCFImage &xcf_image)
|
||||
layer.apply_mask = 1;
|
||||
}
|
||||
|
||||
xcf_io.device()->seek(layer.mask_offset);
|
||||
if (!xcf_io.device()->seek(layer.mask_offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!loadMask(xcf_io, layer, xcf_image.header.precision)) {
|
||||
return false;
|
||||
@@ -1800,6 +1801,9 @@ 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].isNaN() || dataPtr[x + 1].isNaN() || dataPtr[x + 2].isNaN()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1828,6 +1832,9 @@ 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::isnan(dataPtr[x + 0]) || std::isnan(dataPtr[x + 1]) || std::isnan(dataPtr[x + 2])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1948,12 +1955,15 @@ bool XCFImageFormat::loadHierarchy(QDataStream &xcf_io, Layer &layer, const Gimp
|
||||
|
||||
qint64 saved_pos = xcf_io.device()->pos();
|
||||
|
||||
xcf_io.device()->seek(offset);
|
||||
if (!xcf_io.device()->seek(offset)) {
|
||||
return false;
|
||||
}
|
||||
if (!loadLevel(xcf_io, layer, bpp, precision)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
xcf_io.device()->seek(saved_pos);
|
||||
if (!xcf_io.device()->seek(saved_pos)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2056,7 +2066,9 @@ bool XCFImageFormat::loadLevel(QDataStream &xcf_io, Layer &layer, qint32 bpp, co
|
||||
offset2 = offset + blockSize;
|
||||
}
|
||||
|
||||
xcf_io.device()->seek(offset);
|
||||
if (!xcf_io.device()->seek(offset)) {
|
||||
return false;
|
||||
}
|
||||
qint64 bytesParsed = 0;
|
||||
|
||||
switch (layer.compression) {
|
||||
@@ -2163,7 +2175,9 @@ bool XCFImageFormat::loadLevel(QDataStream &xcf_io, Layer &layer, qint32 bpp, co
|
||||
return false;
|
||||
}
|
||||
|
||||
xcf_io.device()->seek(saved_pos);
|
||||
if (!xcf_io.device()->seek(saved_pos)) {
|
||||
return false;
|
||||
}
|
||||
offset = readOffsetPtr(xcf_io);
|
||||
|
||||
if (offset < 0) {
|
||||
@@ -2203,7 +2217,9 @@ bool XCFImageFormat::loadMask(QDataStream &xcf_io, Layer &layer, const GimpPreci
|
||||
return false;
|
||||
}
|
||||
|
||||
xcf_io.device()->seek(hierarchy_offset);
|
||||
if (!xcf_io.device()->seek(hierarchy_offset)) {
|
||||
return false;
|
||||
}
|
||||
layer.assignBytes = assignMaskBytes;
|
||||
|
||||
if (!loadHierarchy(xcf_io, layer, precision)) {
|
||||
@@ -2709,17 +2725,13 @@ bool XCFImageFormat::initializeImage(XCFImage &xcf_image)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (xcf_image.x_resolution > 0 && xcf_image.y_resolution > 0) {
|
||||
const float dpmx = xcf_image.x_resolution * INCHESPERMETER;
|
||||
if (dpmx > float(std::numeric_limits<int>::max())) {
|
||||
return false;
|
||||
}
|
||||
const float dpmy = xcf_image.y_resolution * INCHESPERMETER;
|
||||
if (dpmy > float(std::numeric_limits<int>::max())) {
|
||||
return false;
|
||||
}
|
||||
image.setDotsPerMeterX((int)dpmx);
|
||||
image.setDotsPerMeterY((int)dpmy);
|
||||
const qint32 dpmx = dpi2ppm(xcf_image.x_resolution);
|
||||
if (dpmx > 0) {
|
||||
image.setDotsPerMeterX(dpmx);
|
||||
}
|
||||
const qint32 dpmy = dpi2ppm(xcf_image.y_resolution);
|
||||
if (dpmy > 0) {
|
||||
image.setDotsPerMeterY(dpmy);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -3198,7 +3210,7 @@ void XCFImageFormat::mergeLayerIntoImage(XCFImage &xcf_image)
|
||||
for (uint i = 0; i < layer.ncols; i++) {
|
||||
qint32 x = qint32(i * TILE_WIDTH);
|
||||
|
||||
QImage &tile = layer.image_tiles[j][i];
|
||||
const QImage &tile = layer.image_tiles[j][i];
|
||||
if (x + layer.x_offset < XCF_MAX_IMAGE_WIDTH && y + layer.y_offset < XCF_MAX_IMAGE_HEIGHT) {
|
||||
painter.drawImage(x + layer.x_offset, y + layer.y_offset, tile);
|
||||
}
|
||||
@@ -4235,7 +4247,9 @@ bool XCFHandler::canRead(QIODevice *device)
|
||||
bool failed = !XCFImageFormat::readXCFHeader(ds, &header);
|
||||
ds.setDevice(nullptr);
|
||||
|
||||
device->seek(oldPos);
|
||||
if (!device->seek(oldPos)) {
|
||||
return false;
|
||||
}
|
||||
if (failed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user