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

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