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
qjpeghandler.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 "qjpeghandler_p.h"
5
6#include <qbuffer.h>
7#include <qcolorspace.h>
8#include <qcolortransform.h>
9#include <qdebug.h>
10#include <qimage.h>
11#include <qlist.h>
12#include <qloggingcategory.h>
13#include <qmath.h>
14#include <qvariant.h>
15#include <private/qicc_p.h>
16#include <private/qsimd_p.h>
17#include <private/qimage_p.h> // for qt_getImageText
18
19#include <stdio.h> // jpeglib needs this to be pre-included
20#include <setjmp.h>
21
22#ifdef FAR
23#undef FAR
24#endif
25
26// including jpeglib.h seems to be a little messy
27extern "C" {
28#define XMD_H // shut JPEGlib up
29#include <jpeglib.h>
30#ifdef const
31# undef const // remove crazy C hackery in jconfig.h
32#endif
33}
34
36
37Q_LOGGING_CATEGORY(lcJpeg, "qt.gui.imageio.jpeg")
38
39QT_WARNING_DISABLE_GCC("-Wclobbered")
40
41Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32(quint32 *dst, const uchar *src, int len);
42typedef void (QT_FASTCALL *Rgb888ToRgb32Converter)(quint32 *dst, const uchar *src, int len);
43
44struct my_error_mgr : public jpeg_error_mgr {
46};
47
48extern "C" {
49
50static void my_error_exit (j_common_ptr cinfo)
51{
52 (*cinfo->err->output_message)(cinfo);
53 my_error_mgr* myerr = (my_error_mgr*) cinfo->err;
54 longjmp(myerr->setjmp_buffer, 1);
55}
56
57static void my_output_message(j_common_ptr cinfo)
58{
59 char buffer[JMSG_LENGTH_MAX];
60 (*cinfo->err->format_message)(cinfo, buffer);
61 qCWarning(lcJpeg,"%s", buffer);
62}
63
64}
65
66
67static const int max_buf = 4096;
68
69struct my_jpeg_source_mgr : public jpeg_source_mgr {
70 // Nothing dynamic - cannot rely on destruction over longjump
72 JOCTET buffer[max_buf];
74
75public:
77};
78
79extern "C" {
80
81static void qt_init_source(j_decompress_ptr)
82{
83}
84
85static boolean qt_fill_input_buffer(j_decompress_ptr cinfo)
86{
88 qint64 num_read = 0;
89 if (src->memDevice) {
90 src->next_input_byte = (const JOCTET *)(src->memDevice->data().constData() + src->memDevice->pos());
91 num_read = src->memDevice->data().size() - src->memDevice->pos();
92 src->device->seek(src->memDevice->data().size());
93 } else {
94 src->next_input_byte = src->buffer;
95 num_read = src->device->read((char*)src->buffer, max_buf);
96 }
97 if (num_read <= 0) {
98 // Insert a fake EOI marker - as per jpeglib recommendation
99 src->next_input_byte = src->buffer;
100 src->buffer[0] = (JOCTET) 0xFF;
101 src->buffer[1] = (JOCTET) JPEG_EOI;
102 src->bytes_in_buffer = 2;
103 } else {
104 src->bytes_in_buffer = num_read;
105 }
106 return TRUE;
107}
108
109static void qt_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
110{
112
113 // `dumb' implementation from jpeglib
114
115 /* Just a dumb implementation for now. Could use fseek() except
116 * it doesn't work on pipes. Not clear that being smart is worth
117 * any trouble anyway --- large skips are infrequent.
118 */
119 if (num_bytes > 0) {
120 while (num_bytes > (long) src->bytes_in_buffer) { // Should not happen in case of memDevice
121 num_bytes -= (long) src->bytes_in_buffer;
122 (void) qt_fill_input_buffer(cinfo);
123 /* note we assume that qt_fill_input_buffer will never return false,
124 * so suspension need not be handled.
125 */
126 }
127 src->next_input_byte += (size_t) num_bytes;
128 src->bytes_in_buffer -= (size_t) num_bytes;
129 }
130}
131
132static void qt_term_source(j_decompress_ptr cinfo)
133{
135 if (!src->device->isSequential())
136 src->device->seek(src->device->pos() - src->bytes_in_buffer);
137}
138
139}
140
142{
143 jpeg_source_mgr::init_source = qt_init_source;
144 jpeg_source_mgr::fill_input_buffer = qt_fill_input_buffer;
145 jpeg_source_mgr::skip_input_data = qt_skip_input_data;
146 jpeg_source_mgr::resync_to_restart = jpeg_resync_to_restart;
147 jpeg_source_mgr::term_source = qt_term_source;
148 this->device = device;
149 memDevice = qobject_cast<QBuffer *>(device);
150 bytes_in_buffer = 0;
151 next_input_byte = buffer;
152}
153
154
155inline static bool read_jpeg_size(int &w, int &h, j_decompress_ptr cinfo)
156{
157 (void) jpeg_calc_output_dimensions(cinfo);
158
159 w = cinfo->output_width;
160 h = cinfo->output_height;
161 return true;
162}
163
164#define HIGH_QUALITY_THRESHOLD 50
165
166inline static bool read_jpeg_format(QImage::Format &format, j_decompress_ptr cinfo)
167{
168
169 bool result = true;
170 switch (cinfo->output_components) {
171 case 1:
173 break;
174 case 3:
176 break;
177 case 4:
178 if (cinfo->out_color_space == JCS_CMYK)
180 else
182 break;
183 default:
184 result = false;
185 break;
186 }
187 cinfo->output_scanline = cinfo->output_height;
188 return result;
189}
190
191static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info,
192 const QSize& size)
193{
195 switch (info->output_components) {
196 case 1:
198 break;
199 case 3:
201 break;
202 case 4:
203 if (info->out_color_space == JCS_CMYK)
205 else
207 break;
208 default:
209 return false; // unsupported format
210 }
211
213}
214
215static bool read_jpeg_image(QImage *outImage,
216 QSize scaledSize, QRect scaledClipRect,
217 QRect clipRect, int quality,
218 Rgb888ToRgb32Converter converter,
219 j_decompress_ptr info, struct my_error_mgr* err, bool invertCMYK)
220{
221 if (!setjmp(err->setjmp_buffer)) {
222 // -1 means default quality.
223 if (quality < 0)
224 quality = 75;
225
226 // If possible, merge the scaledClipRect into either scaledSize
227 // or clipRect to avoid doing a separate scaled clipping pass.
228 // Best results are achieved by clipping before scaling, not after.
229 if (!scaledClipRect.isEmpty()) {
230 if (scaledSize.isEmpty() && clipRect.isEmpty()) {
231 // No clipping or scaling before final clip.
232 clipRect = scaledClipRect;
233 scaledClipRect = QRect();
234 } else if (scaledSize.isEmpty()) {
235 // Clipping, but no scaling: combine the clip regions.
236 scaledClipRect.translate(clipRect.topLeft());
237 clipRect = scaledClipRect.intersected(clipRect);
238 scaledClipRect = QRect();
239 } else if (clipRect.isEmpty()) {
240 // No clipping, but scaling: if we can map back to an
241 // integer pixel boundary, then clip before scaling.
242 if ((info->image_width % scaledSize.width()) == 0 &&
243 (info->image_height % scaledSize.height()) == 0) {
244 int x = scaledClipRect.x() * info->image_width /
245 scaledSize.width();
246 int y = scaledClipRect.y() * info->image_height /
247 scaledSize.height();
248 int width = (scaledClipRect.right() + 1) *
249 info->image_width / scaledSize.width() - x;
250 int height = (scaledClipRect.bottom() + 1) *
251 info->image_height / scaledSize.height() - y;
252 clipRect = QRect(x, y, width, height);
253 scaledSize = scaledClipRect.size();
254 scaledClipRect = QRect();
255 }
256 } else {
257 // Clipping and scaling: too difficult to figure out,
258 // and not a likely use case, so do it the long way.
259 }
260 }
261
262 // Determine the scale factor to pass to libjpeg for quick downscaling.
263 if (!scaledSize.isEmpty() && info->image_width && info->image_height) {
264 if (clipRect.isEmpty()) {
265 double f = qMin(double(info->image_width) / scaledSize.width(),
266 double(info->image_height) / scaledSize.height());
267
268 // libjpeg supports M/8 scaling with M=[1,16]. All downscaling factors
269 // are a speed improvement, but upscaling during decode is slower.
270 info->scale_num = qBound(1, qCeil(8/f), 8);
271 info->scale_denom = 8;
272 } else {
273 info->scale_denom = qMin(clipRect.width() / scaledSize.width(),
274 clipRect.height() / scaledSize.height());
275
276 // Only scale by powers of two when clipping so we can
277 // keep the exact pixel boundaries
278 if (info->scale_denom < 2)
279 info->scale_denom = 1;
280 else if (info->scale_denom < 4)
281 info->scale_denom = 2;
282 else if (info->scale_denom < 8)
283 info->scale_denom = 4;
284 else
285 info->scale_denom = 8;
286 info->scale_num = 1;
287
288 // Correct the scale factor so that we clip accurately.
289 // It is recommended that the clip rectangle be aligned
290 // on an 8-pixel boundary for best performance.
291 while (info->scale_denom > 1 &&
292 ((clipRect.x() % info->scale_denom) != 0 ||
293 (clipRect.y() % info->scale_denom) != 0 ||
294 (clipRect.width() % info->scale_denom) != 0 ||
295 (clipRect.height() % info->scale_denom) != 0)) {
296 info->scale_denom /= 2;
297 }
298 }
299 }
300
301 // If high quality not required, use fast decompression
302 if ( quality < HIGH_QUALITY_THRESHOLD ) {
303 info->dct_method = JDCT_IFAST;
304 info->do_fancy_upsampling = FALSE;
305 }
306
307 (void) jpeg_calc_output_dimensions(info);
308
309 // Determine the clip region to extract.
310 QRect imageRect(0, 0, info->output_width, info->output_height);
311 QRect clip;
312 if (clipRect.isEmpty()) {
313 clip = imageRect;
314 } else if (info->scale_denom == info->scale_num) {
315 clip = clipRect.intersected(imageRect);
316 } else {
317 // The scale factor was corrected above to ensure that
318 // we don't miss pixels when we scale the clip rectangle.
319 clip = QRect(clipRect.x() / int(info->scale_denom),
320 clipRect.y() / int(info->scale_denom),
321 clipRect.width() / int(info->scale_denom),
322 clipRect.height() / int(info->scale_denom));
323 clip = clip.intersected(imageRect);
324 }
325
326 // Allocate memory for the clipped QImage.
327 if (!ensureValidImage(outImage, info, clip.size()))
328 return false;
329
330 // Avoid memcpy() overhead if grayscale with no clipping.
331 bool quickGray = (info->output_components == 1 &&
332 clip == imageRect);
333 if (!quickGray) {
334 // Ask the jpeg library to allocate a temporary row.
335 // The library will automatically delete it for us later.
336 // The libjpeg docs say we should do this before calling
337 // jpeg_start_decompress(). We can't use "new" here
338 // because we are inside the setjmp() block and an error
339 // in the jpeg input stream would cause a memory leak.
340 JSAMPARRAY rows = (info->mem->alloc_sarray)
341 ((j_common_ptr)info, JPOOL_IMAGE,
342 info->output_width * info->output_components, 1);
343
344 (void) jpeg_start_decompress(info);
345
346 while (info->output_scanline < info->output_height) {
347 int y = int(info->output_scanline) - clip.y();
348 if (y >= clip.height())
349 break; // We've read the entire clip region, so abort.
350
351 (void) jpeg_read_scanlines(info, rows, 1);
352
353 if (y < 0)
354 continue; // Haven't reached the starting line yet.
355
356 if (info->output_components == 3) {
357 uchar *in = rows[0] + clip.x() * 3;
358 QRgb *out = (QRgb*)outImage->scanLine(y);
359 converter(out, in, clip.width());
360 } else if (info->out_color_space == JCS_CMYK) {
361 uchar *in = rows[0] + clip.x() * 4;
362 quint32 *out = (quint32*)outImage->scanLine(y);
363 if (invertCMYK) {
364 for (int i = 0; i < clip.width(); ++i) {
365 *out++ = 0xffffffffu - (in[0] | in[1] << 8 | in[2] << 16 | in[3] << 24);
366 in += 4;
367 }
368 } else {
369 memcpy(out, in, clip.width() * 4);
370 }
371 } else if (info->output_components == 1) {
372 // Grayscale.
373 memcpy(outImage->scanLine(y),
374 rows[0] + clip.x(), clip.width());
375 }
376 }
377 } else {
378 // Load unclipped grayscale data directly into the QImage.
379 (void) jpeg_start_decompress(info);
380 while (info->output_scanline < info->output_height) {
381 uchar *row = outImage->scanLine(info->output_scanline);
382 (void) jpeg_read_scanlines(info, &row, 1);
383 }
384 }
385
386 if (info->output_scanline == info->output_height)
387 (void) jpeg_finish_decompress(info);
388
389 if (info->density_unit == 1) {
390 outImage->setDotsPerMeterX(int(100. * info->X_density / 2.54));
391 outImage->setDotsPerMeterY(int(100. * info->Y_density / 2.54));
392 } else if (info->density_unit == 2) {
393 outImage->setDotsPerMeterX(int(100. * info->X_density));
394 outImage->setDotsPerMeterY(int(100. * info->Y_density));
395 }
396
397 if (scaledSize.isValid() && scaledSize != clip.size()) {
398 *outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, quality >= HIGH_QUALITY_THRESHOLD ? Qt::SmoothTransformation : Qt::FastTransformation);
399 }
400
401 if (!scaledClipRect.isEmpty())
402 *outImage = outImage->copy(scaledClipRect);
403 return !outImage->isNull();
404 }
405 else {
406 my_output_message(j_common_ptr(info));
407 return false;
408 }
409}
410
411struct my_jpeg_destination_mgr : public jpeg_destination_mgr {
412 // Nothing dynamic - cannot rely on destruction over longjump
415
416public:
418};
419
420
421extern "C" {
422
423static void qt_init_destination(j_compress_ptr)
424{
425}
426
427static boolean qt_empty_output_buffer(j_compress_ptr cinfo)
428{
430
431 int written = dest->device->write((char*)dest->buffer, max_buf);
432 if (written == -1)
433 (*cinfo->err->error_exit)((j_common_ptr)cinfo);
434
435 dest->next_output_byte = dest->buffer;
436 dest->free_in_buffer = max_buf;
437
438 return TRUE;
439}
440
441static void qt_term_destination(j_compress_ptr cinfo)
442{
444 qint64 n = max_buf - dest->free_in_buffer;
445
446 qint64 written = dest->device->write((char*)dest->buffer, n);
447 if (written == -1)
448 (*cinfo->err->error_exit)((j_common_ptr)cinfo);
449}
450
451}
452
454{
455 jpeg_destination_mgr::init_destination = qt_init_destination;
456 jpeg_destination_mgr::empty_output_buffer = qt_empty_output_buffer;
457 jpeg_destination_mgr::term_destination = qt_term_destination;
458 this->device = device;
459 next_output_byte = buffer;
460 free_in_buffer = max_buf;
461}
462
463static constexpr int maxMarkerSize = 65533;
464
465static inline void set_text(const QImage &image, j_compress_ptr cinfo, const QString &description)
466{
467 const QMap<QString, QString> text = qt_getImageText(image, description);
468 for (auto it = text.begin(), end = text.end(); it != end; ++it) {
469 QByteArray comment = it.key().toUtf8();
470 if (!comment.isEmpty())
471 comment += ": ";
472 comment += it.value().toUtf8();
473 if (comment.size() > maxMarkerSize)
474 comment.truncate(maxMarkerSize);
475 jpeg_write_marker(cinfo, JPEG_COM, (const JOCTET *)comment.constData(), comment.size());
476 }
477}
478
479static inline void write_icc_profile(const QImage &image, j_compress_ptr cinfo)
480{
481 const QByteArray iccProfile = image.colorSpace().iccProfile();
482 if (iccProfile.isEmpty())
483 return;
484
485 const QByteArray iccSignature("ICC_PROFILE", 12);
486 constexpr int maxIccMarkerSize = maxMarkerSize - (12 + 2);
487 int index = 0;
488 const int markers = (iccProfile.size() + (maxIccMarkerSize - 1)) / maxIccMarkerSize;
489 Q_ASSERT(markers < 256);
490 for (int marker = 1; marker <= markers; ++marker) {
491 const int len = qMin(iccProfile.size() - index, maxIccMarkerSize);
492 const QByteArray block = iccSignature
493 + QByteArray(1, char(marker)) + QByteArray(1, char(markers))
494 + iccProfile.mid(index, len);
495 jpeg_write_marker(cinfo, JPEG_APP0 + 2, reinterpret_cast<const JOCTET *>(block.constData()), block.size());
496 index += len;
497 }
498}
499
500static bool do_write_jpeg_image(struct jpeg_compress_struct &cinfo,
501 JSAMPROW *row_pointer,
502 const QImage &image,
504 int sourceQuality,
505 const QString &description,
506 bool optimize,
507 bool progressive,
508 bool invertCMYK)
509{
510 bool success = false;
511 const QList<QRgb> cmap = image.colorTable();
512
513 if (image.format() == QImage::Format_Invalid || image.format() == QImage::Format_Alpha8)
514 return false;
515
517 struct my_error_mgr jerr;
518
519 cinfo.err = jpeg_std_error(&jerr);
520 jerr.error_exit = my_error_exit;
521 jerr.output_message = my_output_message;
522
523 if (!setjmp(jerr.setjmp_buffer)) {
524 // WARNING:
525 // this if loop is inside a setjmp/longjmp branch
526 // do not create C++ temporaries here because the destructor may never be called
527 // if you allocate memory, make sure that you can free it (row_pointer[0])
528 jpeg_create_compress(&cinfo);
529
530 cinfo.dest = iod_dest;
531
532 cinfo.image_width = image.width();
533 cinfo.image_height = image.height();
534
535 bool gray = false;
536 switch (image.format()) {
540 gray = true;
541 for (int i = image.colorCount(); gray && i; i--) {
542 gray = gray & qIsGray(cmap[i-1]);
543 }
544 cinfo.input_components = gray ? 1 : 3;
545 cinfo.in_color_space = gray ? JCS_GRAYSCALE : JCS_RGB;
546 break;
549 gray = true;
550 cinfo.input_components = 1;
551 cinfo.in_color_space = JCS_GRAYSCALE;
552 break;
554 cinfo.input_components = 4;
555 cinfo.in_color_space = JCS_CMYK;
556 break;
557 default:
558 cinfo.input_components = 3;
559 cinfo.in_color_space = JCS_RGB;
560 }
561
562 jpeg_set_defaults(&cinfo);
563
564 qreal diffInch = qAbs(image.dotsPerMeterX()*2.54/100. - qRound(image.dotsPerMeterX()*2.54/100.))
565 + qAbs(image.dotsPerMeterY()*2.54/100. - qRound(image.dotsPerMeterY()*2.54/100.));
566 qreal diffCm = (qAbs(image.dotsPerMeterX()/100. - qRound(image.dotsPerMeterX()/100.))
567 + qAbs(image.dotsPerMeterY()/100. - qRound(image.dotsPerMeterY()/100.)))*2.54;
568 if (diffInch < diffCm) {
569 cinfo.density_unit = 1; // dots/inch
570 cinfo.X_density = qRound(image.dotsPerMeterX()*2.54/100.);
571 cinfo.Y_density = qRound(image.dotsPerMeterY()*2.54/100.);
572 } else {
573 cinfo.density_unit = 2; // dots/cm
574 cinfo.X_density = (image.dotsPerMeterX()+50) / 100;
575 cinfo.Y_density = (image.dotsPerMeterY()+50) / 100;
576 }
577
578 if (optimize)
579 cinfo.optimize_coding = true;
580
581 if (progressive)
582 jpeg_simple_progression(&cinfo);
583
584 int quality = sourceQuality >= 0 ? qMin(int(sourceQuality),100) : 75;
585 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
586 jpeg_start_compress(&cinfo, TRUE);
587
588 set_text(image, &cinfo, description);
589 if (cinfo.in_color_space == JCS_RGB || cinfo.in_color_space == JCS_CMYK)
590 write_icc_profile(image, &cinfo);
591
592 row_pointer[0] = new uchar[cinfo.image_width*cinfo.input_components];
593 int w = cinfo.image_width;
594 while (cinfo.next_scanline < cinfo.image_height) {
595 uchar *row = row_pointer[0];
596 switch (image.format()) {
599 if (gray) {
600 const uchar* data = image.constScanLine(cinfo.next_scanline);
601 if (image.format() == QImage::Format_MonoLSB) {
602 for (int i=0; i<w; i++) {
603 bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
604 row[i] = qRed(cmap[bit]);
605 }
606 } else {
607 for (int i=0; i<w; i++) {
608 bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
609 row[i] = qRed(cmap[bit]);
610 }
611 }
612 } else {
613 const uchar* data = image.constScanLine(cinfo.next_scanline);
614 if (image.format() == QImage::Format_MonoLSB) {
615 for (int i=0; i<w; i++) {
616 bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
617 *row++ = qRed(cmap[bit]);
618 *row++ = qGreen(cmap[bit]);
619 *row++ = qBlue(cmap[bit]);
620 }
621 } else {
622 for (int i=0; i<w; i++) {
623 bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
624 *row++ = qRed(cmap[bit]);
625 *row++ = qGreen(cmap[bit]);
626 *row++ = qBlue(cmap[bit]);
627 }
628 }
629 }
630 break;
632 if (gray) {
633 const uchar* pix = image.constScanLine(cinfo.next_scanline);
634 for (int i=0; i<w; i++) {
635 *row = qRed(cmap[*pix]);
636 ++row; ++pix;
637 }
638 } else {
639 const uchar* pix = image.constScanLine(cinfo.next_scanline);
640 for (int i=0; i<w; i++) {
641 *row++ = qRed(cmap[*pix]);
642 *row++ = qGreen(cmap[*pix]);
643 *row++ = qBlue(cmap[*pix]);
644 ++pix;
645 }
646 }
647 break;
649 memcpy(row, image.constScanLine(cinfo.next_scanline), w);
650 break;
652 {
653 QImage rowImg = image.copy(0, cinfo.next_scanline, w, 1).convertToFormat(QImage::Format_Grayscale8);
654 memcpy(row, rowImg.constScanLine(0), w);
655 }
656 break;
658 memcpy(row, image.constScanLine(cinfo.next_scanline), w * 3);
659 break;
663 {
664 const QRgb* rgb = (const QRgb*)image.constScanLine(cinfo.next_scanline);
665 for (int i=0; i<w; i++) {
666 *row++ = qRed(*rgb);
667 *row++ = qGreen(*rgb);
668 *row++ = qBlue(*rgb);
669 ++rgb;
670 }
671 }
672 break;
674 auto *cmykIn = reinterpret_cast<const quint32 *>(image.constScanLine(cinfo.next_scanline));
675 auto *cmykOut = reinterpret_cast<quint32 *>(row);
676 if (invertCMYK) {
677 for (int i = 0; i < w; ++i)
678 cmykOut[i] = 0xffffffffu - cmykIn[i];
679 } else {
680 memcpy(cmykOut, cmykIn, w * 4);
681 }
682 break;
683 }
684 default:
685 {
686 // (Testing shows that this way is actually faster than converting to RGB888 + memcpy)
687 QImage rowImg = image.copy(0, cinfo.next_scanline, w, 1).convertToFormat(QImage::Format_RGB32);
688 const QRgb* rgb = (const QRgb*)rowImg.constScanLine(0);
689 for (int i=0; i<w; i++) {
690 *row++ = qRed(*rgb);
691 *row++ = qGreen(*rgb);
692 *row++ = qBlue(*rgb);
693 ++rgb;
694 }
695 }
696 break;
697 }
698 jpeg_write_scanlines(&cinfo, row_pointer, 1);
699 }
700
701 jpeg_finish_compress(&cinfo);
702 jpeg_destroy_compress(&cinfo);
703 success = true;
704 } else {
705 my_output_message(j_common_ptr(&cinfo));
706 jpeg_destroy_compress(&cinfo);
707 success = false;
708 }
709
710 delete iod_dest;
711 return success;
712}
713
714static bool write_jpeg_image(const QImage &image,
716 int sourceQuality,
717 const QString &description,
718 bool optimize,
719 bool progressive,
720 bool invertCMYK)
721{
722 // protect these objects from the setjmp/longjmp pair inside
723 // do_write_jpeg_image (by making them non-local).
724 struct jpeg_compress_struct cinfo;
725 JSAMPROW row_pointer[1];
726 row_pointer[0] = nullptr;
727
728 const bool success = do_write_jpeg_image(cinfo, row_pointer,
729 image, device,
730 sourceQuality, description,
731 optimize, progressive, invertCMYK);
732
733 delete [] row_pointer[0];
734 return success;
735}
736
738{
739public:
746
751
753 {
754 if (iod_src)
755 {
756 jpeg_destroy_decompress(&info);
757 delete iod_src;
758 iod_src = nullptr;
759 }
760 }
761
763 bool read(QImage *image);
764
766 QImageIOHandler::Transformations transformation;
775
776 // Photoshop historically invertes the quantities in CMYK JPEG files:
777 // 0 means 100% ink, 255 means no ink. Every reader does the same,
778 // for compatibility reasons.
779 // Use such an interpretation by default, but also offer the alternative
780 // of not inverting the channels.
781 // This is just a "fancy" API; it could be reduced to a boolean setting
782 // for CMYK files.
783 enum class SubType {
784 Automatic,
786 CMYK,
788 };
790
791 struct jpeg_decompress_struct info;
794
796
798
801
803};
804
805static const char SupportedJPEGSubtypes[][14] = {
806 "Automatic",
807 "Inverted_CMYK",
808 "CMYK"
809};
810
811static_assert(std::size(SupportedJPEGSubtypes) == size_t(QJpegHandlerPrivate::SubType::NSubTypes));
812
814{
815 char prefix[6];
816 if (stream.readRawData(prefix, sizeof(prefix)) != sizeof(prefix))
817 return false;
818 static const char exifMagic[6] = {'E', 'x', 'i', 'f', 0, 0};
819 return memcmp(prefix, exifMagic, 6) == 0;
820}
821
822/*
823 * Returns -1 on error
824 * Returns 0 if no Exif orientation was found
825 * Returns 1 orientation is horizontal (normal)
826 * Returns 2 mirror horizontal
827 * Returns 3 rotate 180
828 * Returns 4 mirror vertical
829 * Returns 5 mirror horizontal and rotate 270 CCW
830 * Returns 6 rotate 90 CW
831 * Returns 7 mirror horizontal and rotate 90 CW
832 * Returns 8 rotate 270 CW
833 */
834static int getExifOrientation(QByteArray &exifData)
835{
836 // Current EXIF version (2.3) says there can be at most 5 IFDs,
837 // byte we allow for 10 so we're able to deal with future extensions.
838 const int maxIfdCount = 10;
839
841
843 return -1;
844
845 quint16 val;
847 const qint64 headerStart = 6; // the EXIF header has a constant size
848 Q_ASSERT(headerStart == stream.device()->pos());
849
850 // read byte order marker
851 stream >> val;
852 if (val == 0x4949) // 'II' == Intel
853 stream.setByteOrder(QDataStream::LittleEndian);
854 else if (val == 0x4d4d) // 'MM' == Motorola
855 stream.setByteOrder(QDataStream::BigEndian);
856 else
857 return -1; // unknown byte order
858
859 // confirm byte order
860 stream >> val;
861 if (val != 0x2a)
862 return -1;
863
864 stream >> offset;
865
866 // read IFD
867 for (int n = 0; n < maxIfdCount; ++n) {
868 quint16 numEntries;
869
870 const qint64 bytesToSkip = offset - (stream.device()->pos() - headerStart);
871 if (bytesToSkip < 0 || (offset + headerStart >= exifData.size())) {
872 // disallow going backwards, though it's permitted in the spec
873 return -1;
874 } else if (bytesToSkip != 0) {
875 // seek to the IFD
876 if (!stream.device()->seek(offset + headerStart))
877 return -1;
878 }
879
880 stream >> numEntries;
881
882 for (; numEntries > 0 && stream.status() == QDataStream::Ok; --numEntries) {
883 quint16 tag;
887 quint16 dummy;
888
889 stream >> tag >> type >> components >> value >> dummy;
890 if (tag == 0x0112) { // Tag Exif.Image.Orientation
891 if (components != 1)
892 return -1;
893 if (type != 3) // we are expecting it to be an unsigned short
894 return -1;
895 if (value < 1 || value > 8) // check for valid range
896 return -1;
897
898 // It is possible to include the orientation multiple times.
899 // Right now the first value is returned.
900 return value;
901 }
902 }
903
904 // read offset to next IFD
905 stream >> offset;
906 if (stream.status() != QDataStream::Ok)
907 return -1;
908 if (offset == 0) // this is the last IFD
909 return 0; // No Exif orientation was found
910 }
911
912 // too many IFDs
913 return -1;
914}
915
916static QImageIOHandler::Transformations exif2Qt(int exifOrientation)
917{
918 switch (exifOrientation) {
919 case 1: // normal
921 case 2: // mirror horizontal
923 case 3: // rotate 180
925 case 4: // mirror vertical
927 case 5: // mirror horizontal and rotate 270 CW
929 case 6: // rotate 90 CW
931 case 7: // mirror horizontal and rotate 90 CW
933 case 8: // rotate 270 CW
935 }
936 qCWarning(lcJpeg, "Invalid EXIF orientation");
938}
939
944{
945 if (state == Ready)
946 {
947 state = Error;
949
950 info.err = jpeg_std_error(&err);
951 err.error_exit = my_error_exit;
952 err.output_message = my_output_message;
953
954 jpeg_create_decompress(&info);
955 info.src = iod_src;
956
957 if (!setjmp(err.setjmp_buffer)) {
958 jpeg_save_markers(&info, JPEG_COM, 0xFFFF);
959 jpeg_save_markers(&info, JPEG_APP0 + 1, 0xFFFF); // Exif uses APP1 marker
960 jpeg_save_markers(&info, JPEG_APP0 + 2, 0xFFFF); // ICC uses APP2 marker
961
962 (void) jpeg_read_header(&info, TRUE);
963
964 int width = 0;
965 int height = 0;
968
971
972 QByteArray exifData;
973
974 for (jpeg_saved_marker_ptr marker = info.marker_list; marker != nullptr; marker = marker->next) {
975 if (marker->marker == JPEG_COM) {
976#ifndef QT_NO_IMAGEIO_TEXT_LOADING
978 QString s = QString::fromUtf8((const char *)marker->data, marker->data_length);
979 int index = s.indexOf(QLatin1String(": "));
980 if (index == -1 || s.indexOf(QLatin1Char(' ')) < index) {
981 key = QLatin1String("Description");
982 value = s;
983 } else {
984 key = s.left(index);
985 value = s.mid(index + 2);
986 }
987 if (!description.isEmpty())
988 description += QLatin1String("\n\n");
989 description += key + QLatin1String(": ") + value.simplified();
990 readTexts.append(key);
991 readTexts.append(value);
992#endif
993 } else if (marker->marker == JPEG_APP0 + 1) {
994 exifData.append((const char*)marker->data, marker->data_length);
995 } else if (marker->marker == JPEG_APP0 + 2) {
996 if (marker->data_length > 128 + 4 + 14 && strcmp((const char *)marker->data, "ICC_PROFILE") == 0) {
997 iccProfile.append((const char*)marker->data + 14, marker->data_length - 14);
998 }
999 }
1000 }
1001
1002 if (!exifData.isEmpty()) {
1003 // Exif data present
1004 int exifOrientation = getExifOrientation(exifData);
1005 if (exifOrientation > 0)
1006 transformation = exif2Qt(exifOrientation);
1007 }
1008
1009 state = ReadHeader;
1010 return true;
1011 }
1012 else {
1013 my_output_message(j_common_ptr(&info));
1014 return false;
1015 }
1016 }
1017 else if (state == Error)
1018 return false;
1019 return true;
1020}
1021
1023{
1024 if (state == Ready)
1025 readJpegHeader(q->device());
1026
1027 if (state == ReadHeader)
1028 {
1029 const bool invertCMYK = subType != QJpegHandlerPrivate::SubType::CMYK;
1031 if (success) {
1032 for (int i = 0; i < readTexts.size()-1; i+=2)
1033 image->setText(readTexts.at(i), readTexts.at(i+1));
1034
1035 if (!iccProfile.isEmpty())
1037
1038 state = ReadingEnd;
1039 return true;
1040 }
1041
1042 state = Error;
1043 }
1044
1045 return false;
1046}
1047
1051
1053 : d(new QJpegHandlerPrivate(this))
1054{
1055#if defined(__ARM_NEON__)
1056 // from qimage_neon.cpp
1057 if (qCpuHasFeature(NEON))
1058 d->rgb888ToRgb32ConverterPtr = qt_convert_rgb888_to_rgb32_neon;
1059#endif
1060
1061#if defined(QT_COMPILER_SUPPORTS_SSSE3)
1062 // from qimage_ssse3.cpps
1063 if (qCpuHasFeature(SSSE3)) {
1064 d->rgb888ToRgb32ConverterPtr = qt_convert_rgb888_to_rgb32_ssse3;
1065 }
1066#endif // QT_COMPILER_SUPPORTS_SSSE3
1067#if defined(QT_COMPILER_SUPPORTS_MIPS_DSPR2)
1068 if (qCpuHasFeature(DSPR2)) {
1069 d->rgb888ToRgb32ConverterPtr = qt_convert_rgb888_to_rgb32_mips_dspr2_asm;
1070 }
1071#endif // QT_COMPILER_SUPPORTS_DSPR2
1072}
1073
1075{
1076 delete d;
1077}
1078
1080{
1081 if (d->state == QJpegHandlerPrivate::Ready && !canRead(device()))
1082 return false;
1083
1085 setFormat("jpeg");
1086 return true;
1087 }
1088
1089 return false;
1090}
1091
1093{
1094 if (!device) {
1095 qCWarning(lcJpeg, "QJpegHandler::canRead() called with no device");
1096 return false;
1097 }
1098
1099 char buffer[2];
1100 if (device->peek(buffer, 2) != 2)
1101 return false;
1102 return uchar(buffer[0]) == 0xff && uchar(buffer[1]) == 0xd8;
1103}
1104
1106{
1107 if (!canRead())
1108 return false;
1109 return d->read(image);
1110}
1111
1112extern void qt_imageTransform(QImage &src, QImageIOHandler::Transformations orient);
1113
1115{
1116 const bool invertCMYK = d->subType != QJpegHandlerPrivate::SubType::CMYK;
1117 if (d->transformation != QImageIOHandler::TransformationNone) {
1118 // We don't support writing EXIF headers so apply the transform to the data.
1119 QImage img = image;
1120 qt_imageTransform(img, d->transformation);
1121 return write_jpeg_image(img, device(), d->quality, d->description, d->optimize, d->progressive, invertCMYK);
1122 }
1123 return write_jpeg_image(image, device(), d->quality, d->description, d->optimize, d->progressive, invertCMYK);
1124}
1125
1127{
1128 return option == Quality
1129 || option == ScaledSize
1131 || option == ClipRect
1132 || option == Description
1133 || option == Size
1134 || option == SubType
1136 || option == ImageFormat
1140}
1141
1143{
1144 switch(option) {
1145 case Quality:
1146 return d->quality;
1147 case ScaledSize:
1148 return d->scaledSize;
1149 case ScaledClipRect:
1150 return d->scaledClipRect;
1151 case ClipRect:
1152 return d->clipRect;
1153 case Description:
1154 d->readJpegHeader(device());
1155 return d->description;
1156 case Size:
1157 d->readJpegHeader(device());
1158 return d->size;
1159 case SubType:
1160 return QByteArray(SupportedJPEGSubtypes[int(d->subType)]);
1161 case SupportedSubTypes: {
1163 std::end(SupportedJPEGSubtypes));
1164 return QVariant::fromValue(list);
1165 }
1166 case ImageFormat:
1167 d->readJpegHeader(device());
1168 return d->format;
1169 case OptimizedWrite:
1170 return d->optimize;
1172 return d->progressive;
1174 d->readJpegHeader(device());
1175 return int(d->transformation);
1176 default:
1177 break;
1178 }
1179
1180 return QVariant();
1181}
1182
1184{
1185 switch(option) {
1186 case Quality:
1187 d->quality = value.toInt();
1188 break;
1189 case ScaledSize:
1190 d->scaledSize = value.toSize();
1191 break;
1192 case ScaledClipRect:
1193 d->scaledClipRect = value.toRect();
1194 break;
1195 case ClipRect:
1196 d->clipRect = value.toRect();
1197 break;
1198 case Description:
1199 d->description = value.toString();
1200 break;
1201 case SubType: {
1202 const QByteArray subType = value.toByteArray();
1203 for (size_t i = 0; i < std::size(SupportedJPEGSubtypes); ++i) {
1204 if (subType == SupportedJPEGSubtypes[i]) {
1205 d->subType = QJpegHandlerPrivate::SubType(i);
1206 break;
1207 }
1208 }
1209 break;
1210 }
1211 case OptimizedWrite:
1212 d->optimize = value.toBool();
1213 break;
1215 d->progressive = value.toBool();
1216 break;
1217 case ImageTransformation: {
1218 int transformation = value.toInt();
1219 if (transformation > 0 && transformation < 8)
1220 d->transformation = QImageIOHandler::Transformations(transformation);
1221 break;
1222 }
1223 default:
1224 break;
1225 }
1226}
1227
IOBluetoothDevice * device
\inmodule QtCore \reentrant
Definition qbuffer.h:16
\inmodule QtCore
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
void truncate(qsizetype pos)
Truncates the byte array at index position pos.
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QByteArray mid(qsizetype index, qsizetype len=-1) const &
static QColorSpace fromIccProfile(const QByteArray &iccProfile)
Creates a QColorSpace from ICC profile iccProfile.
\inmodule QtCore\reentrant
Definition qdatastream.h:46
\inmodule QtCore \reentrant
Definition qiodevice.h:34
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
qint64 peek(char *data, qint64 maxlen)
The QImageIOHandler class defines the common image I/O interface for all image formats in Qt.
ImageOption
This enum describes the different options supported by QImageIOHandler.
static bool allocateImage(QSize size, QImage::Format format, QImage *image)
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_Grayscale16
Definition qimage.h:70
@ Format_Alpha8
Definition qimage.h:65
@ Format_CMYK8888
Definition qimage.h:78
@ Format_RGB888
Definition qimage.h:55
@ 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_Premultiplied
Definition qimage.h:48
@ Format_ARGB32
Definition qimage.h:47
@ Format_Grayscale8
Definition qimage.h:66
QImage::Format format
bool readJpegHeader(QIODevice *)
struct my_error_mgr err
struct jpeg_decompress_struct info
Rgb888ToRgb32Converter rgb888ToRgb32ConverterPtr
bool read(QImage *image)
QJpegHandlerPrivate(QJpegHandler *qq)
struct my_jpeg_source_mgr * iod_src
QImageIOHandler::Transformations transformation
bool supportsOption(ImageOption option) const override
Returns true if the QImageIOHandler supports the option option; otherwise returns false.
bool read(QImage *image) override
Read an image from the device, and stores it in image.
void setOption(ImageOption option, const QVariant &value) override
Sets the option option with the value value.
QVariant option(ImageOption option) const override
Returns the value assigned to option as a QVariant.
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,...
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr bool isEmpty() const noexcept
Returns true if the rectangle is empty, otherwise returns false.
Definition qrect.h:167
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:239
QRect intersected(const QRect &other) const noexcept
Definition qrect.h:415
constexpr int bottom() const noexcept
Returns the y-coordinate of the rectangle's bottom edge.
Definition qrect.h:182
constexpr QPoint topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:221
constexpr int x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:185
constexpr QSize size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:242
constexpr void translate(int dx, int dy) noexcept
Moves the rectangle dx along the x axis and dy along the y axis, relative to the current position.
Definition qrect.h:245
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:236
constexpr int y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:188
constexpr int right() const noexcept
Returns the x-coordinate of the rectangle's right edge.
Definition qrect.h:179
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
constexpr bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
Definition qsize.h:124
constexpr bool isValid() const noexcept
Returns true if both the width and height is equal to or greater than 0; otherwise returns false.
Definition qsize.h:127
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first character in the string.
Definition qstring.h:1349
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
iterator end()
Returns an \l{STL-style iterators}{STL-style iterator} pointing just after the last character in the ...
Definition qstring.h:1357
\inmodule QtCore
Definition qvariant.h:65
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:536
#define this
Definition dialogs.cpp:9
QString text
QSet< QString >::iterator it
QPixmap pix
Combined button and popup list for selecting options.
@ FastTransformation
@ SmoothTransformation
@ IgnoreAspectRatio
Definition image.cpp:4
#define rgb(r, g, b)
Definition qcolor.cpp:124
#define QT_FASTCALL
#define QT_WARNING_DISABLE_GCC(text)
AudioChannelLayoutTag tag
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLStreamKHR stream
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
Q_GUI_EXPORT void qt_imageTransform(QImage &src, QImageIOHandler::Transformations orient)
Definition qimage.cpp:6410
QMap< QString, QString > qt_getImageText(const QImage &image, const QString &description)
Definition qimage.cpp:6424
void qt_convert_rgb888_to_rgb32_mips_dspr2_asm(uint *dst, const uchar *src, int len)
static void my_error_exit(j_common_ptr cinfo)
#define HIGH_QUALITY_THRESHOLD
static void qt_init_source(j_decompress_ptr)
static void my_output_message(j_common_ptr cinfo)
static void qt_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
static void set_text(const QImage &image, j_compress_ptr cinfo, const QString &description)
static void write_icc_profile(const QImage &image, j_compress_ptr cinfo)
static bool read_jpeg_image(QImage *outImage, QSize scaledSize, QRect scaledClipRect, QRect clipRect, int quality, Rgb888ToRgb32Converter converter, j_decompress_ptr info, struct my_error_mgr *err, bool invertCMYK)
static bool do_write_jpeg_image(struct jpeg_compress_struct &cinfo, JSAMPROW *row_pointer, const QImage &image, QIODevice *device, int sourceQuality, const QString &description, bool optimize, bool progressive, bool invertCMYK)
static bool readExifHeader(QDataStream &stream)
void(QT_FASTCALL * Rgb888ToRgb32Converter)(quint32 *dst, const uchar *src, int len)
static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info, const QSize &size)
static const int max_buf
static constexpr int maxMarkerSize
static bool read_jpeg_format(QImage::Format &format, j_decompress_ptr cinfo)
static void qt_init_destination(j_compress_ptr)
static void qt_term_destination(j_compress_ptr cinfo)
void qt_imageTransform(QImage &src, QImageIOHandler::Transformations orient)
Definition qimage.cpp:6410
static void qt_term_source(j_decompress_ptr cinfo)
static bool read_jpeg_size(int &w, int &h, j_decompress_ptr cinfo)
static int getExifOrientation(QByteArray &exifData)
Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_ssse3(quint32 *dst, const uchar *src, int len)
Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_neon(quint32 *dst, const uchar *src, int len)
void qt_convert_rgb888_to_rgb32_mips_dspr2_asm(quint32 *dst, const uchar *src, int len)
static boolean qt_empty_output_buffer(j_compress_ptr cinfo)
static boolean qt_fill_input_buffer(j_decompress_ptr cinfo)
static const char SupportedJPEGSubtypes[][14]
QT_BEGIN_NAMESPACE Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32(quint32 *dst, const uchar *src, int len)
static QImageIOHandler::Transformations exif2Qt(int exifOrientation)
static bool write_jpeg_image(const QImage &image, QIODevice *device, int sourceQuality, const QString &description, bool optimize, bool progressive, bool invertCMYK)
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
int qCeil(T v)
Definition qmath.h:36
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLint GLint GLint GLint GLint x
[0]
GLuint64 key
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLint GLenum GLint components
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLuint GLuint end
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum src
GLenum GLuint buffer
GLint GLsizei width
GLenum type
GLenum GLenum dst
GLenum GLuint GLintptr offset
const GLchar * marker
GLfloat n
GLint GLsizei GLsizei GLenum format
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLdouble s
[6]
Definition qopenglext.h:235
GLuint GLfloat * val
GLint void * img
Definition qopenglext.h:233
GLuint in
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLenum GLenum GLsizei void * row
GLuint64EXT * result
[6]
GLuint GLenum option
GLenum GLsizei len
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
constexpr bool qIsGray(QRgb rgb)
Definition qrgb.h:42
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
constexpr int qRed(QRgb rgb)
Definition qrgb.h:18
constexpr int qGreen(QRgb rgb)
Definition qrgb.h:21
constexpr int qBlue(QRgb rgb)
Definition qrgb.h:24
#define qCpuHasFeature(feature)
Definition qsimd_p.h:387
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
unsigned short quint16
Definition qtypes.h:48
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
QList< int > list
[14]
QTextStream out(stdout)
[7]
QObject::connect nullptr
QHostInfo info
[0]
\inmodule QtCore \reentrant
Definition qchar.h:18
jmp_buf setjmp_buffer
my_jpeg_destination_mgr(QIODevice *)
my_jpeg_source_mgr(QIODevice *device)
const QBuffer * memDevice
JOCTET buffer[max_buf]