diff options
Diffstat (limited to 'plugins/AdvaImg/src/FreeImage/PluginDDS.cpp')
-rw-r--r-- | plugins/AdvaImg/src/FreeImage/PluginDDS.cpp | 651 |
1 files changed, 651 insertions, 0 deletions
diff --git a/plugins/AdvaImg/src/FreeImage/PluginDDS.cpp b/plugins/AdvaImg/src/FreeImage/PluginDDS.cpp new file mode 100644 index 0000000000..0a1667d7f1 --- /dev/null +++ b/plugins/AdvaImg/src/FreeImage/PluginDDS.cpp @@ -0,0 +1,651 @@ +// ========================================================== +// DDS Loader +// +// Design and implementation by +// - Volker Gärtner (volkerg@gmx.at) +// - Sherman Wilcox +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" + +// ---------------------------------------------------------- +// Definitions for the DDS format +// ---------------------------------------------------------- + +#ifdef _WIN32 +#pragma pack(push, 1) +#else +#pragma pack(1) +#endif + +typedef struct tagDDPIXELFORMAT { + DWORD dwSize; // size of this structure (must be 32) + DWORD dwFlags; // see DDPF_* + DWORD dwFourCC; + DWORD dwRGBBitCount; // Total number of bits for RGB formats + DWORD dwRBitMask; + DWORD dwGBitMask; + DWORD dwBBitMask; + DWORD dwRGBAlphaBitMask; +} DDPIXELFORMAT; + +// DIRECTDRAW PIXELFORMAT FLAGS +enum { + DDPF_ALPHAPIXELS = 0x00000001l, // surface has alpha channel + DDPF_ALPHA = 0x00000002l, // alpha only + DDPF_FOURCC = 0x00000004l, // FOURCC available + DDPF_RGB = 0x00000040l // RGB(A) bitmap +}; + +typedef struct tagDDCAPS2 { + DWORD dwCaps1; // Zero or more of the DDSCAPS_* members + DWORD dwCaps2; // Zero or more of the DDSCAPS2_* members + DWORD dwReserved[2]; +} DDCAPS2; + +// DIRECTDRAWSURFACE CAPABILITY FLAGS +enum { + DDSCAPS_ALPHA = 0x00000002l, // alpha only surface + DDSCAPS_COMPLEX = 0x00000008l, // complex surface structure + DDSCAPS_TEXTURE = 0x00001000l, // used as texture (should always be set) + DDSCAPS_MIPMAP = 0x00400000l // Mipmap present +}; + +enum { + DDSCAPS2_CUBEMAP = 0x00000200L, + DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400L, + DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800L, + DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000L, + DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000L, + DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000L, + DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x00008000L, + DDSCAPS2_VOLUME = 0x00200000L +}; + +typedef struct tagDDSURFACEDESC2 { + DWORD dwSize; // size of this structure (must be 124) + DWORD dwFlags; // combination of the DDSS_* flags + DWORD dwHeight; + DWORD dwWidth; + DWORD dwPitchOrLinearSize; + DWORD dwDepth; // Depth of a volume texture + DWORD dwMipMapCount; + DWORD dwReserved1[11]; + DDPIXELFORMAT ddpfPixelFormat; + DDCAPS2 ddsCaps; + DWORD dwReserved2; +} DDSURFACEDESC2; + +enum { + DDSD_CAPS = 0x00000001l, + DDSD_HEIGHT = 0x00000002l, + DDSD_WITH = 0x00000004l, + DDSD_PITCH = 0x00000008l, + DDSD_ALPHABITDEPTH = 0x00000080l, + DDSD_PIXELFORMAT = 0x00001000l, + DDSD_MIPMAPCOUNT = 0x00020000l, + DDSD_LINEARSIZE = 0x00080000l, + DDSD_DEPTH = 0x00800000l +}; + +typedef struct tagDDSHEADER { + DWORD dwMagic; // FOURCC: "DDS " + DDSURFACEDESC2 surfaceDesc; +} DDSHEADER; + +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \ + ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24 )) + +#define FOURCC_DXT1 MAKEFOURCC('D','X','T','1') +#define FOURCC_DXT2 MAKEFOURCC('D','X','T','2') +#define FOURCC_DXT3 MAKEFOURCC('D','X','T','3') +#define FOURCC_DXT4 MAKEFOURCC('D','X','T','4') +#define FOURCC_DXT5 MAKEFOURCC('D','X','T','5') + +// ---------------------------------------------------------- +// Structures used by DXT textures +// ---------------------------------------------------------- + +typedef struct tagColor8888 { + BYTE b; + BYTE g; + BYTE r; + BYTE a; +} Color8888; + +typedef struct tagColor565 { + WORD b : 5; + WORD g : 6; + WORD r : 5; +} Color565; + +typedef struct tagDXTColBlock { + Color565 colors[2]; + BYTE row[4]; +} DXTColBlock; + +typedef struct tagDXTAlphaBlockExplicit { + WORD row[4]; +} DXTAlphaBlockExplicit; + +typedef struct tagDXTAlphaBlock3BitLinear { + BYTE alpha[2]; + BYTE data[6]; +} DXTAlphaBlock3BitLinear; + +typedef struct tagDXT1Block +{ + DXTColBlock color; +} DXT1Block; + +typedef struct tagDXT3Block { // also used by dxt2 + DXTAlphaBlockExplicit alpha; + DXTColBlock color; +} DXT3Block; + +typedef struct tagDXT5Block { // also used by dxt4 + DXTAlphaBlock3BitLinear alpha; + DXTColBlock color; +} DXT5Block; + +#ifdef _WIN32 +# pragma pack(pop) +#else +# pragma pack() +#endif + +// ---------------------------------------------------------- +// Internal functions +// ---------------------------------------------------------- +#ifdef FREEIMAGE_BIGENDIAN +static void +SwapHeader(DDSHEADER *header) { + SwapLong(&header->dwMagic); + SwapLong(&header->surfaceDesc.dwSize); + SwapLong(&header->surfaceDesc.dwFlags); + SwapLong(&header->surfaceDesc.dwHeight); + SwapLong(&header->surfaceDesc.dwWidth); + SwapLong(&header->surfaceDesc.dwPitchOrLinearSize); + SwapLong(&header->surfaceDesc.dwDepth); + SwapLong(&header->surfaceDesc.dwMipMapCount); + for(int i=0; i<11; i++) { + SwapLong(&header->surfaceDesc.dwReserved1[i]); + } + SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwSize); + SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwFlags); + SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwFourCC); + SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwRGBBitCount); + SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwRBitMask); + SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwGBitMask); + SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwBBitMask); + SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwRGBAlphaBitMask); + SwapLong(&header->surfaceDesc.ddsCaps.dwCaps1); + SwapLong(&header->surfaceDesc.ddsCaps.dwCaps2); + SwapLong(&header->surfaceDesc.ddsCaps.dwReserved[0]); + SwapLong(&header->surfaceDesc.ddsCaps.dwReserved[1]); + SwapLong(&header->surfaceDesc.dwReserved2); +} +#endif + +// ========================================================== + +// Get the 4 possible colors for a block +// +static void +GetBlockColors (const DXTColBlock &block, Color8888 colors[4], bool isDXT1) { + int i; + // expand from 565 to 888 + for (i = 0; i < 2; i++) { + colors[i].a = 0xff; + /* + colors[i].r = (BYTE)(block.colors[i].r * 0xff / 0x1f); + colors[i].g = (BYTE)(block.colors[i].g * 0xff / 0x3f); + colors[i].b = (BYTE)(block.colors[i].b * 0xff / 0x1f); + */ + colors[i].r = (BYTE)((block.colors[i].r << 3U) | (block.colors[i].r >> 2U)); + colors[i].g = (BYTE)((block.colors[i].g << 2U) | (block.colors[i].g >> 4U)); + colors[i].b = (BYTE)((block.colors[i].b << 3U) | (block.colors[i].b >> 2U)); + } + + WORD *wCol = (WORD *)block.colors; + if (wCol[0] > wCol[1] || !isDXT1) { + // 4 color block + for (i = 0; i < 2; i++) { + colors[i + 2].a = 0xff; + colors[i + 2].r = (BYTE)((WORD (colors[0].r) * (2 - i) + WORD (colors[1].r) * (1 + i)) / 3); + colors[i + 2].g = (BYTE)((WORD (colors[0].g) * (2 - i) + WORD (colors[1].g) * (1 + i)) / 3); + colors[i + 2].b = (BYTE)((WORD (colors[0].b) * (2 - i) + WORD (colors[1].b) * (1 + i)) / 3); + } + } + else { + // 3 color block, number 4 is transparent + colors[2].a = 0xff; + colors[2].r = (BYTE)((WORD (colors[0].r) + WORD (colors[1].r)) / 2); + colors[2].g = (BYTE)((WORD (colors[0].g) + WORD (colors[1].g)) / 2); + colors[2].b = (BYTE)((WORD (colors[0].b) + WORD (colors[1].b)) / 2); + + colors[3].a = 0x00; + colors[3].g = 0x00; + colors[3].b = 0x00; + colors[3].r = 0x00; + } +} + +struct DXT_INFO_1 { + typedef DXT1Block Block; + enum { + isDXT1 = 1, + bytesPerBlock = 8 + }; +}; + +struct DXT_INFO_3 { + typedef DXT3Block Block; + enum { + isDXT1 = 1, + bytesPerBlock = 16 + }; +}; + +struct DXT_INFO_5 { + typedef DXT5Block Block; + enum + { + isDXT1 = 1, + bytesPerBlock = 16 + }; +}; + +template <class INFO> class DXT_BLOCKDECODER_BASE { +protected: + Color8888 m_colors[4]; + const typename INFO::Block *m_pBlock; + unsigned m_colorRow; + +public: + void Setup (const BYTE *pBlock) { + m_pBlock = (const typename INFO::Block *)pBlock; + GetBlockColors (m_pBlock->color, m_colors, INFO::isDXT1); + } + + void SetY (int y) { + m_colorRow = m_pBlock->color.row[y]; + } + + void GetColor (int x, int y, Color8888 &color) { + unsigned bits = (m_colorRow >> (x * 2)) & 3; + color = m_colors[bits]; + } +}; + +class DXT_BLOCKDECODER_1 : public DXT_BLOCKDECODER_BASE <DXT_INFO_1> { +public: + typedef DXT_INFO_1 INFO; +}; + +class DXT_BLOCKDECODER_3 : public DXT_BLOCKDECODER_BASE <DXT_INFO_3> { +public: + typedef DXT_BLOCKDECODER_BASE <DXT_INFO_3> base; + typedef DXT_INFO_3 INFO; + +protected: + unsigned m_alphaRow; + +public: + void SetY (int y) { + base::SetY (y); + m_alphaRow = m_pBlock->alpha.row[y]; + } + + void GetColor (int x, int y, Color8888 &color) { + base::GetColor (x, y, color); + const unsigned bits = (m_alphaRow >> (x * 4)) & 0xF; + color.a = (BYTE)((bits * 0xFF) / 0xF); + } +}; + +class DXT_BLOCKDECODER_5 : public DXT_BLOCKDECODER_BASE <DXT_INFO_5> { +public: + typedef DXT_BLOCKDECODER_BASE <DXT_INFO_5> base; + typedef DXT_INFO_5 INFO; + +protected: + unsigned m_alphas[8]; + unsigned m_alphaBits; + int m_offset; + +public: + void Setup (const BYTE *pBlock) { + base::Setup (pBlock); + + const DXTAlphaBlock3BitLinear &block = m_pBlock->alpha; + m_alphas[0] = block.alpha[0]; + m_alphas[1] = block.alpha[1]; + if (m_alphas[0] > m_alphas[1]) { + // 8 alpha block + for (int i = 0; i < 6; i++) { + m_alphas[i + 2] = ((6 - i) * m_alphas[0] + (1 + i) * m_alphas[1] + 3) / 7; + } + } + else { + // 6 alpha block + for (int i = 0; i < 4; i++) { + m_alphas[i + 2] = ((4 - i) * m_alphas[0] + (1 + i) * m_alphas[1] + 2) / 5; + } + m_alphas[6] = 0; + m_alphas[7] = 0xFF; + } + + } + + void SetY (int y) { + base::SetY (y); + int i = y / 2; + const DXTAlphaBlock3BitLinear &block = m_pBlock->alpha; + m_alphaBits = unsigned(block.data[0 + i * 3]) | (unsigned(block.data[1 + i * 3]) << 8) + | (unsigned(block.data[2 + i * 3]) << 16); + m_offset = (y & 1) * 12; + } + + void GetColor (int x, int y, Color8888 &color) { + base::GetColor (x, y, color); + unsigned bits = (m_alphaBits >> (x * 3 + m_offset)) & 7; + color.a = (BYTE)m_alphas[bits]; + } +}; + +template <class DECODER> void DecodeDXTBlock (BYTE *dstData, const BYTE *srcBlock, long dstPitch, int bw, int bh) { + DECODER decoder; + decoder.Setup (srcBlock); + for (int y = 0; y < bh; y++) { + BYTE *dst = dstData - y * dstPitch; + decoder.SetY (y); + for (int x = 0; x < bw; x++) { + decoder.GetColor (x, y, (Color8888 &)*dst); + dst += 4; + } + } +} + +// ========================================================== +// Plugin Interface +// ========================================================== + +static int s_format_id; + +// ========================================================== +// Internal functions +// ========================================================== + +static FIBITMAP * +LoadRGB (DDSURFACEDESC2 &desc, FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { + int width = (int)desc.dwWidth & ~3; + int height = (int)desc.dwHeight & ~3; + int bpp = (int)desc.ddpfPixelFormat.dwRGBBitCount; + + // allocate a new dib + FIBITMAP *dib = FreeImage_Allocate (width, height, bpp, desc.ddpfPixelFormat.dwRBitMask, + desc.ddpfPixelFormat.dwGBitMask, desc.ddpfPixelFormat.dwBBitMask); + if (dib == NULL) + return NULL; + +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB + // Calculate the number of bytes per pixel (3 for 24-bit or 4 for 32-bit) + int bytespp = FreeImage_GetLine(dib) / FreeImage_GetWidth(dib); +#endif + + // read the file + int line = CalculateLine(width, bpp); + int filePitch = (desc.dwFlags & DDSD_PITCH) ? (int)desc.dwPitchOrLinearSize : line; + long delta = (long)filePitch - (long)line; + for (int i = 0; i < height; i++) { + BYTE *pixels = FreeImage_GetScanLine(dib, height - i - 1); + io->read_proc (pixels, 1, line, handle); + io->seek_proc (handle, delta, SEEK_CUR); +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB + for(int x = 0; x < width; x++) { + INPLACESWAP(pixels[FI_RGBA_RED],pixels[FI_RGBA_BLUE]); + pixels += bytespp; + } +#endif + } + + // enable transparency + FreeImage_SetTransparent (dib, (desc.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS) ? TRUE : FALSE); + + if (!(desc.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS) && bpp == 32) { + // no transparency: convert to 24-bit + FIBITMAP *old = dib; + dib = FreeImage_ConvertTo24Bits (old); + FreeImage_Unload (old); + } + return dib; +} + +template <class DECODER> static void +LoadDXT_Helper (FreeImageIO *io, fi_handle handle, int page, int flags, void *data, FIBITMAP *dib, int width, int height, int line) { + typedef typename DECODER::INFO INFO; + typedef typename INFO::Block Block; + + Block *input_buffer = new(std::nothrow) Block[(width + 3) / 4]; + if (!input_buffer) return; + + int widthRest = (int) width & 3; + int heightRest = (int) height & 3; + int inputLine = (width + 3) / 4; + int y = 0; + + if (height >= 4) { + for (; y < height; y += 4) { + io->read_proc (input_buffer, sizeof (typename INFO::Block), inputLine, handle); + // TODO: probably need some endian work here + BYTE *pbSrc = (BYTE *)input_buffer; + BYTE *pbDst = FreeImage_GetScanLine (dib, height - y - 1); + + if (width >= 4) { + for (int x = 0; x < width; x += 4) { + DecodeDXTBlock <DECODER> (pbDst, pbSrc, line, 4, 4); + pbSrc += INFO::bytesPerBlock; + pbDst += 4 * 4; + } + } + if (widthRest) { + DecodeDXTBlock <DECODER> (pbDst, pbSrc, line, widthRest, 4); + } + } + } + if (heightRest) { + io->read_proc (input_buffer, sizeof (typename INFO::Block), inputLine, handle); + // TODO: probably need some endian work here + BYTE *pbSrc = (BYTE *)input_buffer; + BYTE *pbDst = FreeImage_GetScanLine (dib, height - y - 1); + + if (width >= 4) { + for (int x = 0; x < width; x += 4) { + DecodeDXTBlock <DECODER> (pbDst, pbSrc, line, 4, heightRest); + pbSrc += INFO::bytesPerBlock; + pbDst += 4 * 4; + } + } + if (widthRest) { + DecodeDXTBlock <DECODER> (pbDst, pbSrc, line, widthRest, heightRest); + } + + } + + delete [] input_buffer; +} + +static FIBITMAP * +LoadDXT (int type, DDSURFACEDESC2 &desc, FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { + int width = (int)desc.dwWidth & ~3; + int height = (int)desc.dwHeight & ~3; + + // allocate a 32-bit dib + FIBITMAP *dib = FreeImage_Allocate (width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + if (dib == NULL) + return NULL; + + int bpp = FreeImage_GetBPP (dib); + int line = CalculateLine (width, bpp); + BYTE *bits = FreeImage_GetBits (dib); + + // select the right decoder + switch (type) { + case 1: + LoadDXT_Helper <DXT_BLOCKDECODER_1> (io, handle, page, flags, data, dib, width, height, line); + break; + case 3: + LoadDXT_Helper <DXT_BLOCKDECODER_3> (io, handle, page, flags, data, dib, width, height, line); + break; + case 5: + LoadDXT_Helper <DXT_BLOCKDECODER_5> (io, handle, page, flags, data, dib, width, height, line); + break; + } + + return dib; +} +// ========================================================== +// Plugin Implementation +// ========================================================== + +static const char * DLL_CALLCONV +Format() { + return "DDS"; +} + +static const char * DLL_CALLCONV +Description() { + return "DirectX Surface"; +} + +static const char * DLL_CALLCONV +Extension() { + return "dds"; +} + +static const char * DLL_CALLCONV +RegExpr() { + return NULL; +} + +static const char * DLL_CALLCONV +MimeType() { + return "image/x-dds"; +} + +static BOOL DLL_CALLCONV +Validate(FreeImageIO *io, fi_handle handle) { + DDSHEADER header; + memset(&header, 0, sizeof(header)); + io->read_proc(&header, 1, sizeof(header), handle); +#ifdef FREEIMAGE_BIGENDIAN + SwapHeader(&header); +#endif + if (header.dwMagic != MAKEFOURCC ('D','D','S',' ')) + return FALSE; + if (header.surfaceDesc.dwSize != sizeof (header.surfaceDesc) || + header.surfaceDesc.ddpfPixelFormat.dwSize != sizeof (header.surfaceDesc.ddpfPixelFormat)) + return FALSE; + return TRUE; +} + +static BOOL DLL_CALLCONV +SupportsExportDepth(int depth) { + return FALSE; +} + +static BOOL DLL_CALLCONV +SupportsExportType(FREE_IMAGE_TYPE type) { + return FALSE; +} + +// ---------------------------------------------------------- + +static void * DLL_CALLCONV +Open(FreeImageIO *io, fi_handle handle, BOOL read) { + return NULL; +} + +static void DLL_CALLCONV +Close(FreeImageIO *io, fi_handle handle, void *data) { +} + +// ---------------------------------------------------------- + +static FIBITMAP * DLL_CALLCONV +Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { + DDSHEADER header; + FIBITMAP *dib = NULL; + + memset(&header, 0, sizeof(header)); + io->read_proc(&header, 1, sizeof(header), handle); +#ifdef FREEIMAGE_BIGENDIAN + SwapHeader(&header); +#endif + if (header.surfaceDesc.ddpfPixelFormat.dwFlags & DDPF_RGB) { + dib = LoadRGB (header.surfaceDesc, io, handle, page, flags, data); + } + else if (header.surfaceDesc.ddpfPixelFormat.dwFlags & DDPF_FOURCC) { + switch (header.surfaceDesc.ddpfPixelFormat.dwFourCC) { + case FOURCC_DXT1: + dib = LoadDXT (1, header.surfaceDesc, io, handle, page, flags, data); + break; + case FOURCC_DXT3: + dib = LoadDXT (3, header.surfaceDesc, io, handle, page, flags, data); + break; + case FOURCC_DXT5: + dib = LoadDXT (5, header.surfaceDesc, io, handle, page, flags, data); + break; + } + } + return dib; +} + +/* +static BOOL DLL_CALLCONV +Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { + return FALSE; +} +*/ + +// ========================================================== +// Init +// ========================================================== + +void DLL_CALLCONV +InitDDS(Plugin *plugin, int format_id) { + s_format_id = format_id; + + plugin->format_proc = Format; + plugin->description_proc = Description; + plugin->extension_proc = Extension; + plugin->regexpr_proc = RegExpr; + plugin->open_proc = Open; + plugin->close_proc = Close; + plugin->pagecount_proc = NULL; + plugin->pagecapability_proc = NULL; + plugin->load_proc = Load; + plugin->save_proc = NULL; //Save; // not implemented (yet?) + plugin->validate_proc = Validate; + plugin->mime_proc = MimeType; + plugin->supports_export_bpp_proc = SupportsExportDepth; + plugin->supports_export_type_proc = SupportsExportType; + plugin->supports_icc_profiles_proc = NULL; +} |