Subversion Repositories gelsvn

Rev

Rev 594 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/**
 * @file stbi_DDS_aug_c.h
 * @brief DDS file support
 */

///     DDS file support, does decoding, _not_ direct uploading
///     (use SOIL for that ;-)

///     A bunch of DirectDraw Surface structures and flags
typedef 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 {
      unsigned int    dwSize;
      unsigned int    dwFlags;
      unsigned int    dwFourCC;
      unsigned int    dwRGBBitCount;
      unsigned int    dwRBitMask;
      unsigned int    dwGBitMask;
      unsigned int    dwBBitMask;
      unsigned int    dwAlphaBitMask;
    }               sPixelFormat;

    //  DDCAPS2
    struct {
      unsigned int    dwCaps1;
      unsigned int    dwCaps2;
      unsigned int    dwDDSX;
      unsigned int    dwReserved;
    }               sCaps;
    unsigned int    dwReserved2;
} DDS_header ;

//      the following constants were copied directly off the MSDN website

//      The dwFlags member of the original DDSURFACEDESC2 structure
//      can be set to one or more of the following values.
#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

//      DirectDraw Pixel Format
#define DDPF_ALPHAPIXELS        0x00000001
#define DDPF_FOURCC     0x00000004
#define DDPF_RGB        0x00000040

//      The dwCaps1 member of the DDSCAPS2 structure can be
//      set to one or more of the following values.
#define DDSCAPS_COMPLEX 0x00000008
#define DDSCAPS_TEXTURE 0x00001000
#define DDSCAPS_MIPMAP  0x00400000

//      The dwCaps2 member of the DDSCAPS2 structure can be
//      set to one or more of the following values.
#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

static int dds_test(stbi *s)
{
        //      check the magic number
        if (get8(s) != 'D') return 0;
        if (get8(s) != 'D') return 0;
        if (get8(s) != 'S') return 0;
        if (get8(s) != ' ') return 0;
        //      check header size
        if (get32le(s) != 124) return 0;
        return 1;
}
#ifndef STBI_NO_STDIO
int      stbi_dds_test_file        (FILE *f)
{
   stbi s;
   int r,n = ftell(f);
   start_file(&s,f);
   r = dds_test(&s);
   fseek(f,n,SEEK_SET);
   return r;
}
#endif


int      stbi_dds_test_memory      (stbi_uc const *buffer, int len)
{
   stbi s;
   start_mem(&s,buffer, len);
   return dds_test(&s);
}

