mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-05-26 00:00:21 -04:00
PXR: Pixar raster read only support
Limited read only support to Pixar raster as supported by Photoshop (RGB and Gray 8-bit only).
This commit is contained in:
parent
863c424390
commit
950ed43623
@ -18,6 +18,7 @@ The following image formats have read-only support:
|
||||
- Gimp (xcf)
|
||||
- Krita (kra)
|
||||
- OpenRaster (ora)
|
||||
- Pixar raster (pxr)
|
||||
- Photoshop documents (psd, psb, pdd, psdt)
|
||||
- Radiance HDR (hdr)
|
||||
- Sun Raster (im1, im8, im24, im32, ras, sun)
|
||||
|
@ -67,6 +67,7 @@ kimageformats_read_tests(
|
||||
hdr
|
||||
pcx
|
||||
psd
|
||||
pxr
|
||||
qoi
|
||||
ras
|
||||
rgb
|
||||
|
BIN
autotests/read/pxr/testcard_gray.png
Normal file
BIN
autotests/read/pxr/testcard_gray.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.4 KiB |
BIN
autotests/read/pxr/testcard_gray.pxr
Normal file
BIN
autotests/read/pxr/testcard_gray.pxr
Normal file
Binary file not shown.
BIN
autotests/read/pxr/testcard_rgb.png
Normal file
BIN
autotests/read/pxr/testcard_rgb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
autotests/read/pxr/testcard_rgb.pxr
Normal file
BIN
autotests/read/pxr/testcard_rgb.pxr
Normal file
Binary file not shown.
@ -87,6 +87,10 @@ kimageformats_add_plugin(kimg_psd SOURCES psd.cpp scanlineconverter.cpp)
|
||||
|
||||
##################################
|
||||
|
||||
kimageformats_add_plugin(kimg_pxr SOURCES pxr.cpp)
|
||||
|
||||
##################################
|
||||
|
||||
kimageformats_add_plugin(kimg_qoi SOURCES qoi.cpp scanlineconverter.cpp)
|
||||
|
||||
##################################
|
||||
|
267
src/imageformats/pxr.cpp
Normal file
267
src/imageformats/pxr.cpp
Normal file
@ -0,0 +1,267 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2024 Mirco Miranda <mircomir@outlook.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "pxr_p.h"
|
||||
#include "util_p.h"
|
||||
|
||||
#include <QIODevice>
|
||||
#include <QImage>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(LOG_PXRPLUGIN)
|
||||
Q_LOGGING_CATEGORY(LOG_PXRPLUGIN, "kf.imageformats.plugins.pxr", QtWarningMsg)
|
||||
|
||||
class PxrHeader
|
||||
{
|
||||
private:
|
||||
QByteArray m_rawHeader;
|
||||
|
||||
quint16 ui16(quint8 c1, quint8 c2) const {
|
||||
return (quint16(c2) << 8) | quint16(c1);
|
||||
}
|
||||
|
||||
quint32 ui32(quint8 c1, quint8 c2, quint8 c3, quint8 c4) const {
|
||||
return (quint32(c4) << 24) | (quint32(c3) << 16) | (quint32(c2) << 8) | quint32(c1);
|
||||
}
|
||||
|
||||
public:
|
||||
PxrHeader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return (m_rawHeader.size() == 512 &&
|
||||
m_rawHeader.startsWith(QByteArray::fromRawData("\x80\xE8\x00\x00", 4)));
|
||||
}
|
||||
|
||||
bool isSupported() const
|
||||
{
|
||||
return format() != QImage::Format_Invalid;
|
||||
}
|
||||
|
||||
qint32 width() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return qint32(ui16(m_rawHeader.at(418), m_rawHeader.at(419)));
|
||||
}
|
||||
|
||||
qint32 height() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return qint32(ui16(m_rawHeader.at(416), m_rawHeader.at(417)));
|
||||
}
|
||||
|
||||
QSize size() const
|
||||
{
|
||||
return QSize(width(), height());
|
||||
}
|
||||
|
||||
qint32 channel() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return qint32(ui16(m_rawHeader.at(424), m_rawHeader.at(425)));
|
||||
}
|
||||
|
||||
qint32 depth() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return qint32(ui16(m_rawHeader.at(426), m_rawHeader.at(427)));
|
||||
}
|
||||
|
||||
// supposing the image offset (always 1024 on sample files)
|
||||
qint32 offset() const
|
||||
{
|
||||
if (!isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return qint32(ui16(m_rawHeader.at(428), m_rawHeader.at(429)));
|
||||
}
|
||||
|
||||
QImage::Format format() const
|
||||
{
|
||||
if (channel() == 14 && depth() == 2) {
|
||||
return QImage::Format_RGB888;
|
||||
}
|
||||
if (channel() == 8 && depth() == 2) {
|
||||
return QImage::Format_Grayscale8;
|
||||
}
|
||||
return QImage::Format_Invalid;
|
||||
}
|
||||
|
||||
qsizetype strideSize() const
|
||||
{
|
||||
if (format() == QImage::Format_RGB888) {
|
||||
return width() * 3;
|
||||
}
|
||||
if (format() == QImage::Format_Grayscale8) {
|
||||
return width();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool read(QIODevice *d)
|
||||
{
|
||||
m_rawHeader = d->read(512);
|
||||
return isValid();
|
||||
}
|
||||
|
||||
bool peek(QIODevice *d)
|
||||
{
|
||||
d->startTransaction();
|
||||
auto ok = read(d);
|
||||
d->rollbackTransaction();
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool jumpToImageData(QIODevice *d) const
|
||||
{
|
||||
if (d->isSequential()) {
|
||||
if (auto sz = std::max(offset() - qint32(m_rawHeader.size()), 0)) {
|
||||
return d->read(sz).size() == sz;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return d->seek(offset());
|
||||
}
|
||||
};
|
||||
|
||||
PXRHandler::PXRHandler()
|
||||
{
|
||||
}
|
||||
|
||||
bool PXRHandler::canRead() const
|
||||
{
|
||||
if (canRead(device())) {
|
||||
setFormat("pxr");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PXRHandler::canRead(QIODevice *device)
|
||||
{
|
||||
if (!device) {
|
||||
qCWarning(LOG_PXRPLUGIN) << "PXRHandler::canRead() called with no device";
|
||||
return false;
|
||||
}
|
||||
|
||||
PxrHeader h;
|
||||
if (!h.peek(device)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return h.isSupported();
|
||||
}
|
||||
|
||||
bool PXRHandler::read(QImage *image)
|
||||
{
|
||||
PxrHeader header;
|
||||
|
||||
if (!header.read(device())) {
|
||||
qCWarning(LOG_PXRPLUGIN) << "PXRHandler::read() invalid header";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto img = imageAlloc(header.size(), header.format());
|
||||
if (img.isNull()) {
|
||||
qCWarning(LOG_PXRPLUGIN) << "PXRHandler::read() error while allocating the image";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto d = device();
|
||||
if (!header.jumpToImageData(d)) {
|
||||
qCWarning(LOG_PXRPLUGIN) << "PXRHandler::read() error while seeking image data";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto size = std::min(img.bytesPerLine(), header.strideSize());
|
||||
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_PXRPLUGIN) << "PXRHandler::read() error while reading image scanline";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*image = img;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PXRHandler::supportsOption(ImageOption option) const
|
||||
{
|
||||
if (option == QImageIOHandler::Size) {
|
||||
return true;
|
||||
}
|
||||
if (option == QImageIOHandler::ImageFormat) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant PXRHandler::option(ImageOption option) const
|
||||
{
|
||||
QVariant v;
|
||||
|
||||
if (option == QImageIOHandler::Size) {
|
||||
if (auto d = device()) {
|
||||
PxrHeader h;
|
||||
if (h.peek(d)) {
|
||||
v = QVariant::fromValue(h.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (option == QImageIOHandler::ImageFormat) {
|
||||
if (auto d = device()) {
|
||||
PxrHeader h;
|
||||
if (h.peek(d)) {
|
||||
v = QVariant::fromValue(h.format());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
QImageIOPlugin::Capabilities PXRPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
if (format == "pxr") {
|
||||
return Capabilities(CanRead);
|
||||
}
|
||||
if (!format.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
if (!device->isOpen()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Capabilities cap;
|
||||
if (device->isReadable() && PXRHandler::canRead(device)) {
|
||||
cap |= CanRead;
|
||||
}
|
||||
return cap;
|
||||
}
|
||||
|
||||
QImageIOHandler *PXRPlugin::create(QIODevice *device, const QByteArray &format) const
|
||||
{
|
||||
QImageIOHandler *handler = new PXRHandler;
|
||||
handler->setDevice(device);
|
||||
handler->setFormat(format);
|
||||
return handler;
|
||||
}
|
||||
|
||||
#include "moc_pxr_p.cpp"
|
4
src/imageformats/pxr.json
Normal file
4
src/imageformats/pxr.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"Keys": [ "pxr" ],
|
||||
"MimeTypes": [ "image/x-pxr" ]
|
||||
}
|
37
src/imageformats/pxr_p.h
Normal file
37
src/imageformats/pxr_p.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2024 Mirco Miranda <mircomir@outlook.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIMG_PXR_P_H
|
||||
#define KIMG_PXR_P_H
|
||||
|
||||
#include <QImageIOPlugin>
|
||||
|
||||
class PXRHandler : public QImageIOHandler
|
||||
{
|
||||
public:
|
||||
PXRHandler();
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
class PXRPlugin : public QImageIOPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "pxr.json")
|
||||
|
||||
public:
|
||||
Capabilities capabilities(QIODevice *device, const QByteArray &format) const override;
|
||||
QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const override;
|
||||
};
|
||||
|
||||
#endif // KIMG_PXR_P_H
|
Loading…
Reference in New Issue
Block a user