mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2026-05-25 13:08:28 -04:00
Add Farbfeld read only support
This commit is contained in:
@@ -16,6 +16,7 @@ The following image formats have read-only support:
|
||||
|
||||
- Animated Windows cursors (ani)
|
||||
- Camera RAW images (arw, cr2, cr3, dcs, dng, ...)
|
||||
- Farbfeld (ff)
|
||||
- Gimp (xcf)
|
||||
- Interchange Format Files (iff, ilbm, lbm)
|
||||
- Krita (kra)
|
||||
@@ -256,6 +257,7 @@ limit depends on the format encoding).
|
||||
- DDS: 300,000 x 300,000 pixels
|
||||
- EXR: 300,000 x 300,000 pixels
|
||||
- EPS: same size as Qt's JPG plugin
|
||||
- FF: 300,000 x 300,000 pixels
|
||||
- HDR: 300,000 x 300,000 pixels
|
||||
- HEIF: n/a
|
||||
- IFF: 65,535 x 65,535 pixels
|
||||
|
||||
@@ -76,6 +76,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(
|
||||
ff
|
||||
hdr
|
||||
iff
|
||||
pcx
|
||||
|
||||
@@ -162,6 +162,7 @@ HANDLER_TYPES="ANIHandler ani
|
||||
QAVIFHandler avif
|
||||
QDDSHandler dds
|
||||
EXRHandler exr
|
||||
FFHandler ff
|
||||
HDRHandler hdr
|
||||
HEIFHandler heif
|
||||
IFFHandler iff
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
Usage:
|
||||
python infra/helper.py build_image kimageformats
|
||||
python infra/helper.py build_fuzzers --sanitizer undefined|address|memory kimageformats
|
||||
python infra/helper.py run_fuzzer kimageformats kimgio_[ani|avif|dds|exr|hdr|heif|iff|jp2|jxl|jxr|kra|ora|pcx|pfm|pic|psd|pxr|qoi|ras|raw|rgb|sct|tim|tga|xcf]_fuzzer
|
||||
python infra/helper.py run_fuzzer kimageformats kimgio_[ani|avif|dds|exr|ff|hdr|heif|iff|jp2|jxl|jxr|kra|ora|pcx|pfm|pic|psd|pxr|qoi|ras|raw|rgb|sct|tim|tga|xcf]_fuzzer
|
||||
*/
|
||||
|
||||
#include <QBuffer>
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "avif_p.h"
|
||||
#include "dds_p.h"
|
||||
#include "exr_p.h"
|
||||
#include "ff_p.h"
|
||||
#include "hdr_p.h"
|
||||
#include "heif_p.h"
|
||||
#include "iff_p.h"
|
||||
|
||||
BIN
autotests/read/ff/testcard_rgba16.ff
Normal file
BIN
autotests/read/ff/testcard_rgba16.ff
Normal file
Binary file not shown.
BIN
autotests/read/ff/testcard_rgba16.png
Normal file
BIN
autotests/read/ff/testcard_rgba16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.6 KiB |
@@ -73,6 +73,10 @@ endif()
|
||||
|
||||
##################################
|
||||
|
||||
kimageformats_add_plugin(kimg_ff SOURCES ff.cpp)
|
||||
|
||||
##################################
|
||||
|
||||
kimageformats_add_plugin(kimg_hdr SOURCES hdr.cpp)
|
||||
|
||||
##################################
|
||||
|
||||
259
src/imageformats/ff.cpp
Normal file
259
src/imageformats/ff.cpp
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2026 Mirco Miranda <mircomir@outlook.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
// Specs: https://tools.suckless.org/farbfeld/
|
||||
|
||||
#include "ff_p.h"
|
||||
#include "util_p.h"
|
||||
|
||||
#include <QColorSpace>
|
||||
#include <QIODevice>
|
||||
#include <QImage>
|
||||
#include <QLoggingCategory>
|
||||
#include <QtEndian>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(LOG_FFPLUGIN)
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
Q_LOGGING_CATEGORY(LOG_FFPLUGIN, "kf.imageformats.plugins.ff", QtDebugMsg)
|
||||
#else
|
||||
Q_LOGGING_CATEGORY(LOG_FFPLUGIN, "kf.imageformats.plugins.ff", QtWarningMsg)
|
||||
#endif
|
||||
|
||||
/* *** FF_MAX_IMAGE_WIDTH and FF_MAX_IMAGE_HEIGHT ***
|
||||
* The maximum size in pixel allowed by the plugin.
|
||||
*/
|
||||
#ifndef FF_MAX_IMAGE_WIDTH
|
||||
#define FF_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT
|
||||
#endif
|
||||
#ifndef FF_MAX_IMAGE_HEIGHT
|
||||
#define FF_MAX_IMAGE_HEIGHT FF_MAX_IMAGE_WIDTH
|
||||
#endif
|
||||
|
||||
#define HEADER_SIZE 16
|
||||
|
||||
class FFHeader
|
||||
{
|
||||
private:
|
||||
QByteArray m_rawHeader;
|
||||
|
||||
public:
|
||||
FFHeader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
if (m_rawHeader.size() < HEADER_SIZE) {
|
||||
return false;
|
||||
}
|
||||
return m_rawHeader.startsWith(QByteArray::fromRawData("farbfeld", 8));
|
||||
}
|
||||
|
||||
bool isSupported() const
|
||||
{
|
||||
auto w = width();
|
||||
auto h = height();
|
||||
if (w < 1 || w > FF_MAX_IMAGE_WIDTH || h < 1 || h > FF_MAX_IMAGE_HEIGHT) {
|
||||
return false;
|
||||
}
|
||||
return format() != QImage::Format_Invalid;
|
||||
}
|
||||
|
||||
qint32 width() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return qFromBigEndian<qint32>(m_rawHeader.data() + 8);
|
||||
}
|
||||
|
||||
qint32 height() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return qFromBigEndian<qint32>(m_rawHeader.data() + 12);
|
||||
}
|
||||
|
||||
QSize size() const
|
||||
{
|
||||
return QSize(width(), height());
|
||||
}
|
||||
|
||||
QImage::Format format() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return QImage::Format_Invalid;
|
||||
}
|
||||
return QImage::Format_RGBA64;
|
||||
}
|
||||
|
||||
bool read(QIODevice *d)
|
||||
{
|
||||
m_rawHeader = d->read(HEADER_SIZE);
|
||||
if (m_rawHeader.size() != HEADER_SIZE) {
|
||||
return false;
|
||||
}
|
||||
return isValid();
|
||||
}
|
||||
|
||||
bool peek(QIODevice *d)
|
||||
{
|
||||
m_rawHeader = d->peek(HEADER_SIZE);
|
||||
if (m_rawHeader.size() != HEADER_SIZE) {
|
||||
return false;
|
||||
}
|
||||
return isValid();
|
||||
}
|
||||
};
|
||||
|
||||
class FFHandlerPrivate
|
||||
{
|
||||
public:
|
||||
FFHandlerPrivate() {}
|
||||
~FFHandlerPrivate() {}
|
||||
|
||||
FFHeader m_header;
|
||||
};
|
||||
|
||||
FFHandler::FFHandler()
|
||||
: QImageIOHandler()
|
||||
, d(new FFHandlerPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
bool FFHandler::canRead() const
|
||||
{
|
||||
if (canRead(device())) {
|
||||
setFormat("ff");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FFHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qCWarning(LOG_FFPLUGIN) << "FFHandler::canRead() called with no device";
|
||||
return false;
|
||||
}
|
||||
|
||||
FFHeader h;
|
||||
if (!h.peek(device)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return h.isSupported();
|
||||
}
|
||||
|
||||
bool FFHandler::read(QImage *image)
|
||||
{
|
||||
auto&& header = d->m_header;
|
||||
|
||||
if (!header.read(device())) {
|
||||
qCWarning(LOG_FFPLUGIN) << "FFHandler::read() invalid header";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto img = imageAlloc(header.size(), header.format());
|
||||
if (img.isNull()) {
|
||||
qCWarning(LOG_FFPLUGIN) << "FFHandler::read() error while allocating the image";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto d = device();
|
||||
|
||||
auto size = img.bytesPerLine();
|
||||
for (auto y = 0, h = img.height(); y < h; ++y) {
|
||||
auto line = reinterpret_cast<char*>(img.scanLine(y));
|
||||
if (d->read(line, size) != size) {
|
||||
qCWarning(LOG_FFPLUGIN) << "FFHandler::read() error while reading image scanline";
|
||||
return false;
|
||||
}
|
||||
#if Q_LITTLE_ENDIAN
|
||||
for (auto i = 0; i < size; i += 2) {
|
||||
std::swap(line[i], line[i + 1]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
img.setColorSpace(QColorSpace(QColorSpace::SRgb));
|
||||
|
||||
*image = img;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FFHandler::supportsOption(ImageOption option) const
|
||||
{
|
||||
if (option == QImageIOHandler::Size) {
|
||||
return true;
|
||||
}
|
||||
if (option == QImageIOHandler::ImageFormat) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant FFHandler::option(ImageOption option) const
|
||||
{
|
||||
QVariant v;
|
||||
|
||||
if (option == QImageIOHandler::Size) {
|
||||
auto&& h = d->m_header;
|
||||
if (h.isValid()) {
|
||||
v = QVariant::fromValue(h.size());
|
||||
} else if (auto d = device()) {
|
||||
if (h.peek(d)) {
|
||||
v = QVariant::fromValue(h.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (option == QImageIOHandler::ImageFormat) {
|
||||
auto&& h = d->m_header;
|
||||
if (h.isValid()) {
|
||||
v = QVariant::fromValue(h.format());
|
||||
} else if (auto d = device()) {
|
||||
if (h.peek(d)) {
|
||||
v = QVariant::fromValue(h.format());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
QImageIOPlugin::Capabilities FFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
if (format == "ff") {
|
||||
return Capabilities(CanRead);
|
||||
}
|
||||
if (!format.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
if (!device->isOpen()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Capabilities cap;
|
||||
if (device->isReadable() && FFHandler::canRead(device)) {
|
||||
cap |= CanRead;
|
||||
}
|
||||
return cap;
|
||||
}
|
||||
|
||||
QImageIOHandler *FFPlugin::create(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
QImageIOHandler *handler = new FFHandler;
|
||||
handler->setDevice(device);
|
||||
handler->setFormat(format);
|
||||
return handler;
|
||||
}
|
||||
|
||||
#include "moc_ff_p.cpp"
|
||||
4
src/imageformats/ff.json
Normal file
4
src/imageformats/ff.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"Keys": [ "ff" ],
|
||||
"MimeTypes": [ "image/x-farbfeld" ]
|
||||
}
|
||||
42
src/imageformats/ff_p.h
Normal file
42
src/imageformats/ff_p.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2026 Mirco Miranda <mircomir@outlook.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIMG_FF_P_H
|
||||
#define KIMG_FF_P_H
|
||||
|
||||
#include <QImageIOPlugin>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class FFHandlerPrivate;
|
||||
class FFHandler : public QImageIOHandler
|
||||
{
|
||||
public:
|
||||
FFHandler();
|
||||
|
||||
bool canRead() const override;
|
||||
bool read(QImage *image) override;
|
||||
|
||||
bool supportsOption(QImageIOHandler::ImageOption option) const override;
|
||||
QVariant option(QImageIOHandler::ImageOption option) const override;
|
||||
|
||||
static bool canRead(QIODevice *device);
|
||||
|
||||
private:
|
||||
const QScopedPointer<FFHandlerPrivate> d;
|
||||
};
|
||||
|
||||
class FFPlugin : public QImageIOPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "ff.json")
|
||||
|
||||
public:
|
||||
Capabilities capabilities(QIODevice *device, const QByteArray &format) const override;
|
||||
QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const override;
|
||||
};
|
||||
|
||||
#endif // KIMG_FF_P_H
|
||||
Reference in New Issue
Block a user