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