www.xbdev.net
xbdev - software development
Friday April 19, 2024
home | contact | Support | Image File Format... Storing Graphics using Bits and Bytes.. | Image Formats A single picture is worth a thousand words...

     
 

Image Formats

A single picture is worth a thousand words...

 

DirectX Image (.dds) File Format Explained

by bkenwright@xbdev.net



 

It seems to be popping up all the times these days....I mean you see tga, bmp and jpg a lot, but if your doing 3d programming or touching directx in any way, you'll know what the dds format is.  Basically m$ decided to do there own file format for images, which took into acount how the graphics card works with textures and lossless compression techniques. 

 

As the format is supported with photoshop and allows you to export it.  What we'll be doing here, is seeing what exactly is in there.  Be good to poke around, read in a few bytes and analyse how it works and how we could use it in custom demos that don't actually have directx in them.

 

 

For my test dds file, I've started with a simple example that I created myself in photoshop - its a small cross 8x8 pixels in size and was exported in ARGB(8:8:8:8).  Don't want to complicate things to start with.  Of course theres dozens of different compression methods possible with this dds format, and hopefully we'll try and cover some of the simple ones as we go along.

 

 

 

 

code010 - Download Source Code
////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
// File:   main.cpp                                                                   //
// Author: bkenwright@xbdev.net                                                       //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>                             // So we can use fopen and fread

// Simple Debug Feedback - writes to a simple text file located where the exe is.
void debug(char* str)
{
      FILE* fp = fopen("dbg.txt", "a+");
      fprintf(fp, "%s\n", str);
      fclose(fp);
}// End of debug(..)

char buf[500]; // Temp buffer for text output

// Program Entry Point - Where we always start!
void main()
{
      FILE* fp = fopen("cross.dds", "rb");
      char readbuf[3]; // Temp Buffer
      //      +-  Buffer to put the data in
      //      |      +- Size of each value read in bytes (1 byte each)
      //      |      |   +- How many times to read in the value (e.g. twice read in 1 byte)
      //      |      |   |    +- Stream souce
      //      |      |   |    |
      fread(readbuf, 1,  3,  fp);

      debug("First Three Bytes:");
      // Write the 3 bytes as characters to our debug file
      sprintf(buf, "%c %c %c", readbuf[0], readbuf[1], readbuf[2]);
      debug(buf);

      fclose(fp);
}// End of main()

 

output.txt
First Three Bytes:
D D S

 

Of course most formats start with some magic numbers so that we can identify what file we are dealing with.  The next part is to actually get information about the image file we have, such as image width, compression etc.

 

I don't want to include any external files, so I've copied the important parts from ddraw.h header file which define the,  DDSURFACE2 structure, which is how the header file is defined.  Its probably a good idea to put these structure defines in there own .h file, something called ddstypes.h, or something like that....but for now I've just put them in our file.

 

NOTE! I've only read in 3 bytes for the ID above, but in fact we should read in 4 bytes, as the DDSURFACEDESC2 header data is offset from the start of the file by 4 bytes.  The 4th byte is just empty.

 

code020 : Download Source Code
////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
// File:   main.cpp                                                                   //
// Author: bkenwright@xbdev.net                                                       //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>                             // So we can use fopen and fread

// Simple Debug Feedback - writes to a simple text file located where the exe is.
void debug(char* str)
{
      FILE* fp = fopen("output.txt", "a+");
      fprintf(fp, "%s\n", str);
      fclose(fp);
}// End of debug(..)

char buf[500]; // Temp buffer for text output


// ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW *
// ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW *
#define DWORD   unsigned int
#define WORD    unsigned short
#define LONG    unsigned int
#define LPVOID  void*

struct DDCOLORKEY
{
    DWORD       dwColorSpaceLowValue;
    DWORD       dwColorSpaceHighValue;
};
struct DDSCAPS2
{
    DWORD       dwCaps;
    DWORD       dwCaps2;
    DWORD       dwCaps3;
    union
    {
        DWORD       dwCaps4;
        DWORD       dwVolumeDepth;
    };
};


