Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qppmhandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "private/qppmhandler_p.h"
5
6#ifndef QT_NO_IMAGEFORMAT_PPM
7
8#include <qdebug.h>
9#include <qimage.h>
10#include <qlist.h>
11#include <qloggingcategory.h>
12#include <qrgba64.h>
13#include <qvariant.h>
14#include <private/qlocale_p.h>
15#include <private/qtools_p.h>
16
18
20
21/*****************************************************************************
22 PBM/PGM/PPM (ASCII and RAW) image read/write functions
23 *****************************************************************************/
24
26{
27 const int buflen = 100;
28 char buf[buflen];
29 int res = 0;
30 do {
31 res = d->readLine(buf, buflen);
32 } while (res > 0 && buf[res-1] != '\n');
33}
34
35static int read_pbm_int(QIODevice *d, bool *ok)
36{
37 char c;
38 int val = -1;
39 bool digit;
40 bool hasOverflow = false;
41 for (;;) {
42 if (!d->getChar(&c)) // end of file
43 break;
45 if (val != -1) {
46 if (digit) {
47 const int cValue = c - '0';
48 if (val <= (INT_MAX - cValue) / 10) {
49 val = 10*val + cValue;
50 } else {
51 hasOverflow = true;
52 }
53 continue;
54 } else {
55 if (c == '#') // comment
57 break;
58 }
59 }
60 if (digit) // first digit
61 val = c - '0';
62 else if (ascii_isspace(c))
63 continue;
64 else if (c == '#')
66 else
67 break;
68 }
69 if (val < 0)
70 *ok = false;
71 return hasOverflow ? -1 : val;
72}
73
74static bool read_pbm_header(QIODevice *device, char& type, int& w, int& h, int& mcc)
75{
76 char buf[3];
77 if (device->read(buf, 3) != 3) // read P[1-6]<white-space>
78 return false;
79
80 if (!(buf[0] == 'P' && QtMiscUtils::isAsciiDigit(buf[1]) && ascii_isspace(buf[2])))
81 return false;
82
83 type = buf[1];
84 if (type < '1' || type > '6')
85 return false;
86
87 bool ok = true;
88 w = read_pbm_int(device, &ok); // get image width
89 h = read_pbm_int(device, &ok); // get image height
90
91 if (type == '1' || type == '4')
92 mcc = 1; // ignore max color component
93 else
94 mcc = read_pbm_int(device, &ok); // get max color component
95
96 if (!ok || w <= 0 || w > 32767 || h <= 0 || h > 32767 || mcc <= 0 || mcc > 0xffff)
97 return false; // weird P.M image
98
99 return true;
100}
101
102static inline QRgb scale_pbm_color(quint16 mx, quint16 rv, quint16 gv, quint16 bv)
103{
104 return QRgba64::fromRgba64((rv * 0xffffu) / mx, (gv * 0xffffu) / mx, (bv * 0xffffu) / mx, 0xffff).toArgb32();
105}
106
107static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, QImage *outImage)
108{
109 int nbits, y;
110 qsizetype pbm_bpl;
111 bool raw;
112
114 switch (type) {
115 case '1': // ascii PBM
116 case '4': // raw PBM
117 nbits = 1;
119 break;
120 case '2': // ascii PGM
121 case '5': // raw PGM
122 nbits = 8;
124 break;
125 case '3': // ascii PPM
126 case '6': // raw PPM
127 nbits = 32;
129 break;
130 default:
131 return false;
132 }
133 raw = type >= '4';
134
135 if (!QImageIOHandler::allocateImage(QSize(w, h), format, outImage))
136 return false;
137
138 pbm_bpl = (qsizetype(w) * nbits + 7) / 8; // bytes per scanline in PBM
139
140 if (raw) { // read raw data
141 if (nbits == 32) { // type 6
142 pbm_bpl = mcc < 256 ? 3*w : 6*w;
143 uchar *buf24 = new uchar[pbm_bpl], *b;
144 QRgb *p;
145 QRgb *end;
146 for (y=0; y<h; y++) {
147 if (device->read((char *)buf24, pbm_bpl) != pbm_bpl) {
148 delete[] buf24;
149 return false;
150 }
151 p = (QRgb *)outImage->scanLine(y);
152 end = p + w;
153 b = buf24;
154 while (p < end) {
155 if (mcc < 256) {
156 if (mcc == 255)
157 *p++ = qRgb(b[0],b[1],b[2]);
158 else
159 *p++ = scale_pbm_color(mcc, b[0], b[1], b[2]);
160 b += 3;
161 } else {
162 quint16 rv = b[0] << 8 | b[1];
163 quint16 gv = b[2] << 8 | b[3];
164 quint16 bv = b[4] << 8 | b[5];
165 if (mcc == 0xffff)
166 *p++ = QRgba64::fromRgba64(rv, gv, bv, 0xffff).toArgb32();
167 else
168 *p++ = scale_pbm_color(mcc, rv, gv, bv);
169 b += 6;
170 }
171 }
172 }
173 delete[] buf24;
174 } else if (nbits == 8 && mcc > 255) { // type 5 16bit
175 pbm_bpl = 2*w;
176 uchar *buf16 = new uchar[pbm_bpl];
177 for (y=0; y<h; y++) {
178 if (device->read((char *)buf16, pbm_bpl) != pbm_bpl) {
179 delete[] buf16;
180 return false;
181 }
182 uchar *p = outImage->scanLine(y);
183 uchar *end = p + w;
184 uchar *b = buf16;
185 while (p < end) {
186 *p++ = (b[0] << 8 | b[1]) * 255 / mcc;
187 b += 2;
188 }
189 }
190 delete[] buf16;
191 } else { // type 4,5
192 for (y=0; y<h; y++) {
193 uchar *p = outImage->scanLine(y);
194 if (device->read((char *)p, pbm_bpl) != pbm_bpl)
195 return false;
196 if (nbits == 8 && mcc < 255) {
197 for (qsizetype i = 0; i < pbm_bpl; i++)
198 p[i] = (p[i] * 255) / mcc;
199 }
200 }
201 }
202 } else { // read ascii data
203 uchar *p;
204 qsizetype n;
205 bool ok = true;
206 for (y = 0; y < h && ok; y++) {
207 p = outImage->scanLine(y);
208 n = pbm_bpl;
209 if (nbits == 1) {
210 int b;
211 int bitsLeft = w;
212 while (n-- && ok) {
213 b = 0;
214 for (int i=0; i<8; i++) {
215 if (i < bitsLeft)
216 b = (b << 1) | (read_pbm_int(device, &ok) & 1);
217 else
218 b = (b << 1) | (0 & 1); // pad it our self if we need to
219 }
220 bitsLeft -= 8;
221 *p++ = b;
222 }
223 } else if (nbits == 8) {
224 if (mcc == 255) {
225 while (n-- && ok) {
226 *p++ = read_pbm_int(device, &ok);
227 }
228 } else {
229 while (n-- && ok) {
230 *p++ = (read_pbm_int(device, &ok) & 0xffff) * 255 / mcc;
231 }
232 }
233 } else { // 32 bits
234 n /= 4;
235 int r, g, b;
236 if (mcc == 255) {
237 while (n-- && ok) {
238 r = read_pbm_int(device, &ok);
239 g = read_pbm_int(device, &ok);
240 b = read_pbm_int(device, &ok);
241 *((QRgb*)p) = qRgb(r, g, b);
242 p += 4;
243 }
244 } else {
245 while (n-- && ok) {
246 r = read_pbm_int(device, &ok);
247 g = read_pbm_int(device, &ok);
248 b = read_pbm_int(device, &ok);
249 *((QRgb*)p) = scale_pbm_color(mcc, r, g, b);
250 p += 4;
251 }
252 }
253 }
254 }
255 if (!ok)
256 return false;
257 }
258
260 outImage->setColorCount(2);
261 outImage->setColor(0, qRgb(255,255,255)); // white
262 outImage->setColor(1, qRgb(0,0,0)); // black
263 }
264
265 return true;
266}
267
268static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, const QByteArray &sourceFormat)
269{
271 QImage image = sourceImage;
272 QByteArray format = sourceFormat;
273
274 format = format.left(3); // ignore RAW part
275 bool gray = format == "pgm";
276
277 if (format == "pbm") {
278 image = image.convertToFormat(QImage::Format_Mono);
279 } else if (gray) {
280 image = image.convertToFormat(QImage::Format_Grayscale8);
281 } else {
282 switch (image.format()) {
285 image = image.convertToFormat(QImage::Format_Indexed8);
286 break;
290 break;
291 default:
292 if (image.hasAlphaChannel())
293 image = image.convertToFormat(QImage::Format_ARGB32);
294 else
295 image = image.convertToFormat(QImage::Format_RGB32);
296 break;
297 }
298 }
299
300 if (image.depth() == 1 && image.colorCount() == 2) {
301 if (qGray(image.color(0)) < qGray(image.color(1))) {
302 // 0=dark/black, 1=light/white - invert
303 image.detach();
304 for (int y=0; y<image.height(); y++) {
305 uchar *p = image.scanLine(y);
306 uchar *end = p + image.bytesPerLine();
307 while (p < end)
308 *p++ ^= 0xff;
309 }
310 }
311 }
312
313 uint w = image.width();
314 uint h = image.height();
315
316 str = "P\n";
318 str += ' ';
320 str += '\n';
321
322 switch (image.depth()) {
323 case 1: {
324 str.insert(1, '4');
325 if (out->write(str, str.size()) != str.size())
326 return false;
327 w = (w+7)/8;
328 for (uint y=0; y<h; y++) {
329 uchar* line = image.scanLine(y);
330 if (w != (uint)out->write((char*)line, w))
331 return false;
332 }
333 }
334 break;
335
336 case 8: {
337 str.insert(1, gray ? '5' : '6');
338 str.append("255\n");
339 if (out->write(str, str.size()) != str.size())
340 return false;
341 qsizetype bpl = qsizetype(w) * (gray ? 1 : 3);
342 uchar *buf = new uchar[bpl];
343 if (image.format() == QImage::Format_Indexed8) {
344 QList<QRgb> color = image.colorTable();
345 for (uint y=0; y<h; y++) {
346 const uchar *b = image.constScanLine(y);
347 uchar *p = buf;
348 uchar *end = buf+bpl;
349 if (gray) {
350 while (p < end) {
351 uchar g = (uchar)qGray(color[*b++]);
352 *p++ = g;
353 }
354 } else {
355 while (p < end) {
356 QRgb rgb = color[*b++];
357 *p++ = qRed(rgb);
358 *p++ = qGreen(rgb);
359 *p++ = qBlue(rgb);
360 }
361 }
362 if (bpl != (qsizetype)out->write((char*)buf, bpl))
363 return false;
364 }
365 } else {
366 for (uint y=0; y<h; y++) {
367 const uchar *b = image.constScanLine(y);
368 uchar *p = buf;
369 uchar *end = buf + bpl;
370 if (gray) {
371 while (p < end)
372 *p++ = *b++;
373 } else {
374 while (p < end) {
375 uchar color = *b++;
376 *p++ = color;
377 *p++ = color;
378 *p++ = color;
379 }
380 }
381 if (bpl != (qsizetype)out->write((char*)buf, bpl))
382 return false;
383 }
384 }
385 delete [] buf;
386 break;
387 }
388
389 case 32: {
390 str.insert(1, '6');
391 str.append("255\n");
392 if (out->write(str, str.size()) != str.size())
393 return false;
394 qsizetype bpl = qsizetype(w) * 3;
395 uchar *buf = new uchar[bpl];
396 for (uint y=0; y<h; y++) {
397 const QRgb *b = reinterpret_cast<const QRgb *>(image.constScanLine(y));
398 uchar *p = buf;
399 uchar *end = buf+bpl;
400 while (p < end) {
401 QRgb rgb = *b++;
402 *p++ = qRed(rgb);
403 *p++ = qGreen(rgb);
404 *p++ = qBlue(rgb);
405 }
406 if (bpl != (qsizetype)out->write((char*)buf, bpl))
407 return false;
408 }
409 delete [] buf;
410 break;
411 }
412
413 default:
414 return false;
415 }
416
417 return true;
418}
419
421 : state(Ready)
422{
423}
424
425bool QPpmHandler::readHeader()
426{
427 state = Error;
428 if (!read_pbm_header(device(), type, width, height, mcc))
429 return false;
430 state = ReadHeader;
431 return true;
432}
433
435{
436 if (state == Ready && !canRead(device(), &subType))
437 return false;
438
439 if (state != Error) {
440 setFormat(subType);
441 return true;
442 }
443
444 return false;
445}
446
448{
449 if (!device) {
450 qCWarning(lcImageIo, "QPpmHandler::canRead() called with no device");
451 return false;
452 }
453
454 char head[2];
455 if (device->peek(head, sizeof(head)) != sizeof(head))
456 return false;
457
458 if (head[0] != 'P')
459 return false;
460
461 if (head[1] == '1' || head[1] == '4') {
462 if (subType)
463 *subType = "pbm";
464 } else if (head[1] == '2' || head[1] == '5') {
465 if (subType)
466 *subType = "pgm";
467 } else if (head[1] == '3' || head[1] == '6') {
468 if (subType)
469 *subType = "ppm";
470 } else {
471 return false;
472 }
473 return true;
474}
475
477{
478 if (state == Error)
479 return false;
480
481 if (state == Ready && !readHeader()) {
482 state = Error;
483 return false;
484 }
485
486 if (!read_pbm_body(device(), type, width, height, mcc, image)) {
487 state = Error;
488 return false;
489 }
490
491 state = Ready;
492 return true;
493}
494
496{
497 return write_pbm_image(device(), image, subType);
498}
499
501{
502 return option == SubType
503 || option == Size
504 || option == ImageFormat;
505}
506
508{
509 if (option == SubType) {
510 return subType;
511 } else if (option == Size) {
512 if (state == Error)
513 return QVariant();
514 if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
515 return QVariant();
516 return QSize(width, height);
517 } else if (option == ImageFormat) {
518 if (state == Error)
519 return QVariant();
520 if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
521 return QVariant();
523 switch (type) {
524 case '1': // ascii PBM
525 case '4': // raw PBM
527 break;
528 case '2': // ascii PGM
529 case '5': // raw PGM
531 break;
532 case '3': // ascii PPM
533 case '6': // raw PPM
535 break;
536 default:
537 break;
538 }
539 return format;
540 }
541 return QVariant();
542}
543
545{
546 if (option == SubType)
547 subType = value.toByteArray().toLower();
548}
549
551
552#endif // QT_NO_IMAGEFORMAT_PPM
IOBluetoothDevice * device
\inmodule QtCore
Definition qbytearray.h:57
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
QByteArray toLower() const &
Definition qbytearray.h:190
QByteArray left(qsizetype len) const
Returns a byte array that contains the first len bytes of this byte array.
\inmodule QtCore \reentrant
Definition qiodevice.h:34
qint64 peek(char *data, qint64 maxlen)
ImageOption
This enum describes the different options supported by QImageIOHandler.
static bool allocateImage(QSize size, QImage::Format format, QImage *image)
QByteArray format() const
Returns the format that is currently assigned to QImageIOHandler.
QIODevice * device() const
Returns the device currently assigned to the QImageIOHandler.
void setFormat(const QByteArray &format)
Sets the format of the QImageIOHandler to format.
\inmodule QtGui
Definition qimage.h:37
uchar * scanLine(int)
Returns a pointer to the pixel data at the scanline with index i.
Definition qimage.cpp:1615
void setColorCount(int)
Definition qimage.cpp:2116
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_RGB32
Definition qimage.h:46
@ Format_Invalid
Definition qimage.h:42
@ Format_MonoLSB
Definition qimage.h:44
@ Format_Mono
Definition qimage.h:43
@ Format_Indexed8
Definition qimage.h:45
@ Format_ARGB32
Definition qimage.h:47
@ Format_Grayscale8
Definition qimage.h:66
void setColor(int i, QRgb c)
Sets the color at the given index in the color table, to the given to colorValue.
Definition qimage.cpp:1573
Definition qlist.h:74
void setOption(ImageOption option, const QVariant &value) override
Sets the option option with the value value.
bool write(const QImage &image) override
Writes the image image to the assigned device.
bool canRead() const override
Returns true if an image can be read from the device (i.e., the image format is supported,...
bool read(QImage *image) override
Read an image from the device, and stores it in image.
QVariant option(ImageOption option) const override
Returns the value assigned to option as a QVariant.
bool supportsOption(ImageOption option) const override
Returns true if the QImageIOHandler supports the option option; otherwise returns false.
static constexpr QRgba64 fromRgba64(quint64 c)
Definition qrgba64.h:36
constexpr uint toArgb32() const
Definition qrgba64.h:83
\inmodule QtCore
Definition qsize.h:25
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
QString & insert(qsizetype i, QChar c)
Definition qstring.cpp:3110
QString & append(QChar c)
Definition qstring.cpp:3227
\inmodule QtCore
Definition qvariant.h:64
QString str
[2]
else opt state
[0]
Combined button and popup list for selecting options.
constexpr bool isAsciiDigit(char32_t c) noexcept
Definition qtools_p.h:67
Definition image.cpp:4
#define rgb(r, g, b)
Definition qcolor.cpp:124
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
constexpr bool ascii_isspace(uchar c)
Definition qlocale_p.h:539
#define qCWarning(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
GLboolean GLboolean GLboolean b
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLboolean r
[2]
GLuint GLuint end
GLint GLsizei width
GLenum type
GLenum GLuint GLenum GLsizei const GLchar * buf
GLboolean GLboolean g
GLfloat n
GLint GLsizei GLsizei GLenum format
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLuint res
const GLubyte * c
GLuint GLfloat * val
GLfloat GLfloat p
[1]
GLuint GLenum option
static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, const QByteArray &sourceFormat)
static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, QImage *outImage)
static QRgb scale_pbm_color(quint16 mx, quint16 rv, quint16 gv, quint16 bv)
static bool read_pbm_header(QIODevice *device, char &type, int &w, int &h, int &mcc)
static int read_pbm_int(QIODevice *d, bool *ok)
static QT_BEGIN_NAMESPACE void discard_pbm_line(QIODevice *d)
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
constexpr QRgb qRgb(int r, int g, int b)
Definition qrgb.h:30
constexpr int qRed(QRgb rgb)
Definition qrgb.h:18
constexpr int qGreen(QRgb rgb)
Definition qrgb.h:21
constexpr int qGray(int r, int g, int b)
Definition qrgb.h:36
constexpr int qBlue(QRgb rgb)
Definition qrgb.h:24
unsigned char uchar
Definition qtypes.h:27
unsigned short quint16
Definition qtypes.h:43
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
QTextStream out(stdout)
[7]