mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2026-06-16 03:29:09 -04:00
Compare commits
1 Commits
work/qdoc-
...
work/aacid
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f728b87ae8 |
@@ -1,11 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(KF_VERSION "6.7.0") # handled by release scripts
|
||||
set(KF_DEP_VERSION "6.6.0") # handled by release scripts
|
||||
set(KF_VERSION "6.4.0") # handled by release scripts
|
||||
set(KF_DEP_VERSION "6.4.0") # handled by release scripts
|
||||
project(KImageFormats VERSION ${KF_VERSION})
|
||||
|
||||
include(FeatureSummary)
|
||||
find_package(ECM 6.6.0 NO_MODULE)
|
||||
find_package(ECM 6.4.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)
|
||||
|
||||
|
||||
161
README.md
161
README.md
@@ -1,6 +1,6 @@
|
||||
# KImageFormats
|
||||
|
||||
Plugins to allow `QImage` to support extra file formats.
|
||||
Plugins to allow QImage to support extra file formats.
|
||||
|
||||
## Introduction
|
||||
|
||||
@@ -28,9 +28,9 @@ The following image formats have read and write support:
|
||||
|
||||
- AV1 Image File Format (avif)
|
||||
- Encapsulated PostScript (eps)
|
||||
- High Efficiency Image File Format (heif)
|
||||
- High Efficiency Image File Format (heif). Can be enabled with the KIMAGEFORMATS_HEIF build option.
|
||||
- JPEG XL (jxl)
|
||||
- JPEG XR (jxr)
|
||||
- JPEG XR (jxr). Can be enabled with the KIMAGEFORMATS_JXR build option.
|
||||
- OpenEXR (exr)
|
||||
- Personal Computer Exchange (pcx)
|
||||
- Quite OK Image format (qoi)
|
||||
@@ -40,7 +40,7 @@ The following image formats have read and write support:
|
||||
|
||||
## Contributing
|
||||
|
||||
See the [`QImageIOPlugin`](https://doc.qt.io/qt-6/qimageioplugin.html) documentation for information on how to write a
|
||||
See the QImageIOPlugin documentation for information on how to write a
|
||||
new plugin.
|
||||
|
||||
The main difference between this framework and the qimageformats module
|
||||
@@ -68,156 +68,3 @@ This framework is licensed under the
|
||||
The CMake code in this framework is licensed under the
|
||||
[BSD license](http://opensource.org/licenses/BSD-3-Clause).
|
||||
|
||||
## Plugin status
|
||||
|
||||
The current implementation of a plugin may not be complete or may have limitations
|
||||
of various kinds. Typically the limitations are on maximum size and color depth.
|
||||
|
||||
The various plugins are also limited by the formats natively supported by Qt.
|
||||
For example, native support for CMYK images is only available since Qt 6.8.
|
||||
|
||||
### HDR images
|
||||
|
||||
HDR images are supported via floating point image formats from EXR, HDR, JXR,
|
||||
PFM and PSD plugins.
|
||||
It is important to note that in the past these plugins stripped away HDR
|
||||
information, returning SDR images.
|
||||
|
||||
HDR images return R, G and B values outside the range 0.0 - 1.0.
|
||||
While Qt painters handles HDR data correctly, some older programs may display
|
||||
strange artifacts if they do not use a tone mapping operator (or at least a
|
||||
clamp). This is not a plugin issue.
|
||||
|
||||
### Metadata
|
||||
|
||||
Metadata support is implemented in all formats that support it. In particular,
|
||||
in addition to the classic `"Description"`, `"Author"`, `"Copyright"`, etc... where
|
||||
possible, XMP data is supported via the `"XML:com.adobe.xmp"` key.
|
||||
|
||||
Please note that only the most common metadata is supported.
|
||||
|
||||
### ICC profile support
|
||||
|
||||
ICC support is fully implemented in all formats that support it. When saving,
|
||||
some formats convert the image using color profiles according to
|
||||
specifications. In particular, HDR formats almost always convert to linear
|
||||
RGB.
|
||||
|
||||
### Maximum image size
|
||||
|
||||
Where possible, plugins support large images. By convention, many of the
|
||||
large image plugins are limited to a maximum of 300,000 x 300,000 pixels.
|
||||
Anyway, all plugins are also limited by the
|
||||
`QImageIOReader::allocationLimit()`. 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
|
||||
- EXR: 300,000 x 300,000 pixels
|
||||
- HDR: n/a (large image)
|
||||
- HEIF: n/a
|
||||
- JXL: 65,535 x 65,535 pixels, in any case no larger than 256 megapixels
|
||||
- JXR: n/a
|
||||
- PCX: 65,535 x 65,535 pixels
|
||||
- PFM: n/a (large image)
|
||||
- PIC: 65,535 x 65,535 pixels
|
||||
- PSD: 300,000 x 300,000 pixels
|
||||
- PXR: 65,535 x 65,535 pixels
|
||||
- QOI: 300,000 x 300,000 pixels
|
||||
- RAS: n/a (large image)
|
||||
- RAW: n/a (depends on the RAW format loaded)
|
||||
- RGB: 65,535 x 65,535 pixels
|
||||
- TGA: 65,535 x 65,535 pixels
|
||||
- XCF: 300,000 x 300,000 pixels
|
||||
|
||||
### Sequential and random access devices
|
||||
|
||||
All plugins work fine on random access devices while only some work on
|
||||
sequential access devices.
|
||||
Some plugins, such as PSD, allow reading RGB images on sequential access
|
||||
devices, but cannot do the same for Lab files.
|
||||
|
||||
**Important: some plugins use `QIODevice` transactions and/or
|
||||
`QIODevice::ungetChar()`. Therefore, the device used to read the image must not
|
||||
have any active transactions.**
|
||||
|
||||
### Memory usage
|
||||
|
||||
Qt has added many image formats over time. In older plugins, to support new
|
||||
formats, `QImage` conversion functions have been used, causing memory
|
||||
consumption proportional to the size of the image to be saved.
|
||||
Normally this is not a source of problems because the affected plugins
|
||||
are limited to maximum images of 2GiB or less.
|
||||
|
||||
On plugins for formats that support large images, progressive conversion has
|
||||
been used or the maximum size of the image that can be saved has been limited.
|
||||
|
||||
### Non-RGB formats
|
||||
|
||||
PSD plugin loads CMYK, Lab and Multichannel images and converts them to RGB
|
||||
without using the ICC profile.
|
||||
|
||||
JXR and PSD plugins natively support 4-channel CMYK images when compiled
|
||||
with Qt 6.8+.
|
||||
|
||||
### The HEIF plugin
|
||||
|
||||
**This plugin is disabled by default. It can be enabled with the
|
||||
`KIMAGEFORMATS_HEIF` build option in the cmake file.**
|
||||
|
||||
### The EXR plugin
|
||||
|
||||
The following defines can be defined in cmake to modify the behavior of the plugin:
|
||||
- `EXR_CONVERT_TO_SRGB`: the linear data is converted to sRGB on read to accommodate programs that do not support color profiles.
|
||||
- `EXR_DISABLE_XMP_ATTRIBUTE`: disables the stores XMP values in a non-standard attribute named "xmp". Note that Gimp reads the "xmp" attribute and Darktable writes it as well.
|
||||
|
||||
### The HDR plugin
|
||||
|
||||
The following defines can be defined in cmake to modify the behavior of the plugin:
|
||||
- `HDR_HALF_QUALITY`: on read, a 16-bit float image is returned instead of a 32-bit float one.
|
||||
|
||||
### The JXL plugin
|
||||
|
||||
**The current version of the plugin limits the image size to 256 megapixels
|
||||
according to feature level 5 of the JXL stream encoding.**
|
||||
|
||||
### The JXR plugin
|
||||
|
||||
**This plugin is disabled by default. It can be enabled with the
|
||||
`KIMAGEFORMATS_JXR` build option in the cmake file.**
|
||||
|
||||
The following defines can be defined in cmake to modify the behavior of the plugin:
|
||||
- `JXR_DENY_FLOAT_IMAGE`: disables the use of float images and consequently any HDR data will be lost.
|
||||
- `JXR_DISABLE_DEPTH_CONVERSION`: remove the neeeds of additional memory by disabling the conversion between different color depths (e.g. RGBA64bpp to RGBA32bpp) at the cost of reduced compatibility.
|
||||
- `JXR_DISABLE_BGRA_HACK`: Windows displays and opens JXR files correctly out of the box. Unfortunately it doesn't seem to open (P)RGBA @32bpp files as it only wants (P)BGRA32bpp files (a format not supported by Qt). Only for this format an hack is activated to guarantee total compatibility of the plugin with Windows.
|
||||
- `JXR_ENABLE_ADVANCED_METADATA`: enable metadata support (e.g. XMP). Some distributions use an incomplete JXR library that does not allow reading metadata, causing compilation errors.
|
||||
|
||||
### The PSD plugin
|
||||
|
||||
PSD support has the following limitations:
|
||||
- Only images saved by Photoshop using compatibility mode enabled (Photoshop default) can be decoded.
|
||||
- Multichannel images are treated as CMY/CMYK and are only loaded if they have 3 or more channels.
|
||||
- Duotone images are treated as grayscale images.
|
||||
- Extra channels other than alpha are discarded.
|
||||
|
||||
The following defines can be defined in cmake to modify the behavior of the plugin:
|
||||
- `PSD_FAST_LAB_CONVERSION`: the LAB image is converted to linear sRGB instead of sRGB which significantly increases performance.
|
||||
- `PSD_NATIVE_CMYK_SUPPORT_DISABLED`: disable native support for CMYK images when compiled with Qt 6.8+
|
||||
|
||||
### The RAW plugin
|
||||
|
||||
Loading RAW images always requires a conversion. To allow the user to
|
||||
choose how to convert the image, it was chosen to use the quality parameter
|
||||
to act on the converter. The quality parameter can be used with values from
|
||||
0 to 100 (0 = fast, 100 = maximum quality) or by setting flags to
|
||||
selectively change the conversion (see also [raw_p.h](./src/imageformats/raw_p.h)).
|
||||
|
||||
The default setting tries to balance quality and conversion speed.
|
||||
|
||||
### The XCF plugin
|
||||
|
||||
XCF support has the following limitations:
|
||||
- XCF format up to [version 12](https://testing.developer.gimp.org/core/standards/xcf/#version-history) (no support for GIMP 3).
|
||||
- The returned image is always 8-bit.
|
||||
- Cannot read zlib compressed files.
|
||||
- The rendered image may be slightly different (colors/transparencies) than in GIMP.
|
||||
|
||||
Binary file not shown.
@@ -1,19 +0,0 @@
|
||||
[
|
||||
{
|
||||
"minQtVersion" : "6.5.7",
|
||||
"maxQtVersion" : "6.5.99",
|
||||
"disableAutoTransform": true,
|
||||
"fileName" : "orientation6_notranfs.png",
|
||||
"comment" : "Test with automatic transformation disabled."
|
||||
},
|
||||
{
|
||||
"minQtVersion" : "6.7.3",
|
||||
"disableAutoTransform": true,
|
||||
"fileName" : "orientation6_notranfs.png",
|
||||
"comment" : "Test with automatic transformation disabled."
|
||||
},
|
||||
{
|
||||
"unsupportedFormat" : true,
|
||||
"comment" : "It is not possible to disable the transformation with the current version of the plugin."
|
||||
}
|
||||
]
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 593 B |
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
Binary file not shown.
@@ -267,13 +267,10 @@ int main(int argc, char **argv)
|
||||
continue;
|
||||
}
|
||||
|
||||
TemplateImage::TestFlags flags = TemplateImage::None;
|
||||
QString comment;
|
||||
QFileInfo expFileInfo = timg.compareImage(flags, comment);
|
||||
if ((flags & TemplateImage::SkipTest) == TemplateImage::SkipTest) {
|
||||
if(comment.isEmpty())
|
||||
comment = QStringLiteral("image format not supported by current Qt version!");
|
||||
QTextStream(stdout) << "SKIP : " << fi.fileName() << QStringLiteral(": %1\n").arg(comment);
|
||||
bool skipTest = false;
|
||||
QFileInfo expFileInfo = timg.compareImage(skipTest);
|
||||
if (skipTest) {
|
||||
QTextStream(stdout) << "SKIP : " << fi.fileName() << ": image format not supported by current Qt version!\n";
|
||||
++skipped;
|
||||
continue;
|
||||
}
|
||||
@@ -294,7 +291,7 @@ int main(int argc, char **argv)
|
||||
QImage expImage;
|
||||
|
||||
// inputImage is auto-rotated to final orientation
|
||||
inputReader.setAutoTransform((flags & TemplateImage::DisableAutotransform) != TemplateImage::DisableAutotransform);
|
||||
inputReader.setAutoTransform(true);
|
||||
|
||||
if (!expReader.read(&expImage)) {
|
||||
QTextStream(stdout) << "ERROR: " << fi.fileName() << ": could not load " << expfilename << ": " << expReader.errorString() << "\n";
|
||||
|
||||
@@ -28,10 +28,10 @@ bool TemplateImage::isTemplate() const
|
||||
return false;
|
||||
}
|
||||
|
||||
QFileInfo TemplateImage::compareImage(TestFlags &flags, QString& comment) const
|
||||
QFileInfo TemplateImage::compareImage(bool &skipTest) const
|
||||
{
|
||||
auto fi = jsonImage(flags, comment);
|
||||
if ((flags & TestFlag::SkipTest) == TestFlag::SkipTest) {
|
||||
auto fi = jsonImage(skipTest);
|
||||
if (skipTest) {
|
||||
return {};
|
||||
}
|
||||
if (fi.exists()) {
|
||||
@@ -58,9 +58,8 @@ QFileInfo TemplateImage::legacyImage() const
|
||||
return {};
|
||||
}
|
||||
|
||||
QFileInfo TemplateImage::jsonImage(TestFlags &flags, QString& comment) const
|
||||
QFileInfo TemplateImage::jsonImage(bool &skipTest) const
|
||||
{
|
||||
flags = TestFlag::None;
|
||||
auto fi = QFileInfo(QStringLiteral("%1.json").arg(m_fi.filePath()));
|
||||
if (!fi.exists()) {
|
||||
return {};
|
||||
@@ -87,10 +86,6 @@ QFileInfo TemplateImage::jsonImage(TestFlags &flags, QString& comment) const
|
||||
auto maxQt = QVersionNumber::fromString(obj.value("maxQtVersion").toString());
|
||||
auto name = obj.value("fileName").toString();
|
||||
auto unsupportedFormat = obj.value("unsupportedFormat").toBool();
|
||||
comment = obj.value("comment").toString();
|
||||
|
||||
if(obj.value("disableAutoTransform").toBool())
|
||||
flags |= TestFlag::DisableAutotransform;
|
||||
|
||||
// filter
|
||||
if (name.isEmpty() && !unsupportedFormat)
|
||||
@@ -100,7 +95,7 @@ QFileInfo TemplateImage::jsonImage(TestFlags &flags, QString& comment) const
|
||||
if (!maxQt.isNull() && currentQt > maxQt)
|
||||
continue;
|
||||
if (unsupportedFormat) {
|
||||
flags |= TestFlag::SkipTest;
|
||||
skipTest = true;
|
||||
break;
|
||||
}
|
||||
return QFileInfo(QStringLiteral("%1/%2").arg(fi.path(), name));
|
||||
|
||||
@@ -16,13 +16,6 @@
|
||||
class TemplateImage
|
||||
{
|
||||
public:
|
||||
enum TestFlag {
|
||||
None = 0x0,
|
||||
SkipTest = 0x1,
|
||||
DisableAutotransform = 0x2
|
||||
};
|
||||
Q_DECLARE_FLAGS(TestFlags, TestFlag)
|
||||
|
||||
/*!
|
||||
* \brief TemplateImage
|
||||
* \param fi The image to test.
|
||||
@@ -49,10 +42,10 @@ public:
|
||||
|
||||
/*!
|
||||
* \brief compareImage
|
||||
* \param flags Flags for modifying test behavior (e.g. image format not supported by current Qt version).
|
||||
* \param skipTest True if the test should be skipped (e.g. image format not supported by current Qt version).
|
||||
* \return The template image to use for the comparison.
|
||||
*/
|
||||
QFileInfo compareImage(TestFlags &flags, QString& comment) const;
|
||||
QFileInfo compareImage(bool &skipTest) const;
|
||||
|
||||
/*!
|
||||
* \brief suffixes
|
||||
@@ -69,15 +62,13 @@ private:
|
||||
|
||||
/*!
|
||||
* \brief jsonImage
|
||||
* \param flags Flags for modifying test behavior.
|
||||
* \param skipTest True if the test should be skipped (not supported).
|
||||
* \return The template image read from the corresponding JSON.
|
||||
*/
|
||||
QFileInfo jsonImage(TestFlags &flags, QString& comment) const;
|
||||
QFileInfo jsonImage(bool &skipTest) const;
|
||||
|
||||
private:
|
||||
QFileInfo m_fi;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(TemplateImage::TestFlags)
|
||||
|
||||
#endif // TEMPLATEIMAGE_H
|
||||
|
||||
@@ -619,15 +619,7 @@ bool QAVIFHandler::write(const QImage &image)
|
||||
QImage tmpgrayimage = image.convertToFormat(tmpformat);
|
||||
|
||||
avif = avifImageCreate(tmpgrayimage.width(), tmpgrayimage.height(), save_depth, AVIF_PIXEL_FORMAT_YUV400);
|
||||
#if AVIF_VERSION >= 110000
|
||||
res = avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
|
||||
if (res != AVIF_RESULT_OK) {
|
||||
qWarning("ERROR in avifImageAllocatePlanes: %s", avifResultToString(res));
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
avifImageAllocatePlanes(avif, AVIF_PLANES_YUV);
|
||||
#endif
|
||||
|
||||
if (tmpgrayimage.colorSpace().isValid()) {
|
||||
avif->colorPrimaries = (avifColorPrimaries)1;
|
||||
@@ -814,15 +806,7 @@ bool QAVIFHandler::write(const QImage &image)
|
||||
avif->transferCharacteristics = transfer_to_save;
|
||||
|
||||
if (iccprofile.size() > 0) {
|
||||
#if AVIF_VERSION >= 1000000
|
||||
res = avifImageSetProfileICC(avif, reinterpret_cast<const uint8_t *>(iccprofile.constData()), iccprofile.size());
|
||||
if (res != AVIF_RESULT_OK) {
|
||||
qWarning("ERROR in avifImageSetProfileICC: %s", avifResultToString(res));
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
avifImageSetProfileICC(avif, reinterpret_cast<const uint8_t *>(iccprofile.constData()), iccprofile.size());
|
||||
#endif
|
||||
}
|
||||
|
||||
avifRGBImage rgb;
|
||||
@@ -987,8 +971,6 @@ bool QAVIFHandler::jumpToNextImage()
|
||||
return false;
|
||||
}
|
||||
|
||||
avifResult decodeResult;
|
||||
|
||||
if (m_decoder->imageIndex >= 0) {
|
||||
if (m_decoder->imageCount < 2) {
|
||||
m_parseState = ParseAvifSuccess;
|
||||
@@ -996,16 +978,11 @@ 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));
|
||||
m_parseState = ParseAvifError;
|
||||
return false;
|
||||
}
|
||||
avifDecoderReset(m_decoder);
|
||||
}
|
||||
}
|
||||
|
||||
decodeResult = avifDecoderNextImage(m_decoder);
|
||||
avifResult decodeResult = avifDecoderNextImage(m_decoder);
|
||||
|
||||
if (decodeResult != AVIF_RESULT_OK) {
|
||||
qWarning("ERROR: Failed to decode Next image in sequence: %s", avifResultToString(decodeResult));
|
||||
|
||||
@@ -297,27 +297,23 @@ static void readMetadata(const Imf::Header &header, QImage &image)
|
||||
image.setText(QStringLiteral(META_KEY_XMP_ADOBE), QString::fromStdString(xmp->value()));
|
||||
}
|
||||
|
||||
// camera metadata
|
||||
if (auto manufacturer = header.findTypedAttribute<Imf::StringAttribute>("cameraMake")) {
|
||||
image.setText(QStringLiteral(META_KEY_MANUFACTURER), QString::fromStdString(manufacturer->value()));
|
||||
}
|
||||
if (auto model = header.findTypedAttribute<Imf::StringAttribute>("cameraModel")) {
|
||||
image.setText(QStringLiteral(META_KEY_MODEL), QString::fromStdString(model->value()));
|
||||
}
|
||||
if (auto serial = header.findTypedAttribute<Imf::StringAttribute>("cameraSerialNumber")) {
|
||||
image.setText(QStringLiteral(META_KEY_SERIALNUMBER), QString::fromStdString(serial->value()));
|
||||
}
|
||||
|
||||
// lens metadata
|
||||
if (auto manufacturer = header.findTypedAttribute<Imf::StringAttribute>("lensMake")) {
|
||||
image.setText(QStringLiteral(META_KEY_LENS_MANUFACTURER), QString::fromStdString(manufacturer->value()));
|
||||
}
|
||||
if (auto model = header.findTypedAttribute<Imf::StringAttribute>("lensModel")) {
|
||||
image.setText(QStringLiteral(META_KEY_LENS_MODEL), QString::fromStdString(model->value()));
|
||||
}
|
||||
if (auto serial = header.findTypedAttribute<Imf::StringAttribute>("lensSerialNumber")) {
|
||||
image.setText(QStringLiteral(META_KEY_LENS_SERIALNUMBER), QString::fromStdString(serial->value()));
|
||||
}
|
||||
/* TODO: OpenEXR 3.2 metadata
|
||||
*
|
||||
* New Optional Standard Attributes:
|
||||
* - Support automated editorial workflow:
|
||||
* reelName, imageCounter, ascFramingDecisionList
|
||||
*
|
||||
* - Support forensics (“which other shots used that camera and lens before the camera firmware was updated?”):
|
||||
* cameraMake, cameraModel, cameraSerialNumber, cameraFirmware, cameraUuid, cameraLabel, lensMake, lensModel,
|
||||
* lensSerialNumber, lensFirmware, cameraColorBalance
|
||||
*
|
||||
* -Support pickup shots (reproduce critical camera settings):
|
||||
* shutterAngle, cameraCCTSetting, cameraTintSetting
|
||||
*
|
||||
* - Support metadata-driven match move:
|
||||
* sensorCenterOffset, sensorOverallDimensions, sensorPhotositePitch, sensorAcquisitionRectanglenominalFocalLength,
|
||||
* effectiveFocalLength, pinholeFocalLength, entrancePupilOffset, tStop(complementing existing 'aperture')
|
||||
*/
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -507,26 +503,6 @@ static void setMetadata(const QImage &image, Imf::Header &header)
|
||||
header.insert("xmp", Imf::StringAttribute(text.toStdString()));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!key.compare(QStringLiteral(META_KEY_MANUFACTURER), Qt::CaseInsensitive)) {
|
||||
header.insert("cameraMake", Imf::StringAttribute(text.toStdString()));
|
||||
}
|
||||
if (!key.compare(QStringLiteral(META_KEY_MODEL), Qt::CaseInsensitive)) {
|
||||
header.insert("cameraModel", Imf::StringAttribute(text.toStdString()));
|
||||
}
|
||||
if (!key.compare(QStringLiteral(META_KEY_SERIALNUMBER), Qt::CaseInsensitive)) {
|
||||
header.insert("cameraSerialNumber", Imf::StringAttribute(text.toStdString()));
|
||||
}
|
||||
|
||||
if (!key.compare(QStringLiteral(META_KEY_LENS_MANUFACTURER), Qt::CaseInsensitive)) {
|
||||
header.insert("lensMake", Imf::StringAttribute(text.toStdString()));
|
||||
}
|
||||
if (!key.compare(QStringLiteral(META_KEY_LENS_MODEL), Qt::CaseInsensitive)) {
|
||||
header.insert("lensModel", Imf::StringAttribute(text.toStdString()));
|
||||
}
|
||||
if (!key.compare(QStringLiteral(META_KEY_LENS_SERIALNUMBER), Qt::CaseInsensitive)) {
|
||||
header.insert("lensSerialNumber", Imf::StringAttribute(text.toStdString()));
|
||||
}
|
||||
}
|
||||
if (dateTime.isValid()) {
|
||||
header.insert("capDate", Imf::StringAttribute(dateTime.toString(QStringLiteral("yyyy:MM:dd HH:mm:ss")).toStdString()));
|
||||
@@ -543,6 +519,8 @@ static void setMetadata(const QImage &image, Imf::Header &header)
|
||||
// If a file doesn’t have a chromaticities attribute, display software should assume that the
|
||||
// file’s primaries and the white point match Rec. ITU-R BT.709-3.
|
||||
// header.insert("chromaticities", Imf::ChromaticitiesAttribute(Imf::Chromaticities()));
|
||||
|
||||
// TODO: EXR 3.2 attributes (see readMetadata())
|
||||
}
|
||||
|
||||
bool EXRHandler::write(const QImage &image)
|
||||
|
||||
@@ -16,19 +16,11 @@
|
||||
#include <jxl/thread_parallel_runner.h>
|
||||
#include <string.h>
|
||||
|
||||
// Avoid rotation on buggy Qts (see also https://bugreports.qt.io/browse/QTBUG-126575)
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 7) && QT_VERSION < QT_VERSION_CHECK(6, 6, 0)) || (QT_VERSION >= QT_VERSION_CHECK(6, 7, 3))
|
||||
#ifndef JXL_QT_AUTOTRANSFORM
|
||||
#define JXL_QT_AUTOTRANSFORM
|
||||
#endif
|
||||
#endif
|
||||
|
||||
QJpegXLHandler::QJpegXLHandler()
|
||||
: m_parseState(ParseJpegXLNotParsed)
|
||||
, m_quality(90)
|
||||
, m_currentimage_index(0)
|
||||
, m_previousimage_index(-1)
|
||||
, m_transformations(QImageIOHandler::TransformationNone)
|
||||
, m_decoder(nullptr)
|
||||
, m_runner(nullptr)
|
||||
, m_next_image_delay(0)
|
||||
@@ -137,11 +129,6 @@ bool QJpegXLHandler::ensureDecoder()
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef JXL_QT_AUTOTRANSFORM
|
||||
// Let Qt handle the orientation.
|
||||
JxlDecoderSetKeepOrientation(m_decoder, true);
|
||||
#endif
|
||||
|
||||
int num_worker_threads = QThread::idealThreadCount();
|
||||
if (!m_runner && num_worker_threads >= 4) {
|
||||
/* use half of the threads because plug-in is usually used in environment
|
||||
@@ -581,25 +568,10 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
pixel_format.endianness = JXL_NATIVE_ENDIAN;
|
||||
pixel_format.align = 0;
|
||||
|
||||
output_info.orientation = JXL_ORIENT_IDENTITY;
|
||||
output_info.num_color_channels = 3;
|
||||
output_info.animation.tps_numerator = 10;
|
||||
output_info.animation.tps_denominator = 1;
|
||||
output_info.orientation = JXL_ORIENT_IDENTITY;
|
||||
if (m_transformations == QImageIOHandler::TransformationMirror) {
|
||||
output_info.orientation = JXL_ORIENT_FLIP_HORIZONTAL;
|
||||
} else if (m_transformations == QImageIOHandler::TransformationRotate180) {
|
||||
output_info.orientation = JXL_ORIENT_ROTATE_180;
|
||||
} else if (m_transformations == QImageIOHandler::TransformationFlip) {
|
||||
output_info.orientation = JXL_ORIENT_FLIP_VERTICAL;
|
||||
} else if (m_transformations == QImageIOHandler::TransformationFlipAndRotate90) {
|
||||
output_info.orientation = JXL_ORIENT_TRANSPOSE;
|
||||
} else if (m_transformations == QImageIOHandler::TransformationRotate90) {
|
||||
output_info.orientation = JXL_ORIENT_ROTATE_90_CW;
|
||||
} else if (m_transformations == QImageIOHandler::TransformationMirrorAndRotate90) {
|
||||
output_info.orientation = JXL_ORIENT_ANTI_TRANSPOSE;
|
||||
} else if (m_transformations == QImageIOHandler::TransformationRotate270) {
|
||||
output_info.orientation = JXL_ORIENT_ROTATE_90_CCW;
|
||||
}
|
||||
|
||||
if (save_depth > 8) { // 16bit depth
|
||||
pixel_format.data_type = JXL_TYPE_UINT16;
|
||||
@@ -805,24 +777,14 @@ bool QJpegXLHandler::write(const QImage &image)
|
||||
|
||||
QVariant QJpegXLHandler::option(ImageOption option) const
|
||||
{
|
||||
if (!supportsOption(option)) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (option == Quality) {
|
||||
return m_quality;
|
||||
}
|
||||
|
||||
if (!ensureParsed()) {
|
||||
#ifdef JXL_QT_AUTOTRANSFORM
|
||||
if (option == ImageTransformation) {
|
||||
return int(m_transformations);
|
||||
}
|
||||
#endif
|
||||
if (!supportsOption(option) || !ensureParsed()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
|
||||
switch (option) {
|
||||
case Size:
|
||||
return QSize(m_basicinfo.xsize, m_basicinfo.ysize);
|
||||
@@ -832,31 +794,9 @@ QVariant QJpegXLHandler::option(ImageOption option) const
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
#ifdef JXL_QT_AUTOTRANSFORM
|
||||
case ImageTransformation:
|
||||
if (m_basicinfo.orientation == JXL_ORIENT_IDENTITY) {
|
||||
return int(QImageIOHandler::TransformationNone);
|
||||
} else if (m_basicinfo.orientation == JXL_ORIENT_FLIP_HORIZONTAL) {
|
||||
return int(QImageIOHandler::TransformationMirror);
|
||||
} else if (m_basicinfo.orientation == JXL_ORIENT_ROTATE_180) {
|
||||
return int(QImageIOHandler::TransformationRotate180);
|
||||
} else if (m_basicinfo.orientation == JXL_ORIENT_FLIP_VERTICAL) {
|
||||
return int(QImageIOHandler::TransformationFlip);
|
||||
} else if (m_basicinfo.orientation == JXL_ORIENT_TRANSPOSE) {
|
||||
return int(QImageIOHandler::TransformationFlipAndRotate90);
|
||||
} else if (m_basicinfo.orientation == JXL_ORIENT_ROTATE_90_CW) {
|
||||
return int(QImageIOHandler::TransformationRotate90);
|
||||
} else if (m_basicinfo.orientation == JXL_ORIENT_ANTI_TRANSPOSE) {
|
||||
return int(QImageIOHandler::TransformationMirrorAndRotate90);
|
||||
} else if (m_basicinfo.orientation == JXL_ORIENT_ROTATE_90_CCW) {
|
||||
return int(QImageIOHandler::TransformationRotate270);
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void QJpegXLHandler::setOption(ImageOption option, const QVariant &value)
|
||||
@@ -870,14 +810,6 @@ void QJpegXLHandler::setOption(ImageOption option, const QVariant &value)
|
||||
m_quality = 90;
|
||||
}
|
||||
return;
|
||||
#ifdef JXL_QT_AUTOTRANSFORM
|
||||
case ImageTransformation:
|
||||
if (auto t = value.toInt()) {
|
||||
if (t > 0 && t < 8)
|
||||
m_transformations = QImageIOHandler::Transformations(t);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -886,11 +818,7 @@ void QJpegXLHandler::setOption(ImageOption option, const QVariant &value)
|
||||
|
||||
bool QJpegXLHandler::supportsOption(ImageOption option) const
|
||||
{
|
||||
auto supported = option == Quality || option == Size || option == Animation;
|
||||
#ifdef JXL_QT_AUTOTRANSFORM
|
||||
supported = supported || option == ImageTransformation;
|
||||
#endif
|
||||
return supported;
|
||||
return option == Quality || option == Size || option == Animation;
|
||||
}
|
||||
|
||||
int QJpegXLHandler::imageCount() const
|
||||
|
||||
@@ -64,7 +64,6 @@ private:
|
||||
int m_quality;
|
||||
int m_currentimage_index;
|
||||
int m_previousimage_index;
|
||||
QImageIOHandler::Transformations m_transformations;
|
||||
|
||||
QByteArray m_rawData;
|
||||
|
||||
|
||||
@@ -67,40 +67,6 @@ public:
|
||||
{
|
||||
return (Encoding == 1);
|
||||
}
|
||||
/*!
|
||||
* \brief isValid
|
||||
* Checks if the header data are valid for the PCX.
|
||||
* \note Put here the header sanity checks.
|
||||
* \return True if the header is a valid PCX header, otherwise false.
|
||||
*/
|
||||
inline bool isValid() const
|
||||
{
|
||||
return Manufacturer == 10 && BytesPerLine != 0;
|
||||
}
|
||||
/*!
|
||||
* \brief isSupported
|
||||
* \return True if the header is valid and the PCX format is supported by the plugin. Otherwise false.
|
||||
*/
|
||||
inline bool isSupported() const
|
||||
{
|
||||
return isValid() && format() != QImage::Format_Invalid;
|
||||
}
|
||||
inline QImage::Format format() const
|
||||
{
|
||||
auto fmt = QImage::Format_Invalid;
|
||||
if (Bpp == 1 && NPlanes == 1) {
|
||||
fmt = QImage::Format_Mono;
|
||||
} else if (Bpp == 1 && NPlanes == 4) {
|
||||
fmt = QImage::Format_Indexed8;
|
||||
} else if (Bpp == 4 && NPlanes == 1) {
|
||||
fmt = QImage::Format_Indexed8;
|
||||
} else if (Bpp == 8 && NPlanes == 1) {
|
||||
fmt = QImage::Format_Indexed8;
|
||||
} else if (Bpp == 8 && NPlanes == 3) {
|
||||
fmt = QImage::Format_RGB32;
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
|
||||
quint8 Manufacturer; // Constant Flag, 10 = ZSoft .pcx
|
||||
quint8 Version; // Version information·
|
||||
@@ -134,8 +100,6 @@ public:
|
||||
// found only in PB IV/IV Plus
|
||||
quint16 VScreenSize; // Vertical screen size in pixels. New field
|
||||
// found only in PB IV/IV Plus
|
||||
|
||||
quint8 unused[54];
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
@@ -209,8 +173,9 @@ static QDataStream &operator>>(QDataStream &s, PCXHEADER &ph)
|
||||
ph.VScreenSize = vscreensize;
|
||||
|
||||
// Skip the rest of the header
|
||||
for (size_t i = 0, n = sizeof(ph.unused); i < n; ++i) {
|
||||
s >> ph.unused[i];
|
||||
quint8 byte;
|
||||
for (auto i = 0; i < 54; ++i) {
|
||||
s >> byte;
|
||||
}
|
||||
|
||||
return s;
|
||||
@@ -248,8 +213,9 @@ static QDataStream &operator<<(QDataStream &s, const PCXHEADER &ph)
|
||||
s << ph.HScreenSize;
|
||||
s << ph.VScreenSize;
|
||||
|
||||
for (size_t i = 0, n = sizeof(ph.unused); i < n; ++i) {
|
||||
s << ph.unused[i];
|
||||
quint8 byte = 0;
|
||||
for (int i = 0; i < 54; ++i) {
|
||||
s << byte;
|
||||
}
|
||||
|
||||
return s;
|
||||
@@ -264,34 +230,6 @@ PCXHEADER::PCXHEADER()
|
||||
s >> *this;
|
||||
}
|
||||
|
||||
bool peekHeader(QIODevice *d, PCXHEADER& h)
|
||||
{
|
||||
qint64 pos = 0;
|
||||
if (!d->isSequential()) {
|
||||
pos = d->pos();
|
||||
}
|
||||
|
||||
auto ok = false;
|
||||
{ // datastream is destroyed before working on device
|
||||
QDataStream ds(d);
|
||||
ds.setByteOrder(QDataStream::LittleEndian);
|
||||
ds >> h;
|
||||
ok = ds.status() == QDataStream::Ok && h.isValid();
|
||||
}
|
||||
|
||||
if (!d->isSequential()) {
|
||||
return d->seek(pos) && ok;
|
||||
}
|
||||
|
||||
// sequential device undo
|
||||
auto head = reinterpret_cast<char*>(&h);
|
||||
auto readBytes = sizeof(h);
|
||||
while (readBytes > 0) {
|
||||
d->ungetChar(head[readBytes-- - 1]);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool readLine(QDataStream &s, QByteArray &buf, const PCXHEADER &header)
|
||||
{
|
||||
quint32 i = 0;
|
||||
@@ -327,7 +265,7 @@ static bool readImage1(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||
{
|
||||
QByteArray buf(header.BytesPerLine, 0);
|
||||
|
||||
img = imageAlloc(header.width(), header.height(), header.format());
|
||||
img = imageAlloc(header.width(), header.height(), QImage::Format_Mono);
|
||||
img.setColorCount(2);
|
||||
|
||||
if (img.isNull()) {
|
||||
@@ -363,18 +301,13 @@ static bool readImage4(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||
QByteArray buf(header.BytesPerLine * 4, 0);
|
||||
QByteArray pixbuf(header.width(), 0);
|
||||
|
||||
img = imageAlloc(header.width(), header.height(), header.format());
|
||||
img = imageAlloc(header.width(), header.height(), QImage::Format_Indexed8);
|
||||
img.setColorCount(16);
|
||||
if (img.isNull()) {
|
||||
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.BytesPerLine < (header.width() + 7) / 8) {
|
||||
qWarning() << "PCX image has invalid BytesPerLine value";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int y = 0; y < header.height(); ++y) {
|
||||
if (s.atEnd()) {
|
||||
return false;
|
||||
@@ -411,52 +344,11 @@ static bool readImage4(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool readImage4v2(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||
{
|
||||
QByteArray buf(header.BytesPerLine, 0);
|
||||
|
||||
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());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int y = 0; y < header.height(); ++y) {
|
||||
if (s.atEnd()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!readLine(s, buf, header)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uchar *p = img.scanLine(y);
|
||||
if (!p) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const unsigned int bpl = std::min(header.BytesPerLine, static_cast<quint16>(header.width() / 2));
|
||||
for (unsigned int x = 0; x < bpl; ++x) {
|
||||
p[x * 2] = (buf[x] & 240) >> 4;
|
||||
p[x * 2 + 1] = buf[x] & 15;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the palette
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
img.setColor(i, header.ColorMap.color(i));
|
||||
}
|
||||
|
||||
return (s.status() == QDataStream::Ok);
|
||||
}
|
||||
|
||||
static bool readImage8(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||
{
|
||||
QByteArray buf(header.BytesPerLine, 0);
|
||||
|
||||
img = imageAlloc(header.width(), header.height(), header.format());
|
||||
img = imageAlloc(header.width(), header.height(), QImage::Format_Indexed8);
|
||||
img.setColorCount(256);
|
||||
|
||||
if (img.isNull()) {
|
||||
@@ -519,15 +411,13 @@ static bool readImage24(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||
QByteArray g_buf(header.BytesPerLine, 0);
|
||||
QByteArray b_buf(header.BytesPerLine, 0);
|
||||
|
||||
img = imageAlloc(header.width(), header.height(), header.format());
|
||||
img = imageAlloc(header.width(), header.height(), QImage::Format_RGB32);
|
||||
|
||||
if (img.isNull()) {
|
||||
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width(), header.height());
|
||||
return false;
|
||||
}
|
||||
|
||||
const unsigned int bpl = std::min(header.BytesPerLine, static_cast<quint16>(header.width()));
|
||||
|
||||
for (int y = 0; y < header.height(); ++y) {
|
||||
if (s.atEnd()) {
|
||||
return false;
|
||||
@@ -544,8 +434,7 @@ static bool readImage24(QImage &img, QDataStream &s, const PCXHEADER &header)
|
||||
}
|
||||
|
||||
uint *p = (uint *)img.scanLine(y);
|
||||
|
||||
for (unsigned int x = 0; x < bpl; ++x) {
|
||||
for (int x = 0; x < header.width(); ++x) {
|
||||
p[x] = qRgb(r_buf[x], g_buf[x], b_buf[x]);
|
||||
}
|
||||
}
|
||||
@@ -747,18 +636,7 @@ static bool writeImage24(QImage &img, QDataStream &s, PCXHEADER &header)
|
||||
return true;
|
||||
}
|
||||
|
||||
class PCXHandlerPrivate
|
||||
{
|
||||
public:
|
||||
PCXHandlerPrivate() {}
|
||||
~PCXHandlerPrivate() {}
|
||||
|
||||
PCXHEADER m_header;
|
||||
};
|
||||
|
||||
PCXHandler::PCXHandler()
|
||||
: QImageIOHandler()
|
||||
, d(new PCXHandlerPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -780,14 +658,11 @@ bool PCXHandler::read(QImage *outImage)
|
||||
return false;
|
||||
}
|
||||
|
||||
auto&& header = d->m_header;
|
||||
PCXHEADER header;
|
||||
|
||||
s >> header;
|
||||
|
||||
if (s.status() != QDataStream::Ok || s.atEnd()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!header.isSupported()) {
|
||||
if (header.Manufacturer != 10 || header.BytesPerLine == 0 || s.atEnd()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -797,8 +672,6 @@ bool PCXHandler::read(QImage *outImage)
|
||||
ok = readImage1(img, s, header);
|
||||
} else if (header.Bpp == 1 && header.NPlanes == 4) {
|
||||
ok = readImage4(img, s, header);
|
||||
} else if (header.Bpp == 4 && header.NPlanes == 1) {
|
||||
ok = readImage4v2(img, s, header);
|
||||
} else if (header.Bpp == 8 && header.NPlanes == 1) {
|
||||
ok = readImage8(img, s, header);
|
||||
} else if (header.Bpp == 8 && header.NPlanes == 3) {
|
||||
@@ -857,46 +730,6 @@ bool PCXHandler::write(const QImage &image)
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool PCXHandler::supportsOption(ImageOption option) const
|
||||
{
|
||||
if (option == QImageIOHandler::Size) {
|
||||
return true;
|
||||
}
|
||||
if (option == QImageIOHandler::ImageFormat) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant PCXHandler::option(ImageOption option) const
|
||||
{
|
||||
QVariant v;
|
||||
|
||||
if (option == QImageIOHandler::Size) {
|
||||
auto&& header = d->m_header;
|
||||
if (header.isSupported()) {
|
||||
v = QVariant::fromValue(QSize(header.width(), header.height()));
|
||||
} else if (auto dev = device()) {
|
||||
if (peekHeader(dev, header) && header.isSupported()) {
|
||||
v = QVariant::fromValue(QSize(header.width(), header.height()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (option == QImageIOHandler::ImageFormat) {
|
||||
auto&& header = d->m_header;
|
||||
if (header.isSupported()) {
|
||||
v = QVariant::fromValue(header.format());
|
||||
} else if (auto dev = device()) {
|
||||
if (peekHeader(dev, header) && header.isSupported()) {
|
||||
v = QVariant::fromValue(header.format());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
bool PCXHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
@@ -904,11 +737,30 @@ bool PCXHandler::canRead(QIODevice *device)
|
||||
return false;
|
||||
}
|
||||
|
||||
PCXHEADER header;
|
||||
if (!peekHeader(device, header)) {
|
||||
qint64 oldPos = device->pos();
|
||||
|
||||
char head[1];
|
||||
qint64 readBytes = device->read(head, sizeof(head));
|
||||
if (readBytes != sizeof(head)) {
|
||||
if (device->isSequential()) {
|
||||
while (readBytes > 0) {
|
||||
device->ungetChar(head[readBytes-- - 1]);
|
||||
}
|
||||
} else {
|
||||
device->seek(oldPos);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return header.isSupported();
|
||||
|
||||
if (device->isSequential()) {
|
||||
while (readBytes > 0) {
|
||||
device->ungetChar(head[readBytes-- - 1]);
|
||||
}
|
||||
} else {
|
||||
device->seek(oldPos);
|
||||
}
|
||||
|
||||
return qstrncmp(head, "\012", 1) == 0;
|
||||
}
|
||||
|
||||
QImageIOPlugin::Capabilities PCXPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
||||
|
||||
@@ -9,9 +9,7 @@
|
||||
#define KIMG_PCX_P_H
|
||||
|
||||
#include <QImageIOPlugin>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class PCXHandlerPrivate;
|
||||
class PCXHandler : public QImageIOHandler
|
||||
{
|
||||
public:
|
||||
@@ -21,13 +19,7 @@ public:
|
||||
bool read(QImage *image) override;
|
||||
bool write(const QImage &image) override;
|
||||
|
||||
bool supportsOption(QImageIOHandler::ImageOption option) const override;
|
||||
QVariant option(QImageIOHandler::ImageOption option) const override;
|
||||
|
||||
static bool canRead(QIODevice *device);
|
||||
|
||||
private:
|
||||
const QScopedPointer<PCXHandlerPrivate> d;
|
||||
};
|
||||
|
||||
class PCXPlugin : public QImageIOPlugin
|
||||
|
||||
@@ -433,7 +433,7 @@ inline void rgbToRgbX(uchar *target, const uchar *source, qint32 targetSize, qin
|
||||
#define T_FLAGS(a) (((a) >> 31) & 0x1)
|
||||
// clang-format on
|
||||
|
||||
#define DEFAULT_IMAGE_QUALITY (C_IQ(3) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0) | C_FLAGS(1))
|
||||
#define DEFAULT_QUALITY (C_IQ(3) | C_OC(1) | C_CW(1) | C_AW(1) | C_BT(1) | C_HS(0) | C_FLAGS(1))
|
||||
|
||||
void setParams(QImageIOHandler *handler, LibRaw *rawProcessor)
|
||||
{
|
||||
@@ -497,7 +497,7 @@ void setParams(QImageIOHandler *handler, LibRaw *rawProcessor)
|
||||
quality |= C_FLAGS(1);
|
||||
}
|
||||
if (quality == -1) {
|
||||
quality = DEFAULT_IMAGE_QUALITY;
|
||||
quality = DEFAULT_QUALITY;
|
||||
}
|
||||
Q_ASSERT(T_FLAGS(quality));
|
||||
|
||||
|
||||
@@ -22,8 +22,6 @@
|
||||
#include "rgb_p.h"
|
||||
#include "util_p.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
|
||||
@@ -74,25 +72,15 @@ private:
|
||||
uint _offset;
|
||||
};
|
||||
|
||||
class SGIImagePrivate
|
||||
class SGIImage
|
||||
{
|
||||
public:
|
||||
SGIImagePrivate();
|
||||
~SGIImagePrivate();
|
||||
SGIImage(QIODevice *device);
|
||||
~SGIImage();
|
||||
|
||||
bool readImage(QImage &);
|
||||
bool writeImage(const QImage &);
|
||||
|
||||
bool isValid() const;
|
||||
bool isSupported() const;
|
||||
|
||||
bool peekHeader(QIODevice *device);
|
||||
|
||||
QSize size() const;
|
||||
QImage::Format format() const;
|
||||
|
||||
void setDevice(QIODevice *device);
|
||||
|
||||
private:
|
||||
enum {
|
||||
NORMAL,
|
||||
@@ -103,19 +91,16 @@ private:
|
||||
QIODevice *_dev;
|
||||
QDataStream _stream;
|
||||
|
||||
quint16 _magic = 0;
|
||||
quint8 _rle = 0;
|
||||
quint8 _bpc = 0;
|
||||
quint16 _dim = 0;
|
||||
quint16 _xsize = 0;
|
||||
quint16 _ysize = 0;
|
||||
quint16 _zsize = 0;
|
||||
quint32 _pixmin = 0;
|
||||
quint32 _pixmax = 0;
|
||||
quint8 _rle;
|
||||
quint8 _bpc;
|
||||
quint16 _dim;
|
||||
quint16 _xsize;
|
||||
quint16 _ysize;
|
||||
quint16 _zsize;
|
||||
quint32 _pixmin;
|
||||
quint32 _pixmax;
|
||||
char _imagename[80];
|
||||
quint32 _colormap = 0;
|
||||
quint8 _unused[404];
|
||||
quint32 _unused32 = 0;
|
||||
quint32 _colormap;
|
||||
|
||||
quint32 *_starttab;
|
||||
quint32 *_lengthtab;
|
||||
@@ -127,28 +112,24 @@ private:
|
||||
|
||||
bool readData(QImage &);
|
||||
bool getRow(uchar *dest);
|
||||
bool readHeader();
|
||||
|
||||
static bool readHeader(QDataStream &ds, SGIImagePrivate *sgi);
|
||||
|
||||
bool writeHeader();
|
||||
bool writeRle();
|
||||
bool writeVerbatim(const QImage &);
|
||||
void writeHeader();
|
||||
void writeRle();
|
||||
void writeVerbatim(const QImage &);
|
||||
bool scanData(const QImage &);
|
||||
uint compact(uchar *, uchar *);
|
||||
uchar intensity(uchar);
|
||||
};
|
||||
|
||||
SGIImagePrivate::SGIImagePrivate()
|
||||
: _dev(nullptr)
|
||||
, _starttab(nullptr)
|
||||
SGIImage::SGIImage(QIODevice *io)
|
||||
: _starttab(nullptr)
|
||||
, _lengthtab(nullptr)
|
||||
{
|
||||
std::memset(_imagename, 0, sizeof(_imagename));
|
||||
std::memset(_unused, 0, sizeof(_unused));
|
||||
_dev = io;
|
||||
_stream.setDevice(_dev);
|
||||
}
|
||||
|
||||
SGIImagePrivate::~SGIImagePrivate()
|
||||
SGIImage::~SGIImage()
|
||||
{
|
||||
delete[] _starttab;
|
||||
delete[] _lengthtab;
|
||||
@@ -156,13 +137,7 @@ SGIImagePrivate::~SGIImagePrivate()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SGIImagePrivate::setDevice(QIODevice *device)
|
||||
{
|
||||
_dev = device;
|
||||
_stream.setDevice(_dev);
|
||||
}
|
||||
|
||||
bool SGIImagePrivate::getRow(uchar *dest)
|
||||
bool SGIImage::getRow(uchar *dest)
|
||||
{
|
||||
int n;
|
||||
int i;
|
||||
@@ -205,7 +180,7 @@ bool SGIImagePrivate::getRow(uchar *dest)
|
||||
return i == _xsize;
|
||||
}
|
||||
|
||||
bool SGIImagePrivate::readData(QImage &img)
|
||||
bool SGIImage::readData(QImage &img)
|
||||
{
|
||||
QRgb *c;
|
||||
quint32 *start = _starttab;
|
||||
@@ -283,9 +258,66 @@ bool SGIImagePrivate::readData(QImage &img)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SGIImagePrivate::readImage(QImage &img)
|
||||
bool SGIImage::readImage(QImage &img)
|
||||
{
|
||||
if (!readHeader() || !isSupported()) {
|
||||
qint8 u8;
|
||||
qint16 u16;
|
||||
qint32 u32;
|
||||
|
||||
// qDebug() << "reading rgb ";
|
||||
|
||||
// magic
|
||||
_stream >> u16;
|
||||
if (u16 != 0x01da) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// verbatim/rle
|
||||
_stream >> _rle;
|
||||
// qDebug() << (_rle ? "RLE" : "verbatim");
|
||||
if (_rle > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// bytes per channel
|
||||
_stream >> _bpc;
|
||||
// qDebug() << "bytes per channel: " << int(_bpc);
|
||||
if (_bpc == 1) {
|
||||
;
|
||||
} else if (_bpc == 2) {
|
||||
// qDebug() << "dropping least significant byte";
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// number of dimensions
|
||||
_stream >> _dim;
|
||||
// qDebug() << "dimensions: " << _dim;
|
||||
if (_dim < 1 || _dim > 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_stream >> _xsize >> _ysize >> _zsize >> _pixmin >> _pixmax >> u32;
|
||||
// qDebug() << "x: " << _xsize;
|
||||
// qDebug() << "y: " << _ysize;
|
||||
// qDebug() << "z: " << _zsize;
|
||||
|
||||
// name
|
||||
_stream.readRawData(_imagename, 80);
|
||||
_imagename[79] = '\0';
|
||||
|
||||
_stream >> _colormap;
|
||||
// qDebug() << "colormap: " << _colormap;
|
||||
if (_colormap != NORMAL) {
|
||||
return false; // only NORMAL supported
|
||||
}
|
||||
|
||||
for (int i = 0; i < 404; i++) {
|
||||
_stream >> u8;
|
||||
}
|
||||
|
||||
if (_dim == 1) {
|
||||
// qDebug() << "1-dimensional images aren't supported yet";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -293,13 +325,19 @@ bool SGIImagePrivate::readImage(QImage &img)
|
||||
return false;
|
||||
}
|
||||
|
||||
img = imageAlloc(size(), format());
|
||||
img = imageAlloc(_xsize, _ysize, QImage::Format_RGB32);
|
||||
if (img.isNull()) {
|
||||
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(_xsize, _ysize);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_zsize > 4) {
|
||||
if (_zsize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_zsize == 2 || _zsize == 4) {
|
||||
img = img.convertToFormat(QImage::Format_ARGB32);
|
||||
} else if (_zsize > 4) {
|
||||
// qDebug() << "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
|
||||
@@ -397,7 +435,7 @@ QList<const RLEData *> RLEMap::vector()
|
||||
return v;
|
||||
}
|
||||
|
||||
uchar SGIImagePrivate::intensity(uchar c)
|
||||
uchar SGIImage::intensity(uchar c)
|
||||
{
|
||||
if (c < _pixmin) {
|
||||
_pixmin = c;
|
||||
@@ -408,7 +446,7 @@ uchar SGIImagePrivate::intensity(uchar c)
|
||||
return c;
|
||||
}
|
||||
|
||||
uint SGIImagePrivate::compact(uchar *d, uchar *s)
|
||||
uint SGIImage::compact(uchar *d, uchar *s)
|
||||
{
|
||||
uchar *dest = d;
|
||||
uchar *src = s;
|
||||
@@ -451,7 +489,7 @@ uint SGIImagePrivate::compact(uchar *d, uchar *s)
|
||||
return dest - d;
|
||||
}
|
||||
|
||||
bool SGIImagePrivate::scanData(const QImage &img)
|
||||
bool SGIImage::scanData(const QImage &img)
|
||||
{
|
||||
quint32 *start = _starttab;
|
||||
QByteArray lineguard(_xsize * 2, 0);
|
||||
@@ -537,127 +575,13 @@ bool SGIImagePrivate::scanData(const QImage &img)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SGIImagePrivate::isValid() const
|
||||
void SGIImage::writeHeader()
|
||||
{
|
||||
// File signature/magic number
|
||||
if (_magic != 0x01da) {
|
||||
return false;
|
||||
}
|
||||
// Compression, 0 = Uncompressed, 1 = RLE compressed
|
||||
if (_rle > 1) {
|
||||
return false;
|
||||
}
|
||||
// Bytes per pixel, 1 = 8 bit, 2 = 16 bit
|
||||
if (_bpc != 1 && _bpc != 2) {
|
||||
return false;
|
||||
}
|
||||
// Image dimension, 3 for RGBA image
|
||||
if (_dim < 1 || _dim > 3) {
|
||||
return false;
|
||||
}
|
||||
// Number channels in the image file, 4 for RGBA image
|
||||
if (_zsize < 1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SGIImagePrivate::isSupported() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return false;
|
||||
}
|
||||
if (_colormap != NORMAL) {
|
||||
return false; // only NORMAL supported
|
||||
}
|
||||
if (_dim == 1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SGIImagePrivate::peekHeader(QIODevice *device)
|
||||
{
|
||||
qint64 pos = 0;
|
||||
if (!device->isSequential()) {
|
||||
pos = device->pos();
|
||||
}
|
||||
|
||||
auto ok = false;
|
||||
QByteArray header;
|
||||
{ // datastream is destroyed before working on device
|
||||
header = device->read(512);
|
||||
QDataStream ds(header);
|
||||
ok = SGIImagePrivate::readHeader(ds, this) && isValid();
|
||||
}
|
||||
|
||||
if (!device->isSequential()) {
|
||||
return device->seek(pos) && ok;
|
||||
}
|
||||
|
||||
// sequential device undo
|
||||
auto head = header.data();
|
||||
auto readBytes = header.size();
|
||||
while (readBytes > 0) {
|
||||
device->ungetChar(head[readBytes-- - 1]);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
QSize SGIImagePrivate::size() const
|
||||
{
|
||||
return QSize(_xsize, _ysize);
|
||||
}
|
||||
|
||||
QImage::Format SGIImagePrivate::format() const
|
||||
{
|
||||
if (_zsize == 2 || _zsize == 4) {
|
||||
return QImage::Format_ARGB32;
|
||||
}
|
||||
return QImage::Format_RGB32;
|
||||
}
|
||||
|
||||
bool SGIImagePrivate::readHeader()
|
||||
{
|
||||
return readHeader(_stream, this);
|
||||
}
|
||||
|
||||
bool SGIImagePrivate::readHeader(QDataStream &ds, SGIImagePrivate *sgi)
|
||||
{
|
||||
// magic
|
||||
ds >> sgi->_magic;
|
||||
|
||||
// verbatim/rle
|
||||
ds >> sgi->_rle;
|
||||
|
||||
// bytes per channel
|
||||
ds >> sgi->_bpc;
|
||||
|
||||
// number of dimensions
|
||||
ds >> sgi->_dim;
|
||||
|
||||
ds >> sgi->_xsize >> sgi->_ysize >> sgi->_zsize >> sgi->_pixmin >> sgi->_pixmax >> sgi->_unused32;
|
||||
|
||||
// name
|
||||
ds.readRawData(sgi->_imagename, 80);
|
||||
sgi->_imagename[79] = '\0';
|
||||
|
||||
ds >> sgi->_colormap;
|
||||
|
||||
for (size_t i = 0; i < sizeof(_unused); i++) {
|
||||
ds >> sgi->_unused[i];
|
||||
}
|
||||
|
||||
return ds.status() == QDataStream::Ok;
|
||||
}
|
||||
|
||||
bool SGIImagePrivate::writeHeader()
|
||||
{
|
||||
_stream << _magic;
|
||||
_stream << quint16(0x01da);
|
||||
_stream << _rle << _bpc << _dim;
|
||||
_stream << _xsize << _ysize << _zsize;
|
||||
_stream << _pixmin << _pixmax;
|
||||
_stream << _unused32;
|
||||
_stream << quint32(0);
|
||||
|
||||
for (int i = 0; i < 80; i++) {
|
||||
_imagename[i] = '\0';
|
||||
@@ -665,20 +589,16 @@ bool SGIImagePrivate::writeHeader()
|
||||
_stream.writeRawData(_imagename, 80);
|
||||
|
||||
_stream << _colormap;
|
||||
for (size_t i = 0; i < sizeof(_unused); i++) {
|
||||
_stream << _unused[i];
|
||||
for (int i = 0; i < 404; i++) {
|
||||
_stream << quint8(0);
|
||||
}
|
||||
return _stream.status() == QDataStream::Ok;
|
||||
}
|
||||
|
||||
bool SGIImagePrivate::writeRle()
|
||||
void SGIImage::writeRle()
|
||||
{
|
||||
_rle = 1;
|
||||
// qDebug() << "writing RLE data";
|
||||
if (!writeHeader()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
writeHeader();
|
||||
uint i;
|
||||
|
||||
// write start table
|
||||
@@ -695,16 +615,13 @@ bool SGIImagePrivate::writeRle()
|
||||
for (i = 0; (int)i < _rlevector.size(); i++) {
|
||||
const_cast<RLEData *>(_rlevector[i])->write(_stream);
|
||||
}
|
||||
|
||||
return _stream.status() == QDataStream::Ok;
|
||||
}
|
||||
|
||||
bool SGIImagePrivate::writeVerbatim(const QImage &img)
|
||||
void SGIImage::writeVerbatim(const QImage &img)
|
||||
{
|
||||
_rle = 0;
|
||||
if (!writeHeader()) {
|
||||
return false;
|
||||
}
|
||||
// qDebug() << "writing verbatim data";
|
||||
writeHeader();
|
||||
|
||||
const QRgb *c;
|
||||
unsigned x;
|
||||
@@ -718,7 +635,7 @@ bool SGIImagePrivate::writeVerbatim(const QImage &img)
|
||||
}
|
||||
|
||||
if (_zsize == 1) {
|
||||
return _stream.status() == QDataStream::Ok;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_zsize != 2) {
|
||||
@@ -737,7 +654,7 @@ bool SGIImagePrivate::writeVerbatim(const QImage &img)
|
||||
}
|
||||
|
||||
if (_zsize == 3) {
|
||||
return _stream.status() == QDataStream::Ok;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -747,11 +664,9 @@ bool SGIImagePrivate::writeVerbatim(const QImage &img)
|
||||
_stream << quint8(qAlpha(*c++));
|
||||
}
|
||||
}
|
||||
|
||||
return _stream.status() == QDataStream::Ok;
|
||||
}
|
||||
|
||||
bool SGIImagePrivate::writeImage(const QImage &image)
|
||||
bool SGIImage::writeImage(const QImage &image)
|
||||
{
|
||||
// qDebug() << "writing "; // TODO add filename
|
||||
QImage img = image;
|
||||
@@ -783,7 +698,6 @@ bool SGIImagePrivate::writeImage(const QImage &image)
|
||||
return false;
|
||||
}
|
||||
|
||||
_magic = 0x01da;
|
||||
_bpc = 1;
|
||||
_xsize = w;
|
||||
_ysize = h;
|
||||
@@ -808,16 +722,16 @@ bool SGIImagePrivate::writeImage(const QImage &image)
|
||||
}
|
||||
|
||||
if (verbatim_size <= rle_size) {
|
||||
return writeVerbatim(img);
|
||||
writeVerbatim(img);
|
||||
} else {
|
||||
writeRle();
|
||||
}
|
||||
return writeRle();
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RGBHandler::RGBHandler()
|
||||
: QImageIOHandler()
|
||||
, d(new SGIImagePrivate)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -832,54 +746,14 @@ bool RGBHandler::canRead() const
|
||||
|
||||
bool RGBHandler::read(QImage *outImage)
|
||||
{
|
||||
d->setDevice(device());
|
||||
return d->readImage(*outImage);
|
||||
SGIImage sgi(device());
|
||||
return sgi.readImage(*outImage);
|
||||
}
|
||||
|
||||
bool RGBHandler::write(const QImage &image)
|
||||
{
|
||||
d->setDevice(device());
|
||||
return d->writeImage(image);
|
||||
}
|
||||
|
||||
bool RGBHandler::supportsOption(ImageOption option) const
|
||||
{
|
||||
if (option == QImageIOHandler::Size) {
|
||||
return true;
|
||||
}
|
||||
if (option == QImageIOHandler::ImageFormat) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant RGBHandler::option(ImageOption option) const
|
||||
{
|
||||
QVariant v;
|
||||
|
||||
if (option == QImageIOHandler::Size) {
|
||||
auto &&sgi = d;
|
||||
if (sgi->isSupported()) {
|
||||
v = QVariant::fromValue(sgi->size());
|
||||
} else if (auto dev = device()) {
|
||||
if (d->peekHeader(dev) && sgi->isSupported()) {
|
||||
v = QVariant::fromValue(sgi->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (option == QImageIOHandler::ImageFormat) {
|
||||
auto &&sgi = d;
|
||||
if (sgi->isSupported()) {
|
||||
v = QVariant::fromValue(sgi->format());
|
||||
} else if (auto dev = device()) {
|
||||
if (d->peekHeader(dev) && sgi->isSupported()) {
|
||||
v = QVariant::fromValue(sgi->format());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
SGIImage sgi(device());
|
||||
return sgi.writeImage(image);
|
||||
}
|
||||
|
||||
bool RGBHandler::canRead(QIODevice *device)
|
||||
@@ -889,8 +763,20 @@ bool RGBHandler::canRead(QIODevice *device)
|
||||
return false;
|
||||
}
|
||||
|
||||
SGIImagePrivate sgi;
|
||||
return sgi.peekHeader(device) && sgi.isSupported();
|
||||
const qint64 oldPos = device->pos();
|
||||
const QByteArray head = device->readLine(64);
|
||||
int readBytes = head.size();
|
||||
|
||||
if (device->isSequential()) {
|
||||
while (readBytes > 0) {
|
||||
device->ungetChar(head[readBytes-- - 1]);
|
||||
}
|
||||
|
||||
} else {
|
||||
device->seek(oldPos);
|
||||
}
|
||||
|
||||
return head.size() >= 4 && head.startsWith("\x01\xda") && (head[2] == 0 || head[2] == 1) && (head[3] == 1 || head[3] == 2);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -9,9 +9,7 @@
|
||||
#define KIMG_RGB_P_H
|
||||
|
||||
#include <QImageIOPlugin>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class SGIImagePrivate;
|
||||
class RGBHandler : public QImageIOHandler
|
||||
{
|
||||
public:
|
||||
@@ -21,13 +19,7 @@ public:
|
||||
bool read(QImage *image) override;
|
||||
bool write(const QImage &image) override;
|
||||
|
||||
bool supportsOption(QImageIOHandler::ImageOption option) const override;
|
||||
QVariant option(QImageIOHandler::ImageOption option) const override;
|
||||
|
||||
static bool canRead(QIODevice *device);
|
||||
|
||||
private:
|
||||
QScopedPointer<SGIImagePrivate> d;
|
||||
};
|
||||
|
||||
class RGBPlugin : public QImageIOPlugin
|
||||
|
||||
@@ -24,22 +24,15 @@
|
||||
#define META_KEY_HOSTCOMPUTER "HostComputer"
|
||||
#define META_KEY_LATITUDE "Latitude"
|
||||
#define META_KEY_LONGITUDE "Longitude"
|
||||
#define META_KEY_HOSTCOMPUTER "HostComputer"
|
||||
#define META_KEY_MANUFACTURER "Manufacturer"
|
||||
#define META_KEY_MODEL "Model"
|
||||
#define META_KEY_OWNER "Owner"
|
||||
#define META_KEY_SOFTWARE "Software"
|
||||
#define META_KEY_TITLE "Title"
|
||||
#define META_KEY_XML_GIMP "XML:org.gimp.xml"
|
||||
#define META_KEY_XMP_ADOBE "XML:com.adobe.xmp"
|
||||
|
||||
// Camera info metadata keys
|
||||
#define META_KEY_MANUFACTURER "Manufacturer"
|
||||
#define META_KEY_MODEL "Model"
|
||||
#define META_KEY_SERIALNUMBER "SerialNumber"
|
||||
|
||||
// Lens info metadata keys
|
||||
#define META_KEY_LENS_MANUFACTURER "LensManufacturer"
|
||||
#define META_KEY_LENS_MODEL "LensModel"
|
||||
#define META_KEY_LENS_SERIALNUMBER "LensSerialNumber"
|
||||
|
||||
// QList uses some extra space for stuff, hence the 32 here suggested by Thiago Macieira
|
||||
static constexpr int kMaxQVectorSize = std::numeric_limits<int>::max() - 32;
|
||||
|
||||
|
||||
@@ -1672,12 +1672,8 @@ bool XCFImageFormat::assignImageBytes(Layer &layer, uint i, uint j, const GimpPr
|
||||
for (int y = 0; y < height; y++) {
|
||||
uchar *dataPtr = bits + y * bytesPerLine;
|
||||
uchar *alphaPtr = nullptr;
|
||||
if (layer.alpha_tiles.size() > j && layer.alpha_tiles.at(j).size() > i) {
|
||||
QImage &alphaTile = layer.alpha_tiles[j][i];
|
||||
if (alphaTile.width() >= width && alphaTile.height() > y) {
|
||||
alphaPtr = alphaTile.scanLine(y);
|
||||
}
|
||||
}
|
||||
if (!layer.alpha_tiles.isEmpty())
|
||||
alphaPtr = layer.alpha_tiles[j][i].scanLine(y);
|
||||
if (bpc == 4) {
|
||||
#ifdef USE_FLOAT_IMAGES
|
||||
if (precision < GimpPrecision::GIMP_PRECISION_HALF_LINEAR) {
|
||||
@@ -1974,12 +1970,6 @@ static bool convertFloatTo16Bit(uchar *output, quint64 outputSize, uchar *input)
|
||||
*/
|
||||
bool XCFImageFormat::loadLevel(QDataStream &xcf_io, Layer &layer, qint32 bpp, const GimpPrecision precision)
|
||||
{
|
||||
auto bpc = bytesPerChannel(precision);
|
||||
if ((bpc == 0) || (bpp % bpc)) {
|
||||
qCDebug(XCFPLUGIN) << "XCF: the stream seems corrupted";
|
||||
return false;
|
||||
}
|
||||
|
||||
qint32 width;
|
||||
qint32 height;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user