xref: /reactos/dll/directx/wine/wined3d/dxtn.c (revision 9987f029)
1 /*
2  * Copyright 2014 Michael Müller
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include "config.h"
20 #include "wine/port.h"
21 #include "wined3d_private.h"
22 #include "wine/library.h"
23 
24 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
25 
26 static void* txc_dxtn_handle;
27 static void (*pfetch_2d_texel_rgba_dxt1)(int srcRowStride, const BYTE *pixData, int i, int j, DWORD *texel);
28 static void (*pfetch_2d_texel_rgba_dxt3)(int srcRowStride, const BYTE *pixData, int i, int j, DWORD *texel);
29 static void (*pfetch_2d_texel_rgba_dxt5)(int srcRowStride, const BYTE *pixData, int i, int j, DWORD *texel);
30 static void (*ptx_compress_dxtn)(int comps, int width, int height, const BYTE *srcPixData,
31                                  GLenum destformat, BYTE *dest, int dstRowStride);
32 
dxt1_to_x8r8g8b8(const BYTE * src,BYTE * dst,DWORD pitch_in,DWORD pitch_out,unsigned int w,unsigned int h,BOOL alpha)33 static inline BOOL dxt1_to_x8r8g8b8(const BYTE *src, BYTE *dst, DWORD pitch_in,
34         DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha)
35 {
36     unsigned int x, y;
37     DWORD color;
38 
39     TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
40 
41     for (y = 0; y < h; ++y)
42     {
43         DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
44         for (x = 0; x < w; ++x)
45         {
46             /* pfetch_2d_texel_rgba_dxt1 doesn't correctly handle pitch */
47             pfetch_2d_texel_rgba_dxt1(0, src + (y / 4) * pitch_in + (x / 4) * 8,
48                                       x & 3, y & 3, &color);
49             if (alpha)
50             {
51                 dst_line[x] = (color & 0xff00ff00) | ((color & 0xff) << 16) |
52                               ((color & 0xff0000) >> 16);
53             }
54             else
55             {
56                 dst_line[x] = 0xff000000 | ((color & 0xff) << 16) |
57                               (color & 0xff00) | ((color & 0xff0000) >> 16);
58             }
59         }
60     }
61 
62     return TRUE;
63 }
64 
dxt1_to_x4r4g4b4(const BYTE * src,BYTE * dst,DWORD pitch_in,DWORD pitch_out,unsigned int w,unsigned int h,BOOL alpha)65 static inline BOOL dxt1_to_x4r4g4b4(const BYTE *src, BYTE *dst, DWORD pitch_in,
66         DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha)
67 {
68     unsigned int x, y;
69     DWORD color;
70 
71     TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
72 
73     for (y = 0; y < h; ++y)
74     {
75         WORD *dst_line = (WORD *)(dst + y * pitch_out);
76         for (x = 0; x < w; ++x)
77         {
78             /* pfetch_2d_texel_rgba_dxt1 doesn't correctly handle pitch */
79             pfetch_2d_texel_rgba_dxt1(0, src + (y / 4) * pitch_in + (x / 4) * 16,
80                                       x & 3, y & 3, &color);
81             if (alpha)
82             {
83                 dst_line[x] = ((color & 0xf0000000) >> 16) | ((color & 0xf00000) >> 20) |
84                               ((color & 0xf000) >> 8) | ((color & 0xf0) << 4);
85             }
86             else
87             {
88                 dst_line[x] = 0xf000  | ((color & 0xf00000) >> 20) |
89                               ((color & 0xf000) >> 8) | ((color & 0xf0) << 4);
90             }
91         }
92     }
93 
94     return TRUE;
95 }
96 
dxt1_to_x1r5g5b5(const BYTE * src,BYTE * dst,DWORD pitch_in,DWORD pitch_out,unsigned int w,unsigned int h,BOOL alpha)97 static inline BOOL dxt1_to_x1r5g5b5(const BYTE *src, BYTE *dst, DWORD pitch_in,
98         DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha)
99 {
100     unsigned int x, y;
101     DWORD color;
102 
103     TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
104 
105     for (y = 0; y < h; ++y)
106     {
107         WORD *dst_line = (WORD *)(dst + y * pitch_out);
108         for (x = 0; x < w; ++x)
109         {
110             /* pfetch_2d_texel_rgba_dxt1 doesn't correctly handle pitch */
111             pfetch_2d_texel_rgba_dxt1(0, src + (y / 4) * pitch_in + (x / 4) * 16,
112                                       x & 3, y & 3, &color);
113             if (alpha)
114             {
115                 dst_line[x] = ((color & 0x80000000) >> 16) | ((color & 0xf80000) >> 19) |
116                               ((color & 0xf800) >> 6) | ((color & 0xf8) << 7);
117             }
118             else
119             {
120                 dst_line[x] = 0x8000 | ((color & 0xf80000) >> 19) |
121                               ((color & 0xf800) >> 6) | ((color & 0xf8) << 7);
122             }
123         }
124     }
125 
126     return TRUE;
127 }
128 
dxt3_to_x8r8g8b8(const BYTE * src,BYTE * dst,DWORD pitch_in,DWORD pitch_out,unsigned int w,unsigned int h,BOOL alpha)129 static inline BOOL dxt3_to_x8r8g8b8(const BYTE *src, BYTE *dst, DWORD pitch_in,
130         DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha)
131 {
132     unsigned int x, y;
133     DWORD color;
134 
135     TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
136 
137     for (y = 0; y < h; ++y)
138     {
139         DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
140         for (x = 0; x < w; ++x)
141         {
142             /* pfetch_2d_texel_rgba_dxt3 doesn't correctly handle pitch */
143             pfetch_2d_texel_rgba_dxt3(0, src + (y / 4) * pitch_in + (x / 4) * 16,
144                                       x & 3, y & 3, &color);
145             if (alpha)
146             {
147                 dst_line[x] = (color & 0xff00ff00) | ((color & 0xff) << 16) |
148                               ((color & 0xff0000) >> 16);
149             }
150             else
151             {
152                 dst_line[x] = 0xff000000 | ((color & 0xff) << 16) |
153                               (color & 0xff00) | ((color & 0xff0000) >> 16);
154             }
155         }
156     }
157 
158     return TRUE;
159 }
160 
dxt3_to_x4r4g4b4(const BYTE * src,BYTE * dst,DWORD pitch_in,DWORD pitch_out,unsigned int w,unsigned int h,BOOL alpha)161 static inline BOOL dxt3_to_x4r4g4b4(const BYTE *src, BYTE *dst, DWORD pitch_in,
162         DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha)
163 {
164     unsigned int x, y;
165     DWORD color;
166 
167     TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
168 
169     for (y = 0; y < h; ++y)
170     {
171         WORD *dst_line = (WORD *)(dst + y * pitch_out);
172         for (x = 0; x < w; ++x)
173         {
174             /* pfetch_2d_texel_rgba_dxt3 doesn't correctly handle pitch */
175             pfetch_2d_texel_rgba_dxt3(0, src + (y / 4) * pitch_in + (x / 4) * 16,
176                                       x & 3, y & 3, &color);
177             if (alpha)
178             {
179                 dst_line[x] = ((color & 0xf0000000) >> 16) | ((color & 0xf00000) >> 20) |
180                               ((color & 0xf000) >> 8) | ((color & 0xf0) << 4);
181             }
182             else
183             {
184                 dst_line[x] = 0xf000  | ((color & 0xf00000) >> 20) |
185                               ((color & 0xf000) >> 8) | ((color & 0xf0) << 4);
186             }
187         }
188     }
189 
190     return TRUE;
191 }
192 
dxt5_to_x8r8g8b8(const BYTE * src,BYTE * dst,DWORD pitch_in,DWORD pitch_out,unsigned int w,unsigned int h,BOOL alpha)193 static inline BOOL dxt5_to_x8r8g8b8(const BYTE *src, BYTE *dst, DWORD pitch_in,
194         DWORD pitch_out, unsigned int w, unsigned int h, BOOL alpha)
195 {
196     unsigned int x, y;
197     DWORD color;
198 
199     TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
200 
201     for (y = 0; y < h; ++y)
202     {
203         DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
204         for (x = 0; x < w; ++x)
205         {
206             /* pfetch_2d_texel_rgba_dxt5 doesn't correctly handle pitch */
207             pfetch_2d_texel_rgba_dxt5(0, src + (y / 4) * pitch_in + (x / 4) * 16,
208                                       x & 3, y & 3, &color);
209             if (alpha)
210             {
211                 dst_line[x] = (color & 0xff00ff00) | ((color & 0xff) << 16) |
212                               ((color & 0xff0000) >> 16);
213             }
214             else
215             {
216                 dst_line[x] = 0xff000000 | ((color & 0xff) << 16) |
217                               (color & 0xff00) | ((color & 0xff0000) >> 16);
218             }
219         }
220     }
221 
222     return TRUE;
223 }
224 
x8r8g8b8_to_dxtn(const BYTE * src,BYTE * dst,DWORD pitch_in,DWORD pitch_out,unsigned int w,unsigned int h,GLenum destformat,BOOL alpha)225 static inline BOOL x8r8g8b8_to_dxtn(const BYTE *src, BYTE *dst, DWORD pitch_in,
226         DWORD pitch_out, unsigned int w, unsigned int h, GLenum destformat, BOOL alpha)
227 {
228     unsigned int x, y;
229     DWORD color, *tmp;
230 
231     TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
232 
233     tmp = HeapAlloc(GetProcessHeap(), 0, h * w * sizeof(DWORD));
234     if (!tmp)
235     {
236         ERR("Failed to allocate memory for conversion\n");
237         return FALSE;
238     }
239 
240     for (y = 0; y < h; ++y)
241     {
242         const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
243         DWORD *dst_line = tmp + y * w;
244         for (x = 0; x < w; ++x)
245         {
246             color = src_line[x];
247             if (alpha)
248             {
249                 dst_line[x] = (color & 0xff00ff00) | ((color & 0xff) << 16) |
250                               ((color & 0xff0000) >> 16);
251             }
252             else
253             {
254                 dst_line[x] = 0xff000000 | ((color & 0xff) << 16) |
255                               (color & 0xff00) | ((color & 0xff0000) >> 16);
256             }
257         }
258     }
259 
260     ptx_compress_dxtn(4, w, h, (BYTE *)tmp, destformat, dst, pitch_out);
261     HeapFree(GetProcessHeap(), 0, tmp);
262     return TRUE;
263 }
264 
x1r5g5b5_to_dxtn(const BYTE * src,BYTE * dst,DWORD pitch_in,DWORD pitch_out,unsigned int w,unsigned int h,GLenum destformat,BOOL alpha)265 static inline BOOL x1r5g5b5_to_dxtn(const BYTE *src, BYTE *dst, DWORD pitch_in,
266         DWORD pitch_out, unsigned int w, unsigned int h, GLenum destformat, BOOL alpha)
267 {
268     static const unsigned char convert_5to8[] =
269     {
270         0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
271         0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
272         0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
273         0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
274     };
275     unsigned int x, y;
276     DWORD *tmp;
277     WORD color;
278 
279     TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
280 
281     tmp = HeapAlloc(GetProcessHeap(), 0, h * w * sizeof(DWORD));
282     if (!tmp)
283     {
284         ERR("Failed to allocate memory for conversion\n");
285         return FALSE;
286     }
287 
288     for (y = 0; y < h; ++y)
289     {
290         const WORD *src_line = (const WORD *)(src + y * pitch_in);
291         DWORD *dst_line = tmp + y * w;
292         for (x = 0; x < w; ++x)
293         {
294             color = src_line[x];
295             if (alpha)
296             {
297                 dst_line[x] = ((color & 0x8000) ? 0xff000000 : 0) |
298                               convert_5to8[(color & 0x001f)] << 16 |
299                               convert_5to8[(color & 0x03e0) >> 5] << 8 |
300                               convert_5to8[(color & 0x7c00) >> 10];
301             }
302             else
303             {
304                 dst_line[x] = 0xff000000 |
305                               convert_5to8[(color & 0x001f)] << 16 |
306                               convert_5to8[(color & 0x03e0) >> 5] << 8 |
307                               convert_5to8[(color & 0x7c00) >> 10];
308             }
309         }
310     }
311 
312     ptx_compress_dxtn(4, w, h, (BYTE *)tmp, destformat, dst, pitch_out);
313     HeapFree(GetProcessHeap(), 0, tmp);
314     return TRUE;
315 }
316 
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)317 BOOL wined3d_dxt1_decode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out,
318         enum wined3d_format_id format, unsigned int w, unsigned int h)
319 {
320     if (!txc_dxtn_handle)
321         return FALSE;
322 
323     switch (format)
324     {
325         case WINED3DFMT_B8G8R8A8_UNORM:
326             return dxt1_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, TRUE);
327         case WINED3DFMT_B8G8R8X8_UNORM:
328             return dxt1_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, FALSE);
329         case WINED3DFMT_B4G4R4A4_UNORM:
330             return dxt1_to_x4r4g4b4(src, dst, pitch_in, pitch_out, w, h, TRUE);
331         case WINED3DFMT_B4G4R4X4_UNORM:
332             return dxt1_to_x4r4g4b4(src, dst, pitch_in, pitch_out, w, h, FALSE);
333         case WINED3DFMT_B5G5R5A1_UNORM:
334             return dxt1_to_x1r5g5b5(src, dst, pitch_in, pitch_out, w, h, TRUE);
335         case WINED3DFMT_B5G5R5X1_UNORM:
336             return dxt1_to_x1r5g5b5(src, dst, pitch_in, pitch_out, w, h, FALSE);
337         default:
338             break;
339     }
340 
341     FIXME("Cannot find a conversion function from format DXT1 to %s.\n", debug_d3dformat(format));
342     return FALSE;
343 }
344 
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)345 BOOL wined3d_dxt3_decode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out,
346         enum wined3d_format_id format, unsigned int w, unsigned int h)
347 {
348     if (!txc_dxtn_handle)
349         return FALSE;
350 
351     switch (format)
352     {
353         case WINED3DFMT_B8G8R8A8_UNORM:
354             return dxt3_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, TRUE);
355         case WINED3DFMT_B8G8R8X8_UNORM:
356             return dxt3_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, FALSE);
357         case WINED3DFMT_B4G4R4A4_UNORM:
358             return dxt3_to_x4r4g4b4(src, dst, pitch_in, pitch_out, w, h, TRUE);
359         case WINED3DFMT_B4G4R4X4_UNORM:
360             return dxt3_to_x4r4g4b4(src, dst, pitch_in, pitch_out, w, h, FALSE);
361         default:
362             break;
363     }
364 
365     FIXME("Cannot find a conversion function from format DXT3 to %s.\n", debug_d3dformat(format));
366     return FALSE;
367 }
368 
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)369 BOOL wined3d_dxt5_decode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out,
370         enum wined3d_format_id format, unsigned int w, unsigned int h)
371 {
372     if (!txc_dxtn_handle)
373         return FALSE;
374 
375     switch (format)
376     {
377         case WINED3DFMT_B8G8R8A8_UNORM:
378             return dxt5_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, TRUE);
379         case WINED3DFMT_B8G8R8X8_UNORM:
380             return dxt5_to_x8r8g8b8(src, dst, pitch_in, pitch_out, w, h, FALSE);
381         default:
382             break;
383     }
384 
385     FIXME("Cannot find a conversion function from format DXT5 to %s.\n", debug_d3dformat(format));
386     return FALSE;
387 }
388 
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)389 BOOL wined3d_dxt1_encode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out,
390         enum wined3d_format_id format, unsigned int w, unsigned int h)
391 {
392     if (!txc_dxtn_handle)
393         return FALSE;
394 
395     switch (format)
396     {
397         case WINED3DFMT_B8G8R8A8_UNORM:
398             return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
399                                     GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, TRUE);
400         case WINED3DFMT_B8G8R8X8_UNORM:
401             return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
402                                     GL_COMPRESSED_RGB_S3TC_DXT1_EXT, FALSE);
403         case WINED3DFMT_B5G5R5A1_UNORM:
404             return x1r5g5b5_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
405                                     GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, TRUE);
406         case WINED3DFMT_B5G5R5X1_UNORM:
407             return x1r5g5b5_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
408                                     GL_COMPRESSED_RGB_S3TC_DXT1_EXT, FALSE);
409         default:
410             break;
411     }
412 
413     FIXME("Cannot find a conversion function from format %s to DXT1.\n", debug_d3dformat(format));
414     return FALSE;
415 }
416 
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)417 BOOL wined3d_dxt3_encode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out,
418         enum wined3d_format_id format, unsigned int w, unsigned int h)
419 {
420     if (!txc_dxtn_handle)
421         return FALSE;
422 
423     switch (format)
424     {
425         case WINED3DFMT_B8G8R8A8_UNORM:
426             return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
427                                     GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, TRUE);
428         case WINED3DFMT_B8G8R8X8_UNORM:
429             return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
430                                     GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, FALSE);
431         default:
432             break;
433     }
434 
435     FIXME("Cannot find a conversion function from format %s to DXT3.\n", debug_d3dformat(format));
436     return FALSE;
437 }
438 
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)439 BOOL wined3d_dxt5_encode(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out,
440         enum wined3d_format_id format, unsigned int w, unsigned int h)
441 {
442     if (!txc_dxtn_handle)
443         return FALSE;
444 
445     switch (format)
446     {
447         case WINED3DFMT_B8G8R8A8_UNORM:
448             return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
449                                     GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, TRUE);
450         case WINED3DFMT_B8G8R8X8_UNORM:
451             return x8r8g8b8_to_dxtn(src, dst, pitch_in, pitch_out, w, h,
452                                     GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, FALSE);
453         default:
454             break;
455     }
456 
457     FIXME("Cannot find a conversion function from format %s to DXT5.\n", debug_d3dformat(format));
458     return FALSE;
459 }
460 
wined3d_dxtn_init(void)461 BOOL wined3d_dxtn_init(void)
462 {
463     static const char *soname[] =
464     {
465 #ifdef SONAME_LIBTXC_DXTN
466         SONAME_LIBTXC_DXTN,
467 #endif
468 #ifdef __APPLE__
469         "libtxc_dxtn.dylib",
470         "libtxc_dxtn_s2tc.dylib",
471 #endif
472         "libtxc_dxtn.so",
473         "libtxc_dxtn_s2tc.so.0"
474     };
475     int i;
476 
477     for (i = 0; i < sizeof(soname)/sizeof(soname[0]); i++)
478     {
479         txc_dxtn_handle = wine_dlopen(soname[i], RTLD_NOW, NULL, 0);
480         if (txc_dxtn_handle) break;
481     }
482 
483     if (!txc_dxtn_handle)
484     {
485         FIXME("Wine cannot find the txc_dxtn library, DXTn software support unavailable.\n");
486         return FALSE;
487     }
488 
489     #define LOAD_FUNCPTR(f) \
490         if (!(p##f = wine_dlsym(txc_dxtn_handle, #f, NULL, 0))) \
491         { \
492             ERR("Can't find symbol %s , DXTn software support unavailable.\n", #f); \
493             goto error; \
494         }
495 
496     LOAD_FUNCPTR(fetch_2d_texel_rgba_dxt1);
497     LOAD_FUNCPTR(fetch_2d_texel_rgba_dxt3);
498     LOAD_FUNCPTR(fetch_2d_texel_rgba_dxt5);
499     LOAD_FUNCPTR(tx_compress_dxtn);
500 
501     #undef LOAD_FUNCPTR
502     return TRUE;
503 
504 error:
505     wine_dlclose(txc_dxtn_handle, NULL, 0);
506     txc_dxtn_handle = NULL;
507     return FALSE;
508 }
509 
wined3d_dxtn_supported(void)510 BOOL wined3d_dxtn_supported(void)
511 {
512     return (txc_dxtn_handle != NULL);
513 }
514 
wined3d_dxtn_free(void)515 void wined3d_dxtn_free(void)
516 {
517     if (txc_dxtn_handle)
518         wine_dlclose(txc_dxtn_handle, NULL, 0);
519 }
520