Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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, int maxDigits = -1)
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 if (maxDigits > 0 && --maxDigits == 0)
54 break;
55 continue;
56 } else {
57 if (c == '#') // comment
59 break;
60 }
61 }
62 if (digit) // first digit
63 val = c - '0';
64 else if (ascii_isspace(c))
65 continue;
66 else if (c == '#')
68 else
69 break;
70 if (maxDigits > 0 && --maxDigits == 0)
71 break;
72 }
73 if (val < 0)
74 *ok = false;
75 return hasOverflow ? -1 : val;
76}
77
78static bool read_pbm_header(QIODevice *device, char& type, int& w, int& h, int& mcc)
79{
80 char buf[3];
81 if (device->read(buf, 3) != 3) // read P[1-6]<white-space>
82 return false;
83
84 if (!(buf[0] == 'P' && QtMiscUtils::isAsciiDigit(buf[1]) && ascii_isspace(buf[2])))
85 return false;
86
87 type = buf[1];
88 if (type < '1' || type > '6')
89 return false;
90
91 bool ok = true;
92 w = read_pbm_int(device, &ok); // get image width
93 h = read_pbm_int(device, &ok); // get image height
94
95 if (type == '1' || type == '4')
96 mcc = 1; // ignore max color component
97 else
98 mcc = read_pbm_int(device, &ok); // get max color component
99
100 if (!ok || w <= 0 || w > 32767 || h <= 0 || h > 32767 || mcc <= 0 || mcc > 0xffff)
101 return false; // weird P.M image
102
103 return true;
104}
105
106static inline QRgb scale_pbm_color(quint16 mx, quint16 rv, quint16 gv, quint16 bv)
107{
108 return QRgba64::fromRgba64((rv * 0xffffu) / mx, (gv * 0xffffu) / mx, (bv * 0xffffu) / mx, 0xffff).toArgb32();
109}
110
111static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, QImage *outImage)
112{
113 int nbits, y;
114 qsizetype pbm_bpl;
115 bool raw;
116
118 switch (type) {
119 case '1': // ascii PBM
120 case '4': // raw PBM
121 nbits = 1;
123 break;
124 case '2': // ascii PGM
125 case '5': // raw PGM
126 nbits = 8;
128 break;
129 case '3': // ascii PPM
130 case '6': // raw PPM
131 nbits = 32;
133 break;
134 default:
135 return false;
136 }
137 raw = type >= '4';
138
139 if (!QImageIOHandler::allocateImage(QSize(w, h), format, outImage))
140 return false;
141
142 pbm_bpl = (qsizetype(w) * nbits + 7) / 8; // bytes per scanline in PBM
143
144 if (raw) { // read raw data
145 if (nbits == 32) { // type 6
146 pbm_bpl = mcc < 256 ? 3*w : 6*w;
147 uchar *buf24 = new uchar[pbm_bpl], *b;
148 QRgb *p;
149 QRgb *end;
150 for (y=0; y<h; y++) {
151 if (device->read((char *)buf24, pbm_bpl) != pbm_bpl) {
152 delete[] buf24;
153 return false;
154 }
155 p = (QRgb *)outImage->scanLine(y);
156 end = p + w;
157 b = buf24;
158 while (p < end) {
159 if (mcc < 256) {
160 if (mcc == 255)
161 *p++ = qRgb(b[0],b[1],b[2]);
162 else
163 *p++ = scale_pbm_color(mcc, b[0], b[1], b[2]);
164 b += 3;
165 } else {
166 quint16 rv = b[0] << 8 | b[1];
167 quint16 gv = b[2] << 8 | b[3];
168 quint16 bv = b[4] << 8 | b[5];
169 if (mcc == 0xffff)
170 *p++ = QRgba64::fromRgba64(rv, gv, bv, 0xffff).toArgb32();
171 else
172 *p++ = scale_pbm_color(mcc, rv, gv, bv);
173 b += 6;
174 }
175 }
176 }
177 delete[] buf24;
178 } else if (nbits == 8 && mcc > 255) { // type 5 16bit
179 pbm_bpl = 2*w;
180 uchar *buf16 = new uchar[pbm_bpl];
181 for (y=0; y<h; y++) {
182 if (device->read((char *)buf16, pbm_bpl) != pbm_bpl) {
183 delete[] buf16;
184 return false;
185 }
186 uchar *p = outImage->scanLine(y);
187 uchar *end = p + w;
188 uchar *b = buf16;
189 while (p < end) {
190 *p++ = (b[0] << 8 | b[1]) * 255 / mcc;
191 b += 2;
192 }
193 }
194 delete[] buf16;
195 } else { // type 4,5
196 for (y=0; y<h; y++) {
197 uchar *p = outImage->scanLine(y);
198 if (device->read((char *)p, pbm_bpl) != pbm_bpl)
199 return false;
200 if (nbits == 8 && mcc < 255) {
201 for (qsizetype i = 0; i < pbm_bpl; i++)
202 p[i] = (p[i] * 255) / mcc;
203 }
204 }
205 }
206 } else { // read ascii data
207 uchar *p;
208 qsizetype n;
209 bool ok = true;
210 for (y = 0; y < h && ok; y++) {
211 p = outImage->scanLine(y);
212 n = pbm_bpl;
213 if (nbits == 1) {
214 int b;
215 int bitsLeft = w;
216 while (n-- && ok) {
217 b = 0;
218 for (int i=0; i<8; i++) {
219 if (i < bitsLeft)
220 b = (b << 1) | (read_pbm_int(device, &ok, 1) & 1);
221 else
222 b = (b << 1) | (0 & 1); // pad it our self if we need to
223 }
224 bitsLeft -= 8;
225 *p++ = b;
226 }
227 } else if (nbits == 8) {
228 if (mcc == 255) {
229 while (n-- && ok) {
230 *p++ = read_pbm_int(device, &ok);
231 }
232 } else {
233 while (n-- && ok) {
234 *p++ = (read_pbm_int(device, &ok) & 0xffff) * 255 / mcc;
235 }
236 }
237 } else { // 32 bits
238 n /= 4;
239 int r, g, b;
240 if (mcc == 255) {
241 while (n-- && ok) {
242 r = read_pbm_int(device, &ok);
243 g = read_pbm_int(device, &ok);
244 b = read_pbm_int(device, &ok);
245 *((QRgb*)p) = qRgb(r, g, b);
246 p += 4;
247 }
248 } else {
249 while (n-- && ok) {
250 r = read_pbm_int(device, &ok);
251 g = read_pbm_int(device, &ok);
252 b = read_pbm_int(device, &ok);
253 *((QRgb*)p) = scale_pbm_color(mcc, r, g, b);
254 p += 4;
255 }
256 }
257 }
258 }
259 if (!ok)
260 return false;
261 }
262
264 outImage->setColorCount(2);
265 outImage->setColor(0, qRgb(255,255,255)); // white
266 outImage->setColor(1, qRgb(0,0,0)); // black
267 }
268
269 return true;
270}
271
272static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, QByteArrayView sourceFormat)
273{
275 QImage image = sourceImage;
276 const QByteArrayView format = sourceFormat.left(3); // ignore RAW part
277
278 bool gray = format == "pgm";
279
280 if (format == "pbm") {
281 image = image.convertToFormat(QImage::Format_Mono);
282 } else if (gray) {
283 image = image.convertToFormat(QImage::Format_Grayscale8);
284 } else {
285 switch (image.format()) {
288 image = image.convertToFormat(QImage::Format_Indexed8);
289 break;
293 break;
294 default:
295 if (image.hasAlphaChannel())
296 image = image.convertToFormat(QImage::Format_ARGB32);
297 else
298 image = image.convertToFormat(QImage::Format_RGB32);
299 break;
300 }
301 }
302
303 if (image.depth() == 1 && image.colorCount() == 2) {
304 if (qGray(image.color(0)) < qGray(image.color(1))) {
305 // 0=dark/black, 1=light/white - invert
306 image.detach();
307 for (int y=0; y<image.height(); y++) {
308 uchar *p = image.scanLine(y);
309 uchar *end = p + image.bytesPerLine();
310 while (p < end)
311 *p++ ^= 0xff;
312 }
313 }
314 }
315
316 uint w = image.width();
317 uint h = image.height();
318
319 str = "P\n";
321 str += ' ';
323 str += '\n';
324
325 switch (image.depth()) {
326 case 1: {
327 str.insert(1, '4');
328 if (out->write(str, str.size()) != str.size())
329 return false;
330 w = (w+7)/8;
331 for (uint y=0; y<h; y++) {
332 uchar* line = image.scanLine(y);
333 if (w != (uint)out->write((char*)line, w))
334 return false;
335 }
336 }
337 break;
338
339 case 8: {
340 str.insert(1, gray ? '5' : '6');
341 str.append("255\n");
342 if (out->write(str, str.size()) != str.size())
343 return false;
344 qsizetype bpl = qsizetype(w) * (gray ? 1 : 3);
345 uchar *buf = new uchar[bpl];
346 if (image.format() == QImage::Format_Indexed8) {
347 const QList<QRgb> color = image.colorTable();
348 for (uint y=0; y<h; y++) {
349 const uchar *b = image.constScanLine(y);
350 uchar *p = buf;
351 uchar *end = buf+bpl;
352 if (gray) {
353 while (p < end) {
354 uchar g = (uchar)qGray(color[*b++]);
355 *p++ = g;
356 }
357 } else {
358 while (p < end) {
359 QRgb rgb = color[*b++];
360 *p++ = qRed(rgb);
361 *p++ = qGreen(rgb);
362 *p++ = qBlue(rgb);
363 }
364 }
365 if (bpl != (qsizetype)out->write((char*)buf, bpl))
366 return false;
367 }
368 } else {
369 for (uint y=0; y<h; y++) {
370 const uchar *b = image.constScanLine(y);
371 uchar *p = buf;
372 uchar *end = buf + bpl;
373 if (gray) {
374 while (p < end)
375 *p++ = *b++;
376 } else {
377 while (p < end) {
378 uchar color = *b++;
379 *p++ = color;
380 *p++ = color;
381 *p++ = color;
382 }
383 }
384 if (bpl != (qsizetype)out->write((char*)buf, bpl))
385 return false;
386 }
387 }
388 delete [] buf;
389 break;
390 }
391
392 case 32: {
393 str.insert(1, '6');
394 str.append("255\n");
395 if (out->write(str, str.size()) != str.size())
396 return false;
397 qsizetype bpl = qsizetype(w) * 3;
398 uchar *buf = new uchar[bpl];
399 for (uint y=0; y<h; y++) {
400 const QRgb *b = reinterpret_cast<const QRgb *>(image.constScanLine(y));
401 uchar *p = buf;
402 uchar *end = buf+bpl;
403 while (p < end) {
404 QRgb rgb = *b++;
405 *p++ = qRed(rgb);
406 *p++ = qGreen(rgb);
407 *p++ = qBlue(rgb);
408 }
409 if (bpl != (qsizetype)out->write((char*)buf, bpl))
410 return false;
411 }
412 delete [] buf;
413 break;
414 }
415
416 default:
417 return false;
418 }
419
420 return true;
421}
422
424 : state(Ready)
425{
426}
427
428bool QPpmHandler::readHeader()
429{
430 state = Error;
431 if (!read_pbm_header(device(), type, width, height, mcc))
432 return false;
433 state = ReadHeader;
434 return true;
435}
436
438{
439 if (state == Ready && !canRead(device(), &subType))
440 return false;
441
442 if (state != Error) {
443 setFormat(subType);
444 return true;
445 }
446
447 return false;
448}
449
451{
452 if (!device) {
453 qCWarning(lcImageIo, "QPpmHandler::canRead() called with no device");
454 return false;
455 }
456
457 char head[2];
458 if (device->peek(head, sizeof(head)) != sizeof(head))
459 return false;
460
461 if (head[0] != 'P')
462 return false;
463
464 if (head[1] == '1' || head[1] == '4') {
465 if (subType)
466 *subType = "pbm";
467 } else if (head[1] == '2' || head[1] == '5') {
468 if (subType)
469 *subType = "pgm";
470 } else if (head[1] == '3' || head[1] == '6') {
471 if (subType)
472 *subType = "ppm";
473 } else {
474 return false;
475 }
476 return true;
477}
478
480{
481 if (state == Error)
482 return false;
483
484 if (state == Ready && !readHeader()) {
485 state = Error;
486 return false;
487 }
488
489 if (!read_pbm_body(device(), type, width, height, mcc, image)) {
490 state = Error;
491 return false;
492 }
493
494 state = Ready;
495 return true;
496}
497
499{
500 return write_pbm_image(device(), image, subType);
501}
502
504{
505 return option == SubType
506 || option == Size
507 || option == ImageFormat;
508}
509
511{
512 if (option == SubType) {
513 return subType;
514 } else if (option == Size) {
515 if (state == Error)
516 return QVariant();
517 if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
518 return QVariant();
519 return QSize(width, height);
520 } else if (option == ImageFormat) {
521 if (state == Error)
522 return QVariant();
523 if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
524 return QVariant();
526 switch (type) {
527 case '1': // ascii PBM
528 case '4': // raw PBM
530 break;
531 case '2': // ascii PGM
532 case '5': // raw PGM
534 break;
535 case '3': // ascii PPM
536 case '6': // raw PPM
538 break;
539 default:
540 break;
541 }
542 return format;
543 }
544 return QVariant();
545}
546
548{
549 if (option == SubType)
550 subType = value.toByteArray().toLower();
551}
552
554
555#endif // QT_NO_IMAGEFORMAT_PPM
IOBluetoothDevice * device
constexpr QByteArrayView left(qsizetype n) const
\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:254
\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
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 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
\inmodule QtCore
Definition qsize.h:25
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
QString & insert(qsizetype i, QChar c)
Definition qstring.cpp:3132
QString & append(QChar c)
Definition qstring.cpp:3252
\inmodule QtCore
Definition qvariant.h:65
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:556
#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
GLuint color
[2]
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 int read_pbm_int(QIODevice *d, bool *ok, int maxDigits=-1)
static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, QByteArrayView 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 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:32
unsigned short quint16
Definition qtypes.h:48
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
QTextStream out(stdout)
[7]
manager head(request, this, [this](QRestReply &reply) { if(reply.isSuccess()) })
[6]