mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2026-10-03 01:42:05 -04:00
266 lines
6.6 KiB
C++
266 lines
6.6 KiB
C++
/*
|
|
This file is part of the KDE project
|
|
SPDX-FileCopyrightText: 2026 Mirco Miranda <[email protected]>
|
|
|
|
SPDX-License-Identifier: LGPL-2.1-or-later
|
|
*/
|
|
#include "photoshop_p.h"
|
|
#include "util_p.h"
|
|
|
|
#include <QLoggingCategory>
|
|
|
|
#ifdef QT_DEBUG
|
|
Q_LOGGING_CATEGORY(LOG_PSDSHARED, "kf.imageformats.plugins.psdshared", QtDebugMsg)
|
|
#else
|
|
Q_LOGGING_CATEGORY(LOG_PSDSHARED, "kf.imageformats.plugins.psdshared", QtWarningMsg)
|
|
#endif
|
|
|
|
QString readPascalString(QDataStream &s, qint32 alignBytes, qint32 *size)
|
|
{
|
|
qint32 tmp = 0;
|
|
if (size == nullptr)
|
|
size = &tmp;
|
|
|
|
quint8 stringSize;
|
|
s >> stringSize;
|
|
*size = sizeof(stringSize);
|
|
|
|
QString str;
|
|
if (stringSize > 0) {
|
|
QByteArray ba;
|
|
ba.resize(stringSize);
|
|
auto read = s.readRawData(ba.data(), ba.size());
|
|
if (read > 0) {
|
|
*size += read;
|
|
str = QString::fromLatin1(ba);
|
|
}
|
|
}
|
|
|
|
// align
|
|
if (alignBytes > 1)
|
|
if (auto pad = *size % alignBytes)
|
|
*size += s.skipRawData(alignBytes - pad);
|
|
|
|
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;
|
|
|
|
bool tmp = true;
|
|
if (ok == nullptr)
|
|
ok = &tmp;
|
|
*ok = true;
|
|
|
|
// Section size
|
|
quint32 tmpSize;
|
|
s >> tmpSize;
|
|
qint64 sectioSize = tmpSize;
|
|
|
|
// Reading Image resource block
|
|
for (auto size = sectioSize; size > 0;) {
|
|
|
|
#define DEC_SIZE(value) \
|
|
if ((size -= qint64(value)) < 0) { \
|
|
*ok = false; \
|
|
break; }
|
|
|
|
// Length Description
|
|
// -------------------------------------------------------------------
|
|
// 4 Signature: '8BIM'
|
|
// 2 Unique identifier for the resource. Image resource IDs
|
|
// contains a list of resource IDs used by Photoshop.
|
|
// Variable Name: Pascal string, padded to make the size even
|
|
// (a null name consists of two bytes of 0)
|
|
// 4 Actual size of resource data that follows
|
|
// Variable The resource data, described in the sections on the
|
|
// individual resource types. It is padded to make the size
|
|
// even.
|
|
|
|
quint32 signature;
|
|
s >> signature;
|
|
DEC_SIZE(sizeof(signature))
|
|
// NOTE: MeSa signature is not documented but found in some old PSD take from Photoshop 7.0 CD.
|
|
if (signature != S_8BIM && signature != S_MeSa) { // 8BIM and MeSa
|
|
qCDebug(LOG_PSDSHARED) << "Invalid Image Resource Block Signature!";
|
|
*ok = false;
|
|
break;
|
|
}
|
|
|
|
// id
|
|
quint16 id;
|
|
s >> id;
|
|
DEC_SIZE(sizeof(id))
|
|
|
|
// getting data
|
|
PSDImageResourceBlock irb;
|
|
|
|
// name
|
|
qint32 bytes = 0;
|
|
irb.name = readPascalString(s, 2, &bytes);
|
|
DEC_SIZE(bytes)
|
|
|
|
// data read
|
|
quint32 dataSize;
|
|
s >> dataSize;
|
|
DEC_SIZE(sizeof(dataSize))
|
|
if (auto dev = s.device()) {
|
|
if (dataSize > size) {
|
|
qCDebug(LOG_PSDSHARED) << "Invalid Image Resource Block Data Size!";
|
|
*ok = false;
|
|
break;
|
|
}
|
|
irb.data = deviceRead(dev, dataSize);
|
|
}
|
|
auto read = irb.data.size();
|
|
if (read > 0) {
|
|
DEC_SIZE(read)
|
|
}
|
|
if (read != qint64(dataSize)) {
|
|
qCDebug(LOG_PSDSHARED) << "Image Resource Block Read Error!";
|
|
*ok = false;
|
|
break;
|
|
}
|
|
|
|
if (auto pad = dataSize % 2) {
|
|
auto skipped = s.skipRawData(pad);
|
|
if (skipped > 0) {
|
|
DEC_SIZE(skipped);
|
|
}
|
|
}
|
|
|
|
// insert IRB
|
|
irs.insert(ImageResourceId(id), irb);
|
|
|
|
#undef DEC_SIZE
|
|
}
|
|
|
|
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;
|
|
}
|