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 d->startTransaction();
125 auto ok = read(d);
127 return ok;
128 }
129
130 bool jumpToImageData(QIODevice *d) const
131 {
132 if (d->isSequential()) {
133 if (auto sz = std::max(offset() - qint32(m_rawHeader.size()), 0)) {
134 return d->read(sz).size() == sz;
135 }
136 return true;
137 }
138 return d->seek(offset());
139 }
140};
141
142class PXRHandlerPrivate
143{
144public:
145 PXRHandlerPrivate() {}
146 ~PXRHandlerPrivate() {}
147
148 PXRHeader m_header;
149};
150
151PXRHandler::PXRHandler()
153 , d(new PXRHandlerPrivate)
154{
155}
156
157bool PXRHandler::canRead() const
158{
159 if (canRead(device())) {
160 setFormat("pxr");
161 return true;
162 }
163 return false;
164}
165
166bool PXRHandler::canRead(QIODevice *device)
167{
168 if (!device) {
169 qCWarning(LOG_PXRPLUGIN) << "PXRHandler::canRead() called with no device";
170 return false;
171 }
172
173 PXRHeader h;
174 if (!h.peek(device)) {
175 return false;
176 }
177
178 return h.isSupported();
179}
180
181bool PXRHandler::read(QImage *image)
182{
183 auto&& header = d->m_header;
184
185 if (!header.read(device())) {
186 qCWarning(LOG_PXRPLUGIN) << "PXRHandler::read() invalid header";
187 return false;
188 }
189
190 auto img = imageAlloc(header.size(), header.format());
191 if (img.isNull()) {
192 qCWarning(LOG_PXRPLUGIN) << "PXRHandler::read() error while allocating the image";
193 return false;
194 }
195
196 auto d = device();
197 if (!header.jumpToImageData(d)) {
198 qCWarning(LOG_PXRPLUGIN) << "PXRHandler::read() error while seeking image data";
199 return false;
200 }
201
202 auto size = std::min(img.bytesPerLine(), header.strideSize());
203 for (auto y = 0, h = img.height(); y < h; ++y) {
204 auto line = reinterpret_cast<char*>(img.scanLine(y));
205 if (d->read(line, size) != size) {
206 qCWarning(LOG_PXRPLUGIN) << "PXRHandler::read() error while reading image scanline";
207 return false;
208 }
209 }
210
211 *image = img;
212 return true;
213}
214
215bool PXRHandler::supportsOption(ImageOption option) const
216{
217 if (option == QImageIOHandler::Size) {
218 return true;
219 }
220 if (option == QImageIOHandler::ImageFormat) {
221 return true;
222 }
223 return false;
224}
225
226QVariant PXRHandler::option(ImageOption option) const
227{
228 QVariant v;
229
230 if (option == QImageIOHandler::Size) {
231 auto&& h = d->m_header;
232 if (h.isValid()) {
233 v = QVariant::fromValue(h.size());
234 } else if (auto d = device()) {
235 if (h.peek(d)) {
236 v = QVariant::fromValue(h.size());
237 }
238 }
239 }
240
241 if (option == QImageIOHandler::ImageFormat) {
242 auto&& h = d->m_header;
243 if (h.isValid()) {
244 v = QVariant::fromValue(h.format());
245 } else if (auto d = device()) {
246 if (h.peek(d)) {
247 v = QVariant::fromValue(h.format());
248 }
249 }
250 }
251
252 return v;
253}
254
255QImageIOPlugin::Capabilities PXRPlugin::capabilities(QIODevice *device, const QByteArray &format) const
256{
257 if (format == "pxr") {
258 return Capabilities(CanRead);
259 }
260 if (!format.isEmpty()) {
261 return {};
262 }
263 if (!device->isOpen()) {
264 return {};
265 }
266
267 Capabilities cap;
268 if (device->isReadable() && PXRHandler::canRead(device)) {
269 cap |= CanRead;
270 }
271 return cap;
272}
273
274QImageIOHandler *PXRPlugin::create(QIODevice *device, const QByteArray &format) const
275{
276 QImageIOHandler *handler = new PXRHandler;
277 handler->setDevice(device);
278 handler->setFormat(format);
279 return handler;
280}
281
282#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 read(qint64 maxSize)
void rollbackTransaction()
virtual bool seek(qint64 pos)
void startTransaction()
QVariant fromValue(T &&value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 4 2024 12:01:33 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.