mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-07-16 03:24:17 -04:00
Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
105d0fab46 | |||
497b6b81bd | |||
c60e77c048 | |||
f089e860e0 | |||
551e7d44a8 | |||
232075f92e | |||
1d12b345f9 | |||
becd7aff3a | |||
52fbe1863b | |||
203f459536 | |||
693bb34b69 | |||
5c66570b9e | |||
cba2328ae6 | |||
961465a311 | |||
b7cd5cddc1 | |||
ce56b97ee7 | |||
500ce13265 | |||
f03739f222 | |||
69effbf9f5 | |||
f06cf300f1 | |||
2a4d230717 | |||
bdf23e45d5 | |||
e9b52df1d3 | |||
dd98b2b717 | |||
dd76f41c38 | |||
940ca2b081 | |||
b14ef357e5 | |||
bf5502403e | |||
5c4c05257c | |||
7afaacb093 | |||
68bb1a0ee7 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -19,3 +19,4 @@ random_seed
|
||||
/build*/
|
||||
CMakeLists.txt.user*
|
||||
*.unc-backup*
|
||||
.cmake/
|
||||
|
@ -2,9 +2,11 @@ cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(KImageFormats)
|
||||
|
||||
set (CMAKE_CXX_STANDARD 14)
|
||||
|
||||
include(FeatureSummary)
|
||||
find_package(ECM 5.62.0 NO_MODULE)
|
||||
set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules")
|
||||
find_package(ECM 5.70.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)
|
||||
|
||||
|
||||
@ -17,7 +19,7 @@ include(KDECMakeSettings)
|
||||
|
||||
include(CheckIncludeFiles)
|
||||
|
||||
set(REQUIRED_QT_VERSION 5.11.0)
|
||||
set(REQUIRED_QT_VERSION 5.12.0)
|
||||
find_package(Qt5Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE)
|
||||
|
||||
find_package(KF5Archive)
|
||||
@ -46,6 +48,10 @@ set_package_properties(OpenEXR PROPERTIES
|
||||
PURPOSE "Required for the QImage plugin for OpenEXR images"
|
||||
)
|
||||
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(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x054400)
|
||||
add_subdirectory(src)
|
||||
if (BUILD_TESTING)
|
||||
add_subdirectory(autotests)
|
||||
|
@ -55,6 +55,7 @@ endmacro()
|
||||
# Loads each <format> image in read/<format>/, and compares the
|
||||
# result against the data read from the corresponding png file
|
||||
kimageformats_read_tests(
|
||||
hdr
|
||||
pcx
|
||||
psd
|
||||
ras
|
||||
|
BIN
autotests/read/hdr/rgb.hdr
Normal file
BIN
autotests/read/hdr/rgb.hdr
Normal file
Binary file not shown.
BIN
autotests/read/hdr/rgb.png
Normal file
BIN
autotests/read/hdr/rgb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
autotests/read/xcf/bug411327.png
Normal file
BIN
autotests/read/xcf/bug411327.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
BIN
autotests/read/xcf/bug411327.xcf
Normal file
BIN
autotests/read/xcf/bug411327.xcf
Normal file
Binary file not shown.
BIN
autotests/read/xcf/fruktpilot.png
Normal file
BIN
autotests/read/xcf/fruktpilot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 114 KiB |
BIN
autotests/read/xcf/fruktpilot.xcf
Normal file
BIN
autotests/read/xcf/fruktpilot.xcf
Normal file
Binary file not shown.
@ -127,6 +127,12 @@ int main(int argc, char ** argv)
|
||||
<< "Starting basic read tests for "
|
||||
<< suffix << " images *********\n";
|
||||
|
||||
const QList<QByteArray> formats = QImageReader::supportedImageFormats();
|
||||
QStringList formatStrings;
|
||||
formatStrings.reserve(formats.size());
|
||||
std::transform(formats.begin(), formats.end(), std::back_inserter(formatStrings), [](const QByteArray &format) { return QString(format); });
|
||||
QTextStream(stdout) << "QImageReader::supportedImageFormats: " << formatStrings.join(", ") << "\n";
|
||||
|
||||
const QFileInfoList lstImgDir = imgdir.entryInfoList();
|
||||
for (const QFileInfo &fi : lstImgDir) {
|
||||
int suffixPos = fi.filePath().count() - suffix.count();
|
||||
@ -134,7 +140,7 @@ int main(int argc, char ** argv)
|
||||
QString expfile = fi.filePath().replace(suffixPos, suffix.count(), QStringLiteral("png"));
|
||||
QString expfilename = QFileInfo(expfile).fileName();
|
||||
|
||||
QImageReader inputReader(inputfile, format.constData());
|
||||
QImageReader inputReader(inputfile, format);
|
||||
QImageReader expReader(expfile, "png");
|
||||
|
||||
QImage inputImage;
|
||||
|
@ -53,6 +53,11 @@ endif()
|
||||
|
||||
##################################
|
||||
|
||||
kimageformats_add_plugin(kimg_hdr JSON "hdr.json" SOURCES hdr.cpp)
|
||||
install(FILES hdr.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}/qimageioplugins/)
|
||||
|
||||
##################################
|
||||
|
||||
kimageformats_add_plugin(kimg_pcx JSON "pcx.json" SOURCES pcx.cpp)
|
||||
install(FILES pcx.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}/qimageioplugins/)
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include <QCoreApplication>
|
||||
|
||||
// logging category for this framework, default: log stuff >= warning
|
||||
Q_LOGGING_CATEGORY(EPSPLUGIN, "epsplugin", QtWarningMsg)
|
||||
Q_LOGGING_CATEGORY(EPSPLUGIN, "kf5.kimageformats.epsplugin", QtWarningMsg)
|
||||
|
||||
//#define EPS_PERFORMANCE_DEBUG 1
|
||||
|
||||
|
@ -56,16 +56,6 @@ typedef enum {
|
||||
INDEXED
|
||||
} GimpImageBaseType;
|
||||
|
||||
//! Type of individual layers in an XCF file.
|
||||
|
||||
typedef enum {
|
||||
RGB_GIMAGE,
|
||||
RGBA_GIMAGE,
|
||||
GRAY_GIMAGE,
|
||||
GRAYA_GIMAGE,
|
||||
INDEXED_GIMAGE,
|
||||
INDEXEDA_GIMAGE
|
||||
} GimpImageType;
|
||||
|
||||
// From GIMP "libgimp/gimpenums.h" v2.4
|
||||
|
||||
@ -96,49 +86,6 @@ typedef enum {
|
||||
GRAIN_MERGE_MODE
|
||||
} LayerModeEffects;
|
||||
|
||||
// From GIMP "xcf.c" v1.2
|
||||
|
||||
//! Properties which can be stored in an XCF file.
|
||||
|
||||
typedef enum {
|
||||
PROP_END = 0,
|
||||
PROP_COLORMAP = 1,
|
||||
PROP_ACTIVE_LAYER = 2,
|
||||
PROP_ACTIVE_CHANNEL = 3,
|
||||
PROP_SELECTION = 4,
|
||||
PROP_FLOATING_SELECTION = 5,
|
||||
PROP_OPACITY = 6,
|
||||
PROP_MODE = 7,
|
||||
PROP_VISIBLE = 8,
|
||||
PROP_LINKED = 9,
|
||||
PROP_PRESERVE_TRANSPARENCY = 10,
|
||||
PROP_APPLY_MASK = 11,
|
||||
PROP_EDIT_MASK = 12,
|
||||
PROP_SHOW_MASK = 13,
|
||||
PROP_SHOW_MASKED = 14,
|
||||
PROP_OFFSETS = 15,
|
||||
PROP_COLOR = 16,
|
||||
PROP_COMPRESSION = 17,
|
||||
PROP_GUIDES = 18,
|
||||
PROP_RESOLUTION = 19,
|
||||
PROP_TATTOO = 20,
|
||||
PROP_PARASITES = 21,
|
||||
PROP_UNIT = 22,
|
||||
PROP_PATHS = 23,
|
||||
PROP_USER_UNIT = 24,
|
||||
MAX_SUPPORTED_PROPTYPE // should always be at the end so its value is last + 1
|
||||
} PropType;
|
||||
|
||||
// From GIMP "xcf.c" v1.2
|
||||
|
||||
//! Compression type used in layer tiles.
|
||||
|
||||
typedef enum {
|
||||
COMPRESS_NONE = 0,
|
||||
COMPRESS_RLE = 1,
|
||||
COMPRESS_ZLIB = 2,
|
||||
COMPRESS_FRACTAL = 3 /* Unused. */
|
||||
} CompressionType;
|
||||
|
||||
// From GIMP "paint_funcs.c" v1.2
|
||||
|
||||
|
@ -12,10 +12,14 @@
|
||||
|
||||
#include <QImage>
|
||||
#include <QDataStream>
|
||||
#include <QLoggingCategory>
|
||||
#include <QRegularExpressionMatch>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
typedef Q_UINT8 uchar;
|
||||
typedef unsigned char uchar;
|
||||
|
||||
Q_LOGGING_CATEGORY(HDRPLUGIN, "kf5.kimageformats.hdrplugin", QtWarningMsg)
|
||||
|
||||
namespace // Private.
|
||||
{
|
||||
@ -93,19 +97,23 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
||||
uchar val, code;
|
||||
|
||||
// Create dst image.
|
||||
if (!img.create(width, height, 32)) {
|
||||
img = QImage(width, height, QImage::Format_RGB32);
|
||||
if (img.isNull()) {
|
||||
qCDebug(HDRPLUGIN) << "Couldn't create image with size" << width << height << "and format RGB32";
|
||||
return false;
|
||||
}
|
||||
|
||||
QMemArray<uchar> image(width * 4);
|
||||
QByteArray lineArray;
|
||||
lineArray.resize(4 * width);
|
||||
uchar *image = (uchar *) lineArray.data();
|
||||
|
||||
for (int cline = 0; cline < height; cline++) {
|
||||
QRgb *scanline = (QRgb *) img.scanLine(cline);
|
||||
|
||||
// determine scanline type
|
||||
if ((width < MINELEN) || (MAXELEN < width)) {
|
||||
Read_Old_Line(image.data(), width, s);
|
||||
RGBE_To_QRgbLine(image.data(), scanline, width);
|
||||
Read_Old_Line(image, width, s);
|
||||
RGBE_To_QRgbLine(image, scanline, width);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -116,9 +124,9 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
||||
}
|
||||
|
||||
if (val != 2) {
|
||||
s.device()->at(s.device()->at() - 1);
|
||||
Read_Old_Line(image.data(), width, s);
|
||||
RGBE_To_QRgbLine(image.data(), scanline, width);
|
||||
s.device()->ungetChar(val);
|
||||
Read_Old_Line(image, width, s);
|
||||
RGBE_To_QRgbLine(image, scanline, width);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -132,12 +140,13 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
||||
|
||||
if ((image[1] != 2) || (image[2] & 128)) {
|
||||
image[0] = 2;
|
||||
Read_Old_Line(image.data() + 4, width - 1, s);
|
||||
RGBE_To_QRgbLine(image.data(), scanline, width);
|
||||
Read_Old_Line(image + 4, width - 1, s);
|
||||
RGBE_To_QRgbLine(image, scanline, width);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((image[2] << 8 | image[3]) != width) {
|
||||
qCDebug(HDRPLUGIN) << "Line of pixels had width" << (image[2] << 8 | image[3]) << "instead of" << width;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -146,6 +155,7 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
||||
for (int j = 0; j < width;) {
|
||||
s >> code;
|
||||
if (s.atEnd()) {
|
||||
qCDebug(HDRPLUGIN) << "Truncated HDR file";
|
||||
return false;
|
||||
}
|
||||
if (code > 128) {
|
||||
@ -168,7 +178,7 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
||||
}
|
||||
}
|
||||
|
||||
RGBE_To_QRgbLine(image.data(), scanline, width);
|
||||
RGBE_To_QRgbLine(image, scanline, width);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -176,63 +186,110 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
||||
|
||||
} // namespace
|
||||
|
||||
Q_DECL_EXPORT void kimgio_hdr_read(QImageIO *io)
|
||||
bool HDRHandler::read(QImage *outImage)
|
||||
{
|
||||
int len;
|
||||
char line[MAXLINE];
|
||||
//bool validHeader = false;
|
||||
bool validFormat = false;
|
||||
QByteArray line(MAXLINE + 1, Qt::Uninitialized);
|
||||
QByteArray format;
|
||||
|
||||
// Parse header
|
||||
do {
|
||||
len = io->ioDevice()->readLine(line, MAXLINE);
|
||||
len = device()->readLine(line.data(), MAXLINE);
|
||||
|
||||
/*if (strcmp(line, "#?RADIANCE\n") == 0 || strcmp(line, "#?RGBE\n") == 0)
|
||||
{
|
||||
validHeader = true;
|
||||
}*/
|
||||
if (strcmp(line, "FORMAT=32-bit_rle_rgbe\n") == 0) {
|
||||
validFormat = true;
|
||||
if (line.startsWith("FORMAT=")) {
|
||||
format = line.mid(7, len - 7 - 1 /*\n*/);
|
||||
}
|
||||
|
||||
} while ((len > 0) && (line[0] != '\n'));
|
||||
|
||||
if (/*!validHeader ||*/ !validFormat) {
|
||||
// qDebug() << "Unknown HDR format.";
|
||||
io->setImage(0);
|
||||
io->setStatus(-1);
|
||||
return;
|
||||
if (format != "32-bit_rle_rgbe") {
|
||||
qCDebug(HDRPLUGIN) << "Unknown HDR format:" << format;
|
||||
return false;
|
||||
}
|
||||
|
||||
io->ioDevice()->readLine(line, MAXLINE);
|
||||
len = device()->readLine(line.data(), MAXLINE);
|
||||
line.resize(len);
|
||||
|
||||
char s1[3], s2[3];
|
||||
int width, height;
|
||||
if (sscanf(line, "%2[+-XY] %d %2[+-XY] %d\n", s1, &height, s2, &width) != 4)
|
||||
//if( sscanf(line, "-Y %d +X %d", &height, &width) < 2 )
|
||||
{
|
||||
// qDebug() << "Invalid HDR file.";
|
||||
io->setImage(0);
|
||||
io->setStatus(-1);
|
||||
return;
|
||||
/*
|
||||
TODO: handle flipping and rotation, as per the spec below
|
||||
The single resolution line consists of 4 values, a X and Y label each followed by a numerical
|
||||
integer value. The X and Y are immediately preceded by a sign which can be used to indicate
|
||||
flipping, the order of the X and Y indicate rotation. The standard coordinate system for
|
||||
Radiance images would have the following resolution string -Y N +X N. This indicates that the
|
||||
vertical axis runs down the file and the X axis is to the right (imagining the image as a
|
||||
rectangular block of data). A -X would indicate a horizontal flip of the image. A +Y would
|
||||
indicate a vertical flip. If the X value appears before the Y value then that indicates that
|
||||
the image is stored in column order rather than row order, that is, it is rotated by 90 degrees.
|
||||
The reader can convince themselves that the 8 combinations cover all the possible image orientations
|
||||
and rotations.
|
||||
*/
|
||||
QRegularExpression resolutionRegExp(QStringLiteral("([+\\-][XY]) ([0-9]+) ([+\\-][XY]) ([0-9]+)\n"));
|
||||
QRegularExpressionMatch match = resolutionRegExp.match(QString::fromLatin1(line));
|
||||
if (!match.hasMatch()) {
|
||||
qCDebug(HDRPLUGIN) << "Invalid HDR file, the first line after the header didn't have the expected format:" << line;
|
||||
return false;
|
||||
}
|
||||
const int width = match.captured(2).toInt();
|
||||
const int height = match.captured(4).toInt();
|
||||
|
||||
QDataStream s(io->ioDevice());
|
||||
QDataStream s(device());
|
||||
|
||||
QImage img;
|
||||
if (!LoadHDR(s, width, height, img)) {
|
||||
// qDebug() << "Error loading HDR file.";
|
||||
io->setImage(0);
|
||||
io->setStatus(-1);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
io->setImage(img);
|
||||
io->setStatus(0);
|
||||
*outImage = img;
|
||||
return true;
|
||||
}
|
||||
|
||||
Q_DECL_EXPORT void kimgio_hdr_write(QImageIO *)
|
||||
HDRHandler::HDRHandler()
|
||||
{
|
||||
// intentionally not implemented (since writing low dynamic range data to a HDR file is nonsense.)
|
||||
}
|
||||
|
||||
bool HDRHandler::canRead() const
|
||||
{
|
||||
if (canRead(device())) {
|
||||
setFormat("hdr");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HDRHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qWarning("HDRHandler::canRead() called with no device");
|
||||
return false;
|
||||
}
|
||||
|
||||
return device->peek(11) == "#?RADIANCE\n" || device->peek(7) == "#?RGBE\n";
|
||||
}
|
||||
|
||||
QImageIOPlugin::Capabilities HDRPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
if (format == "hdr") {
|
||||
return Capabilities(CanRead);
|
||||
}
|
||||
if (!format.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
if (!device->isOpen()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Capabilities cap;
|
||||
if (device->isReadable() && HDRHandler::canRead(device)) {
|
||||
cap |= CanRead;
|
||||
}
|
||||
return cap;
|
||||
}
|
||||
|
||||
QImageIOHandler *HDRPlugin::create(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
QImageIOHandler *handler = new HDRHandler;
|
||||
handler->setDevice(device);
|
||||
handler->setFormat(format);
|
||||
return handler;
|
||||
}
|
||||
|
4
src/imageformats/hdr.json
Normal file
4
src/imageformats/hdr.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"Keys": [ "hdr" ],
|
||||
"MimeTypes": [ "image/x-hdr", "image/vnd.radiance" ]
|
||||
}
|
@ -10,12 +10,27 @@
|
||||
#ifndef KIMG_HDR_P_H
|
||||
#define KIMG_HDR_P_H
|
||||
|
||||
class QImageIO;
|
||||
#include <QImageIOPlugin>
|
||||
|
||||
extern "C" {
|
||||
void kimgio_hdr_read(QImageIO *);
|
||||
void kimgio_hdr_write(QImageIO *);
|
||||
}
|
||||
class HDRHandler : public QImageIOHandler
|
||||
{
|
||||
public:
|
||||
HDRHandler();
|
||||
|
||||
#endif
|
||||
bool canRead() const override;
|
||||
bool read(QImage *outImage) override;
|
||||
|
||||
static bool canRead(QIODevice *device);
|
||||
};
|
||||
|
||||
class HDRPlugin : public QImageIOPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "hdr.json")
|
||||
|
||||
public:
|
||||
Capabilities capabilities(QIODevice *device, const QByteArray &format) const override;
|
||||
QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const override;
|
||||
};
|
||||
|
||||
#endif // KIMG_HDR_P_H
|
||||
|
@ -130,9 +130,7 @@ static QDataStream &operator>> (QDataStream &s, QList<PicChannel> &channels)
|
||||
PicChannel channel;
|
||||
s >> chained;
|
||||
s >> channel.size;
|
||||
quint8 encoding;
|
||||
s >> encoding;
|
||||
channel.encoding = PicChannelEncoding(encoding);
|
||||
s >> channel.encoding;
|
||||
s >> channel.code;
|
||||
channels << channel;
|
||||
++count;
|
||||
|
@ -119,7 +119,7 @@ struct PicHeader {
|
||||
*/
|
||||
struct PicChannel {
|
||||
quint8 size; /**< Bits per component per pixel. */
|
||||
PicChannelEncoding encoding; /**< How the channel's data is encoded. */
|
||||
quint8 encoding; /**< How the channel's data is encoded. */
|
||||
quint8 code; /**< Flag field to describe which components are encoded in
|
||||
this channel. */
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user