#include "StdAfx.h" #include "EtDDSLoader.h" BOOL CEtDDSLoader::LoadTextureDataFromFile( CHAR* szFileName, BYTE** ppHeapData, DDSURFACEDESC2_32BIT** ppSurfDesc, BYTE** ppBitData, UINT* pBitSize ) { *ppHeapData = NULL; *ppSurfDesc = NULL; *ppBitData = NULL; *pBitSize = 0; // open the file HANDLE hFile = CreateFile( szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL ); if( INVALID_HANDLE_VALUE == hFile ) return FALSE; // Get the file size LARGE_INTEGER FileSize; GetFileSizeEx( hFile, &FileSize ); // create enough space for the file data *ppHeapData = new BYTE[ FileSize.LowPart ]; if( !( *ppHeapData ) ) return FALSE; // read the data in DWORD BytesRead; if( !ReadFile( hFile, *ppHeapData, FileSize.LowPart, &BytesRead, NULL ) ) return FALSE; // DDS files always start with the same magic number DWORD dwMagicNumber = *( DWORD* )( *ppHeapData ); if( dwMagicNumber != 0x20534444 ) return FALSE; // setup the pointers in the process request *ppSurfDesc = ( DDSURFACEDESC2_32BIT* )( *ppHeapData + sizeof( DWORD ) ); *ppBitData = *ppHeapData + sizeof( DWORD ) + sizeof( DDSURFACEDESC2_32BIT ); *pBitSize = FileSize.LowPart - sizeof( DWORD ) - sizeof( DDSURFACEDESC2_32BIT ); CloseHandle( hFile ); return TRUE; } //-------------------------------------------------------------------------------------- // Return the BPP for a particular format //-------------------------------------------------------------------------------------- UINT CEtDDSLoader::BitsPerPixel( D3DFORMAT fmt ) { UINT fmtU = ( UINT )fmt; switch( fmtU ) { case D3DFMT_A32B32G32R32F: return 128; case D3DFMT_A16B16G16R16: case D3DFMT_Q16W16V16U16: case D3DFMT_A16B16G16R16F: case D3DFMT_G32R32F: return 64; case D3DFMT_A8R8G8B8: case D3DFMT_X8R8G8B8: case D3DFMT_A2B10G10R10: case D3DFMT_A8B8G8R8: case D3DFMT_X8B8G8R8: case D3DFMT_G16R16: case D3DFMT_A2R10G10B10: case D3DFMT_Q8W8V8U8: case D3DFMT_V16U16: case D3DFMT_X8L8V8U8: case D3DFMT_A2W10V10U10: case D3DFMT_D32: case D3DFMT_D24S8: case D3DFMT_D24X8: case D3DFMT_D24X4S4: case D3DFMT_D32F_LOCKABLE: case D3DFMT_D24FS8: case D3DFMT_INDEX32: case D3DFMT_G16R16F: case D3DFMT_R32F: return 32; case D3DFMT_R8G8B8: return 24; case D3DFMT_A4R4G4B4: case D3DFMT_X4R4G4B4: case D3DFMT_R5G6B5: case D3DFMT_L16: case D3DFMT_A8L8: case D3DFMT_X1R5G5B5: case D3DFMT_A1R5G5B5: case D3DFMT_A8R3G3B2: case D3DFMT_V8U8: case D3DFMT_CxV8U8: case D3DFMT_L6V5U5: case D3DFMT_G8R8_G8B8: case D3DFMT_R8G8_B8G8: case D3DFMT_D16_LOCKABLE: case D3DFMT_D15S1: case D3DFMT_D16: case D3DFMT_INDEX16: case D3DFMT_R16F: case D3DFMT_YUY2: return 16; case D3DFMT_R3G3B2: case D3DFMT_A8: case D3DFMT_A8P8: case D3DFMT_P8: case D3DFMT_L8: case D3DFMT_A4L4: return 8; case D3DFMT_DXT1: return 4; case D3DFMT_DXT2: case D3DFMT_DXT3: case D3DFMT_DXT4: case D3DFMT_DXT5: return 8; // From DX docs, reference/d3d/enums/d3dformat.asp // (note how it says that D3DFMT_R8G8_B8G8 is "A 16-bit packed RGB format analogous to UYVY (U0Y0, V0Y1, U2Y2, and so on)") case D3DFMT_UYVY: return 16; // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directshow/htm/directxvideoaccelerationdxvavideosubtypes.asp case MAKEFOURCC( 'A', 'I', '4', '4' ): case MAKEFOURCC( 'I', 'A', '4', '4' ): return 8; case MAKEFOURCC( 'Y', 'V', '1', '2' ): return 12; default: assert( FALSE ); // unhandled format return 0; } } //-------------------------------------------------------------------------------------- // Get surface information for a particular format //-------------------------------------------------------------------------------------- void CEtDDSLoader::GetSurfaceInfo( UINT width, UINT height, D3DFORMAT fmt, UINT* pNumBytes, UINT* pRowBytes, UINT* pNumRows ) { UINT numBytes = 0; UINT rowBytes = 0; UINT numRows = 0; // From the DXSDK docs: // // When computing DXTn compressed sizes for non-square textures, the // following formula should be used at each mipmap level: // // max(1, width ?4) x max(1, height ?4) x 8(DXT1) or 16(DXT2-5) // // The pitch for DXTn formats is different from what was returned in // Microsoft DirectX 7.0. It now refers the pitch of a row of blocks. // For example, if you have a width of 16, then you will have a pitch // of four blocks (4*8 for DXT1, 4*16 for DXT2-5.)" if( fmt == D3DFMT_DXT1 || fmt == D3DFMT_DXT2 || fmt == D3DFMT_DXT3 || fmt == D3DFMT_DXT4 || fmt == D3DFMT_DXT5 ) { // Note: we support width and/or height being 0 in order to compute // offsets in functions like CBufferLockEntry::CopyBLEToPerfectSizedBuffer(). int numBlocksWide = 0; if( width > 0 ) numBlocksWide = max( 1, width / 4 ); int numBlocksHigh = 0; if( height > 0 ) numBlocksHigh = max( 1, height / 4 ); //int numBlocks = numBlocksWide * numBlocksHigh; int numBytesPerBlock = ( fmt == D3DFMT_DXT1 ? 8 : 16 ); rowBytes = numBlocksWide * numBytesPerBlock; numRows = numBlocksHigh; } else { UINT bpp = BitsPerPixel( fmt ); rowBytes = ( width * bpp + 7 ) / 8; // round up to nearest byte numRows = height; } numBytes = rowBytes * numRows; if( pNumBytes != NULL ) *pNumBytes = numBytes; if( pRowBytes != NULL ) *pRowBytes = rowBytes; if( pNumRows != NULL ) *pNumRows = numRows; } //-------------------------------------------------------------------------------------- #define ISBITMASK( r,g,b,a ) ( ddpf.dwRBitMask == r && ddpf.dwGBitMask == g && ddpf.dwBBitMask == b && ddpf.dwRGBAlphaBitMask == a ) //-------------------------------------------------------------------------------------- D3DFORMAT CEtDDSLoader::GetD3D9Format( DDPIXELFORMAT ddpf ) { if( ddpf.dwFlags & DDPF_RGB ) //rgb codes // Only do the more common formats { if( 32 == ddpf.dwRGBBitCount ) { if( ISBITMASK(0x00ff0000,0x0000ff00,0x000000ff,0xff000000) ) return D3DFMT_A8R8G8B8; if( ISBITMASK(0x00ff0000,0x0000ff00,0x000000ff,0x00000000) ) return D3DFMT_X8R8G8B8; if( ISBITMASK(0x000000ff,0x00ff0000,0x0000ff00,0xff000000) ) return D3DFMT_A8B8G8R8; if( ISBITMASK(0x000000ff,0x00ff0000,0x0000ff00,0x00000000) ) return D3DFMT_X8B8G8R8; if( ISBITMASK(0xffffffff,0x00000000,0x00000000,0x00000000) ) return D3DFMT_R32F; } if( 24 == ddpf.dwRGBBitCount ) { if( ISBITMASK(0x00ff0000,0x0000ff00,0x000000ff,0x00000000) ) return D3DFMT_R8G8B8; } if( 16 == ddpf.dwRGBBitCount ) { if( ISBITMASK(0x0000F800,0x000007E0,0x0000001F,0x00000000) ) return D3DFMT_R5G6B5; } } else if( ddpf.dwFlags & DDPF_LUMINANCE ) { if( 8 == ddpf.dwRGBBitCount ) { return D3DFMT_L8; } } else if( ddpf.dwFlags & DDPF_ALPHA ) { if( 8 == ddpf.dwRGBBitCount ) { return D3DFMT_A8; } } else if( ddpf.dwFlags & DDPF_FOURCC ) //fourcc codes (dxtn) { if( MAKEFOURCC( 'D', 'X', 'T', '1' ) == ddpf.dwFourCC ) return D3DFMT_DXT1; if( MAKEFOURCC( 'D', 'X', 'T', '2' ) == ddpf.dwFourCC ) return D3DFMT_DXT2; if( MAKEFOURCC( 'D', 'X', 'T', '3' ) == ddpf.dwFourCC ) return D3DFMT_DXT3; if( MAKEFOURCC( 'D', 'X', 'T', '4' ) == ddpf.dwFourCC ) return D3DFMT_DXT4; if( MAKEFOURCC( 'D', 'X', 'T', '5' ) == ddpf.dwFourCC ) return D3DFMT_DXT5; } return D3DFMT_UNKNOWN; } //-------------------------------------------------------------------------------------- HRESULT CEtDDSLoader::CreateTextureFromDDS( LPDIRECT3DDEVICE9 pDev, DDSURFACEDESC2_32BIT* pSurfDesc, BYTE* pBitData, UINT BitSize, LPDIRECT3DTEXTURE9* ppTex ) { HRESULT hr = S_OK; D3DLOCKED_RECT LockedRect; UINT iWidth = pSurfDesc->dwWidth; UINT iHeight = pSurfDesc->dwHeight; UINT iMipCount = pSurfDesc->dwMipMapCount; if( 0 == iMipCount ) iMipCount = 1; D3DFORMAT fmt = GetD3D9Format( pSurfDesc->ddpfPixelFormat ); // Create the texture LPDIRECT3DTEXTURE9 pTexture; LPDIRECT3DTEXTURE9 pStagingTexture; hr = pDev->CreateTexture( iWidth, iHeight, iMipCount, 0, // usage fmt, D3DPOOL_DEFAULT, &pTexture, NULL ); if( FAILED( hr ) ) return hr; hr = pDev->CreateTexture( iWidth, iHeight, iMipCount, 0, // usage fmt, D3DPOOL_SYSTEMMEM, &pStagingTexture, NULL ); if( FAILED( hr ) ) return hr; // Lock, fill, unlock UINT RowBytes, NumRows; BYTE* pSrcBits = pBitData; for( UINT i = 0; i < iMipCount; i++ ) { GetSurfaceInfo( iWidth, iHeight, fmt, NULL, &RowBytes, &NumRows ); if( SUCCEEDED( pStagingTexture->LockRect( i, &LockedRect, NULL, 0 ) ) ) { BYTE* pDestBits = ( BYTE* )LockedRect.pBits; // Copy stride line by line for( UINT h = 0; h < NumRows; h++ ) { CopyMemory( pDestBits, pSrcBits, RowBytes ); pDestBits += LockedRect.Pitch; pSrcBits += RowBytes; } pStagingTexture->UnlockRect( i ); } iWidth = iWidth >> 1; iHeight = iHeight >> 1; if( iWidth == 0 ) iWidth = 1; if( iHeight == 0 ) iHeight = 1; } hr = pDev->UpdateTexture( pStagingTexture, pTexture ); SAFE_RELEASE( pStagingTexture ); if( FAILED( hr ) ) return hr; // Set the result *ppTex = pTexture; return hr; } HRESULT CEtDDSLoader::CreateDDSTextureFromFile( LPDIRECT3DDEVICE9 pDev, CHAR* szFileName, LPDIRECT3DTEXTURE9* ppTex ) { BYTE* pHeapData = NULL; DDSURFACEDESC2_32BIT* pSurfDesc = NULL; BYTE* pBitData = NULL; UINT BitSize = 0; if( !LoadTextureDataFromFile( szFileName, &pHeapData, &pSurfDesc, &pBitData, &BitSize ) ) return E_FAIL; HRESULT hr = CreateTextureFromDDS( pDev, pSurfDesc, pBitData, BitSize, ppTex ); SAFE_DELETEA( pHeapData ); return hr; }