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

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