diff options
Diffstat (limited to 'src/lib/dds.c')
-rw-r--r-- | src/lib/dds.c | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/src/lib/dds.c b/src/lib/dds.c new file mode 100644 index 0000000..a3ec71b --- /dev/null +++ b/src/lib/dds.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2012-2013 Etnaviv Project + * + * 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, sub license, + * 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. 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. + */ +/* DDS loader - Vaguely based on code by Jon Watte 2002 */ +/* Permission granted to use freely, as long as Jon Watte */ +/* is held harmless for all possible damages resulting from */ +/* your use or failure to use this code. */ +/* No warranty is expressed or implied. Use at your own risk, */ +/* or not at all. */ + +#include "dds.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> + +#define DEBUG + +// little-endian, of course +#define DDS_MAGIC 0x20534444 + +// DDS_header.dwFlags +#define DDSD_CAPS 0x00000001 +#define DDSD_HEIGHT 0x00000002 +#define DDSD_WIDTH 0x00000004 +#define DDSD_PITCH 0x00000008 +#define DDSD_PIXELFORMAT 0x00001000 +#define DDSD_MIPMAPCOUNT 0x00020000 +#define DDSD_LINEARSIZE 0x00080000 +#define DDSD_DEPTH 0x00800000 + +// DDS_header.sPixelFormat.dwFlags +#define DDPF_ALPHAPIXELS 0x00000001 +#define DDPF_ALPHA 0x00000002 +#define DDPF_FOURCC 0x00000004 +#define DDPF_INDEXED 0x00000020 +#define DDPF_RGB 0x00000040 +#define DDPF_LUMINANCE 0x00020000 + +// DDS_header.sCaps.dwCaps1 +#define DDSCAPS_COMPLEX 0x00000008 +#define DDSCAPS_TEXTURE 0x00001000 +#define DDSCAPS_MIPMAP 0x00400000 + +// DDS_header.sCaps.dwCaps2 +#define DDSCAPS2_CUBEMAP 0x00000200 +#define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400 +#define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800 +#define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000 +#define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000 +#define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000 +#define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000 +#define DDSCAPS2_VOLUME 0x00200000 + +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + (uint32_t)( \ + (((uint32_t)(uint8_t)(ch3) << 24) & 0xFF000000) | \ + (((uint32_t)(uint8_t)(ch2) << 16) & 0x00FF0000) | \ + (((uint32_t)(uint8_t)(ch1) << 8) & 0x0000FF00) | \ + ((uint32_t)(uint8_t)(ch0) & 0x000000FF) ) + +#define D3DFMT_DXT1 MAKEFOURCC('D','X','T','1') // DXT1 compression texture format +#define D3DFMT_DXT2 MAKEFOURCC('D','X','T','2') // DXT2 compression texture format +#define D3DFMT_DXT3 MAKEFOURCC('D','X','T','3') // DXT3 compression texture format +#define D3DFMT_DXT4 MAKEFOURCC('D','X','T','4') // DXT4 compression texture format +#define D3DFMT_DXT5 MAKEFOURCC('D','X','T','5') // DXT5 compression texture format +#define D3DFMT_ETC1 MAKEFOURCC('E','T','C','1') // Custom (Ericcson Texture Compression) + +struct _DDS_pixelformat { + unsigned int dwSize; + unsigned int dwFlags; + unsigned int dwFourCC; + unsigned int dwRGBBitCount; + unsigned int dwRBitMask; + unsigned int dwGBitMask; + unsigned int dwBBitMask; + unsigned int dwAlphaBitMask; +}; + +typedef union _DDS_header { + struct { + unsigned int dwMagic; + unsigned int dwSize; + unsigned int dwFlags; + unsigned int dwHeight; + unsigned int dwWidth; + unsigned int dwPitchOrLinearSize; + unsigned int dwDepth; + unsigned int dwMipMapCount; + unsigned int dwReserved1[ 11 ]; + + // DDPIXELFORMAT + struct _DDS_pixelformat sPixelFormat; + + // DDCAPS2 + struct { + unsigned int dwCaps1; + unsigned int dwCaps2; + unsigned int dwDDSX; + unsigned int dwReserved; + } sCaps; + unsigned int dwReserved2; + }; + char data[ 128 ]; +} DDS_header; + +typedef struct _DdsLoadInfo { + bool compressed; + unsigned int divSize; + unsigned int blockBytes; + uint32_t flags; + int bitcount; + uint32_t rmask; + uint32_t gmask; + uint32_t bmask; + uint32_t amask; +} DdsLoadInfo; + +static DdsLoadInfo loadInfo[] = { + [FMT_A8R8G8B8] = {false, 1, 4, DDPF_RGB, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000}, + [FMT_X8R8G8B8] = {false, 1, 4, DDPF_RGB, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000}, + [FMT_R8G8B8] = {false, 1, 3, DDPF_RGB, 24, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000}, + [FMT_A1R5G5B5] = {false, 1, 2, DDPF_RGB, 16, 0x00007c00, 0x000003e0, 0x0000001f, 0x00008000}, + [FMT_X1R5G5B5] = {false, 1, 2, DDPF_RGB, 16, 0x00007c00, 0x000003e0, 0x0000001f, 0x00000000}, + [FMT_R5G6B5] = {false, 1, 2, DDPF_RGB, 16, 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000}, + [FMT_A8] = {false, 1, 1, DDPF_ALPHA, 8, 0x00000000, 0, 0, 0x000000ff}, + [FMT_L8] = {false, 1, 1, DDPF_LUMINANCE, 8, 0x000000ff, 0, 0, 0x00000000}, + [FMT_A8L8] = {false, 1, 2, DDPF_LUMINANCE, 16, 0x000000ff, 0, 0, 0x0000ff00}, + [FMT_DXT1] = {true, 4, 8, DDPF_FOURCC, 0, D3DFMT_DXT1}, + [FMT_DXT3] = {true, 4, 16, DDPF_FOURCC, 0, D3DFMT_DXT3}, + [FMT_DXT5] = {true, 4, 16, DDPF_FOURCC, 0, D3DFMT_DXT5}, + [FMT_ETC1] = {true, 4, 8, DDPF_FOURCC, 0, D3DFMT_ETC1} +}; +#define NUM_FORMATS (sizeof(loadInfo) / sizeof(DdsLoadInfo)) + +static inline int max(int a, int b) { return a > b ? a : b; } +static inline int min(int a, int b) { return a < b ? a : b; } + +static int find_fmt(struct _DDS_pixelformat *pf) +{ + int fmt=0; + for(fmt=0; fmt<NUM_FORMATS; ++fmt) + { + DdsLoadInfo *li = &loadInfo[fmt]; + if(pf->dwFlags & DDPF_FOURCC) + { + if(li->flags == DDPF_FOURCC && li->bitcount == 0 && li->rmask == pf->dwFourCC) + { + return fmt; + } + } + else if(pf->dwFlags & DDPF_RGB) + { + if((li->flags == DDPF_RGB) && + (li->bitcount == pf->dwRGBBitCount) && + (li->rmask == pf->dwRBitMask) && (li->gmask == pf->dwGBitMask) && + (li->bmask == pf->dwBBitMask) && (li->amask == ((pf->dwFlags & DDPF_ALPHAPIXELS)?pf->dwAlphaBitMask:0))) + { + return fmt; + } + } + else if(pf->dwFlags & DDPF_LUMINANCE) + { + bool formatHasAlpha = pf->dwAlphaBitMask != 0; + bool ddsHasAlpha = (pf->dwFlags & DDPF_ALPHAPIXELS) != 0; + if(li->flags == DDPF_LUMINANCE && + li->bitcount == pf->dwRGBBitCount && + formatHasAlpha == ddsHasAlpha) + { + return fmt; + } + } + else if(pf->dwFlags & DDPF_ALPHA) + { + if(li->flags == DDPF_ALPHA && + li->bitcount == pf->dwRGBBitCount) + { + return fmt; + } + } + } + return -1; +} + +bool dds_load_file(FILE *f, dds_texture **out) +{ + DDS_header hdr; + unsigned int x = 0; + unsigned int y = 0; + unsigned int mipMapCount = 0; + if(f == NULL || out == NULL) + return false; + fread( &hdr, sizeof( hdr ), 1, f ); + assert( hdr.dwMagic == DDS_MAGIC ); + assert( hdr.dwSize == 124 ); + + if( hdr.dwMagic != DDS_MAGIC || hdr.dwSize != 124 || + !(hdr.dwFlags & DDSD_PIXELFORMAT) || !(hdr.dwFlags & DDSD_CAPS) ) + { + goto failure; + } + + unsigned int xSize = hdr.dwWidth; + unsigned int ySize = hdr.dwHeight; + int fmt = find_fmt(&hdr.sPixelFormat); + if(fmt == -1) { +#ifdef DEBUG + printf("Unknown format\n"); +#endif + goto failure; + } + DdsLoadInfo *li = &loadInfo[fmt]; + dds_texture *rv = (dds_texture*)malloc(sizeof(dds_texture)); + if(!rv) + goto failure; + + //fixme: do cube maps later + //fixme: do 3d later + x = xSize; + y = ySize; + mipMapCount = (hdr.dwFlags & DDSD_MIPMAPCOUNT) ? hdr.dwMipMapCount : 1; +#ifdef DEBUG + printf("Base size: %ix%i, %i mips\n", x, y, mipMapCount); +#endif + + // size of first mip + size_t size = max(li->divSize, x)/li->divSize * max(li->divSize, y)/li->divSize * li->blockBytes; + if((hdr.dwFlags & DDSD_LINEARSIZE) && (hdr.dwPitchOrLinearSize != size)) + { +#ifdef DEBUG + printf("Size mismatch: %i versus %i\n", hdr.dwPitchOrLinearSize, size); +#endif + goto failure; + } + if((hdr.dwFlags & DDSD_PITCH) && (hdr.dwPitchOrLinearSize != x*li->blockBytes)) + goto failure; + + rv->fmt = fmt; + rv->num_slices = 1; + rv->num_mipmaps = mipMapCount; + rv->div_size = li->divSize; + rv->block_bytes = li->blockBytes; + rv->slices = malloc(sizeof(dds_mipmap*) * rv->num_slices); + rv->slices[0] = malloc(sizeof(dds_mipmap) * rv->num_mipmaps); +#ifdef DEBUG + printf("(format %i, %i mipmaps)\n", rv->fmt, rv->num_mipmaps); +#endif + + /* compute offsets */ + size_t offset = 0; + for(unsigned int ix = 0; ix < mipMapCount; ++ix) + { + dds_mipmap *mip = &rv->slices[0][ix]; + mip->width = x; + mip->height = y; + mip->stride = max( li->divSize, x )/li->divSize * li->blockBytes; + mip->offset = offset; + mip->size = size; +#ifdef DEBUG + printf("%08x Loading mipmap %i: %ix%i (%i) stride=%i\n", + (int)mip->offset, ix, (int)mip->width, (int)mip->height, (int)mip->size, + (int)mip->stride); +#endif + offset += size; + if(x != 1) + x = x >> 1; + if(y != 1) + y = y >> 1; + size = max( li->divSize, x )/li->divSize * max( li->divSize, y )/li->divSize * li->blockBytes; + } + rv->data = malloc(offset); + rv->size = offset; + if(!rv->data) + goto failure; + fread(rv->data, rv->size, 1, f); + for(unsigned int ix = 0; ix < mipMapCount; ++ix) + { + rv->slices[0][ix].data = (void*)((size_t)rv->data + rv->slices[0][ix].offset); + } + *out = rv; + + return true; +failure: + return false; +} + +bool dds_load(const char *filename, dds_texture **out) +{ + FILE *f = fopen(filename, "rb"); + if(f == NULL) + { +#ifdef DEBUG + printf("Cannot open texture file\n"); +#endif + return false; + } + bool ret = dds_load_file(f, out); + fclose(f); + return ret; +} + +void dds_free(dds_texture *tex) +{ + if(tex == NULL) + return; + free(tex->slices); + free(tex->data); + free(tex); +} +#if 0 +int main(int argc, char **argv) +{ + dds_texture *tex = 0; + if(dds_load(argv[1], &tex)) + { + printf("Succesfully loaded\n"); + } +} +#endif |