// MIT license see full LICENSE text at end of file #include "DirectDrawSurface.h" #include "ColorBlock.h" #include "Image.h" #include "BlockDXT.h" #include "PixelFormat.h" #include "nvcore/Debug.h" #include "nvcore/Utils.h" // max #include "nvcore/StdStream.h" #include "nvmath/Vector.inl" #include "nvmath/ftoi.h" #include // memset using namespace nv; namespace { static const uint DDSD_CAPS = 0x00000001U; static const uint DDSD_PIXELFORMAT = 0x00001000U; static const uint DDSD_WIDTH = 0x00000004U; static const uint DDSD_HEIGHT = 0x00000002U; static const uint DDSD_PITCH = 0x00000008U; static const uint DDSD_MIPMAPCOUNT = 0x00020000U; static const uint DDSD_LINEARSIZE = 0x00080000U; static const uint DDSD_DEPTH = 0x00800000U; static const uint DDSCAPS_COMPLEX = 0x00000008U; static const uint DDSCAPS_TEXTURE = 0x00001000U; static const uint DDSCAPS_MIPMAP = 0x00400000U; static const uint DDSCAPS2_VOLUME = 0x00200000U; static const uint DDSCAPS2_CUBEMAP = 0x00000200U; static const uint DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400U; static const uint DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800U; static const uint DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000U; static const uint DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000U; static const uint DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000U; static const uint DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x00008000U; static const uint DDSCAPS2_CUBEMAP_ALL_FACES = 0x0000FC00U; const char * getDxgiFormatString(DXGI_FORMAT dxgiFormat) { #define CASE(format) case DXGI_FORMAT_##format: return #format switch(dxgiFormat) { CASE(UNKNOWN); CASE(R32G32B32A32_TYPELESS); CASE(R32G32B32A32_FLOAT); CASE(R32G32B32A32_UINT); CASE(R32G32B32A32_SINT); CASE(R32G32B32_TYPELESS); CASE(R32G32B32_FLOAT); CASE(R32G32B32_UINT); CASE(R32G32B32_SINT); CASE(R16G16B16A16_TYPELESS); CASE(R16G16B16A16_FLOAT); CASE(R16G16B16A16_UNORM); CASE(R16G16B16A16_UINT); CASE(R16G16B16A16_SNORM); CASE(R16G16B16A16_SINT); CASE(R32G32_TYPELESS); CASE(R32G32_FLOAT); CASE(R32G32_UINT); CASE(R32G32_SINT); CASE(R32G8X24_TYPELESS); CASE(D32_FLOAT_S8X24_UINT); CASE(R32_FLOAT_X8X24_TYPELESS); CASE(X32_TYPELESS_G8X24_UINT); CASE(R10G10B10A2_TYPELESS); CASE(R10G10B10A2_UNORM); CASE(R10G10B10A2_UINT); CASE(R11G11B10_FLOAT); CASE(R8G8B8A8_TYPELESS); CASE(R8G8B8A8_UNORM); CASE(R8G8B8A8_UNORM_SRGB); CASE(R8G8B8A8_UINT); CASE(R8G8B8A8_SNORM); CASE(R8G8B8A8_SINT); CASE(R16G16_TYPELESS); CASE(R16G16_FLOAT); CASE(R16G16_UNORM); CASE(R16G16_UINT); CASE(R16G16_SNORM); CASE(R16G16_SINT); CASE(R32_TYPELESS); CASE(D32_FLOAT); CASE(R32_FLOAT); CASE(R32_UINT); CASE(R32_SINT); CASE(R24G8_TYPELESS); CASE(D24_UNORM_S8_UINT); CASE(R24_UNORM_X8_TYPELESS); CASE(X24_TYPELESS_G8_UINT); CASE(R8G8_TYPELESS); CASE(R8G8_UNORM); CASE(R8G8_UINT); CASE(R8G8_SNORM); CASE(R8G8_SINT); CASE(R16_TYPELESS); CASE(R16_FLOAT); CASE(D16_UNORM); CASE(R16_UNORM); CASE(R16_UINT); CASE(R16_SNORM); CASE(R16_SINT); CASE(R8_TYPELESS); CASE(R8_UNORM); CASE(R8_UINT); CASE(R8_SNORM); CASE(R8_SINT); CASE(A8_UNORM); CASE(R1_UNORM); CASE(R9G9B9E5_SHAREDEXP); CASE(R8G8_B8G8_UNORM); CASE(G8R8_G8B8_UNORM); CASE(BC1_TYPELESS); CASE(BC1_UNORM); CASE(BC1_UNORM_SRGB); CASE(BC2_TYPELESS); CASE(BC2_UNORM); CASE(BC2_UNORM_SRGB); CASE(BC3_TYPELESS); CASE(BC3_UNORM); CASE(BC3_UNORM_SRGB); CASE(BC4_TYPELESS); CASE(BC4_UNORM); CASE(BC4_SNORM); CASE(BC5_TYPELESS); CASE(BC5_UNORM); CASE(BC5_SNORM); CASE(B5G6R5_UNORM); CASE(B5G5R5A1_UNORM); CASE(B8G8R8A8_UNORM); CASE(B8G8R8X8_UNORM); default: return "UNKNOWN"; } #undef CASE } const char * getD3d10ResourceDimensionString(DDS_DIMENSION resourceDimension) { switch(resourceDimension) { default: case DDS_DIMENSION_UNKNOWN: return "UNKNOWN"; case DDS_DIMENSION_BUFFER: return "BUFFER"; case DDS_DIMENSION_TEXTURE1D: return "TEXTURE1D"; case DDS_DIMENSION_TEXTURE2D: return "TEXTURE2D"; case DDS_DIMENSION_TEXTURE3D: return "TEXTURE3D"; } } static uint pixelSize(D3DFORMAT format) { if (format == D3DFMT_R16F) return 8*2; if (format == D3DFMT_G16R16F) return 8*4; if (format == D3DFMT_A16B16G16R16F) return 8*8; if (format == D3DFMT_R32F) return 8*4; if (format == D3DFMT_G32R32F) return 8*8; if (format == D3DFMT_A32B32G32R32F) return 8*16; if (format == D3DFMT_R8G8B8) return 8*3; if (format == D3DFMT_A8R8G8B8) return 8*4; if (format == D3DFMT_X8R8G8B8) return 8*4; if (format == D3DFMT_R5G6B5) return 8*2; if (format == D3DFMT_X1R5G5B5) return 8*2; if (format == D3DFMT_A1R5G5B5) return 8*2; if (format == D3DFMT_A4R4G4B4) return 8*2; if (format == D3DFMT_R3G3B2) return 8*1; if (format == D3DFMT_A8) return 8*1; if (format == D3DFMT_A8R3G3B2) return 8*2; if (format == D3DFMT_X4R4G4B4) return 8*2; if (format == D3DFMT_A2B10G10R10) return 8*4; if (format == D3DFMT_A8B8G8R8) return 8*4; if (format == D3DFMT_X8B8G8R8) return 8*4; if (format == D3DFMT_G16R16) return 8*4; if (format == D3DFMT_A2R10G10B10) return 8*4; if (format == D3DFMT_A2B10G10R10) return 8*4; if (format == D3DFMT_L8) return 8*1; if (format == D3DFMT_L16) return 8*2; return 0; } static uint pixelSize(DXGI_FORMAT format) { switch(format) { case DXGI_FORMAT_R32G32B32A32_TYPELESS: case DXGI_FORMAT_R32G32B32A32_FLOAT: case DXGI_FORMAT_R32G32B32A32_UINT: case DXGI_FORMAT_R32G32B32A32_SINT: return 8*16; case DXGI_FORMAT_R32G32B32_TYPELESS: case DXGI_FORMAT_R32G32B32_FLOAT: case DXGI_FORMAT_R32G32B32_UINT: case DXGI_FORMAT_R32G32B32_SINT: return 8*12; case DXGI_FORMAT_R16G16B16A16_TYPELESS: case DXGI_FORMAT_R16G16B16A16_FLOAT: case DXGI_FORMAT_R16G16B16A16_UNORM: case DXGI_FORMAT_R16G16B16A16_UINT: case DXGI_FORMAT_R16G16B16A16_SNORM: case DXGI_FORMAT_R16G16B16A16_SINT: case DXGI_FORMAT_R32G32_TYPELESS: case DXGI_FORMAT_R32G32_FLOAT: case DXGI_FORMAT_R32G32_UINT: case DXGI_FORMAT_R32G32_SINT: case DXGI_FORMAT_R32G8X24_TYPELESS: case DXGI_FORMAT_D32_FLOAT_S8X24_UINT: case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: return 8*8; case DXGI_FORMAT_R10G10B10A2_TYPELESS: case DXGI_FORMAT_R10G10B10A2_UNORM: case DXGI_FORMAT_R10G10B10A2_UINT: case DXGI_FORMAT_R11G11B10_FLOAT: case DXGI_FORMAT_R8G8B8A8_TYPELESS: case DXGI_FORMAT_R8G8B8A8_UNORM: case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: case DXGI_FORMAT_R8G8B8A8_UINT: case DXGI_FORMAT_R8G8B8A8_SNORM: case DXGI_FORMAT_R8G8B8A8_SINT: case DXGI_FORMAT_R16G16_TYPELESS: case DXGI_FORMAT_R16G16_FLOAT: case DXGI_FORMAT_R16G16_UNORM: case DXGI_FORMAT_R16G16_UINT: case DXGI_FORMAT_R16G16_SNORM: case DXGI_FORMAT_R16G16_SINT: case DXGI_FORMAT_R32_TYPELESS: case DXGI_FORMAT_D32_FLOAT: case DXGI_FORMAT_R32_FLOAT: case DXGI_FORMAT_R32_UINT: case DXGI_FORMAT_R32_SINT: case DXGI_FORMAT_R24G8_TYPELESS: case DXGI_FORMAT_D24_UNORM_S8_UINT: case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: case DXGI_FORMAT_X24_TYPELESS_G8_UINT: return 8*4; case DXGI_FORMAT_R8G8_TYPELESS: case DXGI_FORMAT_R8G8_UNORM: case DXGI_FORMAT_R8G8_UINT: case DXGI_FORMAT_R8G8_SNORM: case DXGI_FORMAT_R8G8_SINT: case DXGI_FORMAT_R16_TYPELESS: case DXGI_FORMAT_R16_FLOAT: case DXGI_FORMAT_D16_UNORM: case DXGI_FORMAT_R16_UNORM: case DXGI_FORMAT_R16_UINT: case DXGI_FORMAT_R16_SNORM: case DXGI_FORMAT_R16_SINT: return 8*2; case DXGI_FORMAT_R8_TYPELESS: case DXGI_FORMAT_R8_UNORM: case DXGI_FORMAT_R8_UINT: case DXGI_FORMAT_R8_SNORM: case DXGI_FORMAT_R8_SINT: case DXGI_FORMAT_A8_UNORM: return 8*1; case DXGI_FORMAT_R1_UNORM: return 1; case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: return 8*4; case DXGI_FORMAT_R8G8_B8G8_UNORM: case DXGI_FORMAT_G8R8_G8B8_UNORM: return 8*4; case DXGI_FORMAT_B5G6R5_UNORM: case DXGI_FORMAT_B5G5R5A1_UNORM: return 8*2; case DXGI_FORMAT_B8G8R8A8_UNORM: case DXGI_FORMAT_B8G8R8X8_UNORM: return 8*4; case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: case DXGI_FORMAT_B8G8R8A8_TYPELESS: case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: case DXGI_FORMAT_B8G8R8X8_TYPELESS: case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: return 8*4; default: return 0; } nvUnreachable(); } static bool hasAlpha(DXGI_FORMAT format) { switch(format) { case DXGI_FORMAT_R32G32B32A32_TYPELESS: case DXGI_FORMAT_R32G32B32A32_FLOAT: case DXGI_FORMAT_R32G32B32A32_UINT: case DXGI_FORMAT_R32G32B32A32_SINT: case DXGI_FORMAT_R16G16B16A16_TYPELESS: case DXGI_FORMAT_R16G16B16A16_FLOAT: case DXGI_FORMAT_R16G16B16A16_UNORM: case DXGI_FORMAT_R16G16B16A16_UINT: case DXGI_FORMAT_R16G16B16A16_SNORM: case DXGI_FORMAT_R16G16B16A16_SINT: case DXGI_FORMAT_R10G10B10A2_TYPELESS: case DXGI_FORMAT_R10G10B10A2_UNORM: case DXGI_FORMAT_R10G10B10A2_UINT: case DXGI_FORMAT_R8G8B8A8_TYPELESS: case DXGI_FORMAT_R8G8B8A8_UNORM: case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: case DXGI_FORMAT_R8G8B8A8_UINT: case DXGI_FORMAT_R8G8B8A8_SNORM: case DXGI_FORMAT_R8G8B8A8_SINT: case DXGI_FORMAT_A8_UNORM: case DXGI_FORMAT_B5G5R5A1_UNORM: case DXGI_FORMAT_B8G8R8A8_UNORM: //case DXGI_FORMAT_B8G8R8X8_UNORM: case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: case DXGI_FORMAT_B8G8R8A8_TYPELESS: case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: //case DXGI_FORMAT_B8G8R8X8_TYPELESS: //case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: case DXGI_FORMAT_BC1_UNORM: // BC1a case DXGI_FORMAT_BC1_UNORM_SRGB: case DXGI_FORMAT_BC2_UNORM: case DXGI_FORMAT_BC2_UNORM_SRGB: case DXGI_FORMAT_BC3_UNORM: case DXGI_FORMAT_BC3_UNORM_SRGB: case DXGI_FORMAT_BC7_UNORM: case DXGI_FORMAT_BC7_UNORM_SRGB: return true; } return false; } static bool hasAlpha(D3DFORMAT format) { switch(format) { case D3DFMT_A8R8G8B8: case D3DFMT_A1R5G5B5: case D3DFMT_A4R4G4B4: case D3DFMT_A8: case D3DFMT_A8R3G3B2: case D3DFMT_A2B10G10R10: case D3DFMT_A8B8G8R8: case D3DFMT_A2R10G10B10: case D3DFMT_A16B16G16R16: case D3DFMT_A8P8: case D3DFMT_A8L8: case D3DFMT_A4L4: case D3DFMT_A16B16G16R16F: case D3DFMT_A32B32G32R32F: case FOURCC_DXT1: // DXT1a case FOURCC_DXT2: case FOURCC_DXT3: case FOURCC_DXT4: case FOURCC_DXT5: case FOURCC_BC7L: return true; } return false; } } // namespace namespace nv { static Stream & operator<< (Stream & s, DDSPixelFormat & pf) { nvStaticCheck(sizeof(DDSPixelFormat) == 32); s << pf.size; s << pf.flags; s << pf.fourcc; s << pf.bitcount; s.serialize(&pf.rmask, sizeof(pf.rmask)); s.serialize(&pf.gmask, sizeof(pf.gmask)); s.serialize(&pf.bmask, sizeof(pf.bmask)); s.serialize(&pf.amask, sizeof(pf.amask)); // s << pf.rmask; // s << pf.gmask; // s << pf.bmask; // s << pf.amask; return s; } static Stream & operator<< (Stream & s, DDSCaps & caps) { nvStaticCheck(sizeof(DDSCaps) == 16); s << caps.caps1; s << caps.caps2; s << caps.caps3; s << caps.caps4; return s; } static Stream & operator<< (Stream & s, DDSHeader10 & header) { nvStaticCheck(sizeof(DDSHeader10) == 20); s << header.dxgiFormat; s << header.resourceDimension; s << header.miscFlag; s << header.arraySize; s << header.reserved; return s; } Stream & operator<< (Stream & s, DDSHeader & header) { nvStaticCheck(sizeof(DDSHeader) == 148); s << header.fourcc; s << header.size; s << header.flags; s << header.height; s << header.width; s << header.pitch; s << header.depth; s << header.mipmapcount; for (int i = 0; i < 11; i++) { s << header.reserved[i]; } s << header.pf; s << header.caps; s << header.notused; if (header.hasDX10Header()) { s << header.header10; } return s; } } // nv namespace namespace { struct FormatDescriptor { uint d3d9Format; uint dxgiFormat; RGBAPixelFormat pixelFormat; }; static const FormatDescriptor s_formats[] = { { D3DFMT_R8G8B8, DXGI_FORMAT_UNKNOWN, { 24, 0xFF0000, 0xFF00, 0xFF, 0 } }, { D3DFMT_A8R8G8B8, DXGI_FORMAT_B8G8R8A8_UNORM, { 32, 0xFF0000, 0xFF00, 0xFF, 0xFF000000 } }, { D3DFMT_X8R8G8B8, DXGI_FORMAT_B8G8R8X8_UNORM, { 32, 0xFF0000, 0xFF00, 0xFF, 0 } }, { D3DFMT_R5G6B5, DXGI_FORMAT_B5G6R5_UNORM, { 16, 0xF800, 0x7E0, 0x1F, 0 } }, { D3DFMT_X1R5G5B5, DXGI_FORMAT_UNKNOWN, { 16, 0x7C00, 0x3E0, 0x1F, 0 } }, { D3DFMT_A1R5G5B5, DXGI_FORMAT_B5G5R5A1_UNORM, { 16, 0x7C00, 0x3E0, 0x1F, 0x8000 } }, { D3DFMT_A4R4G4B4, DXGI_FORMAT_UNKNOWN, { 16, 0xF00, 0xF0, 0xF, 0xF000 } }, { D3DFMT_R3G3B2, DXGI_FORMAT_UNKNOWN, { 8, 0xE0, 0x1C, 0x3, 0 } }, { D3DFMT_A8, DXGI_FORMAT_A8_UNORM, { 8, 0, 0, 0, 8 } }, { D3DFMT_A8R3G3B2, DXGI_FORMAT_UNKNOWN, { 16, 0xE0, 0x1C, 0x3, 0xFF00 } }, { D3DFMT_X4R4G4B4, DXGI_FORMAT_UNKNOWN, { 16, 0xF00, 0xF0, 0xF, 0 } }, { D3DFMT_A2B10G10R10, DXGI_FORMAT_R10G10B10A2_UNORM, { 32, 0x3FF, 0xFFC00, 0x3FF00000, 0xC0000000 } }, { D3DFMT_A8B8G8R8, DXGI_FORMAT_R8G8B8A8_UNORM, { 32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 } }, { D3DFMT_X8B8G8R8, DXGI_FORMAT_UNKNOWN, { 32, 0xFF, 0xFF00, 0xFF0000, 0 } }, { D3DFMT_G16R16, DXGI_FORMAT_R16G16_UNORM, { 32, 0xFFFF, 0xFFFF0000, 0, 0 } }, { D3DFMT_A2R10G10B10, DXGI_FORMAT_UNKNOWN, { 32, 0x3FF00000, 0xFFC00, 0x3FF, 0xC0000000 } }, { D3DFMT_A2B10G10R10, DXGI_FORMAT_UNKNOWN, { 32, 0x3FF, 0xFFC00, 0x3FF00000, 0xC0000000 } }, { D3DFMT_L8, DXGI_FORMAT_R8_UNORM , { 8, 0xFF, 0, 0, 0 } }, { D3DFMT_L16, DXGI_FORMAT_R16_UNORM, { 16, 0xFFFF, 0, 0, 0 } }, { D3DFMT_A8L8, 0, { 16, 0xFF, 0, 0, 0xFF00 } }, { 0, DXGI_FORMAT_R8G8_UNORM, { 16, 0xFF, 0xFF00, 0, 0 } }, }; static const uint s_formatCount = NV_ARRAY_SIZE(s_formats); } // namespace uint nv::findD3D9Format(uint bitcount, uint rmask, uint gmask, uint bmask, uint amask) { for (int i = 0; i < s_formatCount; i++) { if (s_formats[i].pixelFormat.bitcount == bitcount && s_formats[i].pixelFormat.rmask == rmask && s_formats[i].pixelFormat.gmask == gmask && s_formats[i].pixelFormat.bmask == bmask && s_formats[i].pixelFormat.amask == amask) { return s_formats[i].d3d9Format; } } return 0; } uint nv::findDXGIFormat(uint bitcount, uint rmask, uint gmask, uint bmask, uint amask) { for (int i = 0; i < s_formatCount; i++) { if (s_formats[i].pixelFormat.bitcount == bitcount && s_formats[i].pixelFormat.rmask == rmask && s_formats[i].pixelFormat.gmask == gmask && s_formats[i].pixelFormat.bmask == bmask && s_formats[i].pixelFormat.amask == amask) { return s_formats[i].dxgiFormat; } } return DXGI_FORMAT_UNKNOWN; } const RGBAPixelFormat *nv::findDXGIPixelFormat(uint dxgiFormat) { for (int i = 0; i < s_formatCount; i++) { if (s_formats[i].dxgiFormat == dxgiFormat) { return &s_formats[i].pixelFormat; } } return NULL; } const RGBAPixelFormat *nv::findD3D9PixelFormat(uint d3d9Format) { for (int i = 0; i < s_formatCount; i++) { if (s_formats[i].d3d9Format == d3d9Format) { return &s_formats[i].pixelFormat; } } return NULL; } DDSHeader::DDSHeader() { this->fourcc = FOURCC_DDS; this->size = 124; this->flags = (DDSD_CAPS|DDSD_PIXELFORMAT); this->height = 0; this->width = 0; this->pitch = 0; this->depth = 0; this->mipmapcount = 0; memset(this->reserved, 0, sizeof(this->reserved)); // Store version information on the reserved header attributes. this->reserved[9] = FOURCC_NVTT; this->reserved[10] = (2 << 16) | (1 << 8) | (2); // major.minor.revision this->pf.size = 32; this->pf.flags = 0; this->pf.fourcc = 0; this->pf.bitcount = 0; this->pf.rmask = 0; this->pf.gmask = 0; this->pf.bmask = 0; this->pf.amask = 0; this->caps.caps1 = DDSCAPS_TEXTURE; this->caps.caps2 = 0; this->caps.caps3 = 0; this->caps.caps4 = 0; this->notused = 0; this->header10.dxgiFormat = DXGI_FORMAT_UNKNOWN; this->header10.resourceDimension = DDS_DIMENSION_UNKNOWN; this->header10.miscFlag = 0; this->header10.arraySize = 0; this->header10.reserved = 0; } void DDSHeader::setWidth(uint w) { this->flags |= DDSD_WIDTH; this->width = w; } void DDSHeader::setHeight(uint h) { this->flags |= DDSD_HEIGHT; this->height = h; } void DDSHeader::setDepth(uint d) { this->flags |= DDSD_DEPTH; this->depth = d; } void DDSHeader::setMipmapCount(uint count) { if (count == 0 || count == 1) { this->flags &= ~DDSD_MIPMAPCOUNT; this->mipmapcount = 1; if (this->caps.caps2 == 0) { this->caps.caps1 = DDSCAPS_TEXTURE; } else { this->caps.caps1 = DDSCAPS_TEXTURE | DDSCAPS_COMPLEX; } } else { this->flags |= DDSD_MIPMAPCOUNT; this->mipmapcount = count; this->caps.caps1 |= DDSCAPS_COMPLEX | DDSCAPS_MIPMAP; } } void DDSHeader::setTexture2D() { this->header10.resourceDimension = DDS_DIMENSION_TEXTURE2D; this->header10.miscFlag = 0; this->header10.arraySize = 1; } void DDSHeader::setTexture3D() { this->caps.caps2 = DDSCAPS2_VOLUME; this->header10.resourceDimension = DDS_DIMENSION_TEXTURE3D; this->header10.miscFlag = 0; this->header10.arraySize = 1; } void DDSHeader::setTextureCube() { this->caps.caps1 |= DDSCAPS_COMPLEX; this->caps.caps2 = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_ALL_FACES; this->header10.resourceDimension = DDS_DIMENSION_TEXTURE2D; this->header10.miscFlag = DDS_MISC_TEXTURECUBE; this->header10.arraySize = 1; } void DDSHeader::setTextureArray(int imageCount) { this->header10.resourceDimension = DDS_DIMENSION_TEXTURE2D; this->header10.arraySize = imageCount; } void DDSHeader::setLinearSize(uint size) { this->flags &= ~DDSD_PITCH; this->flags |= DDSD_LINEARSIZE; this->pitch = size; } void DDSHeader::setPitch(uint pitch) { this->flags &= ~DDSD_LINEARSIZE; this->flags |= DDSD_PITCH; this->pitch = pitch; } void DDSHeader::setFourCC(uint8 c0, uint8 c1, uint8 c2, uint8 c3) { // set fourcc pixel format. this->pf.flags = DDPF_FOURCC; this->pf.fourcc = NV_MAKEFOURCC(c0, c1, c2, c3); this->pf.bitcount = 0; this->pf.rmask = 0; this->pf.gmask = 0; this->pf.bmask = 0; this->pf.amask = 0; } void DDSHeader::setFormatCode(uint32 code) { // set fourcc pixel format. this->pf.flags = DDPF_FOURCC; this->pf.fourcc = code; this->pf.bitcount = 0; this->pf.rmask = 0; this->pf.gmask = 0; this->pf.bmask = 0; this->pf.amask = 0; } void DDSHeader::setSwizzleCode(uint8 c0, uint8 c1, uint8 c2, uint8 c3) { this->pf.bitcount = NV_MAKEFOURCC(c0, c1, c2, c3); } void DDSHeader::setPixelFormat(uint bitcount, uint rmask, uint gmask, uint bmask, uint amask) { // Make sure the masks are correct. nvCheck((rmask & gmask) == 0); nvCheck((rmask & bmask) == 0); nvCheck((rmask & amask) == 0); nvCheck((gmask & bmask) == 0); nvCheck((gmask & amask) == 0); nvCheck((bmask & amask) == 0); if (rmask != 0 || gmask != 0 || bmask != 0) { if (gmask == 0 && bmask == 0) { this->pf.flags = DDPF_LUMINANCE; } else { this->pf.flags = DDPF_RGB; } if (amask != 0) { this->pf.flags |= DDPF_ALPHAPIXELS; } } else if (amask != 0) { this->pf.flags |= DDPF_ALPHA; } if (bitcount == 0) { // Compute bit count from the masks. uint total = rmask | gmask | bmask | amask; while(total != 0) { bitcount++; total >>= 1; } } // D3DX functions do not like this: this->pf.fourcc = 0; //findD3D9Format(bitcount, rmask, gmask, bmask, amask); /*if (this->pf.fourcc) { this->pf.flags |= DDPF_FOURCC; }*/ nvCheck(bitcount > 0 && bitcount <= 32); this->pf.bitcount = bitcount; this->pf.rmask = rmask; this->pf.gmask = gmask; this->pf.bmask = bmask; this->pf.amask = amask; } void DDSHeader::setDX10Format(uint format) { this->pf.flags = DDPF_FOURCC; this->pf.fourcc = FOURCC_DX10; this->header10.dxgiFormat = format; } void DDSHeader::setNormalFlag(bool b) { if (b) this->pf.flags |= DDPF_NORMAL; else this->pf.flags &= ~DDPF_NORMAL; } void DDSHeader::setSrgbFlag(bool b) { if (b) this->pf.flags |= DDPF_SRGB; else this->pf.flags &= ~DDPF_SRGB; } void DDSHeader::setHasAlphaFlag(bool b) { if (b) this->pf.flags |= DDPF_ALPHAPIXELS; else this->pf.flags &= ~DDPF_ALPHAPIXELS; } void DDSHeader::setUserVersion(int version) { this->reserved[7] = FOURCC_UVER; this->reserved[8] = version; } void DDSHeader::swapBytes() { this->fourcc = POSH_LittleU32(this->fourcc); this->size = POSH_LittleU32(this->size); this->flags = POSH_LittleU32(this->flags); this->height = POSH_LittleU32(this->height); this->width = POSH_LittleU32(this->width); this->pitch = POSH_LittleU32(this->pitch); this->depth = POSH_LittleU32(this->depth); this->mipmapcount = POSH_LittleU32(this->mipmapcount); for(int i = 0; i < 11; i++) { this->reserved[i] = POSH_LittleU32(this->reserved[i]); } this->pf.size = POSH_LittleU32(this->pf.size); this->pf.flags = POSH_LittleU32(this->pf.flags); this->pf.fourcc = POSH_LittleU32(this->pf.fourcc); this->pf.bitcount = POSH_LittleU32(this->pf.bitcount); this->pf.rmask = POSH_LittleU32(this->pf.rmask); this->pf.gmask = POSH_LittleU32(this->pf.gmask); this->pf.bmask = POSH_LittleU32(this->pf.bmask); this->pf.amask = POSH_LittleU32(this->pf.amask); this->caps.caps1 = POSH_LittleU32(this->caps.caps1); this->caps.caps2 = POSH_LittleU32(this->caps.caps2); this->caps.caps3 = POSH_LittleU32(this->caps.caps3); this->caps.caps4 = POSH_LittleU32(this->caps.caps4); this->notused = POSH_LittleU32(this->notused); this->header10.dxgiFormat = POSH_LittleU32(this->header10.dxgiFormat); this->header10.resourceDimension = POSH_LittleU32(this->header10.resourceDimension); this->header10.miscFlag = POSH_LittleU32(this->header10.miscFlag); this->header10.arraySize = POSH_LittleU32(this->header10.arraySize); this->header10.reserved = POSH_LittleU32(this->header10.reserved); } bool DDSHeader::hasDX10Header() const { //if (pf.flags & DDPF_FOURCC) { return this->pf.fourcc == FOURCC_DX10; //} //return false; } uint DDSHeader::signature() const { return this->reserved[9]; } uint DDSHeader::toolVersion() const { return this->reserved[10]; } uint DDSHeader::userVersion() const { if (this->reserved[7] == FOURCC_UVER) { return this->reserved[8]; } return 0; } bool DDSHeader::isNormalMap() const { return (pf.flags & DDPF_NORMAL) != 0; } bool DDSHeader::isSrgb() const { return (pf.flags & DDPF_SRGB) != 0; } bool DDSHeader::hasAlpha() const { return (pf.flags & DDPF_ALPHAPIXELS) != 0; } uint DDSHeader::d3d9Format() const { if (pf.flags & DDPF_FOURCC) { return pf.fourcc; } else { return findD3D9Format(pf.bitcount, pf.rmask, pf.gmask, pf.bmask, pf.amask); } } uint DDSHeader::pixelSize() const { if (hasDX10Header()) { return ::pixelSize((DXGI_FORMAT)header10.dxgiFormat); } else { if (pf.flags & DDPF_FOURCC) { return ::pixelSize((D3DFORMAT)pf.fourcc); } else { nvDebugCheck((pf.flags & DDPF_RGB) || (pf.flags & DDPF_LUMINANCE)); return pf.bitcount; } } } uint DDSHeader::blockSize() const { switch(pf.fourcc) { case FOURCC_DXT1: case FOURCC_ATI1: return 8; case FOURCC_DXT2: case FOURCC_DXT3: case FOURCC_DXT4: case FOURCC_DXT5: case FOURCC_RXGB: case FOURCC_ATI2: return 16; case FOURCC_DX10: switch(header10.dxgiFormat) { case DXGI_FORMAT_BC1_TYPELESS: case DXGI_FORMAT_BC1_UNORM: case DXGI_FORMAT_BC1_UNORM_SRGB: case DXGI_FORMAT_BC4_TYPELESS: case DXGI_FORMAT_BC4_UNORM: case DXGI_FORMAT_BC4_SNORM: return 8; case DXGI_FORMAT_BC2_TYPELESS: case DXGI_FORMAT_BC2_UNORM: case DXGI_FORMAT_BC2_UNORM_SRGB: case DXGI_FORMAT_BC3_TYPELESS: case DXGI_FORMAT_BC3_UNORM: case DXGI_FORMAT_BC3_UNORM_SRGB: case DXGI_FORMAT_BC5_TYPELESS: case DXGI_FORMAT_BC5_UNORM: case DXGI_FORMAT_BC5_SNORM: case DXGI_FORMAT_BC6H_TYPELESS: case DXGI_FORMAT_BC6H_SF16: case DXGI_FORMAT_BC6H_UF16: case DXGI_FORMAT_BC7_TYPELESS: case DXGI_FORMAT_BC7_UNORM: case DXGI_FORMAT_BC7_UNORM_SRGB: return 16; }; }; // Not a block image. return 0; } bool DDSHeader::isBlockFormat() const { return blockSize() != 0; } DirectDrawSurface::DirectDrawSurface() : stream(NULL) { } DirectDrawSurface::~DirectDrawSurface() { delete stream; } bool DirectDrawSurface::load(const char * filename) { return load(new StdInputStream(filename)); } bool DirectDrawSurface::load(Stream * stream) { delete this->stream; this->stream = stream; if (!stream->isError()) { (*stream) << header; return true; } return false; } bool DirectDrawSurface::isValid() const { if (stream == NULL || stream->isError()) { return false; } if (header.fourcc != FOURCC_DDS || header.size != 124) { return false; } const uint required = (DDSD_WIDTH|DDSD_HEIGHT/*|DDSD_CAPS|DDSD_PIXELFORMAT*/); if( (header.flags & required) != required ) { return false; } if (header.pf.size != 32) { return false; } if( !(header.caps.caps1 & DDSCAPS_TEXTURE) ) { return false; } return true; } bool DirectDrawSurface::isSupported() const { nvDebugCheck(isValid()); if (header.hasDX10Header()) { if (header.header10.dxgiFormat == DXGI_FORMAT_BC1_UNORM || header.header10.dxgiFormat == DXGI_FORMAT_BC2_UNORM || header.header10.dxgiFormat == DXGI_FORMAT_BC3_UNORM || header.header10.dxgiFormat == DXGI_FORMAT_BC4_UNORM || header.header10.dxgiFormat == DXGI_FORMAT_BC5_UNORM || header.header10.dxgiFormat == DXGI_FORMAT_BC6H_UF16 || header.header10.dxgiFormat == DXGI_FORMAT_BC7_UNORM) { return true; } else { return findDXGIPixelFormat(header.header10.dxgiFormat) != NULL; } } else { if (header.pf.flags & DDPF_FOURCC) { if (header.pf.fourcc != FOURCC_DXT1 && header.pf.fourcc != FOURCC_DXT2 && header.pf.fourcc != FOURCC_DXT3 && header.pf.fourcc != FOURCC_DXT4 && header.pf.fourcc != FOURCC_DXT5 && header.pf.fourcc != FOURCC_RXGB && header.pf.fourcc != FOURCC_ATI1 && header.pf.fourcc != FOURCC_ATI2) { // Unknown fourcc code. return false; } } else if ((header.pf.flags & DDPF_RGB) || (header.pf.flags & DDPF_LUMINANCE)) { // All RGB and luminance formats are supported now. } else { return false; } if (isTextureCube()) { if (header.width != header.height) return false; if ((header.caps.caps2 & DDSCAPS2_CUBEMAP_ALL_FACES) != DDSCAPS2_CUBEMAP_ALL_FACES) { // Cubemaps must contain all faces. return false; } } } return true; } bool DirectDrawSurface::hasAlpha() const { // If the file was generated by us, just use the DDPF_ALPHAPIXELS flag. if (header.reserved[9] == FOURCC_NVTT) { return (header.pf.flags & DDPF_ALPHAPIXELS); } // Otherwise make assumptions based on the pixel format. if (header.hasDX10Header()) { return ::hasAlpha((DXGI_FORMAT)header.header10.dxgiFormat); } else { //if (header.pf.flags & DDPF_ALPHAPIXELS) return true; if (header.pf.flags & DDPF_RGB) { return header.pf.amask != 0; } else if (header.pf.flags & DDPF_FOURCC) { return ::hasAlpha((D3DFORMAT)header.pf.fourcc); } return false; } } bool DirectDrawSurface::isColorsRGB() const { if (header.hasDX10Header()) { switch (header.header10.dxgiFormat) { case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: case DXGI_FORMAT_BC1_UNORM_SRGB: case DXGI_FORMAT_BC2_UNORM_SRGB: case DXGI_FORMAT_BC3_UNORM_SRGB: case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: case DXGI_FORMAT_BC7_UNORM_SRGB: return true; } } else { //if (header.reserved[9] == FOURCC_NVTT) if (header.pf.flags & DDPF_SRGB) return true; } return false; } uint DirectDrawSurface::mipmapCount() const { nvDebugCheck(isValid()); if (header.flags & DDSD_MIPMAPCOUNT) return header.mipmapcount; else return 1; } uint DirectDrawSurface::width() const { nvDebugCheck(isValid()); if (header.flags & DDSD_WIDTH) return header.width; else return 1; } uint DirectDrawSurface::height() const { nvDebugCheck(isValid()); if (header.flags & DDSD_HEIGHT) return header.height; else return 1; } uint DirectDrawSurface::depth() const { nvDebugCheck(isValid()); if (header.flags & DDSD_DEPTH) return header.depth; else return 1; } uint DirectDrawSurface::arraySize() const { nvDebugCheck(isValid()); if (header.hasDX10Header()) return header.header10.arraySize; else return 1; } bool DirectDrawSurface::isTexture1D() const { nvDebugCheck(isValid()); if (header.hasDX10Header()) { return header.header10.resourceDimension == DDS_DIMENSION_TEXTURE1D; } return false; } bool DirectDrawSurface::isTexture2D() const { nvDebugCheck(isValid()); if (header.hasDX10Header()) { return header.header10.resourceDimension == DDS_DIMENSION_TEXTURE2D && header.header10.arraySize == 1; } else { return !isTexture3D() && !isTextureCube(); } } bool DirectDrawSurface::isTexture3D() const { nvDebugCheck(isValid()); if (header.hasDX10Header()) { return header.header10.resourceDimension == DDS_DIMENSION_TEXTURE3D; } else { return (header.caps.caps2 & DDSCAPS2_VOLUME) != 0; } } bool DirectDrawSurface::isTextureCube() const { nvDebugCheck(isValid()); return (header.caps.caps2 & DDSCAPS2_CUBEMAP) != 0; } bool DirectDrawSurface::isTextureArray() const { nvDebugCheck(isValid()); return header.hasDX10Header() && header.header10.arraySize > 1; } void DirectDrawSurface::setNormalFlag(bool b) { nvDebugCheck(isValid()); header.setNormalFlag(b); } void DirectDrawSurface::setHasAlphaFlag(bool b) { nvDebugCheck(isValid()); header.setHasAlphaFlag(b); } void DirectDrawSurface::setUserVersion(int version) { nvDebugCheck(isValid()); header.setUserVersion(version); } static uint mipmapExtent(uint mipmap, uint x) { for (uint m = 0; m < mipmap; m++) { x = max(1U, x / 2); } return x; } uint DirectDrawSurface::surfaceWidth(uint mipmap) const { return mipmapExtent(mipmap, width()); } uint DirectDrawSurface::surfaceHeight(uint mipmap) const { return mipmapExtent(mipmap, height()); } uint DirectDrawSurface::surfaceDepth(uint mipmap) const { return mipmapExtent(mipmap, depth()); } uint DirectDrawSurface::surfaceSize(uint mipmap) const { uint w = surfaceWidth(mipmap); uint h = surfaceHeight(mipmap); uint d = surfaceDepth(mipmap); uint blockSize = header.blockSize(); if (blockSize == 0) { uint bitCount = header.pixelSize(); uint pitch = computeBytePitch(w, bitCount, 1); // Asuming 1 byte alignment, which is the same D3DX expects. return pitch * h * d; } else { w = (w + 3) / 4; h = (h + 3) / 4; //d = d; // @@ How are 3D textures aligned? return blockSize * w * h * d; } } uint DirectDrawSurface::faceSize() const { const uint count = mipmapCount(); uint size = 0; for (uint m = 0; m < count; m++) { size += surfaceSize(m); } return size; } uint DirectDrawSurface::offset(uint face, uint mipmap) { uint size = 128; // sizeof(DDSHeader); if (header.hasDX10Header()) { size += 20; // sizeof(DDSHeader10); } if (face != 0) { size += face * faceSize(); } for (uint m = 0; m < mipmap; m++) { size += surfaceSize(m); } return size; } bool DirectDrawSurface::readSurface(uint face, uint mipmap, void * data, uint size) { if (size != surfaceSize(mipmap)) return false; stream->seek(offset(face, mipmap)); if (stream->isError()) return false; return stream->serialize(data, size) == size; } void DirectDrawSurface::printInfo() const { printf("Flags: 0x%.8X\n", header.flags); if (header.flags & DDSD_CAPS) printf("\tDDSD_CAPS\n"); if (header.flags & DDSD_PIXELFORMAT) printf("\tDDSD_PIXELFORMAT\n"); if (header.flags & DDSD_WIDTH) printf("\tDDSD_WIDTH\n"); if (header.flags & DDSD_HEIGHT) printf("\tDDSD_HEIGHT\n"); if (header.flags & DDSD_DEPTH) printf("\tDDSD_DEPTH\n"); if (header.flags & DDSD_PITCH) printf("\tDDSD_PITCH\n"); if (header.flags & DDSD_LINEARSIZE) printf("\tDDSD_LINEARSIZE\n"); if (header.flags & DDSD_MIPMAPCOUNT) printf("\tDDSD_MIPMAPCOUNT\n"); printf("Height: %d\n", header.height); printf("Width: %d\n", header.width); printf("Depth: %d\n", header.depth); if (header.flags & DDSD_PITCH) printf("Pitch: %d\n", header.pitch); else if (header.flags & DDSD_LINEARSIZE) printf("Linear size: %d\n", header.pitch); printf("Mipmap count: %d\n", header.mipmapcount); printf("Pixel Format:\n"); printf("\tFlags: 0x%.8X\n", header.pf.flags); if (header.pf.flags & DDPF_RGB) printf("\t\tDDPF_RGB\n"); if (header.pf.flags & DDPF_LUMINANCE) printf("\t\tDDPF_LUMINANCE\n"); if (header.pf.flags & DDPF_FOURCC) printf("\t\tDDPF_FOURCC\n"); if (header.pf.flags & DDPF_ALPHAPIXELS) printf("\t\tDDPF_ALPHAPIXELS\n"); if (header.pf.flags & DDPF_ALPHA) printf("\t\tDDPF_ALPHA\n"); if (header.pf.flags & DDPF_PALETTEINDEXED1) printf("\t\tDDPF_PALETTEINDEXED1\n"); if (header.pf.flags & DDPF_PALETTEINDEXED2) printf("\t\tDDPF_PALETTEINDEXED2\n"); if (header.pf.flags & DDPF_PALETTEINDEXED4) printf("\t\tDDPF_PALETTEINDEXED4\n"); if (header.pf.flags & DDPF_PALETTEINDEXED8) printf("\t\tDDPF_PALETTEINDEXED8\n"); if (header.pf.flags & DDPF_ALPHAPREMULT) printf("\t\tDDPF_ALPHAPREMULT\n"); if (header.pf.flags & DDPF_NORMAL) printf("\t\tDDPF_NORMAL\n"); if (header.pf.fourcc != 0) { // Display fourcc code even when DDPF_FOURCC flag not set. printf("\tFourCC: '%c%c%c%c' (0x%.8X)\n", ((header.pf.fourcc >> 0) & 0xFF), ((header.pf.fourcc >> 8) & 0xFF), ((header.pf.fourcc >> 16) & 0xFF), ((header.pf.fourcc >> 24) & 0xFF), header.pf.fourcc); } if ((header.pf.flags & DDPF_FOURCC) && (header.pf.bitcount != 0)) { printf("\tSwizzle: '%c%c%c%c' (0x%.8X)\n", (header.pf.bitcount >> 0) & 0xFF, (header.pf.bitcount >> 8) & 0xFF, (header.pf.bitcount >> 16) & 0xFF, (header.pf.bitcount >> 24) & 0xFF, header.pf.bitcount); } else { printf("\tBit count: %d\n", header.pf.bitcount); } printf("\tRed mask: 0x%.8X\n", header.pf.rmask); printf("\tGreen mask: 0x%.8X\n", header.pf.gmask); printf("\tBlue mask: 0x%.8X\n", header.pf.bmask); printf("\tAlpha mask: 0x%.8X\n", header.pf.amask); printf("Caps:\n"); printf("\tCaps 1: 0x%.8X\n", header.caps.caps1); if (header.caps.caps1 & DDSCAPS_COMPLEX) printf("\t\tDDSCAPS_COMPLEX\n"); if (header.caps.caps1 & DDSCAPS_TEXTURE) printf("\t\tDDSCAPS_TEXTURE\n"); if (header.caps.caps1 & DDSCAPS_MIPMAP) printf("\t\tDDSCAPS_MIPMAP\n"); printf("\tCaps 2: 0x%.8X\n", header.caps.caps2); if (header.caps.caps2 & DDSCAPS2_VOLUME) printf("\t\tDDSCAPS2_VOLUME\n"); else if (header.caps.caps2 & DDSCAPS2_CUBEMAP) { printf("\t\tDDSCAPS2_CUBEMAP\n"); if ((header.caps.caps2 & DDSCAPS2_CUBEMAP_ALL_FACES) == DDSCAPS2_CUBEMAP_ALL_FACES) printf("\t\tDDSCAPS2_CUBEMAP_ALL_FACES\n"); else { if (header.caps.caps2 & DDSCAPS2_CUBEMAP_POSITIVEX) printf("\t\tDDSCAPS2_CUBEMAP_POSITIVEX\n"); if (header.caps.caps2 & DDSCAPS2_CUBEMAP_NEGATIVEX) printf("\t\tDDSCAPS2_CUBEMAP_NEGATIVEX\n"); if (header.caps.caps2 & DDSCAPS2_CUBEMAP_POSITIVEY) printf("\t\tDDSCAPS2_CUBEMAP_POSITIVEY\n"); if (header.caps.caps2 & DDSCAPS2_CUBEMAP_NEGATIVEY) printf("\t\tDDSCAPS2_CUBEMAP_NEGATIVEY\n"); if (header.caps.caps2 & DDSCAPS2_CUBEMAP_POSITIVEZ) printf("\t\tDDSCAPS2_CUBEMAP_POSITIVEZ\n"); if (header.caps.caps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ) printf("\t\tDDSCAPS2_CUBEMAP_NEGATIVEZ\n"); } } printf("\tCaps 3: 0x%.8X\n", header.caps.caps3); printf("\tCaps 4: 0x%.8X\n", header.caps.caps4); if (header.hasDX10Header()) { printf("DX10 Header:\n"); printf("\tDXGI Format: %u (%s)\n", header.header10.dxgiFormat, getDxgiFormatString((DXGI_FORMAT)header.header10.dxgiFormat)); printf("\tResource dimension: %u (%s)\n", header.header10.resourceDimension, getD3d10ResourceDimensionString((DDS_DIMENSION)header.header10.resourceDimension)); printf("\tMisc flag: %u\n", header.header10.miscFlag); printf("\tArray size: %u\n", header.header10.arraySize); } if (header.reserved[9] == FOURCC_NVTT) { int major = (header.reserved[10] >> 16) & 0xFF; int minor = (header.reserved[10] >> 8) & 0xFF; int revision= header.reserved[10] & 0xFF; printf("Version:\n"); printf("\tNVIDIA Texture Tools %d.%d.%d\n", major, minor, revision); } if (header.reserved[7] == FOURCC_UVER) { printf("User Version: %d\n", header.reserved[8]); } } static bool readLinearImage(Image * img, uint8 * data, uint bitcount, uint rmask, uint gmask, uint bmask, uint amask) { nvDebugCheck(img != NULL); nvDebugCheck(data != NULL); const uint w = img->width; const uint h = img->height; const uint d = img->depth; uint rshift, rsize; PixelFormat::maskShiftAndSize(rmask, &rshift, &rsize); uint gshift, gsize; PixelFormat::maskShiftAndSize(gmask, &gshift, &gsize); uint bshift, bsize; PixelFormat::maskShiftAndSize(bmask, &bshift, &bsize); uint ashift, asize; PixelFormat::maskShiftAndSize(amask, &ashift, &asize); uint byteCount = (bitcount + 7) / 8; // Read linear RGB images. for (uint z = 0; z < d; z++) { for (uint y = 0; y < h; y++) { for (uint x = 0; x < w; x++) { uint c; memcpy(&c, data, byteCount); data += byteCount; Color32 pixel(0, 0, 0, 0xFF); pixel.r = PixelFormat::convert((c & rmask) >> rshift, rsize, 8); pixel.g = PixelFormat::convert((c & gmask) >> gshift, gsize, 8); pixel.b = PixelFormat::convert((c & bmask) >> bshift, bsize, 8); pixel.a = PixelFormat::convert((c & amask) >> ashift, asize, 8); img->pixel(x, y, z) = pixel; } } } return true; } static void readBlock(ColorBlock * rgba, uint8 * data, uint dxgiFormat, bool isNormalMap, bool swapRA) { nvDebugCheck(rgba != NULL); nvDebugCheck(data != NULL); if (dxgiFormat == DXGI_FORMAT_BC1_UNORM) { BlockDXT1 * block = (BlockDXT1 *)data; block->decodeBlock(rgba); } else if (dxgiFormat == DXGI_FORMAT_BC2_UNORM) { BlockDXT3 * block = (BlockDXT3 *)data; block->decodeBlock(rgba); } else if (dxgiFormat == DXGI_FORMAT_BC3_UNORM) { BlockDXT5 * block = (BlockDXT5 *)data; block->decodeBlock(rgba); if (swapRA) { // Swap R & A. for (int i = 0; i < 16; i++) { Color32 & c = rgba->color(i); uint tmp = c.r; c.r = c.a; c.a = tmp; } } } else if (DXGI_FORMAT_BC4_UNORM) { BlockATI1 * block = (BlockATI1 *)data; block->decodeBlock(rgba); } else if (DXGI_FORMAT_BC5_UNORM) { BlockATI2 * block = (BlockATI2 *)data; block->decodeBlock(rgba); } else if (dxgiFormat == DXGI_FORMAT_BC6H_UF16) { BlockBC6 * block = (BlockBC6 *)data; Vector4 colors[16]; block->decodeBlock(colors); // Clamp to [0, 1] and round to 8-bit for (int y = 0; y < 4; ++y) { for (int x = 0; x < 4; ++x) { Vector4 px = colors[y * 4 + x]; rgba->color(x, y).setRGBA( ftoi_round(clamp(px.x, 0.0f, 1.0f) * 255.0f), ftoi_round(clamp(px.y, 0.0f, 1.0f) * 255.0f), ftoi_round(clamp(px.z, 0.0f, 1.0f) * 255.0f), 0xFF); } } } else if (dxgiFormat == DXGI_FORMAT_BC7_UNORM) { BlockBC7 * block = (BlockBC7 *)data; block->decodeBlock(rgba); } else { nvDebugCheck(false); } // If normal flag set, reconstruct Z from XY. if (isNormalMap) { for (int i = 0; i < 16; i++) { Color32 & c = rgba->color(i); float nx = 2 * (c.r / 255.0f) - 1; float ny = 2 * (c.g / 255.0f) - 1; float nz = 0.0f; if (1 - nx * nx - ny * ny > 0) nz = sqrtf(1 - nx * nx - ny * ny); c.b = clamp(int(255.0f * (nz + 1) / 2.0f), 0, 255); } } } static bool readBlockImage(Image * img, uint8 * data, uint dxgiFormat, bool isNormalMap, bool swapRA) { nvDebugCheck(img != NULL); nvDebugCheck(data != NULL); switch (dxgiFormat) { case DXGI_FORMAT_BC1_TYPELESS: case DXGI_FORMAT_BC1_UNORM: case DXGI_FORMAT_BC1_UNORM_SRGB: dxgiFormat = DXGI_FORMAT_BC1_UNORM; break; case DXGI_FORMAT_BC2_TYPELESS: case DXGI_FORMAT_BC2_UNORM: case DXGI_FORMAT_BC2_UNORM_SRGB: dxgiFormat = DXGI_FORMAT_BC2_UNORM; break; case DXGI_FORMAT_BC3_TYPELESS: case DXGI_FORMAT_BC3_UNORM: case DXGI_FORMAT_BC3_UNORM_SRGB: dxgiFormat = DXGI_FORMAT_BC3_UNORM; break; case DXGI_FORMAT_BC4_TYPELESS: case DXGI_FORMAT_BC4_UNORM: //case DXGI_FORMAT_BC4_SNORM; // Not supported. dxgiFormat = DXGI_FORMAT_BC4_UNORM; break; case DXGI_FORMAT_BC5_TYPELESS: case DXGI_FORMAT_BC5_UNORM: //case DXGI_FORMAT_BC5_SNORM: // Not supported. dxgiFormat = DXGI_FORMAT_BC5_UNORM; break; case DXGI_FORMAT_BC6H_TYPELESS: case DXGI_FORMAT_BC6H_UF16: case DXGI_FORMAT_BC6H_SF16: dxgiFormat = DXGI_FORMAT_BC6H_UF16; break; case DXGI_FORMAT_BC7_TYPELESS: case DXGI_FORMAT_BC7_UNORM: case DXGI_FORMAT_BC7_UNORM_SRGB: dxgiFormat = DXGI_FORMAT_BC7_UNORM; break; default: return false; } const uint w = img->width; const uint h = img->height; const uint d = img->depth; const uint bw = (w + 3) / 4; const uint bh = (h + 3) / 4; for (uint z = 0; z < d; z++) { for (uint by = 0; by < bh; by++) { for (uint bx = 0; bx < bw; bx++) { ColorBlock block; // Read color block. readBlock(&block, data, dxgiFormat, isNormalMap, swapRA); // Write color block. for (uint y = 0; y < min(4U, h - 4 * by); y++) { for (uint x = 0; x < min(4U, w - 4 * bx); x++) { img->pixel(4 * bx + x, 4 * by + y) = block.color(x, y); } } } } } return true; } bool nv::imageFromDDS(Image * img, DirectDrawSurface & dds, uint face, uint mipmap) { if (!dds.isValid()) return false; uint size = dds.surfaceSize(mipmap); uint8 * data = malloc(size); defer { free(data); }; if (!dds.readSurface(face, mipmap, data, size)) { return false; } uint w = dds.surfaceWidth(mipmap); uint h = dds.surfaceHeight(mipmap); uint d = dds.surfaceDepth(mipmap); img->allocate(w, h, d); if (dds.hasAlpha()) { img->format = Image::Format_ARGB; } else { img->format = Image::Format_XRGB; } img->sRGB = dds.isColorsRGB(); if (dds.header.isBlockFormat()) { bool isNormalMap = false; bool swapRA = false; uint dxgiFormat = DXGI_FORMAT_UNKNOWN; if (dds.header.hasDX10Header()) { dxgiFormat = dds.header.header10.dxgiFormat; } else { switch (dds.header.pf.fourcc) { case FOURCC_DXT1: dxgiFormat = DXGI_FORMAT_BC1_UNORM; break; case FOURCC_DXT3: dxgiFormat = DXGI_FORMAT_BC2_UNORM; break; case FOURCC_DXT5: dxgiFormat = DXGI_FORMAT_BC3_UNORM; break; case FOURCC_ATI1: dxgiFormat = DXGI_FORMAT_BC4_UNORM; break; case FOURCC_ATI2: dxgiFormat = DXGI_FORMAT_BC5_UNORM; break; case FOURCC_RXGB: dxgiFormat = DXGI_FORMAT_BC3_UNORM; swapRA = true; break; } } if (dds.header.pf.flags & DDPF_NORMAL) isNormalMap = true; return readBlockImage(img, data, dxgiFormat, isNormalMap, swapRA); } else { if (dds.header.hasDX10Header()) { if (const RGBAPixelFormat *format = findDXGIPixelFormat(dds.header.header10.dxgiFormat)) { return readLinearImage(img, data, format->bitcount, format->rmask, format->gmask, format->bmask, format->amask); } } else { if (dds.header.pf.flags & DDPF_RGB) { return readLinearImage(img, data, dds.header.pf.bitcount, dds.header.pf.rmask, dds.header.pf.gmask, dds.header.pf.bmask, dds.header.pf.amask); } else if (dds.header.pf.flags & DDPF_FOURCC) { if (const RGBAPixelFormat *format = findD3D9PixelFormat(dds.header.pf.fourcc)) { return readLinearImage(img, data, format->bitcount, format->rmask, format->gmask, format->bmask, format->amask); } } } } return false; // Not supported. } bool nv::imageFromDDS(FloatImage * img, DirectDrawSurface & dds, uint face, uint mipmap) { if (!dds.isValid()) return false; uint size = dds.surfaceSize(mipmap); uint8 * data = malloc(size); defer{ free(data); }; if (!dds.readSurface(face, mipmap, data, size)) { return false; } uint w = dds.surfaceWidth(mipmap); uint h = dds.surfaceHeight(mipmap); uint d = dds.surfaceDepth(mipmap); // @@ return false; } // Copyright NVIDIA Corporation 2007 -- Ignacio Castano // Copyright (c) 2008-2020 -- Ignacio Castano // // 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, sublicense, 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 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 // NONINFRINGEMENT. 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.