Napalm total immersion
[mesa.git] / src / mesa / drivers / glide / fxddtex.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 4.0
4 *
5 * Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
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 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25 /* Authors:
26 * David Bucciarelli
27 * Brian Paul
28 * Daryll Strauss
29 * Keith Whitwell
30 * Daniel Borca
31 * Hiroshi Morii
32 */
33
34
35 #ifdef HAVE_CONFIG_H
36 #include "conf.h"
37 #endif
38
39 #if defined(FX)
40
41 #include "fxdrv.h"
42 #include "enums.h"
43 #include "image.h"
44 #include "teximage.h"
45 #include "texformat.h"
46 #include "texstore.h"
47 #include "texutil.h"
48
49
50 void
51 fxPrintTextureData(tfxTexInfo * ti)
52 {
53 fprintf(stderr, "Texture Data:\n");
54 if (ti->tObj) {
55 fprintf(stderr, "\tName: %d\n", ti->tObj->Name);
56 fprintf(stderr, "\tBaseLevel: %d\n", ti->tObj->BaseLevel);
57 fprintf(stderr, "\tSize: %d x %d\n",
58 ti->tObj->Image[ti->tObj->BaseLevel]->Width,
59 ti->tObj->Image[ti->tObj->BaseLevel]->Height);
60 }
61 else
62 fprintf(stderr, "\tName: UNNAMED\n");
63 fprintf(stderr, "\tLast used: %d\n", ti->lastTimeUsed);
64 fprintf(stderr, "\tTMU: %ld\n", ti->whichTMU);
65 fprintf(stderr, "\t%s\n", (ti->isInTM) ? "In TMU" : "Not in TMU");
66 if (ti->tm[0])
67 fprintf(stderr, "\tMem0: %x-%x\n", (unsigned) ti->tm[0]->startAddr,
68 (unsigned) ti->tm[0]->endAddr);
69 if (ti->tm[1])
70 fprintf(stderr, "\tMem1: %x-%x\n", (unsigned) ti->tm[1]->startAddr,
71 (unsigned) ti->tm[1]->endAddr);
72 fprintf(stderr, "\tMipmaps: %d-%d\n", ti->minLevel, ti->maxLevel);
73 fprintf(stderr, "\tFilters: min %d min %d\n",
74 (int) ti->minFilt, (int) ti->maxFilt);
75 fprintf(stderr, "\tClamps: s %d t %d\n", (int) ti->sClamp,
76 (int) ti->tClamp);
77 fprintf(stderr, "\tScales: s %f t %f\n", ti->sScale, ti->tScale);
78 fprintf(stderr, "\t%s\n",
79 (ti->fixedPalette) ? "Fixed palette" : "Non fixed palette");
80 fprintf(stderr, "\t%s\n", (ti->validated) ? "Validated" : "Not validated");
81 }
82
83
84 /************************************************************************/
85 /*************************** Texture Mapping ****************************/
86 /************************************************************************/
87
88 static void
89 fxTexInvalidate(GLcontext * ctx, struct gl_texture_object *tObj)
90 {
91 fxMesaContext fxMesa = FX_CONTEXT(ctx);
92 tfxTexInfo *ti;
93
94 ti = fxTMGetTexInfo(tObj);
95 if (ti->isInTM)
96 fxTMMoveOutTM(fxMesa, tObj); /* TO DO: SLOW but easy to write */
97
98 ti->validated = GL_FALSE;
99 fxMesa->new_state |= FX_NEW_TEXTURING;
100 }
101
102 static tfxTexInfo *
103 fxAllocTexObjData(fxMesaContext fxMesa)
104 {
105 tfxTexInfo *ti;
106
107 if (!(ti = CALLOC(sizeof(tfxTexInfo)))) {
108 fprintf(stderr, "%s: ERROR: out of memory !\n", __FUNCTION__);
109 fxCloseHardware();
110 exit(-1);
111 }
112
113 ti->validated = GL_FALSE;
114 ti->isInTM = GL_FALSE;
115
116 ti->whichTMU = FX_TMU_NONE;
117
118 ti->tm[FX_TMU0] = NULL;
119 ti->tm[FX_TMU1] = NULL;
120
121 ti->minFilt = GR_TEXTUREFILTER_POINT_SAMPLED;
122 ti->maxFilt = GR_TEXTUREFILTER_BILINEAR;
123
124 ti->sClamp = GR_TEXTURECLAMP_WRAP;
125 ti->tClamp = GR_TEXTURECLAMP_WRAP;
126
127 ti->mmMode = GR_MIPMAP_NEAREST;
128 ti->LODblend = FXFALSE;
129
130 return ti;
131 }
132
133 void
134 fxDDTexBind(GLcontext * ctx, GLenum target, struct gl_texture_object *tObj)
135 {
136 fxMesaContext fxMesa = FX_CONTEXT(ctx);
137 tfxTexInfo *ti;
138
139 if (TDFX_DEBUG & VERBOSE_DRIVER) {
140 fprintf(stderr, "%s(%d, %x)\n", __FUNCTION__, tObj->Name,
141 (GLuint) tObj->DriverData);
142 }
143
144 if (target != GL_TEXTURE_2D)
145 return;
146
147 if (!tObj->DriverData) {
148 tObj->DriverData = fxAllocTexObjData(fxMesa);
149 }
150
151 ti = fxTMGetTexInfo(tObj);
152
153 fxMesa->texBindNumber++;
154 ti->lastTimeUsed = fxMesa->texBindNumber;
155
156 fxMesa->new_state |= FX_NEW_TEXTURING;
157 }
158
159 void
160 fxDDTexEnv(GLcontext * ctx, GLenum target, GLenum pname,
161 const GLfloat * param)
162 {
163 fxMesaContext fxMesa = FX_CONTEXT(ctx);
164
165 if (TDFX_DEBUG & VERBOSE_DRIVER) {
166 if (param)
167 fprintf(stderr, "%s(%x, %x)\n", __FUNCTION__, pname, (GLint) (*param));
168 else
169 fprintf(stderr, "%s(%x)\n", __FUNCTION__, pname);
170 }
171
172 /* apply any lod biasing right now */
173 if (pname == GL_TEXTURE_LOD_BIAS_EXT) {
174 grTexLodBiasValue(GR_TMU0, *param);
175
176 if (fxMesa->haveTwoTMUs) {
177 grTexLodBiasValue(GR_TMU1, *param);
178 }
179
180 }
181
182 fxMesa->new_state |= FX_NEW_TEXTURING;
183 }
184
185 void
186 fxDDTexParam(GLcontext * ctx, GLenum target, struct gl_texture_object *tObj,
187 GLenum pname, const GLfloat * params)
188 {
189 fxMesaContext fxMesa = FX_CONTEXT(ctx);
190 GLenum param = (GLenum) (GLint) params[0];
191 tfxTexInfo *ti;
192
193 if (TDFX_DEBUG & VERBOSE_DRIVER) {
194 fprintf(stderr, "fxDDTexParam(%d, %x, %s, %s)\n",
195 tObj->Name, (GLuint) tObj->DriverData,
196 _mesa_lookup_enum_by_nr(pname),
197 _mesa_lookup_enum_by_nr(param));
198 }
199
200 if (target != GL_TEXTURE_2D)
201 return;
202
203 if (!tObj->DriverData)
204 tObj->DriverData = fxAllocTexObjData(fxMesa);
205
206 ti = fxTMGetTexInfo(tObj);
207
208 switch (pname) {
209
210 case GL_TEXTURE_MIN_FILTER:
211 switch (param) {
212 case GL_NEAREST:
213 ti->mmMode = GR_MIPMAP_DISABLE;
214 ti->minFilt = GR_TEXTUREFILTER_POINT_SAMPLED;
215 ti->LODblend = FXFALSE;
216 break;
217 case GL_LINEAR:
218 ti->mmMode = GR_MIPMAP_DISABLE;
219 ti->minFilt = GR_TEXTUREFILTER_BILINEAR;
220 ti->LODblend = FXFALSE;
221 break;
222 case GL_NEAREST_MIPMAP_LINEAR:
223 if (fxMesa->haveTwoTMUs) {
224 ti->mmMode = GR_MIPMAP_NEAREST;
225 ti->LODblend = FXTRUE;
226 }
227 else {
228 ti->mmMode = GR_MIPMAP_NEAREST_DITHER;
229 ti->LODblend = FXFALSE;
230 }
231 ti->minFilt = GR_TEXTUREFILTER_POINT_SAMPLED;
232 break; /* ZZZ: we may have to fall through here for V3 */
233 case GL_NEAREST_MIPMAP_NEAREST:
234 ti->mmMode = GR_MIPMAP_NEAREST;
235 ti->minFilt = GR_TEXTUREFILTER_POINT_SAMPLED;
236 ti->LODblend = FXFALSE;
237 break;
238 case GL_LINEAR_MIPMAP_LINEAR:
239 /* ZZZ: HACK ALERT! trilinear is bugged! mipmap blending produce
240 incorrect filtered colors for the smallest mipmap levels. */
241 #if 0
242 if (fxMesa->haveTwoTMUs) {
243 ti->mmMode = GR_MIPMAP_NEAREST;
244 ti->LODblend = FXTRUE;
245 }
246 else {
247 ti->mmMode = GR_MIPMAP_NEAREST_DITHER;
248 ti->LODblend = FXFALSE;
249 }
250 ti->minFilt = GR_TEXTUREFILTER_BILINEAR;
251 break; /* ZZZ: we may have to fall through here for V3 */
252 #endif
253 case GL_LINEAR_MIPMAP_NEAREST:
254 ti->mmMode = GR_MIPMAP_NEAREST;
255 ti->minFilt = GR_TEXTUREFILTER_BILINEAR;
256 ti->LODblend = FXFALSE;
257 break;
258 default:
259 break;
260 }
261 fxTexInvalidate(ctx, tObj);
262 break;
263
264 case GL_TEXTURE_MAG_FILTER:
265 switch (param) {
266 case GL_NEAREST:
267 ti->maxFilt = GR_TEXTUREFILTER_POINT_SAMPLED;
268 break;
269 case GL_LINEAR:
270 ti->maxFilt = GR_TEXTUREFILTER_BILINEAR;
271 break;
272 default:
273 break;
274 }
275 fxTexInvalidate(ctx, tObj);
276 break;
277
278 case GL_TEXTURE_WRAP_S:
279 switch (param) {
280 case GL_MIRRORED_REPEAT:
281 ti->sClamp = GR_TEXTURECLAMP_MIRROR_EXT;
282 break;
283 case GL_CLAMP_TO_EDGE: /* CLAMP discarding border */
284 case GL_CLAMP:
285 ti->sClamp = GR_TEXTURECLAMP_CLAMP;
286 break;
287 case GL_REPEAT:
288 ti->sClamp = GR_TEXTURECLAMP_WRAP;
289 break;
290 default:
291 break;
292 }
293 fxMesa->new_state |= FX_NEW_TEXTURING;
294 break;
295
296 case GL_TEXTURE_WRAP_T:
297 switch (param) {
298 case GL_MIRRORED_REPEAT:
299 ti->tClamp = GR_TEXTURECLAMP_MIRROR_EXT;
300 break;
301 case GL_CLAMP_TO_EDGE: /* CLAMP discarding border */
302 case GL_CLAMP:
303 ti->tClamp = GR_TEXTURECLAMP_CLAMP;
304 break;
305 case GL_REPEAT:
306 ti->tClamp = GR_TEXTURECLAMP_WRAP;
307 break;
308 default:
309 break;
310 }
311 fxMesa->new_state |= FX_NEW_TEXTURING;
312 break;
313
314 case GL_TEXTURE_BORDER_COLOR:
315 /* TO DO */
316 break;
317
318 case GL_TEXTURE_MIN_LOD:
319 /* TO DO */
320 break;
321 case GL_TEXTURE_MAX_LOD:
322 /* TO DO */
323 break;
324 case GL_TEXTURE_BASE_LEVEL:
325 fxTexInvalidate(ctx, tObj);
326 break;
327 case GL_TEXTURE_MAX_LEVEL:
328 fxTexInvalidate(ctx, tObj);
329 break;
330
331 default:
332 break;
333 }
334 }
335
336 void
337 fxDDTexDel(GLcontext * ctx, struct gl_texture_object *tObj)
338 {
339 fxMesaContext fxMesa = FX_CONTEXT(ctx);
340 tfxTexInfo *ti = fxTMGetTexInfo(tObj);
341
342 if (TDFX_DEBUG & VERBOSE_DRIVER) {
343 fprintf(stderr, "%s(%d, %p)\n", __FUNCTION__, tObj->Name, (void *) ti);
344 }
345
346 if (!ti)
347 return;
348
349 fxTMFreeTexture(fxMesa, tObj);
350
351 FREE(ti);
352 tObj->DriverData = NULL;
353 }
354
355 /*
356 * Return true if texture is resident, false otherwise.
357 */
358 GLboolean
359 fxDDIsTextureResident(GLcontext *ctx, struct gl_texture_object *tObj)
360 {
361 tfxTexInfo *ti = fxTMGetTexInfo(tObj);
362 return (ti && ti->isInTM);
363 }
364
365
366
367 /*
368 * Convert a gl_color_table texture palette to Glide's format.
369 */
370 static void
371 convertPalette(FxU32 data[256], const struct gl_color_table *table)
372 {
373 const GLubyte *tableUB = (const GLubyte *) table->Table;
374 GLint width = table->Size;
375 FxU32 r, g, b, a;
376 GLint i;
377
378 ASSERT(!table->FloatTable);
379
380 switch (table->Format) {
381 case GL_INTENSITY:
382 for (i = 0; i < width; i++) {
383 r = tableUB[i];
384 g = tableUB[i];
385 b = tableUB[i];
386 a = tableUB[i];
387 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
388 }
389 break;
390 case GL_LUMINANCE:
391 for (i = 0; i < width; i++) {
392 r = tableUB[i];
393 g = tableUB[i];
394 b = tableUB[i];
395 a = 255;
396 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
397 }
398 break;
399 case GL_ALPHA:
400 for (i = 0; i < width; i++) {
401 r = g = b = 255;
402 a = tableUB[i];
403 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
404 }
405 break;
406 case GL_LUMINANCE_ALPHA:
407 for (i = 0; i < width; i++) {
408 r = g = b = tableUB[i * 2 + 0];
409 a = tableUB[i * 2 + 1];
410 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
411 }
412 break;
413 case GL_RGB:
414 for (i = 0; i < width; i++) {
415 r = tableUB[i * 3 + 0];
416 g = tableUB[i * 3 + 1];
417 b = tableUB[i * 3 + 2];
418 a = 255;
419 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
420 }
421 break;
422 case GL_RGBA:
423 for (i = 0; i < width; i++) {
424 r = tableUB[i * 4 + 0];
425 g = tableUB[i * 4 + 1];
426 b = tableUB[i * 4 + 2];
427 a = tableUB[i * 4 + 3];
428 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
429 }
430 break;
431 }
432 }
433
434
435 void
436 fxDDTexPalette(GLcontext * ctx, struct gl_texture_object *tObj)
437 {
438 fxMesaContext fxMesa = FX_CONTEXT(ctx);
439
440 if (tObj) {
441 /* per-texture palette */
442 tfxTexInfo *ti;
443 if (TDFX_DEBUG & VERBOSE_DRIVER) {
444 fprintf(stderr, "%s(%d, %x)\n", __FUNCTION__,
445 tObj->Name, (GLuint) tObj->DriverData);
446 }
447 if (!tObj->DriverData)
448 tObj->DriverData = fxAllocTexObjData(fxMesa);
449 ti = fxTMGetTexInfo(tObj);
450 convertPalette(ti->palette.data, &tObj->Palette);
451 fxTexInvalidate(ctx, tObj);
452 }
453 else {
454 /* global texture palette */
455 if (TDFX_DEBUG & VERBOSE_DRIVER) {
456 fprintf(stderr, "%s(global)\n", __FUNCTION__);
457 }
458 convertPalette(fxMesa->glbPalette.data, &ctx->Texture.Palette);
459 fxMesa->new_state |= FX_NEW_TEXTURING;
460 }
461 }
462
463
464 void
465 fxDDTexUseGlbPalette(GLcontext * ctx, GLboolean state)
466 {
467 fxMesaContext fxMesa = FX_CONTEXT(ctx);
468
469 if (TDFX_DEBUG & VERBOSE_DRIVER) {
470 fprintf(stderr, "%s(%d)\n", __FUNCTION__, state);
471 }
472
473 if (state) {
474 fxMesa->haveGlobalPaletteTexture = 1;
475
476 grTexDownloadTable(GR_TEXTABLE_PALETTE, &(fxMesa->glbPalette));
477 }
478 else {
479 fxMesa->haveGlobalPaletteTexture = 0;
480
481 if ((ctx->Texture.Unit[0]._Current == ctx->Texture.Unit[0].Current2D) &&
482 (ctx->Texture.Unit[0]._Current != NULL)) {
483 struct gl_texture_object *tObj = ctx->Texture.Unit[0]._Current;
484
485 if (!tObj->DriverData)
486 tObj->DriverData = fxAllocTexObjData(fxMesa);
487
488 fxTexInvalidate(ctx, tObj);
489 }
490 }
491 }
492
493
494 static int
495 logbase2(int n)
496 {
497 GLint i = 1;
498 GLint log2 = 0;
499
500 if (n < 0) {
501 return -1;
502 }
503
504 while (n > i) {
505 i *= 2;
506 log2++;
507 }
508 if (i != n) {
509 return -1;
510 }
511 else {
512 return log2;
513 }
514 }
515
516
517 int
518 fxTexGetInfo(int w, int h, GrLOD_t * lodlevel, GrAspectRatio_t * ar,
519 float *sscale, float *tscale,
520 int *wscale, int *hscale)
521 {
522 static GrLOD_t lod[12] = {
523 GR_LOD_LOG2_1,
524 GR_LOD_LOG2_2,
525 GR_LOD_LOG2_4,
526 GR_LOD_LOG2_8,
527 GR_LOD_LOG2_16,
528 GR_LOD_LOG2_32,
529 GR_LOD_LOG2_64,
530 GR_LOD_LOG2_128,
531 GR_LOD_LOG2_256,
532 GR_LOD_LOG2_512,
533 GR_LOD_LOG2_1024,
534 GR_LOD_LOG2_2048
535 };
536
537 int logw, logh, ws, hs;
538 GrLOD_t l;
539 GrAspectRatio_t aspectratio;
540 float s, t;
541
542 logw = logbase2(w);
543 logh = logbase2(h);
544
545 switch (logw - logh) {
546 case 0:
547 aspectratio = GR_ASPECT_LOG2_1x1;
548 l = lod[logw];
549 s = t = 256.0f;
550 ws = hs = 1;
551 break;
552 case 1:
553 aspectratio = GR_ASPECT_LOG2_2x1;
554 l = lod[logw];
555 s = 256.0f;
556 t = 128.0f;
557 ws = 1;
558 hs = 1;
559 break;
560 case 2:
561 aspectratio = GR_ASPECT_LOG2_4x1;
562 l = lod[logw];
563 s = 256.0f;
564 t = 64.0f;
565 ws = 1;
566 hs = 1;
567 break;
568 case 3:
569 aspectratio = GR_ASPECT_LOG2_8x1;
570 l = lod[logw];
571 s = 256.0f;
572 t = 32.0f;
573 ws = 1;
574 hs = 1;
575 break;
576 case -1:
577 aspectratio = GR_ASPECT_LOG2_1x2;
578 l = lod[logh];
579 s = 128.0f;
580 t = 256.0f;
581 ws = 1;
582 hs = 1;
583 break;
584 case -2:
585 aspectratio = GR_ASPECT_LOG2_1x4;
586 l = lod[logh];
587 s = 64.0f;
588 t = 256.0f;
589 ws = 1;
590 hs = 1;
591 break;
592 case -3:
593 aspectratio = GR_ASPECT_LOG2_1x8;
594 l = lod[logh];
595 s = 32.0f;
596 t = 256.0f;
597 ws = 1;
598 hs = 1;
599 break;
600 default:
601 if ((logw - logh) > 3) {
602 aspectratio = GR_ASPECT_LOG2_8x1;
603 l = lod[logw];
604 s = 256.0f;
605 t = 32.0f;
606 ws = 1;
607 hs = 1 << (logw - logh - 3);
608 } else /*if ((logw - logh) < -3)*/ {
609 aspectratio = GR_ASPECT_LOG2_1x8;
610 l = lod[logh];
611 s = 32.0f;
612 t = 256.0f;
613 ws = 1 << (logh - logw - 3);
614 hs = 1;
615 }
616 }
617
618 if (lodlevel)
619 (*lodlevel) = l;
620
621 if (ar)
622 (*ar) = aspectratio;
623
624 if (sscale)
625 (*sscale) = s;
626
627 if (tscale)
628 (*tscale) = t;
629
630 if (wscale)
631 (*wscale) = ws;
632
633 if (hscale)
634 (*hscale) = hs;
635
636
637 return 1;
638 }
639
640 static GLboolean
641 fxIsTexSupported(GLenum target, GLint internalFormat,
642 const struct gl_texture_image *image)
643 {
644 if (target != GL_TEXTURE_2D)
645 return GL_FALSE;
646
647 if (!fxTexGetInfo(image->Width, image->Height, NULL, NULL, NULL, NULL, NULL, NULL))
648 return GL_FALSE;
649
650 if (image->Border > 0)
651 return GL_FALSE;
652
653 return GL_TRUE;
654 }
655
656
657 /**********************************************************************/
658 /**** NEW TEXTURE IMAGE FUNCTIONS ****/
659 /**********************************************************************/
660
661 /* Texel-fetch functions for software texturing and glGetTexImage().
662 * We should have been able to use some "standard" fetch functions (which
663 * may get defined in texutil.c) but we have to account for scaled texture
664 * images on tdfx hardware (the 8:1 aspect ratio limit).
665 * Hence, we need special functions here.
666 */
667
668 static void
669 fetch_intensity8(const struct gl_texture_image *texImage,
670 GLint i, GLint j, GLint k, GLvoid * texelOut)
671 {
672 GLchan *rgba = (GLchan *) texelOut;
673 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
674 const GLubyte *texel;
675
676 i = i * mml->wScale;
677 j = j * mml->hScale;
678
679 texel = ((GLubyte *) texImage->Data) + j * mml->width + i;
680 rgba[RCOMP] = *texel;
681 rgba[GCOMP] = *texel;
682 rgba[BCOMP] = *texel;
683 rgba[ACOMP] = *texel;
684 }
685
686
687 static void
688 fetch_luminance8(const struct gl_texture_image *texImage,
689 GLint i, GLint j, GLint k, GLvoid * texelOut)
690 {
691 GLchan *rgba = (GLchan *) texelOut;
692 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
693 const GLubyte *texel;
694
695 i = i * mml->wScale;
696 j = j * mml->hScale;
697
698 texel = ((GLubyte *) texImage->Data) + j * mml->width + i;
699 rgba[RCOMP] = *texel;
700 rgba[GCOMP] = *texel;
701 rgba[BCOMP] = *texel;
702 rgba[ACOMP] = 255;
703 }
704
705
706 static void
707 fetch_alpha8(const struct gl_texture_image *texImage,
708 GLint i, GLint j, GLint k, GLvoid * texelOut)
709 {
710 GLchan *rgba = (GLchan *) texelOut;
711 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
712 const GLubyte *texel;
713
714 i = i * mml->wScale;
715 j = j * mml->hScale;
716 /* [dBorca] why, oh... why? */
717 i = i * mml->width / texImage->Width;
718 j = j * mml->height / texImage->Height;
719
720 texel = ((GLubyte *) texImage->Data) + j * mml->width + i;
721 rgba[RCOMP] = 255;
722 rgba[GCOMP] = 255;
723 rgba[BCOMP] = 255;
724 rgba[ACOMP] = *texel;
725 }
726
727
728 static void
729 fetch_index8(const struct gl_texture_image *texImage,
730 GLint i, GLint j, GLint k, GLvoid * texelOut)
731 {
732 GLchan *indexOut = (GLchan *) texelOut;
733 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
734 const GLubyte *texel;
735
736 i = i * mml->wScale;
737 j = j * mml->hScale;
738 /* [dBorca] why, oh... why? */
739 i = i * mml->width / texImage->Width;
740 j = j * mml->height / texImage->Height;
741
742 texel = ((GLubyte *) texImage->Data) + j * mml->width + i;
743 *indexOut = *texel;
744 }
745
746
747 static void
748 fetch_luminance8_alpha8(const struct gl_texture_image *texImage,
749 GLint i, GLint j, GLint k, GLvoid * texelOut)
750 {
751 GLchan *rgba = (GLchan *) texelOut;
752 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
753 const GLubyte *texel;
754
755 i = i * mml->wScale;
756 j = j * mml->hScale;
757
758 texel = ((GLubyte *) texImage->Data) + (j * mml->width + i) * 2;
759 rgba[RCOMP] = texel[0];
760 rgba[GCOMP] = texel[0];
761 rgba[BCOMP] = texel[0];
762 rgba[ACOMP] = texel[1];
763 }
764
765
766 static void
767 fetch_r5g6b5(const struct gl_texture_image *texImage,
768 GLint i, GLint j, GLint k, GLvoid * texelOut)
769 {
770 GLchan *rgba = (GLchan *) texelOut;
771 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
772 const GLushort *texel;
773
774 i = i * mml->wScale;
775 j = j * mml->hScale;
776
777 texel = ((GLushort *) texImage->Data) + j * mml->width + i;
778 rgba[RCOMP] = FX_rgb_scale_5[(*texel >> 11) & 0x1F];
779 rgba[GCOMP] = FX_rgb_scale_6[(*texel >> 5) & 0x3F];
780 rgba[BCOMP] = FX_rgb_scale_5[ *texel & 0x1F];
781 rgba[ACOMP] = 255;
782 }
783
784
785 static void
786 fetch_r4g4b4a4(const struct gl_texture_image *texImage,
787 GLint i, GLint j, GLint k, GLvoid * texelOut)
788 {
789 GLchan *rgba = (GLchan *) texelOut;
790 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
791 const GLushort *texel;
792
793 i = i * mml->wScale;
794 j = j * mml->hScale;
795
796 texel = ((GLushort *) texImage->Data) + j * mml->width + i;
797 rgba[RCOMP] = FX_rgb_scale_4[(*texel >> 12) & 0xF];
798 rgba[GCOMP] = FX_rgb_scale_4[(*texel >> 8) & 0xF];
799 rgba[BCOMP] = FX_rgb_scale_4[(*texel >> 4) & 0xF];
800 rgba[ACOMP] = FX_rgb_scale_4[ *texel & 0xF];
801 }
802
803
804 static void
805 fetch_r5g5b5a1(const struct gl_texture_image *texImage,
806 GLint i, GLint j, GLint k, GLvoid * texelOut)
807 {
808 GLchan *rgba = (GLchan *) texelOut;
809 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
810 const GLushort *texel;
811
812 i = i * mml->wScale;
813 j = j * mml->hScale;
814
815 texel = ((GLushort *) texImage->Data) + j * mml->width + i;
816 rgba[RCOMP] = FX_rgb_scale_5[(*texel >> 11) & 0x1F];
817 rgba[GCOMP] = FX_rgb_scale_5[(*texel >> 6) & 0x1F];
818 rgba[BCOMP] = FX_rgb_scale_5[(*texel >> 1) & 0x1F];
819 rgba[ACOMP] = ((*texel) & 0x01) * 255;
820 }
821
822
823 static void
824 fetch_a8r8g8b8(const struct gl_texture_image *texImage,
825 GLint i, GLint j, GLint k, GLvoid * texelOut)
826 {
827 GLchan *rgba = (GLchan *) texelOut;
828 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
829 const GLuint *texel;
830
831 i = i * mml->wScale;
832 j = j * mml->hScale;
833
834 texel = ((GLuint *) texImage->Data) + j * mml->width + i;
835 rgba[RCOMP] = (((*texel) >> 16) & 0xff);
836 rgba[GCOMP] = (((*texel) >> 8) & 0xff);
837 rgba[BCOMP] = (((*texel) ) & 0xff);
838 rgba[ACOMP] = (((*texel) >> 24) & 0xff);
839 }
840
841
842 static void
843 PrintTexture(int w, int h, int c, const GLubyte * data)
844 {
845 int i, j;
846 for (i = 0; i < h; i++) {
847 for (j = 0; j < w; j++) {
848 if (c == 2)
849 fprintf(stderr, "%02x %02x ", data[0], data[1]);
850 else if (c == 3)
851 fprintf(stderr, "%02x %02x %02x ", data[0], data[1], data[2]);
852 data += c;
853 }
854 fprintf(stderr, "\n");
855 }
856 }
857
858
859 const struct gl_texture_format *
860 fxDDChooseTextureFormat( GLcontext *ctx, GLint internalFormat,
861 GLenum srcFormat, GLenum srcType )
862 {
863 fxMesaContext fxMesa = FX_CONTEXT(ctx);
864 GLboolean allow32bpt = fxMesa->HaveTexFmt;
865
866 /* [dBorca] Hack alert:
867 * There is something wrong with this!!! Take an example:
868 * 1) start HW rendering
869 * 2) create a texture like this:
870 * glTexImage2D(GL_TEXTURE_2D, 0, 3, 16, 16, 0,
871 * GL_RGB, GL_UNSIGNED_BYTE, floorTexture);
872 * glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
873 * 3) we get here with internalFormat==3 and return either
874 * _mesa_texformat_argb565 or _mesa_texformat_argb8888
875 * 4) at some point, we encounter total rasterization fallback
876 * 5) displaying a polygon with the above textures yield garbage on areas
877 * where pixel is larger than a texel, because our already set texel
878 * function doesn't match the real _mesa_texformat_argb888
879 */
880
881 if (TDFX_DEBUG & VERBOSE_TEXTURE) {
882 fprintf(stderr, "fxDDChooseTextureFormat(...)\n");
883 }
884
885 switch (internalFormat) {
886 case GL_INTENSITY:
887 case GL_INTENSITY4:
888 case GL_INTENSITY8:
889 case GL_INTENSITY12:
890 case GL_INTENSITY16:
891 case GL_COMPRESSED_INTENSITY:
892 return &_mesa_texformat_i8;
893 case 1:
894 case GL_LUMINANCE:
895 case GL_LUMINANCE4:
896 case GL_LUMINANCE8:
897 case GL_LUMINANCE12:
898 case GL_LUMINANCE16:
899 case GL_COMPRESSED_LUMINANCE:
900 return &_mesa_texformat_l8;
901 case GL_ALPHA:
902 case GL_ALPHA4:
903 case GL_ALPHA8:
904 case GL_ALPHA12:
905 case GL_ALPHA16:
906 case GL_COMPRESSED_ALPHA:
907 return &_mesa_texformat_a8;
908 case GL_COLOR_INDEX:
909 case GL_COLOR_INDEX1_EXT:
910 case GL_COLOR_INDEX2_EXT:
911 case GL_COLOR_INDEX4_EXT:
912 case GL_COLOR_INDEX8_EXT:
913 case GL_COLOR_INDEX12_EXT:
914 case GL_COLOR_INDEX16_EXT:
915 return &_mesa_texformat_ci8;
916 case 2:
917 case GL_LUMINANCE_ALPHA:
918 case GL_LUMINANCE4_ALPHA4:
919 case GL_LUMINANCE6_ALPHA2:
920 case GL_LUMINANCE8_ALPHA8:
921 case GL_LUMINANCE12_ALPHA4:
922 case GL_LUMINANCE12_ALPHA12:
923 case GL_LUMINANCE16_ALPHA16:
924 case GL_COMPRESSED_LUMINANCE_ALPHA:
925 return &_mesa_texformat_al88;
926 case GL_R3_G3_B2:
927 case GL_RGB4:
928 case GL_RGB5:
929 return &_mesa_texformat_rgb565;
930 case 3:
931 case GL_RGB:
932 case GL_COMPRESSED_RGB:
933 if ( srcFormat == GL_RGB && srcType == GL_UNSIGNED_SHORT_5_6_5 ) {
934 return &_mesa_texformat_rgb565;
935 }
936 /* intentional fall through */
937 case GL_RGB8:
938 case GL_RGB10:
939 case GL_RGB12:
940 case GL_RGB16:
941 return (allow32bpt) ? &_mesa_texformat_argb8888
942 : &_mesa_texformat_rgb565;
943 case GL_RGBA2:
944 case GL_RGBA4:
945 return &_mesa_texformat_argb4444;
946 case 4:
947 case GL_RGBA:
948 case GL_COMPRESSED_RGBA:
949 if ( srcFormat == GL_BGRA ) {
950 if ( srcType == GL_UNSIGNED_INT_8_8_8_8_REV ) {
951 return &_mesa_texformat_argb8888;
952 }
953 else if ( srcType == GL_UNSIGNED_SHORT_4_4_4_4_REV ) {
954 return &_mesa_texformat_argb4444;
955 }
956 else if ( srcType == GL_UNSIGNED_SHORT_1_5_5_5_REV ) {
957 return &_mesa_texformat_argb1555;
958 }
959 }
960 /* intentional fall through */
961 case GL_RGBA8:
962 case GL_RGB10_A2:
963 case GL_RGBA12:
964 case GL_RGBA16:
965 return (allow32bpt) ? &_mesa_texformat_argb8888
966 : &_mesa_texformat_argb4444;
967 case GL_RGB5_A1:
968 return &_mesa_texformat_argb1555;
969 #if 0
970 /* GL_EXT_texture_compression_s3tc */
971 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
972 return &_mesa_texformat_rgb_dxt1;
973 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
974 return &_mesa_texformat_rgba_dxt1;
975 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
976 return &_mesa_texformat_rgba_dxt3;
977 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
978 return &_mesa_texformat_rgba_dxt5;
979 /*case GL_COMPRESSED_RGB_FXT1_3DFX:
980 case GL_COMPRESSED_RGBA_FXT1_3DFX:
981 return blah;*/
982 #endif
983 default:
984 _mesa_problem(NULL, "unexpected format in fxDDChooseTextureFormat");
985 return NULL;
986 }
987 }
988
989
990 static GrTextureFormat_t
991 fxGlideFormat(GLint mesaFormat)
992 {
993 switch (mesaFormat) {
994 case MESA_FORMAT_I8:
995 return GR_TEXFMT_ALPHA_8;
996 case MESA_FORMAT_A8:
997 return GR_TEXFMT_ALPHA_8;
998 case MESA_FORMAT_L8:
999 return GR_TEXFMT_INTENSITY_8;
1000 case MESA_FORMAT_CI8:
1001 return GR_TEXFMT_P_8;
1002 case MESA_FORMAT_AL88:
1003 return GR_TEXFMT_ALPHA_INTENSITY_88;
1004 case MESA_FORMAT_RGB565:
1005 return GR_TEXFMT_RGB_565;
1006 case MESA_FORMAT_ARGB4444:
1007 return GR_TEXFMT_ARGB_4444;
1008 case MESA_FORMAT_ARGB1555:
1009 return GR_TEXFMT_ARGB_1555;
1010 case MESA_FORMAT_ARGB8888:
1011 return GR_TEXFMT_ARGB_8888;
1012 #if 0
1013 case MESA_FORMAT_RGB_DXT1:
1014 case MESA_FORMAT_RGBA_DXT1:
1015 return GR_TEXFMT_ARGB_CMP_DXT1;
1016 case MESA_FORMAT_RGBA_DXT3:
1017 return GR_TEXFMT_ARGB_CMP_DXT3;
1018 case MESA_FORMAT_RGBA_DXT5:
1019 return GR_TEXFMT_ARGB_CMP_DXT5;
1020 /*case MESA_FORMAT_ARGB_CMP_FXT1:
1021 return GR_TEXFMT_ARGB_CMP_FXT1;
1022 case MESA_FORMAT_RGB_CMP_FXT1:
1023 return GL_COMPRESSED_RGBA_FXT1_3DFX;*/
1024 #endif
1025 default:
1026 _mesa_problem(NULL, "Unexpected format in fxGlideFormat");
1027 return 0;
1028 }
1029 }
1030
1031
1032 static FetchTexelFunc
1033 fxFetchFunction(GLint mesaFormat)
1034 {
1035 switch (mesaFormat) {
1036 case MESA_FORMAT_I8:
1037 return fetch_intensity8;
1038 case MESA_FORMAT_A8:
1039 return fetch_alpha8;
1040 case MESA_FORMAT_L8:
1041 return fetch_luminance8;
1042 case MESA_FORMAT_CI8:
1043 return fetch_index8;
1044 case MESA_FORMAT_AL88:
1045 return fetch_luminance8_alpha8;
1046 case MESA_FORMAT_RGB565:
1047 return fetch_r5g6b5;
1048 case MESA_FORMAT_ARGB4444:
1049 return fetch_r4g4b4a4;
1050 case MESA_FORMAT_ARGB1555:
1051 return fetch_r5g5b5a1;
1052 case MESA_FORMAT_ARGB8888:
1053 return fetch_a8r8g8b8;
1054 #if 0
1055 case MESA_FORMAT_RGB_DXT1:
1056 case MESA_FORMAT_RGBA_DXT1:
1057 case MESA_FORMAT_RGBA_DXT3:
1058 case MESA_FORMAT_RGBA_DXT5:
1059 return fetch_r4g4b4a4;
1060 #endif
1061 default:
1062 _mesa_problem(NULL, "Unexpected format in fxFetchFunction");
1063 return NULL;
1064 }
1065 }
1066
1067 void
1068 fxDDTexImage2D(GLcontext * ctx, GLenum target, GLint level,
1069 GLint internalFormat, GLint width, GLint height, GLint border,
1070 GLenum format, GLenum type, const GLvoid * pixels,
1071 const struct gl_pixelstore_attrib *packing,
1072 struct gl_texture_object *texObj,
1073 struct gl_texture_image *texImage)
1074 {
1075 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1076 tfxTexInfo *ti;
1077 tfxMipMapLevel *mml;
1078 GLint texelBytes;
1079
1080 if (TDFX_DEBUG & VERBOSE_TEXTURE) {
1081 fprintf(stderr, "fxDDTexImage2D: id=%d int 0x%x format 0x%x type 0x%x %dx%d\n",
1082 texObj->Name, texImage->IntFormat, format, type,
1083 texImage->Width, texImage->Height);
1084 }
1085
1086 if (!fxIsTexSupported(target, internalFormat, texImage)) {
1087 _mesa_problem(NULL, "fx Driver: unsupported texture in fxDDTexImg()\n");
1088 return;
1089 }
1090
1091 if (!texObj->DriverData) {
1092 texObj->DriverData = fxAllocTexObjData(fxMesa);
1093 if (!texObj->DriverData) {
1094 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D");
1095 return;
1096 }
1097 }
1098 ti = fxTMGetTexInfo(texObj);
1099
1100 if (!texImage->DriverData) {
1101 texImage->DriverData = CALLOC(sizeof(tfxMipMapLevel));
1102 if (!texImage->DriverData) {
1103 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D");
1104 return;
1105 }
1106 }
1107 mml = FX_MIPMAP_DATA(texImage);
1108
1109 fxTexGetInfo(width, height, NULL, NULL, NULL, NULL,
1110 &mml->wScale, &mml->hScale);
1111
1112 mml->width = width * mml->wScale;
1113 mml->height = height * mml->hScale;
1114
1115
1116 /* choose the texture format */
1117 assert(ctx->Driver.ChooseTextureFormat);
1118 texImage->TexFormat = (*ctx->Driver.ChooseTextureFormat)(ctx,
1119 internalFormat, format, type);
1120 assert(texImage->TexFormat);
1121 texelBytes = texImage->TexFormat->TexelBytes;
1122 /*if (!fxMesa->HaveTexFmt) assert(texelBytes == 1 || texelBytes == 2);*/
1123
1124 if (mml->wScale != 1 || mml->hScale != 1) {
1125 /* rescale image to overcome 1:8 aspect limitation */
1126 GLvoid *tempImage;
1127 tempImage = MALLOC(width * height * texelBytes);
1128 if (!tempImage) {
1129 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D");
1130 return;
1131 }
1132 /* unpack image, apply transfer ops and store in tempImage */
1133 _mesa_transfer_teximage(ctx, 2, texImage->Format,
1134 texImage->TexFormat,
1135 tempImage,
1136 width, height, 1, 0, 0, 0,
1137 width * texelBytes,
1138 0, /* dstImageStride */
1139 format, type, pixels, packing);
1140 assert(!texImage->Data);
1141 texImage->Data = MESA_PBUFFER_ALLOC(mml->width * mml->height * texelBytes);
1142 if (!texImage->Data) {
1143 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D");
1144 FREE(tempImage);
1145 return;
1146 }
1147 _mesa_rescale_teximage2d(texelBytes,
1148 mml->width * texelBytes, /* dst stride */
1149 width, height,
1150 mml->width, mml->height,
1151 tempImage /*src*/, texImage->Data /*dst*/ );
1152 FREE(tempImage);
1153 }
1154 else {
1155 /* no rescaling needed */
1156 assert(!texImage->Data);
1157 texImage->Data = MESA_PBUFFER_ALLOC(mml->width * mml->height * texelBytes);
1158 if (!texImage->Data) {
1159 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D");
1160 return;
1161 }
1162 /* unpack image, apply transfer ops and store in texImage->Data */
1163 _mesa_transfer_teximage(ctx, 2, texImage->Format,
1164 texImage->TexFormat, texImage->Data,
1165 width, height, 1, 0, 0, 0,
1166 texImage->Width * texelBytes,
1167 0, /* dstImageStride */
1168 format, type, pixels, packing);
1169 }
1170
1171 mml->glideFormat = fxGlideFormat(texImage->TexFormat->MesaFormat);
1172 ti->info.format = mml->glideFormat;
1173 texImage->FetchTexel = fxFetchFunction(texImage->TexFormat->MesaFormat);
1174
1175 /* [dBorca]
1176 * Hack alert: unsure...
1177 */
1178 if (!(fxMesa->new_state & FX_NEW_TEXTURING) && ti->validated && ti->isInTM) {
1179 /*fprintf(stderr, "reloadmipmaplevels\n"); */
1180 fxTMReloadMipMapLevel(fxMesa, texObj, level);
1181 }
1182 else {
1183 /*fprintf(stderr, "invalidate2\n"); */
1184 fxTexInvalidate(ctx, texObj);
1185 }
1186 }
1187
1188
1189 void
1190 fxDDTexSubImage2D(GLcontext * ctx, GLenum target, GLint level,
1191 GLint xoffset, GLint yoffset,
1192 GLsizei width, GLsizei height,
1193 GLenum format, GLenum type, const GLvoid * pixels,
1194 const struct gl_pixelstore_attrib *packing,
1195 struct gl_texture_object *texObj,
1196 struct gl_texture_image *texImage)
1197 {
1198 fxMesaContext fxMesa = FX_CONTEXT(ctx);
1199 tfxTexInfo *ti;
1200 tfxMipMapLevel *mml;
1201 GLint texelBytes;
1202
1203 if (TDFX_DEBUG & VERBOSE_TEXTURE) {
1204 fprintf(stderr, "fxDDTexSubImage2D: id=%d\n", texObj->Name);
1205 }
1206
1207 if (!texObj->DriverData) {
1208 _mesa_problem(ctx, "problem in fxDDTexSubImage2D");
1209 return;
1210 }
1211
1212 ti = fxTMGetTexInfo(texObj);
1213 assert(ti);
1214 mml = FX_MIPMAP_DATA(texImage);
1215 assert(mml);
1216
1217 assert(texImage->Data); /* must have an existing texture image! */
1218 assert(texImage->Format);
1219
1220 texelBytes = texImage->TexFormat->TexelBytes;
1221
1222 if (mml->wScale != 1 || mml->hScale != 1) {
1223 /* need to rescale subimage to match mipmap level's rescale factors */
1224 const GLint newWidth = width * mml->wScale;
1225 const GLint newHeight = height * mml->hScale;
1226 GLvoid *tempImage;
1227 GLubyte *destAddr;
1228 tempImage = MALLOC(width * height * texelBytes);
1229 if (!tempImage) {
1230 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexSubImage2D");
1231 return;
1232 }
1233
1234 _mesa_transfer_teximage(ctx, 2, texImage->Format,/* Tex int format */
1235 texImage->TexFormat, /* dest format */
1236 (GLubyte *) tempImage, /* dest */
1237 width, height, 1, /* subimage size */
1238 0, 0, 0, /* subimage pos */
1239 width * texelBytes, /* dest row stride */
1240 0, /* dst image stride */
1241 format, type, pixels, packing);
1242
1243 /* now rescale */
1244 /* compute address of dest subimage within the overal tex image */
1245 destAddr = (GLubyte *) texImage->Data
1246 + (yoffset * mml->hScale * mml->width
1247 + xoffset * mml->wScale) * texelBytes;
1248
1249 _mesa_rescale_teximage2d(texelBytes,
1250 mml->width * texelBytes, /* dst stride */
1251 width, height,
1252 newWidth, newHeight,
1253 tempImage, destAddr);
1254
1255 FREE(tempImage);
1256 }
1257 else {
1258 /* no rescaling needed */
1259 _mesa_transfer_teximage(ctx, 2, texImage->Format, /* Tex int format */
1260 texImage->TexFormat, /* dest format */
1261 (GLubyte *) texImage->Data,/* dest */
1262 width, height, 1, /* subimage size */
1263 xoffset, yoffset, 0, /* subimage pos */
1264 mml->width * texelBytes, /* dest row stride */
1265 0, /* dst image stride */
1266 format, type, pixels, packing);
1267 }
1268
1269 /* [dBorca]
1270 * Hack alert: unsure...
1271 */
1272 if (!(fxMesa->new_state & FX_NEW_TEXTURING) && ti->validated && ti->isInTM)
1273 fxTMReloadMipMapLevel(fxMesa, texObj, level);
1274 else
1275 fxTexInvalidate(ctx, texObj);
1276 }
1277
1278
1279 #else /* FX */
1280
1281 /*
1282 * Need this to provide at least one external definition.
1283 */
1284
1285 extern int gl_fx_dummy_function_ddtex(void);
1286 int
1287 gl_fx_dummy_function_ddtex(void)
1288 {
1289 return 0;
1290 }
1291
1292 #endif /* FX */