1/*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 1999-2007  Brian Paul   All Rights Reserved.
5 * Copyright (c) 2008 VMware, Inc.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26
27/**
28 * \file texcompress_s3tc.c
29 * GL_EXT_texture_compression_s3tc support.
30 */
31
32#include "glheader.h"
33#include "imports.h"
34#include "dlopen.h"
35#include "image.h"
36#include "macros.h"
37#include "mtypes.h"
38#include "texcompress.h"
39#include "texcompress_s3tc.h"
40#include "texstore.h"
41#include "format_unpack.h"
42#include "util/format_srgb.h"
43
44
45#if defined(_WIN32) || defined(WIN32)
46#define DXTN_LIBNAME "dxtn.dll"
47#define RTLD_LAZY 0
48#define RTLD_GLOBAL 0
49#elif defined(__CYGWIN__)
50#define DXTN_LIBNAME "cygtxc_dxtn.dll"
51#else
52#define DXTN_LIBNAME "libtxc_dxtn.so"
53#endif
54
55typedef void (*dxtFetchTexelFuncExt)( GLint srcRowstride, const GLubyte *pixdata, GLint col, GLint row, GLvoid *texelOut );
56
57static dxtFetchTexelFuncExt fetch_ext_rgb_dxt1 = NULL;
58static dxtFetchTexelFuncExt fetch_ext_rgba_dxt1 = NULL;
59static dxtFetchTexelFuncExt fetch_ext_rgba_dxt3 = NULL;
60static dxtFetchTexelFuncExt fetch_ext_rgba_dxt5 = NULL;
61
62typedef void (*dxtCompressTexFuncExt)(GLint srccomps, GLint width,
63                                      GLint height, const GLubyte *srcPixData,
64                                      GLenum destformat, GLubyte *dest,
65                                      GLint dstRowStride);
66
67static dxtCompressTexFuncExt ext_tx_compress_dxtn = NULL;
68
69static void *dxtlibhandle = NULL;
70
71
72void
73_mesa_init_texture_s3tc( struct gl_context *ctx )
74{
75   /* called during context initialization */
76   ctx->Mesa_DXTn = GL_FALSE;
77   if (!dxtlibhandle) {
78      dxtlibhandle = _mesa_dlopen(DXTN_LIBNAME, 0);
79      if (!dxtlibhandle) {
80	 _mesa_warning(ctx, "couldn't open " DXTN_LIBNAME ", software DXTn "
81	    "compression/decompression unavailable");
82      }
83      else {
84         /* the fetch functions are not per context! Might be problematic... */
85         fetch_ext_rgb_dxt1 = (dxtFetchTexelFuncExt)
86            _mesa_dlsym(dxtlibhandle, "fetch_2d_texel_rgb_dxt1");
87         fetch_ext_rgba_dxt1 = (dxtFetchTexelFuncExt)
88            _mesa_dlsym(dxtlibhandle, "fetch_2d_texel_rgba_dxt1");
89         fetch_ext_rgba_dxt3 = (dxtFetchTexelFuncExt)
90            _mesa_dlsym(dxtlibhandle, "fetch_2d_texel_rgba_dxt3");
91         fetch_ext_rgba_dxt5 = (dxtFetchTexelFuncExt)
92            _mesa_dlsym(dxtlibhandle, "fetch_2d_texel_rgba_dxt5");
93         ext_tx_compress_dxtn = (dxtCompressTexFuncExt)
94            _mesa_dlsym(dxtlibhandle, "tx_compress_dxtn");
95
96         if (!fetch_ext_rgb_dxt1 ||
97             !fetch_ext_rgba_dxt1 ||
98             !fetch_ext_rgba_dxt3 ||
99             !fetch_ext_rgba_dxt5 ||
100             !ext_tx_compress_dxtn) {
101	    _mesa_warning(ctx, "couldn't reference all symbols in "
102	       DXTN_LIBNAME ", software DXTn compression/decompression "
103	       "unavailable");
104            fetch_ext_rgb_dxt1 = NULL;
105            fetch_ext_rgba_dxt1 = NULL;
106            fetch_ext_rgba_dxt3 = NULL;
107            fetch_ext_rgba_dxt5 = NULL;
108            ext_tx_compress_dxtn = NULL;
109            _mesa_dlclose(dxtlibhandle);
110            dxtlibhandle = NULL;
111         }
112      }
113   }
114   if (dxtlibhandle) {
115      ctx->Mesa_DXTn = GL_TRUE;
116   }
117}
118
119/**
120 * Store user's image in rgb_dxt1 format.
121 */
122GLboolean
123_mesa_texstore_rgb_dxt1(TEXSTORE_PARAMS)
124{
125   const GLubyte *pixels;
126   GLubyte *dst;
127   const GLubyte *tempImage = NULL;
128
129   assert(dstFormat == MESA_FORMAT_RGB_DXT1 ||
130          dstFormat == MESA_FORMAT_SRGB_DXT1);
131
132   if (srcFormat != GL_RGB ||
133       srcType != GL_UNSIGNED_BYTE ||
134       ctx->_ImageTransferState ||
135       ALIGN(srcPacking->RowLength, srcPacking->Alignment) != srcWidth ||
136       srcPacking->SwapBytes) {
137      /* convert image to RGB/GLubyte */
138      GLubyte *tempImageSlices[1];
139      int rgbRowStride = 3 * srcWidth * sizeof(GLubyte);
140      tempImage = malloc(srcWidth * srcHeight * 3 * sizeof(GLubyte));
141      if (!tempImage)
142         return GL_FALSE; /* out of memory */
143      tempImageSlices[0] = (GLubyte *) tempImage;
144      _mesa_texstore(ctx, dims,
145                     baseInternalFormat,
146                     MESA_FORMAT_RGB_UNORM8,
147                     rgbRowStride, tempImageSlices,
148                     srcWidth, srcHeight, srcDepth,
149                     srcFormat, srcType, srcAddr,
150                     srcPacking);
151      pixels = tempImage;
152      srcFormat = GL_RGB;
153   }
154   else {
155      pixels = _mesa_image_address2d(srcPacking, srcAddr, srcWidth, srcHeight,
156                                     srcFormat, srcType, 0, 0);
157   }
158
159   dst = dstSlices[0];
160
161   if (ext_tx_compress_dxtn) {
162      (*ext_tx_compress_dxtn)(3, srcWidth, srcHeight, pixels,
163                              GL_COMPRESSED_RGB_S3TC_DXT1_EXT,
164                              dst, dstRowStride);
165   }
166   else {
167      _mesa_warning(ctx, "external dxt library not available: texstore_rgb_dxt1");
168   }
169
170   free((void *) tempImage);
171
172   return GL_TRUE;
173}
174
175
176/**
177 * Store user's image in rgba_dxt1 format.
178 */
179GLboolean
180_mesa_texstore_rgba_dxt1(TEXSTORE_PARAMS)
181{
182   const GLubyte *pixels;
183   GLubyte *dst;
184   const GLubyte *tempImage = NULL;
185
186   assert(dstFormat == MESA_FORMAT_RGBA_DXT1 ||
187          dstFormat == MESA_FORMAT_SRGBA_DXT1);
188
189   if (srcFormat != GL_RGBA ||
190       srcType != GL_UNSIGNED_BYTE ||
191       ctx->_ImageTransferState ||
192       ALIGN(srcPacking->RowLength, srcPacking->Alignment) != srcWidth ||
193       srcPacking->SwapBytes) {
194      /* convert image to RGBA/GLubyte */
195      GLubyte *tempImageSlices[1];
196      int rgbaRowStride = 4 * srcWidth * sizeof(GLubyte);
197      tempImage = malloc(srcWidth * srcHeight * 4 * sizeof(GLubyte));
198      if (!tempImage)
199         return GL_FALSE; /* out of memory */
200      tempImageSlices[0] = (GLubyte *) tempImage;
201      _mesa_texstore(ctx, dims,
202                     baseInternalFormat,
203                     _mesa_little_endian() ? MESA_FORMAT_R8G8B8A8_UNORM
204                                           : MESA_FORMAT_A8B8G8R8_UNORM,
205                     rgbaRowStride, tempImageSlices,
206                     srcWidth, srcHeight, srcDepth,
207                     srcFormat, srcType, srcAddr,
208                     srcPacking);
209      pixels = tempImage;
210      srcFormat = GL_RGBA;
211   }
212   else {
213      pixels = _mesa_image_address2d(srcPacking, srcAddr, srcWidth, srcHeight,
214                                     srcFormat, srcType, 0, 0);
215   }
216
217   dst = dstSlices[0];
218
219   if (ext_tx_compress_dxtn) {
220      (*ext_tx_compress_dxtn)(4, srcWidth, srcHeight, pixels,
221                              GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
222                              dst, dstRowStride);
223   }
224   else {
225      _mesa_warning(ctx, "external dxt library not available: texstore_rgba_dxt1");
226   }
227
228   free((void*) tempImage);
229
230   return GL_TRUE;
231}
232
233
234/**
235 * Store user's image in rgba_dxt3 format.
236 */
237GLboolean
238_mesa_texstore_rgba_dxt3(TEXSTORE_PARAMS)
239{
240   const GLubyte *pixels;
241   GLubyte *dst;
242   const GLubyte *tempImage = NULL;
243
244   assert(dstFormat == MESA_FORMAT_RGBA_DXT3 ||
245          dstFormat == MESA_FORMAT_SRGBA_DXT3);
246
247   if (srcFormat != GL_RGBA ||
248       srcType != GL_UNSIGNED_BYTE ||
249       ctx->_ImageTransferState ||
250       ALIGN(srcPacking->RowLength, srcPacking->Alignment) != srcWidth ||
251       srcPacking->SwapBytes) {
252      /* convert image to RGBA/GLubyte */
253      GLubyte *tempImageSlices[1];
254      int rgbaRowStride = 4 * srcWidth * sizeof(GLubyte);
255      tempImage = malloc(srcWidth * srcHeight * 4 * sizeof(GLubyte));
256      if (!tempImage)
257         return GL_FALSE; /* out of memory */
258      tempImageSlices[0] = (GLubyte *) tempImage;
259      _mesa_texstore(ctx, dims,
260                     baseInternalFormat,
261                     _mesa_little_endian() ? MESA_FORMAT_R8G8B8A8_UNORM
262                                           : MESA_FORMAT_A8B8G8R8_UNORM,
263                     rgbaRowStride, tempImageSlices,
264                     srcWidth, srcHeight, srcDepth,
265                     srcFormat, srcType, srcAddr,
266                     srcPacking);
267      pixels = tempImage;
268   }
269   else {
270      pixels = _mesa_image_address2d(srcPacking, srcAddr, srcWidth, srcHeight,
271                                     srcFormat, srcType, 0, 0);
272   }
273
274   dst = dstSlices[0];
275
276   if (ext_tx_compress_dxtn) {
277      (*ext_tx_compress_dxtn)(4, srcWidth, srcHeight, pixels,
278                              GL_COMPRESSED_RGBA_S3TC_DXT3_EXT,
279                              dst, dstRowStride);
280   }
281   else {
282      _mesa_warning(ctx, "external dxt library not available: texstore_rgba_dxt3");
283   }
284
285   free((void *) tempImage);
286
287   return GL_TRUE;
288}
289
290
291/**
292 * Store user's image in rgba_dxt5 format.
293 */
294GLboolean
295_mesa_texstore_rgba_dxt5(TEXSTORE_PARAMS)
296{
297   const GLubyte *pixels;
298   GLubyte *dst;
299   const GLubyte *tempImage = NULL;
300
301   assert(dstFormat == MESA_FORMAT_RGBA_DXT5 ||
302          dstFormat == MESA_FORMAT_SRGBA_DXT5);
303
304   if (srcFormat != GL_RGBA ||
305       srcType != GL_UNSIGNED_BYTE ||
306       ctx->_ImageTransferState ||
307       ALIGN(srcPacking->RowLength, srcPacking->Alignment) != srcWidth ||
308       srcPacking->SwapBytes) {
309      /* convert image to RGBA/GLubyte */
310      GLubyte *tempImageSlices[1];
311      int rgbaRowStride = 4 * srcWidth * sizeof(GLubyte);
312      tempImage = malloc(srcWidth * srcHeight * 4 * sizeof(GLubyte));
313      if (!tempImage)
314         return GL_FALSE; /* out of memory */
315      tempImageSlices[0] = (GLubyte *) tempImage;
316      _mesa_texstore(ctx, dims,
317                     baseInternalFormat,
318                     _mesa_little_endian() ? MESA_FORMAT_R8G8B8A8_UNORM
319                                           : MESA_FORMAT_A8B8G8R8_UNORM,
320                     rgbaRowStride, tempImageSlices,
321                     srcWidth, srcHeight, srcDepth,
322                     srcFormat, srcType, srcAddr,
323                     srcPacking);
324      pixels = tempImage;
325   }
326   else {
327      pixels = _mesa_image_address2d(srcPacking, srcAddr, srcWidth, srcHeight,
328                                     srcFormat, srcType, 0, 0);
329   }
330
331   dst = dstSlices[0];
332
333   if (ext_tx_compress_dxtn) {
334      (*ext_tx_compress_dxtn)(4, srcWidth, srcHeight, pixels,
335                              GL_COMPRESSED_RGBA_S3TC_DXT5_EXT,
336                              dst, dstRowStride);
337   }
338   else {
339      _mesa_warning(ctx, "external dxt library not available: texstore_rgba_dxt5");
340   }
341
342   free((void *) tempImage);
343
344   return GL_TRUE;
345}
346
347
348/** Report problem with dxt texture decompression, once */
349static void
350problem(const char *func)
351{
352   static GLboolean warned = GL_FALSE;
353   if (!warned) {
354      _mesa_debug(NULL, "attempted to decode DXT texture without "
355                  "library available: %s\n", func);
356      warned = GL_TRUE;
357   }
358}
359
360
361static void
362fetch_rgb_dxt1(const GLubyte *map,
363               GLint rowStride, GLint i, GLint j, GLfloat *texel)
364{
365   if (fetch_ext_rgb_dxt1) {
366      GLubyte tex[4];
367      fetch_ext_rgb_dxt1(rowStride, map, i, j, tex);
368      texel[RCOMP] = UBYTE_TO_FLOAT(tex[RCOMP]);
369      texel[GCOMP] = UBYTE_TO_FLOAT(tex[GCOMP]);
370      texel[BCOMP] = UBYTE_TO_FLOAT(tex[BCOMP]);
371      texel[ACOMP] = UBYTE_TO_FLOAT(tex[ACOMP]);
372   }
373   else {
374      problem("rgb_dxt1");
375   }
376}
377
378static void
379fetch_rgba_dxt1(const GLubyte *map,
380                GLint rowStride, GLint i, GLint j, GLfloat *texel)
381{
382   if (fetch_ext_rgba_dxt1) {
383      GLubyte tex[4];
384      fetch_ext_rgba_dxt1(rowStride, map, i, j, tex);
385      texel[RCOMP] = UBYTE_TO_FLOAT(tex[RCOMP]);
386      texel[GCOMP] = UBYTE_TO_FLOAT(tex[GCOMP]);
387      texel[BCOMP] = UBYTE_TO_FLOAT(tex[BCOMP]);
388      texel[ACOMP] = UBYTE_TO_FLOAT(tex[ACOMP]);
389   }
390   else {
391      problem("rgba_dxt1");
392   }
393}
394
395static void
396fetch_rgba_dxt3(const GLubyte *map,
397                GLint rowStride, GLint i, GLint j, GLfloat *texel)
398{
399   if (fetch_ext_rgba_dxt3) {
400      GLubyte tex[4];
401      fetch_ext_rgba_dxt3(rowStride, map, i, j, tex);
402      texel[RCOMP] = UBYTE_TO_FLOAT(tex[RCOMP]);
403      texel[GCOMP] = UBYTE_TO_FLOAT(tex[GCOMP]);
404      texel[BCOMP] = UBYTE_TO_FLOAT(tex[BCOMP]);
405      texel[ACOMP] = UBYTE_TO_FLOAT(tex[ACOMP]);
406   }
407   else {
408      problem("rgba_dxt3");
409   }
410}
411
412static void
413fetch_rgba_dxt5(const GLubyte *map,
414                GLint rowStride, GLint i, GLint j, GLfloat *texel)
415{
416   if (fetch_ext_rgba_dxt5) {
417      GLubyte tex[4];
418      fetch_ext_rgba_dxt5(rowStride, map, i, j, tex);
419      texel[RCOMP] = UBYTE_TO_FLOAT(tex[RCOMP]);
420      texel[GCOMP] = UBYTE_TO_FLOAT(tex[GCOMP]);
421      texel[BCOMP] = UBYTE_TO_FLOAT(tex[BCOMP]);
422      texel[ACOMP] = UBYTE_TO_FLOAT(tex[ACOMP]);
423   }
424   else {
425      problem("rgba_dxt5");
426   }
427}
428
429
430static void
431fetch_srgb_dxt1(const GLubyte *map,
432                GLint rowStride, GLint i, GLint j, GLfloat *texel)
433{
434   if (fetch_ext_rgb_dxt1) {
435      GLubyte tex[4];
436      fetch_ext_rgb_dxt1(rowStride, map, i, j, tex);
437      texel[RCOMP] = util_format_srgb_8unorm_to_linear_float(tex[RCOMP]);
438      texel[GCOMP] = util_format_srgb_8unorm_to_linear_float(tex[GCOMP]);
439      texel[BCOMP] = util_format_srgb_8unorm_to_linear_float(tex[BCOMP]);
440      texel[ACOMP] = UBYTE_TO_FLOAT(tex[ACOMP]);
441   }
442   else {
443      problem("srgb_dxt1");
444   }
445}
446
447static void
448fetch_srgba_dxt1(const GLubyte *map,
449                 GLint rowStride, GLint i, GLint j, GLfloat *texel)
450{
451   if (fetch_ext_rgba_dxt1) {
452      GLubyte tex[4];
453      fetch_ext_rgba_dxt1(rowStride, map, i, j, tex);
454      texel[RCOMP] = util_format_srgb_8unorm_to_linear_float(tex[RCOMP]);
455      texel[GCOMP] = util_format_srgb_8unorm_to_linear_float(tex[GCOMP]);
456      texel[BCOMP] = util_format_srgb_8unorm_to_linear_float(tex[BCOMP]);
457      texel[ACOMP] = UBYTE_TO_FLOAT(tex[ACOMP]);
458   }
459   else {
460      problem("srgba_dxt1");
461   }
462}
463
464static void
465fetch_srgba_dxt3(const GLubyte *map,
466                 GLint rowStride, GLint i, GLint j, GLfloat *texel)
467{
468   if (fetch_ext_rgba_dxt3) {
469      GLubyte tex[4];
470      fetch_ext_rgba_dxt3(rowStride, map, i, j, tex);
471      texel[RCOMP] = util_format_srgb_8unorm_to_linear_float(tex[RCOMP]);
472      texel[GCOMP] = util_format_srgb_8unorm_to_linear_float(tex[GCOMP]);
473      texel[BCOMP] = util_format_srgb_8unorm_to_linear_float(tex[BCOMP]);
474      texel[ACOMP] = UBYTE_TO_FLOAT(tex[ACOMP]);
475   }
476   else {
477      problem("srgba_dxt3");
478   }
479}
480
481static void
482fetch_srgba_dxt5(const GLubyte *map,
483                 GLint rowStride, GLint i, GLint j, GLfloat *texel)
484{
485   if (fetch_ext_rgba_dxt5) {
486      GLubyte tex[4];
487      fetch_ext_rgba_dxt5(rowStride, map, i, j, tex);
488      texel[RCOMP] = util_format_srgb_8unorm_to_linear_float(tex[RCOMP]);
489      texel[GCOMP] = util_format_srgb_8unorm_to_linear_float(tex[GCOMP]);
490      texel[BCOMP] = util_format_srgb_8unorm_to_linear_float(tex[BCOMP]);
491      texel[ACOMP] = UBYTE_TO_FLOAT(tex[ACOMP]);
492   }
493   else {
494      problem("srgba_dxt5");
495   }
496}
497
498
499
500compressed_fetch_func
501_mesa_get_dxt_fetch_func(mesa_format format)
502{
503   switch (format) {
504   case MESA_FORMAT_RGB_DXT1:
505      return fetch_rgb_dxt1;
506   case MESA_FORMAT_RGBA_DXT1:
507      return fetch_rgba_dxt1;
508   case MESA_FORMAT_RGBA_DXT3:
509      return fetch_rgba_dxt3;
510   case MESA_FORMAT_RGBA_DXT5:
511      return fetch_rgba_dxt5;
512   case MESA_FORMAT_SRGB_DXT1:
513      return fetch_srgb_dxt1;
514   case MESA_FORMAT_SRGBA_DXT1:
515      return fetch_srgba_dxt1;
516   case MESA_FORMAT_SRGBA_DXT3:
517      return fetch_srgba_dxt3;
518   case MESA_FORMAT_SRGBA_DXT5:
519      return fetch_srgba_dxt5;
520   default:
521      return NULL;
522   }
523}
524