//      helper functions
int stbi_convert_bit_range( int c, int from_bits, int to_bits )
{
        int b = (1 << (from_bits - 1)) + c * ((1 << to_bits) - 1);
        return (b + (b >> from_bits)) >> from_bits;
}
void stbi_rgb_888_from_565( unsigned int c, int *r, int *g, int *b )
{
        *r = stbi_convert_bit_range( (c >> 11) & 31, 5, 8 );
        *g = stbi_convert_bit_range( (c >> 05) & 63, 6, 8 );
        *b = stbi_convert_bit_range( (c >> 00) & 31, 5, 8 );
}
void stbi_decode_DXT1_block(
                        unsigned char uncompressed[16*4],
                        unsigned char compressed[8] )
{
        int next_bit = 4*8;
        int i, r, g, b;
        int c0, c1;
        unsigned char decode_colors[4*4];
        //      find the 2 primary colors
        c0 = compressed[0] + (compressed[1] << 8);
        c1 = compressed[2] + (compressed[3] << 8);
        stbi_rgb_888_from_565( c0, &r, &g, &b );
        decode_colors[0] = r;
        decode_colors[1] = g;
        decode_colors[2] = b;
        decode_colors[3] = 255;
        stbi_rgb_888_from_565( c1, &r, &g, &b );
        decode_colors[4] = r;
        decode_colors[5] = g;
        decode_colors[6] = b;
        decode_colors[7] = 255;
        if( c0 > c1 )
        {
                //      no alpha, 2 interpolated colors
                decode_colors[8] = (2*decode_colors[0] + decode_colors[4]) / 3;
                decode_colors[9] = (2*decode_colors[1] + decode_colors[5]) / 3;
                decode_colors[10] = (2*decode_colors[2] + decode_colors[6]) / 3;
                decode_colors[11] = 255;
                decode_colors[12] = (decode_colors[0] + 2*decode_colors[4]) / 3;
                decode_colors[13] = (decode_colors[1] + 2*decode_colors[5]) / 3;
                decode_colors[14] = (decode_colors[2] + 2*decode_colors[6]) / 3;
                decode_colors[15] = 255;
        } else
        {
                //      1 interpolated color, alpha
                decode_colors[8] = (decode_colors[0] + decode_colors[4]) / 2;
                decode_colors[9] = (decode_colors[1] + decode_colors[5]) / 2;
                decode_colors[10] = (decode_colors[2] + decode_colors[6]) / 2;
                decode_colors[11] = 255;
                decode_colors[12] = 0;
                decode_colors[13] = 0;
                decode_colors[14] = 0;
                decode_colors[15] = 0;
        }
        //      decode the block
        for( i = 0; i < 16*4; i += 4 )
        {
                int idx = ((compressed[next_bit>>3] >> (next_bit & 7)) & 3) * 4;
                next_bit += 2;
                uncompressed[i+0] = decode_colors[idx+0];
                uncompressed[i+1] = decode_colors[idx+1];
                uncompressed[i+2] = decode_colors[idx+2];
                uncompressed[i+3] = decode_colors[idx+3];
        }
        //      done
}
void stbi_decode_DXT23_alpha_block(
                        unsigned char uncompressed[16*4],
                        unsigned char compressed[8] )
{
        int i, next_bit = 0;
        //      each alpha value gets 4 bits
        for( i = 3; i < 16*4; i += 4 )
        {
                uncompressed[i] = stbi_convert_bit_range(
                                (compressed[next_bit>>3] >> (next_bit&7)) & 15,
                                4, 8 );
                next_bit += 4;
        }
}
void stbi_decode_DXT45_alpha_block(
                        unsigned char uncompressed[16*4],
                        unsigned char compressed[8] )
{
        int i, next_bit = 8*2;
        unsigned char decode_alpha[8];
        //      each alpha value gets 3 bits, and the 1st 2 bytes are the range
        decode_alpha[0] = compressed[0];
        decode_alpha[1] = compressed[1];
        if( decode_alpha[0] > decode_alpha[1] )
        {
                //      6 step intermediate
                decode_alpha[2] = (6*decode_alpha[0] + 1*decode_alpha[1]) / 7;
                decode_alpha[3] = (5*decode_alpha[0] + 2*decode_alpha[1]) / 7;
                decode_alpha[4] = (4*decode_alpha[0] + 3*decode_alpha[1]) / 7;
                decode_alpha[5] = (3*decode_alpha[0] + 4*decode_alpha[1]) / 7;
                decode_alpha[6] = (2*decode_alpha[0] + 5*decode_alpha[1]) / 7;
                decode_alpha[7] = (1*decode_alpha[0] + 6*decode_alpha[1]) / 7;
        } else
        {
                //      4 step intermediate, pluss full and none
                decode_alpha[2] = (4*decode_alpha[0] + 1*decode_alpha[1]) / 5;
                decode_alpha[3] = (3*decode_alpha[0] + 2*decode_alpha[1]) / 5;
                decode_alpha[4] = (2*decode_alpha[0] + 3*decode_alpha[1]) / 5;
                decode_alpha[5] = (1*decode_alpha[0] + 4*decode_alpha[1]) / 5;
                decode_alpha[6] = 0;
                decode_alpha[7] = 255;
        }
        for( i = 3; i < 16*4; i += 4 )
        {
                int idx = 0, bit;
                bit = (compressed[next_bit>>3] >> (next_bit&7)) & 1;
                idx += bit << 0;
                ++next_bit;
                bit = (compressed[next_bit>>3] >> (next_bit&7)) & 1;
                idx += bit << 1;
                ++next_bit;
                bit = (compressed[next_bit>>3] >> (next_bit&7)) & 1;
                idx += bit << 2;
                ++next_bit;
                uncompressed[i] = decode_alpha[idx & 7];
        }
        //      done
}
void stbi_decode_DXT_color_block(
                        unsigned char uncompressed[16*4],
                        unsigned char compressed[8] )
{
        int next_bit = 4*8;
        int i, r, g, b;
        int c0, c1;
        unsigned char decode_colors[4*3];
        //      find the 2 primary colors
        c0 = compressed[0] + (compressed[1] << 8);
        c1 = compressed[2] + (compressed[3] << 8);
        stbi_rgb_888_from_565( c0, &r, &g, &b );
        decode_colors[0] = r;
        decode_colors[1] = g;
        decode_colors[2] = b;
        stbi_rgb_888_from_565( c1, &r, &g, &b );
        decode_colors[3] = r;
        decode_colors[4] = g;
        decode_colors[5] = b;
        //      Like DXT1, but no choicees:
        //      no alpha, 2 interpolated colors
        decode_colors[6] = (2*decode_colors[0] + decode_colors[3]) / 3;
        decode_colors[7] = (2*decode_colors[1] + decode_colors[4]) / 3;
        decode_colors[8] = (2*decode_colors[2] + decode_colors[5]) / 3;
        decode_colors[9] = (decode_colors[0] + 2*decode_colors[3]) / 3;
        decode_colors[10] = (decode_colors[1] + 2*decode_colors[4]) / 3;
        decode_colors[11] = (decode_colors[2] + 2*decode_colors[5]) / 3;
        //      decode the block
        for( i = 0; i < 16*4; i += 4 )
        {
                int idx = ((compressed[next_bit>>3] >> (next_bit & 7)) & 3) * 3;
                next_bit += 2;
                uncompressed[i+0] = decode_colors[idx+0];
                uncompressed[i+1] = decode_colors[idx+1];
                uncompressed[i+2] = decode_colors[idx+2];
        }
        //      done
}
static stbi_uc *dds_load(stbi *s, int *x, int *y, int *comp, int req_comp)
{
        //      all variables go up front
        stbi_uc *dds_data = NULL;
        stbi_uc block[16*4];
        stbi_uc compressed[8];
        int flags, DXT_family;
        int has_alpha, has_mipmap;
        int is_compressed, cubemap_faces;
        int block_pitch, num_blocks;
        DDS_header header;
        int i, sz, cf;
        //      load the header
        if( sizeof( DDS_header ) != 128 )
        {
                return NULL;
        }
        getn( s, (stbi_uc*)(&header), 128 );
        //      and do some checking
        if( header.dwMagic != (('D' << 0) | ('D' << 8) | ('S' << 16) | (' ' << 24)) ) return NULL;
        if( header.dwSize != 124 ) return NULL;
        flags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
        if( (header.dwFlags & flags) != flags ) return NULL;
        /*      According to the MSDN spec, the dwFlags should contain
                DDSD_LINEARSIZE if it's compressed, or DDSD_PITCH if
                uncompressed.  Some DDS writers do not conform to the
                spec, so I need to make my reader more tolerant */
        if( header.sPixelFormat.dwSize != 32 ) return NULL;
        flags = DDPF_FOURCC | DDPF_RGB;
        if( (header.sPixelFormat.dwFlags & flags) == 0 ) return NULL;
        if( (header.sCaps.dwCaps1 & DDSCAPS_TEXTURE) == 0 ) return NULL;
        //      get the image data
        s->img_x = header.dwWidth;
        s->img_y = header.dwHeight;
        s->img_n = 4;
        is_compressed = (header.sPixelFormat.dwFlags & DDPF_FOURCC) / DDPF_FOURCC;
        has_alpha = (header.sPixelFormat.dwFlags & DDPF_ALPHAPIXELS) / DDPF_ALPHAPIXELS;
        has_mipmap = (header.sCaps.dwCaps1 & DDSCAPS_MIPMAP) && (header.dwMipMapCount > 1);
        cubemap_faces = (header.sCaps.dwCaps2 & DDSCAPS2_CUBEMAP) / DDSCAPS2_CUBEMAP;
        /*      I need cubemaps to have square faces    */
        cubemap_faces &= (s->img_x == s->img_y);
        cubemap_faces *= 5;
        cubemap_faces += 1;
        block_pitch = (s->img_x+3) >> 2;
        num_blocks = block_pitch * ((s->img_y+3) >> 2);
        /*      let the user know what's going on       */
        *x = s->img_x;
        *y = s->img_y;
        *comp = s->img_n;
        /*      is this uncompressed?   */
        if( is_compressed )
        {
                /*      compressed      */
                //      note: header.sPixelFormat.dwFourCC is something like (('D'<<0)|('X'<<8)|('T'<<16)|('1'<<24))
                DXT_family = 1 + (header.sPixelFormat.dwFourCC >> 24) - '1';
                if( (DXT_family < 1) || (DXT_family > 5) ) return NULL;
                /*      check the expected size...oops, nevermind...
                        those non-compliant writers leave
                        dwPitchOrLinearSize == 0        */
                //      passed all the tests, get the RAM for decoding
                sz = (s->img_x)*(s->img_y)*4*cubemap_faces;
                dds_data = (unsigned char*)malloc( sz );
                /*      do this once for each face      */
                for( cf = 0; cf < cubemap_faces; ++ cf )
                {
                        //      now read and decode all the blocks
                        for( i = 0; i < num_blocks; ++i )
                        {
                                //      where are we?
                                int bx, by, bw=4, bh=4;
                                int ref_x = 4 * (i % block_pitch);
                                int ref_y = 4 * (i / block_pitch);
                                //      get the next block's worth of compressed data, and decompress it
                                if( DXT_family == 1 )
                                {
                                        //      DXT1
                                        getn( s, compressed, 8 );
                                        stbi_decode_DXT1_block( block, compressed );
                                } else if( DXT_family < 4 )
                                {
                                        //      DXT2/3
                                        getn( s, compressed, 8 );
                                        stbi_decode_DXT23_alpha_block ( block, compressed );
                                        getn( s, compressed, 8 );
                                        stbi_decode_DXT_color_block ( block, compressed );
                                } else
                                {
                                        //      DXT4/5
                                        getn( s, compressed, 8 );
                                        stbi_decode_DXT45_alpha_block ( block, compressed );
                                        getn( s, compressed, 8 );
                                        stbi_decode_DXT_color_block ( block, compressed );
                                }
                                //      is this a partial block?
                                if( ref_x + 4 > (int)s->img_x )
                                {
                                        bw = s->img_x - ref_x;
                                }
                                if( ref_y + 4 > (int)s->img_y )
                                {
                                        bh = s->img_y - ref_y;
                                }
                                //      now drop our decompressed data into the buffer
                                for( by = 0; by < bh; ++by )
                                {
                                        int idx = 4*((ref_y+by+cf*s->img_x)*s->img_x + ref_x);
                                        for( bx = 0; bx < bw*4; ++bx )
                                        {

                                                dds_data[idx+bx] = block[by*16+bx];
                                        }
                                }
                        }
                        /*      done reading and decoding the main image...
                                skip MIPmaps if present */
                        if( has_mipmap )
                        {
                                int block_size = 16;
                                if( DXT_family == 1 )
                                {
                                        block_size = 8;
                                }
                                for( i = 1; i < (int)header.dwMipMapCount; ++i )
                                {
                                        int mx = s->img_x >> (i + 2);
                                        int my = s->img_y >> (i + 2);
                                        if( mx < 1 )
                                        {
                                                mx = 1;
                                        }
                                        if( my < 1 )
                                        {
                                                my = 1;
                                        }
                                        skip( s, mx*my*block_size );
                                }
                        }
                }/* per cubemap face */
        } else
        {
                /*      uncompressed    */
                DXT_family = 0;
                s->img_n = 3;
                if( has_alpha )
                {
                        s->img_n = 4;
                }
                *comp = s->img_n;
                sz = s->img_x*s->img_y*s->img_n*cubemap_faces;
                dds_data = (unsigned char*)malloc( sz );
                /*      do this once for each face      */
                for( cf = 0; cf < cubemap_faces; ++ cf )
                {
                        /*      read the main image for this face       */
                        getn( s, &dds_data[cf*s->img_x*s->img_y*s->img_n], s->img_x*s->img_y*s->img_n );
                        /*      done reading and decoding the main image...
                                skip MIPmaps if present */
                        if( has_mipmap )
                        {
                                for( i = 1; i < (int)header.dwMipMapCount; ++i )
                                {
                                        int mx = s->img_x >> i;
                                        int my = s->img_y >> i;
                                        if( mx < 1 )
                                        {
                                                mx = 1;
                                        }
                                        if( my < 1 )
                                        {
                                                my = 1;
                                        }
                                        skip( s, mx*my*s->img_n );
                                }
                        }
                }
                /*      data was BGR, I need it RGB     */
                for( i = 0; i < sz; i += s->img_n )
                {
                        unsigned char temp = dds_data[i];
                        dds_data[i] = dds_data[i+2];
                        dds_data[i+2] = temp;
                }
        }
        /*      finished decompressing into RGBA,
                adjust the y size if we have a cubemap
                note: sz is already up to date  */
        s->img_y *= cubemap_faces;
        *y = s->img_y;
        //      did the user want something else, or
        //      see if all the alpha values are 255 (i.e. no transparency)
        has_alpha = 0;
        if( s->img_n == 4)
        {
                for( i = 3; (i < sz) && (has_alpha == 0); i += 4 )
                {
                        has_alpha |= (dds_data[i] < 255);
                }
        }
        if( (req_comp <= 4) && (req_comp >= 1) )
        {
                //      user has some requirements, meet them
                if( req_comp != s->img_n )
                {
                        dds_data = convert_format( dds_data, s->img_n, req_comp, s->img_x, s->img_y );
                        *comp = s->img_n;
                }
        } else
        {
                //      user had no requirements, only drop to RGB is no alpha
                if( (has_alpha == 0) && (s->img_n == 4) )
                {
                        dds_data = convert_format( dds_data, 4, 3, s->img_x, s->img_y );
                        *comp = 3;
                }
        }
        //      OK, done
        return dds_data;
}

#ifndef STBI_NO_STDIO
stbi_uc *stbi_dds_load_from_file   (FILE *f,                  int *x, int *y, int *comp, int req_comp)
{
        stbi s;
   start_file(&s,f);
   return dds_load(&s,x,y,comp,req_comp);
}

stbi_uc *stbi_dds_load             (char *filename,           int *x, int *y, int *comp, int req_comp)
{
   stbi_uc *data;
   FILE *f = fopen(filename, "rb");
   if (!f) return NULL;
   data = stbi_dds_load_from_file(f,x,y,comp,req_comp);
   fclose(f);
   return data;
}
#endif


stbi_uc *stbi_dds_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
{
        stbi s;
   start_mem(&s,buffer, len);
   return dds_load(&s,x,y,comp,req_comp);
}