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
qtiffhandler.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 "qtiffhandler_p.h"
5
6#include <qcolorspace.h>
7#include <qdebug.h>
8#include <qfloat16.h>
9#include <qimage.h>
10#include <qloggingcategory.h>
11#include <qvariant.h>
12#include <qvarlengtharray.h>
13#include <qbuffer.h>
14#include <qfiledevice.h>
15
16extern "C" {
17#include "tiffio.h"
18}
19
20#include <memory>
21
23
24static Q_LOGGING_CATEGORY(lcTiff, "qt.imageformats.tiff")
25
26tsize_t qtiffReadProc(thandle_t fd, tdata_t buf, tsize_t size)
27{
28 QIODevice *device = static_cast<QIODevice *>(fd);
29 return device->isReadable() ? device->read(static_cast<char *>(buf), size) : -1;
30}
31
32tsize_t qtiffWriteProc(thandle_t fd, tdata_t buf, tsize_t size)
33{
34 return static_cast<QIODevice *>(fd)->write(static_cast<char *>(buf), size);
35}
36
37toff_t qtiffSeekProc(thandle_t fd, toff_t off, int whence)
38{
39 QIODevice *device = static_cast<QIODevice *>(fd);
40 switch (whence) {
41 case SEEK_SET:
42 device->seek(off);
43 break;
44 case SEEK_CUR:
45 device->seek(device->pos() + off);
46 break;
47 case SEEK_END:
48 device->seek(device->size() + off);
49 break;
50 }
51
52 return device->pos();
53}
54
55int qtiffCloseProc(thandle_t /*fd*/)
56{
57 return 0;
58}
59
60toff_t qtiffSizeProc(thandle_t fd)
61{
62 return static_cast<QIODevice *>(fd)->size();
63}
64
65int qtiffMapProc(thandle_t fd, void **base, toff_t *size)
66{
67 QIODevice *device = static_cast<QIODevice *>(fd);
68
69 QFileDevice *file = qobject_cast<QFileDevice *>(device);
70 if (file) {
71 *base = file->map(0, file->size());
72 if (*base != nullptr) {
73 *size = file->size();
74 return 1;
75 }
76 } else {
77 QBuffer *buf = qobject_cast<QBuffer *>(device);
78 if (buf) {
79 *base = const_cast<char *>(buf->data().constData());
80 *size = buf->size();
81 return 1;
82 }
83 }
84 return 0;
85}
86
87void qtiffUnmapProc(thandle_t fd, void *base, toff_t /*size*/)
88{
89 QFileDevice *file = qobject_cast<QFileDevice *>(static_cast<QIODevice *>(fd));
90 if (file && base)
91 file->unmap(static_cast<uchar *>(base));
92}
93
94
96{
97public:
100
101 static bool canRead(QIODevice *device);
104 void close();
105 TIFF *openInternal(const char *mode, QIODevice *device);
106#if TIFFLIB_VERSION >= 20221213
107 static int tiffErrorHandler(TIFF *tif, void *user_data, const char *,
108 const char *fmt, va_list ap);
109 static int tiffWarningHandler(TIFF *tif, void *user_data, const char *,
110 const char *fmt, va_list ap);
111#endif
112
113 TIFF *tiff;
115 QImageIOHandler::Transformations transformation;
118 uint16_t photometric;
124};
125
126static QImageIOHandler::Transformations exif2Qt(int exifOrientation)
127{
128 switch (exifOrientation) {
129 case 1: // normal
131 case 2: // mirror horizontal
133 case 3: // rotate 180
135 case 4: // mirror vertical
137 case 5: // mirror horizontal and rotate 270 CW
139 case 6: // rotate 90 CW
141 case 7: // mirror horizontal and rotate 90 CW
143 case 8: // rotate 270 CW
145 }
146 qCWarning(lcTiff, "Invalid EXIF orientation");
148}
149
150static int qt2Exif(QImageIOHandler::Transformations transformation)
151{
152 switch (transformation) {
154 return 1;
156 return 2;
158 return 3;
160 return 4;
162 return 5;
164 return 6;
166 return 7;
168 return 8;
169 }
170 qCWarning(lcTiff, "Invalid Qt image transformation");
171 return 1;
172}
173
175 : tiff(0)
176 , compression(QTiffHandler::NoCompression)
177 , transformation(QImageIOHandler::TransformationNone)
178 , format(QImage::Format_Invalid)
179 , photometric(false)
181 , headersRead(false)
182 , currentDirectory(0)
183 , directoryCount(0)
184{
185}
186
191
193{
194 if (tiff)
195 TIFFClose(tiff);
196 tiff = 0;
197}
198
200{
201// TIFFLIB_VERSION 20221213 -> 4.5.0
202#if TIFFLIB_VERSION >= 20221213
203 TIFFOpenOptions *opts = TIFFOpenOptionsAlloc();
204 TIFFOpenOptionsSetErrorHandlerExtR(opts, &tiffErrorHandler, this);
205 TIFFOpenOptionsSetWarningHandlerExtR(opts, &tiffWarningHandler, this);
206 auto handle = TIFFClientOpenExt("foo",
207 mode,
208 device,
209 qtiffReadProc,
216 opts);
217 TIFFOpenOptionsFree(opts);
218#else
219 auto handle = TIFFClientOpen("foo",
220 mode,
221 device,
222 qtiffReadProc,
229#endif
230 return handle;
231}
232
233
234#if TIFFLIB_VERSION >= 20221213
235int QTiffHandlerPrivate::tiffErrorHandler(TIFF *tif, void *user_data, const char *,
236 const char *fmt, va_list ap)
237{
238 const auto priv = static_cast<QTiffHandlerPrivate *>(user_data);
239 if (!priv || priv->tiff != tif)
240 return 0;
241 qCCritical(lcTiff) << QString::vasprintf(fmt, ap);
242 return 1;
243}
244
245int QTiffHandlerPrivate::tiffWarningHandler(TIFF *tif, void *user_data, const char *,
246 const char *fmt, va_list ap)
247{
248 const auto priv = static_cast<QTiffHandlerPrivate *>(user_data);
249 if (!priv || priv->tiff != tif)
250 return 0;
251 qCWarning(lcTiff) << QString::vasprintf(fmt, ap);
252 return 1;
253}
254#endif
255
257{
258 if (!device) {
259 qCWarning(lcTiff, "QTiffHandler::canRead() called with no device");
260 return false;
261 }
262
263 // current implementation uses TIFFClientOpen which needs to be
264 // able to seek, so sequential devices are not supported
265 char h[4];
266 if (device->peek(h, 4) != 4)
267 return false;
268 if ((h[0] == 0x49 && h[1] == 0x49) && (h[2] == 0x2a || h[2] == 0x2b) && h[3] == 0)
269 return true; // Little endian, classic or bigtiff
270 if ((h[0] == 0x4d && h[1] == 0x4d) && h[2] == 0 && (h[3] == 0x2a || h[3] == 0x2b))
271 return true; // Big endian, classic or bigtiff
272 return false;
273}
274
276{
277 if (tiff)
278 return true;
279
280 if (!canRead(device))
281 return false;
282
283 tiff = openInternal("rh", device);
284 return tiff != nullptr;
285}
286
288{
289 if (headersRead)
290 return true;
291
292 if (!openForRead(device))
293 return false;
294
295 if (!TIFFSetDirectory(tiff, currentDirectory)) {
296 close();
297 return false;
298 }
299
300 uint32_t width;
301 uint32_t height;
302 if (!TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width)
303 || !TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height)
304 || !TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric)) {
305 close();
306 return false;
307 }
309
310 uint16_t orientationTag;
311 if (TIFFGetField(tiff, TIFFTAG_ORIENTATION, &orientationTag))
312 transformation = exif2Qt(orientationTag);
313
314 // BitsPerSample defaults to 1 according to the TIFF spec.
315 uint16_t bitPerSample;
316 if (!TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bitPerSample))
317 bitPerSample = 1;
318 uint16_t samplesPerPixel; // they may be e.g. grayscale with 2 samples per pixel
319 if (!TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &samplesPerPixel))
320 samplesPerPixel = 1;
321 uint16_t sampleFormat;
322 if (!TIFFGetField(tiff, TIFFTAG_SAMPLEFORMAT, &sampleFormat))
323 sampleFormat = SAMPLEFORMAT_VOID;
324 floatingPoint = (sampleFormat == SAMPLEFORMAT_IEEEFP);
325
326 grayscale = photometric == PHOTOMETRIC_MINISBLACK || photometric == PHOTOMETRIC_MINISWHITE;
327
328 if (grayscale && bitPerSample == 1 && samplesPerPixel == 1)
330 else if (photometric == PHOTOMETRIC_MINISBLACK && bitPerSample == 8 && samplesPerPixel == 1)
332 else if (photometric == PHOTOMETRIC_MINISBLACK && bitPerSample == 16 && samplesPerPixel == 1 && !floatingPoint)
334 else if ((grayscale || photometric == PHOTOMETRIC_PALETTE) && bitPerSample == 8 && samplesPerPixel == 1)
336 else if (samplesPerPixel < 4)
337 if (bitPerSample == 16 && (photometric == PHOTOMETRIC_RGB || photometric == PHOTOMETRIC_MINISBLACK))
339 else if (bitPerSample == 32 && floatingPoint && (photometric == PHOTOMETRIC_RGB || photometric == PHOTOMETRIC_MINISBLACK))
341 else
343 else {
344 uint16_t count;
345 uint16_t *extrasamples;
346 // If there is any definition of the alpha-channel, libtiff will return premultiplied
347 // data to us. If there is none, libtiff will not touch it and we assume it to be
348 // non-premultiplied, matching behavior of tested image editors, and how older Qt
349 // versions used to save it.
350 bool premultiplied = true;
351 bool gotField = TIFFGetField(tiff, TIFFTAG_EXTRASAMPLES, &count, &extrasamples);
352 if (!gotField || !count || extrasamples[0] == EXTRASAMPLE_UNSPECIFIED)
353 premultiplied = false;
354
355 if (bitPerSample == 16 && photometric == PHOTOMETRIC_RGB) {
356 // We read 64-bit raw, so unassoc remains unpremultiplied.
357 if (gotField && count && extrasamples[0] == EXTRASAMPLE_UNASSALPHA)
358 premultiplied = false;
359 if (premultiplied)
361 else
363 } else if (bitPerSample == 32 && floatingPoint && photometric == PHOTOMETRIC_RGB) {
364 if (gotField && count && extrasamples[0] == EXTRASAMPLE_UNASSALPHA)
365 premultiplied = false;
366 if (premultiplied)
368 else
370 } else if (samplesPerPixel == 4 && bitPerSample == 8 && photometric == PHOTOMETRIC_SEPARATED) {
371 uint16_t inkSet;
372 const bool gotInkSetField = TIFFGetField(tiff, TIFFTAG_INKSET, &inkSet);
373 if (!gotInkSetField || inkSet == INKSET_CMYK) {
375 } else {
376 close();
377 return false;
378 }
379 } else {
380 if (premultiplied)
382 else
384 }
385 }
386
387 headersRead = true;
388 return true;
389}
390
396
398{
399 if (d->tiff)
400 return true;
402 setFormat("tiff");
403 return true;
404 }
405 return false;
406}
407
412
414{
415 // Open file and read headers if it hasn't already been done.
416 if (!d->readHeaders(device()))
417 return false;
418
420
422 d->close();
423 return false;
424 }
425
426 TIFF *const tiff = d->tiff;
427 if (TIFFIsTiled(tiff) && TIFFTileSize64(tiff) > uint64_t(image->sizeInBytes())) // Corrupt image
428 return false;
429 const quint32 width = d->size.width();
430 const quint32 height = d->size.height();
431
432 // Setup color tables
435 QList<QRgb> colortable(2);
436 if (d->photometric == PHOTOMETRIC_MINISBLACK) {
437 colortable[0] = 0xff000000;
438 colortable[1] = 0xffffffff;
439 } else {
440 colortable[0] = 0xffffffff;
441 colortable[1] = 0xff000000;
442 }
443 image->setColorTable(colortable);
444 } else if (format == QImage::Format_Indexed8) {
445 const uint16_t tableSize = 256;
446 QList<QRgb> qtColorTable(tableSize);
447 if (d->grayscale) {
448 for (int i = 0; i<tableSize; ++i) {
449 const int c = (d->photometric == PHOTOMETRIC_MINISBLACK) ? i : (255 - i);
450 qtColorTable[i] = qRgb(c, c, c);
451 }
452 } else {
453 // create the color table
454 uint16_t *redTable = 0;
455 uint16_t *greenTable = 0;
456 uint16_t *blueTable = 0;
457 if (!TIFFGetField(tiff, TIFFTAG_COLORMAP, &redTable, &greenTable, &blueTable)) {
458 d->close();
459 return false;
460 }
461 if (!redTable || !greenTable || !blueTable) {
462 d->close();
463 return false;
464 }
465
466 for (int i = 0; i<tableSize ;++i) {
467 // emulate libtiff behavior for 16->8 bit color map conversion: just ignore the lower 8 bits
468 const int red = redTable[i] >> 8;
469 const int green = greenTable[i] >> 8;
470 const int blue = blueTable[i] >> 8;
471 qtColorTable[i] = qRgb(red, green, blue);
472 }
473 }
474 image->setColorTable(qtColorTable);
475 // free redTable, greenTable and greenTable done by libtiff
476 }
477 }
479 bool format16bit = (format == QImage::Format_Grayscale16);
480 bool formatCmyk32bit = (format == QImage::Format_CMYK8888);
484
485 // Formats we read directly, instead of over RGBA32:
486 if (format8bit || format16bit || formatCmyk32bit || format64bit || format64fp || format128fp) {
487 int bytesPerPixel = image->depth() / 8;
489 bytesPerPixel = d->photometric == PHOTOMETRIC_RGB ? 6 : 2;
491 bytesPerPixel = d->photometric == PHOTOMETRIC_RGB ? 12 : 4;
492 if (TIFFIsTiled(tiff)) {
493 quint32 tileWidth, tileLength;
494 TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tileWidth);
495 TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tileLength);
496 if (!tileWidth || !tileLength || tileWidth % 16 || tileLength % 16) {
497 d->close();
498 return false;
499 }
500 quint32 byteWidth = (format == QImage::Format_Mono) ? (width + 7)/8 : (width * bytesPerPixel);
501 quint32 byteTileWidth = (format == QImage::Format_Mono) ? tileWidth/8 : (tileWidth * bytesPerPixel);
502 tmsize_t byteTileSize = TIFFTileSize(tiff);
503 if (byteTileSize > image->sizeInBytes() || byteTileSize / tileLength < byteTileWidth) {
504 d->close();
505 return false;
506 }
507 uchar *buf = (uchar *)_TIFFmalloc(byteTileSize);
508 if (!buf) {
509 d->close();
510 return false;
511 }
512 for (quint32 y = 0; y < height; y += tileLength) {
513 for (quint32 x = 0; x < width; x += tileWidth) {
514 if (TIFFReadTile(tiff, buf, x, y, 0, 0) < 0) {
515 _TIFFfree(buf);
516 d->close();
517 return false;
518 }
519 quint32 linesToCopy = qMin(tileLength, height - y);
520 quint32 byteOffset = (format == QImage::Format_Mono) ? x/8 : (x * bytesPerPixel);
521 quint32 widthToCopy = qMin(byteTileWidth, byteWidth - byteOffset);
522 for (quint32 i = 0; i < linesToCopy; i++) {
523 ::memcpy(image->scanLine(y + i) + byteOffset, buf + (i * byteTileWidth), widthToCopy);
524 }
525 }
526 }
527 _TIFFfree(buf);
528 } else {
529 if (image->bytesPerLine() < TIFFScanlineSize(tiff)) {
530 d->close();
531 return false;
532 }
533 for (uint32_t y=0; y<height; ++y) {
534 if (TIFFReadScanline(tiff, image->scanLine(y), y, 0) < 0) {
535 d->close();
536 return false;
537 }
538 }
539 }
541 if (d->photometric == PHOTOMETRIC_RGB)
542 rgb48fixup(image, d->floatingPoint);
543 else
544 rgbFixup(image);
545 } else if (format == QImage::Format_RGBX32FPx4) {
546 if (d->photometric == PHOTOMETRIC_RGB)
547 rgb96fixup(image);
548 else
549 rgbFixup(image);
550 }
551 } else {
552 const int stopOnError = 1;
553 if (TIFFReadRGBAImageOriented(tiff, width, height, reinterpret_cast<uint32_t *>(image->bits()), qt2Exif(d->transformation), stopOnError)) {
554 for (uint32_t y=0; y<height; ++y)
555 convert32BitOrder(image->scanLine(y), width);
556 } else {
557 d->close();
558 return false;
559 }
560 }
561
562
563 float resX = 0;
564 float resY = 0;
565 uint16_t resUnit;
566 if (!TIFFGetField(tiff, TIFFTAG_RESOLUTIONUNIT, &resUnit))
567 resUnit = RESUNIT_INCH;
568
569 if (TIFFGetField(tiff, TIFFTAG_XRESOLUTION, &resX)
570 && TIFFGetField(tiff, TIFFTAG_YRESOLUTION, &resY)) {
571
572 switch(resUnit) {
573 case RESUNIT_CENTIMETER:
574 image->setDotsPerMeterX(qRound(resX * 100));
575 image->setDotsPerMeterY(qRound(resY * 100));
576 break;
577 case RESUNIT_INCH:
578 image->setDotsPerMeterX(qRound(resX * (100 / 2.54)));
579 image->setDotsPerMeterY(qRound(resY * (100 / 2.54)));
580 break;
581 default:
582 // do nothing as defaults have already
583 // been set within the QImage class
584 break;
585 }
586 }
587
588 uint32_t count;
589 void *profile;
590 if (TIFFGetField(tiff, TIFFTAG_ICCPROFILE, &count, &profile)) {
591 QByteArray iccProfile(reinterpret_cast<const char *>(profile), count);
592 image->setColorSpace(QColorSpace::fromIccProfile(iccProfile));
593 }
594 // We do not handle colorimetric metadat not on ICC profile form, it seems to be a lot
595 // less common, and would need additional API in QColorSpace.
596
597 return true;
598}
599
600static bool checkGrayscale(const QList<QRgb> &colorTable)
601{
602 if (colorTable.size() != 256)
603 return false;
604
605 const bool increasing = (colorTable.at(0) == 0xff000000);
606 for (int i = 0; i < 256; ++i) {
607 if ((increasing && colorTable.at(i) != qRgb(i, i, i))
608 || (!increasing && colorTable.at(i) != qRgb(255 - i, 255 - i, 255 - i)))
609 return false;
610 }
611 return true;
612}
613
614static QList<QRgb> effectiveColorTable(const QImage &image)
615{
616 QList<QRgb> colors;
617 switch (image.format()) {
619 colors = image.colorTable();
620 break;
622 colors.resize(256);
623 for (int i = 0; i < 256; ++i)
624 colors[i] = qRgba(0, 0, 0, i);
625 break;
628 colors.resize(256);
629 for (int i = 0; i < 256; ++i)
630 colors[i] = qRgb(i, i, i);
631 break;
632 default:
633 Q_UNREACHABLE();
634 }
635 return colors;
636}
637
638static quint32 defaultStripSize(TIFF *tiff)
639{
640 // Aim for 4MB strips
641 qint64 scanSize = qMax(qint64(1), qint64(TIFFScanlineSize(tiff)));
642 qint64 numRows = (4 * 1024 * 1024) / scanSize;
643 quint32 reqSize = static_cast<quint32>(qBound(qint64(1), numRows, qint64(UINT_MAX)));
644 return TIFFDefaultStripSize(tiff, reqSize);
645}
646
648{
649 if (!device()->isWritable())
650 return false;
651
652 TIFF *const tiff = d->openInternal("wB", device());
653 if (!tiff)
654 return false;
655
656 const int width = image.width();
657 const int height = image.height();
658 const int compression = d->compression;
659
660 if (!TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width)
661 || !TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height)
662 || !TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) {
663 TIFFClose(tiff);
664 return false;
665 }
666
667 // set the resolution
668 bool resolutionSet = false;
669 const int dotPerMeterX = image.dotsPerMeterX();
670 const int dotPerMeterY = image.dotsPerMeterY();
671 if ((dotPerMeterX % 100) == 0
672 && (dotPerMeterY % 100) == 0) {
673 resolutionSet = TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_CENTIMETER)
674 && TIFFSetField(tiff, TIFFTAG_XRESOLUTION, dotPerMeterX/100.0)
675 && TIFFSetField(tiff, TIFFTAG_YRESOLUTION, dotPerMeterY/100.0);
676 } else {
677 resolutionSet = TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)
678 && TIFFSetField(tiff, TIFFTAG_XRESOLUTION, static_cast<float>(image.logicalDpiX()))
679 && TIFFSetField(tiff, TIFFTAG_YRESOLUTION, static_cast<float>(image.logicalDpiY()));
680 }
681 if (!resolutionSet) {
682 TIFFClose(tiff);
683 return false;
684 }
685 // set the orienataion
686 bool orientationSet = false;
687 orientationSet = TIFFSetField(tiff, TIFFTAG_ORIENTATION, qt2Exif(d->transformation));
688 if (!orientationSet) {
689 TIFFClose(tiff);
690 return false;
691 }
692 // set color space
693 const QByteArray iccProfile = image.colorSpace().iccProfile();
694 if (!iccProfile.isEmpty()) {
695 if (!TIFFSetField(tiff, TIFFTAG_ICCPROFILE, iccProfile.size(), reinterpret_cast<const void *>(iccProfile.constData()))) {
696 TIFFClose(tiff);
697 return false;
698 }
699 }
700 // configure image depth
701 const QImage::Format format = image.format();
703 uint16_t photometric = PHOTOMETRIC_MINISBLACK;
704 if (image.colorTable().at(0) == 0xffffffff)
705 photometric = PHOTOMETRIC_MINISWHITE;
706 if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, photometric)
707 || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
708 || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 1)
709 || !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, defaultStripSize(tiff))) {
710 TIFFClose(tiff);
711 return false;
712 }
713
714 // try to do the conversion in chunks no greater than 16 MB
715 const int chunks = int(image.sizeInBytes() / (1024 * 1024 * 16)) + 1;
716 const int chunkHeight = qMax(height / chunks, 1);
717
718 int y = 0;
719 while (y < height) {
720 QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y)).convertToFormat(QImage::Format_Mono);
721
722 int chunkStart = y;
723 int chunkEnd = y + chunk.height();
724 while (y < chunkEnd) {
725 if (TIFFWriteScanline(tiff, reinterpret_cast<uint32_t *>(chunk.scanLine(y - chunkStart)), y) != 1) {
726 TIFFClose(tiff);
727 return false;
728 }
729 ++y;
730 }
731 }
732 TIFFClose(tiff);
733 } else if (format == QImage::Format_Indexed8
737 QList<QRgb> colorTable = effectiveColorTable(image);
738 bool isGrayscale = checkGrayscale(colorTable);
739 if (isGrayscale) {
740 uint16_t photometric = PHOTOMETRIC_MINISBLACK;
741 if (colorTable.at(0) == 0xffffffff)
742 photometric = PHOTOMETRIC_MINISWHITE;
743 if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, photometric)
744 || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
745 || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, image.depth())
746 || !TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT)
747 || !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, defaultStripSize(tiff))) {
748 TIFFClose(tiff);
749 return false;
750 }
751 } else {
752 if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE)
753 || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
754 || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)
755 || !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, defaultStripSize(tiff))) {
756 TIFFClose(tiff);
757 return false;
758 }
760 // allocate the color tables
761 const int tableSize = colorTable.size();
762 Q_ASSERT(tableSize <= 256);
763 QVarLengthArray<uint16_t> redTable(tableSize);
764 QVarLengthArray<uint16_t> greenTable(tableSize);
765 QVarLengthArray<uint16_t> blueTable(tableSize);
766
767 // set the color table
768 for (int i = 0; i<tableSize; ++i) {
769 const QRgb color = colorTable.at(i);
770 redTable[i] = qRed(color) * 257;
771 greenTable[i] = qGreen(color) * 257;
772 blueTable[i] = qBlue(color) * 257;
773 }
774
775 const bool setColorTableSuccess = TIFFSetField(tiff, TIFFTAG_COLORMAP, redTable.data(), greenTable.data(), blueTable.data());
776
777 if (!setColorTableSuccess) {
778 TIFFClose(tiff);
779 return false;
780 }
781 }
782
784 for (int y = 0; y < height; ++y) {
785 if (TIFFWriteScanline(tiff, const_cast<uchar *>(image.scanLine(y)), y) != 1) {
786 TIFFClose(tiff);
787 return false;
788 }
789 }
790 TIFFClose(tiff);
792 if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
793 || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
794 || !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 3)
795 || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 16)
796 || !TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT,
798 ? SAMPLEFORMAT_UINT
799 : SAMPLEFORMAT_IEEEFP)
800 || !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiff, 0))) {
801 TIFFClose(tiff);
802 return false;
803 }
804 std::unique_ptr<quint16[]> rgb48line(new quint16[width * 3]);
805 for (int y = 0; y < height; ++y) {
806 const quint16 *srcLine = reinterpret_cast<const quint16 *>(image.constScanLine(y));
807 for (int x = 0; x < width; ++x) {
808 rgb48line[x * 3 + 0] = srcLine[x * 4 + 0];
809 rgb48line[x * 3 + 1] = srcLine[x * 4 + 1];
810 rgb48line[x * 3 + 2] = srcLine[x * 4 + 2];
811 }
812
813 if (TIFFWriteScanline(tiff, (void*)rgb48line.get(), y) != 1) {
814 TIFFClose(tiff);
815 return false;
816 }
817 }
818 TIFFClose(tiff);
819 } else if (format == QImage::Format_RGBA64
821 const bool premultiplied = image.format() != QImage::Format_RGBA64;
822 const uint16_t extrasamples = premultiplied ? EXTRASAMPLE_ASSOCALPHA : EXTRASAMPLE_UNASSALPHA;
823 if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
824 || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
825 || !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4)
826 || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 16)
827 || !TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT)
828 || !TIFFSetField(tiff, TIFFTAG_EXTRASAMPLES, 1, &extrasamples)
829 || !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiff, 0))) {
830 TIFFClose(tiff);
831 return false;
832 }
833 for (int y = 0; y < height; ++y) {
834 if (TIFFWriteScanline(tiff, (void*)image.scanLine(y), y) != 1) {
835 TIFFClose(tiff);
836 return false;
837 }
838 }
839 TIFFClose(tiff);
840 } else if (format == QImage::Format_RGBX32FPx4) {
841 if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
842 || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
843 || !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 3)
844 || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 32)
845 || !TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP)
846 || !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiff, 0))) {
847 TIFFClose(tiff);
848 return false;
849 }
850 std::unique_ptr<float[]> line(new float[width * 3]);
851 for (int y = 0; y < height; ++y) {
852 const float *srcLine = reinterpret_cast<const float *>(image.constScanLine(y));
853 for (int x = 0; x < width; ++x) {
854 line[x * 3 + 0] = srcLine[x * 4 + 0];
855 line[x * 3 + 1] = srcLine[x * 4 + 1];
856 line[x * 3 + 2] = srcLine[x * 4 + 2];
857 }
858
859 if (TIFFWriteScanline(tiff, (void*)line.get(), y) != 1) {
860 TIFFClose(tiff);
861 return false;
862 }
863 }
864 TIFFClose(tiff);
868 const bool premultiplied = image.format() != QImage::Format_RGBA16FPx4 && image.format() != QImage::Format_RGBA32FPx4;
869 const uint16_t extrasamples = premultiplied ? EXTRASAMPLE_ASSOCALPHA : EXTRASAMPLE_UNASSALPHA;
870 if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
871 || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
872 || !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4)
873 || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, image.depth() == 64 ? 16 : 32)
874 || !TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP)
875 || !TIFFSetField(tiff, TIFFTAG_EXTRASAMPLES, 1, &extrasamples)
876 || !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiff, 0))) {
877 TIFFClose(tiff);
878 return false;
879 }
880 for (int y = 0; y < height; ++y) {
881 if (TIFFWriteScanline(tiff, (void*)image.scanLine(y), y) != 1) {
882 TIFFClose(tiff);
883 return false;
884 }
885 }
886 TIFFClose(tiff);
887 } else if (format == QImage::Format_CMYK8888) {
888 if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED)
889 || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
890 || !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4)
891 || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)
892 || !TIFFSetField(tiff, TIFFTAG_INKSET, INKSET_CMYK)
893 || !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, defaultStripSize(tiff))) {
894 TIFFClose(tiff);
895 return false;
896 }
897
898 for (int y = 0; y < image.height(); ++y) {
899 if (TIFFWriteScanline(tiff, (void*)image.scanLine(y), y) != 1) {
900 TIFFClose(tiff);
901 return false;
902 }
903 }
904
905 TIFFClose(tiff);
906 } else if (!image.hasAlphaChannel()) {
907 if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
908 || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
909 || !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 3)
910 || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)
911 || !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, defaultStripSize(tiff))) {
912 TIFFClose(tiff);
913 return false;
914 }
915 // try to do the RGB888 conversion in chunks no greater than 16 MB
916 const int chunks = int(image.sizeInBytes() / (1024 * 1024 * 16)) + 1;
917 const int chunkHeight = qMax(height / chunks, 1);
918
919 int y = 0;
920 while (y < height) {
921 const QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y)).convertToFormat(QImage::Format_RGB888);
922
923 int chunkStart = y;
924 int chunkEnd = y + chunk.height();
925 while (y < chunkEnd) {
926 if (TIFFWriteScanline(tiff, (void*)chunk.scanLine(y - chunkStart), y) != 1) {
927 TIFFClose(tiff);
928 return false;
929 }
930 ++y;
931 }
932 }
933 TIFFClose(tiff);
934 } else {
935 const bool premultiplied = image.format() != QImage::Format_ARGB32
936 && image.format() != QImage::Format_RGBA8888;
937 const uint16_t extrasamples = premultiplied ? EXTRASAMPLE_ASSOCALPHA : EXTRASAMPLE_UNASSALPHA;
938 if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
939 || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
940 || !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4)
941 || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)
942 || !TIFFSetField(tiff, TIFFTAG_EXTRASAMPLES, 1, &extrasamples)
943 || !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, defaultStripSize(tiff))) {
944 TIFFClose(tiff);
945 return false;
946 }
947 // try to do the RGBA8888 conversion in chunks no greater than 16 MB
948 const int chunks = int(image.sizeInBytes() / (1024 * 1024 * 16)) + 1;
949 const int chunkHeight = qMax(height / chunks, 1);
950
953 int y = 0;
954 while (y < height) {
955 const QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y)).convertToFormat(format);
956
957 int chunkStart = y;
958 int chunkEnd = y + chunk.height();
959 while (y < chunkEnd) {
960 if (TIFFWriteScanline(tiff, (void*)chunk.scanLine(y - chunkStart), y) != 1) {
961 TIFFClose(tiff);
962 return false;
963 }
964 ++y;
965 }
966 }
967 TIFFClose(tiff);
968 }
969
970 return true;
971}
972
974{
975 if (option == Size && canRead()) {
976 if (d->readHeaders(device()))
977 return d->size;
978 } else if (option == CompressionRatio) {
979 return d->compression;
980 } else if (option == ImageFormat) {
981 if (d->readHeaders(device()))
982 return d->format;
983 } else if (option == ImageTransformation) {
984 if (d->readHeaders(device()))
985 return int(d->transformation);
986 }
987 return QVariant();
988}
989
991{
992 if (option == CompressionRatio && value.metaType().id() == QMetaType::Int)
993 d->compression = qBound(0, value.toInt(), 1);
995 int transformation = value.toInt();
996 if (transformation > 0 && transformation < 8)
997 d->transformation = QImageIOHandler::Transformations(transformation);
998 }
999}
1000
1002{
1003 return option == CompressionRatio
1004 || option == Size
1005 || option == ImageFormat
1007}
1008
1010{
1011 if (!ensureHaveDirectoryCount())
1012 return false;
1013 if (d->currentDirectory >= d->directoryCount - 1)
1014 return false;
1015
1016 d->headersRead = false;
1017 ++d->currentDirectory;
1018 return true;
1019}
1020
1021bool QTiffHandler::jumpToImage(int imageNumber)
1022{
1023 if (!ensureHaveDirectoryCount())
1024 return false;
1025 if (imageNumber < 0 || imageNumber >= d->directoryCount)
1026 return false;
1027
1028 if (d->currentDirectory != imageNumber) {
1029 d->headersRead = false;
1030 d->currentDirectory = imageNumber;
1031 }
1032 return true;
1033}
1034
1036{
1037 if (!ensureHaveDirectoryCount())
1038 return 1;
1039
1040 return d->directoryCount;
1041}
1042
1044{
1045 return d->currentDirectory;
1046}
1047
1048void QTiffHandler::convert32BitOrder(void *buffer, int width)
1049{
1050 uint32_t *target = reinterpret_cast<uint32_t *>(buffer);
1051 for (int32_t x=0; x<width; ++x) {
1052 uint32_t p = target[x];
1053 // convert between ARGB and ABGR
1054 target[x] = (p & 0xff000000)
1055 | ((p & 0x00ff0000) >> 16)
1056 | (p & 0x0000ff00)
1057 | ((p & 0x000000ff) << 16);
1058 }
1059}
1060
1061void QTiffHandler::rgb48fixup(QImage *image, bool floatingPoint)
1062{
1063 Q_ASSERT(image->depth() == 64);
1064 const int h = image->height();
1065 const int w = image->width();
1066 uchar *scanline = image->bits();
1067 const qsizetype bpl = image->bytesPerLine();
1068 quint16 mask = 0xffff;
1069 const qfloat16 fp_mask = qfloat16(1.0f);
1070 if (floatingPoint)
1071 memcpy(&mask, &fp_mask, 2);
1072 for (int y = 0; y < h; ++y) {
1073 quint16 *dst = reinterpret_cast<uint16_t *>(scanline);
1074 for (int x = w - 1; x >= 0; --x) {
1075 dst[x * 4 + 3] = mask;
1076 dst[x * 4 + 2] = dst[x * 3 + 2];
1077 dst[x * 4 + 1] = dst[x * 3 + 1];
1078 dst[x * 4 + 0] = dst[x * 3 + 0];
1079 }
1080 scanline += bpl;
1081 }
1082}
1083
1084void QTiffHandler::rgb96fixup(QImage *image)
1085{
1086 Q_ASSERT(image->depth() == 128);
1087 const int h = image->height();
1088 const int w = image->width();
1089 uchar *scanline = image->bits();
1090 const qsizetype bpl = image->bytesPerLine();
1091 for (int y = 0; y < h; ++y) {
1092 float *dst = reinterpret_cast<float *>(scanline);
1093 for (int x = w - 1; x >= 0; --x) {
1094 dst[x * 4 + 3] = 1.0f;
1095 dst[x * 4 + 2] = dst[x * 3 + 2];
1096 dst[x * 4 + 1] = dst[x * 3 + 1];
1097 dst[x * 4 + 0] = dst[x * 3 + 0];
1098 }
1099 scanline += bpl;
1100 }
1101}
1102
1103void QTiffHandler::rgbFixup(QImage *image)
1104{
1106 if (image->depth() == 64) {
1107 const int h = image->height();
1108 const int w = image->width();
1109 uchar *scanline = image->bits();
1110 const qsizetype bpl = image->bytesPerLine();
1111 for (int y = 0; y < h; ++y) {
1112 qfloat16 *dst = reinterpret_cast<qfloat16 *>(scanline);
1113 for (int x = w - 1; x >= 0; --x) {
1114 dst[x * 4 + 3] = qfloat16(1.0f);
1115 dst[x * 4 + 2] = dst[x];
1116 dst[x * 4 + 1] = dst[x];
1117 dst[x * 4 + 0] = dst[x];
1118 }
1119 scanline += bpl;
1120 }
1121 } else {
1122 const int h = image->height();
1123 const int w = image->width();
1124 uchar *scanline = image->bits();
1125 const qsizetype bpl = image->bytesPerLine();
1126 for (int y = 0; y < h; ++y) {
1127 float *dst = reinterpret_cast<float *>(scanline);
1128 for (int x = w - 1; x >= 0; --x) {
1129 dst[x * 4 + 3] = 1.0f;
1130 dst[x * 4 + 2] = dst[x];
1131 dst[x * 4 + 1] = dst[x];
1132 dst[x * 4 + 0] = dst[x];
1133 }
1134 scanline += bpl;
1135 }
1136 }
1137}
1138
1139bool QTiffHandler::ensureHaveDirectoryCount() const
1140{
1141 if (d->directoryCount > 0)
1142 return true;
1143
1144 TIFF *tiff = d->openInternal("rh", device());
1145
1146 if (!tiff) {
1147 device()->reset();
1148 return false;
1149 }
1150
1151 while (TIFFReadDirectory(tiff))
1152 ++d->directoryCount;
1153 TIFFClose(tiff);
1154 device()->reset();
1155 return true;
1156}
1157
IOBluetoothDevice * device
\inmodule QtCore \reentrant
Definition qbuffer.h:16
\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
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
static QColorSpace fromIccProfile(const QByteArray &iccProfile)
Creates a QColorSpace from ICC profile iccProfile.
\inmodule QtCore
Definition qfiledevice.h:32
uchar * map(qint64 offset, qint64 size, MemoryMapFlags flags=NoOptions)
Maps size bytes of the file into memory starting at offset.
bool unmap(uchar *address)
Unmaps the memory address.
qint64 size() const override
\reimp
Definition qfile.cpp:1179
\inmodule QtCore \reentrant
Definition qiodevice.h:34
virtual bool reset()
Seeks to the start of input for random-access devices.
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
uchar * scanLine(int)
Returns a pointer to the pixel data at the scanline with index i.
Definition qimage.cpp:1637
int height() const
Returns the height of the image.
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_RGBA8888
Definition qimage.h:59
@ Format_RGB888
Definition qimage.h:55
@ Format_RGBA16FPx4
Definition qimage.h:73
@ Format_RGBA32FPx4_Premultiplied
Definition qimage.h:77
@ Format_RGB32
Definition qimage.h:46
@ Format_RGBX32FPx4
Definition qimage.h:75
@ Format_RGBA64_Premultiplied
Definition qimage.h:69
@ Format_MonoLSB
Definition qimage.h:44
@ Format_RGBA8888_Premultiplied
Definition qimage.h:60
@ Format_RGBA64
Definition qimage.h:68
@ Format_RGBA32FPx4
Definition qimage.h:76
@ Format_Mono
Definition qimage.h:43
@ Format_RGBA16FPx4_Premultiplied
Definition qimage.h:74
@ Format_RGBX64
Definition qimage.h:67
@ Format_RGBX16FPx4
Definition qimage.h:72
@ 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
\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
static QString vasprintf(const char *format, va_list ap) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7357
QImage::Format format
static bool canRead(QIODevice *device)
bool readHeaders(QIODevice *device)
QImageIOHandler::Transformations transformation
bool openForRead(QIODevice *device)
TIFF * openInternal(const char *mode, QIODevice *device)
bool jumpToNextImage() override
For image formats that support animation, this function jumps to the next image.
QVariant option(ImageOption option) const override
Returns the value assigned to option as a QVariant.
bool read(QImage *image) override
Read an image from the device, and stores it in image.
bool supportsOption(ImageOption option) const override
Returns true if the QImageIOHandler supports the option option; otherwise returns false.
void setOption(ImageOption option, const QVariant &value) override
Sets the option option with the value value.
bool canRead() const override
Returns true if an image can be read from the device (i.e., the image format is supported,...
bool jumpToImage(int imageNumber) override
For image formats that support animation, this function jumps to the image whose sequence number is i...
int imageCount() const override
For image formats that support animation, this function returns the number of images in the animation...
int currentImageNumber() const override
For image formats that support animation, this function returns the sequence number of the current im...
bool write(const QImage &image) override
Writes the image image to the assigned device.
\inmodule QtCore
Definition qvariant.h:65
\keyword 16-bit Floating Point Support\inmodule QtCore \inheaderfile QFloat16
Definition qfloat16.h:47
Combined button and popup list for selecting options.
Definition image.cpp:4
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void * user_data
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
#define Q_LOGGING_CATEGORY(name,...)
#define qCCritical(category,...)
#define qCWarning(category,...)
static const QMetaObjectPrivate * priv(const uint *data)
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 const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLuint64 GLenum void * handle
GLint GLint GLint GLint GLint x
[0]
GLenum mode
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLenum GLsizei count
GLenum GLuint buffer
GLint GLsizei width
GLuint color
[2]
GLenum GLenum dst
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum target
GLuint64 GLenum GLint fd
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLint GLsizei GLsizei GLenum format
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLbyte GLbyte blue
Definition qopenglext.h:385
const GLubyte * c
GLfloat GLfloat p
[1]
GLuint GLenum option
GLbyte green
Definition qopenglext.h:385
static void grayscale(const QImage &image, QImage &dest, const QRect &rect=QRect())
static QT_BEGIN_NAMESPACE const QRgb colors[][14]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
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 QRgb qRgba(int r, int g, int b, int a)
Definition qrgb.h:33
constexpr int qBlue(QRgb rgb)
Definition qrgb.h:24
toff_t qtiffSeekProc(thandle_t fd, toff_t off, int whence)
tsize_t qtiffWriteProc(thandle_t fd, tdata_t buf, tsize_t size)
void qtiffUnmapProc(thandle_t fd, void *base, toff_t)
toff_t qtiffSizeProc(thandle_t fd)
static bool checkGrayscale(const QList< QRgb > &colorTable)
static QList< QRgb > effectiveColorTable(const QImage &image)
static quint32 defaultStripSize(TIFF *tiff)
static int qt2Exif(QImageIOHandler::Transformations transformation)
int qtiffCloseProc(thandle_t)
static QT_BEGIN_NAMESPACE tdata_t tsize_t size
static QImageIOHandler::Transformations exif2Qt(int exifOrientation)
int qtiffMapProc(thandle_t fd, void **base, toff_t *size)
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
unsigned short quint16
Definition qtypes.h:48
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
static const uint base
Definition qurlidna.cpp:20
QVideoFrameFormat::PixelFormat fmt
QFile file
[0]
gzip write("uncompressed data")
QGraphicsSvgItem * red