mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2026-09-20 06:37:00 -04:00
EXR: store of "adobe_rsrc" attribute with XMP/EXIF/Resolution data
This commit is contained in:
@@ -384,9 +384,12 @@ 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.
|
||||
- `EXR_DISABLE_XMP_ATTRIBUTE`: disables the storage of XMP values in a
|
||||
non-standard attribute named "xmp". Note that Gimp reads the "xmp" attribute
|
||||
and Darktable writes it as well.
|
||||
- `EXR_DISABLE_ADOBE_ATTRIBUTE`: disables the storage of non-standard
|
||||
attribute "adobe_rsrc". Note that Photoshop uses "adobe_rsrc" attribute to
|
||||
read and write XMP/EXIF/IPTC data.
|
||||
|
||||
The plugin can set the following additional metadata:
|
||||
- `EXRLayerName`: A string containing the name of the EXR layer used to decode
|
||||
|
||||
+121
-24
@@ -21,8 +21,8 @@
|
||||
*/
|
||||
//#define EXR_CONVERT_TO_SRGB // default: commented -> you should define it in your cmake file
|
||||
|
||||
/* *** EXR_STORE_XMP_ATTRIBUTE ***
|
||||
* If defined, disables the stores XMP values in a non-standard attribute named "xmp".
|
||||
/* *** EXR_DISABLE_XMP_ATTRIBUTE ***
|
||||
* If defined, disables the storage of XMP values in the non-standard attribute named "xmp"
|
||||
* The QImage metadata used is "XML:com.adobe.xmp".
|
||||
* NOTE: The use of non-standard attributes is possible but discouraged by the specification. However,
|
||||
* metadata is essential for good image management and programs like darktable also set this
|
||||
@@ -30,6 +30,15 @@
|
||||
*/
|
||||
//#define EXR_DISABLE_XMP_ATTRIBUTE // default: commented -> you should define it in your cmake file
|
||||
|
||||
/* *** EXR_DISABLE_ADOBE_ATTRIBUTE ***
|
||||
* If defined, disables the storage of non-standard attribute "adobe_rsrc".
|
||||
* NOTE: The use of non-standard attributes is possible but discouraged by the specification. However,
|
||||
* metadata is essential for good image management and programs like Photoshop also set this
|
||||
* attribute. Photoshop uses "adobe_rsrc" attribute to read/write XMP/EXIF/IPTC data.
|
||||
*/
|
||||
//#define EXR_DISABLE_ADOBE_ATTRIBUTE // default: commented -> you should define it in your cmake file
|
||||
|
||||
|
||||
/* *** EXR_MAX_IMAGE_WIDTH and EXR_MAX_IMAGE_HEIGHT ***
|
||||
* The maximum size in pixel allowed by the plugin.
|
||||
*/
|
||||
@@ -92,6 +101,12 @@ Q_LOGGING_CATEGORY(LOG_EXRPLUGIN, "kf.imageformats.plugins.exr", QtDebugMsg)
|
||||
Q_LOGGING_CATEGORY(LOG_EXRPLUGIN, "kf.imageformats.plugins.exr", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
#ifndef EXR_DISABLE_ADOBE_ATTRIBUTE
|
||||
#if QT_VERSION_CHECK(OPENEXR_VERSION_MAJOR, OPENEXR_VERSION_MINOR, OPENEXR_VERSION_PATCH) < QT_VERSION_CHECK(3, 3, 0)
|
||||
#define EXR_DISABLE_ADOBE_ATTRIBUTE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class K_IStream : public Imf::IStream
|
||||
{
|
||||
public:
|
||||
@@ -213,6 +228,7 @@ EXRHandler::EXRHandler()
|
||||
, m_imageCount(0)
|
||||
, m_startPos(-1)
|
||||
, m_subType(EXR_SUBFORMAT_RGB)
|
||||
, m_transformation(QImageIOHandler::TransformationNone)
|
||||
{
|
||||
// Set the number of threads to use (0 is allowed)
|
||||
Imf::setGlobalThreadCount(QThread::idealThreadCount() / 2);
|
||||
@@ -292,6 +308,27 @@ static void printAttributes(const Imf::Header &h)
|
||||
}
|
||||
#endif
|
||||
|
||||
static PSDImageResourceSection readPSDImageResourceSection(const Imf::Header &header)
|
||||
{
|
||||
if (auto adobe = header.findTypedAttribute<Imf::OpaqueAttribute>("adobe_rsrc")) {
|
||||
auto &&data = adobe->data();
|
||||
auto ba = QByteArray(data, data.size());
|
||||
ba.prepend((data.size()) & 0xFF);
|
||||
ba.prepend((data.size() >> 8) & 0xFF);
|
||||
ba.prepend((data.size() >> 16) & 0xFF);
|
||||
ba.prepend((data.size() >> 24) & 0xFF);
|
||||
QDataStream ds(ba);
|
||||
ds.setByteOrder(QDataStream::BigEndian);
|
||||
auto ok = false;
|
||||
auto irs = readImageResourceSection(ds, &ok);
|
||||
if (ok) {
|
||||
return irs;
|
||||
}
|
||||
}
|
||||
return{};
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* \brief readMetadata
|
||||
* Reads EXR attributes from the \a header and set its as metadata in the \a image.
|
||||
@@ -389,24 +426,12 @@ static void readMetadata(const Imf::Header &header, QImage &image)
|
||||
}
|
||||
|
||||
// Photoshop image resource section
|
||||
if (auto adobe = header.findTypedAttribute<Imf::OpaqueAttribute>("adobe_rsrc")) {
|
||||
auto &&data = adobe->data();
|
||||
auto ba = QByteArray(data, data.size());
|
||||
ba.prepend((data.size()) & 0xFF);
|
||||
ba.prepend((data.size() >> 8) & 0xFF);
|
||||
ba.prepend((data.size() >> 16) & 0xFF);
|
||||
ba.prepend((data.size() >> 24) & 0xFF);
|
||||
QDataStream ds(ba);
|
||||
ds.setByteOrder(QDataStream::BigEndian);
|
||||
auto ok = false;
|
||||
auto irs = readImageResourceSection(ds, &ok);
|
||||
if (ok) {
|
||||
auto irs = readPSDImageResourceSection(header);
|
||||
if (irs.contains(IRI_EXIFDATA1)) {
|
||||
auto exif = MicroExif::fromByteArray(irs.value(IRI_EXIFDATA1).data);
|
||||
exif.updateImageMetadata(image);
|
||||
exif.updateImageResolution(image);
|
||||
}
|
||||
|
||||
if (irs.contains(IRI_XMPMETADATA)) {
|
||||
auto irb = irs.value(IRI_XMPMETADATA);
|
||||
auto xmp = QString::fromUtf8(irb.data);
|
||||
@@ -414,8 +439,6 @@ static void readMetadata(const Imf::Header &header, QImage &image)
|
||||
image.setText(QStringLiteral(META_KEY_XMP_ADOBE), xmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief readColorSpace
|
||||
@@ -580,8 +603,11 @@ bool makePreview(const QImage &image, Imf::Array2D<Imf::PreviewRgba> &pixels)
|
||||
* \brief setMetadata
|
||||
* Reades the metadata from \a image and set its as attributes in the \a header.
|
||||
*/
|
||||
static void setMetadata(const QImage &image, Imf::Header &header)
|
||||
static void setMetadata(const QImage &image, Imf::Header &header, const QImageIOHandler::Transformation &transformation)
|
||||
{
|
||||
PSDImageResourceSection irs;
|
||||
QString xmpData;
|
||||
|
||||
auto dateTime = QDateTime::currentDateTime();
|
||||
for (auto &&key : image.textKeys()) {
|
||||
auto text = image.text(key);
|
||||
@@ -612,11 +638,9 @@ static void setMetadata(const QImage &image, Imf::Header &header)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef EXR_DISABLE_XMP_ATTRIBUTE // warning: Non-standard attribute!
|
||||
if (!key.compare(QStringLiteral(META_KEY_XMP_ADOBE), Qt::CaseInsensitive)) {
|
||||
header.insert("xmp", Imf::StringAttribute(text.toStdString()));
|
||||
xmpData = text;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!key.compare(QStringLiteral(META_KEY_MANUFACTURER), Qt::CaseInsensitive)) {
|
||||
header.insert("cameraMake", Imf::StringAttribute(text.toStdString()));
|
||||
@@ -676,6 +700,41 @@ static void setMetadata(const QImage &image, Imf::Header &header)
|
||||
header.insert("pixelAspectRatio", Imf::FloatAttribute(float(image.dotsPerMeterY()) / float(image.dotsPerMeterX())));
|
||||
}
|
||||
|
||||
#ifndef EXR_DISABLE_XMP_ATTRIBUTE
|
||||
if (!xmpData.isEmpty()) {
|
||||
header.insert("xmp", Imf::StringAttribute(xmpData.toStdString()));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef EXR_DISABLE_ADOBE_ATTRIBUTE
|
||||
auto resInfo = PSDResolutionInfoBlock::fromImage(image);
|
||||
if (resInfo.isValid()) {
|
||||
PSDImageResourceBlock irb;
|
||||
irb.data = resInfo.toByteArray();
|
||||
if (!irb.data.isEmpty())
|
||||
irs.insert(IRI_RESOLUTIONINFO, irb);
|
||||
}
|
||||
auto exif = MicroExif::fromImage(image);
|
||||
if (!exif.isEmpty()) {
|
||||
exif.setTransformation(transformation);
|
||||
PSDImageResourceBlock irb;
|
||||
irb.data = exif.toByteArray(QDataStream::BigEndian);
|
||||
irs.insert(IRI_EXIFDATA1, irb);
|
||||
}
|
||||
if (!xmpData.isEmpty()) {
|
||||
PSDImageResourceBlock irb;
|
||||
irb.data = xmpData.toUtf8();
|
||||
irs.insert(IRI_XMPMETADATA, irb);
|
||||
}
|
||||
if (!irs.isEmpty()) {
|
||||
bool ok = false;
|
||||
auto ba = irs.toByteArray(&ok);
|
||||
if (ok) {
|
||||
header.insert("adobe_rsrc", Imf::OpaqueAttribute("adobe_rsrc", ba.size(), ba.data()));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// set default chroma (default constructor ITU-R BT.709-3 -> sRGB)
|
||||
// The image is converted to Linear sRGB so, the chroma is the default EXR value.
|
||||
// If a file doesn’t have a chromaticities attribute, display software should assume that the
|
||||
@@ -718,7 +777,7 @@ bool EXRHandler::write(const QImage &image)
|
||||
}
|
||||
|
||||
// set metadata (EXR attributes)
|
||||
setMetadata(image, header);
|
||||
setMetadata(image, header, m_transformation);
|
||||
|
||||
// write the EXR
|
||||
K_OStream ostr(device());
|
||||
@@ -791,6 +850,15 @@ void EXRHandler::setOption(ImageOption option, const QVariant &value)
|
||||
m_subType = EXR_SUBFORMAT_RGB;
|
||||
}
|
||||
}
|
||||
#ifndef EXR_DISABLE_ADOBE_ATTRIBUTE
|
||||
if (option == QImageIOHandler::ImageTransformation) {
|
||||
auto ok = false;
|
||||
auto t = value.toInt(&ok);
|
||||
if (ok) {
|
||||
m_transformation = QImageIOHandler::Transformation(t);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool EXRHandler::supportsOption(ImageOption option) const
|
||||
@@ -815,6 +883,11 @@ bool EXRHandler::supportsOption(ImageOption option) const
|
||||
if (option == QImageIOHandler::SupportedSubTypes) {
|
||||
return true;
|
||||
}
|
||||
#ifndef EXR_DISABLE_ADOBE_ATTRIBUTE
|
||||
if (option == QImageIOHandler::ImageTransformation) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -870,13 +943,37 @@ QVariant EXRHandler::option(ImageOption option) const
|
||||
}
|
||||
|
||||
if (option == QImageIOHandler::SupportedSubTypes) {
|
||||
return QVariant::fromValue(QList<QByteArray>() << EXR_SUBFORMAT_RGB << EXR_SUBFORMAT_YC);
|
||||
v = QVariant::fromValue(QList<QByteArray>() << EXR_SUBFORMAT_RGB << EXR_SUBFORMAT_YC);
|
||||
}
|
||||
|
||||
if (option == QImageIOHandler::SubType) {
|
||||
return QVariant::fromValue(m_subType);
|
||||
v = QVariant::fromValue(m_subType);
|
||||
}
|
||||
|
||||
#ifndef EXR_DISABLE_ADOBE_ATTRIBUTE
|
||||
if (option == QImageIOHandler::ImageTransformation) {
|
||||
if (auto d = device()) {
|
||||
// transactions works on both random and sequential devices
|
||||
d->startTransaction();
|
||||
if (m_startPos > -1) {
|
||||
d->seek(m_startPos);
|
||||
}
|
||||
try {
|
||||
K_IStream istr(d);
|
||||
Imf::RgbaInputFile file(istr);
|
||||
auto irs = readPSDImageResourceSection(file.header());
|
||||
if (irs.contains(IRI_EXIFDATA1)) {
|
||||
auto exif = MicroExif::fromByteArray(irs.value(IRI_EXIFDATA1).data);
|
||||
v = int(exif.transformation());
|
||||
}
|
||||
} catch (const std::exception &) {
|
||||
// broken file or unsupported version
|
||||
}
|
||||
d->rollbackTransaction();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
@@ -126,6 +126,12 @@ private:
|
||||
* - YC: converts RGB data into luminance (Y) and 2x2 subsampled chroma (RY, BY), cutting raw color data by ~50% for significantly smaller file sizes.
|
||||
*/
|
||||
QByteArray m_subType;
|
||||
|
||||
/*!
|
||||
* \brief m_transformation
|
||||
* The Qt image transformation.
|
||||
*/
|
||||
QImageIOHandler::Transformation m_transformation;
|
||||
};
|
||||
|
||||
class EXRPlugin : public QImageIOPlugin
|
||||
|
||||
@@ -84,6 +84,8 @@
|
||||
#define EXIF_TAG_SIZEOF(dataType) (quint16(dataType) & 0x3F)
|
||||
#define EXIF_TAG_DATATYPE(dataType) (quint16(dataType) >> 6)
|
||||
|
||||
#define GPS_GPSVERSION_VALUE QList<quint8>{0x02, 0x04, 0x00, 0x00}
|
||||
|
||||
enum class ExifTagType : quint16 {
|
||||
// Base data types
|
||||
Byte = EXIF_TAG_VALUE(1, 1),
|
||||
@@ -1325,7 +1327,7 @@ QByteArray MicroExif::gpsIfdByteArray(const QDataStream::ByteOrder &byteOrder, c
|
||||
QDataStream ds(&ba, QIODevice::WriteOnly);
|
||||
ds.setByteOrder(byteOrder);
|
||||
auto gpsTags = m_gpsTags;
|
||||
gpsTags.insert(GPS_GPSVERSION, QByteArray("2400"));
|
||||
gpsTags.insert(GPS_GPSVERSION, QVariant::fromValue(GPS_GPSVERSION_VALUE));
|
||||
TagPos positions;
|
||||
if (!writeIfd(ds, version, gpsTags, positions, 0, staticGpsTagTypes))
|
||||
return {};
|
||||
@@ -1723,7 +1725,7 @@ void MicroExif::updateTags(Tags &tiffTags, Tags &exifTags, Tags &gpsTags, const
|
||||
tiffTags.remove(EXIF_GPSIFD);
|
||||
} else {
|
||||
tiffTags.insert(EXIF_GPSIFD, quint32());
|
||||
gpsTags.insert(GPS_GPSVERSION, QByteArray("2400"));
|
||||
gpsTags.insert(GPS_GPSVERSION, QVariant::fromValue(GPS_GPSVERSION_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,23 @@ QString readPascalString(QDataStream &s, qint32 alignBytes, qint32 *size)
|
||||
return str;
|
||||
}
|
||||
|
||||
bool writePascalString(const QString &str, QDataStream &s, qint32 alignBytes)
|
||||
{
|
||||
auto data = str.toLatin1();
|
||||
if(data.size() > 250) {
|
||||
data = data.left(250);
|
||||
}
|
||||
auto sz = data.size();
|
||||
s << quint8(sz);
|
||||
if (sz && s.writeRawData(data.data(), sz) != sz) {
|
||||
return false;
|
||||
}
|
||||
for(alignBytes = std::max(1, alignBytes), sz += 1; sz % alignBytes; ++sz) {
|
||||
s << char();
|
||||
}
|
||||
return s.status() == QDataStream::Ok;
|
||||
}
|
||||
|
||||
PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok)
|
||||
{
|
||||
PSDImageResourceSection irs;
|
||||
@@ -138,3 +155,111 @@ PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok)
|
||||
|
||||
return irs;
|
||||
}
|
||||
|
||||
bool writeImageResourceSection(const PSDImageResourceSection &irs, QDataStream &s)
|
||||
{
|
||||
bool ok = false;
|
||||
auto ba = irs.toByteArray(&ok);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
s << quint32(ba.size());
|
||||
if (s.writeRawData(ba.data(), ba.size()) != ba.size()) {
|
||||
return false;
|
||||
}
|
||||
return (s.status() == QDataStream::Ok);
|
||||
}
|
||||
|
||||
|
||||
QByteArray PSDImageResourceSection::toByteArray(bool *ok) const
|
||||
{
|
||||
QByteArray ba;
|
||||
|
||||
bool tmp = true;
|
||||
if (ok == nullptr)
|
||||
ok = &tmp;
|
||||
*ok = true;
|
||||
|
||||
if (!isEmpty()) {
|
||||
QDataStream s(&ba, QDataStream::WriteOnly);
|
||||
s.setByteOrder(QDataStream::BigEndian);
|
||||
|
||||
auto ids = keys();
|
||||
for(auto &&id : ids) {
|
||||
auto irb = value(id);
|
||||
if (irb.data.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// signature
|
||||
s << quint32(S_8BIM);
|
||||
|
||||
// resource id
|
||||
s << quint16(id);
|
||||
|
||||
// resource name (2 bytes aligned)
|
||||
if (!writePascalString(irb.name, s, 2)) {
|
||||
*ok = false;
|
||||
return{};
|
||||
}
|
||||
|
||||
// data (2 bytes aligned)
|
||||
auto sz = irb.data.size();
|
||||
s << quint32(sz);
|
||||
if (s.writeRawData(irb.data.data(), sz) != sz) {
|
||||
*ok = false;
|
||||
return{};
|
||||
}
|
||||
if (sz % 2) {
|
||||
s << char();
|
||||
}
|
||||
|
||||
if (s.status() != QDataStream::Ok) {
|
||||
*ok = false;
|
||||
return{};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Q_ASSERT(ba.size() % 2 == 0);
|
||||
return ba;
|
||||
}
|
||||
|
||||
PSDResolutionInfoBlock::PSDResolutionInfoBlock(qint32 ppmX, qint32 ppmY)
|
||||
: m_ppmX(ppmX)
|
||||
, m_ppmY(ppmY)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool PSDResolutionInfoBlock::isValid() const
|
||||
{
|
||||
return m_ppmX > 0 && m_ppmY > 0;
|
||||
}
|
||||
|
||||
PSDResolutionInfoBlock PSDResolutionInfoBlock::fromImage(const QImage &image)
|
||||
{
|
||||
return PSDResolutionInfoBlock(image.dotsPerMeterX(), image.dotsPerMeterY());
|
||||
}
|
||||
|
||||
QByteArray PSDResolutionInfoBlock::toByteArray() const
|
||||
{
|
||||
QByteArray ba;
|
||||
QDataStream ds(&ba, QIODevice::WriteOnly);
|
||||
ds.setByteOrder(QDataStream::BigEndian);
|
||||
|
||||
auto hres = qRoundOrZero(dppm2dpi(m_ppmX) * 65536);
|
||||
ds << hres;
|
||||
ds << quint16(1); // dpi
|
||||
ds << quint16(2); // cm (display)
|
||||
auto vres = qRoundOrZero(dppm2dpi(m_ppmY) * 65536);
|
||||
ds << vres;
|
||||
ds << quint16(1);
|
||||
ds << quint16(2);
|
||||
|
||||
if (hres == 0 || vres == 0) {
|
||||
return{};
|
||||
}
|
||||
|
||||
return ba;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QHash>
|
||||
#include <QImage>
|
||||
#include <QString>
|
||||
|
||||
enum Signature : quint32 {
|
||||
@@ -55,7 +56,50 @@ struct PSDImageResourceBlock {
|
||||
QByteArray data;
|
||||
};
|
||||
|
||||
using PSDImageResourceSection = QHash<ImageResourceId, PSDImageResourceBlock>;
|
||||
class PSDImageResourceSection : public QHash<ImageResourceId, PSDImageResourceBlock>
|
||||
{
|
||||
public:
|
||||
PSDImageResourceSection() : QHash<ImageResourceId, PSDImageResourceBlock>() {}
|
||||
PSDImageResourceSection(const PSDImageResourceSection& other) = default;
|
||||
PSDImageResourceSection& operator =(const PSDImageResourceSection& other) = default;
|
||||
|
||||
/*!
|
||||
* \brief toByteArray
|
||||
* \param ok Pointer to the operation result variable.
|
||||
* \return The binary IRB to be written into the PSD file.
|
||||
*/
|
||||
QByteArray toByteArray(bool *ok = nullptr) const;
|
||||
};
|
||||
|
||||
class PSDResolutionInfoBlock
|
||||
{
|
||||
public:
|
||||
PSDResolutionInfoBlock(qint32 ppmX, qint32 ppmY);
|
||||
PSDResolutionInfoBlock(const PSDResolutionInfoBlock& other) = default;
|
||||
PSDResolutionInfoBlock& operator =(const PSDResolutionInfoBlock& other) = default;
|
||||
|
||||
/*!
|
||||
* \brief isValid
|
||||
* \return true if both m_ppmX and m_ppmY are grater than 0. Otherwise false.
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
/*!
|
||||
* \brief fromImage
|
||||
* Initialize the class using the image resolution.
|
||||
*/
|
||||
static PSDResolutionInfoBlock fromImage(const QImage& image);
|
||||
|
||||
/*!
|
||||
* \brief toByteArray
|
||||
* \return The binary data ready for the IMage Resource Section.
|
||||
*/
|
||||
QByteArray toByteArray() const;
|
||||
|
||||
private:
|
||||
qint32 m_ppmX;
|
||||
qint32 m_ppmY;
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
@@ -68,6 +112,16 @@ using PSDImageResourceSection = QHash<ImageResourceId, PSDImageResourceBlock>;
|
||||
*/
|
||||
QString readPascalString(QDataStream &s, qint32 alignBytes = 1, qint32 *size = nullptr);
|
||||
|
||||
/*!
|
||||
* \brief writePascalString
|
||||
* Writes the Pascal string as defined in the PSD specification.
|
||||
* \param str The string to be written.
|
||||
* \param s The stream.
|
||||
* \param alignBytes Alignment of the string.
|
||||
* \return True on success, otherwise false.
|
||||
*/
|
||||
bool writePascalString(const QString& str, QDataStream &s, qint32 alignBytes = 1);
|
||||
|
||||
/*!
|
||||
* \brief readImageResourceSection
|
||||
* Reads the image resource section.
|
||||
@@ -77,4 +131,13 @@ QString readPascalString(QDataStream &s, qint32 alignBytes = 1, qint32 *size = n
|
||||
*/
|
||||
PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok = nullptr);
|
||||
|
||||
/*!
|
||||
* \brief writeImageResourceSection
|
||||
* Writes the image resource section.
|
||||
* \param irs The image resource section raw data.
|
||||
* \param s The stream.
|
||||
* \return True on success, otherwise false.
|
||||
*/
|
||||
bool writeImageResourceSection(const PSDImageResourceSection& irs, QDataStream &s);
|
||||
|
||||
#endif // PHOTOSHOP_P_H
|
||||
|
||||
Reference in New Issue
Block a user