From c769170fe1e0ba441add74dc4dc23bdee6182699 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Sat, 20 Sep 2014 02:48:07 +0200
Subject: wined3d: Add support for DXTn software decoding through libtxc_dxtn.
 (rev 3)

Changes in rev 2:
* Do not use dxtn library when some imports are missing.
* Do not advertise dxtn converter functions when they are not supported (because
  of missing library or support not compiled in).

Changes in rev 3:
* Do not require txc_dxtn at compile time by trying some fallback paths.
---
 configure.ac                   |   3 +
 dlls/wined3d/Makefile.in       |   1 +
 dlls/wined3d/dxtn.c            | 299 +++++++++++++++++++++++++++++++++++++++++
 dlls/wined3d/surface.c         |  80 +++++++++++
 dlls/wined3d/wined3d_main.c    |   5 +
 dlls/wined3d/wined3d_private.h |  13 ++
 6 files changed, 401 insertions(+)
 create mode 100644 dlls/wined3d/dxtn.c

diff --git a/configure.ac b/configure.ac
index f611fa5..a172206 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1710,6 +1710,9 @@ fi
 WINE_NOTICE_WITH(tiff,[test "x$ac_cv_lib_soname_tiff" = "x"],
                  [libtiff ${notice_platform}development files not found, TIFF won't be supported.])
 
+dnl **** Check for libtxc_dxtn ****
+WINE_CHECK_SONAME(txc_dxtn,tx_compress_dxtn,,,,[[libtxc_dxtn\\(_s2tc\\)\\{0,1\\}]])
+
 dnl **** Check for mpg123 ****
 if test "x$with_mpg123" != "xno"
 then
diff --git a/dlls/wined3d/Makefile.in b/dlls/wined3d/Makefile.in
index 655800b..ee0615f 100644
--- a/dlls/wined3d/Makefile.in
+++ b/dlls/wined3d/Makefile.in
@@ -11,6 +11,7 @@ C_SRCS = \
 	device.c \
 	directx.c \
 	drawprim.c \
+	dxtn.c \
 	gl_compat.c \
 	glsl_shader.c \
 	nvidia_texture_shader.c \
diff --git a/dlls/wined3d/dxtn.c b/dlls/wined3d/dxtn.c
new file mode 100644
index 0000000..ce98949
--- /dev/null
+++ b/dlls/wined3d/dxtn.c
@@ -0,0 +1,299 @@
+/*
+ * 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 (*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 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);
+        default:
+            break;
+    }
+
+    FIXME("Cannot find a conversion function from format DXT1 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
+        "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(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);
+}
diff --git a/dlls/wined3d/surface.c b/dlls/wined3d/surface.c
index 36806ec..03b5f4f 100644
--- a/dlls/wined3d/surface.c
+++ b/dlls/wined3d/surface.c
@@ -2396,6 +2396,66 @@ static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
     }
 }
 
+static void convert_dxt1_a8r8g8b8(const BYTE *src, BYTE *dst,
+        DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
+{
+    wined3d_dxt1_decode(src, dst, pitch_in, pitch_out, WINED3DFMT_B8G8R8A8_UNORM, w, h);
+}
+
+static void convert_dxt1_x8r8g8b8(const BYTE *src, BYTE *dst,
+        DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
+{
+    wined3d_dxt1_decode(src, dst, pitch_in, pitch_out, WINED3DFMT_B8G8R8X8_UNORM, w, h);
+}
+
+static void convert_a8r8g8b8_dxt1(const BYTE *src, BYTE *dst,
+        DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
+{
+    wined3d_dxt1_encode(src, dst, pitch_in, pitch_out, WINED3DFMT_B8G8R8A8_UNORM, w, h);
+}
+
+static void convert_x8r8g8b8_dxt1(const BYTE *src, BYTE *dst,
+        DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
+{
+    wined3d_dxt1_encode(src, dst, pitch_in, pitch_out, WINED3DFMT_B8G8R8X8_UNORM, w, h);
+}
+
+static void convert_a1r5g5b5_dxt1(const BYTE *src, BYTE *dst,
+        DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
+{
+    wined3d_dxt1_encode(src, dst, pitch_in, pitch_out, WINED3DFMT_B5G5R5A1_UNORM, w, h);
+}
+
+static void convert_x1r5g5b5_dxt1(const BYTE *src, BYTE *dst,
+        DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
+{
+    wined3d_dxt1_encode(src, dst, pitch_in, pitch_out, WINED3DFMT_B5G5R5X1_UNORM, w, h);
+}
+
+static void convert_a8r8g8b8_dxt3(const BYTE *src, BYTE *dst,
+        DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
+{
+    wined3d_dxt3_encode(src, dst, pitch_in, pitch_out, WINED3DFMT_B8G8R8A8_UNORM, w, h);
+}
+
+static void convert_x8r8g8b8_dxt3(const BYTE *src, BYTE *dst,
+        DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
+{
+    wined3d_dxt3_encode(src, dst, pitch_in, pitch_out, WINED3DFMT_B8G8R8X8_UNORM, w, h);
+}
+
+static void convert_a8r8g8b8_dxt5(const BYTE *src, BYTE *dst,
+        DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
+{
+    wined3d_dxt5_encode(src, dst, pitch_in, pitch_out, WINED3DFMT_B8G8R8A8_UNORM, w, h);
+}
+
+static void convert_x8r8g8b8_dxt5(const BYTE *src, BYTE *dst,
+        DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
+{
+    wined3d_dxt5_encode(src, dst, pitch_in, pitch_out, WINED3DFMT_B8G8R8X8_UNORM, w, h);
+}
+
 struct d3dfmt_converter_desc
 {
     enum wined3d_format_id from, to;
@@ -2412,6 +2472,20 @@ static const struct d3dfmt_converter_desc converters[] =
     {WINED3DFMT_YUY2,           WINED3DFMT_B5G6R5_UNORM,    convert_yuy2_r5g6b5},
 };
 
+static const struct d3dfmt_converter_desc dxtn_converters[] =
+{
+    {WINED3DFMT_DXT1,           WINED3DFMT_B8G8R8A8_UNORM,  convert_dxt1_a8r8g8b8},
+    {WINED3DFMT_DXT1,           WINED3DFMT_B8G8R8X8_UNORM,  convert_dxt1_x8r8g8b8},
+    {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_DXT1,            convert_a8r8g8b8_dxt1},
+    {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_DXT1,            convert_x8r8g8b8_dxt1},
+    {WINED3DFMT_B5G5R5A1_UNORM, WINED3DFMT_DXT1,            convert_a1r5g5b5_dxt1},
+    {WINED3DFMT_B5G5R5X1_UNORM, WINED3DFMT_DXT1,            convert_x1r5g5b5_dxt1},
+    {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_DXT3,            convert_a8r8g8b8_dxt3},
+    {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_DXT3,            convert_x8r8g8b8_dxt3},
+    {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_DXT5,            convert_a8r8g8b8_dxt5},
+    {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_DXT5,            convert_x8r8g8b8_dxt5}
+};
+
 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
         enum wined3d_format_id to)
 {
@@ -2423,6 +2497,12 @@ static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_fo
             return &converters[i];
     }
 
+    for (i = 0; i < (sizeof(dxtn_converters) / sizeof(*dxtn_converters)); ++i)
+    {
+        if (dxtn_converters[i].from == from && dxtn_converters[i].to == to)
+            return wined3d_dxtn_supported() ? &dxtn_converters[i] : NULL;
+    }
+
     return NULL;
 }
 
diff --git a/dlls/wined3d/wined3d_main.c b/dlls/wined3d/wined3d_main.c
index 758ba43..08021a2 100644
--- a/dlls/wined3d/wined3d_main.c
+++ b/dlls/wined3d/wined3d_main.c
@@ -309,6 +309,8 @@ static BOOL wined3d_dll_init(HINSTANCE hInstDLL)
     if (appkey) RegCloseKey( appkey );
     if (hkey) RegCloseKey( hkey );
 
+    wined3d_dxtn_init();
+
     return TRUE;
 }
 
@@ -340,6 +342,9 @@ static BOOL wined3d_dll_destroy(HINSTANCE hInstDLL)
 
     DeleteCriticalSection(&wined3d_wndproc_cs);
     DeleteCriticalSection(&wined3d_cs);
+
+    wined3d_dxtn_free();
+
     return TRUE;
 }
 
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index b0c7a53..67105f4 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -3176,6 +3176,19 @@ static inline void context_apply_state(struct wined3d_context *context,
     state_table[rep].apply(context, state, rep);
 }
 
+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) DECLSPEC_HIDDEN;
+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) DECLSPEC_HIDDEN;
+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) DECLSPEC_HIDDEN;
+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) DECLSPEC_HIDDEN;
+
+BOOL wined3d_dxtn_init(void) DECLSPEC_HIDDEN;
+BOOL wined3d_dxtn_supported(void) DECLSPEC_HIDDEN;
+void wined3d_dxtn_free(void) DECLSPEC_HIDDEN;
+
 /* The WNDCLASS-Name for the fake window which we use to retrieve the GL capabilities */
 #define WINED3D_OPENGL_WINDOW_CLASS_NAME "WineD3D_OpenGL"
 
-- 
2.3.0

