1/****************************************************************************
2**
3** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qtextureimage.h"
41#include "qabstracttextureimage.h"
42#include "qtextureimage_p.h"
43#include "qtextureimagedata_p.h"
44#include "qtexturedata.h"
45#include "qtexture.h"
46#include "qtexture_p.h"
47#include <QFileInfo>
48#include <QMimeDatabase>
49#include <QMimeType>
50#include <QtCore/QBuffer>
51#include <qendian.h>
52#include <Qt3DCore/private/qscene_p.h>
53#include <Qt3DCore/qaspectengine.h>
54#include <Qt3DCore/private/qdownloadhelperservice_p.h>
55#include <Qt3DRender/private/qrenderaspect_p.h>
56#include <Qt3DRender/private/nodemanagers_p.h>
57#include <Qt3DRender/private/managers_p.h>
58#include <Qt3DRender/private/texture_p.h>
59#include <Qt3DRender/private/qurlhelper_p.h>
60#include <qmath.h>
61
62QT_BEGIN_NAMESPACE
63
64namespace Qt3DRender {
65
66namespace {
67
68struct DdsPixelFormat
69{
70 quint32 size;
71 quint32 flags;
72 quint32 fourCC;
73 quint32 rgbBitCount;
74 quint32 redMask;
75 quint32 greenMask;
76 quint32 blueMask;
77 quint32 alphaMask;
78};
79
80struct DdsHeader
81{
82 char magic[4];
83 quint32 size;
84 quint32 flags;
85 quint32 height;
86 quint32 width;
87 quint32 pitchOrLinearSize;
88 quint32 depth;
89 quint32 mipmapCount;
90 quint32 reserved[11];
91 DdsPixelFormat pixelFormat;
92 quint32 caps;
93 quint32 caps2;
94 quint32 caps3;
95 quint32 caps4;
96 quint32 reserved2;
97};
98
99struct DdsDX10Header
100{
101 quint32 format;
102 quint32 dimension;
103 quint32 miscFlag;
104 quint32 arraySize;
105 quint32 miscFlags2;
106};
107
108enum DdsFlags
109{
110 MipmapCountFlag = 0x20000,
111};
112
113enum PixelFormatFlag
114{
115 AlphaFlag = 0x1,
116 FourCCFlag = 0x4,
117 RGBFlag = 0x40,
118 RGBAFlag = RGBFlag | AlphaFlag,
119 YUVFlag = 0x200,
120 LuminanceFlag = 0x20000
121};
122
123enum Caps2Flags
124{
125 CubemapFlag = 0x200,
126 CubemapPositiveXFlag = 0x400,
127 CubemapNegativeXFlag = 0x800,
128 CubemapPositiveYFlag = 0x1000,
129 CubemapNegativeYFlag = 0x2000,
130 CubemapPositiveZFlag = 0x4000,
131 CubemapNegativeZFlag = 0x8000,
132 AllCubemapFaceFlags = CubemapPositiveXFlag |
133 CubemapNegativeXFlag |
134 CubemapPositiveYFlag |
135 CubemapNegativeYFlag |
136 CubemapPositiveZFlag |
137 CubemapNegativeZFlag,
138 VolumeFlag = 0x200000
139};
140
141enum DXGIFormat
142{
143 DXGI_FORMAT_UNKNOWN = 0,
144 DXGI_FORMAT_R32G32B32A32_TYPELESS = 1,
145 DXGI_FORMAT_R32G32B32A32_FLOAT = 2,
146 DXGI_FORMAT_R32G32B32A32_UINT = 3,
147 DXGI_FORMAT_R32G32B32A32_SINT = 4,
148 DXGI_FORMAT_R32G32B32_TYPELESS = 5,
149 DXGI_FORMAT_R32G32B32_FLOAT = 6,
150 DXGI_FORMAT_R32G32B32_UINT = 7,
151 DXGI_FORMAT_R32G32B32_SINT = 8,
152 DXGI_FORMAT_R16G16B16A16_TYPELESS = 9,
153 DXGI_FORMAT_R16G16B16A16_FLOAT = 10,
154 DXGI_FORMAT_R16G16B16A16_UNORM = 11,
155 DXGI_FORMAT_R16G16B16A16_UINT = 12,
156 DXGI_FORMAT_R16G16B16A16_SNORM = 13,
157 DXGI_FORMAT_R16G16B16A16_SINT = 14,
158 DXGI_FORMAT_R32G32_TYPELESS = 15,
159 DXGI_FORMAT_R32G32_FLOAT = 16,
160 DXGI_FORMAT_R32G32_UINT = 17,
161 DXGI_FORMAT_R32G32_SINT = 18,
162 DXGI_FORMAT_R32G8X24_TYPELESS = 19,
163 DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20,
164 DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21,
165 DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22,
166 DXGI_FORMAT_R10G10B10A2_TYPELESS = 23,
167 DXGI_FORMAT_R10G10B10A2_UNORM = 24,
168 DXGI_FORMAT_R10G10B10A2_UINT = 25,
169 DXGI_FORMAT_R11G11B10_FLOAT = 26,
170 DXGI_FORMAT_R8G8B8A8_TYPELESS = 27,
171 DXGI_FORMAT_R8G8B8A8_UNORM = 28,
172 DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29,
173 DXGI_FORMAT_R8G8B8A8_UINT = 30,
174 DXGI_FORMAT_R8G8B8A8_SNORM = 31,
175 DXGI_FORMAT_R8G8B8A8_SINT = 32,
176 DXGI_FORMAT_R16G16_TYPELESS = 33,
177 DXGI_FORMAT_R16G16_FLOAT = 34,
178 DXGI_FORMAT_R16G16_UNORM = 35,
179 DXGI_FORMAT_R16G16_UINT = 36,
180 DXGI_FORMAT_R16G16_SNORM = 37,
181 DXGI_FORMAT_R16G16_SINT = 38,
182 DXGI_FORMAT_R32_TYPELESS = 39,
183 DXGI_FORMAT_D32_FLOAT = 40,
184 DXGI_FORMAT_R32_FLOAT = 41,
185 DXGI_FORMAT_R32_UINT = 42,
186 DXGI_FORMAT_R32_SINT = 43,
187 DXGI_FORMAT_R24G8_TYPELESS = 44,
188 DXGI_FORMAT_D24_UNORM_S8_UINT = 45,
189 DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46,
190 DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47,
191 DXGI_FORMAT_R8G8_TYPELESS = 48,
192 DXGI_FORMAT_R8G8_UNORM = 49,
193 DXGI_FORMAT_R8G8_UINT = 50,
194 DXGI_FORMAT_R8G8_SNORM = 51,
195 DXGI_FORMAT_R8G8_SINT = 52,
196 DXGI_FORMAT_R16_TYPELESS = 53,
197 DXGI_FORMAT_R16_FLOAT = 54,
198 DXGI_FORMAT_D16_UNORM = 55,
199 DXGI_FORMAT_R16_UNORM = 56,
200 DXGI_FORMAT_R16_UINT = 57,
201 DXGI_FORMAT_R16_SNORM = 58,
202 DXGI_FORMAT_R16_SINT = 59,
203 DXGI_FORMAT_R8_TYPELESS = 60,
204 DXGI_FORMAT_R8_UNORM = 61,
205 DXGI_FORMAT_R8_UINT = 62,
206 DXGI_FORMAT_R8_SNORM = 63,
207 DXGI_FORMAT_R8_SINT = 64,
208 DXGI_FORMAT_A8_UNORM = 65,
209 DXGI_FORMAT_R1_UNORM = 66,
210 DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67,
211 DXGI_FORMAT_R8G8_B8G8_UNORM = 68,
212 DXGI_FORMAT_G8R8_G8B8_UNORM = 69,
213 DXGI_FORMAT_BC1_TYPELESS = 70,
214 DXGI_FORMAT_BC1_UNORM = 71,
215 DXGI_FORMAT_BC1_UNORM_SRGB = 72,
216 DXGI_FORMAT_BC2_TYPELESS = 73,
217 DXGI_FORMAT_BC2_UNORM = 74,
218 DXGI_FORMAT_BC2_UNORM_SRGB = 75,
219 DXGI_FORMAT_BC3_TYPELESS = 76,
220 DXGI_FORMAT_BC3_UNORM = 77,
221 DXGI_FORMAT_BC3_UNORM_SRGB = 78,
222 DXGI_FORMAT_BC4_TYPELESS = 79,
223 DXGI_FORMAT_BC4_UNORM = 80,
224 DXGI_FORMAT_BC4_SNORM = 81,
225 DXGI_FORMAT_BC5_TYPELESS = 82,
226 DXGI_FORMAT_BC5_UNORM = 83,
227 DXGI_FORMAT_BC5_SNORM = 84,
228 DXGI_FORMAT_B5G6R5_UNORM = 85,
229 DXGI_FORMAT_B5G5R5A1_UNORM = 86,
230 DXGI_FORMAT_B8G8R8A8_UNORM = 87,
231 DXGI_FORMAT_B8G8R8X8_UNORM = 88,
232 DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89,
233 DXGI_FORMAT_B8G8R8A8_TYPELESS = 90,
234 DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91,
235 DXGI_FORMAT_B8G8R8X8_TYPELESS = 92,
236 DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93,
237 DXGI_FORMAT_BC6H_TYPELESS = 94,
238 DXGI_FORMAT_BC6H_UF16 = 95,
239 DXGI_FORMAT_BC6H_SF16 = 96,
240 DXGI_FORMAT_BC7_TYPELESS = 97,
241 DXGI_FORMAT_BC7_UNORM = 98,
242 DXGI_FORMAT_BC7_UNORM_SRGB = 99,
243 DXGI_FORMAT_AYUV = 100,
244 DXGI_FORMAT_Y410 = 101,
245 DXGI_FORMAT_Y416 = 102,
246 DXGI_FORMAT_NV12 = 103,
247 DXGI_FORMAT_P010 = 104,
248 DXGI_FORMAT_P016 = 105,
249 DXGI_FORMAT_420_OPAQUE = 106,
250 DXGI_FORMAT_YUY2 = 107,
251 DXGI_FORMAT_Y210 = 108,
252 DXGI_FORMAT_Y216 = 109,
253 DXGI_FORMAT_NV11 = 110,
254 DXGI_FORMAT_AI44 = 111,
255 DXGI_FORMAT_IA44 = 112,
256 DXGI_FORMAT_P8 = 113,
257 DXGI_FORMAT_A8P8 = 114,
258 DXGI_FORMAT_B4G4R4A4_UNORM = 115,
259 DXGI_FORMAT_P208 = 130,
260 DXGI_FORMAT_V208 = 131,
261 DXGI_FORMAT_V408 = 132,
262};
263
264template <int a, int b, int c, int d>
265struct DdsFourCC
266{
267 static const quint32 value = a | (b << 8) | (c << 16) | (d << 24);
268};
269
270struct FormatInfo
271{
272 QOpenGLTexture::PixelFormat pixelFormat;
273 QOpenGLTexture::TextureFormat textureFormat;
274 QOpenGLTexture::PixelType pixelType;
275 int components;
276 bool compressed;
277};
278
279const struct RGBAFormat
280{
281 quint32 redMask;
282 quint32 greenMask;
283 quint32 blueMask;
284 quint32 alphaMask;
285 FormatInfo formatInfo;
286
287} rgbaFormats[] = {
288 // unorm formats
289{ .redMask: 0x000000ff, .greenMask: 0x0000ff00, .blueMask: 0x00ff0000, .alphaMask: 0xff000000, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::RGBA8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 4, .compressed: false } },
290{ .redMask: 0x00ff0000, .greenMask: 0x0000ff00, .blueMask: 0x000000ff, .alphaMask: 0xff000000, .formatInfo: { .pixelFormat: QOpenGLTexture::BGRA, .textureFormat: QOpenGLTexture::RGBA8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 4, .compressed: false } },
291{ .redMask: 0x000000ff, .greenMask: 0x0000ff00, .blueMask: 0x00ff0000, .alphaMask: 0x00000000, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::RGBA8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 4, .compressed: false } },
292{ .redMask: 0x00ff0000, .greenMask: 0x0000ff00, .blueMask: 0x000000ff, .alphaMask: 0x00000000, .formatInfo: { .pixelFormat: QOpenGLTexture::BGRA, .textureFormat: QOpenGLTexture::RGBA8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 4, .compressed: false } },
293{ .redMask: 0x000000ff, .greenMask: 0x0000ff00, .blueMask: 0x00ff0000, .alphaMask: 0x00000000, .formatInfo: { .pixelFormat: QOpenGLTexture::RGB, .textureFormat: QOpenGLTexture::RGB8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 3, .compressed: false } },
294{ .redMask: 0x00ff0000, .greenMask: 0x0000ff00, .blueMask: 0x000000ff, .alphaMask: 0x00000000, .formatInfo: { .pixelFormat: QOpenGLTexture::BGR, .textureFormat: QOpenGLTexture::RGB8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 3, .compressed: false } },
295
296// packed formats
297{ .redMask: 0x0000f800, .greenMask: 0x000007e0, .blueMask: 0x0000001f, .alphaMask: 0x00000000, .formatInfo: { .pixelFormat: QOpenGLTexture::RGB, .textureFormat: QOpenGLTexture::R5G6B5, .pixelType: QOpenGLTexture::UInt16_R5G6B5, .components: 2, .compressed: false } },
298{ .redMask: 0x00007c00, .greenMask: 0x000003e0, .blueMask: 0x0000001f, .alphaMask: 0x00008000, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::RGB5A1, .pixelType: QOpenGLTexture::UInt16_RGB5A1, .components: 2, .compressed: false } },
299{ .redMask: 0x00000f00, .greenMask: 0x000000f0, .blueMask: 0x0000000f, .alphaMask: 0x0000f000, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::RGBA4, .pixelType: QOpenGLTexture::UInt16_RGBA4, .components: 2, .compressed: false } },
300{ .redMask: 0x000000e0, .greenMask: 0x0000001c, .blueMask: 0x00000003, .alphaMask: 0x00000000, .formatInfo: { .pixelFormat: QOpenGLTexture::RGB, .textureFormat: QOpenGLTexture::RG3B2, .pixelType: QOpenGLTexture::UInt8_RG3B2, .components: 1, .compressed: false } },
301{ .redMask: 0x3ff00000, .greenMask: 0x000ffc00, .blueMask: 0x000003ff, .alphaMask: 0xc0000000, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::RGB10A2, .pixelType: QOpenGLTexture::UInt32_RGB10A2, .components: 4, .compressed: false } },
302
303// luminance alpha
304{ .redMask: 0x000000ff, .greenMask: 0x000000ff, .blueMask: 0x000000ff, .alphaMask: 0x00000000, .formatInfo: { .pixelFormat: QOpenGLTexture::Red, .textureFormat: QOpenGLTexture::R8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 1, .compressed: false } },
305{ .redMask: 0x000000ff, .greenMask: 0x00000000, .blueMask: 0x00000000, .alphaMask: 0x00000000, .formatInfo: { .pixelFormat: QOpenGLTexture::Red, .textureFormat: QOpenGLTexture::R8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 1, .compressed: false } },
306{ .redMask: 0x000000ff, .greenMask: 0x000000ff, .blueMask: 0x000000ff, .alphaMask: 0x0000ff00, .formatInfo: { .pixelFormat: QOpenGLTexture::RG, .textureFormat: QOpenGLTexture::RG8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 2, .compressed: false } },
307{ .redMask: 0x000000ff, .greenMask: 0x00000000, .blueMask: 0x00000000, .alphaMask: 0x0000ff00, .formatInfo: { .pixelFormat: QOpenGLTexture::RG, .textureFormat: QOpenGLTexture::RG8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 2, .compressed: false } },
308};
309
310const struct FourCCFormat
311{
312 quint32 fourCC;
313 FormatInfo formatInfo;
314} fourCCFormats[] = {
315{ .fourCC: DdsFourCC<'D','X','T','1'>::value, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::RGBA_DXT1, .pixelType: QOpenGLTexture::NoPixelType, .components: 8, .compressed: true } },
316{ .fourCC: DdsFourCC<'D','X','T','3'>::value, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::RGBA_DXT3, .pixelType: QOpenGLTexture::NoPixelType, .components: 16, .compressed: true } },
317{ .fourCC: DdsFourCC<'D','X','T','5'>::value, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::RGBA_DXT5, .pixelType: QOpenGLTexture::NoPixelType, .components: 16, .compressed: true } },
318{ .fourCC: DdsFourCC<'A','T','I','1'>::value, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::R_ATI1N_UNorm, .pixelType: QOpenGLTexture::NoPixelType, .components: 8, .compressed: true } },
319{ .fourCC: DdsFourCC<'A','T','I','2'>::value, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::RG_ATI2N_UNorm, .pixelType: QOpenGLTexture::NoPixelType, .components: 16, .compressed: true } },
320{ /* DXGI_FORMAT_R16_FLOAT */ .fourCC: 111, .formatInfo: { .pixelFormat: QOpenGLTexture::Red, .textureFormat: QOpenGLTexture::R16F, .pixelType: QOpenGLTexture::Float16, .components: 2, .compressed: false } },
321{ /* DXGI_FORMAT_R16_FLOAT */ .fourCC: 112, .formatInfo: { .pixelFormat: QOpenGLTexture::RG, .textureFormat: QOpenGLTexture::RG16F, .pixelType: QOpenGLTexture::Float16, .components: 4, .compressed: false } },
322{ /* DXGI_FORMAT_R16G16B16A16_FLOAT */.fourCC: 113, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::RGBA16F, .pixelType: QOpenGLTexture::Float16, .components: 8, .compressed: false } },
323{ /* DXGI_FORMAT_R32_FLOAT */ .fourCC: 114, .formatInfo: { .pixelFormat: QOpenGLTexture::Red, .textureFormat: QOpenGLTexture::R32F, .pixelType: QOpenGLTexture::Float32, .components: 4, .compressed: false } },
324{ /* DXGI_FORMAT_R32G32_FLOAT */ .fourCC: 115, .formatInfo: { .pixelFormat: QOpenGLTexture::RG, .textureFormat: QOpenGLTexture::RG32F, .pixelType: QOpenGLTexture::Float32, .components: 8, .compressed: false } },
325{ /* DXGI_FORMAT_R32G32B32A32_FLOAT */.fourCC: 116, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::RGBA32F, .pixelType: QOpenGLTexture::Float32, .components: 16, .compressed: false } }
326};
327
328const struct DX10Format
329{
330 DXGIFormat dxgiFormat;
331 FormatInfo formatInfo;
332} dx10Formats[] = {
333 // unorm formats
334{ .dxgiFormat: DXGI_FORMAT_R8_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::Red, .textureFormat: QOpenGLTexture::R8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 1, .compressed: false } },
335{ .dxgiFormat: DXGI_FORMAT_R8G8_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::RG, .textureFormat: QOpenGLTexture::RG8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 2, .compressed: false } },
336{ .dxgiFormat: DXGI_FORMAT_R8G8B8A8_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::RGBA8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 4, .compressed: false } },
337
338{ .dxgiFormat: DXGI_FORMAT_R16_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::Red, .textureFormat: QOpenGLTexture::R16_UNorm, .pixelType: QOpenGLTexture::UInt16, .components: 2, .compressed: false } },
339{ .dxgiFormat: DXGI_FORMAT_R16G16_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::RG, .textureFormat: QOpenGLTexture::RG16_UNorm, .pixelType: QOpenGLTexture::UInt16, .components: 4, .compressed: false } },
340{ .dxgiFormat: DXGI_FORMAT_R16G16B16A16_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::RGBA16_UNorm, .pixelType: QOpenGLTexture::UInt16, .components: 8, .compressed: false } },
341
342// snorm formats
343{ .dxgiFormat: DXGI_FORMAT_R8_SNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::Red, .textureFormat: QOpenGLTexture::R8_SNorm, .pixelType: QOpenGLTexture::Int8, .components: 1, .compressed: false } },
344{ .dxgiFormat: DXGI_FORMAT_R8G8_SNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::RG, .textureFormat: QOpenGLTexture::RG8_SNorm, .pixelType: QOpenGLTexture::Int8, .components: 2, .compressed: false } },
345{ .dxgiFormat: DXGI_FORMAT_R8G8B8A8_SNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::RGBA8_SNorm, .pixelType: QOpenGLTexture::Int8, .components: 4, .compressed: false } },
346
347{ .dxgiFormat: DXGI_FORMAT_R16_SNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::Red, .textureFormat: QOpenGLTexture::R16_SNorm, .pixelType: QOpenGLTexture::Int16, .components: 2, .compressed: false } },
348{ .dxgiFormat: DXGI_FORMAT_R16G16_SNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::RG, .textureFormat: QOpenGLTexture::RG16_SNorm, .pixelType: QOpenGLTexture::Int16, .components: 4, .compressed: false } },
349{ .dxgiFormat: DXGI_FORMAT_R16G16B16A16_SNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::RGBA16_SNorm, .pixelType: QOpenGLTexture::Int16, .components: 8, .compressed: false } },
350
351// unsigned integer formats
352{ .dxgiFormat: DXGI_FORMAT_R8_UINT, .formatInfo: { .pixelFormat: QOpenGLTexture::Red_Integer, .textureFormat: QOpenGLTexture::R8U, .pixelType: QOpenGLTexture::UInt8, .components: 1, .compressed: false } },
353{ .dxgiFormat: DXGI_FORMAT_R8G8_UINT, .formatInfo: { .pixelFormat: QOpenGLTexture::RG_Integer, .textureFormat: QOpenGLTexture::RG8U, .pixelType: QOpenGLTexture::UInt8, .components: 2, .compressed: false } },
354{ .dxgiFormat: DXGI_FORMAT_R8G8B8A8_UINT, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA_Integer, .textureFormat: QOpenGLTexture::RGBA8U, .pixelType: QOpenGLTexture::UInt8, .components: 4, .compressed: false } },
355
356{ .dxgiFormat: DXGI_FORMAT_R16_UINT, .formatInfo: { .pixelFormat: QOpenGLTexture::Red_Integer, .textureFormat: QOpenGLTexture::R16U, .pixelType: QOpenGLTexture::UInt16, .components: 2, .compressed: false } },
357{ .dxgiFormat: DXGI_FORMAT_R16G16_UINT, .formatInfo: { .pixelFormat: QOpenGLTexture::RG_Integer, .textureFormat: QOpenGLTexture::RG16U, .pixelType: QOpenGLTexture::UInt16, .components: 4, .compressed: false } },
358{ .dxgiFormat: DXGI_FORMAT_R16G16B16A16_UINT, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA_Integer, .textureFormat: QOpenGLTexture::RGBA16U, .pixelType: QOpenGLTexture::UInt16, .components: 8, .compressed: false } },
359
360{ .dxgiFormat: DXGI_FORMAT_R32_UINT, .formatInfo: { .pixelFormat: QOpenGLTexture::Red_Integer, .textureFormat: QOpenGLTexture::R32U, .pixelType: QOpenGLTexture::UInt32, .components: 4, .compressed: false } },
361{ .dxgiFormat: DXGI_FORMAT_R32G32_UINT, .formatInfo: { .pixelFormat: QOpenGLTexture::RG_Integer, .textureFormat: QOpenGLTexture::RG32U, .pixelType: QOpenGLTexture::UInt32, .components: 8, .compressed: false } },
362{ .dxgiFormat: DXGI_FORMAT_R32G32B32_UINT, .formatInfo: { .pixelFormat: QOpenGLTexture::RGB_Integer, .textureFormat: QOpenGLTexture::RGB32U, .pixelType: QOpenGLTexture::UInt32, .components: 12, .compressed: false } },
363{ .dxgiFormat: DXGI_FORMAT_R32G32B32A32_UINT, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA_Integer, .textureFormat: QOpenGLTexture::RGBA32U, .pixelType: QOpenGLTexture::UInt32, .components: 16, .compressed: false } },
364
365// signed integer formats
366{ .dxgiFormat: DXGI_FORMAT_R8_SINT, .formatInfo: { .pixelFormat: QOpenGLTexture::Red_Integer, .textureFormat: QOpenGLTexture::R8I, .pixelType: QOpenGLTexture::Int8, .components: 1, .compressed: false } },
367{ .dxgiFormat: DXGI_FORMAT_R8G8_SINT, .formatInfo: { .pixelFormat: QOpenGLTexture::RG_Integer, .textureFormat: QOpenGLTexture::RG8I, .pixelType: QOpenGLTexture::Int8, .components: 2, .compressed: false } },
368{ .dxgiFormat: DXGI_FORMAT_R8G8B8A8_SINT, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA_Integer, .textureFormat: QOpenGLTexture::RGBA8I, .pixelType: QOpenGLTexture::Int8, .components: 4, .compressed: false } },
369
370{ .dxgiFormat: DXGI_FORMAT_R16_SINT, .formatInfo: { .pixelFormat: QOpenGLTexture::Red_Integer, .textureFormat: QOpenGLTexture::R16I, .pixelType: QOpenGLTexture::Int16, .components: 2, .compressed: false } },
371{ .dxgiFormat: DXGI_FORMAT_R16G16_SINT, .formatInfo: { .pixelFormat: QOpenGLTexture::RG_Integer, .textureFormat: QOpenGLTexture::RG16I, .pixelType: QOpenGLTexture::Int16, .components: 4, .compressed: false } },
372{ .dxgiFormat: DXGI_FORMAT_R16G16B16A16_SINT, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA_Integer, .textureFormat: QOpenGLTexture::RGBA16I, .pixelType: QOpenGLTexture::Int16, .components: 8, .compressed: false } },
373
374{ .dxgiFormat: DXGI_FORMAT_R32_SINT, .formatInfo: { .pixelFormat: QOpenGLTexture::Red_Integer, .textureFormat: QOpenGLTexture::R32I, .pixelType: QOpenGLTexture::Int32, .components: 4, .compressed: false } },
375{ .dxgiFormat: DXGI_FORMAT_R32G32_SINT, .formatInfo: { .pixelFormat: QOpenGLTexture::RG_Integer, .textureFormat: QOpenGLTexture::RG32I, .pixelType: QOpenGLTexture::Int32, .components: 8, .compressed: false } },
376{ .dxgiFormat: DXGI_FORMAT_R32G32B32_SINT, .formatInfo: { .pixelFormat: QOpenGLTexture::RGB_Integer, .textureFormat: QOpenGLTexture::RGB32I, .pixelType: QOpenGLTexture::Int32, .components: 12, .compressed: false } },
377{ .dxgiFormat: DXGI_FORMAT_R32G32B32A32_SINT, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA_Integer, .textureFormat: QOpenGLTexture::RGBA32I, .pixelType: QOpenGLTexture::Int32, .components: 16, .compressed: false } },
378
379// floating formats
380{ .dxgiFormat: DXGI_FORMAT_R16_FLOAT, .formatInfo: { .pixelFormat: QOpenGLTexture::Red, .textureFormat: QOpenGLTexture::R16F, .pixelType: QOpenGLTexture::Float16, .components: 2, .compressed: false } },
381{ .dxgiFormat: DXGI_FORMAT_R16G16_FLOAT, .formatInfo: { .pixelFormat: QOpenGLTexture::RG, .textureFormat: QOpenGLTexture::RG16F, .pixelType: QOpenGLTexture::Float16, .components: 4, .compressed: false } },
382{ .dxgiFormat: DXGI_FORMAT_R16G16B16A16_FLOAT, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::RGBA16F, .pixelType: QOpenGLTexture::Float16, .components: 8, .compressed: false } },
383
384{ .dxgiFormat: DXGI_FORMAT_R32_FLOAT, .formatInfo: { .pixelFormat: QOpenGLTexture::Red, .textureFormat: QOpenGLTexture::R32F, .pixelType: QOpenGLTexture::Float32, .components: 4, .compressed: false } },
385{ .dxgiFormat: DXGI_FORMAT_R32G32_FLOAT, .formatInfo: { .pixelFormat: QOpenGLTexture::RG, .textureFormat: QOpenGLTexture::RG32F, .pixelType: QOpenGLTexture::Float32, .components: 8, .compressed: false } },
386{ .dxgiFormat: DXGI_FORMAT_R32G32B32_FLOAT, .formatInfo: { .pixelFormat: QOpenGLTexture::RGB, .textureFormat: QOpenGLTexture::RGB32F, .pixelType: QOpenGLTexture::Float32, .components: 12, .compressed: false } },
387{ .dxgiFormat: DXGI_FORMAT_R32G32B32A32_FLOAT, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::RGBA32F, .pixelType: QOpenGLTexture::Float32, .components: 16, .compressed: false } },
388
389// sRGB formats
390{ .dxgiFormat: DXGI_FORMAT_B8G8R8X8_UNORM_SRGB, .formatInfo: { .pixelFormat: QOpenGLTexture::RGB, .textureFormat: QOpenGLTexture::SRGB8, .pixelType: QOpenGLTexture::UInt8, .components: 4, .compressed: false } },
391{ .dxgiFormat: DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::SRGB8_Alpha8, .pixelType: QOpenGLTexture::UInt8, .components: 4, .compressed: false } },
392
393// packed formats
394// { DXGI_FORMAT_R10G10B10A2_UNORM, { QOpenGLTexture::RGB10A2_UNORM, QOpenGLTexture::RGBA, QOpenGLTexture::UInt32_RGB10A2, 4, false } },
395{ .dxgiFormat: DXGI_FORMAT_R10G10B10A2_UINT, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA_Integer, .textureFormat: QOpenGLTexture::RGB10A2, .pixelType: QOpenGLTexture::UInt32_RGB10A2, .components: 4, .compressed: false } },
396{ .dxgiFormat: DXGI_FORMAT_R9G9B9E5_SHAREDEXP, .formatInfo: { .pixelFormat: QOpenGLTexture::RGB, .textureFormat: QOpenGLTexture::RGB9E5, .pixelType: QOpenGLTexture::UInt32_RGB9_E5, .components: 4, .compressed: false } },
397{ .dxgiFormat: DXGI_FORMAT_R11G11B10_FLOAT, .formatInfo: { .pixelFormat: QOpenGLTexture::RGB, .textureFormat: QOpenGLTexture::RG11B10F, .pixelType: QOpenGLTexture::UInt32_RG11B10F, .components: 4, .compressed: false } },
398{ .dxgiFormat: DXGI_FORMAT_B5G6R5_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::RGB, .textureFormat: QOpenGLTexture::R5G6B5, .pixelType: QOpenGLTexture::UInt16_R5G6B5, .components: 2, .compressed: false } },
399{ .dxgiFormat: DXGI_FORMAT_B5G5R5A1_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::RGB5A1, .pixelType: QOpenGLTexture::UInt16_RGB5A1, .components: 2, .compressed: false } },
400{ .dxgiFormat: DXGI_FORMAT_B4G4R4A4_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::RGBA, .textureFormat: QOpenGLTexture::RGBA4, .pixelType: QOpenGLTexture::UInt16_RGBA4, .components: 2, .compressed: false } },
401
402// swizzle formats
403{ .dxgiFormat: DXGI_FORMAT_B8G8R8X8_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::BGRA, .textureFormat: QOpenGLTexture::RGB8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 4, .compressed: false } },
404{ .dxgiFormat: DXGI_FORMAT_B8G8R8A8_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::BGRA, .textureFormat: QOpenGLTexture::RGBA8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 4, .compressed: false } },
405{ .dxgiFormat: DXGI_FORMAT_B8G8R8X8_UNORM_SRGB, .formatInfo: { .pixelFormat: QOpenGLTexture::BGRA, .textureFormat: QOpenGLTexture::SRGB8, .pixelType: QOpenGLTexture::UInt8, .components: 4, .compressed: false } },
406{ .dxgiFormat: DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, .formatInfo: { .pixelFormat: QOpenGLTexture::BGRA, .textureFormat: QOpenGLTexture::SRGB8_Alpha8, .pixelType: QOpenGLTexture::UInt8, .components: 4, .compressed: false } },
407
408// luminance alpha formats
409{ .dxgiFormat: DXGI_FORMAT_R8_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::Red, .textureFormat: QOpenGLTexture::R8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 1, .compressed: false } },
410{ .dxgiFormat: DXGI_FORMAT_R8G8_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::RG, .textureFormat: QOpenGLTexture::RG8_UNorm, .pixelType: QOpenGLTexture::UInt8, .components: 2, .compressed: false } },
411{ .dxgiFormat: DXGI_FORMAT_R16_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::Red, .textureFormat: QOpenGLTexture::R16_UNorm, .pixelType: QOpenGLTexture::UInt16, .components: 2, .compressed: false } },
412{ .dxgiFormat: DXGI_FORMAT_R16G16_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::RG, .textureFormat: QOpenGLTexture::RG16_UNorm, .pixelType: QOpenGLTexture::UInt16, .components: 4, .compressed: false } },
413
414// depth formats
415{ .dxgiFormat: DXGI_FORMAT_D16_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::Depth, .textureFormat: QOpenGLTexture::D16, .pixelType: QOpenGLTexture::NoPixelType, .components: 2, .compressed: false } },
416{ .dxgiFormat: DXGI_FORMAT_D24_UNORM_S8_UINT, .formatInfo: { .pixelFormat: QOpenGLTexture::DepthStencil, .textureFormat: QOpenGLTexture::D24S8, .pixelType: QOpenGLTexture::NoPixelType, .components: 4, .compressed: false } },
417{ .dxgiFormat: DXGI_FORMAT_D32_FLOAT, .formatInfo: { .pixelFormat: QOpenGLTexture::Depth, .textureFormat: QOpenGLTexture::D32F, .pixelType: QOpenGLTexture::NoPixelType, .components: 4, .compressed: false } },
418{ .dxgiFormat: DXGI_FORMAT_D32_FLOAT_S8X24_UINT, .formatInfo: { .pixelFormat: QOpenGLTexture::DepthStencil, .textureFormat: QOpenGLTexture::D32FS8X24, .pixelType: QOpenGLTexture::NoPixelType, .components: 8, .compressed: false } },
419
420// compressed formats
421{ .dxgiFormat: DXGI_FORMAT_BC1_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::RGBA_DXT1, .pixelType: QOpenGLTexture::NoPixelType, .components: 8, .compressed: true } },
422{ .dxgiFormat: DXGI_FORMAT_BC2_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::RGBA_DXT3, .pixelType: QOpenGLTexture::NoPixelType, .components: 16, .compressed: true } },
423{ .dxgiFormat: DXGI_FORMAT_BC3_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::RGBA_DXT5, .pixelType: QOpenGLTexture::NoPixelType, .components: 16, .compressed: true } },
424{ .dxgiFormat: DXGI_FORMAT_BC4_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::R_ATI1N_UNorm, .pixelType: QOpenGLTexture::NoPixelType, .components: 8, .compressed: true } },
425{ .dxgiFormat: DXGI_FORMAT_BC4_SNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::R_ATI1N_SNorm, .pixelType: QOpenGLTexture::NoPixelType, .components: 8, .compressed: true } },
426{ .dxgiFormat: DXGI_FORMAT_BC5_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::RG_ATI2N_UNorm, .pixelType: QOpenGLTexture::NoPixelType, .components: 16, .compressed: true } },
427{ .dxgiFormat: DXGI_FORMAT_BC5_SNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::RG_ATI2N_SNorm, .pixelType: QOpenGLTexture::NoPixelType, .components: 16, .compressed: true } },
428{ .dxgiFormat: DXGI_FORMAT_BC6H_UF16, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::RGB_BP_UNSIGNED_FLOAT, .pixelType: QOpenGLTexture::NoPixelType, .components: 16, .compressed: true } },
429{ .dxgiFormat: DXGI_FORMAT_BC6H_SF16, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::RGB_BP_SIGNED_FLOAT, .pixelType: QOpenGLTexture::NoPixelType, .components: 16, .compressed: true } },
430{ .dxgiFormat: DXGI_FORMAT_BC7_UNORM, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::RGB_BP_UNorm, .pixelType: QOpenGLTexture::NoPixelType, .components: 16, .compressed: true } },
431
432// compressed sRGB formats
433{ .dxgiFormat: DXGI_FORMAT_BC1_UNORM_SRGB, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::SRGB_DXT1, .pixelType: QOpenGLTexture::NoPixelType, .components: 8, .compressed: true } },
434{ .dxgiFormat: DXGI_FORMAT_BC1_UNORM_SRGB, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::SRGB_Alpha_DXT1, .pixelType: QOpenGLTexture::NoPixelType, .components: 8, .compressed: true } },
435{ .dxgiFormat: DXGI_FORMAT_BC2_UNORM_SRGB, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::SRGB_Alpha_DXT3, .pixelType: QOpenGLTexture::NoPixelType, .components: 16, .compressed: true } },
436{ .dxgiFormat: DXGI_FORMAT_BC3_UNORM_SRGB, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::SRGB_Alpha_DXT5, .pixelType: QOpenGLTexture::NoPixelType, .components: 16, .compressed: true } },
437{ .dxgiFormat: DXGI_FORMAT_BC7_UNORM_SRGB, .formatInfo: { .pixelFormat: QOpenGLTexture::NoSourceFormat, .textureFormat: QOpenGLTexture::SRGB_BP_UNorm, .pixelType: QOpenGLTexture::NoPixelType, .components: 16, .compressed: true } },
438};
439
440struct PkmHeader
441{
442 char magic[4];
443 char version[2];
444 quint16 textureType;
445 quint16 paddedWidth;
446 quint16 paddedHeight;
447 quint16 width;
448 quint16 height;
449};
450
451enum ImageFormat {
452 GenericImageFormat = 0,
453 DDS,
454 PKM,
455 HDR,
456 KTX
457};
458
459ImageFormat imageFormatFromSuffix(const QString &suffix)
460{
461 if (suffix == QStringLiteral("pkm"))
462 return PKM;
463 if (suffix == QStringLiteral("dds"))
464 return DDS;
465 if (suffix == QStringLiteral("hdr"))
466 return HDR;
467 if (suffix == QStringLiteral("ktx"))
468 return KTX;
469 return GenericImageFormat;
470}
471
472// NOTE: the ktx loading code is a near-duplication of the code in qt3d-runtime, and changes
473// should be kept up to date in both locations.
474quint32 blockSizeForTextureFormat(QOpenGLTexture::TextureFormat format)
475{
476 switch (format) {
477 case QOpenGLTexture::RGB8_ETC1:
478 case QOpenGLTexture::RGB8_ETC2:
479 case QOpenGLTexture::SRGB8_ETC2:
480 case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2:
481 case QOpenGLTexture::SRGB8_PunchThrough_Alpha1_ETC2:
482 case QOpenGLTexture::R11_EAC_UNorm:
483 case QOpenGLTexture::R11_EAC_SNorm:
484 case QOpenGLTexture::RGB_DXT1:
485 return 8;
486
487 default:
488 return 16;
489 }
490}
491
492QTextureImageDataPtr setKtxFile(QIODevice *source)
493{
494 static const int KTX_IDENTIFIER_LENGTH = 12;
495 static const char ktxIdentifier[KTX_IDENTIFIER_LENGTH] = { '\xAB', 'K', 'T', 'X', ' ', '1', '1', '\xBB', '\r', '\n', '\x1A', '\n' };
496 static const quint32 platformEndianIdentifier = 0x04030201;
497 static const quint32 inversePlatformEndianIdentifier = 0x01020304;
498
499 struct KTXHeader {
500 quint8 identifier[KTX_IDENTIFIER_LENGTH];
501 quint32 endianness;
502 quint32 glType;
503 quint32 glTypeSize;
504 quint32 glFormat;
505 quint32 glInternalFormat;
506 quint32 glBaseInternalFormat;
507 quint32 pixelWidth;
508 quint32 pixelHeight;
509 quint32 pixelDepth;
510 quint32 numberOfArrayElements;
511 quint32 numberOfFaces;
512 quint32 numberOfMipmapLevels;
513 quint32 bytesOfKeyValueData;
514 };
515
516 KTXHeader header;
517 QTextureImageDataPtr imageData;
518 if (source->read(data: reinterpret_cast<char *>(&header), maxlen: sizeof(header)) != sizeof(header)
519 || qstrncmp(str1: reinterpret_cast<char *>(header.identifier), str2: ktxIdentifier, len: KTX_IDENTIFIER_LENGTH) != 0
520 || (header.endianness != platformEndianIdentifier && header.endianness != inversePlatformEndianIdentifier))
521 {
522 return imageData;
523 }
524
525 const bool isInverseEndian = (header.endianness == inversePlatformEndianIdentifier);
526 auto decode = [isInverseEndian](quint32 val) {
527 return isInverseEndian ? qbswap<quint32>(source: val) : val;
528 };
529
530 const bool isCompressed = decode(header.glType) == 0 && decode(header.glFormat) == 0 && decode(header.glTypeSize) == 1;
531 if (!isCompressed) {
532 qWarning(msg: "Uncompressed ktx texture data is not supported");
533 return imageData;
534 }
535
536 if (decode(header.numberOfArrayElements) != 0) {
537 qWarning(msg: "Array ktx textures not supported");
538 return imageData;
539 }
540
541 if (decode(header.pixelDepth) != 0) {
542 qWarning(msg: "Only 2D and cube ktx textures are supported");
543 return imageData;
544 }
545
546 const int bytesToSkip = decode(header.bytesOfKeyValueData);
547 if (source->read(maxlen: bytesToSkip).count() != bytesToSkip) {
548 qWarning(msg: "Unexpected end of ktx data");
549 return imageData;
550 }
551
552 const int level0Width = decode(header.pixelWidth);
553 const int level0Height = decode(header.pixelHeight);
554 const int faceCount = decode(header.numberOfFaces);
555 const int mipMapLevels = decode(header.numberOfMipmapLevels);
556 const QOpenGLTexture::TextureFormat format = QOpenGLTexture::TextureFormat(decode(header.glInternalFormat));
557 const int blockSize = blockSizeForTextureFormat(format);
558
559 // now for each mipmap level we have (arrays and 3d textures not supported here)
560 // uint32 imageSize
561 // for each array element
562 // for each face
563 // for each z slice
564 // compressed data
565 // padding so that each face data starts at an offset that is a multiple of 4
566 // padding so that each imageSize starts at an offset that is a multiple of 4
567
568 // assumes no depth or uncompressed textures (per above)
569 auto computeMipMapLevelSize = [&] (int level) {
570 const int w = qMax(a: level0Width >> level, b: 1);
571 const int h = qMax(a: level0Height >> level, b: 1);
572 return ((w + 3) / 4) * ((h + 3) / 4) * blockSize;
573 };
574
575 int dataSize = 0;
576 for (auto i = 0; i < mipMapLevels; ++i)
577 dataSize += computeMipMapLevelSize(i) * faceCount + 4; // assumes a single layer (per above)
578
579 const QByteArray rawData = source->read(maxlen: dataSize);
580 if (rawData.size() < dataSize) {
581 qWarning() << "Unexpected end of data in" << source;
582 return imageData;
583 }
584
585 if (!source->atEnd())
586 qWarning() << "Unrecognized data in" << source;
587
588 imageData = QTextureImageDataPtr::create();
589 imageData->setTarget(faceCount == 6 ? QOpenGLTexture::TargetCubeMap : QOpenGLTexture::Target2D);
590 imageData->setFormat(format);
591 imageData->setWidth(level0Width);
592 imageData->setHeight(level0Height);
593 imageData->setLayers(1);
594 imageData->setDepth(1);
595 imageData->setFaces(faceCount);
596 imageData->setMipLevels(mipMapLevels);
597 imageData->setPixelFormat(QOpenGLTexture::NoSourceFormat);
598 imageData->setPixelType(QOpenGLTexture::NoPixelType);
599 imageData->setData(data: rawData, blockSize, isCompressed: true);
600 QTextureImageDataPrivate::get(imageData: imageData.data())->m_isKtx = true; // see note in QTextureImageDataPrivate
601
602 return imageData;
603}
604
605QTextureImageDataPtr setPkmFile(QIODevice *source)
606{
607 QTextureImageDataPtr imageData;
608
609 PkmHeader header;
610 if ((source->read(data: reinterpret_cast<char *>(&header), maxlen: sizeof header) != sizeof header)
611 || (qstrncmp(str1: header.magic, str2: "PKM ", len: 4) != 0))
612 return imageData;
613
614 QOpenGLTexture::TextureFormat format = QOpenGLTexture::NoFormat;
615 int blockSize = 0;
616
617 if (header.version[0] == '2' && header.version[1] == '0') {
618 switch (qFromBigEndian(source: header.textureType)) {
619 case 0:
620 format = QOpenGLTexture::RGB8_ETC1;
621 blockSize = 8;
622 break;
623
624 case 1:
625 format = QOpenGLTexture::RGB8_ETC2;
626 blockSize = 8;
627 break;
628
629 case 3:
630 format = QOpenGLTexture::RGBA8_ETC2_EAC;
631 blockSize = 16;
632 break;
633
634 case 4:
635 format = QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2;
636 blockSize = 8;
637 break;
638
639 case 5:
640 format = QOpenGLTexture::R11_EAC_UNorm;
641 blockSize = 8;
642 break;
643
644 case 6:
645 format = QOpenGLTexture::RG11_EAC_UNorm;
646 blockSize = 16;
647 break;
648
649 case 7:
650 format = QOpenGLTexture::R11_EAC_SNorm;
651 blockSize = 8;
652 break;
653
654 case 8:
655 format = QOpenGLTexture::RG11_EAC_SNorm;
656 blockSize = 16;
657 break;
658 }
659 } else {
660 format = QOpenGLTexture::RGB8_ETC1;
661 blockSize = 8;
662 }
663
664 if (format == QOpenGLTexture::NoFormat) {
665 qWarning() << "Unrecognized compression format in" << source;
666 return imageData;
667 }
668
669 // get the extended (multiple of 4) width and height
670 const int width = qFromBigEndian(source: header.paddedWidth);
671 const int height = qFromBigEndian(source: header.paddedHeight);
672
673 const QByteArray data = source->readAll();
674 if (data.size() != (width / 4) * (height / 4) * blockSize) {
675 qWarning() << "Unexpected data size in" << source;
676 return imageData;
677 }
678
679 imageData = QTextureImageDataPtr::create();
680 imageData->setTarget(QOpenGLTexture::Target2D);
681 imageData->setFormat(format);
682 imageData->setWidth(width);
683 imageData->setHeight(height);
684 imageData->setLayers(1);
685 imageData->setDepth(1);
686 imageData->setFaces(1);
687 imageData->setMipLevels(1);
688 imageData->setPixelFormat(QOpenGLTexture::NoSourceFormat);
689 imageData->setPixelType(QOpenGLTexture::NoPixelType);
690 imageData->setData(data, blockSize, isCompressed: true);
691
692 return imageData;
693}
694
695QTextureImageDataPtr setDdsFile(QIODevice *source)
696{
697 QTextureImageDataPtr imageData;
698
699 DdsHeader header;
700 if ((source->read(data: reinterpret_cast<char *>(&header), maxlen: sizeof header) != sizeof header)
701 || (qstrncmp(str1: header.magic, str2: "DDS ", len: 4) != 0))
702 return imageData;
703
704 int layers = 1;
705 const quint32 pixelFlags = qFromLittleEndian(source: header.pixelFormat.flags);
706 const quint32 fourCC = qFromLittleEndian(source: header.pixelFormat.fourCC);
707 const FormatInfo *formatInfo = nullptr;
708
709 if ((pixelFlags & FourCCFlag) == FourCCFlag) {
710 if (fourCC == DdsFourCC<'D', 'X', '1', '0'>::value) {
711 // DX10 texture
712 DdsDX10Header dx10Header;
713 if (source->read(data: reinterpret_cast<char *>(&dx10Header), maxlen: sizeof dx10Header) != sizeof dx10Header)
714 return imageData;
715
716 layers = qFromLittleEndian(source: dx10Header.arraySize);
717 DXGIFormat format = static_cast<DXGIFormat>(qFromLittleEndian(source: dx10Header.format));
718
719 for (const auto &info : dx10Formats) {
720 if (info.dxgiFormat == format) {
721 formatInfo = &info.formatInfo;
722 break;
723 }
724 }
725 } else {
726 // compressed, FourCC texture
727 for (const auto &info : fourCCFormats) {
728 if (info.fourCC == fourCC) {
729 formatInfo = &info.formatInfo;
730 break;
731 }
732 }
733 }
734 } else {
735 // uncompressed texture
736 const quint32 rgbBitCount = qFromLittleEndian(source: header.pixelFormat.rgbBitCount);
737 const quint32 redMask = qFromLittleEndian(source: header.pixelFormat.redMask);
738 const quint32 greenMask = qFromLittleEndian(source: header.pixelFormat.greenMask);
739 const quint32 blueMask = qFromLittleEndian(source: header.pixelFormat.blueMask);
740 const quint32 alphaMask = qFromLittleEndian(source: header.pixelFormat.alphaMask);
741
742 for (const auto &info : rgbaFormats) {
743 if (info.formatInfo.components * 8u == rgbBitCount &&
744 info.redMask == redMask && info.greenMask == greenMask &&
745 info.blueMask == blueMask && info.alphaMask == alphaMask) {
746 formatInfo = &info.formatInfo;
747 break;
748 }
749 }
750 }
751
752 if (formatInfo == nullptr) {
753 qWarning() << "Unrecognized pixel format in" << source;
754 return imageData;
755 }
756
757 // target
758 // XXX should worry about Target1D?
759 QOpenGLTexture::Target target;
760 const int width = qFromLittleEndian(source: header.width);
761 const int height = qFromLittleEndian(source: header.height);
762 const quint32 caps2Flags = qFromLittleEndian(source: header.caps2);
763 const int blockSize = formatInfo->components;
764 const bool isCompressed = formatInfo->compressed;
765 const int mipLevelCount = ((qFromLittleEndian(source: header.flags) & MipmapCountFlag) == MipmapCountFlag) ? qFromLittleEndian(source: header.mipmapCount) : 1;
766 int depth;
767 int faces;
768
769 if ((caps2Flags & VolumeFlag) == VolumeFlag) {
770 target = QOpenGLTexture::Target3D;
771 depth = qFromLittleEndian(source: header.depth);
772 faces = 1;
773 } else if ((caps2Flags & CubemapFlag) == CubemapFlag) {
774 target = layers > 1 ? QOpenGLTexture::TargetCubeMapArray : QOpenGLTexture::TargetCubeMap;
775 depth = 1;
776 faces = qPopulationCount(v: caps2Flags & AllCubemapFaceFlags);
777 } else {
778 target = layers > 1 ? QOpenGLTexture::Target2DArray : QOpenGLTexture::Target2D;
779 depth = 1;
780 faces = 1;
781 }
782
783 int layerSize = 0;
784 int tmpSize = 0;
785
786 auto computeMipMapLevelSize = [&] (int level) {
787 const int w = qMax(a: width >> level, b: 1);
788 const int h = qMax(a: height >> level, b: 1);
789 const int d = qMax(a: depth >> level, b: 1);
790
791 if (isCompressed)
792 return ((w + 3) / 4) * ((h + 3) / 4) * blockSize * d;
793 else
794 return w * h * blockSize * d;
795 };
796
797 for (auto i = 0; i < mipLevelCount; ++i)
798 tmpSize += computeMipMapLevelSize(i);
799
800 layerSize = faces * tmpSize;
801
802 // data
803 const int dataSize = layers * layerSize;
804
805 const QByteArray data = source->read(maxlen: dataSize);
806 if (data.size() < dataSize) {
807 qWarning() << "Unexpected end of data in" << source;
808 return imageData;
809 }
810
811 if (!source->atEnd())
812 qWarning() << "Unrecognized data in" << source;
813
814 imageData = QTextureImageDataPtr::create();
815 imageData->setData(data,blockSize, isCompressed);
816
817 // target
818 imageData->setTarget(target);
819
820 // mip levels
821 imageData->setMipLevels(mipLevelCount);
822
823 // texture format
824 imageData->setFormat(formatInfo->textureFormat);
825 imageData->setPixelType(formatInfo->pixelType);
826 imageData->setPixelFormat(formatInfo->pixelFormat);
827
828 // dimensions
829 imageData->setLayers(layers);
830 imageData->setDepth(depth);
831 imageData->setWidth(width);
832 imageData->setHeight(height);
833 imageData->setFaces(faces);
834
835 return imageData;
836}
837
838// Loads Radiance RGBE images into RGBA32F image data. RGBA is chosen over RGB
839// because this allows passing such images to compute shaders (image2D).
840QTextureImageDataPtr setHdrFile(QIODevice *source)
841{
842 QTextureImageDataPtr imageData;
843 char sig[256];
844 source->read(data: sig, maxlen: 11);
845 if (strncmp(s1: sig, s2: "#?RADIANCE\n", n: 11))
846 return imageData;
847
848 QByteArray buf = source->readAll();
849 const char *p = buf.constData();
850 const char *pEnd = p + buf.size();
851
852 // Process lines until the empty one.
853 QByteArray line;
854 while (p < pEnd) {
855 char c = *p++;
856 if (c == '\n') {
857 if (line.isEmpty())
858 break;
859 if (line.startsWith(QByteArrayLiteral("FORMAT="))) {
860 const QByteArray format = line.mid(index: 7).trimmed();
861 if (format != QByteArrayLiteral("32-bit_rle_rgbe")) {
862 qWarning(msg: "HDR format '%s' is not supported", format.constData());
863 return imageData;
864 }
865 }
866 line.clear();
867 } else {
868 line.append(c);
869 }
870 }
871 if (p == pEnd) {
872 qWarning(msg: "Malformed HDR image data at property strings");
873 return imageData;
874 }
875
876 // Get the resolution string.
877 while (p < pEnd) {
878 char c = *p++;
879 if (c == '\n')
880 break;
881 line.append(c);
882 }
883 if (p == pEnd) {
884 qWarning(msg: "Malformed HDR image data at resolution string");
885 return imageData;
886 }
887
888 int w = 0, h = 0;
889 // We only care about the standard orientation.
890 if (!sscanf(s: line.constData(), format: "-Y %d +X %d", &h, &w)) {
891 qWarning(msg: "Unsupported HDR resolution string '%s'", line.constData());
892 return imageData;
893 }
894 if (w <= 0 || h <= 0) {
895 qWarning(msg: "Invalid HDR resolution");
896 return imageData;
897 }
898
899 const QOpenGLTexture::TextureFormat textureFormat = QOpenGLTexture::RGBA32F;
900 const QOpenGLTexture::PixelFormat pixelFormat = QOpenGLTexture::RGBA;
901 const QOpenGLTexture::PixelType pixelType = QOpenGLTexture::Float32;
902 const int blockSize = 4 * sizeof(float);
903 QByteArray data;
904 data.resize(size: w * h * blockSize);
905
906 typedef unsigned char RGBE[4];
907 RGBE *scanline = new RGBE[w];
908
909 for (int y = 0; y < h; ++y) {
910 if (pEnd - p < 4) {
911 qWarning(msg: "Unexpected end of HDR data");
912 delete[] scanline;
913 return imageData;
914 }
915
916 scanline[0][0] = *p++;
917 scanline[0][1] = *p++;
918 scanline[0][2] = *p++;
919 scanline[0][3] = *p++;
920
921 if (scanline[0][0] == 2 && scanline[0][1] == 2 && scanline[0][2] < 128) {
922 // new rle, the first pixel was a dummy
923 for (int channel = 0; channel < 4; ++channel) {
924 for (int x = 0; x < w && p < pEnd; ) {
925 unsigned char c = *p++;
926 if (c > 128) { // run
927 if (p < pEnd) {
928 int repCount = c & 127;
929 c = *p++;
930 while (repCount--)
931 scanline[x++][channel] = c;
932 }
933 } else { // not a run
934 while (c-- && p < pEnd)
935 scanline[x++][channel] = *p++;
936 }
937 }
938 }
939 } else {
940 // old rle
941 scanline[0][0] = 2;
942 int bitshift = 0;
943 int x = 1;
944 while (x < w && pEnd - p >= 4) {
945 scanline[x][0] = *p++;
946 scanline[x][1] = *p++;
947 scanline[x][2] = *p++;
948 scanline[x][3] = *p++;
949
950 if (scanline[x][0] == 1 && scanline[x][1] == 1 && scanline[x][2] == 1) { // run
951 int repCount = scanline[x][3] << bitshift;
952 while (repCount--) {
953 memcpy(dest: scanline[x], src: scanline[x - 1], n: 4);
954 ++x;
955 }
956 bitshift += 8;
957 } else { // not a run
958 ++x;
959 bitshift = 0;
960 }
961 }
962 }
963
964 // adjust for -Y orientation
965 float *fp = reinterpret_cast<float *>(data.data() + (h - 1 - y) * blockSize * w);
966 for (int x = 0; x < w; ++x) {
967 float d = qPow(x: 2.0f, y: float(scanline[x][3]) - 128.0f);
968 // r, g, b, a
969 *fp++ = scanline[x][0] / 256.0f * d;
970 *fp++ = scanline[x][1] / 256.0f * d;
971 *fp++ = scanline[x][2] / 256.0f * d;
972 *fp++ = 1.0f;
973 }
974 }
975
976 delete[] scanline;
977
978 imageData = QTextureImageDataPtr::create();
979 imageData->setTarget(QOpenGLTexture::Target2D);
980 imageData->setFormat(textureFormat);
981 imageData->setWidth(w);
982 imageData->setHeight(h);
983 imageData->setLayers(1);
984 imageData->setDepth(1);
985 imageData->setFaces(1);
986 imageData->setMipLevels(1);
987 imageData->setPixelFormat(pixelFormat);
988 imageData->setPixelType(pixelType);
989 imageData->setData(data, blockSize, isCompressed: false);
990
991 return imageData;
992}
993
994} // anonynous
995
996QTextureImageDataPtr TextureLoadingHelper::loadTextureData(const QUrl &url, bool allow3D, bool mirrored)
997{
998 QTextureImageDataPtr textureData;
999 if (url.isLocalFile() || url.scheme() == QLatin1String("qrc")
1000#ifdef Q_OS_ANDROID
1001 || url.scheme() == QLatin1String("assets")
1002#endif
1003 ) {
1004 const QString source = Qt3DRender::QUrlHelper::urlToLocalFileOrQrc(url);
1005 QFile f(source);
1006 if (!f.open(flags: QIODevice::ReadOnly))
1007 qWarning() << "Failed to open" << source;
1008 else
1009 textureData = loadTextureData(data: &f, suffix: QFileInfo(source).suffix().toLower(), allow3D, mirrored);
1010 }
1011 return textureData;
1012}
1013
1014QTextureImageDataPtr TextureLoadingHelper::loadTextureData(QIODevice *data, const QString& suffix,
1015 bool allow3D, bool mirrored)
1016{
1017 QTextureImageDataPtr textureData;
1018 ImageFormat fmt = imageFormatFromSuffix(suffix);
1019 switch (fmt) {
1020 case DDS:
1021 textureData = setDdsFile(data);
1022 break;
1023 case PKM:
1024 textureData = setPkmFile(data);
1025 break;
1026 case HDR:
1027 textureData = setHdrFile(data);
1028 break;
1029 case KTX: {
1030 textureData = setKtxFile(data);
1031 break;
1032 }
1033 default: {
1034 QImage img;
1035 if (img.load(device: data, format: suffix.toLatin1())) {
1036 textureData = QTextureImageDataPtr::create();
1037 textureData->setImage(mirrored ? img.mirrored() : img);
1038 } else {
1039 qWarning() << "Failed to load textureImage data using QImage";
1040 }
1041 break;
1042 }
1043 }
1044
1045 if (!allow3D && textureData && (textureData->layers() > 1 || textureData->depth() > 1))
1046 qWarning() << "Texture data has a 3rd dimension which wasn't expected";
1047 return textureData;
1048}
1049
1050QTextureDataPtr QTextureFromSourceGenerator::operator ()()
1051{
1052 QTextureDataPtr generatedData = QTextureDataPtr::create();
1053 QTextureImageDataPtr textureData;
1054
1055 // Note: First and Second call can be seen as operator() being called twice
1056 // on the same object but actually call 2 will be made on a new generator
1057 // which is the copy of the generator used for call 1 but with m_sourceData
1058 // set.
1059 // This is required because updating the same functor wouldn't be picked up
1060 // by the backend texture sharing system.
1061 if (!Qt3DCore::QDownloadHelperService::isLocal(url: m_url)) {
1062 if (m_sourceData.isEmpty()) {
1063 // first time around, trigger a download
1064 if (m_texture) {
1065 auto downloadService = Qt3DCore::QDownloadHelperService::getService(engine: m_engine);
1066 Qt3DCore::QDownloadRequestPtr request(new TextureDownloadRequest(sharedFromThis(),
1067 m_url,
1068 m_engine,
1069 m_texture));
1070 downloadService->submitRequest(request);
1071 }
1072 return generatedData;
1073 }
1074
1075 // second time around, we have the data
1076 QT_PREPEND_NAMESPACE(QBuffer) buffer(&m_sourceData);
1077 if (buffer.open(openMode: QIODevice::ReadOnly)) {
1078 QString suffix = m_url.toString();
1079 suffix = suffix.right(n: suffix.length() - suffix.lastIndexOf(c: '.'));
1080
1081 QStringList ext(suffix);
1082
1083 QMimeDatabase db;
1084 QMimeType mtype = db.mimeTypeForData(data: m_sourceData);
1085 if (mtype.isValid()) {
1086 ext << mtype.suffixes();
1087 }
1088
1089 for (const QString &s: qAsConst(t&: ext)) {
1090 textureData = TextureLoadingHelper::loadTextureData(data: &buffer, suffix: s, allow3D: true, mirrored: m_mirrored);
1091 if (textureData && textureData->data().length() > 0)
1092 break;
1093 }
1094 }
1095 } else {
1096 textureData = TextureLoadingHelper::loadTextureData(url: m_url, allow3D: true, mirrored: m_mirrored);
1097 }
1098
1099 // Update any properties explicitly set by the user
1100 if (textureData && m_format != QAbstractTexture::NoFormat && m_format != QAbstractTexture::Automatic)
1101 textureData->setFormat(static_cast<QOpenGLTexture::TextureFormat>(m_format));
1102
1103 if (textureData && textureData->data().length() > 0) {
1104 generatedData->setTarget(static_cast<QAbstractTexture::Target>(textureData->target()));
1105 generatedData->setFormat(static_cast<QAbstractTexture::TextureFormat>(textureData->format()));
1106 generatedData->setWidth(textureData->width());
1107 generatedData->setHeight(textureData->height());
1108 generatedData->setDepth(textureData->depth());
1109 generatedData->setLayers(textureData->layers());
1110 generatedData->addImageData(imageData: textureData);
1111 }
1112
1113 return generatedData;
1114}
1115
1116TextureDownloadRequest::TextureDownloadRequest(const QTextureFromSourceGeneratorPtr &functor,
1117 const QUrl &source,
1118 Qt3DCore::QAspectEngine *engine,
1119 Qt3DCore::QNodeId texNodeId)
1120 : Qt3DCore::QDownloadRequest(source)
1121 , m_functor(functor)
1122 , m_engine(engine)
1123 , m_texNodeId(texNodeId)
1124{
1125
1126}
1127
1128// Executed in main thread
1129void TextureDownloadRequest::onCompleted()
1130{
1131 if (cancelled() || !succeeded())
1132 return;
1133
1134 QRenderAspectPrivate* d_aspect = QRenderAspectPrivate::findPrivate(engine: m_engine);
1135 if (!d_aspect)
1136 return;
1137
1138 Render::TextureManager *textureManager = d_aspect->m_nodeManagers->textureManager();
1139 Render::Texture *texture = textureManager->lookupResource(id: m_texNodeId);
1140 if (texture == nullptr)
1141 return;
1142
1143 QTextureFromSourceGeneratorPtr oldGenerator = qSharedPointerCast<QTextureFromSourceGenerator>(src: texture->dataGenerator());
1144
1145 // Set raw data on functor so that it can really load something
1146 oldGenerator->m_sourceData = m_data;
1147
1148 // Mark the texture as dirty so that the functor runs again with the downloaded data
1149 texture->addDirtyFlag(flags: Render::Texture::DirtyDataGenerator);
1150}
1151
1152/*!
1153 \class Qt3DRender::QTexture1D
1154 \inheaderfile Qt3DRender/QTexture
1155 \inmodule Qt3DRender
1156 \since 5.5
1157 \brief A QAbstractTexture with a Target1D target format.
1158 */
1159/*!
1160 \qmltype Texture1D
1161 \instantiates Qt3DRender::QTexture1D
1162 \inqmlmodule Qt3D.Render
1163 \since 5.5
1164 \brief An AbstractTexture with a Target1D target format.
1165 */
1166
1167/*!
1168 Constructs a new Qt3DRender::QTexture1D instance with \a parent as parent.
1169 */
1170QTexture1D::QTexture1D(QNode *parent)
1171 : QAbstractTexture(Target1D, parent)
1172{
1173}
1174
1175/*! \internal */
1176QTexture1D::~QTexture1D()
1177{
1178}
1179
1180/*!
1181 \class Qt3DRender::QTexture1DArray
1182 \inheaderfile Qt3DRender/QTexture
1183 \inmodule Qt3DRender
1184 \since 5.5
1185 \brief A QAbstractTexture with a Target1DArray target format.
1186 */
1187/*!
1188 \qmltype Texture1DArray
1189 \instantiates Qt3DRender::QTexture1DArray
1190 \inqmlmodule Qt3D.Render
1191 \since 5.5
1192 \brief An AbstractTexture with a Target1DArray target format.
1193 */
1194
1195/*!
1196 Constructs a new Qt3DRender::QTexture1DArray instance with \a parent as parent.
1197 */
1198QTexture1DArray::QTexture1DArray(QNode *parent)
1199 : QAbstractTexture(Target1DArray, parent)
1200{
1201}
1202
1203/*! \internal */
1204QTexture1DArray::~QTexture1DArray()
1205{
1206}
1207
1208/*!
1209 \class Qt3DRender::QTexture2D
1210 \inheaderfile Qt3DRender/QTexture
1211 \inmodule Qt3DRender
1212 \since 5.5
1213 \brief A QAbstractTexture with a Target2D target format.
1214 */
1215/*!
1216 \qmltype Texture2D
1217 \instantiates Qt3DRender::QTexture2D
1218 \inqmlmodule Qt3D.Render
1219 \since 5.5
1220 \brief An AbstractTexture with a Target2D target format.
1221 */
1222
1223/*!
1224 Constructs a new Qt3DRender::QTexture2D instance with \a parent as parent.
1225 */
1226QTexture2D::QTexture2D(QNode *parent)
1227 : QAbstractTexture(Target2D, parent)
1228{
1229}
1230
1231/*! \internal */
1232QTexture2D::~QTexture2D()
1233{
1234}
1235
1236/*!
1237 \class Qt3DRender::QTexture2DArray
1238 \inheaderfile Qt3DRender/QTexture
1239 \inmodule Qt3DRender
1240 \since 5.5
1241 \brief A QAbstractTexture with a Target2DArray target format.
1242 */
1243/*!
1244 \qmltype Texture2DArray
1245 \instantiates Qt3DRender::QTexture2DArray
1246 \inqmlmodule Qt3D.Render
1247 \since 5.5
1248 \brief An AbstractTexture with a Target2DArray target format.
1249 */
1250
1251/*!
1252 Constructs a new Qt3DRender::QTexture2DArray instance with \a parent as parent.
1253 */
1254QTexture2DArray::QTexture2DArray(QNode *parent)
1255 : QAbstractTexture(Target2DArray, parent)
1256{
1257}
1258
1259/*! \internal */
1260QTexture2DArray::~QTexture2DArray()
1261{
1262}
1263
1264/*!
1265 \class Qt3DRender::QTexture3D
1266 \inheaderfile Qt3DRender/QTexture
1267 \inmodule Qt3DRender
1268 \since 5.5
1269 \brief A QAbstractTexture with a Target3D target format.
1270 */
1271/*!
1272 \qmltype Texture3D
1273 \instantiates Qt3DRender::QTexture3D
1274 \inqmlmodule Qt3D.Render
1275 \since 5.5
1276 \brief An AbstractTexture with a Target3D target format.
1277 */
1278
1279/*!
1280 Constructs a new Qt3DRender::QTexture3D instance with \a parent as parent.
1281 */
1282QTexture3D::QTexture3D(QNode *parent)
1283 : QAbstractTexture(Target3D, parent)
1284{
1285}
1286
1287/*! \internal */
1288QTexture3D::~QTexture3D()
1289{
1290}
1291
1292/*!
1293 \class Qt3DRender::QTextureCubeMap
1294 \inheaderfile Qt3DRender/QTexture
1295 \inmodule Qt3DRender
1296 \since 5.5
1297 \brief A QAbstractTexture with a TargetCubeMap target format.
1298 */
1299/*!
1300 \qmltype TextureCubeMap
1301 \instantiates Qt3DRender::QTextureCubeMap
1302 \inqmlmodule Qt3D.Render
1303 \since 5.5
1304 \brief An AbstractTexture with a TargetCubeMap target format.
1305 */
1306
1307/*!
1308 Constructs a new Qt3DRender::QTextureCubeMap instance with \a parent as parent.
1309 */
1310QTextureCubeMap::QTextureCubeMap(QNode *parent)
1311 : QAbstractTexture(TargetCubeMap, parent)
1312{
1313}
1314
1315/*! \internal */
1316QTextureCubeMap::~QTextureCubeMap()
1317{
1318}
1319
1320/*!
1321 \class Qt3DRender::QTextureCubeMapArray
1322 \inheaderfile Qt3DRender/QTexture
1323 \inmodule Qt3DRender
1324 \since 5.5
1325 \brief A QAbstractTexture with a TargetCubeMapArray target format.
1326 */
1327/*!
1328 \qmltype TextureCubeMapArray
1329 \instantiates Qt3DRender::QTextureCubeMapArray
1330 \inqmlmodule Qt3D.Render
1331 \since 5.5
1332 \brief An AbstractTexture with a TargetCubeMapArray target format.
1333 */
1334
1335/*!
1336 Constructs a new Qt3DRender::QTextureCubeMapArray instance with \a parent as parent.
1337 */
1338QTextureCubeMapArray::QTextureCubeMapArray(QNode *parent)
1339 : QAbstractTexture(TargetCubeMapArray, parent)
1340{
1341}
1342
1343/*! \internal */
1344QTextureCubeMapArray::~QTextureCubeMapArray()
1345{
1346}
1347
1348/*!
1349 \class Qt3DRender::QTexture2DMultisample
1350 \inheaderfile Qt3DRender/QTexture
1351 \inmodule Qt3DRender
1352 \since 5.5
1353 \brief A QAbstractTexture with a Target2DMultisample target format.
1354 */
1355/*!
1356 \qmltype Texture2DMultisample
1357 \instantiates Qt3DRender::QTexture2DMultisample
1358 \inqmlmodule Qt3D.Render
1359 \since 5.5
1360 \brief An AbstractTexture with a Target2DMultisample target format.
1361 */
1362
1363/*!
1364 Constructs a new Qt3DRender::QTexture2DMultisample instance with \a parent as parent.
1365 */
1366QTexture2DMultisample::QTexture2DMultisample(QNode *parent)
1367 : QAbstractTexture(Target2DMultisample, parent)
1368{
1369}
1370
1371/*! \internal */
1372QTexture2DMultisample::~QTexture2DMultisample()
1373{
1374}
1375
1376/*!
1377 \class Qt3DRender::QTexture2DMultisampleArray
1378 \inheaderfile Qt3DRender/QTexture
1379 \inmodule Qt3DRender
1380 \since 5.5
1381 \brief A QAbstractTexture with a Target2DMultisampleArray target format.
1382 */
1383/*!
1384 \qmltype Texture2DMultisampleArray
1385 \instantiates Qt3DRender::QTexture2DMultisampleArray
1386 \inqmlmodule Qt3D.Render
1387 \since 5.5
1388 \brief An AbstractTexture with a Target2DMultisampleArray target format.
1389 */
1390
1391/*!
1392 Constructs a new Qt3DRender::QTexture2DMultisampleArray instance with \a parent as parent.
1393 */
1394QTexture2DMultisampleArray::QTexture2DMultisampleArray(QNode *parent)
1395 : QAbstractTexture(Target2DMultisampleArray, parent)
1396{
1397}
1398
1399/*! \internal */
1400QTexture2DMultisampleArray::~QTexture2DMultisampleArray()
1401{
1402}
1403
1404/*!
1405 \class Qt3DRender::QTextureRectangle
1406 \inheaderfile Qt3DRender/QTexture
1407 \inmodule Qt3DRender
1408 \since 5.5
1409 \brief A QAbstractTexture with a TargetRectangle target format.
1410 */
1411/*!
1412 \qmltype TextureRectangle
1413 \instantiates Qt3DRender::QTextureRectangle
1414 \inqmlmodule Qt3D.Render
1415 \since 5.5
1416 \brief An AbstractTexture with a TargetRectangle target format.
1417 */
1418
1419/*!
1420 Constructs a new Qt3DRender::QTextureRectangle instance with \a parent as parent.
1421 */
1422QTextureRectangle::QTextureRectangle(QNode *parent)
1423 : QAbstractTexture(TargetRectangle, parent)
1424{
1425}
1426
1427/*! \internal */
1428QTextureRectangle::~QTextureRectangle()
1429{
1430}
1431
1432/*!
1433 \class Qt3DRender::QTextureBuffer
1434 \inheaderfile Qt3DRender/QTexture
1435 \inmodule Qt3DRender
1436 \since 5.5
1437 \brief A QAbstractTexture with a TargetBuffer target format.
1438 */
1439/*!
1440 \qmltype TextureBuffer
1441 \instantiates Qt3DRender::QTextureBuffer
1442 \inqmlmodule Qt3D.Render
1443 \since 5.5
1444 \brief An AbstractTexture with a TargetBuffer target format.
1445 */
1446
1447/*!
1448 Constructs a new Qt3DRender::QTextureBuffer instance with \a parent as parent.
1449 */
1450QTextureBuffer::QTextureBuffer(QNode *parent)
1451 : QAbstractTexture(TargetBuffer, parent)
1452{
1453}
1454
1455/*! \internal */
1456QTextureBuffer::~QTextureBuffer()
1457{
1458}
1459
1460QTextureLoaderPrivate::QTextureLoaderPrivate()
1461 : QAbstractTexturePrivate()
1462 , m_mirrored(true)
1463{
1464}
1465
1466void QTextureLoaderPrivate::setScene(Qt3DCore::QScene *scene)
1467{
1468 QAbstractTexturePrivate::setScene(scene);
1469 updateGenerator();
1470}
1471
1472void QTextureLoaderPrivate::updateGenerator()
1473{
1474 Q_Q(QTextureLoader);
1475 Qt3DCore::QAspectEngine *engine = m_scene ? m_scene->engine() : nullptr;
1476 setDataFunctor(QTextureFromSourceGeneratorPtr::create(arguments: q, arguments&: engine, arguments&: m_id));
1477}
1478
1479/*!
1480 \class Qt3DRender::QTextureLoader
1481 \inheaderfile Qt3DRender/QTexture
1482 \inmodule Qt3DRender
1483 \brief Handles the texture loading and setting the texture's properties.
1484*/
1485/*!
1486 \qmltype TextureLoader
1487 \instantiates Qt3DRender::QTextureLoader
1488 \inqmlmodule Qt3D.Render
1489
1490 \brief Handles the texture loading and setting the texture's properties.
1491*/
1492/*!
1493 * Constructs a new Qt3DRender::QTextureLoader instance with \a parent as parent.
1494 *
1495 * Note that by default, if not contradicted by the file metadata, the loaded texture
1496 * will have the following properties set:
1497 * - wrapMode set to Repeat
1498 * - minificationFilter set to LinearMipMapLinear
1499 * - magnificationFilter set to Linear
1500 * - generateMipMaps set to true
1501 * - maximumAnisotropy set to 16.0f
1502 * - target set to TargetAutomatic
1503 */
1504QTextureLoader::QTextureLoader(QNode *parent)
1505 : QAbstractTexture(*new QTextureLoaderPrivate, parent)
1506{
1507 d_func()->m_wrapMode.setX(QTextureWrapMode::Repeat);
1508 d_func()->m_wrapMode.setY(QTextureWrapMode::Repeat);
1509 d_func()->m_minFilter = LinearMipMapLinear;
1510 d_func()->m_magFilter = Linear;
1511 d_func()->m_autoMipMap = true;
1512 d_func()->m_maximumAnisotropy = 16.0f;
1513 d_func()->m_target = TargetAutomatic;
1514
1515 // Regenerate the texture functor when properties we support overriding
1516 // from QAbstractTexture get changed.
1517 Q_D(QTextureLoader);
1518 auto regenerate = [=] () {
1519 if (!notificationsBlocked()) // check the change doesn't come from the backend
1520 d->updateGenerator();
1521 };
1522 connect(sender: this, signal: &QAbstractTexture::formatChanged, slot: regenerate);
1523}
1524
1525/*! \internal */
1526QTextureLoader::~QTextureLoader()
1527{
1528}
1529
1530/*!
1531 \property QTextureLoader::source
1532
1533 \brief The current texture source.
1534*/
1535/*!
1536 \qmlproperty url Qt3D.Render::TextureLoader::source
1537
1538 This property holds the current texture source.
1539*/
1540QUrl QTextureLoader::source() const
1541{
1542 Q_D(const QTextureLoader);
1543 return d->m_source;
1544}
1545
1546bool QTextureLoader::isMirrored() const
1547{
1548 Q_D(const QTextureLoader);
1549 return d->m_mirrored;
1550}
1551
1552/*!
1553 * Sets the texture loader source to \a source.
1554 * \param source
1555 */
1556void QTextureLoader::setSource(const QUrl& source)
1557{
1558 Q_D(QTextureLoader);
1559 if (source != d->m_source) {
1560 d->m_source = source;
1561
1562 // Reset target and format
1563 d->m_target = TargetAutomatic;
1564 setFormat(NoFormat);
1565
1566 d->updateGenerator();
1567 const bool blocked = blockNotifications(block: true);
1568 emit sourceChanged(source);
1569 blockNotifications(block: blocked);
1570 }
1571}
1572
1573/*!
1574 \property Qt3DRender::QTextureLoader::mirrored
1575
1576 This property specifies whether the texture should be mirrored when loaded. This
1577 is a convenience to avoid having to manipulate images to match the origin of
1578 the texture coordinates used by the rendering API. By default this property
1579 is set to true. This has no effect when using GPU compressed texture formats.
1580
1581 \warning This property results in a performance price payed at runtime when
1582 loading uncompressed or CPU compressed image formats such as PNG. To avoid this
1583 performance price it is better to set this property to false and load texture
1584 assets that have been pre-mirrored.
1585
1586 \note OpenGL specifies the origin of texture coordinates from the lower left
1587 hand corner whereas DirectX uses the the upper left hand corner.
1588
1589 \note When using cube map texture you'll probably want mirroring disabled as
1590 the cube map sampler takes a direction rather than regular texture
1591 coordinates.
1592*/
1593
1594/*!
1595 \qmlproperty bool Qt3D.Render::TextureLoader::mirrored
1596
1597 This property specifies whether the texture should be mirrored when loaded. This
1598 is a convenience to avoid having to manipulate images to match the origin of
1599 the texture coordinates used by the rendering API. By default this property
1600 is set to true. This has no effect when using GPU compressed texture formats.
1601
1602 \warning This property results in a performance price payed at runtime when
1603 loading uncompressed or CPU compressed image formats such as PNG. To avoid this
1604 performance price it is better to set this property to false and load texture
1605 assets that have been pre-mirrored.
1606
1607 \note OpenGL specifies the origin of texture coordinates from the lower left
1608 hand corner whereas DirectX uses the the upper left hand corner.
1609
1610 \note When using cube map texture you'll probably want mirroring disabled as
1611 the cube map sampler takes a direction rather than regular texture
1612 coordinates.
1613*/
1614
1615/*!
1616 Sets mirroring to \a mirrored.
1617 \note This internally triggers a call to update the data generator.
1618 */
1619void QTextureLoader::setMirrored(bool mirrored)
1620{
1621 Q_D(QTextureLoader);
1622 if (mirrored != d->m_mirrored) {
1623 d->m_mirrored = mirrored;
1624 d->updateGenerator();
1625 const bool blocked = blockNotifications(block: true);
1626 emit mirroredChanged(mirrored);
1627 blockNotifications(block: blocked);
1628 }
1629}
1630
1631/*
1632 * Constructs a new QTextureFromSourceGenerator::QTextureFromSourceGenerator
1633 * instance with properties passed in via \a textureLoader
1634 * \param url
1635 */
1636QTextureFromSourceGenerator::QTextureFromSourceGenerator(QTextureLoader *textureLoader,
1637 Qt3DCore::QAspectEngine *engine,
1638 Qt3DCore::QNodeId textureId)
1639 : QTextureGenerator()
1640 , QEnableSharedFromThis<QTextureFromSourceGenerator>()
1641 , m_url()
1642 , m_status(QAbstractTexture::None)
1643 , m_mirrored()
1644 , m_texture(textureId)
1645 , m_engine(engine)
1646 , m_format(QAbstractTexture::NoFormat)
1647{
1648 Q_ASSERT(textureLoader);
1649
1650 // We always get QTextureLoader's "own" additional properties
1651 m_url = textureLoader->source();
1652 m_mirrored = textureLoader->isMirrored();
1653
1654 // For the properties on the base QAbstractTexture we only apply
1655 // those that have been explicitly set and which we support here.
1656 // For more control, the user can themselves use a QTexture2D and
1657 // create the texture images themselves, or even better, go create
1658 // proper texture files themselves (dds/ktx etc). This is purely a
1659 // convenience for some common use cases and will always be less
1660 // ideal than using compressed textures and generating mips offline.
1661 m_format = textureLoader->format();
1662}
1663
1664QTextureFromSourceGenerator::QTextureFromSourceGenerator(const QTextureFromSourceGenerator &other)
1665 : QTextureGenerator()
1666 , QEnableSharedFromThis<QTextureFromSourceGenerator>()
1667 , m_url(other.m_url)
1668 , m_status(other.m_status)
1669 , m_mirrored(other.m_mirrored)
1670 , m_sourceData(other.m_sourceData)
1671 , m_texture(other.m_texture)
1672 , m_engine(other.m_engine)
1673 , m_format(other.m_format)
1674{
1675}
1676
1677/*
1678 * Takes in a TextureGenerator via \a other and
1679 * \return whether generators have the same source.
1680 */
1681bool QTextureFromSourceGenerator::operator ==(const QTextureGenerator &other) const
1682{
1683 const QTextureFromSourceGenerator *otherFunctor = functor_cast<QTextureFromSourceGenerator>(other: &other);
1684 return (otherFunctor != nullptr &&
1685 otherFunctor->m_url == m_url &&
1686 otherFunctor->m_mirrored == m_mirrored &&
1687 otherFunctor->m_engine == m_engine &&
1688 otherFunctor->m_format == m_format &&
1689 otherFunctor->m_sourceData == m_sourceData);
1690}
1691
1692QUrl QTextureFromSourceGenerator::url() const
1693{
1694 return m_url;
1695}
1696
1697bool QTextureFromSourceGenerator::isMirrored() const
1698{
1699 return m_mirrored;
1700}
1701
1702/*!
1703 * \class Qt3DRender::QSharedGLTexture
1704 * \inmodule Qt3DRender
1705 * \inheaderfile Qt3DRender/QTexture
1706 * \brief Allows to use a textureId from a separate OpenGL context in a Qt 3D scene.
1707 *
1708 * Depending on the rendering mode used by Qt 3D, the shared context will either be:
1709 * \list
1710 * \li qt_gl_global_share_context when letting Qt 3D drive the rendering. When
1711 * setting the attribute Qt::AA_ShareOpenGLContexts on the QApplication class,
1712 * this will automatically make QOpenGLWidget instances have their context shared
1713 * with qt_gl_global_share_context.
1714 * \li the shared context from the QtQuick scene. You might have to subclass
1715 * QWindow or use QtQuickRenderControl to have control over what that shared
1716 * context is though as of 5.13 it is qt_gl_global_share_context.
1717 * \endlist
1718 *
1719 * \since 5.13
1720 *
1721 * Any 3rd party engine that shares its context with the Qt 3D renderer can now
1722 * provide texture ids that will be referenced by the Qt 3D texture.
1723 *
1724 * You can omit specifying the texture properties, Qt 3D will try at runtime to
1725 * determine what they are. If you know them, you can of course provide them,
1726 * avoid additional work for Qt 3D.
1727 *
1728 * Keep in mind that if you are using custom materials and shaders, you need to
1729 * specify the correct sampler type to be used.
1730 */
1731
1732/*!
1733 \qmltype SharedGLTexture
1734 \instantiates Qt3DRender::QSharedGLTexture
1735 \inqmlmodule Qt3D.Render
1736 \brief Allows to use a textureId from a separate OpenGL context in a Qt 3D scene.
1737 \since 5.13
1738*/
1739
1740QSharedGLTexture::QSharedGLTexture(Qt3DCore::QNode *parent)
1741 : QAbstractTexture(parent)
1742{
1743 QAbstractTexturePrivate *d = static_cast<QAbstractTexturePrivate *>(Qt3DCore::QNodePrivate::get(q: this));
1744 d->m_target = TargetAutomatic;
1745}
1746
1747QSharedGLTexture::~QSharedGLTexture()
1748{
1749}
1750
1751/*!
1752 * \qmlproperty int SharedGLTexture::textureId
1753 *
1754 * The OpenGL texture id value that you want Qt3D to gain access to.
1755 */
1756/*!
1757 *\property Qt3DRender::QSharedGLTexture::textureId
1758 *
1759 * The OpenGL texture id value that you want Qt3D to gain access to.
1760 */
1761int QSharedGLTexture::textureId() const
1762{
1763 return static_cast<QAbstractTexturePrivate *>(d_ptr.get())->m_sharedTextureId;
1764}
1765
1766void QSharedGLTexture::setTextureId(int id)
1767{
1768 QAbstractTexturePrivate *d = static_cast<QAbstractTexturePrivate *>(Qt3DCore::QNodePrivate::get(q: this));
1769 if (d->m_sharedTextureId != id) {
1770 d->m_sharedTextureId = id;
1771 emit textureIdChanged(textureId: id);
1772 }
1773}
1774
1775} // namespace Qt3DRender
1776
1777QT_END_NAMESPACE
1778

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