1// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
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 "qtextureimagedata_p.h"
5#include <QFileInfo>
6#include <QFile>
7#include <QDebug>
8
9QT_BEGIN_NAMESPACE
10
11namespace Qt3DRender {
12
13QTextureImageDataPrivate::QTextureImageDataPrivate()
14 : m_width(-1)
15 , m_height(-1)
16 , m_depth(-1)
17 , m_layers(-1)
18 , m_faces(-1)
19 , m_mipLevels(-1)
20 , m_blockSize(-1)
21 , m_alignment(1)
22 , m_target(QOpenGLTexture::Target2D)
23 , m_format(QOpenGLTexture::NoFormat)
24 , m_pixelFormat(QOpenGLTexture::RGBA)
25 , m_pixelType(QOpenGLTexture::UInt8)
26 , m_isCompressed(false)
27 , m_isKtx(false)
28{
29}
30
31QByteArray QTextureImageDataPrivate::ktxData(int layer, int face, int mipmapLevel) const
32{
33 Q_ASSERT(layer >= 0 && layer < m_layers &&
34 face >= 0 && face < m_faces &&
35 mipmapLevel >= 0 && mipmapLevel < m_mipLevels);
36
37 int offset = 0;
38 for (int i = 0; i < mipmapLevel; i++)
39 offset += (mipmapLevelSize(level: i) * m_faces * m_layers) + 4;
40 const int selectedMipmapLevelSize = mipmapLevelSize(level: mipmapLevel);
41 offset += (selectedMipmapLevelSize * m_faces * layer) + (selectedMipmapLevelSize * face) + 4;
42
43 return QByteArray::fromRawData(data: m_data.constData() + offset, size: selectedMipmapLevelSize);
44}
45
46QByteArray QTextureImageDataPrivate::data(int layer, int face, int mipmapLevel) const
47{
48 if (layer < 0 || layer >= m_layers ||
49 face < 0 || face >= m_faces ||
50 mipmapLevel < 0 || mipmapLevel >= m_mipLevels) {
51 qWarning() << Q_FUNC_INFO << "Requesting texture data for invalid layer, face or mipMapLevel";
52 return QByteArray();
53 }
54
55 if (m_dataExtractor)
56 return m_dataExtractor(m_data, layer, face, mipmapLevel);
57
58 if (m_isKtx)
59 return ktxData(layer, face, mipmapLevel);
60
61 int offset = layer * ddsLayerSize() + face * ddsFaceSize();
62
63 for (int i = 0; i < mipmapLevel; i++)
64 offset += mipmapLevelSize(level: i);
65
66 return QByteArray::fromRawData(data: m_data.constData() + offset, size: mipmapLevelSize(level: mipmapLevel));
67}
68
69QTextureImageDataPrivate *QTextureImageDataPrivate::get(QTextureImageData *imageData)
70{
71 return imageData->d_func();
72}
73
74void QTextureImageDataPrivate::setData(const QByteArray &data,
75 int blockSize,
76 bool isCompressed)
77{
78 m_isCompressed = isCompressed;
79 m_data = data;
80 m_blockSize = blockSize;
81}
82
83void QTextureImageDataPrivate::setData(const QByteArray &data,
84 std::function<QByteArray(QByteArray rawData, int layer, int face, int mipmapLevel)> dataExtractor,
85 bool isCompressed)
86{
87 m_isCompressed = isCompressed;
88 m_data = data;
89 m_dataExtractor = dataExtractor;
90}
91
92int QTextureImageDataPrivate::ddsLayerSize() const
93{
94 return m_faces * ddsFaceSize();
95}
96
97int QTextureImageDataPrivate::ddsFaceSize() const
98{
99 int size = 0;
100
101 for (int i = 0; i < m_mipLevels; i++)
102 size += mipmapLevelSize(level: i);
103
104 return size;
105}
106
107// XXX check if this works for ETC1 compression
108int QTextureImageDataPrivate::mipmapLevelSize(int level) const
109{
110 int w = qMax(a: m_width >> level, b: 1);
111 int h = qMax(a: m_height >> level, b: 1);
112 int d = qMax(a: m_depth >> level, b: 1);
113
114 if (m_isCompressed)
115 return ((w + 3) / 4) * ((h + 3) / 4) * m_blockSize * d;
116 else
117 return w * h * m_blockSize * d;
118}
119
120/*!
121 \class Qt3DRender::QTextureImageData
122 \inmodule Qt3DRender
123 \since 5.5
124 \brief QTextureImageData stores data representing a texture.
125 */
126
127/*!
128 Constructs a new Qt3DRender::QTextureImageData.
129*/
130QTextureImageData::QTextureImageData()
131 : d_ptr(new QTextureImageDataPrivate())
132{
133}
134
135/*! \internal */
136QTextureImageData::QTextureImageData(QTextureImageDataPrivate &dd)
137 : d_ptr(&dd)
138{
139}
140
141/*! \internal */
142QTextureImageData::~QTextureImageData()
143{
144 cleanup();
145 delete d_ptr;
146}
147
148QTextureImageData &QTextureImageData::operator=(const QTextureImageData &other)
149{
150 Q_D(QTextureImageData);
151 *d = *other.d_ptr;
152 return *this;
153}
154
155/*!
156 Remove stored texture data and return the object to its initial state
157 */
158void QTextureImageData::cleanup() noexcept
159{
160 Q_D(QTextureImageData);
161 d->m_width = -1;
162 d->m_height = -1;
163 d->m_depth = -1;
164 d->m_layers = -1;
165 d->m_faces = -1;
166 d->m_mipLevels = -1;
167 d->m_blockSize = 0;
168 d->m_alignment = 1;
169 d->m_isCompressed = false;
170 d->m_data.clear();
171}
172
173/*!
174 \return true if the stored texture is in a compressed format
175 */
176bool QTextureImageData::isCompressed() const noexcept
177{
178 Q_D(const QTextureImageData);
179 return d->m_isCompressed;
180}
181
182/*!
183 \return the width of the stored texture
184*/
185int QTextureImageData::width() const noexcept
186{
187 Q_D(const QTextureImageData);
188 return d->m_width;
189}
190
191/*!
192 \return the height of the stored texture
193 */
194int QTextureImageData::height() const noexcept
195{
196 Q_D(const QTextureImageData);
197 return d->m_height;
198}
199
200/*!
201 \return the depth of the stored texture
202 */
203int QTextureImageData::depth() const noexcept
204{
205 Q_D(const QTextureImageData);
206 return d->m_depth;
207}
208
209/*!
210 \return the number of layers in the stored texture
211 */
212int QTextureImageData::layers() const noexcept
213{
214 Q_D(const QTextureImageData);
215 return d->m_layers;
216}
217
218/*!
219 \return the number of mip levels in the stored texture
220 */
221int QTextureImageData::mipLevels() const noexcept
222{
223 Q_D(const QTextureImageData);
224 return d->m_mipLevels;
225}
226
227/*!
228 \return the number of faces in the stored texture
229 */
230int QTextureImageData::faces() const noexcept
231{
232 Q_D(const QTextureImageData);
233 return d->m_faces;;
234}
235
236/*!
237 * Sets the width to \a width.
238 * \param setWidth
239 */
240void QTextureImageData::setWidth(int width) noexcept
241{
242 Q_D(QTextureImageData);
243 d->m_width = width;
244}
245
246/*!
247 * Sets the height to \a height.
248 * \param setHeight
249 */
250void QTextureImageData::setHeight(int height) noexcept
251{
252 Q_D(QTextureImageData);
253 d->m_height = height;
254}
255
256/*!
257 * Sets the depth to \a depth.
258 * \param setDepth
259 */
260void QTextureImageData::setDepth(int depth) noexcept
261{
262 Q_D(QTextureImageData);
263 d->m_depth = depth;
264}
265
266/*!
267 * Sets the layers to \a layers.
268 * \param setLayers
269 */
270void QTextureImageData::setLayers(int layers) noexcept
271{
272 Q_D(QTextureImageData);
273 d->m_layers = layers;
274}
275
276/*!
277 * Sets the mip levels to \a mipLevels.
278 * \param setMipLevels
279 */
280void QTextureImageData::setMipLevels(int mipLevels) noexcept
281{
282 Q_D(QTextureImageData);
283 d->m_mipLevels = mipLevels;
284}
285
286/*!
287 * Sets the faces to \a faces.
288 * \param setFaces
289 */
290void QTextureImageData::setFaces(int faces) noexcept
291{
292 Q_D(QTextureImageData);
293 d->m_faces = faces;
294}
295
296/*!
297 * Sets the \a alignment requirements for the image.
298 */
299void QTextureImageData::setAlignment(int alignment) noexcept
300{
301 Q_D(QTextureImageData);
302 d->m_alignment = alignment;
303}
304
305/*!
306 \return the alignment requirement for the image.
307 */
308int QTextureImageData::alignment() const noexcept
309{
310 Q_D(const QTextureImageData);
311 return d->m_alignment;
312}
313
314/*!
315 \return the target for the stored texture.
316 */
317QOpenGLTexture::Target QTextureImageData::target() const noexcept
318{
319 Q_D(const QTextureImageData);
320 return d->m_target;
321}
322
323/*!
324 \return the format of the stored texture.
325 */
326QOpenGLTexture::TextureFormat QTextureImageData::format() const noexcept
327{
328 Q_D(const QTextureImageData);
329 return d->m_format;
330}
331
332/*!
333 * Sets the target to \a target.
334 */
335void QTextureImageData::setTarget(QOpenGLTexture::Target target) noexcept
336{
337 Q_D(QTextureImageData);
338 d->m_target = target;
339}
340
341/*!
342 * Sets the format to \a format.
343 */
344void QTextureImageData::setFormat(QOpenGLTexture::TextureFormat format) noexcept
345{
346 Q_D(QTextureImageData);
347 d->m_format = format;
348}
349
350/*!
351 * Sets the pixel format to \a pixelFormat.
352 */
353void QTextureImageData::setPixelFormat(QOpenGLTexture::PixelFormat pixelFormat) noexcept
354{
355 Q_D(QTextureImageData);
356 d->m_pixelFormat = pixelFormat;
357}
358
359/*!
360 * Sets the pixel type to \a pixelType.
361 */
362void QTextureImageData::setPixelType(QOpenGLTexture::PixelType pixelType) noexcept
363{
364 Q_D(QTextureImageData);
365 d->m_pixelType = pixelType;
366}
367
368/*!
369 Copies the image \a image as raw data within this object.
370 */
371void QTextureImageData::setImage(const QImage &image)
372{
373 Q_D(QTextureImageData);
374 d->m_width = image.width();
375 d->m_height = image.height();
376 d->m_depth = 1;
377 d->m_faces = 1;
378 d->m_layers = 1;
379 d->m_mipLevels = 1;
380 QImage glImage = image.convertToFormat(f: QImage::Format_RGBA8888);
381 Q_ASSERT_X(glImage.bytesPerLine() == (glImage.width() * glImage.depth() + 7) / 8,
382 "QTextureImageData::setImage", "glImage is not packed"); // QTBUG-48330
383 d->m_blockSize = 4;
384 QByteArray imageBytes((const char*) glImage.constBits(), glImage.sizeInBytes());
385 setData(data: imageBytes, blockSize: d->m_blockSize, isCompressed: false);
386 d->m_format = QOpenGLTexture::RGBA8_UNorm;
387 d->m_pixelFormat = QOpenGLTexture::RGBA;
388 d->m_pixelType = QOpenGLTexture::UInt8;
389 d->m_target = QOpenGLTexture::Target2D;
390}
391
392/*!
393 Stores the data \a data with blocksize \a blockSize and if the data to be stored is compressed \a isCompressed.
394 */
395void QTextureImageData::setData(const QByteArray &data, int blockSize, bool isCompressed)
396{
397 Q_D(QTextureImageData);
398 d->setData(data, blockSize, isCompressed);
399}
400
401void QTextureImageData::setData(const QByteArray &data, std::function<QByteArray(QByteArray data, int layer, int face, int mipmapLevel)> dataExtractor, bool isCompressed)
402{
403 Q_D(QTextureImageData);
404 d->setData(data, dataExtractor, isCompressed);
405}
406
407/*!
408 \return the raw image data for the texture at layer \a layer, face \a face and mipmapLevel \a mipmapLevel.
409 */
410QByteArray QTextureImageData::data(int layer, int face, int mipmapLevel) const
411{
412 Q_D(const QTextureImageData);
413 return d->data(layer, face, mipmapLevel);
414}
415
416/*!
417 \return the pixel format of the stored texture.
418 */
419QOpenGLTexture::PixelFormat QTextureImageData::pixelFormat() const noexcept
420{
421 Q_D(const QTextureImageData);
422 return d->m_pixelFormat;
423}
424
425/*!
426 \return the pixel type of the stored texture.
427 */
428QOpenGLTexture::PixelType QTextureImageData::pixelType() const noexcept
429{
430 Q_D(const QTextureImageData);
431 return d->m_pixelType;
432}
433
434} // namespace Qt3DRender
435
436QT_END_NAMESPACE
437

source code of qt3d/src/render/texture/qtextureimagedata.cpp