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 int curPos = mDevice->pos();
144 int fileBytes = mDevice->size();
145 if (!mDevice->seek(fileBytes - FooterSize))
146 {
147 mErrorMessage = tr("Could not seek to image read footer");
148 return;
149 }
150 char footer[FooterSize];
151 if (mDevice->read(reinterpret_cast<char*>(footer), FooterSize) != FooterSize) {
152 mErrorMessage = tr("Could not read footer");
153 }
154 if (qstrncmp(&footer[SignatureOffset], "TRUEVISION-XFILE", 16) != 0)
155 {
156 mErrorMessage = tr("Image type (non-TrueVision 2.0) not supported");
157 }
158 if (!mDevice->seek(curPos))
159 {
160 mErrorMessage = tr("Could not reset to read data");
161 }
162}
163
164/*!
165 \internal
166 Destroy the device, recovering any resources.
167*/
169{
170}
171
172/*!
173 \internal
174 Reads an image file from the QTgaFile's device, and returns it.
175
176 This method seeks to the absolute position of the image data in the file,
177 so no assumptions are made about where the devices read pointer is when this
178 method is called. For this reason only random access devices are supported.
179
180 If the constructor completed successfully, such that isValid() returns true,
181 then this method is likely to succeed, unless the file is somehow corrupted.
182
183 In the case that the read fails, the QImage returned will be null, such that
184 QImage::isNull() will be true.
185*/
187{
188 if (!isValid())
189 return QImage();
190
191 int offset = mHeader[IdLength]; // Mostly always zero
192
193 // Even in TrueColor files a color palette may be present so we have to check it here
194 // even we only support image type 2 (= uncompressed true-color image)
195 if (mHeader[ColorMapType] == 1) {
196 int cmapDepth = mHeader[CMapDepth];
197 if (cmapDepth == 15) // 15 bit is stored as 16 bit + ignoring the highest bit (no alpha)
198 cmapDepth = 16;
199 if (cmapDepth != 16 && cmapDepth != 24 && cmapDepth != 32) {
200 mErrorMessage = tr("Invalid color map depth (%1)").arg(cmapDepth);
201 return {};
202 }
203 offset += littleEndianInt(&mHeader[CMapLength]) * cmapDepth / 8;
204 }
205
206 mDevice->seek(HeaderSize + offset);
207
208 char dummy;
209 for (int i = 0; i < offset; ++i)
210 mDevice->getChar(&dummy);
211
212 int bitsPerPixel = mHeader[PixelDepth];
213 int imageWidth = width();
214 int imageHeight = height();
215
216 unsigned char desc = mHeader[ImageDescriptor];
217 //unsigned char xCorner = desc & 0x10; // 0 = left, 1 = right
218 unsigned char yCorner = desc & 0x20; // 0 = lower, 1 = upper
219
220 QImage im;
221 if (!QImageIOHandler::allocateImage(QSize(imageWidth, imageHeight), QImage::Format_ARGB32, &im))
222 return QImage();
223 TgaReader *reader = 0;
224 if (bitsPerPixel == 16)
225 reader = new Tga16Reader();
226 else if (bitsPerPixel == 24)
227 reader = new Tga24Reader();
228 else if (bitsPerPixel == 32)
229 reader = new Tga32Reader();
230 else
231 return QImage();
232 TgaReader &read = *reader;
233
234 // For now only deal with yCorner, since no one uses xCorner == 1
235 // Also this is upside down, since Qt has the origin flipped
236 if (yCorner)
237 {
238 for (int y = 0; y < imageHeight; ++y)
239 for (int x = 0; x < imageWidth; ++x)
240 im.setPixel(x, y, read(mDevice));
241 }
242 else
243 {
244 for (int y = imageHeight - 1; y >= 0; --y)
245 for (int x = 0; x < imageWidth; ++x)
246 im.setPixel(x, y, read(mDevice));
247 }
248
249 delete reader;
250
251 // TODO: add processing of TGA extension information - ie TGA 2.0 files
252 return im;
253}
bool isValid() const
Definition qtgafile.h:70
@ SignatureOffset
Definition qtgafile.h:45
@ FooterSize
Definition qtgafile.h:46
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:186