KImageFormats

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

KDE's Doxygen guidelines are available online.