KImageFormats

pxr.cpp
1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 2024 Mirco Miranda <mircomir@outlook.com>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "pxr_p.h"
9#include "util_p.h"
10
11#include <QIODevice>
12#include <QImage>
13#include <QLoggingCategory>
14
15Q_DECLARE_LOGGING_CATEGORY(LOG_PXRPLUGIN)
16Q_LOGGING_CATEGORY(LOG_PXRPLUGIN, "kf.imageformats.plugins.pxr", QtWarningMsg)
17
18class PXRHeader
19{
20private:
21 QByteArray m_rawHeader;
22
23 quint16 ui16(quint8 c1, quint8 c2) const {
24 return (quint16(c2) << 8) | quint16(c1);
25 }
26
27 quint32 ui32(quint8 c1, quint8 c2, quint8 c3, quint8 c4) const {
28 return (quint32(c4) << 24) | (quint32(c3) << 16) | (quint32(c2) << 8) | quint32(c1);
29 }
30
31public:
32 PXRHeader()
33 {
34
35 }
36
37 bool isValid() const
38 {
39 return (m_rawHeader.size() == 512 &&
40 m_rawHeader.startsWith(QByteArray::fromRawData("\x80\xE8\x00\x00", 4)));
41 }
42
43 bool isSupported() const
44 {
45 return format() != QImage::Format_Invalid;
46 }
47
48 qint32 width() const
49 {
50 if (!isValid()) {
51 return 0;
52 }
53 return qint32(ui16(m_rawHeader.at(418), m_rawHeader.at(419)));
54 }
55
56 qint32 height() const
57 {
58 if (!isValid()) {
59 return 0;
60 }
61 return qint32(ui16(m_rawHeader.at(416), m_rawHeader.at(417)));
62 }
63
64 QSize size() const
65 {
66 return QSize(width(), height());
67 }
68
69 qint32 channel() const
70 {
71 if (!isValid()) {
72 return 0;
73 }
74 return qint32(ui16(m_rawHeader.at(424), m_rawHeader.at(425)));
75 }
76
77 qint32 depth() const
78 {
79 if (!isValid()) {
80 return 0;
81 }
82 return qint32(ui16(m_rawHeader.at(426), m_rawHeader.at(427)));
83 }
84
85 // supposing the image offset (always 1024 on sample files)
86 qint32 offset() const
87 {
88 if (!isValid()) {
89 return 0;
90 }
91 return qint32(ui16(m_rawHeader.at(428), m_rawHeader.at(429)));
92 }
93
94 QImage::Format format() const
95 {
96 if (channel() == 14 && depth() == 2) {
98 }
99 if (channel() == 8 && depth() == 2) {
101 }
103 }
104
105 qsizetype strideSize() const
106 {
107 if (format() == QImage::Format_RGB888) {
108 return width() * 3;
109 }
110 if (format() == QImage::Format_Grayscale8) {
111 return width();
112 }
113 return 0;
114 }
115
116 bool read(QIODevice *d)
117 {
118 m_rawHeader = d->read(512);
119 return isValid();
120 }
121
122 bool peek(QIODevice *d)
123 {
124 m_rawHeader = d->peek(512);
125 return isValid();
126 }
127
128 bool jumpToImageData(QIODevice *d) const
129 {
130 if (d->isSequential()) {
131 if (auto sz = std::max(offset() - qint32(m_rawHeader.size()), 0)) {
132 return d->read(sz).size() == sz;
133 }
134 return true;
135 }
136 return d->seek(offset());
137 }
138};
139
140class PXRHandlerPrivate
141{
142public:
143 PXRHandlerPrivate() {}
144 ~PXRHandlerPrivate() {}
145
146 PXRHeader m_header;
147};
148
149PXRHandler::PXRHandler()
151 , d(new PXRHandlerPrivate)
152{
153}
154
155bool PXRHandler::canRead() const
156{
157 if (canRead(device())) {
158 setFormat("pxr");
159 return true;
160 }
161 return false;
162}
163
164bool PXRHandler::canRead(QIODevice *device)
165{
166 if (!device) {
167 qCWarning(LOG_PXRPLUGIN) << "PXRHandler::canRead() called with no device";
168 return false;
169 }
170
171 PXRHeader h;
172 if (!h.peek(device)) {
173 return false;
174 }
175
176 return h.isSupported();
177}
178
179bool PXRHandler::read(QImage *image)
180{
181 auto&& header = d->m_header;
182
183 if (!header.read(device())) {
184 qCWarning(LOG_PXRPLUGIN) << "PXRHandler::read() invalid header";
185 return false;
186 }
187
188 auto img = imageAlloc(header.size(), header.format());
189 if (img.isNull()) {
190 qCWarning(LOG_PXRPLUGIN) << "PXRHandler::read() error while allocating the image";
191 return false;
192 }
193
194 auto d = device();
195 if (!header.jumpToImageData(d)) {
196 qCWarning(LOG_PXRPLUGIN) << "PXRHandler::read() error while seeking image data";
197 return false;
198 }
199
200 auto size = std::min(img.bytesPerLine(), header.strideSize());
201 for (auto y = 0, h = img.height(); y < h; ++y) {
202 auto line = reinterpret_cast<char*>(img.scanLine(y));
203 if (d->read(line, size) != size) {
204 qCWarning(LOG_PXRPLUGIN) << "PXRHandler::read() error while reading image scanline";
205 return false;
206 }
207 }
208
209 *image = img;
210 return true;
211}
212
213bool PXRHandler::supportsOption(ImageOption option) const
214{
215 if (option == QImageIOHandler::Size) {
216 return true;
217 }
218 if (option == QImageIOHandler::ImageFormat) {
219 return true;
220 }
221 return false;
222}
223
224QVariant PXRHandler::option(ImageOption option) const
225{
226 QVariant v;
227
228 if (option == QImageIOHandler::Size) {
229 auto&& h = d->m_header;
230 if (h.isValid()) {
231 v = QVariant::fromValue(h.size());
232 } else if (auto d = device()) {
233 if (h.peek(d)) {
234 v = QVariant::fromValue(h.size());
235 }
236 }
237 }
238
239 if (option == QImageIOHandler::ImageFormat) {
240 auto&& h = d->m_header;
241 if (h.isValid()) {
242 v = QVariant::fromValue(h.format());
243 } else if (auto d = device()) {
244 if (h.peek(d)) {
245 v = QVariant::fromValue(h.format());
246 }
247 }
248 }
249
250 return v;
251}
252
253QImageIOPlugin::Capabilities PXRPlugin::capabilities(QIODevice *device, const QByteArray &format) const
254{
255 if (format == "pxr") {
256 return Capabilities(CanRead);
257 }
258 if (!format.isEmpty()) {
259 return {};
260 }
261 if (!device->isOpen()) {
262 return {};
263 }
264
265 Capabilities cap;
266 if (device->isReadable() && PXRHandler::canRead(device)) {
267 cap |= CanRead;
268 }
269 return cap;
270}
271
272QImageIOHandler *PXRPlugin::create(QIODevice *device, const QByteArray &format) const
273{
274 QImageIOHandler *handler = new PXRHandler;
275 handler->setDevice(device);
276 handler->setFormat(format);
277 return handler;
278}
279
280#include "moc_pxr_p.cpp"
QFlags< Capability > Capabilities
char at(qsizetype i) const const
QByteArray fromRawData(const char *data, qsizetype size)
bool isEmpty() const const
qsizetype size() const const
bool startsWith(QByteArrayView bv) const const
void setDevice(QIODevice *device)
void setFormat(const QByteArray &format)
bool isOpen() const const
bool isReadable() const const
virtual bool isSequential() const const
QByteArray peek(qint64 maxSize)
QByteArray read(qint64 maxSize)
virtual bool seek(qint64 pos)
QVariant fromValue(T &&value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:07:21 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.