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
1035 quint16 rawData;
1036 s >> rawData;
1037 memcpy(&f16, &rawData, sizeof(rawData));
1038
1039 return f16;
1040}
1041
1042static inline float readFloat32(QDataStream &s)
1043{
1044 Q_ASSERT(sizeof(float) == 4);
1045 float value;
1046 // TODO: find better way to avoid setting precision each time
1049 s >> value;
1050 s.setFloatingPointPrecision(precision);
1051 return value;
1052}
1053
1054static QImage readR16F(QDataStream &s, const quint32 width, const quint32 height)
1055{
1056 QImage image = imageAlloc(width, height, QImage::Format_RGBX16FPx4);
1057 if (image.isNull()) {
1058 return image;
1059 }
1060
1061 for (quint32 y = 0; y < height; y++) {
1062 qfloat16 *line = reinterpret_cast<qfloat16 *>(image.scanLine(y));
1063 for (quint32 x = 0; x < width; x++) {
1064 line[x * 4] = readFloat16(s);
1065 line[x * 4 + 1] = 0;
1066 line[x * 4 + 2] = 0;
1067 line[x * 4 + 3] = 1;
1068 if (s.status() != QDataStream::Ok)
1069 return QImage();
1070 }
1071 }
1072
1074 return image;
1075}
1076
1077static QImage readRG16F(QDataStream &s, const quint32 width, const quint32 height)
1078{
1079 QImage image = imageAlloc(width, height, QImage::Format_RGBX16FPx4);
1080 if (image.isNull()) {
1081 return image;
1082 }
1083
1084 for (quint32 y = 0; y < height; y++) {
1085 qfloat16 *line = reinterpret_cast<qfloat16 *>(image.scanLine(y));
1086 for (quint32 x = 0; x < width; x++) {
1087 line[x * 4] = readFloat16(s);
1088 line[x * 4 + 1] = readFloat16(s);
1089 line[x * 4 + 2] = 0;
1090 line[x * 4 + 3] = 1;
1091 if (s.status() != QDataStream::Ok)
1092 return QImage();
1093 }
1094 }
1095
1097 return image;
1098}
1099
1100static QImage readARGB16F(QDataStream &s, const quint32 width, const quint32 height, bool alphaPremul)
1101{
1102 QImage image = imageAlloc(width, height, alphaPremul ? QImage::Format_RGBA16FPx4_Premultiplied : QImage::Format_RGBA16FPx4);
1103 if (image.isNull()) {
1104 return image;
1105 }
1106
1107 for (quint32 y = 0; y < height; y++) {
1108 qfloat16 *line = reinterpret_cast<qfloat16 *>(image.scanLine(y));
1109 for (quint32 x = 0; x < width; x++) {
1110 line[x * 4] = readFloat16(s);
1111 line[x * 4 + 1] = readFloat16(s);
1112 line[x * 4 + 2] = readFloat16(s);
1113 line[x * 4 + 3] = readFloat16(s);
1114 if (s.status() != QDataStream::Ok)
1115 return QImage();
1116 }
1117 }
1118
1120 return image;
1121}
1122
1123static QImage readR32F(QDataStream &s, const quint32 width, const quint32 height)
1124{
1125 QImage image = imageAlloc(width, height, QImage::Format_RGBX32FPx4);
1126 if (image.isNull()) {
1127 return image;
1128 }
1129
1130 for (quint32 y = 0; y < height; y++) {
1131 float *line = reinterpret_cast<float *>(image.scanLine(y));
1132 for (quint32 x = 0; x < width; x++) {
1133 line[x * 4] = readFloat32(s);
1134 line[x * 4 + 1] = 0;
1135 line[x * 4 + 2] = 0;
1136 line[x * 4 + 3] = 1;
1137 if (s.status() != QDataStream::Ok)
1138 return QImage();
1139 }
1140 }
1141
1143 return image;
1144}
1145
1146static QImage readRG32F(QDataStream &s, const quint32 width, const quint32 height)
1147{
1148 QImage image = imageAlloc(width, height, QImage::Format_RGBX32FPx4);
1149 if (image.isNull()) {
1150 return image;
1151 }
1152
1153 for (quint32 y = 0; y < height; y++) {
1154 float *line = reinterpret_cast<float *>(image.scanLine(y));
1155 for (quint32 x = 0; x < width; x++) {
1156 line[x * 4] = readFloat32(s);
1157 line[x * 4 + 1] = readFloat32(s);
1158 line[x * 4 + 2] = 0;
1159 line[x * 4 + 3] = 1;
1160 if (s.status() != QDataStream::Ok)
1161 return QImage();
1162 }
1163 }
1164
1166 return image;
1167}
1168
1169static QImage readARGB32F(QDataStream &s, const quint32 width, const quint32 height, bool alphaPremul)
1170{
1171 QImage image = imageAlloc(width, height, alphaPremul ? QImage::Format_RGBA32FPx4_Premultiplied : QImage::Format_RGBA32FPx4);
1172 if (image.isNull()) {
1173 return image;
1174 }
1175
1176 for (quint32 y = 0; y < height; y++) {
1177 float *line = reinterpret_cast<float *>(image.scanLine(y));
1178 for (quint32 x = 0; x < width; x++) {
1179 line[x * 4] = readFloat32(s);
1180 line[x * 4 + 1] = readFloat32(s);
1181 line[x * 4 + 2] = readFloat32(s);
1182 line[x * 4 + 3] = readFloat32(s);
1183 if (s.status() != QDataStream::Ok)
1184 return QImage();
1185 }
1186 }
1187
1189 return image;
1190}
1191
1192static QImage readQ16W16V16U16(QDataStream &s, const quint32 width, const quint32 height)
1193{
1194 QImage image = imageAlloc(width, height, QImage::Format_ARGB32);
1195 if (image.isNull()) {
1196 return image;
1197 }
1198
1199 quint8 colors[ColorCount];
1200 qint16 tmp;
1201 for (quint32 y = 0; y < height; y++) {
1202 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1203 for (quint32 x = 0; x < width; x++) {
1204 for (int i = 0; i < ColorCount; i++) {
1205 s >> tmp;
1206 colors[i] = (tmp + 0x7FFF) >> 8;
1207 }
1208 line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]);
1209 if (s.status() != QDataStream::Ok)
1210 return QImage();
1211 }
1212 }
1213
1214 return image;
1215}
1216
1217static QImage readCxV8U8(QDataStream &s, const quint32 width, const quint32 height)
1218{
1219 QImage image = imageAlloc(width, height, QImage::Format_RGB32);
1220 if (image.isNull()) {
1221 return image;
1222 }
1223
1224 for (quint32 y = 0; y < height; y++) {
1225 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1226 for (quint32 x = 0; x < width; x++) {
1227 qint8 v, u;
1228 s >> v >> u;
1229
1230 const quint8 vn = v + 128;
1231 const quint8 un = u + 128;
1232 const quint8 c = getNormalZ(vn, un);
1233
1234 line[x] = qRgb(vn, un, c);
1235
1236 if (s.status() != QDataStream::Ok)
1237 return QImage();
1238 }
1239 }
1240
1241 return image;
1242}
1243
1244static QImage readPalette8Image(QDataStream &s, const DDSHeader &dds, quint32 width, quint32 height)
1245{
1246 QImage image = imageAlloc(width, height, QImage::Format_Indexed8);
1247 if (image.isNull()) {
1248 return image;
1249 }
1250
1251 for (int i = 0; i < 256; ++i) {
1252 quint8 r, g, b, a;
1253 s >> r >> g >> b >> a;
1254 image.setColor(i, qRgba(r, g, b, a));
1255 }
1256
1257 for (quint32 y = 0; y < height; y++) {
1258 quint8 *scanLine = reinterpret_cast<quint8 *>(image.scanLine(y));
1259 for (quint32 x = 0; x < width; x++) {
1260 quint32 value = readValue(s, dds.pixelFormat.rgbBitCount);
1261 if (s.status() != QDataStream::Ok)
1262 return QImage();
1263 scanLine[x] = (value & 0xff); // any alpha channel discarded
1264 }
1265 }
1266
1267 return image;
1268}
1269
1270static QImage readPalette4Image(QDataStream &s, quint32 width, quint32 height)
1271{
1272 QImage image = imageAlloc(width, height, QImage::Format_Indexed8);
1273 if (image.isNull()) {
1274 return image;
1275 }
1276
1277 for (int i = 0; i < 16; ++i) {
1278 quint8 r, g, b, a;
1279 s >> r >> g >> b >> a;
1280 image.setColor(i, qRgba(r, g, b, a));
1281 }
1282
1283 for (quint32 y = 0; y < height; y++) {
1284 quint8 index;
1285 for (quint32 x = 0; x < width - 1; ) {
1286 s >> index;
1287 image.setPixel(x++, y, (index & 0x0f) >> 0);
1288 image.setPixel(x++, y, (index & 0xf0) >> 4);
1289 if (s.status() != QDataStream::Ok)
1290 return QImage();
1291 }
1292 if (width % 2 == 1) {
1293 s >> index;
1294 image.setPixel(width - 1, y, (index & 0x0f) >> 0);
1295 if (s.status() != QDataStream::Ok)
1296 return QImage();
1297 }
1298 }
1299
1300 return image;
1301}
1302
1303static QImage readARGB16(QDataStream &s, quint32 width, quint32 height)
1304{
1305 QImage image = imageAlloc(width, height, QImage::Format_ARGB32);
1306 if (image.isNull()) {
1307 return image;
1308 }
1309
1310 for (quint32 y = 0; y < height; y++) {
1311 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1312 for (quint32 x = 0; x < width; x++) {
1313 quint8 colors[ColorCount];
1314 for (int i = 0; i < ColorCount; ++i) {
1315 quint16 color;
1316 s >> color;
1317 colors[i] = quint8(color >> 8);
1318 }
1319 line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]);
1320 if (s.status() != QDataStream::Ok)
1321 return QImage();
1322 }
1323 }
1324
1325 return image;
1326}
1327
1328static QImage readV8U8(QDataStream &s, quint32 width, quint32 height)
1329{
1330 QImage image = imageAlloc(width, height, QImage::Format_RGB32);
1331 if (image.isNull()) {
1332 return image;
1333 }
1334
1335 for (quint32 y = 0; y < height; y++) {
1336 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1337 for (quint32 x = 0; x < width; x++) {
1338 qint8 v, u;
1339 s >> v >> u;
1340 line[x] = qRgb(v + 128, u + 128, 255);
1341 if (s.status() != QDataStream::Ok)
1342 return QImage();
1343 }
1344 }
1345
1346 return image;
1347}
1348
1349static QImage readL6V5U5(QDataStream &s, quint32 width, quint32 height)
1350{
1351 QImage image = imageAlloc(width, height, QImage::Format_ARGB32);
1352 if (image.isNull()) {
1353 return image;
1354 }
1355
1356 quint16 tmp;
1357 for (quint32 y = 0; y < height; y++) {
1358 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1359 for (quint32 x = 0; x < width; x++) {
1360 s >> tmp;
1361 quint8 r = qint8((tmp & 0x001f) >> 0) * 0xff/0x1f + 128;
1362 quint8 g = qint8((tmp & 0x03e0) >> 5) * 0xff/0x1f + 128;
1363 quint8 b = quint8((tmp & 0xfc00) >> 10) * 0xff/0x3f;
1364 line[x] = qRgba(r, g, 0xff, b);
1365 if (s.status() != QDataStream::Ok)
1366 return QImage();
1367 }
1368 }
1369 return image;
1370}
1371
1372static QImage readX8L8V8U8(QDataStream &s, quint32 width, quint32 height)
1373{
1374 QImage image = imageAlloc(width, height, QImage::Format_ARGB32);
1375 if (image.isNull()) {
1376 return image;
1377 }
1378
1379 quint8 a, l;
1380 qint8 v, u;
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 >> v >> u >> a >> l;
1385 line[x] = qRgba(v + 128, u + 128, 255, a);
1386 if (s.status() != QDataStream::Ok)
1387 return QImage();
1388 }
1389 }
1390
1391 return image;
1392}
1393
1394static QImage readQ8W8V8U8(QDataStream &s, quint32 width, quint32 height)
1395{
1396 QImage image = imageAlloc(width, height, QImage::Format_ARGB32);
1397 if (image.isNull()) {
1398 return image;
1399 }
1400
1401 quint8 colors[ColorCount];
1402 qint8 tmp;
1403 for (quint32 y = 0; y < height; y++) {
1404 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1405 for (quint32 x = 0; x < width; x++) {
1406 for (int i = 0; i < ColorCount; i++) {
1407 s >> tmp;
1408 colors[i] = tmp + 128;
1409 }
1410 line[x] = qRgba(colors[Red], colors[Green], colors[Blue], colors[Alpha]);
1411 if (s.status() != QDataStream::Ok)
1412 return QImage();
1413 }
1414 }
1415
1416 return image;
1417}
1418
1419static QImage readV16U16(QDataStream &s, quint32 width, quint32 height)
1420{
1421 QImage image = imageAlloc(width, height, QImage::Format_RGB32);
1422 if (image.isNull()) {
1423 return image;
1424 }
1425
1426 for (quint32 y = 0; y < height; y++) {
1427 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1428 for (quint32 x = 0; x < width; x++) {
1429 qint16 v, u;
1430 s >> v >> u;
1431 v = (v + 0x8000) >> 8;
1432 u = (u + 0x8000) >> 8;
1433 line[x] = qRgb(v, u, 255);
1434 if (s.status() != QDataStream::Ok)
1435 return QImage();
1436 }
1437 }
1438
1439 return image;
1440}
1441
1442static QImage readA2W10V10U10(QDataStream &s, quint32 width, quint32 height)
1443{
1444 QImage image = imageAlloc(width, height, QImage::Format_ARGB32);
1445 if (image.isNull()) {
1446 return image;
1447 }
1448
1449 quint32 tmp;
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 s >> tmp;
1454 quint8 r = qint8((tmp & 0x3ff00000) >> 20 >> 2) + 128;
1455 quint8 g = qint8((tmp & 0x000ffc00) >> 10 >> 2) + 128;
1456 quint8 b = qint8((tmp & 0x000003ff) >> 0 >> 2) + 128;
1457 quint8 a = 0xff * ((tmp & 0xc0000000) >> 30) / 3;
1458 // dunno why we should swap b and r here
1459 std::swap(b, r);
1460 line[x] = qRgba(r, g, b, a);
1461 if (s.status() != QDataStream::Ok)
1462 return QImage();
1463 }
1464 }
1465
1466 return image;
1467}
1468
1469static QImage readUYVY(QDataStream &s, quint32 width, quint32 height)
1470{
1471 QImage image = imageAlloc(width, height, QImage::Format_RGB32);
1472 if (image.isNull()) {
1473 return image;
1474 }
1475
1476 quint8 uyvy[4];
1477 for (quint32 y = 0; y < height; y++) {
1478 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1479 for (quint32 x = 0; x < width - 1; ) {
1480 s >> uyvy[0] >> uyvy[1] >> uyvy[2] >> uyvy[3];
1481 line[x++] = yuv2rgb(uyvy[1], uyvy[0], uyvy[2]);
1482 line[x++] = yuv2rgb(uyvy[3], uyvy[0], uyvy[2]);
1483 if (s.status() != QDataStream::Ok)
1484 return QImage();
1485 }
1486 if (width % 2 == 1) {
1487 s >> uyvy[0] >> uyvy[1] >> uyvy[2] >> uyvy[3];
1488 line[width - 1] = yuv2rgb(uyvy[1], uyvy[0], uyvy[2]);
1489 if (s.status() != QDataStream::Ok)
1490 return QImage();
1491 }
1492 }
1493
1494 return image;
1495}
1496
1497static QImage readR8G8B8G8(QDataStream &s, quint32 width, quint32 height)
1498{
1499 QImage image = imageAlloc(width, height, QImage::Format_RGB32);
1500 if (image.isNull()) {
1501 return image;
1502 }
1503
1504 quint8 rgbg[4];
1505 for (quint32 y = 0; y < height; y++) {
1506 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1507 for (quint32 x = 0; x < width - 1; ) {
1508 s >> rgbg[1] >> rgbg[0] >> rgbg[3] >> rgbg[2];
1509 line[x++] = qRgb(rgbg[0], rgbg[1], rgbg[2]);
1510 line[x++] = qRgb(rgbg[0], rgbg[3], rgbg[2]);
1511 if (s.status() != QDataStream::Ok)
1512 return QImage();
1513 }
1514 if (width % 2 == 1) {
1515 s >> rgbg[1] >> rgbg[0] >> rgbg[3] >> rgbg[2];
1516 line[width - 1] = qRgb(rgbg[0], rgbg[1], rgbg[2]);
1517 if (s.status() != QDataStream::Ok)
1518 return QImage();
1519 }
1520 }
1521
1522 return image;
1523}
1524
1525static QImage readYUY2(QDataStream &s, quint32 width, quint32 height)
1526{
1527 QImage image = imageAlloc(width, height, QImage::Format_RGB32);
1528 if (image.isNull()) {
1529 return image;
1530 }
1531
1532 quint8 yuyv[4];
1533 for (quint32 y = 0; y < height; y++) {
1534 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1535 for (quint32 x = 0; x < width - 1; ) {
1536 s >> yuyv[0] >> yuyv[1] >> yuyv[2] >> yuyv[3];
1537 line[x++] = yuv2rgb(yuyv[0], yuyv[1], yuyv[3]);
1538 line[x++] = yuv2rgb(yuyv[2], yuyv[1], yuyv[3]);
1539 if (s.status() != QDataStream::Ok)
1540 return QImage();
1541 }
1542 if (width % 2 == 1) {
1543 s >> yuyv[0] >> yuyv[1] >> yuyv[2] >> yuyv[3];
1544 line[width - 1] = yuv2rgb(yuyv[2], yuyv[1], yuyv[3]);
1545 if (s.status() != QDataStream::Ok)
1546 return QImage();
1547 }
1548 }
1549
1550 return image;
1551}
1552
1553static QImage readG8R8G8B8(QDataStream &s, quint32 width, quint32 height)
1554{
1555 QImage image = imageAlloc(width, height, QImage::Format_RGB32);
1556 if (image.isNull()) {
1557 return image;
1558 }
1559
1560 quint8 grgb[4];
1561 for (quint32 y = 0; y < height; y++) {
1562 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1563 for (quint32 x = 0; x < width - 1; ) {
1564 s >> grgb[1] >> grgb[0] >> grgb[3] >> grgb[2];
1565 line[x++] = qRgb(grgb[1], grgb[0], grgb[3]);
1566 line[x++] = qRgb(grgb[1], grgb[2], grgb[3]);
1567 if (s.status() != QDataStream::Ok)
1568 return QImage();
1569 }
1570 if (width % 2 == 1) {
1571 s >> grgb[1] >> grgb[0] >> grgb[3] >> grgb[2];
1572 line[width - 1] = qRgb(grgb[1], grgb[0], grgb[3]);
1573 if (s.status() != QDataStream::Ok)
1574 return QImage();
1575 }
1576 }
1577
1578 return image;
1579}
1580
1581static QImage readA2R10G10B10(QDataStream &s, const DDSHeader &dds, quint32 width, quint32 height)
1582{
1583 QImage image = readUnsignedImage(s, dds, width, height, true);
1584 if (image.isNull()) {
1585 return image;
1586 }
1587
1588 for (quint32 y = 0; y < height; y++) {
1589 QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y));
1590 for (quint32 x = 0; x < width; x++) {
1591 QRgb pixel = image.pixel(x, y);
1592 line[x] = qRgba(qBlue(pixel), qGreen(pixel), qRed(pixel), qAlpha(pixel));
1593 if (s.status() != QDataStream::Ok)
1594 return QImage();
1595 }
1596 }
1597 return image;
1598}
1599
1600static QImage readLayer(QDataStream &s, const DDSHeader &dds, const int format, quint32 width, quint32 height)
1601{
1602 if (width * height == 0)
1603 return QImage();
1604
1605 bool alphaPremul = dds.header10.miscFlags2 == DXGIAlphaModePremultiplied;
1606
1607 switch (format) {
1608 case FormatR8G8B8:
1609 case FormatX8R8G8B8:
1610 case FormatR5G6B5:
1611 case FormatR3G3B2:
1612 case FormatX1R5G5B5:
1613 case FormatX4R4G4B4:
1614 case FormatX8B8G8R8:
1615 case FormatG16R16:
1616 case FormatL8:
1617 case FormatL16:
1618 return readUnsignedImage(s, dds, width, height, false);
1619 case FormatA8R8G8B8:
1620 case FormatA1R5G5B5:
1621 case FormatA4R4G4B4:
1622 case FormatA8:
1623 case FormatA8R3G3B2:
1624 case FormatA8B8G8R8:
1625 case FormatA8L8:
1626 case FormatA4L4:
1627 return readUnsignedImage(s, dds, width, height, true);
1628 case FormatA2R10G10B10:
1629 case FormatA2B10G10R10:
1630 return readA2R10G10B10(s, dds, width, height);
1631 case FormatP8:
1632 case FormatA8P8:
1633 return readPalette8Image(s, dds, width, height);
1634 case FormatP4:
1635 case FormatA4P4:
1636 return readPalette4Image(s, width, height);
1637 case FormatA16B16G16R16:
1638 return readARGB16(s, width, height);
1639 case FormatV8U8:
1640 return readV8U8(s, width, height);
1641 case FormatL6V5U5:
1642 return readL6V5U5(s, width, height);
1643 case FormatX8L8V8U8:
1644 return readX8L8V8U8(s, width, height);
1645 case FormatQ8W8V8U8:
1646 return readQ8W8V8U8(s, width, height);
1647 case FormatV16U16:
1648 return readV16U16(s, width, height);
1649 case FormatA2W10V10U10:
1650 return readA2W10V10U10(s, width, height);
1651 case FormatUYVY:
1652 return readUYVY(s, width, height);
1653 case FormatR8G8B8G8:
1654 return readR8G8B8G8(s, width, height);
1655 case FormatYUY2:
1656 return readYUY2(s, width, height);
1657 case FormatG8R8G8B8:
1658 return readG8R8G8B8(s, width, height);
1659 case FormatDXT1:
1660 return readDXT1(s, width, height);
1661 case FormatDXT2:
1662 return readDXT2(s, width, height);
1663 case FormatDXT3:
1664 return readDXT3(s, width, height);
1665 case FormatDXT4:
1666 return readDXT4(s, width, height);
1667 case FormatDXT5:
1668 return readDXT5(s, width, height);
1669 case FormatRXGB:
1670 return readRXGB(s, width, height);
1671 case FormatATI2:
1672 return readATI2(s, width, height);
1673 case FormatR16F:
1674 return readR16F(s, width, height);
1675 case FormatG16R16F:
1676 return readRG16F(s, width, height);
1677 case FormatA16B16G16R16F:
1678 return readARGB16F(s, width, height, alphaPremul);
1679 case FormatR32F:
1680 return readR32F(s, width, height);
1681 case FormatG32R32F:
1682 return readRG32F(s, width, height);
1683 case FormatA32B32G32R32F:
1684 return readARGB32F(s, width, height, alphaPremul);
1685 case FormatD16Lockable:
1686 case FormatD32:
1687 case FormatD15S1:
1688 case FormatD24S8:
1689 case FormatD24X8:
1690 case FormatD24X4S4:
1691 case FormatD16:
1692 case FormatD32FLockable:
1693 case FormatD24FS8:
1694 case FormatD32Lockable:
1695 case FormatS8Lockable:
1696 case FormatVertexData:
1697 case FormatIndex16:
1698 case FormatIndex32:
1699 break;
1700 case FormatQ16W16V16U16:
1701 return readQ16W16V16U16(s, width, height);
1702 case FormatMulti2ARGB8:
1703 break;
1704 case FormatCxV8U8:
1705 return readCxV8U8(s, width, height);
1706 case FormatA1:
1707 case FormatA2B10G10R10_XR_BIAS:
1708 case FormatBinaryBuffer:
1709 case FormatLast:
1710 break;
1711 }
1712
1713 return QImage();
1714}
1715
1716static inline QImage readTexture(QDataStream &s, const DDSHeader &dds, const int format, const int mipmapLevel)
1717{
1718 quint32 width = dds.width / (1 << mipmapLevel);
1719 quint32 height = dds.height / (1 << mipmapLevel);
1720 return readLayer(s, dds, format, width, height);
1721}
1722
1723static qint64 mipmapSize(const DDSHeader &dds, const int format, const int level)
1724{
1725 quint32 w = dds.width/(1 << level);
1726 quint32 h = dds.height/(1 << level);
1727
1728 switch (format) {
1729 case FormatR8G8B8:
1730 case FormatX8R8G8B8:
1731 case FormatR5G6B5:
1732 case FormatX1R5G5B5:
1733 case FormatX4R4G4B4:
1734 case FormatX8B8G8R8:
1735 case FormatG16R16:
1736 case FormatL8:
1737 case FormatL16:
1738 return w * h * dds.pixelFormat.rgbBitCount / 8;
1739 case FormatA8R8G8B8:
1740 case FormatA1R5G5B5:
1741 case FormatA4R4G4B4:
1742 case FormatA8:
1743 case FormatA8R3G3B2:
1744 case FormatA2B10G10R10:
1745 case FormatA8B8G8R8:
1746 case FormatA2R10G10B10:
1747 case FormatA8L8:
1748 case FormatA4L4:
1749 return w * h * dds.pixelFormat.rgbBitCount / 8;
1750 case FormatP8:
1751 return 256 + w * h * 8;
1752 case FormatA16B16G16R16:
1753 return w * h * 4 * 2;
1754 case FormatA8P8:
1755 break;
1756 case FormatV8U8:
1757 case FormatL6V5U5:
1758 return w * h * 2;
1759 case FormatX8L8V8U8:
1760 case FormatQ8W8V8U8:
1761 case FormatV16U16:
1762 case FormatA2W10V10U10:
1763 return w * h * 4;
1764 case FormatUYVY:
1765 case FormatR8G8B8G8:
1766 case FormatYUY2:
1767 case FormatG8R8G8B8:
1768 return w * h * 2;
1769 case FormatDXT1:
1770 return ((w + 3)/4) * ((h + 3)/4) * 8;
1771 case FormatDXT2:
1772 case FormatDXT3:
1773 case FormatDXT4:
1774 case FormatDXT5:
1775 return ((w + 3)/4) * ((h + 3)/4) * 16;
1776 case FormatD16Lockable:
1777 case FormatD32:
1778 case FormatD15S1:
1779 case FormatD24S8:
1780 case FormatD24X8:
1781 case FormatD24X4S4:
1782 case FormatD16:
1783 case FormatD32FLockable:
1784 case FormatD24FS8:
1785 case FormatD32Lockable:
1786 case FormatS8Lockable:
1787 case FormatVertexData:
1788 case FormatIndex16:
1789 case FormatIndex32:
1790 break;
1791 case FormatQ16W16V16U16:
1792 return w * h * 4 * 2;
1793 case FormatMulti2ARGB8:
1794 break;
1795 case FormatR16F:
1796 return w * h * 1 * 2;
1797 case FormatG16R16F:
1798 return w * h * 2 * 2;
1799 case FormatA16B16G16R16F:
1800 return w * h * 4 * 2;
1801 case FormatR32F:
1802 return w * h * 1 * 4;
1803 case FormatG32R32F:
1804 return w * h * 2 * 4;
1805 case FormatA32B32G32R32F:
1806 return w * h * 4 * 4;
1807 case FormatCxV8U8:
1808 return w * h * 2;
1809 case FormatA1:
1810 case FormatA2B10G10R10_XR_BIAS:
1811 case FormatBinaryBuffer:
1812 case FormatLast:
1813 break;
1814 }
1815
1816 return 0;
1817}
1818
1819static qint64 mipmapOffset(const DDSHeader &dds, const int format, const int level)
1820{
1821 qint64 result = 0;
1822 for (int i = 0; i < level; ++i)
1823 result += mipmapSize(dds, format, i);
1824 return result;
1825}
1826
1827static QImage readCubeMap(QDataStream &s, const DDSHeader &dds, const int fmt)
1828{
1830 QImage image = imageAlloc(4 * dds.width, 3 * dds.height, format);
1831 if (image.isNull()) {
1832 return image;
1833 }
1834
1835 image.fill(0);
1836
1837 for (int i = 0; i < 6; i++) {
1838 if (!(dds.caps2 & faceFlags[i]))
1839 continue; // Skip face.
1840
1841 QImage face = readLayer(s, dds, fmt, dds.width, dds.height);
1842 if (face.isNull()) {
1843 return {};
1844 }
1845 face.convertTo(format);
1846 if (face.isNull()) {
1847 return {};
1848 }
1849 if (face.colorSpace().isValid()) {
1850 image.setColorSpace(face.colorSpace());
1851 }
1852
1853 // Compute face offsets.
1854 int offset_x = faceOffsets[i].x * dds.width;
1855 int offset_y = faceOffsets[i].y * dds.height;
1856
1857 // Copy face on the image.
1858 for (quint32 y = 0; y < dds.height; y++) {
1859 if (y + offset_y >= quint32(image.height())) {
1860 return {};
1861 }
1862 const QRgb *src = reinterpret_cast<const QRgb *>(face.constScanLine(y));
1863 QRgb *dst = reinterpret_cast<QRgb *>(image.scanLine(y + offset_y)) + offset_x;
1864 qsizetype sz = sizeof(QRgb) * dds.width;
1865 if (ptrDiff(face.bits() + face.sizeInBytes(), src) < sz || ptrDiff(image.bits() + image.sizeInBytes(), dst) < sz) {
1866 return {};
1867 }
1868 memcpy(dst, src, sz);
1869 }
1870 }
1871
1872 return image;
1873}
1874
1875static QByteArray formatName(int format)
1876{
1877 for (size_t i = 0; i < formatNamesSize; ++i) {
1878 if (formatNames[i].format == format)
1879 return formatNames[i].name;
1880 }
1881
1882 return formatNames[0].name;
1883}
1884
1885static int formatByName(const QByteArray &name)
1886{
1887 const QByteArray loweredName = name.toLower();
1888 for (size_t i = 0; i < formatNamesSize; ++i) {
1889 if (QByteArray(formatNames[i].name).toLower() == loweredName)
1890 return formatNames[i].format;
1891 }
1892
1893 return FormatUnknown;
1894}
1895
1896QDDSHandler::QDDSHandler() :
1897 m_header(),
1898 m_format(FormatUnknown),
1899 m_currentImage(0),
1900 m_scanState(ScanNotScanned)
1901{
1902}
1903
1904bool QDDSHandler::canRead() const
1905{
1906 if (m_scanState == ScanNotScanned && !canRead(device()))
1907 return false;
1908
1909 if (m_scanState != ScanError) {
1910 setFormat(QByteArrayLiteral("dds"));
1911 return true;
1912 }
1913
1914 return false;
1915}
1916
1917bool QDDSHandler::read(QImage *outImage)
1918{
1919 if (!ensureScanned() || device()->isSequential())
1920 return false;
1921
1922 qint64 pos = headerSize;
1923 if (m_header.pixelFormat.fourCC == dx10Magic)
1924 pos += 20;
1925 pos += mipmapOffset(m_header, m_format, m_currentImage);
1926 if (!device()->seek(pos))
1927 return false;
1928 QDataStream s(device());
1930
1931 QImage image = isCubeMap(m_header) ?
1932 readCubeMap(s, m_header, m_format) :
1933 readTexture(s, m_header, m_format, m_currentImage);
1934 if (image.isNull()) {
1935 return false;
1936 }
1937
1938 bool ok = s.status() == QDataStream::Ok && !image.isNull();
1939 if (ok)
1940 *outImage = image;
1941 return ok;
1942}
1943
1944bool writeA8R8G8B8(const QImage &outImage, QDataStream &s)
1945{
1946 DDSHeader dds;
1947 // Filling header
1948 dds.magic = ddsMagic;
1949 dds.size = 124;
1950 dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
1951 DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat |
1952 DDSHeader::FlagPitch;
1953 dds.height = outImage.height();
1954 dds.width = outImage.width();
1955 dds.pitchOrLinearSize = dds.width * 32 / 8;
1956 dds.depth = 0;
1957 dds.mipMapCount = 0;
1958 for (int i = 0; i < DDSHeader::ReservedCount; i++)
1959 dds.reserved1[i] = 0;
1960 dds.caps = DDSHeader::CapsTexture;
1961 dds.caps2 = 0;
1962 dds.caps3 = 0;
1963 dds.caps4 = 0;
1964 dds.reserved2 = 0;
1965
1966 // Filling pixelformat
1967 dds.pixelFormat.size = 32;
1968 dds.pixelFormat.flags = DDSPixelFormat::FlagAlphaPixels | DDSPixelFormat::FlagRGB;
1969 dds.pixelFormat.fourCC = 0;
1970 dds.pixelFormat.rgbBitCount = 32;
1971 dds.pixelFormat.aBitMask = 0xff000000;
1972 dds.pixelFormat.rBitMask = 0x00ff0000;
1973 dds.pixelFormat.gBitMask = 0x0000ff00;
1974 dds.pixelFormat.bBitMask = 0x000000ff;
1975
1976 s << dds;
1977 if (s.status() != QDataStream::Ok) {
1978 return false;
1979 }
1980
1981 ScanLineConverter slc(QImage::Format_ARGB32);
1982 if(outImage.colorSpace().isValid())
1983 slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgb));
1984
1985 for (int y = 0, h = outImage.height(); y < h; ++y) {
1986 const QRgb *scanLine = reinterpret_cast<const QRgb*>(slc.convertedScanLine(outImage, y));
1987 if (scanLine == nullptr) {
1988 return false;
1989 }
1990 for (int x = 0, w = outImage.width(); x < w; ++x) {
1991 s << quint32(scanLine[x]);
1992 }
1993 if (s.status() != QDataStream::Ok) {
1994 return false;
1995 }
1996 }
1997
1998 return true;
1999}
2000
2001bool writeR8G8B8(const QImage &outImage, QDataStream &s)
2002{
2003 DDSHeader dds;
2004 // Filling header
2005 dds.magic = ddsMagic;
2006 dds.size = 124;
2007 dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
2008 DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat |
2009 DDSHeader::FlagPitch;
2010 dds.height = outImage.height();
2011 dds.width = outImage.width();
2012 dds.pitchOrLinearSize = dds.width * 24 / 8;
2013 dds.depth = 1;
2014 dds.mipMapCount = 0;
2015 for (int i = 0; i < DDSHeader::ReservedCount; i++)
2016 dds.reserved1[i] = 0;
2017 dds.caps = DDSHeader::CapsTexture;
2018 dds.caps2 = 0;
2019 dds.caps3 = 0;
2020 dds.caps4 = 0;
2021 dds.reserved2 = 0;
2022
2023 // Filling pixelformat
2024 dds.pixelFormat.size = 32;
2025 dds.pixelFormat.flags = DDSPixelFormat::FlagRGB;
2026 dds.pixelFormat.fourCC = 0;
2027 dds.pixelFormat.rgbBitCount = 24;
2028 dds.pixelFormat.aBitMask = 0x00000000;
2029 dds.pixelFormat.rBitMask = 0x00ff0000;
2030 dds.pixelFormat.gBitMask = 0x0000ff00;
2031 dds.pixelFormat.bBitMask = 0x000000ff;
2032
2033 s << dds;
2034 if (s.status() != QDataStream::Ok) {
2035 return false;
2036 }
2037
2038 ScanLineConverter slc(QImage::Format_RGB888);
2039 if(outImage.colorSpace().isValid())
2040 slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgb));
2041
2042 for (int y = 0, h = outImage.height(); y < h; ++y) {
2043 const quint8 *scanLine = reinterpret_cast<const quint8*>(slc.convertedScanLine(outImage, y));
2044 if (scanLine == nullptr) {
2045 return false;
2046 }
2047 for (int x = 0, w = outImage.width(); x < w; ++x) {
2048 size_t x3 = size_t(x) * 3;
2049 s << scanLine[x3 + 2];
2050 s << scanLine[x3 + 1];
2051 s << scanLine[x3];
2052 }
2053 if (s.status() != QDataStream::Ok) {
2054 return false;
2055 }
2056 }
2057
2058 return true;
2059}
2060
2061bool writeL8(const QImage &outImage, QDataStream &s)
2062{
2063 DDSHeader dds;
2064 // Filling header
2065 dds.magic = ddsMagic;
2066 dds.size = 124;
2067 dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
2068 DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat |
2069 DDSHeader::FlagPitch;
2070 dds.height = outImage.height();
2071 dds.width = outImage.width();
2072 dds.pitchOrLinearSize = dds.width;
2073 dds.depth = 1;
2074 dds.mipMapCount = 0;
2075 for (int i = 0; i < DDSHeader::ReservedCount; i++)
2076 dds.reserved1[i] = 0;
2077 dds.caps = DDSHeader::CapsTexture;
2078 dds.caps2 = 0;
2079 dds.caps3 = 0;
2080 dds.caps4 = 0;
2081 dds.reserved2 = 0;
2082
2083 // Filling pixelformat
2084 dds.pixelFormat.size = 32;
2085 dds.pixelFormat.flags = DDSPixelFormat::FlagLuminance | DDSPixelFormat::FlagRGB;
2086 dds.pixelFormat.fourCC = 0;
2087 dds.pixelFormat.rgbBitCount = 8;
2088 dds.pixelFormat.aBitMask = 0x00000000;
2089 dds.pixelFormat.rBitMask = 0x000000ff;
2090 dds.pixelFormat.gBitMask = 0x00000000;
2091 dds.pixelFormat.bBitMask = 0x00000000;
2092
2093 s << dds;
2094 if (s.status() != QDataStream::Ok) {
2095 return false;
2096 }
2097
2098 ScanLineConverter slc(QImage::Format_Grayscale8);
2099#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
2100 if(outImage.colorSpace().isValid())
2101 slc.setTargetColorSpace(QColorSpace(QPointF(0.3127, 0.3291), QColorSpace::TransferFunction::SRgb));
2102#endif
2103
2104 for (int y = 0, h = outImage.height(); y < h; ++y) {
2105 const quint8 *scanLine = reinterpret_cast<const quint8*>(slc.convertedScanLine(outImage, y));
2106 if (scanLine == nullptr) {
2107 return false;
2108 }
2109 for (int x = 0, w = outImage.width(); x < w; ++x) {
2110 s << scanLine[x];
2111 }
2112 if (s.status() != QDataStream::Ok) {
2113 return false;
2114 }
2115 }
2116
2117 return true;
2118}
2119
2120bool writeP8(const QImage &image, QDataStream &s)
2121{
2122 QImage outImage = image;
2123 // indexed not supported by ScanlineConverter class
2124 if (image.format() != QImage::Format_Indexed8) {
2125 if (image.colorSpace().isValid())
2127 outImage = outImage.convertToFormat(QImage::Format_Indexed8);
2128 }
2129
2130 DDSHeader dds;
2131 // Filling header
2132 dds.magic = ddsMagic;
2133 dds.size = 124;
2134 dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
2135 DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat |
2136 DDSHeader::FlagPitch;
2137 dds.height = outImage.height();
2138 dds.width = outImage.width();
2139 dds.pitchOrLinearSize = dds.width;
2140 dds.depth = 1;
2141 dds.mipMapCount = 0;
2142 for (int i = 0; i < DDSHeader::ReservedCount; i++)
2143 dds.reserved1[i] = 0;
2144 dds.caps = DDSHeader::CapsTexture;
2145 dds.caps2 = 0;
2146 dds.caps3 = 0;
2147 dds.caps4 = 0;
2148 dds.reserved2 = 0;
2149
2150 // Filling pixelformat
2151 dds.pixelFormat.size = 32;
2152 dds.pixelFormat.flags = DDSPixelFormat::FlagPaletteIndexed8;
2153 dds.pixelFormat.fourCC = 0;
2154 dds.pixelFormat.rgbBitCount = 8;
2155 dds.pixelFormat.aBitMask = 0x00000000;
2156 dds.pixelFormat.rBitMask = 0x00000000;
2157 dds.pixelFormat.gBitMask = 0x00000000;
2158 dds.pixelFormat.bBitMask = 0x00000000;
2159
2160 s << dds;
2161
2162 QList<QRgb> palette = outImage.colorTable();
2163 for (int i = 0; i < 256; ++i) {
2164 quint8 r = 0, g = 0, b = 0, a = 0xff;
2165 if (i < palette.size()) {
2166 auto&& rgba = palette.at(i);
2167 r = qRed(rgba);
2168 g = qGreen(rgba);
2169 b = qBlue(rgba);
2170 a = qAlpha(rgba);
2171 }
2172 s << r;
2173 s << g;
2174 s << b;
2175 s << a;
2176 }
2177
2178 if (s.status() != QDataStream::Ok) {
2179 return false;
2180 }
2181
2182 for (int y = 0, h = outImage.height(); y < h; ++y) {
2183 const quint8 *scanLine = reinterpret_cast<const quint8*>(outImage.constScanLine(y));
2184 if (scanLine == nullptr) {
2185 return false;
2186 }
2187 for (int x = 0, w = outImage.width(); x < w; ++x) {
2188 s << scanLine[x];
2189 }
2190 if (s.status() != QDataStream::Ok) {
2191 return false;
2192 }
2193 }
2194
2195 return true;
2196}
2197
2198bool writeA16B16G16R16F(const QImage &outImage, QDataStream &s)
2199{
2200 DDSHeader dds;
2201 // Filling header
2202 dds.magic = ddsMagic;
2203 dds.size = 124;
2204 dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
2205 DDSHeader::FlagWidth | DDSHeader::FlagPitch |
2206 DDSHeader::FlagPixelFormat;
2207 dds.height = outImage.height();
2208 dds.width = outImage.width();
2209 dds.pitchOrLinearSize = dds.width * 64 / 8;
2210 dds.depth = 1;
2211 dds.mipMapCount = 0;
2212 for (int i = 0; i < DDSHeader::ReservedCount; i++)
2213 dds.reserved1[i] = 0;
2214 dds.caps = DDSHeader::CapsTexture;
2215 dds.caps2 = 0;
2216 dds.caps3 = 0;
2217 dds.caps4 = 0;
2218 dds.reserved2 = 0;
2219
2220 // Filling pixelformat
2221 dds.pixelFormat.size = 32;
2222 dds.pixelFormat.flags = DDSPixelFormat::FlagFourCC;
2223 dds.pixelFormat.fourCC = 113;
2224 dds.pixelFormat.rgbBitCount = 0;
2225 dds.pixelFormat.aBitMask = 0;
2226 dds.pixelFormat.rBitMask = 0;
2227 dds.pixelFormat.gBitMask = 0;
2228 dds.pixelFormat.bBitMask = 0;
2229
2230 s << dds;
2231 if (s.status() != QDataStream::Ok) {
2232 return false;
2233 }
2234
2235 ScanLineConverter slc(QImage::Format_RGBA16FPx4);
2236 if(outImage.colorSpace().isValid())
2237 slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgbLinear));
2238
2239 for (int y = 0, h = outImage.height(); y < h; ++y) {
2240 const quint16 *scanLine = reinterpret_cast<const quint16*>(slc.convertedScanLine(outImage, y));
2241 if (scanLine == nullptr) {
2242 return false;
2243 }
2244 for (int x = 0, w = outImage.width(); x < w; ++x) {
2245 size_t x4 = size_t(x) * 4;
2246 s << scanLine[x4];
2247 s << scanLine[x4 + 1];
2248 s << scanLine[x4 + 2];
2249 s << scanLine[x4 + 3];
2250 }
2251 if (s.status() != QDataStream::Ok) {
2252 return false;
2253 }
2254 }
2255
2256 return true;
2257}
2258
2259bool writeA32B32G32R32F(const QImage &outImage, QDataStream &s)
2260{
2261 DDSHeader dds;
2262 // Filling header
2263 dds.magic = ddsMagic;
2264 dds.size = 124;
2265 dds.flags = DDSHeader::FlagCaps | DDSHeader::FlagHeight |
2266 DDSHeader::FlagWidth | DDSHeader::FlagPitch |
2267 DDSHeader::FlagPixelFormat;
2268 dds.height = outImage.height();
2269 dds.width = outImage.width();
2270 dds.pitchOrLinearSize = dds.width * 128 / 8;
2271 dds.depth = 1;
2272 dds.mipMapCount = 0;
2273 for (int i = 0; i < DDSHeader::ReservedCount; i++)
2274 dds.reserved1[i] = 0;
2275 dds.caps = DDSHeader::CapsTexture;
2276 dds.caps2 = 0;
2277 dds.caps3 = 0;
2278 dds.caps4 = 0;
2279 dds.reserved2 = 0;
2280
2281 // Filling pixelformat
2282 dds.pixelFormat.size = 32;
2283 dds.pixelFormat.flags = DDSPixelFormat::FlagFourCC;
2284 dds.pixelFormat.fourCC = 116;
2285 dds.pixelFormat.rgbBitCount = 0;
2286 dds.pixelFormat.aBitMask = 0;
2287 dds.pixelFormat.rBitMask = 0;
2288 dds.pixelFormat.gBitMask = 0;
2289 dds.pixelFormat.bBitMask = 0;
2290
2291 s << dds;
2292 if (s.status() != QDataStream::Ok) {
2293 return false;
2294 }
2295
2296 ScanLineConverter slc(QImage::Format_RGBA32FPx4);
2297 if(outImage.colorSpace().isValid())
2298 slc.setTargetColorSpace(QColorSpace(QColorSpace::SRgbLinear));
2299
2300 for (int y = 0, h = outImage.height(); y < h; ++y) {
2301 const quint32 *scanLine = reinterpret_cast<const quint32*>(slc.convertedScanLine(outImage, y));
2302 if (scanLine == nullptr) {
2303 return false;
2304 }
2305 for (int x = 0, w = outImage.width(); x < w; ++x) {
2306 size_t x4 = size_t(x) * 4;
2307 s << scanLine[x4];
2308 s << scanLine[x4 + 1];
2309 s << scanLine[x4 + 2];
2310 s << scanLine[x4 + 3];
2311 }
2312 if (s.status() != QDataStream::Ok) {
2313 return false;
2314 }
2315 }
2316
2317 return true;
2318}
2319
2320bool QDDSHandler::write(const QImage &outImage)
2321{
2322 if (outImage.isNull() || device() == nullptr) {
2323 return false;
2324 }
2325
2326 QDataStream s(device());
2328
2329 int format = m_format;
2330 if (format == FormatUnknown) {
2331 switch (outImage.format()) {
2335 format = FormatA16B16G16R16F;
2336 break;
2337
2341 format = FormatA32B32G32R32F;
2342 break;
2343
2348 format = FormatL8;
2349 break;
2350
2352 format = FormatP8;
2353 break;
2354
2355 default:
2356 format = outImage.hasAlphaChannel() ? FormatA8R8G8B8 : FormatR8G8B8;
2357 }
2358 }
2359
2360 if (format == FormatA8R8G8B8) {
2361 return writeA8R8G8B8(outImage, s);
2362 }
2363
2364 if (format == FormatR8G8B8) {
2365 return writeR8G8B8(outImage, s);
2366 }
2367
2368 if (format == FormatL8) {
2369 return writeL8(outImage, s);
2370 }
2371
2372 if (format == FormatP8) {
2373 return writeP8(outImage, s);
2374 }
2375
2376 if (format == FormatA16B16G16R16F) {
2377 return writeA16B16G16R16F(outImage, s);
2378 }
2379
2380 if (format == FormatA32B32G32R32F) {
2381 return writeA32B32G32R32F(outImage, s);
2382 }
2383
2384 qWarning() << "Format" << formatName(format) << "is not supported";
2385 return false;
2386}
2387
2388QVariant QDDSHandler::option(QImageIOHandler::ImageOption option) const
2389{
2390 if (!supportsOption(option)) {
2391 return QVariant();
2392 }
2393
2394 // *** options that do not require a valid stream ***
2395 if (option == QImageIOHandler::SupportedSubTypes) {
2397 << QByteArrayLiteral("Automatic")
2398 << formatName(FormatA8R8G8B8)
2399 << formatName(FormatR8G8B8)
2400 << formatName(FormatL8)
2401 << formatName(FormatP8)
2402 << formatName(FormatA16B16G16R16F)
2403 << formatName(FormatA32B32G32R32F));
2404 }
2405
2406 // *** options that require a valid stream ***
2407 if (!ensureScanned()) {
2408 return QVariant();
2409 }
2410
2411 if (option == QImageIOHandler::Size) {
2412 return isCubeMap(m_header) ? QSize(m_header.width * 4, m_header.height * 3) : QSize(m_header.width, m_header.height);
2413 }
2414
2415 if (option == QImageIOHandler::SubType) {
2416 return m_format == FormatUnknown ? QByteArrayLiteral("Automatic") : formatName(m_format);
2417 }
2418
2419 return QVariant();
2420}
2421
2422void QDDSHandler::setOption(QImageIOHandler::ImageOption option, const QVariant &value)
2423{
2424 if (option == QImageIOHandler::SubType) {
2425 const QByteArray subType = value.toByteArray();
2426 m_format = formatByName(subType.toUpper());
2427 }
2428}
2429
2430bool QDDSHandler::supportsOption(QImageIOHandler::ImageOption option) const
2431{
2432 return (option == QImageIOHandler::Size)
2433 || (option == QImageIOHandler::SubType)
2435}
2436
2437int QDDSHandler::imageCount() const
2438{
2439 if (!ensureScanned())
2440 return 0;
2441
2442 return qMax<quint32>(1, m_header.mipMapCount);
2443}
2444
2445bool QDDSHandler::jumpToImage(int imageNumber)
2446{
2447 if (imageNumber >= imageCount())
2448 return false;
2449
2450 m_currentImage = imageNumber;
2451 return true;
2452}
2453
2454bool QDDSHandler::canRead(QIODevice *device)
2455{
2456 if (!device) {
2457 qWarning() << "DDSHandler::canRead() called with no device";
2458 return false;
2459 }
2460
2461 if (device->isSequential())
2462 return false;
2463
2464 return device->peek(4) == QByteArrayLiteral("DDS ");
2465}
2466
2467bool QDDSHandler::ensureScanned() const
2468{
2469 if (device() == nullptr) {
2470 return false;
2471 }
2472
2473 if (m_scanState != ScanNotScanned)
2474 return m_scanState == ScanSuccess;
2475
2476 m_scanState = ScanError;
2477
2478 QDDSHandler *that = const_cast<QDDSHandler *>(this);
2479 that->m_format = FormatUnknown;
2480
2481 if (device()->isSequential()) {
2482 qWarning() << "Sequential devices are not supported";
2483 return false;
2484 }
2485
2486 qint64 oldPos = device()->pos();
2487 device()->seek(0);
2488
2489 QDataStream s(device());
2491 s >> that->m_header;
2492
2493 device()->seek(oldPos);
2494
2495 if (s.status() != QDataStream::Ok)
2496 return false;
2497
2498 if (!verifyHeader(m_header))
2499 return false;
2500
2501 that->m_format = getFormat(m_header);
2502 if (that->m_format == FormatUnknown)
2503 return false;
2504
2505 m_scanState = ScanSuccess;
2506 return true;
2507}
2508
2509bool QDDSHandler::verifyHeader(const DDSHeader &dds) const
2510{
2511 quint32 flags = dds.flags;
2512 quint32 requiredFlags = DDSHeader::FlagCaps | DDSHeader::FlagHeight
2513 | DDSHeader::FlagWidth | DDSHeader::FlagPixelFormat;
2514 if ((flags & requiredFlags) != requiredFlags) {
2515 qWarning() << "Wrong dds.flags - not all required flags present. "
2516 "Actual flags :" << flags;
2517 return false;
2518 }
2519
2520 if (dds.size != ddsSize) {
2521 qWarning() << "Wrong dds.size: actual =" << dds.size
2522 << "expected =" << ddsSize;
2523 return false;
2524 }
2525
2526 if (dds.pixelFormat.size != pixelFormatSize) {
2527 qWarning() << "Wrong dds.pixelFormat.size: actual =" << dds.pixelFormat.size
2528 << "expected =" << pixelFormatSize;
2529 return false;
2530 }
2531
2532 if (dds.width > INT_MAX || dds.height > INT_MAX) {
2533 qWarning() << "Can't read image with w/h bigger than INT_MAX";
2534 return false;
2535 }
2536
2537 return true;
2538}
2539
2540QImageIOPlugin::Capabilities QDDSPlugin::capabilities(QIODevice *device, const QByteArray &format) const
2541{
2542 if (format == QByteArrayLiteral("dds"))
2543 return Capabilities(CanRead | CanWrite);
2544 if (!format.isEmpty())
2545 return {};
2546 if (!device || !device->isOpen())
2547 return {};
2548
2549 Capabilities cap;
2550 if (device->isReadable() && QDDSHandler::canRead(device))
2551 cap |= CanRead;
2552 if (device->isWritable())
2553 cap |= CanWrite;
2554 return cap;
2555}
2556
2557QImageIOHandler *QDDSPlugin::create(QIODevice *device, const QByteArray &format) const
2558{
2559 QImageIOHandler *handler = new QDDSHandler;
2560 handler->setDevice(device);
2561 handler->setFormat(format);
2562 return handler;
2563}
Q_SCRIPTABLE Q_NOREPLY void start()
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
KCOREADDONS_EXPORT unsigned int version()
QDebug operator<<(QDebug dbg, const DcrawInfoContainer &c)
QFlags< Capability > Capabilities
QStringView level(QStringView ifopt)
QString name(StandardAction id)
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)
qsizetype sizeInBytes() const const
int width() const const
void setDevice(QIODevice *device)
void setFormat(const QByteArray &format)
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
QString toLower() const const
QVariant fromValue(T &&value)
QByteArray toByteArray() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:00:39 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.