struct DDPIXELFORMAT
{
    DWORD       dwSize;
    DWORD       dwFlags;
    DWORD       dwFourCC;
    union
    {
        DWORD   dwRGBBitCount;
        DWORD   dwYUVBitCount;
        DWORD   dwZBufferBitDepth;
        DWORD   dwAlphaBitDepth;
        DWORD   dwLuminanceBitCount;
        DWORD   dwBumpBitCount;
        DWORD   dwPrivateFormatBitCount;
    };
    union
    {
        DWORD   dwRBitMask;
        DWORD   dwYBitMask;
        DWORD   dwStencilBitDepth;
        DWORD   dwLuminanceBitMask;
        DWORD   dwBumpDuBitMask;
        DWORD   dwOperations;
    };
    union
    {
        DWORD   dwGBitMask;
        DWORD   dwUBitMask;
        DWORD   dwZBitMask;
        DWORD   dwBumpDvBitMask;
        struct
        {
            WORD    wFlipMSTypes;
            WORD    wBltMSTypes;
        }MultiSampleCaps;
    };
    union
    {
        DWORD   dwBBitMask;
        DWORD   dwVBitMask;
        DWORD   dwStencilBitMask;
        DWORD   dwBumpLuminanceBitMask;
    };
    union
    {
        DWORD   dwRGBAlphaBitMask;
        DWORD   dwYUVAlphaBitMask;
        DWORD   dwLuminanceAlphaBitMask;
        DWORD   dwRGBZBitMask;
        DWORD   dwYUVZBitMask;
    };
};

// Basically a copy from the ddraw.h directx header file which defines
// the DDSURFACEDESC2 structure, which we need for our file, as the file
// head is basically a copy of this.
struct DDSURFACEDESC2 
{
    DWORD  dwSize;
    DWORD  dwFlags; 
    DWORD  dwHeight; 
    DWORD  dwWidth;
    union 
    { 
        LONG    lPitch;
        DWORD   dwLinearSize; 
    };
    DWORD  dwBackBufferCount;
    union 
    { 
        DWORD   dwMipMapCount; 
        DWORD   dwRefreshRate; 
        DWORD   dwSrcVBHandle; 
    };
    DWORD   dwAlphaBitDepth; 
    DWORD   dwReserved; 
    LPVOID  lpSurface; 
    union 
    { 
        DDCOLORKEY   ddckCKDestOverlay; 
        DWORD   dwEmptyFaceColor; 
    };
    DDCOLORKEY  ddckCKDestBlt; 
    DDCOLORKEY  ddckCKSrcOverlay; 
    DDCOLORKEY  ddckCKSrcBlt; 
    union
    { 
        DDPIXELFORMAT   ddpfPixelFormat;
        DWORD           dwFVF; 
    };
    DDSCAPS2    ddsCaps; 
    DWORD       dwTextureStage;
};

// Program Entry Point - Where we always start!
void main()
{
      FILE* fp = fopen("cross.dds", "rb");
      char readbuf[4]; // Temp Buffer
      //      +-  Buffer to put the data in
      //      |      +- Size of each value read in bytes (1 byte each)
      //      |      |   +- How many times to read in the value
      //      |      |   |    +- Stream souce
      //      |      |   |    |
      fread(readbuf, 1,  4,  fp);

      debug("First Four Bytes:");
      // Write the 4 bytes as characters to our debug file
      sprintf(buf, "'%c' '%c' '%c' '%c'", readbuf[0], 
                                          readbuf[1], 
                                          readbuf[2],
                                          readbuf[3]);
      debug(buf);


      // ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW **
      // ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW **
      // Create an instance of our giant structure which we just defined above.
      DDSURFACEDESC2 ddsd;
      // Lets print out out big it is, just for curiosity
      sprintf(buf, "sizeof(DDSURFACEDESC2): %d (bytes)", sizeof(DDSURFACEDESC2)); 
      debug(buf);

      // Read in the header data
      fread(&ddsd, sizeof(ddsd),  1,  fp);
      // Lets write out what we've actually read in!
      sprintf(buf, "dwSize: %d", ddsd.dwSize);
      debug(buf);
      sprintf(buf, "dwFlags: 0x%x", ddsd.dwFlags);
      debug(buf);
      sprintf(buf, "dwHeight: %d", ddsd.dwHeight);
      debug(buf);
      sprintf(buf, "dwWidth: %d", ddsd.dwWidth);
      debug(buf);
      sprintf(buf, "dwLinearSize: %d", ddsd.dwLinearSize);
      debug(buf);
      sprintf(buf, "dwBackBufferCount: %d", ddsd.dwBackBufferCount);
      debug(buf);
      sprintf(buf, "dwMipMapCount: %d", ddsd.dwMipMapCount);
      debug(buf);
      sprintf(buf, "dwAlphaBitDepth: %d", ddsd.dwAlphaBitDepth);
      debug(buf);
      sprintf(buf, "dwReserved: %d", ddsd.dwReserved);
      debug(buf);
      sprintf(buf, "lpSurface: %d", ddsd.lpSurface);
      debug(buf);
      debug("DDCOLORKEY");
      debug("{");
      sprintf(buf, "\tddckCKDestOverlay(low): %d", ddsd.ddckCKDestOverlay.dwColorSpaceLowValue);
      debug(buf);
      sprintf(buf, "\tddckCKDestOverlay(high): %d", ddsd.ddckCKDestOverlay.dwColorSpaceHighValue);
      debug(buf);
      debug("}");
      debug("..."); // To lazy to do these, don't think there important
      sprintf(buf, "ddpfPixelFormat: %d", ddsd.ddpfPixelFormat);
      debug(buf);
      sprintf(buf, "ddsCaps: %d", ddsd.ddsCaps);
      debug(buf);
      sprintf(buf, "dwTextureStage: %d", ddsd.dwTextureStage);
      debug(buf);

      fclose(fp);
}// End of main()

 

