Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtgafile.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// Qt-Security score:critical reason:data-parser
4
5#include "qtgafile.h"
6
7#include <QtCore/QIODevice>
8#include <QtCore/QDebug>
9#include <QtCore/QDateTime>
10#include <QtGui/QImageIOHandler>
11
12namespace {
13
14struct TgaReader
15{
16 Q_DISABLE_COPY(TgaReader)
17
18 TgaReader() = default;
19
20 virtual ~TgaReader() {}
21 virtual QRgb operator()(QIODevice *s) const = 0;
22};
23
24struct Tga16Reader : public TgaReader
25{
26 ~Tga16Reader() {}
27 QRgb operator()(QIODevice *s) const override
28 {
29 char ch1, ch2;
30 if (s->getChar(&ch1) && s->getChar(&ch2)) {
31 quint16 d = (int(ch1) & 0xFF) | ((int(ch2) & 0xFF) << 8);
32 QRgb result = (d & 0x8000) ? 0xFF000000 : 0x00000000;
33 result |= ((d & 0x7C00) << 6) | ((d & 0x03E0) << 3) | (d & 0x001F);
34 return result;
35 } else {
36 return 0;
37 }
38 }
39};
40
41struct Tga24Reader : public TgaReader
42{
43 QRgb operator()(QIODevice *s) const override
44 {
45 char r, g, b;
46 if (s->getChar(&b) && s->getChar(&g) && s->getChar(&r))
47 return qRgb(uchar(r), uchar(g), uchar(b));
48 else
49 return 0;
50 }
51};
52
53struct Tga32Reader : public TgaReader
54{
55 QRgb operator()(QIODevice *s) const override
56 {
57 char r, g, b, a;
58 if (s->getChar(&b) && s->getChar(&g) && s->getChar(&r) && s->getChar(&a))
59 return qRgba(uchar(r), uchar(g), uchar(b), uchar(a));
60 else
61 return 0;
62 }
63};
64
65} // namespace
66
67/*!
68 \class QTgaFile
69 \since 4.8
70 \internal
71
72 File data container for a TrueVision Graphics format file.
73
74 Format is as described here:
75 http://local.wasp.uwa.edu.au/~pbourke/dataformats/tga/
76 http://netghost.narod.ru/gff2/graphics/summary/tga.htm
77
78 Usage is:
79 \code
80 QTgaFile tga(myFile);
81 QImage tgaImage;
82 if (tga.isValid())
83 tgaImage = tga.readImage();
84 \endcode
85
86 The class is designed to handle sequential and non-sequential
87 sources, so during construction the mHeader is read. Then during
88 the readImage() call the rest of the data is read.
89
90 After passing myFile to the constructor, if the QIODevice *myFile
91 is read, or has seek() called, the results are undefined - so don't
92 do that.
93*/
94
95/*!
96 Construct a new QTgaFile object getting data from \a device.
97
98 The object does not take ownership of the \a device, but until the
99 object is destroyed do not do any non-const operations, eg seek or
100 read on the device.
101*/
102QTgaFile::QTgaFile(QIODevice *device)
103 : mDevice(device)
104{
105 ::memset(mHeader, 0, HeaderSize);
106 if (!mDevice->isReadable())
107 {
108 mErrorMessage = tr("Could not read image data");
109 return;
110 }
111 if (mDevice->isSequential())
112 {
113 mErrorMessage = tr("Sequential device (eg socket) for image read not supported");
114 return;
115 }
116 if (!mDevice->seek(0))
117 {
118 mErrorMessage = tr("Seek file/device for image read failed");
119 return;
120 }
121 if (device->read(reinterpret_cast<char*>(mHeader), HeaderSize) != HeaderSize) {
122 mErrorMessage = tr("Image header read failed");
123 return;
124 }
125 if (mHeader[ImageType] != 2)
126 {
127 // TODO: should support other image types
128 mErrorMessage = tr("Image type not supported");
129 return;
130 }
131 int bitsPerPixel = mHeader[PixelDepth];
132 bool validDepth = (bitsPerPixel == 16 || bitsPerPixel == 24 || bitsPerPixel == 32);
133 if (!validDepth)
134 {
135 mErrorMessage = tr("Image depth not valid");
136 return;
137 }
138 if (quint64(width()) * quint64(height()) > (8192 * 8192))
139 {
140 mErrorMessage = tr("Image size exceeds limit");
141 return;
142 }
143}
144
145/*!
146 \internal
147 Destroy the device, recovering any resources.
148*/
150{
151}
152
153/*!
154 \internal
155 Reads an image file from the QTgaFile's device, and returns it.
156
157 This method seeks to the absolute position of the image data in the file,
158 so no assumptions are made about where the devices read pointer is when this
159 method is called. For this reason only random access devices are supported.
160
161 If the constructor completed successfully, such that isValid() returns true,
162 then this method is likely to succeed, unless the file is somehow corrupted.
163
164 In the case that the read fails, the QImage returned will be null, such that
165 QImage::isNull() will be true.
166*/
168{
169 if (!isValid())
170 return QImage();
171
172 int offset = mHeader[IdLength]; // Mostly always zero
173
174 // Even in TrueColor files a color palette may be present so we have to check it here
175 // even we only support image type 2 (= uncompressed true-color image)
176 if (mHeader[ColorMapType] == 1) {
177 int cmapDepth = mHeader[CMapDepth];
178 if (cmapDepth == 15) // 15 bit is stored as 16 bit + ignoring the highest bit (no alpha)
179 cmapDepth = 16;
180 if (cmapDepth != 16 && cmapDepth != 24 && cmapDepth != 32) {
181 mErrorMessage = tr("Invalid color map depth (%1)").arg(cmapDepth);
182 return {};
183 }
184 offset += littleEndianInt(&mHeader[CMapLength]) * cmapDepth / 8;
185 }
186
187 mDevice->seek(HeaderSize + offset);
188
189 char dummy;
190 for (int i = 0; i < offset; ++i)
191 mDevice->getChar(&dummy);
192
193 int bitsPerPixel = mHeader[PixelDepth];
194 int imageWidth = width();
195 int imageHeight = height();
196
197 unsigned char desc = mHeader[ImageDescriptor];
198 //unsigned char xCorner = desc & 0x10; // 0 = left, 1 = right
199 unsigned char yCorner = desc & 0x20; // 0 = lower, 1 = upper
200
201 QImage im;
202 if (!QImageIOHandler::allocateImage(QSize(imageWidth, imageHeight), QImage::Format_ARGB32, &im))
203 return QImage();
204 TgaReader *reader = 0;
205 if (bitsPerPixel == 16)
206 reader = new Tga16Reader();
207 else if (bitsPerPixel == 24)
208 reader = new Tga24Reader();
209 else if (bitsPerPixel == 32)
210 reader = new Tga32Reader();
211 else
212 return QImage();
213 TgaReader &read = *reader;
214
215 // For now only deal with yCorner, since no one uses xCorner == 1
216 // Also this is upside down, since Qt has the origin flipped
217 if (yCorner)
218 {
219 for (int y = 0; y < imageHeight; ++y)
220 for (int x = 0; x < imageWidth; ++x)
221 im.setPixel(x, y, read(mDevice));
222 }
223 else
224 {
225 for (int y = imageHeight - 1; y >= 0; --y)
226 for (int x = 0; x < imageWidth; ++x)
227 im.setPixel(x, y, read(mDevice));
228 }
229
230 delete reader;
231
232 // TODO: add processing of TGA extension information - ie TGA 2.0 files
233 return im;
234}
bool isValid() const
Definition qtgafile.h:70
int width() const
Definition qtgafile.h:99
QTgaFile(QIODevice *)
Construct a new QTgaFile object getting data from device.
Definition qtgafile.cpp:102
int height() const
Definition qtgafile.h:104
@ ColorMapType
Definition qtgafile.h:28
@ CMapDepth
Definition qtgafile.h:32
@ IdLength
Definition qtgafile.h:27
@ ImageDescriptor
Definition qtgafile.h:38
@ PixelDepth
Definition qtgafile.h:37
@ HeaderSize
Definition qtgafile.h:39
@ ImageType
Definition qtgafile.h:29
QImage readImage()
Definition qtgafile.cpp:167