mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-07-17 11:44:16 -04:00
Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
ae6b724824 | |||
3e751dd80d | |||
e69dff73e6 | |||
64cfe52bee | |||
8732fc8487 |
@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.16)
|
|||||||
project(KImageFormats)
|
project(KImageFormats)
|
||||||
|
|
||||||
include(FeatureSummary)
|
include(FeatureSummary)
|
||||||
find_package(ECM 5.92.0 NO_MODULE)
|
find_package(ECM 5.93.0 NO_MODULE)
|
||||||
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules")
|
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules")
|
||||||
feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||||
|
|
||||||
|
BIN
autotests/read/xcf/fruktpilot_icc.png
Normal file
BIN
autotests/read/xcf/fruktpilot_icc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 115 KiB |
BIN
autotests/read/xcf/fruktpilot_icc.xcf
Normal file
BIN
autotests/read/xcf/fruktpilot_icc.xcf
Normal file
Binary file not shown.
@ -446,6 +446,16 @@ bool QAVIFHandler::write(const QImage &image)
|
|||||||
return false;
|
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!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_quality >= 100 && !avifCodecName(AVIF_CODEC_CHOICE_AOM, AVIF_CODEC_FLAG_CAN_ENCODE)) {
|
||||||
|
qWarning("You are using %s encoder. It is recommended to enable libAOM encoder in libavif for better near-lossless compression.", encoder_name);
|
||||||
|
}
|
||||||
|
|
||||||
int maxQuantizer = AVIF_QUANTIZER_WORST_QUALITY * (100 - qBound(0, m_quality, 100)) / 100;
|
int maxQuantizer = AVIF_QUANTIZER_WORST_QUALITY * (100 - qBound(0, m_quality, 100)) / 100;
|
||||||
int minQuantizer = 0;
|
int minQuantizer = 0;
|
||||||
int maxQuantizerAlpha = 0;
|
int maxQuantizerAlpha = 0;
|
||||||
@ -738,7 +748,7 @@ bool QAVIFHandler::write(const QImage &image)
|
|||||||
encoder->maxQuantizerAlpha = maxQuantizerAlpha;
|
encoder->maxQuantizerAlpha = maxQuantizerAlpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
encoder->speed = 7;
|
encoder->speed = 6;
|
||||||
|
|
||||||
res = avifEncoderWrite(encoder, avif, &raw);
|
res = avifEncoderWrite(encoder, avif, &raw);
|
||||||
avifEncoderDestroy(encoder);
|
avifEncoderDestroy(encoder);
|
||||||
@ -891,7 +901,8 @@ bool QAVIFHandler::jumpToImage(int imageNumber)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (imageNumber == m_decoder->imageCount) { // we are here already
|
if (imageNumber == m_decoder->imageIndex) { // we are here already
|
||||||
|
m_must_jump_to_next_image = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,8 +16,9 @@
|
|||||||
#include <QStack>
|
#include <QStack>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QtEndian>
|
#include <QtEndian>
|
||||||
#include <stdlib.h>
|
#include <QColorSpace>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "gimp_p.h"
|
#include "gimp_p.h"
|
||||||
@ -351,6 +352,8 @@ private:
|
|||||||
bool initialized; //!< Is the QImage initialized?
|
bool initialized; //!< Is the QImage initialized?
|
||||||
QImage image; //!< final QImage
|
QImage image; //!< final QImage
|
||||||
|
|
||||||
|
QHash<QString,QByteArray> parasites; //!< parasites data
|
||||||
|
|
||||||
XCFImage(void)
|
XCFImage(void)
|
||||||
: initialized(false)
|
: initialized(false)
|
||||||
{
|
{
|
||||||
@ -405,6 +408,7 @@ private:
|
|||||||
bool composeTiles(XCFImage &xcf_image);
|
bool composeTiles(XCFImage &xcf_image);
|
||||||
void setGrayPalette(QImage &image);
|
void setGrayPalette(QImage &image);
|
||||||
void setPalette(XCFImage &xcf_image, QImage &image);
|
void setPalette(XCFImage &xcf_image, QImage &image);
|
||||||
|
void setImageParasites(const XCFImage &xcf_image, QImage &image);
|
||||||
static void assignImageBytes(Layer &layer, uint i, uint j);
|
static void assignImageBytes(Layer &layer, uint i, uint j);
|
||||||
bool loadHierarchy(QDataStream &xcf_io, Layer &layer);
|
bool loadHierarchy(QDataStream &xcf_io, Layer &layer);
|
||||||
bool loadLevel(QDataStream &xcf_io, Layer &layer, qint32 bpp);
|
bool loadLevel(QDataStream &xcf_io, Layer &layer, qint32 bpp);
|
||||||
@ -665,6 +669,9 @@ bool XCFImageFormat::readXCF(QIODevice *device, QImage *outImage)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The image was created: now I can set metadata and ICC color profile inside it.
|
||||||
|
setImageParasites(xcf_image, xcf_image.image);
|
||||||
|
|
||||||
*outImage = xcf_image.image;
|
*outImage = xcf_image.image;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -715,15 +722,15 @@ bool XCFImageFormat::loadImageProperties(QDataStream &xcf_io, XCFImage &xcf_imag
|
|||||||
property.readBytes(tag, size);
|
property.readBytes(tag, size);
|
||||||
|
|
||||||
quint32 flags;
|
quint32 flags;
|
||||||
char *data = nullptr;
|
QByteArray data;
|
||||||
property >> flags >> data;
|
property >> flags >> data;
|
||||||
|
|
||||||
if (tag && strncmp(tag, "gimp-comment", strlen("gimp-comment")) == 0) {
|
// WARNING: you cannot add metadata to QImage here because it can be null.
|
||||||
xcf_image.image.setText(QStringLiteral("Comment"), QString::fromUtf8(data));
|
// Adding a metadata to a QImage when it is null, does nothing (metas are lost).
|
||||||
}
|
if(tag) // store metadata for future use
|
||||||
|
xcf_image.parasites.insert(QString::fromUtf8(tag), data);
|
||||||
|
|
||||||
delete[] tag;
|
delete[] tag;
|
||||||
delete[] data;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1234,6 +1241,77 @@ void XCFImageFormat::setPalette(XCFImage &xcf_image, QImage &image)
|
|||||||
image.setColorTable(xcf_image.palette);
|
image.setColorTable(xcf_image.palette);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Copy the parasites info to QImage.
|
||||||
|
* \param xcf_image XCF image containing the parasites read from the data stream.
|
||||||
|
* \param image image to apply the parasites data.
|
||||||
|
* \note Some comment taken from https://gitlab.gnome.org/GNOME/gimp/-/blob/master/devel-docs/parasites.txt
|
||||||
|
*/
|
||||||
|
void XCFImageFormat::setImageParasites(const XCFImage &xcf_image, QImage &image)
|
||||||
|
{
|
||||||
|
auto&& p = xcf_image.parasites;
|
||||||
|
auto keys = p.keys();
|
||||||
|
for (auto&& key : qAsConst(keys)) {
|
||||||
|
auto value = p.value(key);
|
||||||
|
if(value.isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// "icc-profile" (IMAGE, PERSISTENT | UNDOABLE)
|
||||||
|
// This contains an ICC profile describing the color space the
|
||||||
|
// image was produced in. TIFF images stored in PhotoShop do
|
||||||
|
// oftentimes contain embedded profiles. An experimental color
|
||||||
|
// manager exists to use this parasite, and it will be used
|
||||||
|
// for interchange between TIFF and PNG (identical profiles)
|
||||||
|
if (key == QStringLiteral("icc-profile")) {
|
||||||
|
auto cs = QColorSpace::fromIccProfile(value);
|
||||||
|
if(cs.isValid())
|
||||||
|
image.setColorSpace(cs);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "gimp-comment" (IMAGE, PERSISTENT)
|
||||||
|
// Standard GIF-style image comments. This parasite should be
|
||||||
|
// human-readable text in UTF-8 encoding. A trailing \0 might
|
||||||
|
// be included and is not part of the comment. Note that image
|
||||||
|
// comments may also be present in the "gimp-metadata" parasite.
|
||||||
|
if (key == QStringLiteral("gimp-comment")) {
|
||||||
|
value.replace('\0', QByteArray());
|
||||||
|
image.setText(QStringLiteral("Comment"), QString::fromUtf8(value));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "gimp-image-metadata"
|
||||||
|
// Saved by GIMP 2.10.30 but it is not mentioned in the specification.
|
||||||
|
// It is an XML block with the properties set using GIMP.
|
||||||
|
if (key == QStringLiteral("gimp-image-metadata")) {
|
||||||
|
// NOTE: I arbitrary defined the metadata "XML:org.gimp.xml" because it seems
|
||||||
|
// a GIMP proprietary XML format (no xmlns defined)
|
||||||
|
value.replace('\0', QByteArray());
|
||||||
|
image.setText(QStringLiteral("XML:org.gimp.xml"), QString::fromUtf8(value));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0 // Unable to generate it using latest GIMP version
|
||||||
|
// "gimp-metadata" (IMAGE, PERSISTENT)
|
||||||
|
// The metadata associated with the image, serialized as one XMP
|
||||||
|
// packet. This metadata includes the contents of any XMP, EXIF
|
||||||
|
// and IPTC blocks from the original image, as well as
|
||||||
|
// user-specified values such as image comment, copyright,
|
||||||
|
// license, etc.
|
||||||
|
if (key == QStringLiteral("gimp-metadata")) {
|
||||||
|
// NOTE: "XML:com.adobe.xmp" is the meta set by Qt reader when an
|
||||||
|
// XMP packet is found (e.g. when reading a PNG saved by Photoshop).
|
||||||
|
// I reused the same key because some programs could search for it.
|
||||||
|
value.replace('\0', QByteArray());
|
||||||
|
image.setText(QStringLiteral("XML:com.adobe.xmp"), QString::fromUtf8(value));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Copy the bytes from the tile buffer into the image tile QImage, taking into
|
* Copy the bytes from the tile buffer into the image tile QImage, taking into
|
||||||
* account all the myriad different modes.
|
* account all the myriad different modes.
|
||||||
|
Reference in New Issue
Block a user