output.txt
First Four Bytes:
'D' 'D' 'S' ' '
sizeof(DDSURFACEDESC2): 124 (bytes)
dwSize: 124
dwFlags: 0x81007
dwHeight: 8
dwWidth: 8
dwLinearSize: 256
dwBackBufferCount: 0
dwMipMapCount: 0
dwAlphaBitDepth: 0
dwReserved: 0
lpSurface: 0
DDCOLORKEY
{
     ddckCKDestOverlay(low): 0
     ddckCKDestOverlay(high): 0
}
...
ddpfPixelFormat: 32
ddsCaps: 4096
dwTextureStage: 0

 

So what we have now found, is essentially our basic image information.  We know our image width and height, and we know what type of pixel format its in, which is 32 bit, ARGB, which is what I exported it as.

 

I've modified the code a bit so that I don't automatically write a new line '\n' in my debug text out function, so I can write out the pixel information in a nice tidy square....but of course this means I've had to add the newline comment to all my other lines that write info to the file instead.

 

Also as I said I was going to do above, I just put all those struct files in an include file, 'ddstypes.h'...its tidier I think.

 

We now go about getting the pixel information.  Because where only working with a 8x8 image which I've created, and of course I know what the file format looks like, well then its not so hard to dump the data.

 

code030 : Download Source Code
////////////////////////////////////////////////////////////////////////////////////////
//                                                                                    //
// File:   main.cpp                                                                   //
// Author: bkenwright@xbdev.net                                                       //
//                                                                                    //
////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>                             // So we can use fopen and fread

// ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW *
// ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW *
#include "ddstypes.h"                          // dds structures / defines

// Simple Debug Feedback - writes to a simple text file located where the exe is.
void debug(char* str)
{
      FILE* fp = fopen("output.txt", "a+");
      fprintf(fp, "%s", str);
      fclose(fp);
}// End of debug(..)

char buf[500]; // Temp buffer for text output



