Coverage Report

Created: 2026-05-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibGfx/ImageFormats/DDSLoader.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2021, the SerenityOS developers.
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <AK/Debug.h>
8
#include <AK/Endian.h>
9
#include <AK/Error.h>
10
#include <AK/MemoryStream.h>
11
#include <AK/StringBuilder.h>
12
#include <AK/Try.h>
13
#include <AK/Vector.h>
14
#include <LibGfx/ImageFormats/DDSLoader.h>
15
#include <fcntl.h>
16
#include <math.h>
17
#include <stdio.h>
18
#include <string.h>
19
#include <sys/mman.h>
20
#include <sys/stat.h>
21
#include <unistd.h>
22
23
namespace Gfx {
24
25
struct DDSLoadingContext {
26
    DDSLoadingContext(FixedMemoryStream stream)
27
3.53k
        : stream(move(stream))
28
3.53k
    {
29
3.53k
    }
30
31
    enum State {
32
        NotDecoded = 0,
33
        Error,
34
        HeaderDecoded,
35
        BitmapDecoded,
36
    };
37
38
    State state { State::NotDecoded };
39
40
    FixedMemoryStream stream;
41
42
    DDSHeader header;
43
    DDSHeaderDXT10 header10;
44
    DXGIFormat format;
45
    RefPtr<Gfx::Bitmap> bitmap;
46
47
    void dump_debug();
48
};
49
50
static constexpr u32 create_four_cc(char c0, char c1, char c2, char c3)
51
14.9k
{
52
14.9k
    return c0 | c1 << 8 | c2 << 16 | c3 << 24;
53
14.9k
}
54
55
static u64 get_width(DDSHeader header, size_t mipmap_level)
56
441
{
57
441
    if (mipmap_level >= header.mip_map_count) {
58
19
        return header.width;
59
19
    }
60
61
422
    return header.width >> mipmap_level;
62
441
}
63
64
static u64 get_height(DDSHeader header, size_t mipmap_level)
65
441
{
66
441
    if (mipmap_level >= header.mip_map_count) {
67
19
        return header.height;
68
19
    }
69
70
422
    return header.height >> mipmap_level;
71
441
}
72
73
static constexpr bool has_bitmask(DDSPixelFormat format, u32 r, u32 g, u32 b, u32 a)
74
9.67k
{
75
9.67k
    return format.r_bit_mask == r && format.g_bit_mask == g && format.b_bit_mask == b && format.a_bit_mask == a;
76
9.67k
}
77
78
static DXGIFormat get_format(DDSPixelFormat format)
79
3.41k
{
80
3.41k
    if ((format.flags & PixelFormatFlags::DDPF_RGB) == PixelFormatFlags::DDPF_RGB) {
81
1.48k
        switch (format.rgb_bit_count) {
82
722
        case 32: {
83
722
            if (has_bitmask(format, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000))
84
1
                return DXGI_FORMAT_R8G8B8A8_UNORM;
85
721
            if (has_bitmask(format, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000))
86
1
                return DXGI_FORMAT_B8G8R8A8_UNORM;
87
720
            if (has_bitmask(format, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000))
88
1
                return DXGI_FORMAT_B8G8R8X8_UNORM;
89
719
            if (has_bitmask(format, 0x3FF00000, 0x000FFC00, 0x000003FF, 0xC0000000))
90
1
                return DXGI_FORMAT_R10G10B10A2_UNORM;
91
718
            if (has_bitmask(format, 0x0000FFFF, 0xFFFF0000, 0x00000000, 0x00000000))
92
1
                return DXGI_FORMAT_R16G16_UNORM;
93
717
            if (has_bitmask(format, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000))
94
1
                return DXGI_FORMAT_R32_FLOAT;
95
716
            break;
96
717
        }
97
716
        case 24:
98
0
            break;
99
601
        case 16: {
100
601
            if (has_bitmask(format, 0x7C00, 0x03E0, 0x001F, 0x8000))
101
1
                return DXGI_FORMAT_B5G5R5A1_UNORM;
102
600
            if (has_bitmask(format, 0xF800, 0x07E0, 0x001F, 0x0000))
103
1
                return DXGI_FORMAT_B5G6R5_UNORM;
104
599
            if (has_bitmask(format, 0xF800, 0x07E0, 0x001F, 0x0000))
105
0
                return DXGI_FORMAT_B5G6R5_UNORM;
106
599
            if (has_bitmask(format, 0x0F00, 0x00F0, 0x000F, 0xF000))
107
1
                return DXGI_FORMAT_B4G4R4A4_UNORM;
108
598
            if (has_bitmask(format, 0x00FF, 0x0000, 0x0000, 0xFF00))
109
1
                return DXGI_FORMAT_R8G8_UNORM;
110
597
            if (has_bitmask(format, 0xFFFF, 0x0000, 0x0000, 0x0000))
111
1
                return DXGI_FORMAT_R16_UNORM;
112
596
            break;
113
597
        }
114
596
        case 8: {
115
156
            if (has_bitmask(format, 0xFF, 0x00, 0x00, 0x00))
116
1
                return DXGI_FORMAT_R8_UNORM;
117
155
            break;
118
156
        }
119
1.48k
        }
120
1.92k
    } else if ((format.flags & PixelFormatFlags::DDPF_LUMINANCE) == PixelFormatFlags::DDPF_LUMINANCE) {
121
462
        switch (format.rgb_bit_count) {
122
283
        case 16: {
123
283
            if (has_bitmask(format, 0xFFFF, 0x0000, 0x0000, 0x0000))
124
1
                return DXGI_FORMAT_R16_UNORM;
125
282
            if (has_bitmask(format, 0x00FF, 0x0000, 0x0000, 0xFF00))
126
1
                return DXGI_FORMAT_R8G8_UNORM;
127
281
            break;
128
282
        }
129
281
        case 8: {
130
174
            if (has_bitmask(format, 0xFF, 0x00, 0x00, 0x00))
131
1
                return DXGI_FORMAT_R8_UNORM;
132
133
            // Some writers mistakenly write this as 8 bpp.
134
173
            if (has_bitmask(format, 0x00FF, 0x0000, 0x0000, 0xFF00))
135
1
                return DXGI_FORMAT_R8G8_UNORM;
136
172
            break;
137
173
        }
138
462
        }
139
1.46k
    } else if ((format.flags & PixelFormatFlags::DDPF_ALPHA) == PixelFormatFlags::DDPF_ALPHA) {
140
40
        if (format.rgb_bit_count == 8)
141
1
            return DXGI_FORMAT_A8_UNORM;
142
1.42k
    } else if ((format.flags & PixelFormatFlags::DDPF_BUMPDUDV) == PixelFormatFlags::DDPF_BUMPDUDV) {
143
425
        switch (format.rgb_bit_count) {
144
276
        case 32: {
145
276
            if (has_bitmask(format, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000))
146
1
                return DXGI_FORMAT_R8G8B8A8_SNORM;
147
275
            if (has_bitmask(format, 0x0000FFFF, 0xFFFF0000, 0x00000000, 0x00000000))
148
1
                return DXGI_FORMAT_R16G16_SNORM;
149
274
            break;
150
275
        }
151
274
        case 16: {
152
147
            if (has_bitmask(format, 0x00FF, 0xFF00, 0x0000, 0x0000))
153
1
                return DXGI_FORMAT_R8G8_SNORM;
154
146
            break;
155
147
        }
156
425
        }
157
1.00k
    } else if ((format.flags & PixelFormatFlags::DDPF_FOURCC) == PixelFormatFlags::DDPF_FOURCC) {
158
1.00k
        if (format.four_cc == create_four_cc('D', 'X', 'T', '1'))
159
113
            return DXGI_FORMAT_BC1_UNORM;
160
888
        if (format.four_cc == create_four_cc('D', 'X', 'T', '2'))
161
97
            return DXGI_FORMAT_BC2_UNORM;
162
791
        if (format.four_cc == create_four_cc('D', 'X', 'T', '3'))
163
71
            return DXGI_FORMAT_BC2_UNORM;
164
720
        if (format.four_cc == create_four_cc('D', 'X', 'T', '4'))
165
113
            return DXGI_FORMAT_BC3_UNORM;
166
607
        if (format.four_cc == create_four_cc('D', 'X', 'T', '5'))
167
47
            return DXGI_FORMAT_BC3_UNORM;
168
560
        if (format.four_cc == create_four_cc('A', 'T', 'I', '1'))
169
1
            return DXGI_FORMAT_BC4_UNORM;
170
559
        if (format.four_cc == create_four_cc('B', 'C', '4', 'U'))
171
1
            return DXGI_FORMAT_BC4_UNORM;
172
558
        if (format.four_cc == create_four_cc('B', 'C', '4', 'S'))
173
1
            return DXGI_FORMAT_BC4_SNORM;
174
557
        if (format.four_cc == create_four_cc('A', 'T', 'I', '2'))
175
1
            return DXGI_FORMAT_BC5_UNORM;
176
556
        if (format.four_cc == create_four_cc('B', 'C', '5', 'U'))
177
1
            return DXGI_FORMAT_BC5_UNORM;
178
555
        if (format.four_cc == create_four_cc('B', 'C', '5', 'S'))
179
1
            return DXGI_FORMAT_BC5_SNORM;
180
554
        if (format.four_cc == create_four_cc('R', 'G', 'B', 'G'))
181
1
            return DXGI_FORMAT_R8G8_B8G8_UNORM;
182
553
        if (format.four_cc == create_four_cc('G', 'R', 'G', 'B'))
183
1
            return DXGI_FORMAT_G8R8_G8B8_UNORM;
184
552
        if (format.four_cc == create_four_cc('Y', 'U', 'Y', '2'))
185
1
            return DXGI_FORMAT_YUY2;
186
187
551
        switch (format.four_cc) {
188
1
        case 36:
189
1
            return DXGI_FORMAT_R16G16B16A16_UNORM;
190
1
        case 110:
191
1
            return DXGI_FORMAT_R16G16B16A16_SNORM;
192
1
        case 111:
193
1
            return DXGI_FORMAT_R16_FLOAT;
194
1
        case 112:
195
1
            return DXGI_FORMAT_R16G16_FLOAT;
196
1
        case 113:
197
1
            return DXGI_FORMAT_R16G16B16A16_FLOAT;
198
1
        case 114:
199
1
            return DXGI_FORMAT_R32_FLOAT;
200
1
        case 115:
201
1
            return DXGI_FORMAT_R32G32_FLOAT;
202
1
        case 116:
203
1
            return DXGI_FORMAT_R32G32B32A32_FLOAT;
204
551
        }
205
551
    }
206
207
2.93k
    return DXGI_FORMAT_UNKNOWN;
208
3.41k
}
209
210
static ErrorOr<void> decode_dx5_alpha_block(Stream& stream, DDSLoadingContext& context, u64 bitmap_x, u64 bitmap_y)
211
80.6k
{
212
80.6k
    auto color0 = TRY(stream.read_value<LittleEndian<u8>>());
213
80.6k
    auto color1 = TRY(stream.read_value<LittleEndian<u8>>());
214
215
80.6k
    auto code0 = TRY(stream.read_value<LittleEndian<u8>>());
216
80.6k
    auto code1 = TRY(stream.read_value<LittleEndian<u8>>());
217
80.6k
    auto code2 = TRY(stream.read_value<LittleEndian<u8>>());
218
80.6k
    auto code3 = TRY(stream.read_value<LittleEndian<u8>>());
219
80.6k
    auto code4 = TRY(stream.read_value<LittleEndian<u8>>());
220
80.6k
    auto code5 = TRY(stream.read_value<LittleEndian<u8>>());
221
222
80.6k
    u32 codes[6] = { 0 };
223
80.6k
    codes[0] = code0 + 256 * (code1 + 256);
224
80.6k
    codes[1] = code1 + 256 * (code2 + 256);
225
80.6k
    codes[2] = code2 + 256 * (code3 + 256);
226
80.6k
    codes[3] = code3 + 256 * (code4 + 256);
227
80.6k
    codes[4] = code4 + 256 * code5;
228
80.6k
    codes[5] = code5;
229
230
80.6k
    u32 color[8] = { 0 };
231
232
80.6k
    if (color0 > 128) {
233
52.3k
        color[0] = color0;
234
52.3k
    }
235
236
80.6k
    if (color1 > 128) {
237
52.2k
        color[1] = color1;
238
52.2k
    }
239
240
80.6k
    if (color0 > color1) {
241
1.92k
        color[2] = (6 * color[0] + 1 * color[1]) / 7;
242
1.92k
        color[3] = (5 * color[0] + 2 * color[1]) / 7;
243
1.92k
        color[4] = (4 * color[0] + 3 * color[1]) / 7;
244
1.92k
        color[5] = (3 * color[0] + 4 * color[1]) / 7;
245
1.92k
        color[6] = (2 * color[0] + 5 * color[1]) / 7;
246
1.92k
        color[7] = (1 * color[0] + 6 * color[1]) / 7;
247
78.7k
    } else {
248
78.7k
        color[2] = (4 * color[0] + 1 * color[1]) / 5;
249
78.7k
        color[3] = (3 * color[0] + 2 * color[1]) / 5;
250
78.7k
        color[4] = (2 * color[0] + 3 * color[1]) / 5;
251
78.7k
        color[5] = (1 * color[0] + 4 * color[1]) / 5;
252
78.7k
        color[6] = 0;
253
78.7k
        color[7] = 255;
254
78.7k
    }
255
256
388k
    for (size_t y = 0; y < 4 && bitmap_y + y < static_cast<u64>(context.bitmap->height()); y++) {
257
1.49M
        for (size_t x = 0; x < 4 && bitmap_x + x < static_cast<u64>(context.bitmap->width()); x++) {
258
1.19M
            u8 index = 3 * (4 * y + x);
259
1.19M
            u8 bit_location = floor(index / 8.0);
260
1.19M
            u8 adjusted_index = index - (bit_location * 8);
261
262
1.19M
            u8 code = (codes[bit_location] >> adjusted_index) & 7;
263
1.19M
            u8 alpha = color[code];
264
265
1.19M
            Color color = Color(0, 0, 0, alpha);
266
1.19M
            context.bitmap->set_pixel(bitmap_x + x, bitmap_y + y, color);
267
1.19M
        }
268
308k
    }
269
270
80.6k
    return {};
271
80.6k
}
272
273
static ErrorOr<void> decode_dx3_alpha_block(Stream& stream, DDSLoadingContext& context, u64 bitmap_x, u64 bitmap_y)
274
129k
{
275
129k
    auto a0 = TRY(stream.read_value<LittleEndian<u8>>());
276
129k
    auto a1 = TRY(stream.read_value<LittleEndian<u8>>());
277
129k
    auto a2 = TRY(stream.read_value<LittleEndian<u8>>());
278
129k
    auto a3 = TRY(stream.read_value<LittleEndian<u8>>());
279
129k
    auto a4 = TRY(stream.read_value<LittleEndian<u8>>());
280
129k
    auto a5 = TRY(stream.read_value<LittleEndian<u8>>());
281
129k
    auto a6 = TRY(stream.read_value<LittleEndian<u8>>());
282
129k
    auto a7 = TRY(stream.read_value<LittleEndian<u8>>());
283
284
129k
    u64 alpha_0 = a0 + 256u * (a1 + 256u * (a2 + 256u * (a3 + 256u)));
285
129k
    u64 alpha_1 = a4 + 256u * (a5 + 256u * (a6 + 256u * a7));
286
287
648k
    for (size_t y = 0; y < 4 && bitmap_y + y < static_cast<u64>(context.bitmap->height()); y++) {
288
2.45M
        for (size_t x = 0; x < 4 && bitmap_x + x < static_cast<u64>(context.bitmap->width()); x++) {
289
1.93M
            u8 code = 4 * (4 * y + x);
290
291
1.93M
            if (code >= 32) {
292
966k
                code = code - 32;
293
966k
                u8 alpha = ((alpha_1 >> code) & 0x0F) * 17;
294
295
966k
                Color color = Color(0, 0, 0, alpha);
296
966k
                context.bitmap->set_pixel(bitmap_x + x, bitmap_y + y, color);
297
969k
            } else {
298
969k
                u8 alpha = ((alpha_0 >> code) & 0x0F) * 17;
299
300
969k
                Color color = Color(0, 0, 0, alpha);
301
969k
                context.bitmap->set_pixel(bitmap_x + x, bitmap_y + y, color);
302
969k
            }
303
1.93M
        }
304
518k
    }
305
306
129k
    return {};
307
129k
}
308
309
static void unpack_rbg_565(u32 rgb, u8* output)
310
439k
{
311
439k
    u8 r = (rgb >> 11) & 0x1F;
312
439k
    u8 g = (rgb >> 5) & 0x3F;
313
439k
    u8 b = rgb & 0x1F;
314
315
439k
    output[0] = (r << 3) | (r >> 2);
316
439k
    output[1] = (g << 2) | (g >> 4);
317
439k
    output[2] = (b << 3) | (b >> 2);
318
439k
    output[3] = 255;
319
439k
}
320
321
static ErrorOr<void> decode_color_block(Stream& stream, DDSLoadingContext& context, bool dxt1, u64 bitmap_x, u64 bitmap_y)
322
219k
{
323
219k
    auto c0_low = TRY(stream.read_value<LittleEndian<u8>>());
324
219k
    auto c0_high = TRY(stream.read_value<LittleEndian<u8>>());
325
219k
    auto c1_low = TRY(stream.read_value<LittleEndian<u8>>());
326
219k
    auto c1_high = TRY(stream.read_value<LittleEndian<u8>>());
327
328
219k
    auto codes_0 = TRY(stream.read_value<LittleEndian<u8>>());
329
219k
    auto codes_1 = TRY(stream.read_value<LittleEndian<u8>>());
330
219k
    auto codes_2 = TRY(stream.read_value<LittleEndian<u8>>());
331
219k
    auto codes_3 = TRY(stream.read_value<LittleEndian<u8>>());
332
333
219k
    u64 code = codes_0 + 256ul * (codes_1 + 256ul * (codes_2 + 256ul * codes_3));
334
219k
    u32 color_0 = c0_low + (c0_high * 256);
335
219k
    u32 color_1 = c1_low + (c1_high * 256);
336
337
219k
    u8 rgba[4][4];
338
219k
    unpack_rbg_565(color_0, rgba[0]);
339
219k
    unpack_rbg_565(color_1, rgba[1]);
340
341
219k
    if (color_0 > color_1) {
342
34.5k
        for (size_t i = 0; i < 3; i++) {
343
25.9k
            rgba[2][i] = (2 * rgba[0][i] + rgba[1][i]) / 3;
344
25.9k
            rgba[3][i] = (rgba[0][i] + 2 * rgba[1][i]) / 3;
345
25.9k
        }
346
347
8.64k
        rgba[2][3] = 255;
348
8.64k
        rgba[3][3] = 255;
349
211k
    } else {
350
844k
        for (size_t i = 0; i < 3; i++) {
351
633k
            rgba[2][i] = (rgba[0][i] + rgba[1][i]) / 2;
352
633k
            rgba[3][i] = 0;
353
633k
        }
354
355
211k
        rgba[2][3] = 255;
356
211k
        rgba[3][3] = dxt1 ? 0 : 255;
357
211k
    }
358
359
219k
    size_t i = 0;
360
1.08M
    for (size_t y = 0; y < 4 && bitmap_y + y < static_cast<u64>(context.bitmap->height()); y++) {
361
4.12M
        for (size_t x = 0; x < 4 && bitmap_x + x < static_cast<u64>(context.bitmap->width()); x++) {
362
3.26M
            u8 code_byte = (code >> (i * 2)) & 3;
363
3.26M
            u8 r = rgba[code_byte][0];
364
3.26M
            u8 g = rgba[code_byte][1];
365
3.26M
            u8 b = rgba[code_byte][2];
366
3.26M
            u8 a = dxt1 ? rgba[code_byte][3] : context.bitmap->get_pixel(bitmap_x + x, bitmap_y + y).alpha();
367
368
3.26M
            Color color = Color(r, g, b, a);
369
3.26M
            context.bitmap->set_pixel(bitmap_x + x, bitmap_y + y, color);
370
3.26M
            i++;
371
3.26M
        }
372
861k
    }
373
374
219k
    return {};
375
219k
}
376
377
static ErrorOr<void> decode_dxt(Stream& stream, DDSLoadingContext& context, u64 width, u64 y)
378
24.7k
{
379
24.7k
    if (context.format == DXGI_FORMAT_BC1_UNORM) {
380
9.75k
        for (size_t x = 0; x < width; x += 4) {
381
9.48k
            TRY(decode_color_block(stream, context, true, x, y));
382
9.40k
        }
383
352
    }
384
385
24.7k
    if (context.format == DXGI_FORMAT_BC2_UNORM) {
386
144k
        for (size_t x = 0; x < width; x += 4) {
387
129k
            TRY(decode_dx3_alpha_block(stream, context, x, y));
388
129k
            TRY(decode_color_block(stream, context, false, x, y));
389
129k
        }
390
14.8k
    }
391
392
24.6k
    if (context.format == DXGI_FORMAT_BC3_UNORM) {
393
90.1k
        for (size_t x = 0; x < width; x += 4) {
394
80.6k
            TRY(decode_dx5_alpha_block(stream, context, x, y));
395
80.6k
            TRY(decode_color_block(stream, context, false, x, y));
396
80.5k
        }
397
9.55k
    }
398
399
24.5k
    return {};
400
24.5k
}
401
static ErrorOr<void> decode_bitmap(Stream& stream, DDSLoadingContext& context, u64 width, u64 height)
402
339
{
403
339
    static constexpr Array dxt_formats = { DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC3_UNORM };
404
339
    if (dxt_formats.contains_slow(context.format)) {
405
24.7k
        for (u64 y = 0; y < height; y += 4) {
406
24.7k
            TRY(decode_dxt(stream, context, width, y));
407
24.4k
        }
408
339
    }
409
410
    // FIXME: Support more encodings (ATI, YUV, RAW, etc...).
411
339
    return {};
412
339
}
413
414
static ErrorOr<void> decode_header(DDSLoadingContext& context)
415
3.53k
{
416
    // All valid DDS files are at least 128 bytes long.
417
7.06k
    if (TRY(context.stream.size()) < 128) {
418
18
        dbgln_if(DDS_DEBUG, "File is too short for DDS");
419
18
        context.state = DDSLoadingContext::State::Error;
420
18
        return Error::from_string_literal("File is too short for DDS");
421
18
    }
422
423
3.51k
    auto magic = TRY(context.stream.read_value<u32>());
424
425
3.51k
    if (magic != create_four_cc('D', 'D', 'S', ' ')) {
426
52
        dbgln_if(DDS_DEBUG, "Missing magic number");
427
52
        context.state = DDSLoadingContext::State::Error;
428
52
        return Error::from_string_literal("Missing magic number");
429
52
    }
430
431
3.46k
    context.header = TRY(context.stream.read_value<DDSHeader>());
432
433
3.46k
    if (context.header.size != 124) {
434
14
        dbgln_if(DDS_DEBUG, "Header size is malformed");
435
14
        context.state = DDSLoadingContext::State::Error;
436
14
        return Error::from_string_literal("Header size is malformed");
437
14
    }
438
3.45k
    if (context.header.pixel_format.size != 32) {
439
31
        dbgln_if(DDS_DEBUG, "Pixel format size is malformed");
440
31
        context.state = DDSLoadingContext::State::Error;
441
31
        return Error::from_string_literal("Pixel format size is malformed");
442
31
    }
443
444
3.41k
    if ((context.header.pixel_format.flags & PixelFormatFlags::DDPF_FOURCC) == PixelFormatFlags::DDPF_FOURCC) {
445
2.39k
        if (context.header.pixel_format.four_cc == create_four_cc('D', 'X', '1', '0')) {
446
88
            if (TRY(context.stream.size()) < 148) {
447
9
                dbgln_if(DDS_DEBUG, "DX10 header is too short");
448
9
                context.state = DDSLoadingContext::State::Error;
449
9
                return Error::from_string_literal("DX10 header is too short");
450
9
            }
451
452
35
            context.header10 = TRY(context.stream.read_value<DDSHeaderDXT10>());
453
35
        }
454
2.39k
    }
455
456
    if constexpr (DDS_DEBUG) {
457
        context.dump_debug();
458
    }
459
460
3.41k
    context.format = get_format(context.header.pixel_format);
461
462
3.41k
    static constexpr Array supported_formats = { DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC3_UNORM };
463
3.41k
    if (!supported_formats.contains_slow(context.format)) {
464
2.96k
        dbgln_if(DDS_DEBUG, "Format of type {} is not supported at the moment", to_underlying(context.format));
465
2.96k
        context.state = DDSLoadingContext::State::Error;
466
2.96k
        return Error::from_string_literal("Format type is not supported at the moment");
467
2.96k
    }
468
469
441
    context.state = DDSLoadingContext::HeaderDecoded;
470
471
441
    return {};
472
3.41k
}
473
474
static ErrorOr<void> decode_dds(DDSLoadingContext& context)
475
441
{
476
441
    VERIFY(context.state == DDSLoadingContext::HeaderDecoded);
477
478
    // We support parsing mipmaps, but we only care about the largest one :^) (At least for now)
479
441
    if (size_t mipmap_level = 0; mipmap_level < max(context.header.mip_map_count, 1u)) {
480
441
        u64 width = get_width(context.header, mipmap_level);
481
441
        u64 height = get_height(context.header, mipmap_level);
482
483
441
        context.bitmap = TRY(Bitmap::create(BitmapFormat::BGRA8888, { width, height }));
484
485
339
        TRY(decode_bitmap(context.stream, context, width, height));
486
11
    }
487
488
441
    context.state = DDSLoadingContext::State::BitmapDecoded;
489
490
11
    return {};
491
441
}
492
493
void DDSLoadingContext::dump_debug()
494
0
{
495
0
    StringBuilder builder;
496
497
0
    builder.append("\nDDS:\n"sv);
498
0
    builder.appendff("\tHeader Size: {}\n", header.size);
499
500
0
    builder.append("\tFlags:"sv);
501
0
    if ((header.flags & DDSFlags::DDSD_CAPS) == DDSFlags::DDSD_CAPS)
502
0
        builder.append(" DDSD_CAPS"sv);
503
0
    if ((header.flags & DDSFlags::DDSD_HEIGHT) == DDSFlags::DDSD_HEIGHT)
504
0
        builder.append(" DDSD_HEIGHT"sv);
505
0
    if ((header.flags & DDSFlags::DDSD_WIDTH) == DDSFlags::DDSD_WIDTH)
506
0
        builder.append(" DDSD_WIDTH"sv);
507
0
    if ((header.flags & DDSFlags::DDSD_PITCH) == DDSFlags::DDSD_PITCH)
508
0
        builder.append(" DDSD_PITCH"sv);
509
0
    if ((header.flags & DDSFlags::DDSD_PIXELFORMAT) == DDSFlags::DDSD_PIXELFORMAT)
510
0
        builder.append(" DDSD_PIXELFORMAT"sv);
511
0
    if ((header.flags & DDSFlags::DDSD_MIPMAPCOUNT) == DDSFlags::DDSD_MIPMAPCOUNT)
512
0
        builder.append(" DDSD_MIPMAPCOUNT"sv);
513
0
    if ((header.flags & DDSFlags::DDSD_LINEARSIZE) == DDSFlags::DDSD_LINEARSIZE)
514
0
        builder.append(" DDSD_LINEARSIZE"sv);
515
0
    if ((header.flags & DDSFlags::DDSD_DEPTH) == DDSFlags::DDSD_DEPTH)
516
0
        builder.append(" DDSD_DEPTH"sv);
517
0
    builder.append("\n"sv);
518
519
0
    builder.appendff("\tHeight: {}\n", header.height);
520
0
    builder.appendff("\tWidth: {}\n", header.width);
521
0
    builder.appendff("\tPitch: {}\n", header.pitch);
522
0
    builder.appendff("\tDepth: {}\n", header.depth);
523
0
    builder.appendff("\tMipmap Count: {}\n", header.mip_map_count);
524
525
0
    builder.append("\tCaps:"sv);
526
0
    if ((header.caps1 & Caps1Flags::DDSCAPS_COMPLEX) == Caps1Flags::DDSCAPS_COMPLEX)
527
0
        builder.append(" DDSCAPS_COMPLEX"sv);
528
0
    if ((header.caps1 & Caps1Flags::DDSCAPS_MIPMAP) == Caps1Flags::DDSCAPS_MIPMAP)
529
0
        builder.append(" DDSCAPS_MIPMAP"sv);
530
0
    if ((header.caps1 & Caps1Flags::DDSCAPS_TEXTURE) == Caps1Flags::DDSCAPS_TEXTURE)
531
0
        builder.append(" DDSCAPS_TEXTURE"sv);
532
0
    builder.append("\n"sv);
533
534
0
    builder.append("\tCaps2:"sv);
535
0
    if ((header.caps2 & Caps2Flags::DDSCAPS2_CUBEMAP) == Caps2Flags::DDSCAPS2_CUBEMAP)
536
0
        builder.append(" DDSCAPS2_CUBEMAP"sv);
537
0
    if ((header.caps2 & Caps2Flags::DDSCAPS2_CUBEMAP_POSITIVEX) == Caps2Flags::DDSCAPS2_CUBEMAP_POSITIVEX)
538
0
        builder.append(" DDSCAPS2_CUBEMAP_POSITIVEX"sv);
539
0
    if ((header.caps2 & Caps2Flags::DDSCAPS2_CUBEMAP_NEGATIVEX) == Caps2Flags::DDSCAPS2_CUBEMAP_NEGATIVEX)
540
0
        builder.append(" DDSCAPS2_CUBEMAP_NEGATIVEX"sv);
541
0
    if ((header.caps2 & Caps2Flags::DDSCAPS2_CUBEMAP_POSITIVEY) == Caps2Flags::DDSCAPS2_CUBEMAP_POSITIVEY)
542
0
        builder.append(" DDSCAPS2_CUBEMAP_POSITIVEY"sv);
543
0
    if ((header.caps2 & Caps2Flags::DDSCAPS2_CUBEMAP_NEGATIVEY) == Caps2Flags::DDSCAPS2_CUBEMAP_NEGATIVEY)
544
0
        builder.append(" DDSCAPS2_CUBEMAP_NEGATIVEY"sv);
545
0
    if ((header.caps2 & Caps2Flags::DDSCAPS2_CUBEMAP_POSITIVEZ) == Caps2Flags::DDSCAPS2_CUBEMAP_POSITIVEZ)
546
0
        builder.append(" DDSCAPS2_CUBEMAP_POSITIVEZ"sv);
547
0
    if ((header.caps2 & Caps2Flags::DDSCAPS2_CUBEMAP_NEGATIVEZ) == Caps2Flags::DDSCAPS2_CUBEMAP_NEGATIVEZ)
548
0
        builder.append(" DDSCAPS2_CUBEMAP_NEGATIVEZ"sv);
549
0
    if ((header.caps2 & Caps2Flags::DDSCAPS2_VOLUME) == Caps2Flags::DDSCAPS2_VOLUME)
550
0
        builder.append(" DDSCAPS2_VOLUME"sv);
551
0
    builder.append("\n"sv);
552
553
0
    builder.append("Pixel Format:\n"sv);
554
0
    builder.appendff("\tStruct Size: {}\n", header.pixel_format.size);
555
556
0
    builder.append("\tFlags:"sv);
557
0
    if ((header.pixel_format.flags & PixelFormatFlags::DDPF_ALPHAPIXELS) == PixelFormatFlags::DDPF_ALPHAPIXELS)
558
0
        builder.append(" DDPF_ALPHAPIXELS"sv);
559
0
    if ((header.pixel_format.flags & PixelFormatFlags::DDPF_ALPHA) == PixelFormatFlags::DDPF_ALPHA)
560
0
        builder.append(" DDPF_ALPHA"sv);
561
0
    if ((header.pixel_format.flags & PixelFormatFlags::DDPF_FOURCC) == PixelFormatFlags::DDPF_FOURCC)
562
0
        builder.append(" DDPF_FOURCC"sv);
563
0
    if ((header.pixel_format.flags & PixelFormatFlags::DDPF_PALETTEINDEXED8) == PixelFormatFlags::DDPF_PALETTEINDEXED8)
564
0
        builder.append(" DDPF_PALETTEINDEXED8"sv);
565
0
    if ((header.pixel_format.flags & PixelFormatFlags::DDPF_RGB) == PixelFormatFlags::DDPF_RGB)
566
0
        builder.append(" DDPF_RGB"sv);
567
0
    if ((header.pixel_format.flags & PixelFormatFlags::DDPF_YUV) == PixelFormatFlags::DDPF_YUV)
568
0
        builder.append(" DDPF_YUV"sv);
569
0
    if ((header.pixel_format.flags & PixelFormatFlags::DDPF_LUMINANCE) == PixelFormatFlags::DDPF_LUMINANCE)
570
0
        builder.append(" DDPF_LUMINANCE"sv);
571
0
    if ((header.pixel_format.flags & PixelFormatFlags::DDPF_BUMPDUDV) == PixelFormatFlags::DDPF_BUMPDUDV)
572
0
        builder.append(" DDPF_BUMPDUDV"sv);
573
0
    if ((header.pixel_format.flags & PixelFormatFlags::DDPF_NORMAL) == PixelFormatFlags::DDPF_NORMAL)
574
0
        builder.append(" DDPF_NORMAL"sv);
575
0
    builder.append("\n"sv);
576
577
0
    builder.append("\tFour CC: "sv);
578
0
    builder.appendff("{:c}", (header.pixel_format.four_cc >> (8 * 0)) & 0xFF);
579
0
    builder.appendff("{:c}", (header.pixel_format.four_cc >> (8 * 1)) & 0xFF);
580
0
    builder.appendff("{:c}", (header.pixel_format.four_cc >> (8 * 2)) & 0xFF);
581
0
    builder.appendff("{:c}", (header.pixel_format.four_cc >> (8 * 3)) & 0xFF);
582
0
    builder.append("\n"sv);
583
0
    builder.appendff("\tRGB Bit Count: {}\n", header.pixel_format.rgb_bit_count);
584
0
    builder.appendff("\tR Bit Mask: {}\n", header.pixel_format.r_bit_mask);
585
0
    builder.appendff("\tG Bit Mask: {}\n", header.pixel_format.g_bit_mask);
586
0
    builder.appendff("\tB Bit Mask: {}\n", header.pixel_format.b_bit_mask);
587
0
    builder.appendff("\tA Bit Mask: {}\n", header.pixel_format.a_bit_mask);
588
589
0
    builder.append("DDS10:\n"sv);
590
0
    builder.appendff("\tFormat: {}\n", static_cast<u32>(header10.format));
591
592
0
    builder.append("\tResource Dimension:"sv);
593
0
    if ((header10.resource_dimension & ResourceDimensions::DDS_DIMENSION_UNKNOWN) == ResourceDimensions::DDS_DIMENSION_UNKNOWN)
594
0
        builder.append(" DDS_DIMENSION_UNKNOWN"sv);
595
0
    if ((header10.resource_dimension & ResourceDimensions::DDS_DIMENSION_BUFFER) == ResourceDimensions::DDS_DIMENSION_BUFFER)
596
0
        builder.append(" DDS_DIMENSION_BUFFER"sv);
597
0
    if ((header10.resource_dimension & ResourceDimensions::DDS_DIMENSION_TEXTURE1D) == ResourceDimensions::DDS_DIMENSION_TEXTURE1D)
598
0
        builder.append(" DDS_DIMENSION_TEXTURE1D"sv);
599
0
    if ((header10.resource_dimension & ResourceDimensions::DDS_DIMENSION_TEXTURE2D) == ResourceDimensions::DDS_DIMENSION_TEXTURE2D)
600
0
        builder.append(" DDS_DIMENSION_TEXTURE2D"sv);
601
0
    if ((header10.resource_dimension & ResourceDimensions::DDS_DIMENSION_TEXTURE3D) == ResourceDimensions::DDS_DIMENSION_TEXTURE3D)
602
0
        builder.append(" DDS_DIMENSION_TEXTURE3D"sv);
603
0
    builder.append("\n"sv);
604
605
0
    builder.appendff("\tArray Size: {}\n", header10.array_size);
606
607
0
    builder.append("\tMisc Flags:"sv);
608
0
    if ((header10.misc_flag & MiscFlags::DDS_RESOURCE_MISC_TEXTURECUBE) == MiscFlags::DDS_RESOURCE_MISC_TEXTURECUBE)
609
0
        builder.append(" DDS_RESOURCE_MISC_TEXTURECUBE"sv);
610
0
    builder.append("\n"sv);
611
612
0
    builder.append("\tMisc Flags 2:"sv);
613
0
    if ((header10.misc_flag2 & Misc2Flags::DDS_ALPHA_MODE_UNKNOWN) == Misc2Flags::DDS_ALPHA_MODE_UNKNOWN)
614
0
        builder.append(" DDS_ALPHA_MODE_UNKNOWN"sv);
615
0
    if ((header10.misc_flag2 & Misc2Flags::DDS_ALPHA_MODE_STRAIGHT) == Misc2Flags::DDS_ALPHA_MODE_STRAIGHT)
616
0
        builder.append(" DDS_ALPHA_MODE_STRAIGHT"sv);
617
0
    if ((header10.misc_flag2 & Misc2Flags::DDS_ALPHA_MODE_PREMULTIPLIED) == Misc2Flags::DDS_ALPHA_MODE_PREMULTIPLIED)
618
0
        builder.append(" DDS_ALPHA_MODE_PREMULTIPLIED"sv);
619
0
    if ((header10.misc_flag2 & Misc2Flags::DDS_ALPHA_MODE_OPAQUE) == Misc2Flags::DDS_ALPHA_MODE_OPAQUE)
620
0
        builder.append(" DDS_ALPHA_MODE_OPAQUE"sv);
621
0
    if ((header10.misc_flag2 & Misc2Flags::DDS_ALPHA_MODE_CUSTOM) == Misc2Flags::DDS_ALPHA_MODE_CUSTOM)
622
0
        builder.append(" DDS_ALPHA_MODE_CUSTOM"sv);
623
0
    builder.append("\n"sv);
624
625
0
    dbgln("{}", builder.to_byte_string());
626
0
}
627
628
DDSImageDecoderPlugin::DDSImageDecoderPlugin(FixedMemoryStream stream)
629
3.53k
{
630
3.53k
    m_context = make<DDSLoadingContext>(move(stream));
631
3.53k
}
632
633
3.53k
DDSImageDecoderPlugin::~DDSImageDecoderPlugin() = default;
634
635
IntSize DDSImageDecoderPlugin::size()
636
0
{
637
0
    return { m_context->header.width, m_context->header.height };
638
0
}
639
640
bool DDSImageDecoderPlugin::sniff(ReadonlyBytes data)
641
0
{
642
    // The header is always at least 128 bytes, so if the file is smaller, it can't be a DDS.
643
0
    return data.size() > 128
644
0
        && data.data()[0] == 0x44
645
0
        && data.data()[1] == 0x44
646
0
        && data.data()[2] == 0x53
647
0
        && data.data()[3] == 0x20;
648
0
}
649
650
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> DDSImageDecoderPlugin::create(ReadonlyBytes data)
651
3.53k
{
652
3.53k
    FixedMemoryStream stream { data };
653
3.53k
    auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) DDSImageDecoderPlugin(move(stream))));
654
3.53k
    TRY(decode_header(*plugin->m_context));
655
441
    return plugin;
656
3.53k
}
657
658
ErrorOr<ImageFrameDescriptor> DDSImageDecoderPlugin::frame(size_t index, Optional<IntSize>)
659
441
{
660
441
    if (index > 0)
661
0
        return Error::from_string_literal("DDSImageDecoderPlugin: Invalid frame index");
662
663
441
    if (m_context->state == DDSLoadingContext::State::Error)
664
0
        return Error::from_string_literal("DDSImageDecoderPlugin: Decoding failed");
665
666
441
    if (m_context->state < DDSLoadingContext::State::BitmapDecoded) {
667
441
        TRY(decode_dds(*m_context));
668
11
    }
669
670
441
    VERIFY(m_context->bitmap);
671
11
    return ImageFrameDescriptor { m_context->bitmap, 0 };
672
11
}
673
674
}