Compare commits

...

5 Commits

7 changed files with 77 additions and 83 deletions

View File

@ -5,7 +5,7 @@ project(KImageFormats)
set (CMAKE_CXX_STANDARD 14)
include(FeatureSummary)
find_package(ECM 5.76.0 NO_MODULE)
find_package(ECM 5.77.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)
@ -19,7 +19,7 @@ include(KDECMakeSettings)
include(CheckIncludeFiles)
set(REQUIRED_QT_VERSION 5.12.0)
set(REQUIRED_QT_VERSION 5.13.0)
find_package(Qt5Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
find_package(KF5Archive)
@ -50,7 +50,7 @@ set_package_properties(OpenEXR PROPERTIES
add_definitions(-DQT_NO_FOREACH)
# 050d00 (5.13) triggers a BIC in qimageiohandler.h, in Qt 5.13, so do not enable that until we can require 5.14
# https://codereview.qt-project.org/c/qt/qtbase/+/279215
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050c00)
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050d00)
add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x054B00)
add_subdirectory(src)
if (BUILD_TESTING)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 628 B

After

Width:  |  Height:  |  Size: 983 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 534 B

After

Width:  |  Height:  |  Size: 827 B

View File

@ -36,14 +36,17 @@ static void writeImageData(const char *name, const QString &filename, const QIma
}
}
// allow each byte to be different by up to 1, to allow for rounding errors
template<class Trait>
static bool fuzzyeq(const QImage &im1, const QImage &im2, uchar fuzziness)
{
Q_ASSERT(im1.format() == im2.format());
Q_ASSERT(im1.depth() == 24 || im1.depth() == 32 || im1.depth() == 64);
const int height = im1.height();
const int width = im1.width();
for (int i = 0; i < height; ++i) {
const uchar *line1 = im1.scanLine(i);
const uchar *line2 = im2.scanLine(i);
const Trait *line1 = reinterpret_cast<const Trait*>(im1.scanLine(i));
const Trait *line2 = reinterpret_cast<const Trait*>(im2.scanLine(i));
for (int j = 0; j < width; ++j) {
if (line1[j] > line2[j]) {
if (line1[j] - line2[j] > fuzziness)
@ -57,6 +60,30 @@ static bool fuzzyeq(const QImage &im1, const QImage &im2, uchar fuzziness)
return true;
}
// allow each byte to be different by up to 1, to allow for rounding errors
static bool fuzzyeq(const QImage &im1, const QImage &im2, uchar fuzziness)
{
return (im1.depth() == 64) ? fuzzyeq<quint16>(im1, im2, fuzziness)
: fuzzyeq<quint8>(im1, im2, fuzziness);
}
// Returns the original format if we support, or returns
// format which we preferred to use for `fuzzyeq()`.
// We do only support formats with 8-bits/16-bits pre pixel.
// If that changed, don't forget to update `fuzzyeq()` too
static QImage::Format preferredFormat(QImage::Format fmt)
{
switch (fmt) {
case QImage::Format_RGB32:
case QImage::Format_ARGB32:
case QImage::Format_RGBX64:
case QImage::Format_RGBA64:
return fmt;
default:
return QImage::Format_ARGB32;
}
}
int main(int argc, char ** argv)
{
QCoreApplication app(argc, argv);
@ -168,19 +195,23 @@ int main(int argc, char ** argv)
<< expImage.height() << "\n";
++failed;
} else {
if (inputImage.format() != QImage::Format_ARGB32) {
QImage::Format inputFormat = preferredFormat(inputImage.format());
QImage::Format expFormat = preferredFormat(expImage.format());
QImage::Format cmpFormat = inputFormat == expFormat ? inputFormat : QImage::Format_ARGB32;
if (inputImage.format() != cmpFormat) {
QTextStream(stdout) << "INFO : " << fi.fileName()
<< ": converting " << fi.fileName()
<< " from " << formatToString(inputImage.format())
<< " to ARGB32\n";
inputImage = inputImage.convertToFormat(QImage::Format_ARGB32);
<< " to " << formatToString(cmpFormat) << '\n';
inputImage = inputImage.convertToFormat(cmpFormat);
}
if (expImage.format() != QImage::Format_ARGB32) {
if (expImage.format() != cmpFormat) {
QTextStream(stdout) << "INFO : " << fi.fileName()
<< ": converting " << expfilename
<< " from " << formatToString(expImage.format())
<< " to ARGB32\n";
expImage = expImage.convertToFormat(QImage::Format_ARGB32);
<< " to " << formatToString(cmpFormat) << '\n';
expImage = expImage.convertToFormat(cmpFormat);
}
if (fuzzyeq(inputImage, expImage, fuzziness)) {
QTextStream(stdout) << "PASS : " << fi.fileName() << "\n";

View File

@ -104,14 +104,9 @@ static void skip_section(QDataStream &s)
s.skipRawData(section_length);
}
static quint8 readPixel_u16(QDataStream &stream) {
quint16 pixel;
stream >> pixel;
return pixel / (1 << 8);
}
static int readPixel_u8(QDataStream &stream) {
quint8 pixel;
template <class Trait>
static Trait readPixel(QDataStream &stream) {
Trait pixel;
stream >> pixel;
return pixel;
}
@ -156,23 +151,18 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
quint32 channel_num = header.channel_count;
QImage::Format fmt = QImage::Format_RGB32;
QImage::Format fmt = header.depth == 8 ? QImage::Format_RGB32
: QImage::Format_RGBX64;
// Clear the image.
if (channel_num >= 4) {
// Enable alpha.
fmt = QImage::Format_ARGB32;
fmt = header.depth == 8 ? QImage::Format_ARGB32
: QImage::Format_RGBA64;
// Ignore the other channels.
channel_num = 4;
}
if (compression == 1 && header.depth == 16) {
fmt = QImage::Format_RGBX64;
if (channel_num >= 4) {
fmt = QImage::Format_RGBA64;
}
}
img = QImage(header.width, header.height, fmt);
if (img.isNull()) {
qWarning() << "Failed to allocate image, invalid dimensions?" << QSize(header.width, header.height);
@ -181,9 +171,10 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
img.fill(qRgb(0,0,0));
const quint32 pixel_count = header.height * header.width;
const quint32 channel_size = pixel_count * header.depth / 8;
// Verify this, as this is used to write into the memory of the QImage
if (pixel_count > img.sizeInBytes() / sizeof(QRgb)) {
if (pixel_count > img.sizeInBytes() / (header.depth == 8 ? sizeof(QRgb) : sizeof(QRgba64))) {
qWarning() << "Invalid pixel count!" << pixel_count << "bytes available:" << img.sizeInBytes();
return false;
}
@ -220,13 +211,13 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
bool success = false;
if (header.depth == 8) {
success = decodeRLEData(RLEVariant::PackBits, stream,
image_data, pixel_count,
&readPixel_u8, updaters[channel]);
image_data, channel_size,
&readPixel<quint8>, updaters[channel]);
} else if (header.depth == 16) {
QRgba64 *image_data = reinterpret_cast<QRgba64*>(img.bits());
success = decodeRLEData(RLEVariant::PackBits16, stream,
image_data, pixel_count * 2,
&readPixel_u8, updaters64[channel]);
image_data, channel_size,
&readPixel<quint8>, updaters64[channel]);
}
if (!success) {
@ -236,11 +227,15 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
}
} else {
for (unsigned short channel = 0; channel < channel_num; channel++) {
for (unsigned i = 0; i < pixel_count; ++i) {
image_data[i] = updaters[channel](
image_data[i],
header.depth == 8 ? readPixel_u8(stream)
: readPixel_u16(stream));
if (header.depth == 8) {
for (unsigned i = 0; i < pixel_count; ++i) {
image_data[i] = updaters[channel](image_data[i], readPixel<quint8>(stream));
}
} else if (header.depth == 16) {
QRgba64 *image_data = reinterpret_cast<QRgba64*>(img.bits());
for (unsigned i = 0; i < pixel_count; ++i) {
image_data[i] = updaters64[channel](image_data[i], readPixel<quint16>(stream));
}
}
// make sure we didn't try to read past the end of the stream
if (stream.status() != QDataStream::Ok) {

View File

@ -7,56 +7,24 @@
#ifndef FORMAT_ENUM_H
#define FORMAT_ENUM_H
#include <QMetaEnum>
#include <QImage>
// Generated from QImage::Format enum
static const char * qimage_format_enum_names[] = {
"Invalid",
"Mono",
"MonoLSB",
"Indexed8",
"RGB32",
"ARGB32",
"ARGB32_Premultiplied",
"RGB16",
"ARGB8565_Premultiplied",
"RGB666",
"ARGB6666_Premultiplied",
"RGB555",
"ARGB8555_Premultiplied",
"RGB888",
"RGB444",
"ARGB4444_Premultiplied",
"RGBX8888",
"RGBA8888",
"RGBA8888_Premultiplied"
};
// Never claim there are more than QImage::NImageFormats supported formats.
// This is future-proofing against the above list being extended.
static const int qimage_format_enum_names_count =
(sizeof(qimage_format_enum_names) / sizeof(*qimage_format_enum_names) > int(QImage::NImageFormats))
? int(QImage::NImageFormats)
: (sizeof(qimage_format_enum_names) / sizeof(*qimage_format_enum_names));
QImage::Format formatFromString(const QString &str)
{
for (int i = 0; i < qimage_format_enum_names_count; ++i) {
if (str.compare(QLatin1String(qimage_format_enum_names[i]), Qt::CaseInsensitive) == 0) {
return (QImage::Format)(i);
}
}
return QImage::Format_Invalid;
const QMetaEnum metaEnum = QMetaEnum::fromType<QImage::Format>();
const QString enumString = QStringLiteral("Format_") + str;
bool ok;
const int res = metaEnum.keyToValue(enumString.toLatin1().constData(), &ok);
return ok ? static_cast<QImage::Format>(res) : QImage::Format_Invalid;
}
QString formatToString(QImage::Format format)
{
int index = int(format);
if (index > 0 && index < qimage_format_enum_names_count) {
return QLatin1String(qimage_format_enum_names[index]);
}
return QLatin1String("<unknown:") +
QString::number(index) +
QLatin1String(">");
const QMetaEnum metaEnum = QMetaEnum::fromType<QImage::Format>();
return QString::fromLatin1(metaEnum.valueToKey(format)).remove(QStringLiteral("Format_"));
}
#endif

View File

@ -67,8 +67,8 @@ int main(int argc, char **argv)
QTextStream out(stdout);
out << "QImage formats:\n";
// skip QImage::Format_Invalid
for (int i = 1; i < qimage_format_enum_names_count; ++i) {
out << " " << qimage_format_enum_names[i] << '\n';
for (int i = 1; i < QImage::NImageFormats; ++i) {
out << " " << formatToString(static_cast<QImage::Format>(i)) << '\n';
}
return 0;
}