// Program Entry Point - Where we always start!
void main()
{
      FILE* fp = fopen("cross.dds", "rb");
      char readbuf[4]; // Temp Buffer
      //      +-  Buffer to put the data in
      //      |      +- Size of each value read in bytes (1 byte each)
      //      |      |   +- How many times to read in the value
      //      |      |   |    +- Stream souce
      //      |      |   |    |
      fread(readbuf, 1,  4,  fp);

      debug("First Four Bytes:\n");
      // Write the 4 bytes as characters to our debug file
      sprintf(buf, "'%c' '%c' '%c' '%c'\n", readbuf[0], 
                                          readbuf[1], 
                                          readbuf[2],
                                          readbuf[3]);
      debug(buf);

      // Create an instance of our giant structure which we just defined above.
      DDSURFACEDESC2 ddsd;
      // Lets print out out big it is, just for curiosity
      sprintf(buf, "sizeof(DDSURFACEDESC2): %d (bytes)", sizeof(DDSURFACEDESC2)); 
      debug(buf);

      // Read in the header data
      fread(&ddsd, sizeof(ddsd),  1,  fp);
      // Lets write out what we've actually read in!
      sprintf(buf, "dwSize: %d\n", ddsd.dwSize);
      debug(buf);
      sprintf(buf, "dwFlags: 0x%x\n", ddsd.dwFlags);
      debug(buf);
      sprintf(buf, "dwHeight: %d\n", ddsd.dwHeight);
      debug(buf);
      sprintf(buf, "dwWidth: %d\n", ddsd.dwWidth);
      debug(buf);
      sprintf(buf, "dwLinearSize: %d\n", ddsd.dwLinearSize);
      debug(buf);
      sprintf(buf, "dwBackBufferCount: %d\n", ddsd.dwBackBufferCount);
      debug(buf);
      sprintf(buf, "dwMipMapCount: %d\n", ddsd.dwMipMapCount);
      debug(buf);
      sprintf(buf, "dwAlphaBitDepth: %d\n", ddsd.dwAlphaBitDepth);
      debug(buf);
      sprintf(buf, "dwReserved: %d\n", ddsd.dwReserved);
      debug(buf);
      sprintf(buf, "lpSurface: %d\n", ddsd.lpSurface);
      debug(buf);
      debug("...\n"); // This information really isn't needed
      sprintf(buf, "ddpfPixelFormat: %d\n", ddsd.ddpfPixelFormat);
      debug(buf);
      sprintf(buf, "ddsCaps: %d\n", ddsd.ddsCaps);
      debug(buf);
      sprintf(buf, "dwTextureStage: %d\n", ddsd.dwTextureStage);
      debug(buf);

      // ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW **
      // ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW ** NEW **
      // Lets get the image pixel information
      // If we have mipmaps - i.e. multiple resolution images saved, then we'll have
      // additional data to get.  Not as complicated as it sounds.
      int bufSize = ddsd.dwMipMapCount > 1 ? ddsd.dwLinearSize * 2 : ddsd.dwLinearSize;
      char* buffer = new char[bufSize];
      fread(buffer, 1,  bufSize,  fp);

      // Since we have ARGB pixels, its easier to just put it to a dword
      DWORD* pixels = (DWORD*)buffer;
      // Well just dump it as hex for now
      debug("Pixel Data:\n");
      for (int y=0; y<ddsd.dwHeight; y++)
      {
          debug("\t");
          for (int x=0; x<ddsd.dwWidth; x++)
          {
              sprintf(buf, "0x%.8x ", pixels[0]);
              debug(buf);
              pixels++;
          }//End for x
          debug("\n");
      }//End for y

      delete[] buffer; // Always remember to tidy up

      fclose(fp);
}// End of main()

 

output.txt
First Four Bytes:
'D' 'D' 'S' ' '
sizeof(DDSURFACEDESC2): 124 (bytes)dwSize: 124
dwFlags: 0x81007
dwHeight: 8
dwWidth: 8
dwLinearSize: 256
dwBackBufferCount: 0
dwMipMapCount: 0
dwAlphaBitDepth: 0
dwReserved: 0
lpSurface: 0
...
ddpfPixelFormat: 32
ddsCaps: 4096
dwTextureStage: 0
Pixel Data:
	0xffff0000 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffff0000 
	0xffffffff 0xffff0000 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffff0000 0xffffffff 
	0xffffffff 0xffffffff 0xffff0000 0xffffffff 0xffffffff 0xffff0000 0xffffffff 0xffffffff 
	0xffffffff 0xffffffff 0xffffffff 0xffff0000 0xffff0000 0xffffffff 0xffffffff 0xffffffff 
	0xffffffff 0xffffffff 0xffffffff 0xffff0000 0xffff0000 0xffffffff 0xffffffff 0xffffffff 
	0xffffffff 0xffffffff 0xffff0000 0xffffffff 0xffffffff 0xffff0000 0xffffffff 0xffffffff 
	0xffffffff 0xffff0000 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffff0000 0xffffffff 
	0xffff0000 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffffffff 0xffff0000 

 

Well as we can see, we have got our pixel info!  Its not so bad now, basically all we need to do now, is do some checks for compression and various file formats and mipmaps.

 

 

TODO :

- Uncompression Functions

- Various Pixel Formats

- MipMaps

 

 

 

 

 

 

 

 
Advert (Support Website)

 
 Visitor:
Copyright (c) 2002-2024 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.