commit aca51cdd616eb04e4ed94d96d12c6d8fae207ccb Author: sean <43609023+spnda@users.noreply.github.com> Date: Tue Oct 19 03:48:18 2021 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..44098c1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +cmake-build-debug/* +cmake-build-release/* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..738f30e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.0) + +project(dds_image LANGUAGES CXX) + +add_library(dds_image INTERFACE) +target_include_directories(dds_image INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c6ebc87 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2021 spnda. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..37d4142 --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# dds_image + +A C++11 single-header DDS (DirectDraw Surface) image library, +with no dependencies. +It also includes utilities to work with Vulkan, as well +as optional support for C++17 specific features. +You can quickly add this as a submodule and add the header files, +or import it as a subdirectory in CMake. + +### Example + +```cpp +#include + +dds::Image image; +dds::readFile("example.dds", &image); +``` + +On MSVC you might need to build with `/Zc:__cplusplus` for the built-in C++17 features to work. +You can then optionally also define `DDS_USE_STD_FILESYSTEM`, which will make `dds::readFile` accept +a `std::filesystem::path` instead. + +### Vulkan Usage + +If you want to use the built-in Vulkan features, you **have** to include +the Vulkan header before you import this library. +To create the VkImage and VkImageView you can use the following two functions. +Note that you still need to set some fields yourself, most notably `VkImageCreateInfo::usage`, +`VkImageCreateInfo::samples` and `VkImageViewCreateInfo::image`. + +```cpp +#include // Before! +#include +``` +```cpp +// Optional, for own usage. +VkImageFormat imageFormat = dds::getVulkanFormat(image.format, image.supportsAlpha); + +// Will automatically fill VkImageCreateInfo::format with a separate call to dds::getVulkanFormat. +VkImageCreateInfo imageCreateInfo = dds::getVulkanImageCreateInfo(&image); +VkImageViewCreateInfo imageViewCreateInfo = dds::getVulkanImageViewCreateInfo(&image); +``` diff --git a/include/dds.hpp b/include/dds.hpp new file mode 100644 index 0000000..95a3de8 --- /dev/null +++ b/include/dds.hpp @@ -0,0 +1,346 @@ +#pragma once + +#if __cplusplus > 201703L && defined(DDS_USE_STD_FILESYSTEM) +#include +namespace fs = std::filesystem; +#endif + +#include +#include + +#include "dds_formats.hpp" + +#if __cplusplus > 201703L +#define NO_DISCARD [[nodiscard]] +#else +#define NO_DISCARD +#endif + +namespace dds { + NO_DISCARD inline uint32_t getBlockSize(const DXGI_FORMAT format) { + switch (format) { + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + return 8; + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + return 16; + default: + return 0; + } + } + + NO_DISCARD inline uint32_t getBitsPerPixel(const DXGI_FORMAT format) { + switch (format) { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + return 128; + + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R32G32B32_FLOAT: + case DXGI_FORMAT_R32G32B32_UINT: + case DXGI_FORMAT_R32G32B32_SINT: + return 96; + + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + case DXGI_FORMAT_R32G32_TYPELESS: + case DXGI_FORMAT_R32G32_FLOAT: + case DXGI_FORMAT_R32G32_UINT: + case DXGI_FORMAT_R32G32_SINT: + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: + return 64; + + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R11G11B10_FLOAT: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + case DXGI_FORMAT_R16G16_TYPELESS: + case DXGI_FORMAT_R16G16_FLOAT: + case DXGI_FORMAT_R16G16_UNORM: + case DXGI_FORMAT_R16G16_UINT: + case DXGI_FORMAT_R16G16_SNORM: + case DXGI_FORMAT_R16G16_SINT: + case DXGI_FORMAT_R32_TYPELESS: + case DXGI_FORMAT_D32_FLOAT: + case DXGI_FORMAT_R32_FLOAT: + case DXGI_FORMAT_R32_UINT: + case DXGI_FORMAT_R32_SINT: + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_D24_UNORM_S8_UINT: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_X24_TYPELESS_G8_UINT: + case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: + case DXGI_FORMAT_R8G8_B8G8_UNORM: + case DXGI_FORMAT_G8R8_G8B8_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_B8G8R8X8_UNORM: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: + return 32; + + case DXGI_FORMAT_R8G8_TYPELESS: + case DXGI_FORMAT_R8G8_UNORM: + case DXGI_FORMAT_R8G8_UINT: + case DXGI_FORMAT_R8G8_SNORM: + case DXGI_FORMAT_R8G8_SINT: + case DXGI_FORMAT_R16_TYPELESS: + case DXGI_FORMAT_R16_FLOAT: + case DXGI_FORMAT_D16_UNORM: + case DXGI_FORMAT_R16_UNORM: + case DXGI_FORMAT_R16_UINT: + case DXGI_FORMAT_R16_SNORM: + case DXGI_FORMAT_R16_SINT: + case DXGI_FORMAT_B5G6R5_UNORM: + case DXGI_FORMAT_B5G5R5A1_UNORM: + case DXGI_FORMAT_B4G4R4A4_UNORM: + return 16; + + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_R8_UNORM: + case DXGI_FORMAT_R8_UINT: + case DXGI_FORMAT_R8_SNORM: + case DXGI_FORMAT_R8_SINT: + case DXGI_FORMAT_A8_UNORM: + return 8; + + case DXGI_FORMAT_R1_UNORM: + return 1; + + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC4_UNORM: + case DXGI_FORMAT_BC4_SNORM: + return 4; + + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_BC5_UNORM: + case DXGI_FORMAT_BC5_SNORM: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC6H_UF16: + case DXGI_FORMAT_BC6H_SF16: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: + return 8; + default: + return 0; + } + } + +#ifdef VK_VERSION_1_0 + NO_DISCARD inline VkFormat getVulkanFormat(DXGI_FORMAT format, const bool alphaFlag) { + switch (format) { + case DXGI_FORMAT_BC1_UNORM: { + if (alphaFlag) return VK_FORMAT_BC1_RGBA_UNORM_BLOCK; + else return VK_FORMAT_BC1_RGB_UNORM_BLOCK; + } + case DXGI_FORMAT_BC1_UNORM_SRGB: { + if (alphaFlag) return VK_FORMAT_BC1_RGBA_SRGB_BLOCK; + else return VK_FORMAT_BC1_RGB_SRGB_BLOCK; + } + + case DXGI_FORMAT_BC2_UNORM: return VK_FORMAT_BC2_UNORM_BLOCK; + case DXGI_FORMAT_BC2_UNORM_SRGB: return VK_FORMAT_BC2_SRGB_BLOCK; + case DXGI_FORMAT_BC3_UNORM: return VK_FORMAT_BC3_UNORM_BLOCK; + case DXGI_FORMAT_BC3_UNORM_SRGB: return VK_FORMAT_BC3_SRGB_BLOCK; + case DXGI_FORMAT_BC4_UNORM: return VK_FORMAT_BC4_UNORM_BLOCK; + case DXGI_FORMAT_BC4_SNORM: return VK_FORMAT_BC4_SNORM_BLOCK; + case DXGI_FORMAT_BC5_UNORM: return VK_FORMAT_BC5_UNORM_BLOCK; + case DXGI_FORMAT_BC5_SNORM: return VK_FORMAT_BC5_SNORM_BLOCK; + + case DXGI_FORMAT_R8G8B8A8_UNORM: return VK_FORMAT_R8G8B8A8_UNORM; + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: return VK_FORMAT_R8G8B8A8_SRGB; + case DXGI_FORMAT_R8G8B8A8_UINT: return VK_FORMAT_R8G8B8A8_UINT; + case DXGI_FORMAT_R8G8B8A8_SNORM: return VK_FORMAT_R8G8B8A8_SNORM; + case DXGI_FORMAT_R8G8B8A8_SINT: return VK_FORMAT_R8G8B8A8_SINT; + case DXGI_FORMAT_B8G8R8A8_UNORM: return VK_FORMAT_B8G8R8A8_UNORM; + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: return VK_FORMAT_B8G8R8A8_SRGB; + + case DXGI_FORMAT_R16G16B16A16_FLOAT: return VK_FORMAT_R16G16B16A16_SFLOAT; + case DXGI_FORMAT_R16G16B16A16_SINT: return VK_FORMAT_R16G16B16A16_SINT; + case DXGI_FORMAT_R16G16B16A16_UINT: return VK_FORMAT_R16G16B16A16_UINT; + case DXGI_FORMAT_R16G16B16A16_UNORM: return VK_FORMAT_R16G16B16A16_UNORM; + case DXGI_FORMAT_R16G16B16A16_SNORM: return VK_FORMAT_R16G16B16A16_SNORM; + + case DXGI_FORMAT_R8G8_B8G8_UNORM: + case DXGI_FORMAT_G8R8_G8B8_UNORM: + case DXGI_FORMAT_YUY2: + default: + return VK_FORMAT_UNDEFINED; + } + } + + NO_DISCARD inline VkImageCreateInfo getVulkanImageCreateInfo(dds::Image* image) { + VkImageCreateInfo imageInfo = {}; + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + switch (image->dimension) { + case Texture1D: imageInfo.imageType = VK_IMAGE_TYPE_1D; break; + case Texture2D: imageInfo.imageType = VK_IMAGE_TYPE_2D; break; + case Texture3D: imageInfo.imageType = VK_IMAGE_TYPE_3D; break; + default: break; + } + imageInfo.format = getVulkanFormat(image->format, image->supportsAlpha); + imageInfo.extent.width = image->width; + imageInfo.extent.height = image->height; + imageInfo.extent.depth = image->depth; + imageInfo.mipLevels = image->numMips; + imageInfo.arrayLayers = image->arraySize; + return imageInfo; + } + + NO_DISCARD inline VkImageViewCreateInfo getVulkanImageViewCreateInfo(dds::Image* image) { + VkImageViewCreateInfo imageViewInfo = {}; + imageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewInfo.format = getVulkanFormat(image->format, image->supportsAlpha); + imageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageViewInfo.subresourceRange.baseMipLevel = 0; + imageViewInfo.subresourceRange.levelCount = image->numMips; + imageViewInfo.subresourceRange.baseArrayLayer = 0; + imageViewInfo.subresourceRange.layerCount = image->arraySize; + return imageViewInfo; + } +#endif // #ifdef VK_VERSION_1_0 + +#if __cplusplus > 201703L && defined(DDS_USE_STD_FILESYSTEM) + NO_DISCARD inline dds::ReadResult readFile(const fs::path& filepath, dds::Image* image) { +#else + NO_DISCARD inline dds::ReadResult readFile(const std::string& filepath, dds::Image* image) { +#endif + std::ifstream filestream(filepath, std::ios::binary | std::ios::in); + if (!filestream.is_open()) + return dds::ReadResult::Failure; + + // Read the file into a vector. + filestream.seekg(0, std::ios::end); + size_t fileSize = filestream.tellg(); + image->data.resize(fileSize); + filestream.seekg(0); + filestream.read(image->data.data(), static_cast(fileSize)); + + // Read the magic number + auto* ptr = image->data.data(); + auto* ddsMagic = reinterpret_cast(ptr); + ptr += 4; + + // Read the header + if (fileSize < sizeof(dds::FileHeader)) + return dds::ReadResult::Failure; + const auto* header = reinterpret_cast(ptr); + ptr += sizeof(dds::FileHeader); + + // Validate header + if (*ddsMagic != dds::DdsMagicNumber::DDS) + return dds::ReadResult::Failure; + +// UNDO_FOUR_CHARACTER_CODE(header->pixelFormat.fourCC, fourCCStr); +// std::cout << fourCCStr << std::endl; + if (header->pixelFormat.flags & PixelFormatFlags::FourCC) { + const dds::Dx10Header* additionalHeader; + switch (header->pixelFormat.fourCC) { + case dds::DdsMagicNumber::DX10: { + additionalHeader = reinterpret_cast(ptr); + ptr += sizeof(dds::Dx10Header); + + image->arraySize = additionalHeader->arraySize; + image->format = additionalHeader->dxgiFormat; + image->dimension = additionalHeader->resourceDimension; + break; + } + case DXT1: image->format = DXGI_FORMAT_BC1_UNORM; break; + case DXT2: case DXT3: image->format = DXGI_FORMAT_BC2_UNORM; break; + case DXT4: case DXT5: image->format = DXGI_FORMAT_BC3_UNORM; break; + case ATI1: case BC4U: image->format = DXGI_FORMAT_BC4_UNORM; break; + case BC4S: image->format = DXGI_FORMAT_BC4_SNORM; break; + case ATI2: case BC5U: image->format = DXGI_FORMAT_BC5_UNORM; break; + case BC5S: image->format = DXGI_FORMAT_BC5_SNORM; break; + default: + return ReadResult::UnsupportedFormat; + } + } else { + if (header->pixelFormat.flags & PixelFormatFlags::RGB) { + + } + } + + if (header->flags & HeaderFlags::Volume || header->caps2 & Caps2Flags::Cubemap) { + image->dimension = Texture3D; + } else { + image->dimension = Texture2D; + } + + const auto blockSizeBytes = getBlockSize(image->format); + const auto pixelSizeBits = getBitsPerPixel(image->format); + if (!blockSizeBytes && !pixelSizeBits) + return dds::ReadResult::UnsupportedFormat; + + // Read the image fileSize + uint32_t totalOffset = 0; + { + auto width = header->width; + auto height = header->height; + for (uint32_t mip = 0; mip < header->mipmapCount; ++mip) { + uint32_t surfaceSize; + if (blockSizeBytes) { + auto temp = ((width + 3) / 4); + surfaceSize = ((1 < temp) ? temp : 1) * blockSizeBytes; + } else { + const auto pitch = ((width * pixelSizeBits) + 7) / 8; // Divide by 8 for byte alignment. + surfaceSize = pitch * height; + } + totalOffset += surfaceSize; + + // Not going to include just for std::max. + uint32_t halfWidth = width / 2; + width = (1u < halfWidth) ? halfWidth : 1u; + uint32_t halfHeight = height / 2; + height = (1u < halfHeight) ? halfHeight : 1u; + } + } + const auto ddsSize = ptr - fileSize + size_t(totalOffset); + // if (ddsSize > fileSize || ddsSize < fileSize) + // return dds::ReadResult::InvalidSize; + + image->numMips = header->mipmapCount; + image->width = header->width; + image->height = header->height; + image->supportsAlpha = header->hasAlphaFlag(); + + // Close the file and return success + filestream.close(); + return dds::ReadResult::Success; + } +} diff --git a/include/dds_formats.hpp b/include/dds_formats.hpp new file mode 100644 index 0000000..6ae63dc --- /dev/null +++ b/include/dds_formats.hpp @@ -0,0 +1,268 @@ +#pragma once + +#include + +#define MAKE_FOUR_CHARACTER_CODE(char1, char2, char3, char4) \ + static_cast(char1) | \ + (static_cast(char2) << 8) | \ + (static_cast(char3) << 16) | \ + (static_cast(char4) << 24) + +#define UNDO_FOUR_CHARACTER_CODE(x, name) \ + std::string name; \ + name.reserve(4); \ + name.push_back(x >> 0); \ + name.push_back(x >> 8); \ + name.push_back(x >> 16); \ + name.push_back(x >> 24); \ + +#ifndef __dxgiformat_h__ // We'll try and act as if we're the actual dxgiformat.h header. +#define __dxgiformat_h__ +// See https://docs.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format +enum DXGI_FORMAT { + DXGI_FORMAT_UNKNOWN = 0, + DXGI_FORMAT_R32G32B32A32_TYPELESS = 1, + DXGI_FORMAT_R32G32B32A32_FLOAT = 2, + DXGI_FORMAT_R32G32B32A32_UINT = 3, + DXGI_FORMAT_R32G32B32A32_SINT = 4, + DXGI_FORMAT_R32G32B32_TYPELESS = 5, + DXGI_FORMAT_R32G32B32_FLOAT = 6, + DXGI_FORMAT_R32G32B32_UINT = 7, + DXGI_FORMAT_R32G32B32_SINT = 8, + DXGI_FORMAT_R16G16B16A16_TYPELESS = 9, + DXGI_FORMAT_R16G16B16A16_FLOAT = 10, + DXGI_FORMAT_R16G16B16A16_UNORM = 11, + DXGI_FORMAT_R16G16B16A16_UINT = 12, + DXGI_FORMAT_R16G16B16A16_SNORM = 13, + DXGI_FORMAT_R16G16B16A16_SINT = 14, + DXGI_FORMAT_R32G32_TYPELESS = 15, + DXGI_FORMAT_R32G32_FLOAT = 16, + DXGI_FORMAT_R32G32_UINT = 17, + DXGI_FORMAT_R32G32_SINT = 18, + DXGI_FORMAT_R32G8X24_TYPELESS = 19, + DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20, + DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21, + DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22, + DXGI_FORMAT_R10G10B10A2_TYPELESS = 23, + DXGI_FORMAT_R10G10B10A2_UNORM = 24, + DXGI_FORMAT_R10G10B10A2_UINT = 25, + DXGI_FORMAT_R11G11B10_FLOAT = 26, + DXGI_FORMAT_R8G8B8A8_TYPELESS = 27, + DXGI_FORMAT_R8G8B8A8_UNORM = 28, + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29, + DXGI_FORMAT_R8G8B8A8_UINT = 30, + DXGI_FORMAT_R8G8B8A8_SNORM = 31, + DXGI_FORMAT_R8G8B8A8_SINT = 32, + DXGI_FORMAT_R16G16_TYPELESS = 33, + DXGI_FORMAT_R16G16_FLOAT = 34, + DXGI_FORMAT_R16G16_UNORM = 35, + DXGI_FORMAT_R16G16_UINT = 36, + DXGI_FORMAT_R16G16_SNORM = 37, + DXGI_FORMAT_R16G16_SINT = 38, + DXGI_FORMAT_R32_TYPELESS = 39, + DXGI_FORMAT_D32_FLOAT = 40, + DXGI_FORMAT_R32_FLOAT = 41, + DXGI_FORMAT_R32_UINT = 42, + DXGI_FORMAT_R32_SINT = 43, + DXGI_FORMAT_R24G8_TYPELESS = 44, + DXGI_FORMAT_D24_UNORM_S8_UINT = 45, + DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46, + DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47, + DXGI_FORMAT_R8G8_TYPELESS = 48, + DXGI_FORMAT_R8G8_UNORM = 49, + DXGI_FORMAT_R8G8_UINT = 50, + DXGI_FORMAT_R8G8_SNORM = 51, + DXGI_FORMAT_R8G8_SINT = 52, + DXGI_FORMAT_R16_TYPELESS = 53, + DXGI_FORMAT_R16_FLOAT = 54, + DXGI_FORMAT_D16_UNORM = 55, + DXGI_FORMAT_R16_UNORM = 56, + DXGI_FORMAT_R16_UINT = 57, + DXGI_FORMAT_R16_SNORM = 58, + DXGI_FORMAT_R16_SINT = 59, + DXGI_FORMAT_R8_TYPELESS = 60, + DXGI_FORMAT_R8_UNORM = 61, + DXGI_FORMAT_R8_UINT = 62, + DXGI_FORMAT_R8_SNORM = 63, + DXGI_FORMAT_R8_SINT = 64, + DXGI_FORMAT_A8_UNORM = 65, + DXGI_FORMAT_R1_UNORM = 66, + DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67, + DXGI_FORMAT_R8G8_B8G8_UNORM = 68, + DXGI_FORMAT_G8R8_G8B8_UNORM = 69, + DXGI_FORMAT_BC1_TYPELESS = 70, + DXGI_FORMAT_BC1_UNORM = 71, + DXGI_FORMAT_BC1_UNORM_SRGB = 72, + DXGI_FORMAT_BC2_TYPELESS = 73, + DXGI_FORMAT_BC2_UNORM = 74, + DXGI_FORMAT_BC2_UNORM_SRGB = 75, + DXGI_FORMAT_BC3_TYPELESS = 76, + DXGI_FORMAT_BC3_UNORM = 77, + DXGI_FORMAT_BC3_UNORM_SRGB = 78, + DXGI_FORMAT_BC4_TYPELESS = 79, + DXGI_FORMAT_BC4_UNORM = 80, + DXGI_FORMAT_BC4_SNORM = 81, + DXGI_FORMAT_BC5_TYPELESS = 82, + DXGI_FORMAT_BC5_UNORM = 83, + DXGI_FORMAT_BC5_SNORM = 84, + DXGI_FORMAT_B5G6R5_UNORM = 85, + DXGI_FORMAT_B5G5R5A1_UNORM = 86, + DXGI_FORMAT_B8G8R8A8_UNORM = 87, + DXGI_FORMAT_B8G8R8X8_UNORM = 88, + DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89, + DXGI_FORMAT_B8G8R8A8_TYPELESS = 90, + DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91, + DXGI_FORMAT_B8G8R8X8_TYPELESS = 92, + DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93, + DXGI_FORMAT_BC6H_TYPELESS = 94, + DXGI_FORMAT_BC6H_UF16 = 95, + DXGI_FORMAT_BC6H_SF16 = 96, + DXGI_FORMAT_BC7_TYPELESS = 97, + DXGI_FORMAT_BC7_UNORM = 98, + DXGI_FORMAT_BC7_UNORM_SRGB = 99, + DXGI_FORMAT_AYUV = 100, + DXGI_FORMAT_Y410 = 101, + DXGI_FORMAT_Y416 = 102, + DXGI_FORMAT_NV12 = 103, + DXGI_FORMAT_P010 = 104, + DXGI_FORMAT_P016 = 105, + DXGI_FORMAT_420_OPAQUE = 106, + DXGI_FORMAT_YUY2 = 107, + DXGI_FORMAT_Y210 = 108, + DXGI_FORMAT_Y216 = 109, + DXGI_FORMAT_NV11 = 110, + DXGI_FORMAT_AI44 = 111, + DXGI_FORMAT_IA44 = 112, + DXGI_FORMAT_P8 = 113, + DXGI_FORMAT_A8P8 = 114, + DXGI_FORMAT_B4G4R4A4_UNORM = 115, + DXGI_FORMAT_P208 = 130, + DXGI_FORMAT_V208 = 131, + DXGI_FORMAT_V408 = 132, + DXGI_FORMAT_FORCE_UINT = 0xffffffff +}; +#endif // #ifndef __dxgiformat_h__ + +namespace dds { + enum ResourceDimension { + Unknown, + Buffer, + Texture1D, + Texture2D, + Texture3D + }; + + enum ReadResult { + Success = 0, + Failure = -1, + UnsupportedFormat = -2, + NoDx10Header = -3, + InvalidSize = -4, + }; + + enum DdsMagicNumber { + DDS = MAKE_FOUR_CHARACTER_CODE('D', 'D', 'S', ' '), + + DXT1 = MAKE_FOUR_CHARACTER_CODE('D', 'X', 'T', '1'), // BC1_UNORM + DXT2 = MAKE_FOUR_CHARACTER_CODE('D', 'X', 'T', '2'), // BC2_UNORM + DXT3 = MAKE_FOUR_CHARACTER_CODE('D', 'X', 'T', '3'), // BC2_UNORM + DXT4 = MAKE_FOUR_CHARACTER_CODE('D', 'X', 'T', '4'), // BC3_UNORM + DXT5 = MAKE_FOUR_CHARACTER_CODE('D', 'X', 'T', '5'), // BC3_UNORM + ATI1 = MAKE_FOUR_CHARACTER_CODE('A', 'T', 'I', '1'), // BC4_UNORM + BC4U = MAKE_FOUR_CHARACTER_CODE('B', 'C', '4', 'U'), // BC4_UNORM + BC4S = MAKE_FOUR_CHARACTER_CODE('B', 'C', '4', 'S'), // BC4_SNORM + ATI2 = MAKE_FOUR_CHARACTER_CODE('A', 'T', 'I', '2'), // BC5_UNORM + BC5U = MAKE_FOUR_CHARACTER_CODE('B', 'C', '5', 'U'), // BC5_UNORM + BC5S = MAKE_FOUR_CHARACTER_CODE('B', 'C', '5', 'S'), // BC5_SNORM + RGBG = MAKE_FOUR_CHARACTER_CODE('R', 'G', 'B', 'G'), // R8G8_B8G8_UNORM + GRBG = MAKE_FOUR_CHARACTER_CODE('G', 'R', 'B', 'G'), // G8R8_G8B8_UNORM + YUY2 = MAKE_FOUR_CHARACTER_CODE('Y', 'U', 'Y', '2'), // YUY2 + UYVY = MAKE_FOUR_CHARACTER_CODE('U', 'Y', 'V', 'Y'), + + DX10 = MAKE_FOUR_CHARACTER_CODE('D', 'X', '1', '0'), + }; + + enum PixelFormatFlags : uint32_t { + AlphaPixels = 0x1, + Alpha = 0x2, + FourCC = 0x4, + PAL8 = 0x20, + RGB = 0x40, + RGBA = RGB | AlphaPixels, + YUV = 0x200, + Luminance = 0x20000, + LuminanceA = Luminance | AlphaPixels, + }; + + struct FilePixelFormat { + uint32_t size; + PixelFormatFlags flags; + uint32_t fourCC; + uint32_t bitCount; + uint32_t rBitMask; + uint32_t gBitMask; + uint32_t bBitMask; + uint32_t aBitMask; + }; + + enum HeaderFlags : uint32_t { + Caps = 0x1, + Height = 0x2, + Width = 0x4, + Pitch = 0x8, + PixelFormat = 0x1000, + Texture = Caps | Height | Width | PixelFormat, + Mipmap = 0x20000, + Volume = 0x800000, + LinearSize = 0x00080000, + }; + + enum Caps2Flags : uint32_t { + Cubemap = 0x200, + }; + + struct FileHeader { + uint32_t size; + HeaderFlags flags; + uint32_t height; + uint32_t width; + uint32_t pitch; + uint32_t depth; + uint32_t mipmapCount; + uint32_t reserved[11]; + FilePixelFormat pixelFormat; + uint32_t caps1; + uint32_t caps2; + uint32_t caps3; + uint32_t caps4; + uint32_t reserved2; + + bool hasAlphaFlag() const; + }; + static_assert(sizeof(FileHeader) == 124, "DDS Header size mismatch. Must be 124 bytes."); + + bool FileHeader::hasAlphaFlag() const { + return !!(pixelFormat.flags & PixelFormatFlags::AlphaPixels); + } + + /** An additional header for DX10 */ + struct Dx10Header { + DXGI_FORMAT dxgiFormat; + ResourceDimension resourceDimension; + uint32_t miscFlags; + uint32_t arraySize; + uint32_t miscFlags2; + }; + + struct Image { + uint32_t numMips; + uint32_t arraySize = 1; + uint32_t width = 1; + uint32_t height = 1; + uint32_t depth = 1; + ResourceDimension dimension; + bool supportsAlpha = false; + DXGI_FORMAT format; + std::vector data = {}; + }; +}