1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 2015 The Qt Company Ltd
4 SPDX-FileCopyrightText: 2013 Ivan Komissarov
5 SPDX-FileCopyrightText: 2024 Mirco Miranda
6
7 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only
8*/
9
10// Forked from Qt 5.6 branch
11
12#include "dds_p.h"
13#include "util_p.h"
14#include "scanlineconverter_p.h"
15
16#include <QColorSpace>
17#include <QDataStream>
18#include <QLoggingCategory>
19
20#include <cmath>
21
22#ifndef DDS_DISABLE_STRIDE_ALIGNMENT
23// Disable the stride alignment based on DDS pitch: it is known that some writers do not set it correctly
24// #define DDS_DISABLE_STRIDE_ALIGNMENT
25#endif
26
27/* *** DDS_MAX_IMAGE_WIDTH and DDS_MAX_IMAGE_HEIGHT ***
28 * The maximum size in pixel allowed by the plugin.
29 */
30#ifndef DDS_MAX_IMAGE_WIDTH
31#define DDS_MAX_IMAGE_WIDTH KIF_LARGE_IMAGE_PIXEL_LIMIT
32#endif
33#ifndef DDS_MAX_IMAGE_HEIGHT
34#define DDS_MAX_IMAGE_HEIGHT DDS_MAX_IMAGE_WIDTH
35#endif
36
37#ifdef QT_DEBUG
38Q_LOGGING_CATEGORY(LOG_DDSPLUGIN, "kf.imageformats.plugins.dds", QtDebugMsg)
39#else
40Q_LOGGING_CATEGORY(LOG_DDSPLUGIN, "kf.imageformats.plugins.dds", QtWarningMsg)
41#endif
42
43enum Format {
44 FormatUnknown = 0,
45
46 FormatR8G8B8 = 20,
47 FormatA8R8G8B8 = 21,
48 FormatX8R8G8B8 = 22,
49 FormatR5G6B5 = 23,
50 FormatX1R5G5B5 = 24,
51 FormatA1R5G5B5 = 25,
52 FormatA4R4G4B4 = 26,
53 FormatR3G3B2 = 27,
54 FormatA8 = 28,
55 FormatA8R3G3B2 = 29,
56 FormatX4R4G4B4 = 30,
57 FormatA2B10G10R10 = 31,
58 FormatA8B8G8R8 = 32,
59 FormatX8B8G8R8 = 33,
60 FormatG16R16 = 34,
61 FormatA2R10G10B10 = 35,
62 FormatA16B16G16R16 = 36,
63
64 FormatA8P8 = 40,
65 FormatP8 = 41,
66
67 FormatL8 = 50,
68 FormatA8L8 = 51,
69 FormatA4L4 = 52,
70
71 FormatV8U8 = 60,
72 FormatL6V5U5 = 61,
73 FormatX8L8V8U8 = 62,
74 FormatQ8W8V8U8 = 63,
75 FormatV16U16 = 64,
76 FormatA2W10V10U10 = 67,
77
78 FormatUYVY = 0x59565955, // "UYVY"
79 FormatR8G8B8G8 = 0x47424752, // "RGBG"
80 FormatYUY2 = 0x32595559, // "YUY2"
81 FormatG8R8G8B8 = 0x42475247, // "GRGB"
82 FormatDXT1 = 0x31545844, // "DXT1"
83 FormatDXT2 = 0x32545844, // "DXT2"
84 FormatDXT3 = 0x33545844, // "DXT3"
85 FormatDXT4 = 0x34545844, // "DXT4"
86 FormatDXT5 = 0x35545844, // "DXT5"
87 FormatRXGB = 0x42475852, // "RXGB"
88 FormatATI2 = 0x32495441, // "ATI2"
89
90 FormatD16Lockable = 70,
91 FormatD32 = 71,
92 FormatD15S1 = 73,
93 FormatD24S8 = 75,
94 FormatD24X8 = 77,
95 FormatD24X4S4 = 79,
96 FormatD16 = 80,
97
98 FormatD32FLockable = 82,
99 FormatD24FS8 = 83,
100
101 FormatD32Lockable = 84,
102 FormatS8Lockable = 85,
103
104 FormatL16 = 81,
105
106 FormatVertexData =100,
107 FormatIndex16 =101,
108 FormatIndex32 =102,
109
110 FormatQ16W16V16U16 = 110,
111
112 FormatMulti2ARGB8 = 0x3154454d, // "MET1"
113
114 FormatR16F = 111,
115 FormatG16R16F = 112,
116 FormatA16B16G16R16F = 113,
117
118 FormatR32F = 114,
119 FormatG32R32F = 115,
120 FormatA32B32G32R32F = 116,
121
122 FormatCxV8U8 = 117,
123
124 FormatA1 = 118,
125 FormatA2B10G10R10_XR_BIAS = 119,
126 FormatBinaryBuffer = 199,
127
128 FormatP4,
129 FormatA4P4,
130
131 FormatLast = 0x7fffffff
132};
133
134enum DXGIFormat {
135 DXGIFormatUNKNOWN = 0,
136 DXGIFormatR32G32B32A32_TYPELESS = 1,
137 DXGIFormatR32G32B32A32_FLOAT = 2,
138 DXGIFormatR32G32B32A32_UINT = 3,
139 DXGIFormatR32G32B32A32_SINT = 4,
140 DXGIFormatR32G32B32_TYPELESS = 5,
141 DXGIFormatR32G32B32_FLOAT = 6,
142 DXGIFormatR32G32B32_UINT = 7,
143 DXGIFormatR32G32B32_SINT = 8,
144 DXGIFormatR16G16B16A16_TYPELESS = 9,
145 DXGIFormatR16G16B16A16_FLOAT = 10,
146 DXGIFormatR16G16B16A16_UNORM = 11,
147 DXGIFormatR16G16B16A16_UINT = 12,
148 DXGIFormatR16G16B16A16_SNORM = 13,
149 DXGIFormatR16G16B16A16_SINT = 14,
150 DXGIFormatR32G32_TYPELESS = 15,
151 DXGIFormatR32G32_FLOAT = 16,
152 DXGIFormatR32G32_UINT = 17,
153 DXGIFormatR32G32_SINT = 18,
154 DXGIFormatR32G8X24_TYPELESS = 19,
155 DXGIFormatD32_FLOAT_S8X24_UINT = 20,
156 DXGIFormatR32_FLOAT_X8X24_TYPELESS = 21,
157 DXGIFormatX32_TYPELESS_G8X24_UINT = 22,
158 DXGIFormatR10G10B10A2_TYPELESS = 23,
159 DXGIFormatR10G10B10A2_UNORM = 24,
160 DXGIFormatR10G10B10A2_UINT = 25,
161 DXGIFormatR11G11B10_FLOAT = 26,
162 DXGIFormatR8G8B8A8_TYPELESS = 27,
163 DXGIFormatR8G8B8A8_UNORM = 28,
164 DXGIFormatR8G8B8A8_UNORM_SRGB = 29,
165 DXGIFormatR8G8B8A8_UINT = 30,
166 DXGIFormatR8G8B8A8_SNORM = 31,
167 DXGIFormatR8G8B8A8_SINT = 32,
168 DXGIFormatR16G16_TYPELESS = 33,
169 DXGIFormatR16G16_FLOAT = 34,
170 DXGIFormatR16G16_UNORM = 35,
171 DXGIFormatR16G16_UINT = 36,
172 DXGIFormatR16G16_SNORM = 37,
173 DXGIFormatR16G16_SINT = 38,
174 DXGIFormatR32_TYPELESS = 39,
175 DXGIFormatD32_FLOAT = 40,
176 DXGIFormatR32_FLOAT = 41,
177 DXGIFormatR32_UINT = 42,
178 DXGIFormatR32_SINT = 43,
179 DXGIFormatR24G8_TYPELESS = 44,
180 DXGIFormatD24_UNORM_S8_UINT = 45,
181 DXGIFormatR24_UNORM_X8_TYPELESS = 46,
182 DXGIFormatX24_TYPELESS_G8_UINT = 47,
183 DXGIFormatR8G8_TYPELESS = 48,
184 DXGIFormatR8G8_UNORM = 49,
185 DXGIFormatR8G8_UINT = 50,
186 DXGIFormatR8G8_SNORM = 51,
187 DXGIFormatR8G8_SINT = 52,
188 DXGIFormatR16_TYPELESS = 53,
189 DXGIFormatR16_FLOAT = 54,
190 DXGIFormatD16_UNORM = 55,
191 DXGIFormatR16_UNORM = 56,
192 DXGIFormatR16_UINT = 57,
193 DXGIFormatR16_SNORM = 58,
194 DXGIFormatR16_SINT = 59,
195 DXGIFormatR8_TYPELESS = 60,
196 DXGIFormatR8_UNORM = 61,
197 DXGIFormatR8_UINT = 62,
198 DXGIFormatR8_SNORM = 63,
199 DXGIFormatR8_SINT = 64,
200 DXGIFormatA8_UNORM = 65,
201 DXGIFormatR1_UNORM = 66,
202 DXGIFormatR9G9B9E5_SHAREDEXP = 67,
203 DXGIFormatR8G8_B8G8_UNORM = 68,
204 DXGIFormatG8R8_G8B8_UNORM = 69,
205 DXGIFormatBC1_TYPELESS = 70,
206 DXGIFormatBC1_UNORM = 71,
207 DXGIFormatBC1_UNORM_SRGB = 72,
208 DXGIFormatBC2_TYPELESS = 73,
209 DXGIFormatBC2_UNORM = 74,
210 DXGIFormatBC2_UNORM_SRGB = 75,
211 DXGIFormatBC3_TYPELESS = 76,
212 DXGIFormatBC3_UNORM = 77,
213 DXGIFormatBC3_UNORM_SRGB = 78,
214 DXGIFormatBC4_TYPELESS = 79,
215 DXGIFormatBC4_UNORM = 80,
216 DXGIFormatBC4_SNORM = 81,
217 DXGIFormatBC5_TYPELESS = 82,
218 DXGIFormatBC5_UNORM = 83,
219 DXGIFormatBC5_SNORM = 84,
220 DXGIFormatB5G6R5_UNORM = 85,
221 DXGIFormatB5G5R5A1_UNORM = 86,
222 DXGIFormatB8G8R8A8_UNORM = 87,
223 DXGIFormatB8G8R8X8_UNORM = 88,
224 DXGIFormatR10G10B10_XR_BIAS_A2_UNORM = 89,
225 DXGIFormatB8G8R8A8_TYPELESS = 90,
226 DXGIFormatB8G8R8A8_UNORM_SRGB = 91,
227 DXGIFormatB8G8R8X8_TYPELESS = 92,
228 DXGIFormatB8G8R8X8_UNORM_SRGB = 93,
229 DXGIFormatBC6H_TYPELESS = 94,
230 DXGIFormatBC6H_UF16 = 95,
231 DXGIFormatBC6H_SF16 = 96,
232 DXGIFormatBC7_TYPELESS = 97,
233 DXGIFormatBC7_UNORM = 98,
234 DXGIFormatBC7_UNORM_SRGB = 99,
235 DXGIFormatAYUV = 100,
236 DXGIFormatY410 = 101,
237 DXGIFormatY416 = 102,
238 DXGIFormatNV12 = 103,
239 DXGIFormatP010 = 104,
240 DXGIFormatP016 = 105,
241 DXGIFormat420_OPAQUE = 106,
242 DXGIFormatYUY2 = 107,
243 DXGIFormatY210 = 108,
244 DXGIFormatY216 = 109,
245 DXGIFormatNV11 = 110,
246 DXGIFormatAI44 = 111,
247 DXGIFormatIA44 = 112,
248 DXGIFormatP8 = 113,
249 DXGIFormatA8P8 = 114,
250 DXGIFormatB4G4R4A4_UNORM = 115,
251 DXGIFormatP208 = 130,
252 DXGIFormatV208 = 131,
253 DXGIFormatV408 = 132,
254 DXGIFormatSAMPLER_FEEDBACK_MIN_MIP_OPAQUE,
255 DXGIFormatSAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE,
256 DXGIFormatFORCE_UINT = 0xffffffff
257};
258
259enum DXGIMiscFlags2
260{
261 // not really flags...
262 DXGIAlphaModeUnknow = 0,
263 DXGIAlphaModeStraight = 1,
264 DXGIAlphaModePremultiplied = 2,
265 DXGIAlphaModeOpaque = 3,
266 DXGIAlphaModeCustom = 4
267};
268
269enum Colors {
270 Red = 0,
271 Green,
272 Blue,
273 Alpha,
274 ColorCount
275};
276
277enum DXTVersions {
278 One = 1,
279 Two = 2,
280 Three = 3,
281 Four = 4,
282 Five = 5,
283 RXGB = 6
284};
285
286// All magic numbers are little-endian as long as dds format has little
287// endian byte order
288static const quint32 ddsMagic = 0x20534444; // "DDS "
289static const quint32 dx10Magic = 0x30315844; // "DX10"
290
291static const qint64 headerSize = 128;
292static const quint32 ddsSize = 124; // headerSize without magic
293static const quint32 pixelFormatSize = 32;
294
295struct FaceOffset
296{
297 int x, y;
298};
299
300static const FaceOffset faceOffsets[6] = { {.x: 2, .y: 1}, {.x: 0, .y: 1}, {.x: 1, .y: 0}, {.x: 1, .y: 2}, {.x: 1, .y: 1}, {.x: 3, .y: 1} };
301
302static int faceFlags[6] = {
303 DDSHeader::Caps2CubeMapPositiveX,
304 DDSHeader::Caps2CubeMapNegativeX,
305 DDSHeader::Caps2CubeMapPositiveY,
306 DDSHeader::Caps2CubeMapNegativeY,
307 DDSHeader::Caps2CubeMapPositiveZ,
308 DDSHeader::Caps2CubeMapNegativeZ
309};
310
311struct FormatInfo
312{
313 Format format;
314 quint32 flags;
315 quint32 bitCount;
316 quint32 rBitMask;
317 quint32 gBitMask;
318 quint32 bBitMask;
319 quint32 aBitMask;
320};
321
322static const FormatInfo formatInfos[] = {
323 { .format: FormatA8R8G8B8, .flags: DDSPixelFormat::FlagRGBA, .bitCount: 32, .rBitMask: 0x00ff0000, .gBitMask: 0x0000ff00, .bBitMask: 0x000000ff, .aBitMask: 0xff000000 },
324 { .format: FormatX8R8G8B8, .flags: DDSPixelFormat::FlagRGB, .bitCount: 32, .rBitMask: 0x00ff0000, .gBitMask: 0x0000ff00, .bBitMask: 0x000000ff, .aBitMask: 0x00000000 },
325 { .format: FormatA2B10G10R10, .flags: DDSPixelFormat::FlagRGBA, .bitCount: 32, .rBitMask: 0x000003ff, .gBitMask: 0x000ffc00, .bBitMask: 0x3ff00000, .aBitMask: 0xc0000000 },
326 { .format: FormatA8B8G8R8, .flags: DDSPixelFormat::FlagRGBA, .bitCount: 32, .rBitMask: 0x000000ff, .gBitMask: 0x0000ff00, .bBitMask: 0x00ff0000, .aBitMask: 0xff000000 },
327 { .format: FormatX8B8G8R8, .flags: DDSPixelFormat::FlagRGB, .bitCount: 32, .rBitMask: 0x000000ff, .gBitMask: 0x0000ff00, .bBitMask: 0x00ff0000, .aBitMask: 0x00000000 },
328 { .format: FormatG16R16, .flags: DDSPixelFormat::FlagRGBA, .bitCount: 32, .rBitMask: 0x0000ffff, .gBitMask: 0xffff0000, .bBitMask: 0x00000000, .aBitMask: 0x00000000 },
329 { .format: FormatG16R16, .flags: DDSPixelFormat::FlagRGB, .bitCount: 32, .rBitMask: 0x0000ffff, .gBitMask: 0xffff0000, .bBitMask: 0x00000000, .aBitMask: 0x00000000 },
330 { .format: FormatA2R10G10B10, .flags: DDSPixelFormat::FlagRGBA, .bitCount: 32, .rBitMask: 0x3ff00000, .gBitMask: 0x000ffc00, .bBitMask: 0x000003ff, .aBitMask: 0xc0000000 },
331
332 { .format: FormatR8G8B8, .flags: DDSPixelFormat::FlagRGB, .bitCount: 24, .rBitMask: 0x00ff0000, .gBitMask: 0x0000ff00, .bBitMask: 0x000000ff, .aBitMask: 0x00000000 },
333
334 { .format: FormatR5G6B5, .flags: DDSPixelFormat::FlagRGB, .bitCount: 16, .rBitMask: 0x0000f800, .gBitMask: 0x000007e0, .bBitMask: 0x0000001f, .aBitMask: 0x00000000 },
335 { .format: FormatX1R5G5B5, .flags: DDSPixelFormat::FlagRGB, .bitCount: 16, .rBitMask: 0x00007c00, .gBitMask: 0x000003e0, .bBitMask: 0x0000001f, .aBitMask: 0x00000000 },
336 { .format: FormatA1R5G5B5, .flags: DDSPixelFormat::FlagRGBA, .bitCount: 16, .rBitMask: 0x00007c00, .gBitMask: 0x000003e0, .bBitMask: 0x0000001f, .aBitMask: 0x00008000 },
337 { .format: FormatA4R4G4B4, .flags: DDSPixelFormat::FlagRGBA, .bitCount: 16, .rBitMask: 0x00000f00, .gBitMask: 0x000000f0, .bBitMask: 0x0000000f, .aBitMask: 0x0000f000 },
338 { .format: FormatA8R3G3B2, .flags: DDSPixelFormat::FlagRGBA, .bitCount: 16, .rBitMask: 0x000000e0, .gBitMask: 0x0000001c, .bBitMask: 0x00000003, .aBitMask: 0x0000ff00 },
339 { .format: FormatX4R4G4B4, .flags: DDSPixelFormat::FlagRGB, .bitCount: 16, .rBitMask: 0x00000f00, .gBitMask: 0x000000f0, .bBitMask: 0x0000000f, .aBitMask: 0x00000000 },
340 { .format: FormatA8L8, .flags: DDSPixelFormat::FlagLA, .bitCount: 16, .rBitMask: 0x000000ff, .gBitMask: 0x00000000, .bBitMask: 0x00000000, .aBitMask: 0x0000ff00 },
341 { .format: FormatL16, .flags: DDSPixelFormat::FlagLuminance, .bitCount: 16, .rBitMask: 0x0000ffff, .gBitMask: 0x00000000, .bBitMask: 0x00000000, .aBitMask: 0x00000000 },
342
343 { .format: FormatR3G3B2, .flags: DDSPixelFormat::FlagRGB, .bitCount: 8, .rBitMask: 0x000000e0, .gBitMask: 0x0000001c, .bBitMask: 0x00000003, .aBitMask: 0x00000000 },
344 { .format: FormatA8, .flags: DDSPixelFormat::FlagAlpha, .bitCount: 8, .rBitMask: 0x00000000, .gBitMask: 0x00000000, .bBitMask: 0x00000000, .aBitMask: 0x000000ff },
345 { .format: FormatL8, .flags: DDSPixelFormat::FlagLuminance, .bitCount: 8, .rBitMask: 0x000000ff, .gBitMask: 0x00000000, .bBitMask: 0x00000000, .aBitMask: 0x00000000 },
346 { .format: FormatA4L4, .flags: DDSPixelFormat::FlagLA, .bitCount: 8, .rBitMask: 0x0000000f, .gBitMask: 0x00000000, .bBitMask: 0x00000000, .aBitMask: 0x000000f0 },
347
348 { .format: FormatV8U8, .flags: DDSPixelFormat::FlagNormal, .bitCount: 16, .rBitMask: 0x000000ff, .gBitMask: 0x0000ff00, .bBitMask: 0x00000000, .aBitMask: 0x00000000 },
349 { .format: FormatL6V5U5, .flags: 0, .bitCount: 16, .rBitMask: 0x0000001f, .gBitMask: 0x000003e0, .bBitMask: 0x0000fc00, .aBitMask: 0x00000000 },
350 { .format: FormatX8L8V8U8, .flags: 0, .bitCount: 32, .rBitMask: 0x000000ff, .gBitMask: 0x0000ff00, .bBitMask: 0x00ff0000, .aBitMask: 0x00000000 },
351 { .format: FormatQ8W8V8U8, .flags: DDSPixelFormat::FlagNormal, .bitCount: 32, .rBitMask: 0x000000ff, .gBitMask: 0x0000ff00, .bBitMask: 0x00ff0000, .aBitMask: 0xff000000 },
352 { .format: FormatV16U16, .flags: DDSPixelFormat::FlagNormal, .bitCount: 32, .rBitMask: 0x0000ffff, .gBitMask: 0xffff0000, .bBitMask: 0x00000000, .aBitMask: 0x00000000 },
353 { .format: FormatA2W10V10U10, .flags: DDSPixelFormat::FlagNormal, .bitCount: 32, .rBitMask: 0x3ff00000, .gBitMask: 0x000ffc00, .bBitMask: 0x000003ff, .aBitMask: 0xc0000000 }
354};
355static const size_t formatInfosSize = sizeof(formatInfos)/sizeof(FormatInfo);
356
357static const Format knownFourCCs[] = {
358 FormatA16B16G16R16,
359 FormatV8U8,
360 FormatUYVY,
361 FormatR8G8B8G8,
362 FormatYUY2,
363 FormatG8R8G8B8,
364 FormatDXT1,
365 FormatDXT2,
366 FormatDXT3,
367 FormatDXT4,
368 FormatDXT5,
369 FormatRXGB,
370 FormatATI2,
371 FormatQ16W16V16U16,
372 FormatR16F,
373 FormatG16R16F,
374 FormatA16B16G16R16F,
375 FormatR32F,
376 FormatG32R32F,
377 FormatA32B32G32R32F,
378 FormatCxV8U8
379};
380static const size_t knownFourCCsSize = sizeof(knownFourCCs)/sizeof(Format);
381
382struct DXGIFormatToFormat
383{
384 DXGIFormat dxgiFormat;
385 Format format;
386};
387
388static const DXGIFormatToFormat knownDXGIFormat[] = {
389 { .dxgiFormat: DXGIFormatR16G16B16A16_FLOAT, .format: FormatA16B16G16R16F },
390 { .dxgiFormat: DXGIFormatR32G32B32A32_FLOAT, .format: FormatA32B32G32R32F },
391 { .dxgiFormat: DXGIFormatR16G16_FLOAT, .format: FormatG16R16F },
392 { .dxgiFormat: DXGIFormatR32G32_FLOAT, .format: FormatG32R32F },
393 { .dxgiFormat: DXGIFormatR16_FLOAT, .format: FormatR16F },
394 { .dxgiFormat: DXGIFormatR32_FLOAT, .format: FormatR32F }
395};
396static const size_t knownDXGIFormatSize = sizeof(knownDXGIFormat)/sizeof(DXGIFormatToFormat);
397
398struct FormatName
399{
400 Format format;
401 const char *const name;
402};
403static const FormatName formatNames[] = {
404 { .format: FormatUnknown, .name: "unknown" },
405
406 { .format: FormatR8G8B8, .name: "R8G8B8" },
407 { .format: FormatA8R8G8B8, .name: "A8R8G8B8" },
408 { .format: FormatX8R8G8B8, .name: "X8R8G8B8" },
409 { .format: FormatR5G6B5, .name: "R5G6B5" },
410 { .format: FormatX1R5G5B5, .name: "X1R5G5B5" },
411 { .format: FormatA1R5G5B5, .name: "A1R5G5B5" },
412 { .format: FormatA4R4G4B4, .name: "A4R4G4B4" },
413 { .format: FormatR3G3B2, .name: "R3G3B2" },
414 { .format: FormatA8, .name: "A8" },
415 { .format: FormatA8R3G3B2, .name: "A8R3G3B2" },
416 { .format: FormatX4R4G4B4, .name: "X4R4G4B4" },
417 { .format: FormatA2B10G10R10, .name: "A2B10G10R10" },
418 { .format: FormatA8B8G8R8, .name: "A8B8G8R8" },
419 { .format: FormatX8B8G8R8, .name: "X8B8G8R8" },
420 { .format: FormatG16R16, .name: "G16R16" },
421 { .format: FormatA2R10G10B10, .name: "A2R10G10B10" },
422 { .format: FormatA16B16G16R16, .name: "A16B16G16R16" },
423
424 { .format: FormatA8P8, .name: "A8P8" },
425 { .format: FormatP8, .name: "P8" },
426
427 { .format: FormatL8, .name: "L8" },
428 { .format: FormatA8L8, .name: "A8L8" },
429 { .format: FormatA4L4, .name: "A4L4" },
430
431 { .format: FormatV8U8, .name: "V8U8" },
432 { .format: FormatL6V5U5, .name: "L6V5U5" },
433 { .format: FormatX8L8V8U8, .name: "X8L8V8U8" },
434 { .format: FormatQ8W8V8U8, .name: "Q8W8V8U8" },
435 { .format: FormatV16U16, .name: "V16U16" },
436 { .format: FormatA2W10V10U10, .name: "A2W10V10U10" },
437
438 { .format: FormatUYVY, .name: "UYVY" },
439 { .format: FormatR8G8B8G8, .name: "R8G8_B8G8" },
440 { .format: FormatYUY2, .name: "YUY2" },
441 { .format: FormatG8R8G8B8, .name: "G8R8_G8B8" },
442 { .format: FormatDXT1, .name: "DXT1" },
443 { .format: FormatDXT2, .name: "DXT2" },
444 { .format: FormatDXT3, .name: "DXT3" },
445 { .format: FormatDXT4, .name: "DXT4" },
446 { .format: FormatDXT5, .name: "DXT5" },
447 { .format: FormatRXGB, .name: "RXGB" },
448 { .format: FormatATI2, .name: "ATI2" },
449
450 { .format: FormatD16Lockable, .name: "D16Lockable" },
451 { .format: FormatD32, .name: "D32" },
452 { .format: FormatD15S1, .name: "D15S1" },
453 { .format: FormatD24S8, .name: "D24S8" },
454 { .format: FormatD24X8, .name: "D24X8" },
455 { .format: FormatD24X4S4, .name: "D24X4S4" },
456 { .format: FormatD16, .name: "D16" },
457
458 { .format: FormatD32FLockable, .name: "D32FLockable" },
459 { .format: FormatD24FS8, .name: "D24FS8" },
460
461 { .format: FormatD32Lockable, .name: "D32Lockable" },
462 { .format: FormatS8Lockable, .name: "S8Lockable" },
463
464 { .format: FormatL16, .name: "L16" },
465
466 { .format: FormatVertexData, .name: "VertexData" },
467 { .format: FormatIndex32, .name: "Index32" },
468 { .format: FormatIndex32, .name: "Index32" },
469
470 { .format: FormatQ16W16V16U16, .name: "Q16W16V16U16" },
471
472 { .format: FormatMulti2ARGB8, .name: "Multi2ARGB8" },
473
474 { .format: FormatR16F, .name: "R16F" },
475 { .format: FormatG16R16F, .name: "G16R16F" },
476 { .format: FormatA16B16G16R16F, .name: "A16B16G16R16F" },
477
478 { .format: FormatR32F, .name: "R32F" },
479 { .format: FormatG32R32F, .name: "G32R32F" },
480 { .format: FormatA32B32G32R32F, .name: "A32B32G32R32F" },
481
482 { .format: FormatCxV8U8, .name: "CxV8U8" },
483
484 { .format: FormatA1, .name: "A1" },
485 { .format: FormatA2B10G10R10_XR_BIAS, .name: "A2B10G10R10_XR_BIAS" },
486 { .format: FormatBinaryBuffer, .name: "BinaryBuffer" },
487
488 { .format: FormatP4, .name: "P4" },
489 { .format: FormatA4P4, .name: "A4P4" }
490};
491static const size_t formatNamesSize = sizeof(formatNames)/sizeof(FormatName);
492
493QDataStream &operator>>(QDataStream &s, DDSPixelFormat &pixelFormat)
494{
495 s >> pixelFormat.size;
496 s >> pixelFormat.flags;
497 s >> pixelFormat.fourCC;
498 s >> pixelFormat.rgbBitCount;
499 s >> pixelFormat.rBitMask;
500 s >> pixelFormat.gBitMask;
501 s >> pixelFormat.bBitMask;
502 s >> pixelFormat.aBitMask;
503 return s;
504}
505
506QDataStream &operator<<(QDataStream &s, const DDSPixelFormat &pixelFormat)
507{
508 s << pixelFormat.size;
509 s << pixelFormat.flags;
510 s << pixelFormat.fourCC;
511 s << pixelFormat.rgbBitCount;
512 s << pixelFormat.rBitMask;
513 s << pixelFormat.gBitMask;
514 s << pixelFormat.bBitMask;
515 s << pixelFormat.aBitMask;
516 return s;
517}
518
519QDataStream &operator>>(QDataStream &s, DDSHeaderDX10 &header)
520{
521 s >> header.dxgiFormat;
522 s >> header.resourceDimension;
523 s >> header.miscFlag;
524 s >> header.arraySize;
525 s >> header.miscFlags2;
526 return s;
527}
528
529QDataStream &operator<<(QDataStream &s, const DDSHeaderDX10 &header)
530{
531 s << header.dxgiFormat;
532 s << header.resourceDimension;
533 s << header.miscFlag;
534 s << header.arraySize;
535 s << header.miscFlags2;
536 return s;
537}
538
539QDataStream &operator>>(QDataStream &s, DDSHeader &header)
540{
541 s >> header.magic;
542 s >> header.size;
543 s >> header.flags;
544 s >> header.height;
545 s >> header.width;
546 s >> header.pitchOrLinearSize;
547 s >> header.depth;
548 s >> header.mipMapCount;
549 for (int i = 0; i < DDSHeader::ReservedCount; i++)
550 s >> header.reserved1[i];
551 s >> header.pixelFormat;
552 s >> header.caps;
553 s >> header.caps2;
554 s >> header.caps3;
555 s >> header.caps4;
556 s >> header.reserved2;
557 if (header.pixelFormat.fourCC == dx10Magic)
558 s >> header.header10;
559
560 return s;
561}
562
563QDataStream &operator<<(QDataStream &s, const DDSHeader &header)
564{
565 s << header.magic;
566 s << header.size;
567 s << header.flags;
568 s << header.height;
569 s << header.width;
570 s << header.pitchOrLinearSize;
571 s << header.depth;
572 s << header.mipMapCount;
573 for (int i = 0; i < DDSHeader::ReservedCount; i++)
574 s << header.reserved1[i];
575 s << header.pixelFormat;
576 s << header.caps;
577 s << header.caps2;
578 s << header.caps3;
579 s << header.caps4;
580 s << header.reserved2;
581 if (header.pixelFormat.fourCC == dx10Magic)
582 s << header.header10;
583
584 return s;
585}
586
587inline qsizetype ptrDiff(const void *end, const void *start)
588{
589 return qsizetype(reinterpret_cast<const char*>(end) - reinterpret_cast<const char*>(start));
590}
591
592static inline int maskToShift(quint32 mask)
593{
594 if (mask == 0)
595 return 0;
596
597 int result = 0;
598 while (!((mask >> result) & 1))
599 result++;
600 return result;
601}
602
603static inline int maskLength(quint32 mask)
604{
605 int result = 0;
606 while (mask) {
607 if (mask & 1)
608 result++;
609 mask >>= 1;
610 }
611 return result;
612}
613
614static inline quint32 readValue(QDataStream &s, quint32 size)
615{
616 quint32 value = 0;
617 if (size != 8 && size != 16 && size != 24 && size != 32) {
618 s.setStatus(QDataStream::ReadCorruptData);
619 return value;
620 }
621
622 quint8 tmp;
623 for (unsigned bit = 0; bit < size; bit += 8) {
624 s >> tmp;
625 value += (quint32(tmp) << bit);
626 }
627 return value;
628}
629
630static inline bool hasAlpha(const DDSHeader &dds)
631{
632 return (dds.pixelFormat.flags & (DDSPixelFormat::FlagAlphaPixels | DDSPixelFormat::FlagAlpha)) != 0;
633}
634
635static inline bool isCubeMap(const DDSHeader &dds)
636{
637 return (dds.caps2 & DDSHeader::Caps2CubeMap) != 0;
638}
639
640static inline QRgb yuv2rgb(quint8 Y, quint8 U, quint8 V)
641{
642 return qRgb(r: quint8(Y + 1.13983 * (V - 128)),
643 g: quint8(Y - 0.39465 * (U - 128) - 0.58060 * (V - 128)),
644 b: quint8(Y + 2.03211 * (U - 128)));
645}
646
647static void strideAlignment(QDataStream &s, const DDSHeader &dds, quint32 width)
648{
649#ifdef DDS_DISABLE_STRIDE_ALIGNMENT
650 Q_UNUSED(s)
651 Q_UNUSED(dds)
652 Q_UNUSED(width)
653#else
654 if (dds.flags & DDSHeader::FlagPitch) {
655 if (auto alignBytes = qint64(dds.pitchOrLinearSize) - (width * dds.pixelFormat.rgbBitCount + 7) / 8) {
656 quint8 tmp;
657 for (; alignBytes > 0 && alignBytes < 4; --alignBytes) {
658 s >> tmp;
659 }
660 }
661 }
662#endif
663}
664
665static Format getFormat(const DDSHeader &dds)
666{
667 const DDSPixelFormat &format = dds.pixelFormat;
668 if (format.flags & DDSPixelFormat::FlagPaletteIndexed4) {
669 return FormatP4;
670 } else if (format.flags & DDSPixelFormat::FlagPaletteIndexed8) {
671 return FormatP8;
672 } else if (format.flags & DDSPixelFormat::FlagFourCC) {
673 if (dds.pixelFormat.fourCC == dx10Magic) {
674 for (size_t i = 0; i < knownDXGIFormatSize; ++i) {
675 if (dds.header10.dxgiFormat == knownDXGIFormat[i].dxgiFormat)
676 return knownDXGIFormat[i].format;
677 }
678 } else {
679 for (size_t i = 0; i < knownFourCCsSize; ++i) {
680 if (dds.pixelFormat.fourCC == knownFourCCs[i])
681 return knownFourCCs[i];
682 }
683 }
684 } else {
685 for (size_t i = 0; i < formatInfosSize; ++i) {
686 const FormatInfo &info = formatInfos[i];
687 if ((format.flags & info.flags) == info.flags &&
688 format.rgbBitCount == info.bitCount &&
689 format.rBitMask == info.rBitMask &&
690 format.gBitMask == info.gBitMask &&
691 format.bBitMask == info.bBitMask &&
692 format.aBitMask == info.aBitMask) {
693 return info.format;
694 }
695 }
696 }
697
698 return FormatUnknown;
699}
700
701static inline quint8 getNormalZ(quint8 nx, quint8 ny)
702{
703 const double fx = nx / 127.5 - 1.0;
704 const double fy = ny / 127.5 - 1.0;
705 const double fxfy = 1.0 - fx * fx - fy * fy;
706 return fxfy > 0 ? 255 * std::sqrt(x: fxfy) : 0;
707}
708
709static inline void decodeColor(quint16 color, quint8 &red, quint8 &green, quint8 &blue)
710{
711 red = ((color >> 11) & 0x1f) << 3;
712 green = ((color >> 5) & 0x3f) << 2;
713 blue = (color & 0x1f) << 3;
714}
715
716static inline quint8 calcC2(quint8 c0, quint8 c1)
717{
718 return 2.0 * c0 / 3.0 + c1 / 3.0;
719}
720
721static inline quint8 calcC2a(quint8 c0, quint8 c1)
722{
723 return c0 / 2.0 + c1 / 2.0;
724}
725
726static inline quint8 calcC3(quint8 c0, quint8 c1)
727{
728 return c0 / 3.0 + 2.0 * c1 / 3.0;
729}
730
731static void DXTFillColors(QRgb *result, quint16 c0, quint16 c1, quint32 table, bool dxt1a = false)
732{
733 quint8 r[4];
734 quint8 g[4];
735 quint8 b[4];
736 quint8 a[4];
737
738 a[0] = a[1] = a[2] = a[3] = 255;
739
740 decodeColor(color: c0, red&: r[0], green&: g[0], blue&: b[0]);
741 decodeColor(color: c1, red&: r[1], green&: g[1], blue&: b[1]);
742 if (!dxt1a) {
743 r[2] = calcC2(c0: r[0], c1: r[1]);
744 g[2] = calcC2(c0: g[0], c1: g[1]);
745 b[2] = calcC2(c0: b[0], c1: b[1]);
746 r[3] = calcC3(c0: r[0], c1: r[1]);
747 g[3] = calcC3(c0: g[0], c1: g[1]);
748 b[3] = calcC3(c0: b[0], c1: b[1]);
749 } else {
750 r[2] = calcC2a(c0: r[0], c1: r[1]);
751 g[2] = calcC2a(c0: g[0], c1: g[1]);
752 b[2] = calcC2a(c0: b[0], c1: b[1]);
753 r[3] = g[3] = b[3] = a[3] = 0;
754 }
755
756 for (int k = 0; k < 4; k++)
757 for (int l = 0; l < 4; l++) {
758 unsigned index = table & 0x0003;
759 table >>= 2;
760
761 result[k * 4 + l] = qRgba(r: r[index], g: g[index], b: b[index], a: a[index]);
762 }
763}
764
765template <DXTVersions version>
766inline void setAlphaDXT32Helper(QRgb *rgbArr, quint64 alphas)
767{
768 Q_STATIC_ASSERT(version == Two || version == Three);
769 for (int i = 0; i < 16; i++) {
770 quint8 alpha = 16 * (alphas & 0x0f);
771 QRgb rgb = rgbArr[i];
772 if (version == Two) // DXT2
773 rgbArr[i] = qRgba(r: qRed(rgb) * alpha / 0xff, g: qGreen(rgb) * alpha / 0xff, b: qBlue(rgb) * alpha / 0xff, a: alpha);
774 else if (version == Three) // DXT3
775 rgbArr[i] = qRgba(r: qRed(rgb), g: qGreen(rgb), b: qBlue(rgb), a: alpha);
776 alphas = alphas >> 4;
777 }
778}
779
780template <DXTVersions version>
781inline void setAlphaDXT45Helper(QRgb *rgbArr, quint64 alphas)
782{
783 Q_STATIC_ASSERT(version == Four || version == Five);
784 quint8 a[8];
785 a[0] = alphas & 0xff;
786 a[1] = (alphas >> 8) & 0xff;
787 if (a[0] > a[1]) {
788 a[2] = (6*a[0] + 1*a[1]) / 7;
789 a[3] = (5*a[0] + 2*a[1]) / 7;
790 a[4] = (4*a[0] + 3*a[1]) / 7;
791 a[5] = (3*a[0] + 4*a[1]) / 7;
792 a[6] = (2*a[0] + 5*a[1]) / 7;
793 a[7] = (1*a[0] + 6*a[1]) / 7;
794 } else {
795 a[2] = (4*a[0] + 1*a[1]) / 5;
796 a[3] = (3*a[0] + 2*a[1]) / 5;
797 a[4] = (2*a[0] + 3*a[1]) / 5;
798 a[5] = (1*a[0] + 4*a[1]) / 5;
799 a[6] = 0;
800 a[7] = 255;
801 }
802 alphas >>= 16;
803 for (int i = 0; i < 16; i++) {
804 quint8 index = alphas & 0x07;
805 quint8 alpha = a[index];
806 QRgb rgb = rgbArr[i];
807 if (version == Four) // DXT4
808 rgbArr[i] = qRgba(r: qRed(rgb) * alpha / 0xff, g: qGreen(rgb) * alpha / 0xff, b: qBlue(rgb) * alpha / 0xff, a: alpha);
809 else if (version == Five) // DXT5
810 rgbArr[i] = qRgba(r: qRed(rgb), g: qGreen(rgb), b: qBlue(rgb), a: alpha);
811 alphas = alphas >> 3;
812 }
813}
814
815template <DXTVersions version>
816inline void setAlphaDXT(QRgb *rgbArr, quint64 alphas)
817{
818 Q_UNUSED(rgbArr);
819 Q_UNUSED(alphas);
820}
821
822template <>
823inline void setAlphaDXT<Two>(QRgb *rgbArr, quint64 alphas)
824{
825 setAlphaDXT32Helper<Two>(rgbArr, alphas);
826}
827
828template <>
829inline void setAlphaDXT<Three>(QRgb *rgbArr, quint64 alphas)
830{
831 setAlphaDXT32Helper<Three>(rgbArr, alphas);
832}
833
834template <>
835inline void setAlphaDXT<Four>(QRgb *rgbArr, quint64 alphas)
836{
837 setAlphaDXT45Helper<Four>(rgbArr, alphas);
838}
839
840template <>
841inline void setAlphaDXT<Five>(QRgb *rgbArr, quint64 alphas)
842{
843 setAlphaDXT45Helper<Five>(rgbArr, alphas);
844}
845
846template <>
847inline void setAlphaDXT<RXGB>(QRgb *rgbArr, quint64 alphas)
848{
849 setAlphaDXT45Helper<Five>(rgbArr, alphas);
850}
851
852static inline QRgb invertRXGBColors(QRgb pixel)
853{
854 return qRgb(r: qAlpha(rgb: pixel), g: qGreen(rgb: pixel), b: qBlue(rgb: pixel));
855}
856
857template <DXTVersions version>
858static QImage readDXT(QDataStream &s, quint32 width, quint32 height)
859{
860 QImage::Format format = (version == Two || version == Four) ?
861 QImage::Format_ARGB32_Premultiplied : QImage::Format_ARGB32;
862
863 QImage image = imageAlloc(width, height, format);
864 if (image.isNull()) {
865 return image;
866 }
867
868 for (quint32 i = 0; i < height; i += 4) {
869 for (quint32 j = 0; j < width; j += 4) {
870 quint64 alpha = 0;
871 quint16 c0, c1;
872 quint32 table;
873 if (version != One)
874 s >> alpha;
875 s >> c0;
876 s >> c1;
877 s >> table;
878 if (s.status() != QDataStream::Ok)
879 return QImage();
880
881 QRgb arr[16];
882
883 DXTFillColors(result: arr, c0, c1, table, dxt1a: version == One && c0 <= c1);
884 setAlphaDXT<version>(arr, alpha);
885
886 const quint32 kMax = qMin<quint32>(a: 4, b: height - i);
887 const quint32 lMax = qMin<quint32>(a: 4, b: width - j);
888 for (quint32 k = 0; k < kMax; k++) {
889 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(i + k));
890 for (quint32 l = 0; l < lMax; l++) {
891 QRgb pixel = arr[k * 4 + l];
892 if (version == RXGB)
893 pixel = invertRXGBColors(pixel);
894
895 line[j + l] = pixel;
896 }
897 }
898 }
899 }
900 return image;
901}
902
903static inline QImage readDXT1(QDataStream &s, quint32 width, quint32 height)
904{
905 return readDXT<One>(s, width, height);
906}
907
908static inline QImage readDXT2(QDataStream &s, quint32 width, quint32 height)
909{
910 return readDXT<Two>(s, width, height);
911}
912
913static inline QImage readDXT3(QDataStream &s, quint32 width, quint32 height)
914{
915 return readDXT<Three>(s, width, height);
916}
917
918static inline QImage readDXT4(QDataStream &s, quint32 width, quint32 height)
919{
920 return readDXT<Four>(s, width, height);
921}
922
923static inline QImage readDXT5(QDataStream &s, quint32 width, quint32 height)
924{
925 return readDXT<Five>(s, width, height);
926}
927
928static inline QImage readRXGB(QDataStream &s, quint32 width, quint32 height)
929{
930 return readDXT<RXGB>(s, width, height);
931}
932
933static QImage readATI2(QDataStream &s, quint32 width, quint32 height)
934{
935 QImage image = imageAlloc(width, height, format: QImage::Format_RGB32);
936 if (image.isNull()) {
937 return image;
938 }
939
940 for (quint32 i = 0; i < height; i += 4) {
941 for (quint32 j = 0; j < width; j += 4) {
942 quint64 alpha1;
943 quint64 alpha2;
944 s >> alpha1;
945 s >> alpha2;
946 if (s.status() != QDataStream::Ok)
947 return QImage();
948
949 QRgb arr[16];
950 memset(s: arr, c: 0, n: sizeof(QRgb) * 16);
951 setAlphaDXT<Five>(rgbArr: arr, alphas: alpha1);
952 for (int k = 0; k < 16; ++k) {
953 quint8 a = qAlpha(rgb: arr[k]);
954 arr[k] = qRgba(r: 0, g: 0, b: a, a: 0);
955 }
956 setAlphaDXT<Five>(rgbArr: arr, alphas: alpha2);
957
958 const quint32 kMax = qMin<quint32>(a: 4, b: height - i);
959 const quint32 lMax = qMin<quint32>(a: 4, b: width - j);
960 for (quint32 k = 0; k < kMax; k++) {
961 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(i + k));
962 for (quint32 l = 0; l < lMax; l++) {
963 QRgb pixel = arr[k * 4 + l];
964 const quint8 nx = qAlpha(rgb: pixel);
965 const quint8 ny = qBlue(rgb: pixel);
966 const quint8 nz = getNormalZ(nx, ny);
967 line[j + l] = qRgb(r: nx, g: ny, b: nz);
968 }
969 }
970 }
971 }
972 return image;
973}
974
975static QImage readUnsignedImage(QDataStream &s, const DDSHeader &dds, quint32 width, quint32 height, bool hasAlpha)
976{
977 quint32 flags = dds.pixelFormat.flags;
978
979 quint32 masks[ColorCount];
980 quint8 shifts[ColorCount];
981 quint8 bits[ColorCount];
982 masks[Red] = dds.pixelFormat.rBitMask;
983 masks[Green] = dds.pixelFormat.gBitMask;
984 masks[Blue] = dds.pixelFormat.bBitMask;
985 masks[Alpha] = hasAlpha ? dds.pixelFormat.aBitMask : 0;
986 for (int i = 0; i < ColorCount; ++i) {
987 shifts[i] = maskToShift(mask: masks[i]);
988 bits[i] = maskLength(mask: masks[i]);
989
990 // move mask to the left
991 if (bits[i] <= 8)
992 masks[i] = (masks[i] >> shifts[i]) << (8 - bits[i]);
993 }
994
995 QImage::Format format = hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32;
996 if (!hasAlpha && (flags & DDSPixelFormat::FlagLuminance))
997 format = QImage::Format_Grayscale8;
998 QImage image = imageAlloc(width, height, format);
999 if (image.isNull()) {
1000 return image;
1001 }
1002
1003 for (quint32 y = 0; y < height; y++) {
1004 for (quint32 x = 0; x < width; x++) {
1005 quint8 *byteLine = reinterpret_cast<quint8 *>(image.scanLine(y));
1006 QRgb *line = reinterpret_cast<QRgb *>(byteLine);
1007
1008 quint32 value = readValue(s, size: dds.pixelFormat.rgbBitCount);
1009 quint8 colors[ColorCount];
1010
1011 for (int c = 0; c < ColorCount; ++c) {
1012 if (bits[c] > 8) {
1013 // truncate unneseccary bits
1014 colors[c] = (value & masks[c]) >> shifts[c] >> (bits[c] - 8);
1015 } else {
1016 // move color to the left
1017 quint8 color = value >> shifts[c] << (8 - bits[c]) & masks[c];
1018 if (masks[c])
1019 colors[c] = color * 0xff / masks[c];
1020 else
1021 colors[c] = c == Alpha ? 0xff : 0;
1022 }
1023 }
1024
1025 if (flags & DDSPixelFormat::FlagLuminance) {
1026 if (hasAlpha)
1027 line[x] = qRgba(r: colors[Red], g: colors[Red], b: colors[Red], a: colors[Alpha]);
1028 else
1029 byteLine[x] = colors[Red];
1030 }
1031 else if (flags & DDSPixelFormat::FlagYUV) {
1032 line[x] = yuv2rgb(Y: colors[Red], U: colors[Green], V: colors[Blue]);
1033 }
1034 else {
1035 line[x] = qRgba(r: colors[Red], g: colors[Green], b: colors[Blue], a: colors[Alpha]);
1036 }
1037
1038 if (s.status() != QDataStream::Ok)
1039 return QImage();
1040 }
1041 strideAlignment(s, dds, width); // some dds seems aligned to 32 bits
1042 }
1043
1044 return image;
1045}
1046
1047static qfloat16 readFloat16(QDataStream &s)
1048{
1049 qfloat16 f16 = 0;
1050 if (s.status() != QDataStream::Ok) {
1051 return f16;
1052 }
1053 s >> f16;
1054 if (qIsNaN(f: f16)) {
1055 s.setStatus(QDataStream::ReadCorruptData);
1056 }
1057 return f16;
1058}
1059
1060static inline float readFloat32(QDataStream &s)
1061{
1062 Q_ASSERT(sizeof(float) == 4);
1063 float value = 0;
1064 if (s.status() != QDataStream::Ok) {
1065 return value;
1066 }
1067 // TODO: find better way to avoid setting precision each time
1068 QDataStream::FloatingPointPrecision precision = s.floatingPointPrecision();
1069 s.setFloatingPointPrecision(QDataStream::SinglePrecision);
1070 s >> value;
1071 s.setFloatingPointPrecision(precision);
1072 if (qIsNaN(f: value)) {
1073 s.setStatus(QDataStream::ReadCorruptData);
1074 }
1075 return value;
1076}
1077
1078static QImage readR16F(QDataStream &s, const quint32 width, const quint32 height)
1079{
1080 QImage image = imageAlloc(width, height, format: QImage::Format_RGBX16FPx4);
1081 if (image.isNull()) {
1082 return image;
1083 }
1084
1085 for (quint32 y = 0; y < height; y++) {
1086 qfloat16 *line = reinterpret_cast<qfloat16 *>(image.scanLine(y));
1087 for (quint32 x = 0; x < width; x++) {
1088 line[x * 4] = readFloat16(s);
1089 line[x * 4 + 1] = 0;
1090 line[x * 4 + 2] = 0;
1091 line[x * 4 + 3] = 1;
1092 if (s.status() != QDataStream::Ok)
1093 return QImage();
1094 }
1095 }
1096
1097 image.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
1098 return image;
1099}
1100
1101static QImage readRG16F(QDataStream &s, const quint32 width, const quint32 height)
1102{
1103 QImage image = imageAlloc(width, height, format: QImage::Format_RGBX16FPx4);
1104 if (image.isNull()) {
1105 return image;
1106 }
1107
1108 for (quint32 y = 0; y < height; y++) {
1109 qfloat16 *line = reinterpret_cast<qfloat16 *>(image.scanLine(y));
1110 for (quint32 x = 0; x < width; x++) {
1111 line[x * 4] = readFloat16(s);
1112 line[x * 4 + 1] = readFloat16(s);
1113 line[x * 4 + 2] = 0;
1114 line[x * 4 + 3] = 1;
1115 if (s.status() != QDataStream::Ok)
1116 return QImage();
1117 }
1118 }
1119
1120 image.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
1121 return image;
1122}
1123
1124static QImage readARGB16F(QDataStream &s, const quint32 width, const quint32 height, bool alphaPremul)
1125{
1126 QImage image = imageAlloc(width, height, format: alphaPremul ? QImage::Format_RGBA16FPx4_Premultiplied : QImage::Format_RGBA16FPx4);
1127 if (image.isNull()) {
1128 return image;
1129 }
1130
1131 for (quint32 y = 0; y < height; y++) {
1132 qfloat16 *line = reinterpret_cast<qfloat16 *>(image.scanLine(y));
1133 for (quint32 x = 0; x < width; x++) {
1134 line[x * 4] = readFloat16(s);
1135 line[x * 4 + 1] = readFloat16(s);
1136 line[x * 4 + 2] = readFloat16(s);
1137 line[x * 4 + 3] = readFloat16(s);
1138 if (s.status() != QDataStream::Ok)
1139 return QImage();
1140 }
1141 }
1142
1143 image.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
1144 return image;
1145}
1146
1147static QImage readR32F(QDataStream &s, const quint32 width, const quint32 height)
1148{
1149 QImage image = imageAlloc(width, height, format: QImage::Format_RGBX32FPx4);
1150 if (image.isNull()) {
1151 return image;
1152 }
1153
1154 for (quint32 y = 0; y < height; y++) {
1155 float *line = reinterpret_cast<float *>(image.scanLine(y));
1156 for (quint32 x = 0; x < width; x++) {
1157 line[x * 4] = readFloat32(s);
1158 line[x * 4 + 1] = 0;
1159 line[x * 4 + 2] = 0;
1160 line[x * 4 + 3] = 1;
1161 if (s.status() != QDataStream::Ok)
1162 return QImage();
1163 }
1164 }
1165
1166 image.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
1167 return image;
1168}
1169
1170static QImage readRG32F(QDataStream &s, const quint32 width, const quint32 height)
1171{
1172 QImage image = imageAlloc(width, height, format: QImage::Format_RGBX32FPx4);
1173 if (image.isNull()) {
1174 return image;
1175 }
1176
1177 for (quint32 y = 0; y < height; y++) {
1178 float *line = reinterpret_cast<float *>(image.scanLine(y));
1179 for (quint32 x = 0; x < width; x++) {
1180 line[x * 4] = readFloat32(s);
1181 line[x * 4 + 1] = readFloat32(s);
1182 line[x * 4 + 2] = 0;
1183 line[x * 4 + 3] = 1;
1184 if (s.status() != QDataStream::Ok)
1185 return QImage();
1186 }
1187 }
1188
1189 image.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
1190 return image;
1191}
1192
1193static QImage readARGB32F(QDataStream &s, const quint32 width, const quint32 height, bool alphaPremul)
1194{
1195 QImage image = imageAlloc(width, height, format: alphaPremul ? QImage::Format_RGBA32FPx4_Premultiplied : QImage::Format_RGBA32FPx4);
1196 if (image.isNull()) {
1197 return image;
1198 }
1199
1200 for (quint32 y = 0; y < height; y++) {
1201 float *line = reinterpret_cast<float *>(image.scanLine(y));
1202 for (quint32 x = 0; x < width; x++) {
1203 line[x * 4] = readFloat32(s);
1204 line[x * 4 + 1] = readFloat32(s);
1205 line[x * 4 + 2] = readFloat32(s);
1206 line[x * 4 + 3] = readFloat32(s);
1207 if (s.status() != QDataStream::Ok)
1208 return QImage();
1209 }
1210 }
1211
1212 image.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
1213 return image;
1214}
1215
1216static QImage readQ16W16V16U16(QDataStream &s, const quint32 width, const quint32 height)
1217{
1218 QImage image = imageAlloc(width, height, format: QImage::Format_ARGB32);
1219 if (image.isNull()) {
1220 return image;
1221 }
1222
1223 quint8 colors[ColorCount];
1224 qint16 tmp;
1225 for (quint32 y = 0; y < height; y++) {
1226 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1227 for (quint32 x = 0; x < width; x++) {
1228 for (int i = 0; i < ColorCount; i++) {
1229 s >> tmp;
1230 colors[i] = (tmp + 0x7FFF) >> 8;
1231 }
1232 line[x] = qRgba(r: colors[Red], g: colors[Green], b: colors[Blue], a: colors[Alpha]);
1233 if (s.status() != QDataStream::Ok)
1234 return QImage();
1235 }
1236 }
1237
1238 return image;
1239}
1240
1241static QImage readCxV8U8(QDataStream &s, const quint32 width, const quint32 height)
1242{
1243 QImage image = imageAlloc(width, height, format: QImage::Format_RGB32);
1244 if (image.isNull()) {
1245 return image;
1246 }
1247
1248 for (quint32 y = 0; y < height; y++) {
1249 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1250 for (quint32 x = 0; x < width; x++) {
1251 qint8 v, u;
1252 s >> v >> u;
1253
1254 const quint8 vn = v + 128;
1255 const quint8 un = u + 128;
1256 const quint8 c = getNormalZ(nx: vn, ny: un);
1257
1258 line[x] = qRgb(r: vn, g: un, b: c);
1259
1260 if (s.status() != QDataStream::Ok)
1261 return QImage();
1262 }
1263 }
1264
1265 return image;
1266}
1267
1268static QImage readPalette8Image(QDataStream &s, const DDSHeader &dds, quint32 width, quint32 height)
1269{
1270 QImage image = imageAlloc(width, height, format: QImage::Format_Indexed8);
1271 if (image.isNull()) {
1272 return image;
1273 }
1274
1275 for (int i = 0; i < 256; ++i) {
1276 quint8 r, g, b, a;
1277 s >> r >> g >> b >> a;
1278 image.setColor(i, c: qRgba(r, g, b, a));
1279 }
1280
1281 for (quint32 y = 0; y < height; y++) {
1282 quint8 *scanLine = reinterpret_cast<quint8 *>(image.scanLine(y));
1283 for (quint32 x = 0; x < width; x++) {
1284 quint32 value = readValue(s, size: dds.pixelFormat.rgbBitCount);
1285 if (s.status() != QDataStream::Ok)
1286 return QImage();
1287 scanLine[x] = (value & 0xff); // any alpha channel discarded
1288 }
1289 }
1290
1291 return image;
1292}
1293
1294static QImage readPalette4Image(QDataStream &s, quint32 width, quint32 height)
1295{
1296 QImage image = imageAlloc(width, height, format: QImage::Format_Indexed8);
1297 if (image.isNull()) {
1298 return image;
1299 }
1300
1301 for (int i = 0; i < 16; ++i) {
1302 quint8 r, g, b, a;
1303 s >> r >> g >> b >> a;
1304 image.setColor(i, c: qRgba(r, g, b, a));
1305 }
1306
1307 for (quint32 y = 0; y < height; y++) {
1308 quint8 index;
1309 for (quint32 x = 0; x < width - 1; ) {
1310 s >> index;
1311 image.setPixel(x: x++, y, index_or_rgb: (index & 0x0f) >> 0);
1312 image.setPixel(x: x++, y, index_or_rgb: (index & 0xf0) >> 4);
1313 if (s.status() != QDataStream::Ok)
1314 return QImage();
1315 }
1316 if (width % 2 == 1) {
1317 s >> index;
1318 image.setPixel(x: width - 1, y, index_or_rgb: (index & 0x0f) >> 0);
1319 if (s.status() != QDataStream::Ok)
1320 return QImage();
1321 }
1322 }
1323
1324 return image;
1325}
1326
1327static QImage readARGB16(QDataStream &s, quint32 width, quint32 height)
1328{
1329 QImage image = imageAlloc(width, height, format: QImage::Format_ARGB32);
1330 if (image.isNull()) {
1331 return image;
1332 }
1333
1334 for (quint32 y = 0; y < height; y++) {
1335 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1336 for (quint32 x = 0; x < width; x++) {
1337 quint8 colors[ColorCount];
1338 for (int i = 0; i < ColorCount; ++i) {
1339 quint16 color;
1340 s >> color;
1341 colors[i] = quint8(color >> 8);
1342 }
1343 line[x] = qRgba(r: colors[Red], g: colors[Green], b: colors[Blue], a: colors[Alpha]);
1344 if (s.status() != QDataStream::Ok)
1345 return QImage();
1346 }
1347 }
1348
1349 return image;
1350}
1351
1352static QImage readV8U8(QDataStream &s, quint32 width, quint32 height)
1353{
1354 QImage image = imageAlloc(width, height, format: QImage::Format_RGB32);
1355 if (image.isNull()) {
1356 return image;
1357 }
1358
1359 for (quint32 y = 0; y < height; y++) {
1360 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1361 for (quint32 x = 0; x < width; x++) {
1362 qint8 v, u;
1363 s >> v >> u;
1364 line[x] = qRgb(r: v + 128, g: u + 128, b: 255);
1365 if (s.status() != QDataStream::Ok)
1366 return QImage();
1367 }
1368 }
1369
1370 return image;
1371}
1372
1373static QImage readL6V5U5(QDataStream &s, quint32 width, quint32 height)
1374{
1375 QImage image = imageAlloc(width, height, format: QImage::Format_ARGB32);
1376 if (image.isNull()) {
1377 return image;
1378 }
1379
1380 quint16 tmp;
1381 for (quint32 y = 0; y < height; y++) {
1382 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1383 for (quint32 x = 0; x < width; x++) {
1384 s >> tmp;
1385 quint8 r = qint8((tmp & 0x001f) >> 0) * 0xff/0x1f + 128;
1386 quint8 g = qint8((tmp & 0x03e0) >> 5) * 0xff/0x1f + 128;
1387 quint8 b = quint8((tmp & 0xfc00) >> 10) * 0xff/0x3f;
1388 line[x] = qRgba(r, g, b: 0xff, a: b);
1389 if (s.status() != QDataStream::Ok)
1390 return QImage();
1391 }
1392 }
1393 return image;
1394}
1395
1396static QImage readX8L8V8U8(QDataStream &s, quint32 width, quint32 height)
1397{
1398 QImage image = imageAlloc(width, height, format: QImage::Format_ARGB32);
1399 if (image.isNull()) {
1400 return image;
1401 }
1402
1403 quint8 a, l;
1404 qint8 v, u;
1405 for (quint32 y = 0; y < height; y++) {
1406 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1407 for (quint32 x = 0; x < width; x++) {
1408 s >> v >> u >> a >> l;
1409 line[x] = qRgba(r: v + 128, g: u + 128, b: 255, a);
1410 if (s.status() != QDataStream::Ok)
1411 return QImage();
1412 }
1413 }
1414
1415 return image;
1416}
1417
1418static QImage readQ8W8V8U8(QDataStream &s, quint32 width, quint32 height)
1419{
1420 QImage image = imageAlloc(width, height, format: QImage::Format_ARGB32);
1421 if (image.isNull()) {
1422 return image;
1423 }
1424
1425 quint8 colors[ColorCount];
1426 qint8 tmp;
1427 for (quint32 y = 0; y < height; y++) {
1428 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1429 for (quint32 x = 0; x < width; x++) {
1430 for (int i = 0; i < ColorCount; i++) {
1431 s >> tmp;
1432 colors[i] = tmp + 128;
1433 }
1434 line[x] = qRgba(r: colors[Red], g: colors[Green], b: colors[Blue], a: colors[Alpha]);
1435 if (s.status() != QDataStream::Ok)
1436 return QImage();
1437 }
1438 }
1439
1440 return image;
1441}
1442
1443static QImage readV16U16(QDataStream &s, quint32 width, quint32 height)
1444{
1445 QImage image = imageAlloc(width, height, format: QImage::Format_RGB32);
1446 if (image.isNull()) {
1447 return image;
1448 }
1449
1450 for (quint32 y = 0; y < height; y++) {
1451 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1452 for (quint32 x = 0; x < width; x++) {
1453 qint16 v, u;
1454 s >> v >> u;
1455 v = (v + 0x8000) >> 8;
1456 u = (u + 0x8000) >> 8;
1457 line[x] = qRgb(r: v, g: u, b: 255);
1458 if (s.status() != QDataStream::Ok)
1459 return QImage();
1460 }
1461 }
1462
1463 return image;
1464}
1465
1466static QImage readA2W10V10U10(QDataStream &s, quint32 width, quint32 height)
1467{
1468 QImage image = imageAlloc(width, height, format: QImage::Format_ARGB32);
1469 if (image.isNull()) {
1470 return image;
1471 }
1472
1473 quint32 tmp;
1474 for (quint32 y = 0; y < height; y++) {
1475 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1476 for (quint32 x = 0; x < width; x++) {
1477 s >> tmp;
1478 quint8 r = qint8((tmp & 0x3ff00000) >> 20 >> 2) + 128;
1479 quint8 g = qint8((tmp & 0x000ffc00) >> 10 >> 2) + 128;
1480 quint8 b = qint8((tmp & 0x000003ff) >> 0 >> 2) + 128;
1481 quint8 a = 0xff * ((tmp & 0xc0000000) >> 30) / 3;
1482 // dunno why we should swap b and r here
1483 std::swap(a&: b, b&: r);
1484 line[x] = qRgba(r, g, b, a);
1485 if (s.status() != QDataStream::Ok)
1486 return QImage();
1487 }
1488 }
1489
1490 return image;
1491}
1492
1493static QImage readUYVY(QDataStream &s, quint32 width, quint32 height)
1494{
1495 QImage image = imageAlloc(width, height, format: QImage::Format_RGB32);
1496 if (image.isNull()) {
1497 return image;
1498 }
1499
1500 quint8 uyvy[4];
1501 for (quint32 y = 0; y < height; y++) {
1502 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1503 for (quint32 x = 0; x < width - 1; ) {
1504 s >> uyvy[0] >> uyvy[1] >> uyvy[2] >> uyvy[3];
1505 line[x++] = yuv2rgb(Y: uyvy[1], U: uyvy[0], V: uyvy[2]);
1506 line[x++] = yuv2rgb(Y: uyvy[3], U: uyvy[0], V: uyvy[2]);
1507 if (s.status() != QDataStream::Ok)
1508 return QImage();
1509 }
1510 if (width % 2 == 1) {
1511 s >> uyvy[0] >> uyvy[1] >> uyvy[2] >> uyvy[3];
1512 line[width - 1] = yuv2rgb(Y: uyvy[1], U: uyvy[0], V: uyvy[2]);
1513 if (s.status() != QDataStream::Ok)
1514 return QImage();
1515 }
1516 }
1517
1518 return image;
1519}
1520
1521static QImage readR8G8B8G8(QDataStream &s, quint32 width, quint32 height)
1522{
1523 QImage image = imageAlloc(width, height, format: QImage::Format_RGB32);
1524 if (image.isNull()) {
1525 return image;
1526 }
1527
1528 quint8 rgbg[4];
1529 for (quint32 y = 0; y < height; y++) {
1530 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1531 for (quint32 x = 0; x < width - 1; ) {
1532 s >> rgbg[1] >> rgbg[0] >> rgbg[3] >> rgbg[2];
1533 line[x++] = qRgb(r: rgbg[0], g: rgbg[1], b: rgbg[2]);
1534 line[x++] = qRgb(r: rgbg[0], g: rgbg[3], b: rgbg[2]);
1535 if (s.status() != QDataStream::Ok)
1536 return QImage();
1537 }
1538 if (width % 2 == 1) {
1539 s >> rgbg[1] >> rgbg[0] >> rgbg[3] >> rgbg[2];
1540 line[width - 1] = qRgb(r: rgbg[0], g: rgbg[1], b: rgbg[2]);
1541 if (s.status() != QDataStream::Ok)
1542 return QImage();
1543 }
1544 }
1545
1546 return image;
1547}
1548
1549static QImage readYUY2(QDataStream &s, quint32 width, quint32 height)
1550{
1551 QImage image = imageAlloc(width, height, format: QImage::Format_RGB32);
1552 if (image.isNull()) {
1553 return image;
1554 }
1555
1556 quint8 yuyv[4];
1557 for (quint32 y = 0; y < height; y++) {
1558 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1559 for (quint32 x = 0; x < width - 1; ) {
1560 s >> yuyv[0] >> yuyv[1] >> yuyv[2] >> yuyv[3];
1561 line[x++] = yuv2rgb(Y: yuyv[0], U: yuyv[1], V: yuyv[3]);
1562 line[x++] = yuv2rgb(Y: yuyv[2], U: yuyv[1], V: yuyv[3]);
1563 if (s.status() != QDataStream::Ok)
1564 return QImage();
1565 }
1566 if (width % 2 == 1) {
1567 s >> yuyv[0] >> yuyv[1] >> yuyv[2] >> yuyv[3];
1568 line[width - 1] = yuv2rgb(Y: yuyv[2], U: yuyv[1], V: yuyv[3]);
1569 if (s.status() != QDataStream::Ok)
1570 return QImage();
1571 }
1572 }
1573
1574 return image;
1575}
1576
1577static QImage readG8R8G8B8(QDataStream &s, quint32 width, quint32 height)
1578{
1579 QImage image = imageAlloc(width, height, format: QImage::Format_RGB32);
1580 if (image.isNull()) {
1581 return image;
1582 }
1583
1584 quint8 grgb[4];
1585 for (quint32 y = 0; y < height; y++) {
1586 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1587 for (quint32 x = 0; x < width - 1; ) {
1588 s >> grgb[1] >> grgb[0] >> grgb[3] >> grgb[2];
1589 line[x++] = qRgb(r: grgb[1], g: grgb[0], b: grgb[3]);
1590 line[x++] = qRgb(r: grgb[1], g: grgb[2], b: grgb[3]);
1591 if (s.status() != QDataStream::Ok)
1592 return QImage();
1593 }
1594 if (width % 2 == 1) {
1595 s >> grgb[1] >> grgb[0] >> grgb[3] >> grgb[2];
1596 line[width - 1] = qRgb(r: grgb[1], g: grgb[0], b: grgb[3]);
1597 if (s.status() != QDataStream::Ok)
1598 return QImage();
1599 }
1600 }
1601
1602 return image;
1603}
1604
1605static QImage readA2R10G10B10(QDataStream &s, const DDSHeader &dds, quint32 width, quint32 height)
1606{
1607 QImage image = readUnsignedImage(s, dds, width, height, hasAlpha: true);
1608 if (image.isNull()) {
1609 return image;
1610 }
1611
1612 for (quint32 y = 0; y < height; y++) {
1613 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1614 for (quint32 x = 0; x < width; x++) {
1615 QRgb pixel = image.pixel(x, y);
1616 line[x] = qRgba(r: qBlue(rgb: pixel), g: qGreen(rgb: pixel), b: qRed(rgb: pixel), a: qAlpha(rgb: pixel));
1617 if (s.status() != QDataStream::Ok)
1618 return QImage();
1619 }
1620 }
1621 return image;
1622}
1623
1624static QImage readLayer(QDataStream &s, const DDSHeader &dds, const int format, quint32 width, quint32 height)
1625{
1626 if (width * height == 0)
1627 return QImage();
1628
1629 bool alphaPremul = dds.header10.miscFlags2 == DXGIAlphaModePremultiplied;
1630
1631 switch (format) {
1632 case FormatR8G8B8:
1633 case FormatX8R8G8B8:
1634 case FormatR5G6B5:
1635 case FormatR3G3B2:
1636 case FormatX1R5G5B5:
1637 case FormatX4R4G4B4:
1638 case FormatX8B8G8R8:
1639 case FormatG16R16:
1640 case FormatL8:
1641 case FormatL16:
1642 return readUnsignedImage(s, dds, width, height, hasAlpha: false);
1643 case FormatA8R8G8B8:
1644 case FormatA1R5G5B5:
1645 case FormatA4R4G4B4:
1646 case FormatA8:
1647 case FormatA8R3G3B2:
1648 case FormatA8B8G8R8:
1649 case FormatA8L8:
1650 case FormatA4L4:
1651 return readUnsignedImage(s, dds, width, height, hasAlpha: true);
1652 case FormatA2R10G10B10:
1653 case FormatA2B10G10R10:
1654 return readA2R10G10B10(s, dds, width, height);
1655 case FormatP8:
1656 case FormatA8P8:
1657 return readPalette8Image(s, dds, width, height);
1658 case FormatP4:
1659 case FormatA4P4:
1660 return readPalette4Image(s, width, height);
1661 case FormatA16B16G16R16:
1662 return readARGB16(s, width, height);
1663 case FormatV8U8:
1664 return readV8U8(s, width, height);
1665 case FormatL6V5U5:
1666 return readL6V5U5(s, width, height);
1667 case FormatX8L8V8U8:
1668 return readX8L8V8U8(s, width, height);
1669 case FormatQ8W8V8U8:
1670 return readQ8W8V8U8(s, width, height);
1671 case FormatV16U16:
1672 return readV16U16(s, width, height);
1673 case FormatA2W10V10U10:
1674 return readA2W10V10U10(s, width, height);
1675 case FormatUYVY:
1676 return readUYVY(s, width, height);
1677 case FormatR8G8B8G8:
1678 return readR8G8B8G8(s, width, height);
1679 case FormatYUY2:
1680 return readYUY2(s, width, height);
1681 case FormatG8R8G8B8:
1682 return readG8R8G8B8(s, width, height);
1683 case FormatDXT1:
1684 return readDXT1(s, width, height);
1685 case FormatDXT2:
1686 return readDXT2(s, width, height);
1687 case FormatDXT3:
1688 return readDXT3(s, width, height);
1689 case FormatDXT4:
1690 return readDXT4(s, width, height);
1691 case FormatDXT5:
1692 return readDXT5(s, width, height);
1693 case FormatRXGB:
1694 return readRXGB(s, width, height);
1695 case FormatATI2:
1696 return readATI2(s, width, height);
1697 case FormatR16F:
1698 return readR16F(s, width, height);
1699 case FormatG16R16F:
1700 return readRG16F(s, width, height);
1701 case FormatA16B16G16R16F:
1702 return readARGB16F(s, width, height, alphaPremul);
1703 case FormatR32F:
1704 return readR32F(s, width, height);
1705 case FormatG32R32F:
1706 return readRG32F(s, width, height);
1707 case FormatA32B32G32R32F:
1708 return readARGB32F(s, width, height, alphaPremul);
1709 case FormatD16Lockable:
1710 case FormatD32:
1711 case FormatD15S1:
1712 case FormatD24S8:
1713 case FormatD24X8:
1714 case FormatD24X4S4:
1715 case FormatD16:
1716 case FormatD32FLockable:
1717 case FormatD24FS8:
1718 case FormatD32Lockable:
1719 case FormatS8Lockable:
1720 case FormatVertexData:
1721 case FormatIndex16:
1722 case FormatIndex32:
1723 break;
1724 case FormatQ16W16V16U16:
1725 return readQ16W16V16U16(s, width, height);
1726 case FormatMulti2ARGB8:
1727 break;
1728 case FormatCxV8U8:
1729 return readCxV8U8(s, width, height);
1730 case FormatA1:
1731 case FormatA2B10G10R10_XR_BIAS:
1732 case FormatBinaryBuffer:
1733 case FormatLast:
1734 break;
1735 }
1736
1737 return QImage();
1738}
1739
1740static inline QImage readTexture(QDataStream &s, const DDSHeader &dds, const int format, const int mipmapLevel)
1741{
1742 quint32 width = dds.width / (1 << mipmapLevel);
1743 quint32 height = dds.height / (1 << mipmapLevel);
1744 return readLayer(s, dds, format, width, height);
1745}
1746
1747static qint64 mipmapSize(const DDSHeader &dds, const int format, const int level)
1748{
1749 quint32 w = dds.width/(1 << level);
1750 quint32 h = dds.height/(1 << level);
1751
1752 switch (format) {
1753 case FormatR8G8B8:
1754 case FormatX8R8G8B8:
1755 case FormatR5G6B5:
1756 case FormatX1R5G5B5:
1757 case FormatX4R4G4B4:
1758 case FormatX8B8G8R8:
1759 case FormatG16R16:
1760 case FormatL8:
1761 case FormatL16:
1762 return w * h * dds.pixelFormat.rgbBitCount / 8;
1763 case FormatA8R8G8B8:
1764 case FormatA1R5G5B5:
1765 case FormatA4R4G4B4:
1766 case FormatA8:
1767 case FormatA8R3G3B2:
1768 case FormatA2B10G10R10:
1769 case FormatA8B8G8R8:
1770 case FormatA2R10G10B10:
1771 case FormatA8L8:
1772 case FormatA4L4:
1773 return w * h * dds.pixelFormat.rgbBitCount / 8;
1774 case FormatP8:
1775 return 256 + w * h * 8;
1776 case FormatA16B16G16R16:
1777 return w * h * 4 * 2;
1778 case FormatA8P8:
1779 break;
1780 case FormatV8U8:
1781 case FormatL6V5U5:
1782 return w * h * 2;
1783 case FormatX8L8V8U8:
1784 case FormatQ8W8V8U8:
1785 case FormatV16U16:
1786 case FormatA2W10V10U10:
1787 return w * h * 4;
1788 case FormatUYVY:
1789 case FormatR8G8B8G8:
1790 case FormatYUY2:
1791 case FormatG8R8G8B8:
1792 return w * h * 2;
1793 case FormatDXT1:
1794 return ((w + 3)/4) * ((h + 3)/4) * 8;
1795 case FormatDXT2:
1796 case FormatDXT3:
1797 case FormatDXT4:
1798 case FormatDXT5:
1799 return ((w + 3)/4) * ((h + 3)/4) * 16;
1800 case FormatD16Lockable:
1801 case FormatD32:
1802 case FormatD15S1:
1803 case FormatD24S8:
1804 case FormatD24X8:
1805 case FormatD24X4S4:
1806 case FormatD16:
1807 case FormatD32FLockable:
1808 case FormatD24FS8:
1809 case FormatD32Lockable:
1810 case FormatS8Lockable:
1811 case FormatVertexData:
1812 case FormatIndex16:
1813 case FormatIndex32:
1814 break;
1815 case FormatQ16W16V16U16:
1816 return w * h * 4 * 2;
1817 case FormatMulti2ARGB8:
1818 break;
1819 case FormatR16F:
1820 return w * h * 1 * 2;
1821 case FormatG16R16F:
1822 return w * h * 2 * 2;
1823 case FormatA16B16G16R16F:
1824 return w * h * 4 * 2;
1825 case FormatR32F:
1826 return w * h * 1 * 4;
1827 case FormatG32R32F:
1828 return w * h * 2 * 4;
1829 case FormatA32B32G32R32F:
1830 return w * h * 4 * 4;
1831 case FormatCxV8U8:
1832 return w * h * 2;
1833 case FormatA1:
1834 case FormatA2B10G10R10_XR_BIAS:
1835 case FormatBinaryBuffer:
1836 case FormatLast:
1837 break;
1838 }
1839
1840 return 0;
1841}
1842
1843static qint64 mipmapOffset(const DDSHeader &dds, const int format, const int level)
1844{
1845 qint64 result = 0;
1846 for (int i = 0; i < level; ++i)
1847 result += mipmapSize(dds, format, level: i);
1848 return result;
1849}
1850
1851static QImage readCubeMap(QDataStream &s, const DDSHeader &dds, const int fmt)
1852{
1853 QImage::Format format = hasAlpha(dds) ? QImage::Format_ARGB32 : QImage::Format_RGB32;
1854 QImage image = imageAlloc(width: 4 * dds.width, height: 3 * dds.height, format);
1855 if (image.isNull()) {
1856 return image;
1857 }
1858
1859 image.fill(pixel: 0);
1860
1861 for (int i = 0; i < 6; i++) {
1862 if (!(dds.caps2 & faceFlags[i]))
1863 continue; // Skip face.
1864
1865 QImage face = readLayer(s, dds, format: fmt, width: dds.width, height: dds.height);
1866 if (face.isNull()) {
1867 return {};
1868 }
1869 face.convertTo(f: format);
1870 if (face.isNull()) {
1871 return {};
1872 }
1873 if (face.colorSpace().isValid()) {
1874 image.setColorSpace(face.colorSpace());
1875 }
1876
1877 // Compute face offsets.
1878 int offset_x = faceOffsets[i].x * dds.width;
1879 int offset_y = faceOffsets[i].y * dds.height;
1880
1881 // Copy face on the image.
1882 for (quint32 y = 0; y < dds.height; y++) {
1883 if (y + offset_y >= quint32(image.height())) {
1884 return {};
1885 }
1886 const QRgb *src = reinterpret_cast<const QRgb *>(face.constScanLine(y));
1887 QRgb *dst = reinterpret_cast<QRgb *>(image.scanLine(y + offset_y)) + offset_x;
1888 qsizetype sz = sizeof(QRgb) * dds.width;
1889 if (ptrDiff(end: face.bits() + face.sizeInBytes(), start: src) < sz || ptrDiff(end: image.bits() + image.sizeInBytes(), start: dst) < sz) {
1890 return {};
1891 }
1892 memcpy(dest: dst, src: src, n: sz);
1893 }
1894 }
1895
1896 return image;
1897}
1898
1899static QByteArray formatName(int format)
1900{
1901 for (size_t i = 0; i < formatNamesSize; ++i) {
1902 if (formatNames[i].format == format)
1903 return formatNames[i].name;
1904 }
1905
1906 return formatNames[0].name;
1907}
1908
1909static int formatByName(const QByteArray &name)
1910{
1911 const QByteArray loweredName = name.toLower();
1912 for (size_t i = 0; i < formatNamesSize; ++i) {
1913 if (QByteArray(formatNames[i].name).toLower() == loweredName)
1914 return formatNames[i].format;
1915 }
1916
1917 return FormatUnknown;
1918}
1919
1920QDDSHandler::QDDSHandler() :
1921 m_header(),
1922 m_format(FormatUnknown),
1923 m_currentImage(0),
1924 m_scanState(ScanNotScanned)
1925{
1926}
1927
1928bool QDDSHandler::canRead() const
1929{
1930 if (m_scanState == ScanNotScanned && !canRead(device: device()))
1931 return false;
1932
1933 if (m_scanState != ScanError) {
1934 setFormat(QByteArrayLiteral("dds"));
1935 return true;
1936 }
1937
1938 return false;
1939}
1940
1941bool QDDSHandler::read(QImage *outImage)
1942{
1943 if (!ensureScanned() || device()->isSequential())
1944 return false;
1945
1946 qint64 pos = headerSize;
1947 if (m_header.pixelFormat.fourCC == dx10Magic)
1948 pos += 20;
1949 pos += mipmapOffset(dds: m_header, format: m_format, level: m_currentImage);
1950 if (!device()->seek(pos))
1951 return false;
1952 QDataStream s(device());
1953 s.setByteOrder(QDataStream::LittleEndian);
1954
1955 QImage image = isCubeMap(dds: m_header) ?
1956 readCubeMap(s, dds: m_header, fmt: m_format) :
1957 readTexture(s, dds: m_header, format: m_format, mipmapLevel: m_currentImage);
1958 if (image.isNull()) {
1959 return false;
1960 }
1961
1962 bool ok = s.status() == QDataStream::Ok && !image.isNull();
1963 if (ok)
1964 *outImage = image;
1965 return ok;
1966}
1967
1968bool writeA8R8G8B8(const QImage &outImage, QDataStream &s)
1969{
1970 DDSHeader dds;
1971 // Filling header
1972 dds.magic = ddsMagic;
1973 dds.size = 124;
1974 dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
1975 DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat |
1976 DDSHeader::FlagPitch;
1977 dds.height = outImage.height();
1978 dds.width = outImage.width();
1979 dds.pitchOrLinearSize = dds.width * 32 / 8;
1980 dds.depth = 0;
1981 dds.mipMapCount = 0;
1982 for (int i = 0; i < DDSHeader::ReservedCount; i++)
1983 dds.reserved1[i] = 0;
1984 dds.caps = DDSHeader::CapsTexture;
1985 dds.caps2 = 0;
1986 dds.caps3 = 0;
1987 dds.caps4 = 0;
1988 dds.reserved2 = 0;
1989
1990 // Filling pixelformat
1991 dds.pixelFormat.size = 32;
1992 dds.pixelFormat.flags = DDSPixelFormat::FlagAlphaPixels | DDSPixelFormat::FlagRGB;
1993 dds.pixelFormat.fourCC = 0;
1994 dds.pixelFormat.rgbBitCount = 32;
1995 dds.pixelFormat.aBitMask = 0xff000000;
1996 dds.pixelFormat.rBitMask = 0x00ff0000;
1997 dds.pixelFormat.gBitMask = 0x0000ff00;
1998 dds.pixelFormat.bBitMask = 0x000000ff;
1999
2000 s << dds;
2001 if (s.status() != QDataStream::Ok) {
2002 return false;
2003 }
2004
2005 ScanLineConverter slc(QImage::Format_ARGB32);
2006 if(outImage.colorSpace().isValid())
2007 slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgb));
2008
2009 for (int y = 0, h = outImage.height(); y < h; ++y) {
2010 const QRgb *scanLine = reinterpret_cast<const QRgb*>(slc.convertedScanLine(image: outImage, y));
2011 if (scanLine == nullptr) {
2012 return false;
2013 }
2014 for (int x = 0, w = outImage.width(); x < w; ++x) {
2015 s << quint32(scanLine[x]);
2016 }
2017 if (s.status() != QDataStream::Ok) {
2018 return false;
2019 }
2020 }
2021
2022 return true;
2023}
2024
2025bool writeR8G8B8(const QImage &outImage, QDataStream &s)
2026{
2027 DDSHeader dds;
2028 // Filling header
2029 dds.magic = ddsMagic;
2030 dds.size = 124;
2031 dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
2032 DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat |
2033 DDSHeader::FlagPitch;
2034 dds.height = outImage.height();
2035 dds.width = outImage.width();
2036 dds.pitchOrLinearSize = dds.width * 24 / 8;
2037 dds.depth = 1;
2038 dds.mipMapCount = 0;
2039 for (int i = 0; i < DDSHeader::ReservedCount; i++)
2040 dds.reserved1[i] = 0;
2041 dds.caps = DDSHeader::CapsTexture;
2042 dds.caps2 = 0;
2043 dds.caps3 = 0;
2044 dds.caps4 = 0;
2045 dds.reserved2 = 0;
2046
2047 // Filling pixelformat
2048 dds.pixelFormat.size = 32;
2049 dds.pixelFormat.flags = DDSPixelFormat::FlagRGB;
2050 dds.pixelFormat.fourCC = 0;
2051 dds.pixelFormat.rgbBitCount = 24;
2052 dds.pixelFormat.aBitMask = 0x00000000;
2053 dds.pixelFormat.rBitMask = 0x00ff0000;
2054 dds.pixelFormat.gBitMask = 0x0000ff00;
2055 dds.pixelFormat.bBitMask = 0x000000ff;
2056
2057 s << dds;
2058 if (s.status() != QDataStream::Ok) {
2059 return false;
2060 }
2061
2062 ScanLineConverter slc(QImage::Format_RGB888);
2063 if(outImage.colorSpace().isValid())
2064 slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgb));
2065
2066 for (int y = 0, h = outImage.height(); y < h; ++y) {
2067 const quint8 *scanLine = reinterpret_cast<const quint8*>(slc.convertedScanLine(image: outImage, y));
2068 if (scanLine == nullptr) {
2069 return false;
2070 }
2071 for (int x = 0, w = outImage.width(); x < w; ++x) {
2072 size_t x3 = size_t(x) * 3;
2073 s << scanLine[x3 + 2];
2074 s << scanLine[x3 + 1];
2075 s << scanLine[x3];
2076 }
2077 if (s.status() != QDataStream::Ok) {
2078 return false;
2079 }
2080 }
2081
2082 return true;
2083}
2084
2085bool writeL8(const QImage &outImage, QDataStream &s)
2086{
2087 DDSHeader dds;
2088 // Filling header
2089 dds.magic = ddsMagic;
2090 dds.size = 124;
2091 dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
2092 DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat |
2093 DDSHeader::FlagPitch;
2094 dds.height = outImage.height();
2095 dds.width = outImage.width();
2096 dds.pitchOrLinearSize = dds.width;
2097 dds.depth = 1;
2098 dds.mipMapCount = 0;
2099 for (int i = 0; i < DDSHeader::ReservedCount; i++)
2100 dds.reserved1[i] = 0;
2101 dds.caps = DDSHeader::CapsTexture;
2102 dds.caps2 = 0;
2103 dds.caps3 = 0;
2104 dds.caps4 = 0;
2105 dds.reserved2 = 0;
2106
2107 // Filling pixelformat
2108 dds.pixelFormat.size = 32;
2109 dds.pixelFormat.flags = DDSPixelFormat::FlagLuminance | DDSPixelFormat::FlagRGB;
2110 dds.pixelFormat.fourCC = 0;
2111 dds.pixelFormat.rgbBitCount = 8;
2112 dds.pixelFormat.aBitMask = 0x00000000;
2113 dds.pixelFormat.rBitMask = 0x000000ff;
2114 dds.pixelFormat.gBitMask = 0x00000000;
2115 dds.pixelFormat.bBitMask = 0x00000000;
2116
2117 s << dds;
2118 if (s.status() != QDataStream::Ok) {
2119 return false;
2120 }
2121
2122 ScanLineConverter slc(QImage::Format_Grayscale8);
2123 if(outImage.colorSpace().isValid())
2124 slc.setTargetColorSpace(QColorSpace(QPointF(0.3127, 0.3291), QColorSpace::TransferFunction::SRgb));
2125
2126 for (int y = 0, h = outImage.height(); y < h; ++y) {
2127 const quint8 *scanLine = reinterpret_cast<const quint8*>(slc.convertedScanLine(image: outImage, y));
2128 if (scanLine == nullptr) {
2129 return false;
2130 }
2131 for (int x = 0, w = outImage.width(); x < w; ++x) {
2132 s << scanLine[x];
2133 }
2134 if (s.status() != QDataStream::Ok) {
2135 return false;
2136 }
2137 }
2138
2139 return true;
2140}
2141
2142bool writeP8(const QImage &image, QDataStream &s)
2143{
2144 QImage outImage = image;
2145 // indexed not supported by ScanlineConverter class
2146 if (image.format() != QImage::Format_Indexed8) {
2147 if (image.colorSpace().isValid())
2148 outImage.convertToColorSpace(colorSpace: QColorSpace(QColorSpace::SRgb));
2149 outImage = outImage.convertToFormat(f: QImage::Format_Indexed8);
2150 }
2151
2152 DDSHeader dds;
2153 // Filling header
2154 dds.magic = ddsMagic;
2155 dds.size = 124;
2156 dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
2157 DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat |
2158 DDSHeader::FlagPitch;
2159 dds.height = outImage.height();
2160 dds.width = outImage.width();
2161 dds.pitchOrLinearSize = dds.width;
2162 dds.depth = 1;
2163 dds.mipMapCount = 0;
2164 for (int i = 0; i < DDSHeader::ReservedCount; i++)
2165 dds.reserved1[i] = 0;
2166 dds.caps = DDSHeader::CapsTexture;
2167 dds.caps2 = 0;
2168 dds.caps3 = 0;
2169 dds.caps4 = 0;
2170 dds.reserved2 = 0;
2171
2172 // Filling pixelformat
2173 dds.pixelFormat.size = 32;
2174 dds.pixelFormat.flags = DDSPixelFormat::FlagPaletteIndexed8;
2175 dds.pixelFormat.fourCC = 0;
2176 dds.pixelFormat.rgbBitCount = 8;
2177 dds.pixelFormat.aBitMask = 0x00000000;
2178 dds.pixelFormat.rBitMask = 0x00000000;
2179 dds.pixelFormat.gBitMask = 0x00000000;
2180 dds.pixelFormat.bBitMask = 0x00000000;
2181
2182 s << dds;
2183
2184 QList<QRgb> palette = outImage.colorTable();
2185 for (int i = 0; i < 256; ++i) {
2186 quint8 r = 0, g = 0, b = 0, a = 0xff;
2187 if (i < palette.size()) {
2188 auto&& rgba = palette.at(i);
2189 r = qRed(rgb: rgba);
2190 g = qGreen(rgb: rgba);
2191 b = qBlue(rgb: rgba);
2192 a = qAlpha(rgb: rgba);
2193 }
2194 s << r;
2195 s << g;
2196 s << b;
2197 s << a;
2198 }
2199
2200 if (s.status() != QDataStream::Ok) {
2201 return false;
2202 }
2203
2204 for (int y = 0, h = outImage.height(); y < h; ++y) {
2205 const quint8 *scanLine = reinterpret_cast<const quint8*>(outImage.constScanLine(y));
2206 if (scanLine == nullptr) {
2207 return false;
2208 }
2209 for (int x = 0, w = outImage.width(); x < w; ++x) {
2210 s << scanLine[x];
2211 }
2212 if (s.status() != QDataStream::Ok) {
2213 return false;
2214 }
2215 }
2216
2217 return true;
2218}
2219
2220bool writeA16B16G16R16F(const QImage &outImage, QDataStream &s)
2221{
2222 DDSHeader dds;
2223 // Filling header
2224 dds.magic = ddsMagic;
2225 dds.size = 124;
2226 dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
2227 DDSHeader::FlagWidth | DDSHeader::FlagPitch |
2228 DDSHeader::FlagPixelFormat;
2229 dds.height = outImage.height();
2230 dds.width = outImage.width();
2231 dds.pitchOrLinearSize = dds.width * 64 / 8;
2232 dds.depth = 1;
2233 dds.mipMapCount = 0;
2234 for (int i = 0; i < DDSHeader::ReservedCount; i++)
2235 dds.reserved1[i] = 0;
2236 dds.caps = DDSHeader::CapsTexture;
2237 dds.caps2 = 0;
2238 dds.caps3 = 0;
2239 dds.caps4 = 0;
2240 dds.reserved2 = 0;
2241
2242 // Filling pixelformat
2243 dds.pixelFormat.size = 32;
2244 dds.pixelFormat.flags = DDSPixelFormat::FlagFourCC;
2245 dds.pixelFormat.fourCC = 113;
2246 dds.pixelFormat.rgbBitCount = 0;
2247 dds.pixelFormat.aBitMask = 0;
2248 dds.pixelFormat.rBitMask = 0;
2249 dds.pixelFormat.gBitMask = 0;
2250 dds.pixelFormat.bBitMask = 0;
2251
2252 s << dds;
2253 if (s.status() != QDataStream::Ok) {
2254 return false;
2255 }
2256
2257 ScanLineConverter slc(QImage::Format_RGBA16FPx4);
2258 if(outImage.colorSpace().isValid())
2259 slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgbLinear));
2260
2261 for (int y = 0, h = outImage.height(); y < h; ++y) {
2262 const quint16 *scanLine = reinterpret_cast<const quint16*>(slc.convertedScanLine(image: outImage, y));
2263 if (scanLine == nullptr) {
2264 return false;
2265 }
2266 for (int x = 0, w = outImage.width(); x < w; ++x) {
2267 size_t x4 = size_t(x) * 4;
2268 s << scanLine[x4];
2269 s << scanLine[x4 + 1];
2270 s << scanLine[x4 + 2];
2271 s << scanLine[x4 + 3];
2272 }
2273 if (s.status() != QDataStream::Ok) {
2274 return false;
2275 }
2276 }
2277
2278 return true;
2279}
2280
2281bool writeA32B32G32R32F(const QImage &outImage, QDataStream &s)
2282{
2283 DDSHeader dds;
2284 // Filling header
2285 dds.magic = ddsMagic;
2286 dds.size = 124;
2287 dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
2288 DDSHeader::FlagWidth | DDSHeader::FlagPitch |
2289 DDSHeader::FlagPixelFormat;
2290 dds.height = outImage.height();
2291 dds.width = outImage.width();
2292 dds.pitchOrLinearSize = dds.width * 128 / 8;
2293 dds.depth = 1;
2294 dds.mipMapCount = 0;
2295 for (int i = 0; i < DDSHeader::ReservedCount; i++)
2296 dds.reserved1[i] = 0;
2297 dds.caps = DDSHeader::CapsTexture;
2298 dds.caps2 = 0;
2299 dds.caps3 = 0;
2300 dds.caps4 = 0;
2301 dds.reserved2 = 0;
2302
2303 // Filling pixelformat
2304 dds.pixelFormat.size = 32;
2305 dds.pixelFormat.flags = DDSPixelFormat::FlagFourCC;
2306 dds.pixelFormat.fourCC = 116;
2307 dds.pixelFormat.rgbBitCount = 0;
2308 dds.pixelFormat.aBitMask = 0;
2309 dds.pixelFormat.rBitMask = 0;
2310 dds.pixelFormat.gBitMask = 0;
2311 dds.pixelFormat.bBitMask = 0;
2312
2313 s << dds;
2314 if (s.status() != QDataStream::Ok) {
2315 return false;
2316 }
2317
2318 ScanLineConverter slc(QImage::Format_RGBA32FPx4);
2319 if(outImage.colorSpace().isValid())
2320 slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgbLinear));
2321
2322 for (int y = 0, h = outImage.height(); y < h; ++y) {
2323 const quint32 *scanLine = reinterpret_cast<const quint32*>(slc.convertedScanLine(image: outImage, y));
2324 if (scanLine == nullptr) {
2325 return false;
2326 }
2327 for (int x = 0, w = outImage.width(); x < w; ++x) {
2328 size_t x4 = size_t(x) * 4;
2329 s << scanLine[x4];
2330 s << scanLine[x4 + 1];
2331 s << scanLine[x4 + 2];
2332 s << scanLine[x4 + 3];
2333 }
2334 if (s.status() != QDataStream::Ok) {
2335 return false;
2336 }
2337 }
2338
2339 return true;
2340}
2341
2342bool QDDSHandler::write(const QImage &outImage)
2343{
2344 if (outImage.isNull() || device() == nullptr) {
2345 return false;
2346 }
2347
2348 QDataStream s(device());
2349 s.setByteOrder(QDataStream::LittleEndian);
2350
2351 int format = m_format;
2352 if (format == FormatUnknown) {
2353 switch (outImage.format()) {
2354 case QImage::Format_RGBX16FPx4:
2355 case QImage::Format_RGBA16FPx4:
2356 case QImage::Format_RGBA16FPx4_Premultiplied:
2357 format = FormatA16B16G16R16F;
2358 break;
2359
2360 case QImage::Format_RGBX32FPx4:
2361 case QImage::Format_RGBA32FPx4:
2362 case QImage::Format_RGBA32FPx4_Premultiplied:
2363 format = FormatA32B32G32R32F;
2364 break;
2365
2366 case QImage::Format_Grayscale16:
2367 case QImage::Format_Grayscale8:
2368 case QImage::Format_Mono:
2369 case QImage::Format_MonoLSB:
2370 format = FormatL8;
2371 break;
2372
2373 case QImage::Format_Indexed8:
2374 format = FormatP8;
2375 break;
2376
2377 default:
2378 format = outImage.hasAlphaChannel() ? FormatA8R8G8B8 : FormatR8G8B8;
2379 }
2380 }
2381
2382 if (format == FormatA8R8G8B8) {
2383 return writeA8R8G8B8(outImage, s);
2384 }
2385
2386 if (format == FormatR8G8B8) {
2387 return writeR8G8B8(outImage, s);
2388 }
2389
2390 if (format == FormatL8) {
2391 return writeL8(outImage, s);
2392 }
2393
2394 if (format == FormatP8) {
2395 return writeP8(image: outImage, s);
2396 }
2397
2398 if (format == FormatA16B16G16R16F) {
2399 return writeA16B16G16R16F(outImage, s);
2400 }
2401
2402 if (format == FormatA32B32G32R32F) {
2403 return writeA32B32G32R32F(outImage, s);
2404 }
2405
2406 qCWarning(LOG_DDSPLUGIN) << "Format" << formatName(format) << "is not supported";
2407 return false;
2408}
2409
2410QVariant QDDSHandler::option(QImageIOHandler::ImageOption option) const
2411{
2412 if (!supportsOption(option)) {
2413 return QVariant();
2414 }
2415
2416 // *** options that do not require a valid stream ***
2417 if (option == QImageIOHandler::SupportedSubTypes) {
2418 return QVariant::fromValue(value: QList<QByteArray>()
2419 << QByteArrayLiteral("Automatic")
2420 << formatName(format: FormatA8R8G8B8)
2421 << formatName(format: FormatR8G8B8)
2422 << formatName(format: FormatL8)
2423 << formatName(format: FormatP8)
2424 << formatName(format: FormatA16B16G16R16F)
2425 << formatName(format: FormatA32B32G32R32F));
2426 }
2427
2428 // *** options that require a valid stream ***
2429 if (!ensureScanned()) {
2430 return QVariant();
2431 }
2432
2433 if (option == QImageIOHandler::Size) {
2434 return isCubeMap(dds: m_header) ? QSize(m_header.width * 4, m_header.height * 3) : QSize(m_header.width, m_header.height);
2435 }
2436
2437 if (option == QImageIOHandler::SubType) {
2438 return m_format == FormatUnknown ? QByteArrayLiteral("Automatic") : formatName(format: m_format);
2439 }
2440
2441 return QVariant();
2442}
2443
2444void QDDSHandler::setOption(QImageIOHandler::ImageOption option, const QVariant &value)
2445{
2446 if (option == QImageIOHandler::SubType) {
2447 const QByteArray subType = value.toByteArray();
2448 m_format = formatByName(name: subType.toUpper());
2449 }
2450}
2451
2452bool QDDSHandler::supportsOption(QImageIOHandler::ImageOption option) const
2453{
2454 return (option == QImageIOHandler::Size)
2455 || (option == QImageIOHandler::SubType)
2456 || (option == QImageIOHandler::SupportedSubTypes);
2457}
2458
2459int QDDSHandler::imageCount() const
2460{
2461 if (!ensureScanned())
2462 return 0;
2463
2464 return qMax<quint32>(a: 1, b: m_header.mipMapCount);
2465}
2466
2467bool QDDSHandler::jumpToImage(int imageNumber)
2468{
2469 if (imageNumber >= imageCount())
2470 return false;
2471
2472 m_currentImage = imageNumber;
2473 return true;
2474}
2475
2476bool QDDSHandler::canRead(QIODevice *device)
2477{
2478 if (!device) {
2479 qCWarning(LOG_DDSPLUGIN) << "DDSHandler::canRead() called with no device";
2480 return false;
2481 }
2482
2483 if (device->isSequential())
2484 return false;
2485
2486 return device->peek(maxlen: 4) == QByteArrayLiteral("DDS ");
2487}
2488
2489bool QDDSHandler::ensureScanned() const
2490{
2491 if (device() == nullptr) {
2492 return false;
2493 }
2494
2495 if (m_scanState != ScanNotScanned)
2496 return m_scanState == ScanSuccess;
2497
2498 m_scanState = ScanError;
2499
2500 QDDSHandler *that = const_cast<QDDSHandler *>(this);
2501 that->m_format = FormatUnknown;
2502
2503 if (device()->isSequential()) {
2504 qCWarning(LOG_DDSPLUGIN) << "Sequential devices are not supported";
2505 return false;
2506 }
2507
2508 qint64 oldPos = device()->pos();
2509 device()->seek(pos: 0);
2510
2511 QDataStream s(device());
2512 s.setByteOrder(QDataStream::LittleEndian);
2513 s >> that->m_header;
2514
2515 device()->seek(pos: oldPos);
2516
2517 if (s.status() != QDataStream::Ok)
2518 return false;
2519
2520 if (!verifyHeader(dds: m_header))
2521 return false;
2522
2523 that->m_format = getFormat(dds: m_header);
2524 if (that->m_format == FormatUnknown)
2525 return false;
2526
2527 m_scanState = ScanSuccess;
2528 return true;
2529}
2530
2531bool QDDSHandler::verifyHeader(const DDSHeader &dds) const
2532{
2533 quint32 flags = dds.flags;
2534 quint32 requiredFlags = DDSHeader::FlagCaps | DDSHeader::FlagHeight
2535 | DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat;
2536 if ((flags & requiredFlags) != requiredFlags) {
2537 qCWarning(LOG_DDSPLUGIN) << "Wrong dds.flags - not all required flags present. "
2538 "Actual flags :" << flags;
2539 return false;
2540 }
2541
2542 if (dds.size != ddsSize) {
2543 qCWarning(LOG_DDSPLUGIN) << "Wrong dds.size: actual =" << dds.size
2544 << "expected =" << ddsSize;
2545 return false;
2546 }
2547
2548 if (dds.pixelFormat.size != pixelFormatSize) {
2549 qCWarning(LOG_DDSPLUGIN) << "Wrong dds.pixelFormat.size: actual =" << dds.pixelFormat.size
2550 << "expected =" << pixelFormatSize;
2551 return false;
2552 }
2553
2554 if (dds.width > DDS_MAX_IMAGE_WIDTH || dds.height > DDS_MAX_IMAGE_HEIGHT) {
2555 qCWarning(LOG_DDSPLUGIN) << "Can't read image with size bigger than" << DDS_MAX_IMAGE_WIDTH << "x" << DDS_MAX_IMAGE_HEIGHT << "pixels";
2556 return false;
2557 }
2558
2559 return true;
2560}
2561
2562QImageIOPlugin::Capabilities QDDSPlugin::capabilities(QIODevice *device, const QByteArray &format) const
2563{
2564 if (format == QByteArrayLiteral("dds"))
2565 return Capabilities(CanRead | CanWrite);
2566 if (!format.isEmpty())
2567 return {};
2568 if (!device || !device->isOpen())
2569 return {};
2570
2571 Capabilities cap;
2572 if (device->isReadable() && QDDSHandler::canRead(device))
2573 cap |= CanRead;
2574 if (device->isWritable())
2575 cap |= CanWrite;
2576 return cap;
2577}
2578
2579QImageIOHandler *QDDSPlugin::create(QIODevice *device, const QByteArray &format) const
2580{
2581 QImageIOHandler *handler = new QDDSHandler;
2582 handler->setDevice(device);
2583 handler->setFormat(format);
2584 return handler;
2585}
2586
2587#include "moc_dds_p.cpp"
2588

source code of kimageformats/src/imageformats/dds.cpp