From 344320a19d87c887a0a568abcf5aabf04589d9ca Mon Sep 17 00:00:00 2001 From: bjorn Date: Sat, 22 Jul 2017 03:42:05 -0700 Subject: [PATCH] Parse DDS; --- src/lib/dds.h | 199 ++++++++++++++++++++++++++++++++++++++++++ src/loaders/texture.c | 113 ++++++++++++++++++++++++ src/loaders/texture.h | 8 ++ 3 files changed, 320 insertions(+) create mode 100644 src/lib/dds.h diff --git a/src/lib/dds.h b/src/lib/dds.h new file mode 100644 index 00000000..90928348 --- /dev/null +++ b/src/lib/dds.h @@ -0,0 +1,199 @@ +#include +#include +#include "lib/vec/vec.h" + +#pragma once + +typedef enum DDPF { + DDPF_ALPHAPIXELS = 0x000001, + DDPF_ALPHA = 0x000002, + DDPF_FOURCC = 0x000004, + DDPF_RGB = 0x000040, + DDPF_YUV = 0x000200, + DDPF_LUMINANCE = 0x020000 +} DDPF; + +typedef enum D3D10ResourceDimension { + D3D10_RESOURCE_DIMENSION_UNKNOWN = 0, + D3D10_RESOURCE_DIMENSION_BUFFER = 1, + D3D10_RESOURCE_DIMENSION_TEXTURE1D = 2, + D3D10_RESOURCE_DIMENSION_TEXTURE2D = 3, + D3D10_RESOURCE_DIMENSION_TEXTURE3D = 4 +} D3D10ResourceDimension; + +typedef enum DXGIFormat { + 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 +} DXGIFormat; + +typedef struct DDSPixelFormat { + uint32_t size; + uint32_t flags; + uint32_t fourCC; + uint32_t rgbBitCount; + uint32_t rBitMask; + uint32_t gBitMask; + uint32_t bBitMask; + uint32_t aBitMask; +} DDSPixelFormat; + +typedef struct DDSHeader { + uint32_t size; + uint32_t flags; + uint32_t height; + uint32_t width; + uint32_t pitchOrLinearSize; + uint32_t depth; + uint32_t mipMapCount; + uint32_t reserved[11]; + + DDSPixelFormat format; + + uint32_t caps1; + uint32_t caps2; + uint32_t caps3; + uint32_t caps4; + uint32_t reserved2; +} DDSHeader; + +typedef struct DDSHeader10 { + DXGIFormat dxgiFormat; + D3D10ResourceDimension resourceDimension; + + uint32_t miscFlag; + uint32_t arraySize; + uint32_t reserved; +} DDSHeader10; + +typedef enum { + DDS_FORMAT_DXT1, + DDS_FORMAT_DXT3, + DDS_FORMAT_DXT5, + DDS_FORMAT_UNKNOWN +} DDSFormat; + +int ddsParse(uint8_t* data, size_t size, ptrdiff_t* offset, DDSHeader* header, DDSHeader10* header10, DDSFormat* format); diff --git a/src/loaders/texture.c b/src/loaders/texture.c index 4b6ad6b4..b6858e58 100644 --- a/src/loaders/texture.c +++ b/src/loaders/texture.c @@ -1,4 +1,6 @@ #include "loaders/texture.h" +#include "math/math.h" +#include "lib/dds.h" #include "lib/stb/stb_image.h" #include #include @@ -38,6 +40,111 @@ const TextureFormat FORMAT_DXT5 = { .blockBytes = 16 }; +#define FOUR_CC(a, b, c, d) ((uint32_t) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a))) + +static int parseDDS(uint8_t* data, size_t size, TextureData* textureData) { + if (size < sizeof(uint32_t) + sizeof(DDSHeader) || *(uint32_t*) data != FOUR_CC('D', 'D', 'S', ' ')) { + return 1; + } + + // Header + size_t offset = sizeof(uint32_t); + DDSHeader* header = (DDSHeader*) (data + offset); + offset += sizeof(DDSHeader); + if (header->size != sizeof(DDSHeader) || header->format.size != sizeof(DDSPixelFormat)) { + return 1; + } + + // DX10 header + if ((header->format.flags & DDPF_FOURCC) && (header->format.fourCC == FOUR_CC('D', 'X', '1', '0'))) { + if (size < (sizeof(uint32_t) + sizeof(DDSHeader) + sizeof(DDSHeader10))) { + return 1; + } + + DDSHeader10* header10 = (DDSHeader10*) (data + offset); + offset += sizeof(DDSHeader10); + + // Only accept 2D textures + D3D10ResourceDimension dimension = header10->resourceDimension; + if (dimension != D3D10_RESOURCE_DIMENSION_TEXTURE2D && dimension != D3D10_RESOURCE_DIMENSION_UNKNOWN) { + return 1; + } + + // Can't deal with texture arrays and cubemaps. + if (header10->arraySize > 1) { + return 1; + } + + // Ensure DXT 1/3/5 + switch (header10->dxgiFormat) { + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: + textureData->format = FORMAT_DXT1; + break; + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + textureData->format = FORMAT_DXT3; + break; + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + textureData->format = FORMAT_DXT5; + break; + default: + return 1; + } + } else { + if ((header->format.flags & DDPF_FOURCC) == 0) { + return 1; + } + + // Ensure DXT 1/3/5 + switch (header->format.fourCC) { + case FOUR_CC('D', 'X', 'T', '1'): + textureData->format = FORMAT_DXT1; + return DDS_FORMAT_DXT1; + case FOUR_CC('D', 'X', 'T', '3'): + textureData->format = FORMAT_DXT3; + break; + case FOUR_CC('D', 'X', 'T', '5'): + textureData->format = FORMAT_DXT5; + break; + default: + return 1; + } + } + + int width = textureData->width = header->width; + int height = textureData->height = header->height; + int mipmapCount = header->mipMapCount; + + // Load mipmaps + vec_init(&textureData->mipmaps); + for (int i = 0; i < mipmapCount; i++) { + size_t numBlocksWide = width ? MAX(1, (width + 3) / 4) : 0; + size_t numBlocksHigh = height ? MAX(1, (height + 3) / 4) : 0; + size_t mipmapSize = numBlocksWide * numBlocksHigh * textureData->format.blockBytes; + + // Overflow check + if (mipmapSize == 0 || (offset + mipmapSize) > size) { + vec_deinit(&textureData->mipmaps); + return 1; + } + + Mipmap mipmap = { .width = width, .height = height, .data = &data[offset], .size = mipmapSize }; + vec_push(&textureData->mipmaps, mipmap); + offset += mipmapSize; + width >>= 1; + height >>= 1; + } + + textureData->data = NULL; + + return 0; +} + TextureData* lovrTextureDataGetBlank(int width, int height, uint8_t value, TextureFormat format) { TextureData* textureData = malloc(sizeof(TextureData)); if (!textureData) return NULL; @@ -66,6 +173,12 @@ TextureData* lovrTextureDataFromBlob(Blob* blob) { TextureData* textureData = malloc(sizeof(TextureData)); if (!textureData) return NULL; + if (!parseDDS(blob->data, blob->size, textureData)) { + textureData->blob = blob; + lovrRetain(&blob->ref); + return textureData; + } + stbi_set_flip_vertically_on_load(0); textureData->format = FORMAT_RGBA; textureData->data = stbi_load_from_memory(blob->data, blob->size, &textureData->width, &textureData->height, NULL, 4); diff --git a/src/loaders/texture.h b/src/loaders/texture.h index cf1f1bb0..c1cff024 100644 --- a/src/loaders/texture.h +++ b/src/loaders/texture.h @@ -14,13 +14,21 @@ typedef struct { extern const TextureFormat FORMAT_RGB, FORMAT_RGBA, FORMAT_DXT1, FORMAT_DXT3, FORMAT_DXT5; typedef struct { + int width; + int height; + void* data; + size_t size; +} Mipmap; +typedef vec_t(Mipmap) vec_mipmap_t; typedef struct { int width; int height; TextureFormat format; void* data; + vec_mipmap_t mipmaps; + Blob* blob; } TextureData; TextureData* lovrTextureDataGetBlank(int width, int height, uint8_t value, TextureFormat format);