From 79fbd9300854bd59c93aee3dd336fb2df0c9d97d Mon Sep 17 00:00:00 2001 From: BlueCube3310 <53150244+BlueCube3310@users.noreply.github.com> Date: Sun, 17 Aug 2025 18:11:12 +0200 Subject: [PATCH] [3.x] DDS: Backport fixes from 4.x branch --- modules/dds/texture_loader_dds.cpp | 637 +++++++++++++++++++++-------- 1 file changed, 473 insertions(+), 164 deletions(-) diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index fa2120dccae..cf3589c9873 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -38,61 +38,243 @@ enum { DDS_MAGIC = 0x20534444, + DDS_HEADER_SIZE = 124, + DDSD_PITCH = 0x00000008, DDSD_LINEARSIZE = 0x00080000, DDSD_MIPMAPCOUNT = 0x00020000, - DDPF_FOURCC = 0x00000004, + DDSD_CAPS = 0x1, + DDSD_HEIGHT = 0x2, + DDSD_WIDTH = 0x4, + DDSD_PIXELFORMAT = 0x1000, DDPF_ALPHAPIXELS = 0x00000001, - DDPF_INDEXED = 0x00000020, + DDPF_ALPHAONLY = 0x00000002, + DDPF_FOURCC = 0x00000004, DDPF_RGB = 0x00000040, }; +enum DDSFourCC { + DDFCC_DXT1 = PF_FOURCC("DXT1"), + DDFCC_DXT2 = PF_FOURCC("DXT2"), + DDFCC_DXT3 = PF_FOURCC("DXT3"), + DDFCC_DXT4 = PF_FOURCC("DXT4"), + DDFCC_DXT5 = PF_FOURCC("DXT5"), + DDFCC_ATI1 = PF_FOURCC("ATI1"), + DDFCC_BC4U = PF_FOURCC("BC4U"), + DDFCC_ATI2 = PF_FOURCC("ATI2"), + DDFCC_BC5U = PF_FOURCC("BC5U"), + DDFCC_A2XY = PF_FOURCC("A2XY"), + DDFCC_DX10 = PF_FOURCC("DX10"), + DDFCC_R16F = 111, + DDFCC_RG16F = 112, + DDFCC_RGBA16F = 113, + DDFCC_R32F = 114, + DDFCC_RG32F = 115, + DDFCC_RGBA32F = 116, +}; + +// Reference: https://learn.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format +enum DXGIFormat { + DXGI_R32G32B32A32_FLOAT = 2, + DXGI_R32G32B32_FLOAT = 6, + DXGI_R16G16B16A16_FLOAT = 10, + DXGI_R32G32_FLOAT = 16, + DXGI_R10G10B10A2_UNORM = 24, + DXGI_R8G8B8A8_UNORM = 28, + DXGI_R8G8B8A8_UNORM_SRGB = 29, + DXGI_R16G16_FLOAT = 34, + DXGI_R32_FLOAT = 41, + DXGI_R8G8_UNORM = 49, + DXGI_R16_FLOAT = 54, + DXGI_R8_UNORM = 61, + DXGI_A8_UNORM = 65, + DXGI_R9G9B9E5 = 67, + DXGI_BC1_UNORM = 71, + DXGI_BC1_UNORM_SRGB = 72, + DXGI_BC2_UNORM = 74, + DXGI_BC2_UNORM_SRGB = 75, + DXGI_BC3_UNORM = 77, + DXGI_BC3_UNORM_SRGB = 78, + DXGI_BC4_UNORM = 80, + DXGI_BC5_UNORM = 83, + DXGI_B5G6R5_UNORM = 85, + DXGI_B5G5R5A1_UNORM = 86, + DXGI_B8G8R8A8_UNORM = 87, + DXGI_BC6H_UF16 = 95, + DXGI_BC6H_SF16 = 96, + DXGI_BC7_UNORM = 98, + DXGI_BC7_UNORM_SRGB = 99, + DXGI_B4G4R4A4_UNORM = 115, +}; + +// The legacy bitmasked format names here represent the actual data layout in the files, +// while their official names are flipped (e.g. RGBA8 layout is officially called ABGR8). enum DDSFormat { DDS_DXT1, DDS_DXT3, DDS_DXT5, DDS_ATI1, DDS_ATI2, - DDS_A2XY, - DDS_BGRA8, + DDS_BC6U, + DDS_BC6S, + DDS_BC7, + DDS_R16F, + DDS_RG16F, + DDS_RGBA16F, + DDS_R32F, + DDS_RG32F, + DDS_RGB32F, + DDS_RGBA32F, + DDS_RGB9E5, + DDS_RGB8, + DDS_RGBA8, + DDS_RGBX8, DDS_BGR8, - DDS_RGBA8, //flipped in dds - DDS_RGB8, //flipped in dds + DDS_BGRA8, + DDS_BGRX8, DDS_BGR5A1, DDS_BGR565, + DDS_B2GR3, + DDS_B2GR3A8, DDS_BGR10A2, + DDS_RGB10A2, + DDS_BGRA4, DDS_LUMINANCE, DDS_LUMINANCE_ALPHA, + DDS_LUMINANCE_ALPHA_4, DDS_MAX }; struct DDSFormatInfo { const char *name; bool compressed; - bool palette; uint32_t divisor; uint32_t block_size; Image::Format format; }; static const DDSFormatInfo dds_format_info[DDS_MAX] = { - { "DXT1/BC1", true, false, 4, 8, Image::FORMAT_DXT1 }, - { "DXT3/BC2", true, false, 4, 16, Image::FORMAT_DXT3 }, - { "DXT5/BC3", true, false, 4, 16, Image::FORMAT_DXT5 }, - { "ATI1/BC4", true, false, 4, 8, Image::FORMAT_RGTC_R }, - { "ATI2/3DC/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG }, - { "A2XY/DXN/BC5", true, false, 4, 16, Image::FORMAT_RGTC_RG }, - { "BGRA8", false, false, 1, 4, Image::FORMAT_RGBA8 }, - { "BGR8", false, false, 1, 3, Image::FORMAT_RGB8 }, - { "RGBA8", false, false, 1, 4, Image::FORMAT_RGBA8 }, - { "RGB8", false, false, 1, 3, Image::FORMAT_RGB8 }, - { "BGR5A1", false, false, 1, 2, Image::FORMAT_RGBA8 }, - { "BGR565", false, false, 1, 2, Image::FORMAT_RGB8 }, - { "BGR10A2", false, false, 1, 4, Image::FORMAT_RGBA8 }, - { "GRAYSCALE", false, false, 1, 1, Image::FORMAT_L8 }, - { "GRAYSCALE_ALPHA", false, false, 1, 2, Image::FORMAT_LA8 } + { "DXT1/BC1", true, 4, 8, Image::FORMAT_DXT1 }, + { "DXT2/DXT3/BC2", true, 4, 16, Image::FORMAT_DXT3 }, + { "DXT4/DXT5/BC3", true, 4, 16, Image::FORMAT_DXT5 }, + { "ATI1/BC4", true, 4, 8, Image::FORMAT_RGTC_R }, + { "ATI2/A2XY/BC5", true, 4, 16, Image::FORMAT_RGTC_RG }, + { "BC6UF", true, 4, 16, Image::FORMAT_BPTC_RGBFU }, + { "BC6SF", true, 4, 16, Image::FORMAT_BPTC_RGBF }, + { "BC7", true, 4, 16, Image::FORMAT_BPTC_RGBA }, + { "R16F", false, 1, 2, Image::FORMAT_RH }, + { "RG16F", false, 1, 4, Image::FORMAT_RGH }, + { "RGBA16F", false, 1, 8, Image::FORMAT_RGBAH }, + { "R32F", false, 1, 4, Image::FORMAT_RF }, + { "RG32F", false, 1, 8, Image::FORMAT_RGF }, + { "RGB32F", false, 1, 12, Image::FORMAT_RGBF }, + { "RGBA32F", false, 1, 16, Image::FORMAT_RGBAF }, + { "RGB9E5", false, 1, 4, Image::FORMAT_RGBE9995 }, + { "RGB8", false, 1, 3, Image::FORMAT_RGB8 }, + { "RGBA8", false, 1, 4, Image::FORMAT_RGBA8 }, + { "RGBX8", false, 1, 4, Image::FORMAT_RGBA8 }, + { "BGR8", false, 1, 3, Image::FORMAT_RGB8 }, + { "BGRA8", false, 1, 4, Image::FORMAT_RGBA8 }, + { "BGRX8", false, 1, 4, Image::FORMAT_RGBA8 }, + { "BGR5A1", false, 1, 2, Image::FORMAT_RGBA8 }, + { "BGR565", false, 1, 2, Image::FORMAT_RGB8 }, + { "B2GR3", false, 1, 1, Image::FORMAT_RGB8 }, + { "B2GR3A8", false, 1, 2, Image::FORMAT_RGBA8 }, + { "BGR10A2", false, 1, 4, Image::FORMAT_RGBA8 }, + { "RGB10A2", false, 1, 4, Image::FORMAT_RGBA8 }, + { "BGRA4", false, 1, 2, Image::FORMAT_RGBA4444 }, + { "GRAYSCALE", false, 1, 1, Image::FORMAT_L8 }, + { "GRAYSCALE_ALPHA", false, 1, 2, Image::FORMAT_LA8 }, + { "GRAYSCALE_ALPHA_4", false, 1, 1, Image::FORMAT_LA8 }, }; +DDSFormat _dxgi_to_dds_format(uint32_t p_dxgi_format) { + switch (p_dxgi_format) { + case DXGI_R32G32B32A32_FLOAT: { + return DDS_RGBA32F; + } + case DXGI_R32G32B32_FLOAT: { + return DDS_RGB32F; + } + case DXGI_R16G16B16A16_FLOAT: { + return DDS_RGBA16F; + } + case DXGI_R32G32_FLOAT: { + return DDS_RG32F; + } + case DXGI_R10G10B10A2_UNORM: { + return DDS_RGB10A2; + } + case DXGI_R8G8B8A8_UNORM: + case DXGI_R8G8B8A8_UNORM_SRGB: { + return DDS_RGBA8; + } + case DXGI_R16G16_FLOAT: { + return DDS_RG16F; + } + case DXGI_R32_FLOAT: { + return DDS_R32F; + } + case DXGI_R8_UNORM: + case DXGI_A8_UNORM: { + return DDS_LUMINANCE; + } + case DXGI_R16_FLOAT: { + return DDS_R16F; + } + case DXGI_R8G8_UNORM: { + return DDS_LUMINANCE_ALPHA; + } + case DXGI_R9G9B9E5: { + return DDS_RGB9E5; + } + case DXGI_BC1_UNORM: + case DXGI_BC1_UNORM_SRGB: { + return DDS_DXT1; + } + case DXGI_BC2_UNORM: + case DXGI_BC2_UNORM_SRGB: { + return DDS_DXT3; + } + case DXGI_BC3_UNORM: + case DXGI_BC3_UNORM_SRGB: { + return DDS_DXT5; + } + case DXGI_BC4_UNORM: { + return DDS_ATI1; + } + case DXGI_BC5_UNORM: { + return DDS_ATI2; + } + case DXGI_B5G6R5_UNORM: { + return DDS_BGR565; + } + case DXGI_B5G5R5A1_UNORM: { + return DDS_BGR5A1; + } + case DXGI_B8G8R8A8_UNORM: { + return DDS_BGRA8; + } + case DXGI_BC6H_UF16: { + return DDS_BC6U; + } + case DXGI_BC6H_SF16: { + return DDS_BC6S; + } + case DXGI_BC7_UNORM: + case DXGI_BC7_UNORM_SRGB: { + return DDS_BC7; + } + case DXGI_B4G4R4A4_UNORM: { + return DDS_BGRA4; + } + + default: { + return DDS_MAX; + } + } +} + RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_no_subresource_cache) { if (r_error) { *r_error = ERR_CANT_OPEN; @@ -120,16 +302,14 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, /* uint32_t depth = */ f->get_32(); uint32_t mipmaps = f->get_32(); - //skip 11 + // Skip 11. for (int i = 0; i < 11; i++) { f->get_32(); } - //validate - // We don't check DDSD_CAPS or DDSD_PIXELFORMAT, as they're mandatory when writing, // but non-mandatory when reading (as some writers don't set them)... - if (magic != DDS_MAGIC || hsize != 124) { + if (magic != DDS_MAGIC || hsize != DDS_HEADER_SIZE) { ERR_FAIL_V_MSG(RES(), "Invalid or unsupported DDS texture file '" + p_path + "'."); } @@ -146,64 +326,138 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, /* uint32_t caps_2 = */ f->get_32(); /* uint32_t caps_ddsx = */ f->get_32(); - //reserved skip + // Reserved skip. f->get_32(); f->get_32(); - /* - print_line("DDS width: "+itos(width)); - print_line("DDS height: "+itos(height)); - print_line("DDS mipmaps: "+itos(mipmaps)); - - printf("fourcc: %x fflags: %x, rgbbits: %x, fsize: %x\n",format_fourcc,format_flags,format_rgb_bits,format_size); - printf("rmask: %x gmask: %x, bmask: %x, amask: %x\n",format_red_mask,format_green_mask,format_blue_mask,format_alpha_mask); - */ - - //must avoid this later - while (f->get_position() < 128) { - f->get_8(); + if (f->get_position() < 128) { + f->seek(128); } - DDSFormat dds_format; + DDSFormat dds_format = DDS_MAX; - if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT1")) { - dds_format = DDS_DXT1; - } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT3")) { - dds_format = DDS_DXT3; + if (format_flags & DDPF_FOURCC) { + // FourCC formats. + switch (format_fourcc) { + case DDFCC_DXT1: { + dds_format = DDS_DXT1; + } break; + case DDFCC_DXT2: + case DDFCC_DXT3: { + dds_format = DDS_DXT3; + } break; + case DDFCC_DXT4: + case DDFCC_DXT5: { + dds_format = DDS_DXT5; + } break; + case DDFCC_ATI1: + case DDFCC_BC4U: { + dds_format = DDS_ATI1; + } break; + case DDFCC_ATI2: + case DDFCC_BC5U: + case DDFCC_A2XY: { + dds_format = DDS_ATI2; + } break; + case DDFCC_R16F: { + dds_format = DDS_R16F; + } break; + case DDFCC_RG16F: { + dds_format = DDS_RG16F; + } break; + case DDFCC_RGBA16F: { + dds_format = DDS_RGBA16F; + } break; + case DDFCC_R32F: { + dds_format = DDS_R32F; + } break; + case DDFCC_RG32F: { + dds_format = DDS_RG32F; + } break; + case DDFCC_RGBA32F: { + dds_format = DDS_RGBA32F; + } break; + case DDFCC_DX10: { + uint32_t dxgi_format = f->get_32(); + /* uint32_t dimension = */ f->get_32(); + /* uint32_t misc_flags_1 = */ f->get_32(); + /* uint32_t array_size = */ f->get_32(); + /* uint32_t misc_flags_2 = */ f->get_32(); - } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("DXT5")) { - dds_format = DDS_DXT5; - } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI1")) { - dds_format = DDS_ATI1; - } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("ATI2")) { - dds_format = DDS_ATI2; - } else if (format_flags & DDPF_FOURCC && format_fourcc == PF_FOURCC("A2XY")) { - dds_format = DDS_A2XY; + dds_format = _dxgi_to_dds_format(dxgi_format); + } break; - } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff && format_alpha_mask == 0xff000000) { - dds_format = DDS_BGRA8; - } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff) { - dds_format = DDS_BGR8; - } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000 && format_alpha_mask == 0xff000000) { - dds_format = DDS_RGBA8; - } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 24 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000) { - dds_format = DDS_RGB8; + default: { + ERR_FAIL_V_MSG(RES(), vformat("Unrecognized or unsupported FourCC in DDS '%s'.", p_path)); + } + } + } else if (format_flags & DDPF_RGB) { + // Channel-bitmasked formats. + if (format_flags & DDPF_ALPHAPIXELS) { + // With alpha. + if (format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff && format_alpha_mask == 0xff000000) { + dds_format = DDS_BGRA8; + } else if (format_rgb_bits == 32 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000 && format_alpha_mask == 0xff000000) { + dds_format = DDS_RGBA8; + } else if (format_rgb_bits == 16 && format_red_mask == 0x00007c00 && format_green_mask == 0x000003e0 && format_blue_mask == 0x0000001f && format_alpha_mask == 0x00008000) { + dds_format = DDS_BGR5A1; + } else if (format_rgb_bits == 32 && format_red_mask == 0x3ff00000 && format_green_mask == 0xffc00 && format_blue_mask == 0x3ff && format_alpha_mask == 0xc0000000) { + dds_format = DDS_BGR10A2; + } else if (format_rgb_bits == 32 && format_red_mask == 0x3ff && format_green_mask == 0xffc00 && format_blue_mask == 0x3ff00000 && format_alpha_mask == 0xc0000000) { + dds_format = DDS_RGB10A2; + } else if (format_rgb_bits == 16 && format_red_mask == 0xf00 && format_green_mask == 0xf0 && format_blue_mask == 0xf && format_alpha_mask == 0xf000) { + dds_format = DDS_BGRA4; + } else if (format_rgb_bits == 16 && format_red_mask == 0xe0 && format_green_mask == 0x1c && format_blue_mask == 0x3 && format_alpha_mask == 0xff00) { + dds_format = DDS_B2GR3A8; + } + + } else { + // Without alpha. + if (format_rgb_bits == 24 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff) { + dds_format = DDS_BGR8; + } else if (format_rgb_bits == 24 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000) { + dds_format = DDS_RGB8; + } else if (format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) { + dds_format = DDS_BGR565; + } else if (format_rgb_bits == 8 && format_red_mask == 0xe0 && format_green_mask == 0x1c && format_blue_mask == 0x3) { + dds_format = DDS_B2GR3; + } else if (format_rgb_bits == 32 && format_red_mask == 0xff0000 && format_green_mask == 0xff00 && format_blue_mask == 0xff) { + dds_format = DDS_BGRX8; + } else if (format_rgb_bits == 32 && format_red_mask == 0xff && format_green_mask == 0xff00 && format_blue_mask == 0xff0000) { + dds_format = DDS_RGBX8; + } + } - } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 16 && format_red_mask == 0x00007c00 && format_green_mask == 0x000003e0 && format_blue_mask == 0x0000001f && format_alpha_mask == 0x00008000) { - dds_format = DDS_BGR5A1; - } else if (format_flags & DDPF_RGB && format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 32 && format_red_mask == 0x3ff00000 && format_green_mask == 0xffc00 && format_blue_mask == 0x3ff && format_alpha_mask == 0xc0000000) { - dds_format = DDS_BGR10A2; - } else if (format_flags & DDPF_RGB && !(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) { - dds_format = DDS_BGR565; - } else if (!(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 8 && format_red_mask == 0xff) { - dds_format = DDS_LUMINANCE; - } else if ((format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 16 && format_red_mask == 0xff && format_alpha_mask == 0xff00) { - dds_format = DDS_LUMINANCE_ALPHA; - } else if (format_flags & DDPF_INDEXED && format_rgb_bits == 8) { - dds_format = DDS_BGR565; } else { - printf("unrecognized fourcc %x format_flags: %x - rgbbits %i - red_mask %x green mask %x blue mask %x alpha mask %x\n", format_fourcc, format_flags, format_rgb_bits, format_red_mask, format_green_mask, format_blue_mask, format_alpha_mask); - ERR_FAIL_V_MSG(RES(), "Unrecognized or unsupported color layout in DDS '" + p_path + "'."); + // Other formats. + if (format_flags & DDPF_ALPHAONLY && format_rgb_bits == 8 && format_alpha_mask == 0xff) { + // Alpha only. + dds_format = DDS_LUMINANCE; + } + } + + // Depending on the writer, luminance formats may or may not have the DDPF_RGB or DDPF_LUMINANCE flags defined, + // so we check for these formats after everything else failed. + if (dds_format == DDS_MAX) { + if (format_flags & DDPF_ALPHAPIXELS) { + // With alpha. + if (format_rgb_bits == 16 && format_red_mask == 0xff && format_alpha_mask == 0xff00) { + dds_format = DDS_LUMINANCE_ALPHA; + } else if (format_rgb_bits == 8 && format_red_mask == 0xf && format_alpha_mask == 0xf0) { + dds_format = DDS_LUMINANCE_ALPHA_4; + } + + } else { + // Without alpha. + if (format_rgb_bits == 8 && format_red_mask == 0xff) { + dds_format = DDS_LUMINANCE; + } + } + } + + // No format detected, error. + if (dds_format == DDS_MAX) { + ERR_FAIL_V_MSG(RES(), vformat("Unrecognized or unsupported color layout in DDS '%s'.", p_path)); } if (!(flags & DDSD_MIPMAPCOUNT)) { @@ -217,80 +471,63 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, uint32_t h = height; if (info.compressed) { - //compressed bc + // BC compressed. + w += w % info.divisor; + h += h % info.divisor; + if (w != width) { + WARN_PRINT(vformat("%s: DDS width '%d' is not divisible by %d. This is not allowed as per the DDS specification, attempting to load anyway.", f->get_path(), width, info.divisor)); + } + if (h != height) { + WARN_PRINT(vformat("%s: DDS height '%d' is not divisible by %d. This is not allowed as per the DDS specification, attempting to load anyway.", f->get_path(), height, info.divisor)); + } - uint32_t size = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size; - ERR_FAIL_COND_V(size != pitch, RES()); - ERR_FAIL_COND_V(!(flags & DDSD_LINEARSIZE), RES()); + uint32_t size = MAX(1u, (w + 3) / 4) * MAX(1u, (h + 3) / 4) * info.block_size; + + if (flags & DDSD_LINEARSIZE) { + ERR_FAIL_COND_V_MSG(size != pitch, RES(), "DDS header flags specify that a linear size of the top-level image is present, but the specified size does not match the expected value."); + } else { + ERR_FAIL_COND_V_MSG(pitch != 0, RES(), "DDS header flags specify that no linear size will given for the top-level image, but a non-zero linear size value is present in the header."); + } for (uint32_t i = 1; i < mipmaps; i++) { w = MAX(1, w >> 1); h = MAX(1, h >> 1); - uint32_t bsize = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size; - //printf("%i x %i - block: %i\n",w,h,bsize); + + uint32_t bsize = MAX(1u, (w + 3) / 4) * MAX(1u, (h + 3) / 4) * info.block_size; size += bsize; } src_data.resize(size); PoolVector::Write wb = src_data.write(); f->get_buffer(wb.ptr(), size); - - } else if (info.palette) { - //indexed - ERR_FAIL_COND_V(!(flags & DDSD_PITCH), RES()); - ERR_FAIL_COND_V(format_rgb_bits != 8, RES()); - - uint32_t size = pitch * height; - ERR_FAIL_COND_V(size != width * height * info.block_size, RES()); - - uint8_t palette[256 * 4]; - f->get_buffer(palette, 256 * 4); - - int colsize = 3; - for (int i = 0; i < 256; i++) { - if (palette[i * 4 + 3] < 255) { - colsize = 4; - } - } - - int w2 = width; - int h2 = height; - - for (uint32_t i = 1; i < mipmaps; i++) { - w2 = (w2 + 1) >> 1; - h2 = (h2 + 1) >> 1; - size += w2 * h2 * info.block_size; - } - - src_data.resize(size + 256 * colsize); - PoolVector::Write wb = src_data.write(); - f->get_buffer(wb.ptr(), size); - - for (int i = 0; i < 256; i++) { - int dst_ofs = size + i * colsize; - int src_ofs = i * 4; - wb[dst_ofs + 0] = palette[src_ofs + 2]; - wb[dst_ofs + 1] = palette[src_ofs + 1]; - wb[dst_ofs + 2] = palette[src_ofs + 0]; - if (colsize == 4) { - wb[dst_ofs + 3] = palette[src_ofs + 3]; - } - } } else { - //uncompressed generic... - + // Generic uncompressed. uint32_t size = width * height * info.block_size; for (uint32_t i = 1; i < mipmaps; i++) { - w = (w + 1) >> 1; - h = (h + 1) >> 1; + w = MAX(1u, w >> 1); + h = MAX(1u, h >> 1); size += w * h * info.block_size; } - if (dds_format == DDS_BGR565) { - size = size * 3 / 2; - } else if (dds_format == DDS_BGR5A1) { - size = size * 2; + // Calculate the space these formats will take up after decoding. + switch (dds_format) { + case DDS_BGR5A1: + case DDS_B2GR3A8: + case DDS_LUMINANCE_ALPHA_4: + size = size * 2; + break; + + case DDS_B2GR3: + size = size * 3; + break; + + case DDS_BGR565: + size = size * 3 / 2; + break; + + default: + break; } src_data.resize(size); @@ -299,7 +536,7 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, switch (dds_format) { case DDS_BGR5A1: { - // TO RGBA + // To RGBA8. int colcount = size / 4; for (int i = colcount - 1; i >= 0; i--) { @@ -310,6 +547,7 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, uint8_t b = wb[src_ofs] & 0x1F; uint8_t g = (wb[src_ofs] >> 5) | ((wb[src_ofs + 1] & 0x3) << 3); uint8_t r = (wb[src_ofs + 1] >> 2) & 0x1F; + wb[dst_ofs + 0] = r << 3; wb[dst_ofs + 1] = g << 3; wb[dst_ofs + 2] = b << 3; @@ -328,19 +566,88 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, uint8_t r = wb[src_ofs + 1] >> 3; wb[dst_ofs + 0] = r << 3; wb[dst_ofs + 1] = g << 2; - wb[dst_ofs + 2] = b << 3; //b<<3; + wb[dst_ofs + 2] = b << 3; } - } break; - case DDS_BGR10A2: { - // TO RGBA + case DDS_BGRA4: { + // To RGBA4. + for (uint32_t i = 0; i < size; i += 2) { + uint8_t ar = wb[i + 0]; + uint8_t gb = wb[i + 1]; + + wb[i + 0] = ((ar & 0x0F) << 4) | ((gb & 0xF0) >> 4); + wb[i + 1] = ((ar & 0xF0) >> 4) | ((gb & 0x0F) << 4); + } + } break; + case DDS_B2GR3: { + // To RGB8. + int colcount = size / 3; + + for (int i = colcount - 1; i >= 0; i--) { + int src_ofs = i; + int dst_ofs = i * 3; + + uint8_t b = (wb[src_ofs] & 0x3) << 6; + uint8_t g = (wb[src_ofs] & 0x1C) << 3; + uint8_t r = (wb[src_ofs] & 0xE0); + + wb[dst_ofs] = r; + wb[dst_ofs + 1] = g; + wb[dst_ofs + 2] = b; + } + } break; + case DDS_B2GR3A8: { + // To RGBA8. int colcount = size / 4; for (int i = colcount - 1; i >= 0; i--) { + int src_ofs = i * 2; + int dst_ofs = i * 4; + + uint8_t b = (wb[src_ofs] & 0x3) << 6; + uint8_t g = (wb[src_ofs] & 0x1C) << 3; + uint8_t r = (wb[src_ofs] & 0xE0); + uint8_t a = wb[src_ofs + 1]; + + wb[dst_ofs] = r; + wb[dst_ofs + 1] = g; + wb[dst_ofs + 2] = b; + wb[dst_ofs + 3] = a; + } + } break; + case DDS_RGB10A2: { + // To RGBA8. + int colcount = size / 4; + + for (int i = 0; i < colcount; i++) { int ofs = i * 4; uint32_t w32 = uint32_t(wb[ofs + 0]) | (uint32_t(wb[ofs + 1]) << 8) | (uint32_t(wb[ofs + 2]) << 16) | (uint32_t(wb[ofs + 3]) << 24); + // This method follows the 'standard' way of decoding 10-bit dds files, + // which means the ones created with DirectXTex will be loaded incorrectly. + uint8_t a = (w32 & 0xc0000000) >> 24; + uint8_t r = (w32 & 0x3ff) >> 2; + uint8_t g = (w32 & 0xffc00) >> 12; + uint8_t b = (w32 & 0x3ff00000) >> 22; + + wb[ofs + 0] = r; + wb[ofs + 1] = g; + wb[ofs + 2] = b; + wb[ofs + 3] = a == 0xc0 ? 255 : a; // 0xc0 should be opaque. + } + } break; + case DDS_BGR10A2: { + // To RGBA8. + int colcount = size / 4; + + for (int i = 0; i < colcount; i++) { + int ofs = i * 4; + + uint32_t w32 = uint32_t(wb[ofs + 0]) | (uint32_t(wb[ofs + 1]) << 8) | (uint32_t(wb[ofs + 2]) << 16) | (uint32_t(wb[ofs + 3]) << 24); + + // This method follows the 'standard' way of decoding 10-bit dds files, + // which means the ones created with DirectXTex will be loaded incorrectly. uint8_t a = (w32 & 0xc0000000) >> 24; uint8_t r = (w32 & 0x3ff00000) >> 22; uint8_t g = (w32 & 0xffc00) >> 12; @@ -349,59 +656,61 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, wb[ofs + 0] = r; wb[ofs + 1] = g; wb[ofs + 2] = b; - wb[ofs + 3] = a == 0xc0 ? 255 : a; //0xc0 should be opaque + wb[ofs + 3] = a == 0xc0 ? 255 : a; // 0xc0 should be opaque. } } break; + + // Channel-swapped. case DDS_BGRA8: { + // To RGBA8. int colcount = size / 4; for (int i = 0; i < colcount; i++) { SWAP(wb[i * 4 + 0], wb[i * 4 + 2]); } - } break; case DDS_BGR8: { + // To RGB8. int colcount = size / 3; for (int i = 0; i < colcount; i++) { SWAP(wb[i * 3 + 0], wb[i * 3 + 2]); } } break; - case DDS_RGBA8: { - /* do nothing either - int colcount = size/4; - for(int i=0;i= 0; i--) { + int src_ofs = i; + int dst_ofs = i * 2; + + uint8_t l = wb[src_ofs] & 0x0F; + uint8_t a = wb[src_ofs] & 0xF0; + + wb[dst_ofs] = (l << 4) | l; + wb[dst_ofs + 1] = a | (a >> 4); + } } break; default: {