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 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | namespace Qt3DRender { |
12 | |
13 | QTextureImageDataPrivate::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 | |
31 | QByteArray 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 | |
46 | QByteArray 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 | |
69 | QTextureImageDataPrivate *QTextureImageDataPrivate::get(QTextureImageData *imageData) |
70 | { |
71 | return imageData->d_func(); |
72 | } |
73 | |
74 | void 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 | |
83 | void QTextureImageDataPrivate::setData(const QByteArray &data, |
84 | std::function<QByteArray(QByteArray rawData, int layer, int face, int mipmapLevel)> , |
85 | bool isCompressed) |
86 | { |
87 | m_isCompressed = isCompressed; |
88 | m_data = data; |
89 | m_dataExtractor = dataExtractor; |
90 | } |
91 | |
92 | int QTextureImageDataPrivate::ddsLayerSize() const |
93 | { |
94 | return m_faces * ddsFaceSize(); |
95 | } |
96 | |
97 | int 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 |
108 | int 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 | */ |
130 | QTextureImageData::QTextureImageData() |
131 | : d_ptr(new QTextureImageDataPrivate()) |
132 | { |
133 | } |
134 | |
135 | /*! \internal */ |
136 | QTextureImageData::QTextureImageData(QTextureImageDataPrivate &dd) |
137 | : d_ptr(&dd) |
138 | { |
139 | } |
140 | |
141 | /*! \internal */ |
142 | QTextureImageData::~QTextureImageData() |
143 | { |
144 | cleanup(); |
145 | delete d_ptr; |
146 | } |
147 | |
148 | QTextureImageData &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 | */ |
158 | void 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 | */ |
176 | bool 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 | */ |
185 | int 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 | */ |
194 | int 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 | */ |
203 | int 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 | */ |
212 | int 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 | */ |
221 | int 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 | */ |
230 | int 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 | */ |
240 | void 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 | */ |
250 | void 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 | */ |
260 | void 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 | */ |
270 | void 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 | */ |
280 | void 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 | */ |
290 | void 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 | */ |
299 | void 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 | */ |
308 | int 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 | */ |
317 | QOpenGLTexture::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 | */ |
326 | QOpenGLTexture::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 | */ |
335 | void 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 | */ |
344 | void 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 | */ |
353 | void 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 | */ |
362 | void 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 | */ |
371 | void 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 | */ |
395 | void QTextureImageData::setData(const QByteArray &data, int blockSize, bool isCompressed) |
396 | { |
397 | Q_D(QTextureImageData); |
398 | d->setData(data, blockSize, isCompressed); |
399 | } |
400 | |
401 | void QTextureImageData::setData(const QByteArray &data, std::function<QByteArray(QByteArray data, int layer, int face, int mipmapLevel)> , 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 | */ |
410 | QByteArray 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 | */ |
419 | QOpenGLTexture::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 | */ |
428 | QOpenGLTexture::PixelType QTextureImageData::pixelType() const noexcept |
429 | { |
430 | Q_D(const QTextureImageData); |
431 | return d->m_pixelType; |
432 | } |
433 | |
434 | } // namespace Qt3DRender |
435 | |
436 | QT_END_NAMESPACE |
437 | |