reactos/dll/directx/wine/wined3d/dxtn.c

520 lines
17 KiB
C

/*
* Copyright 2014 Michael Müller
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "config.h"
#include "wine/port.h"
#include "wined3d_private.h"
#include "wine/library.h"
WINE_DEFAULT_DEBUG_CHANNEL(d3d);
static void* txc_dxtn_handle;
static void (*pfetch_2d_texel_rgba_dxt1)(int srcRowStride, const BYTE *pixData, int i, int j, DWORD *texel);
static void (*pfetch_2d_texel_rgba_dxt3)(int srcRowStride, const BYTE *pixData, int i, int j, DWORD *texel);
static void (*pfetch_2d_texel_rgba_dxt5)(int srcRowStride, const BYTE *pixData, int i, int j, DWORD *texel);
static void (*ptx_compress_dxtn)(int comps, int width, int height, const BYTE *srcPixData,
GLenum destformat, BYTE *dest, int dstRowStride);
static inline BOOL dxt1_to_x8r8g8b8(const BYTE *src, BYTE *dst, DWORD pitch_in,
DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha)
{
unsigned int x, y;
DWORD color;
TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
for (y = 0; y < h; ++y)
{
DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
for (x = 0; x < w; ++x)
{
/* pfetch_2d_texel_rgba_dxt1 doesn't correctly handle pitch */
pfetch_2d_texel_rgba_dxt1(0, src + (y / 4) * pitch_in + (x / 4) * 8,
x & 3, y & 3, &color);
if (alpha)
{
dst_line[x] = (color & 0xff00ff00) | ((color & 0xff) << 16) |
((color & 0xff0000) >> 16);
}
else
{
dst_line[x] = 0xff000000 | ((color & 0xff) << 16) |
(color & 0xff00) | ((color & 0xff0000) >> 16);
}
}
}
return TRUE;
}
static inline BOOL dxt1_to_x4r4g4b4(const BYTE *src, BYTE *dst, DWORD pitch_in,
DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha)
{
unsigned int x, y;
DWORD color;
TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
for (y = 0; y < h; ++y)
{
WORD *dst_line = (WORD *)(dst + y * pitch_out);
for (x = 0; x < w; ++x)
{
/* pfetch_2d_texel_rgba_dxt1 doesn't correctly handle pitch */
pfetch_2d_texel_rgba_dxt1(0, src + (y / 4) * pitch_in + (x / 4) * 16,
x & 3, y & 3, &color);
if (alpha)
{
dst_line[x] = ((color & 0xf0000000) >> 16) | ((color & 0xf00000) >> 20) |
((color & 0xf000) >> 8) | ((color & 0xf0) << 4);
}
else
{
dst_line[x] = 0xf000 | ((color & 0xf00000) >> 20) |
((color & 0xf000) >> 8) | ((color & 0xf0) << 4);
}
}
}
return TRUE;
}
static inline BOOL dxt1_to_x1r5g5b5(const BYTE *src, BYTE *dst, DWORD pitch_in,
DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha)
{
unsigned int x, y;
DWORD color;
TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
for (y = 0; y < h; ++y)
{
WORD *dst_line = (WORD *)(dst + y * pitch_out);
for (x = 0; x < w; ++x)
{
/* pfetch_2d_texel_rgba_dxt1 doesn't correctly handle pitch */
pfetch_2d_texel_rgba_dxt1(0, src + (y / 4) * pitch_in + (x / 4) * 16,
x & 3, y & 3, &color);
if (alpha)
{
dst_line[x] = ((color & 0x80000000) >> 16) | ((color & 0xf80000) >> 19) |
((color & 0xf800) >> 6) | ((color & 0xf8) << 7);
}
else
{
dst_line[x] = 0x8000 | ((color & 0xf80000) >> 19) |
((color & 0xf800) >> 6) | ((color & 0xf8) << 7);
}
}
}
return TRUE;
}
static inline BOOL dxt3_to_x8r8g8b8(const BYTE *src, BYTE *dst, DWORD pitch_in,
DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha)
{
unsigned int x, y;
DWORD color;
TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
for (y = 0; y < h; ++y)
{
DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
for (x = 0; x < w; ++x)
{
/* pfetch_2d_texel_rgba_dxt3 doesn't correctly handle pitch */
pfetch_2d_texel_rgba_dxt3(0, src + (y / 4) * pitch_in + (x / 4) * 16,
x & 3, y & 3, &color);
if (alpha)
{
dst_line[x] = (color & 0xff00ff00) | ((color & 0xff) << 16) |
((color & 0xff0000) >> 16);
}
else
{
dst_line[x] = 0xff000000 | ((color & 0xff) << 16) |
(color & 0xff00) | ((color & 0xff0000) >> 16);
}
}
}
return TRUE;
}
static inline BOOL dxt3_to_x4r4g4b4(const BYTE *src, BYTE *dst, DWORD pitch_in,
DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha)
{
unsigned int x, y;
DWORD color;
TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
for (y = 0; y < h; ++y)
{
WORD *dst_line = (WORD *)(dst + y * pitch_out);
for (x = 0; x < w; ++x)
{
/* pfetch_2d_texel_rgba_dxt3 doesn't correctly handle pitch */
pfetch_2d_texel_rgba_dxt3(0, src + (y / 4) * pitch_in + (x / 4) * 16,
x & 3, y & 3, &color);
if (alpha)
{
dst_line[x] = ((color & 0xf0000000) >> 16) | ((color & 0xf00000) >> 20) |
((color & 0xf000) >> 8) | ((color & 0xf0) << 4);
}
else
{
dst_line[x] = 0xf000 | ((color & 0xf00000) >> 20) |
((color & 0xf000) >> 8) | ((color & 0xf0) << 4);
}
}
}
return TRUE;
}
static inline BOOL dxt5_to_x8r8g8b8(const BYTE *src, BYTE *dst, DWORD pitch_in,
DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha)
{
unsigned int x, y;
DWORD color;
TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
for (y = 0; y < h; ++y)
{
DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
for (x = 0; x < w; ++x)
{
/* pfetch_2d_texel_rgba_dxt5 doesn't correctly handle pitch */
pfetch_2d_texel_rgba_dxt5(0, src + (y / 4) * pitch_in + (x / 4) * 16,
x & 3, y & 3, &color);
if (alpha)
{
dst_line[x] = (color & 0xff00ff00) | ((color & 0xff) << 16) |
((color & 0xff0000) >> 16);
}
else
{
dst_line[x] = 0xff000000 | ((color & 0xff) << 16) |
(color & 0xff00) | ((color & 0xff0000) >> 16);
}
}
}
return TRUE;
}
static inline BOOL x8r8g8b8_to_dxtn(const BYTE *src, BYTE *dst, DWORD pitch_in,
DWORD pitch_out, unsigned int w, unsigned int h, GLenum destformat, BOOL alpha)
{
unsigned int x, y;
DWORD color, *tmp;
TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
tmp = HeapAlloc(GetProcessHeap(), 0, h * w * sizeof(DWORD));
if (!tmp)
{
ERR("Failed to allocate memory for conversion\n");
return FALSE;
}
for (y = 0; y < h; ++y)
{
const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
DWORD *dst_line = tmp + y * w;
for (x = 0; x < w; ++x)
{
color = src_line[x];
if (alpha)
{
dst_line[x] = (color & 0xff00ff00) | ((color & 0xff) << 16) |
((color & 0xff0000) >> 16);
}
else
{
dst_line[x] = 0xff000000 | ((color & 0xff) << 16) |
(color & 0xff00) | ((color & 0xff0000) >> 16);
}
}
}
ptx_compress_dxtn(4, w, h, (BYTE *)tmp, destformat, dst, pitch_out);
HeapFree(GetProcessHeap(), 0, tmp);
return TRUE;
}
static inline BOOL x1r5g5b5_to_dxtn(const BYTE *src, BYTE *dst, DWORD pitch_in,
DWORD pitch_out, unsigned int w, unsigned int h, GLenum destformat, BOOL alpha)
{
static const unsigned char convert_5to8[] =
{
0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
};
unsigned int x, y;
DWORD *tmp;
WORD color;
TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
tmp = HeapAlloc(GetProcessHeap(), 0, h * w * sizeof(DWORD));
if (!tmp)
{
ERR("Failed to allocate memory for conversion\n");
return FALSE;
}
for (y = 0; y < h; ++y)
{
const WORD *src_line = (const WORD *)(src + y * pitch_in);
DWORD *dst_line = tmp + y * w;
for (x = 0; x < w; ++x)
{
color = src_line[x];
if (alpha)
{
dst_line[x] = ((color & 0x8000) ? 0xff000000 : 0) |
convert_5to8[(color & 0x001f)] << 16 |
convert_5to8[(color & 0x03e0) >> 5] << 8 |
convert_5to8[(color & 0x7c00) >> 10];
}
else
{
dst_line[x] = 0xff000000 |
convert_5to8[(color & 0x001f)] << 16 |
convert_5to8[(color & 0x03e0) >> 5] << 8 |
convert_5to8[(color & 0x7c00) >> 10];
}
}
}
ptx_compress_dxtn(4, w, h, (BYTE *)tmp, destformat, dst, pitch_out);
HeapFree(GetProcessHeap(), 0, tmp);
return TRUE;
}
BOOL wined3d_dxt1_decode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out,
enum wined3d_format_id format, unsigned int w, unsigned int h)
{
if (!txc_dxtn_handle)
return FALSE;
switch (format)
{
case WINED3DFMT_B8G8R8A8_UNORM:
return dxt1_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, TRUE);
case WINED3DFMT_B8G8R8X8_UNORM:
return dxt1_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, FALSE);
case WINED3DFMT_B4G4R4A4_UNORM:
return dxt1_to_x4r4g4b4(src, dst, pitch_in, pitch_out, w, h, TRUE);
case WINED3DFMT_B4G4R4X4_UNORM:
return dxt1_to_x4r4g4b4(src, dst, pitch_in, pitch_out, w, h, FALSE);
case WINED3DFMT_B5G5R5A1_UNORM:
return dxt1_to_x1r5g5b5(src, dst, pitch_in, pitch_out, w, h, TRUE);
case WINED3DFMT_B5G5R5X1_UNORM:
return dxt1_to_x1r5g5b5(src, dst, pitch_in, pitch_out, w, h, FALSE);
default:
break;
}
FIXME("Cannot find a conversion function from format DXT1 to %s.\n", debug_d3dformat(format));
return FALSE;
}
BOOL wined3d_dxt3_decode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out,
enum wined3d_format_id format, unsigned int w, unsigned int h)
{
if (!txc_dxtn_handle)
return FALSE;
switch (format)
{
case WINED3DFMT_B8G8R8A8_UNORM:
return dxt3_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, TRUE);
case WINED3DFMT_B8G8R8X8_UNORM:
return dxt3_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, FALSE);
case WINED3DFMT_B4G4R4A4_UNORM:
return dxt3_to_x4r4g4b4(src, dst, pitch_in, pitch_out, w, h, TRUE);
case WINED3DFMT_B4G4R4X4_UNORM:
return dxt3_to_x4r4g4b4(src, dst, pitch_in, pitch_out, w, h, FALSE);
default:
break;
}
FIXME("Cannot find a conversion function from format DXT3 to %s.\n", debug_d3dformat(format));
return FALSE;
}
BOOL wined3d_dxt5_decode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out,
enum wined3d_format_id format, unsigned int w, unsigned int h)
{
if (!txc_dxtn_handle)
return FALSE;
switch (format)
{
case WINED3DFMT_B8G8R8A8_UNORM:
return dxt5_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, TRUE);
case WINED3DFMT_B8G8R8X8_UNORM:
return dxt5_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, FALSE);
default:
break;
}
FIXME("Cannot find a conversion function from format DXT5 to %s.\n", debug_d3dformat(format));
return FALSE;
}
BOOL wined3d_dxt1_encode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out,
enum wined3d_format_id format, unsigned int w, unsigned int h)
{
if (!txc_dxtn_handle)
return FALSE;
switch (format)
{
case WINED3DFMT_B8G8R8A8_UNORM:
return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, TRUE);
case WINED3DFMT_B8G8R8X8_UNORM:
return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
GL_COMPRESSED_RGB_S3TC_DXT1_EXT, FALSE);
case WINED3DFMT_B5G5R5A1_UNORM:
return x1r5g5b5_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, TRUE);
case WINED3DFMT_B5G5R5X1_UNORM:
return x1r5g5b5_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
GL_COMPRESSED_RGB_S3TC_DXT1_EXT, FALSE);
default:
break;
}
FIXME("Cannot find a conversion function from format %s to DXT1.\n", debug_d3dformat(format));
return FALSE;
}
BOOL wined3d_dxt3_encode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out,
enum wined3d_format_id format, unsigned int w, unsigned int h)
{
if (!txc_dxtn_handle)
return FALSE;
switch (format)
{
case WINED3DFMT_B8G8R8A8_UNORM:
return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, TRUE);
case WINED3DFMT_B8G8R8X8_UNORM:
return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, FALSE);
default:
break;
}
FIXME("Cannot find a conversion function from format %s to DXT3.\n", debug_d3dformat(format));
return FALSE;
}
BOOL wined3d_dxt5_encode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out,
enum wined3d_format_id format, unsigned int w, unsigned int h)
{
if (!txc_dxtn_handle)
return FALSE;
switch (format)
{
case WINED3DFMT_B8G8R8A8_UNORM:
return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, TRUE);
case WINED3DFMT_B8G8R8X8_UNORM:
return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, FALSE);
default:
break;
}
FIXME("Cannot find a conversion function from format %s to DXT5.\n", debug_d3dformat(format));
return FALSE;
}
BOOL wined3d_dxtn_init(void)
{
static const char *soname[] =
{
#ifdef SONAME_LIBTXC_DXTN
SONAME_LIBTXC_DXTN,
#endif
#ifdef __APPLE__
"libtxc_dxtn.dylib",
"libtxc_dxtn_s2tc.dylib",
#endif
"libtxc_dxtn.so",
"libtxc_dxtn_s2tc.so.0"
};
int i;
for (i = 0; i < sizeof(soname)/sizeof(soname[0]); i++)
{
txc_dxtn_handle = wine_dlopen(soname[i], RTLD_NOW, NULL, 0);
if (txc_dxtn_handle) break;
}
if (!txc_dxtn_handle)
{
FIXME("Wine cannot find the txc_dxtn library, DXTn software support unavailable.\n");
return FALSE;
}
#define LOAD_FUNCPTR(f) \
if (!(p##f = wine_dlsym(txc_dxtn_handle, #f, NULL, 0))) \
{ \
ERR("Can't find symbol %s , DXTn software support unavailable.\n", #f); \
goto error; \
}
LOAD_FUNCPTR(fetch_2d_texel_rgba_dxt1);
LOAD_FUNCPTR(fetch_2d_texel_rgba_dxt3);
LOAD_FUNCPTR(fetch_2d_texel_rgba_dxt5);
LOAD_FUNCPTR(tx_compress_dxtn);
#undef LOAD_FUNCPTR
return TRUE;
error:
wine_dlclose(txc_dxtn_handle, NULL, 0);
txc_dxtn_handle = NULL;
return FALSE;
}
BOOL wined3d_dxtn_supported(void)
{
return (txc_dxtn_handle != NULL);
}
void wined3d_dxtn_free(void)
{
if (txc_dxtn_handle)
wine_dlclose(txc_dxtn_handle, NULL, 0);
}