texture image code updates (not finished)
[mesa.git] / src / mesa / drivers / glide / fxddtex.c
1
2 /*
3 * Mesa 3-D graphics library
4 * Version: 3.5
5 *
6 * Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 *
26 * Original Mesa / 3Dfx device driver (C) 1999 David Bucciarelli, by the
27 * terms stated above.
28 *
29 * Thank you for your contribution, David!
30 *
31 * Please make note of the above copyright/license statement. If you
32 * contributed code or bug fixes to this code under the previous (GNU
33 * Library) license and object to the new license, your code will be
34 * removed at your request. Please see the Mesa docs/COPYRIGHT file
35 * for more information.
36 *
37 * Additional Mesa/3Dfx driver developers:
38 * Daryll Strauss <daryll@precisioninsight.com>
39 * Keith Whitwell <keith@precisioninsight.com>
40 *
41 * See fxapi.h for more revision/author details.
42 */
43
44
45 #ifdef HAVE_CONFIG_H
46 #include "conf.h"
47 #endif
48
49 #if defined(FX)
50
51 #include "fxdrv.h"
52 #include "image.h"
53 #include "teximage.h"
54 #include "texformat.h"
55 #include "texstore.h"
56 #include "texutil.h"
57
58
59 void
60 fxPrintTextureData(tfxTexInfo * ti)
61 {
62 fprintf(stderr, "Texture Data:\n");
63 if (ti->tObj) {
64 fprintf(stderr, "\tName: %d\n", ti->tObj->Name);
65 fprintf(stderr, "\tBaseLevel: %d\n", ti->tObj->BaseLevel);
66 fprintf(stderr, "\tSize: %d x %d\n",
67 ti->tObj->Image[ti->tObj->BaseLevel]->Width,
68 ti->tObj->Image[ti->tObj->BaseLevel]->Height);
69 }
70 else
71 fprintf(stderr, "\tName: UNNAMED\n");
72 fprintf(stderr, "\tLast used: %d\n", ti->lastTimeUsed);
73 fprintf(stderr, "\tTMU: %ld\n", ti->whichTMU);
74 fprintf(stderr, "\t%s\n", (ti->isInTM) ? "In TMU" : "Not in TMU");
75 if (ti->tm[0])
76 fprintf(stderr, "\tMem0: %x-%x\n", (unsigned) ti->tm[0]->startAddr,
77 (unsigned) ti->tm[0]->endAddr);
78 if (ti->tm[1])
79 fprintf(stderr, "\tMem1: %x-%x\n", (unsigned) ti->tm[1]->startAddr,
80 (unsigned) ti->tm[1]->endAddr);
81 fprintf(stderr, "\tMipmaps: %d-%d\n", ti->minLevel, ti->maxLevel);
82 fprintf(stderr, "\tFilters: min %d min %d\n",
83 (int) ti->minFilt, (int) ti->maxFilt);
84 fprintf(stderr, "\tClamps: s %d t %d\n", (int) ti->sClamp,
85 (int) ti->tClamp);
86 fprintf(stderr, "\tScales: s %f t %f\n", ti->sScale, ti->tScale);
87 fprintf(stderr, "\tInt Scales: s %d t %d\n",
88 ti->int_sScale / 0x800000, ti->int_tScale / 0x800000);
89 fprintf(stderr, "\t%s\n",
90 (ti->fixedPalette) ? "Fixed palette" : "Non fixed palette");
91 fprintf(stderr, "\t%s\n", (ti->validated) ? "Validated" : "Not validated");
92 }
93
94
95 /************************************************************************/
96 /*************************** Texture Mapping ****************************/
97 /************************************************************************/
98
99 static void
100 fxTexInvalidate(GLcontext * ctx, struct gl_texture_object *tObj)
101 {
102 fxMesaContext fxMesa = (fxMesaContext) ctx->DriverCtx;
103 tfxTexInfo *ti;
104
105 ti = fxTMGetTexInfo(tObj);
106 if (ti->isInTM)
107 fxTMMoveOutTM(fxMesa, tObj); /* TO DO: SLOW but easy to write */
108
109 ti->validated = GL_FALSE;
110 fxMesa->new_state |= FX_NEW_TEXTURING;
111 }
112
113 static tfxTexInfo *
114 fxAllocTexObjData(fxMesaContext fxMesa)
115 {
116 tfxTexInfo *ti;
117
118 if (!(ti = CALLOC(sizeof(tfxTexInfo)))) {
119 fprintf(stderr, "fx Driver: out of memory !\n");
120 fxCloseHardware();
121 exit(-1);
122 }
123
124 ti->validated = GL_FALSE;
125 ti->isInTM = GL_FALSE;
126
127 ti->whichTMU = FX_TMU_NONE;
128
129 ti->tm[FX_TMU0] = NULL;
130 ti->tm[FX_TMU1] = NULL;
131
132 ti->minFilt = GR_TEXTUREFILTER_POINT_SAMPLED;
133 ti->maxFilt = GR_TEXTUREFILTER_BILINEAR;
134
135 ti->sClamp = GR_TEXTURECLAMP_WRAP;
136 ti->tClamp = GR_TEXTURECLAMP_WRAP;
137
138 ti->mmMode = GR_MIPMAP_NEAREST;
139 ti->LODblend = FXFALSE;
140
141 return ti;
142 }
143
144 void
145 fxDDTexBind(GLcontext * ctx, GLenum target, struct gl_texture_object *tObj)
146 {
147 fxMesaContext fxMesa = (fxMesaContext) ctx->DriverCtx;
148 tfxTexInfo *ti;
149
150 if (MESA_VERBOSE & VERBOSE_DRIVER) {
151 fprintf(stderr, "fxmesa: fxDDTexBind(%d,%x)\n", tObj->Name,
152 (GLuint) tObj->DriverData);
153 }
154
155 if (target != GL_TEXTURE_2D)
156 return;
157
158 if (!tObj->DriverData) {
159 tObj->DriverData = fxAllocTexObjData(fxMesa);
160 }
161
162 ti = fxTMGetTexInfo(tObj);
163
164 fxMesa->texBindNumber++;
165 ti->lastTimeUsed = fxMesa->texBindNumber;
166
167 fxMesa->new_state |= FX_NEW_TEXTURING;
168 }
169
170 void
171 fxDDTexEnv(GLcontext * ctx, GLenum target, GLenum pname,
172 const GLfloat * param)
173 {
174 fxMesaContext fxMesa = (fxMesaContext) ctx->DriverCtx;
175
176 if (MESA_VERBOSE & VERBOSE_DRIVER) {
177 if (param)
178 fprintf(stderr, "fxmesa: texenv(%x,%x)\n", pname, (GLint) (*param));
179 else
180 fprintf(stderr, "fxmesa: texenv(%x)\n", pname);
181 }
182
183 /* apply any lod biasing right now */
184 if (pname == GL_TEXTURE_LOD_BIAS_EXT) {
185 FX_grTexLodBiasValue(GR_TMU0, *param);
186
187 if (fxMesa->haveTwoTMUs) {
188 FX_grTexLodBiasValue(GR_TMU1, *param);
189 }
190
191 }
192
193 fxMesa->new_state |= FX_NEW_TEXTURING;
194 }
195
196 void
197 fxDDTexParam(GLcontext * ctx, GLenum target, struct gl_texture_object *tObj,
198 GLenum pname, const GLfloat * params)
199 {
200 fxMesaContext fxMesa = (fxMesaContext) ctx->DriverCtx;
201 GLenum param = (GLenum) (GLint) params[0];
202 tfxTexInfo *ti;
203
204 if (MESA_VERBOSE & VERBOSE_DRIVER) {
205 fprintf(stderr, "fxmesa: fxDDTexParam(%d,%x,%x,%x)\n", tObj->Name,
206 (GLuint) tObj->DriverData, pname, param);
207 }
208
209 if (target != GL_TEXTURE_2D)
210 return;
211
212 if (!tObj->DriverData)
213 tObj->DriverData = fxAllocTexObjData(fxMesa);
214
215 ti = fxTMGetTexInfo(tObj);
216
217 switch (pname) {
218
219 case GL_TEXTURE_MIN_FILTER:
220 switch (param) {
221 case GL_NEAREST:
222 ti->mmMode = GR_MIPMAP_DISABLE;
223 ti->minFilt = GR_TEXTUREFILTER_POINT_SAMPLED;
224 ti->LODblend = FXFALSE;
225 break;
226 case GL_LINEAR:
227 ti->mmMode = GR_MIPMAP_DISABLE;
228 ti->minFilt = GR_TEXTUREFILTER_BILINEAR;
229 ti->LODblend = FXFALSE;
230 break;
231 case GL_NEAREST_MIPMAP_NEAREST:
232 ti->mmMode = GR_MIPMAP_NEAREST;
233 ti->minFilt = GR_TEXTUREFILTER_POINT_SAMPLED;
234 ti->LODblend = FXFALSE;
235 break;
236 case GL_LINEAR_MIPMAP_NEAREST:
237 ti->mmMode = GR_MIPMAP_NEAREST;
238 ti->minFilt = GR_TEXTUREFILTER_BILINEAR;
239 ti->LODblend = FXFALSE;
240 break;
241 case GL_NEAREST_MIPMAP_LINEAR:
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_POINT_SAMPLED;
251 break;
252 case GL_LINEAR_MIPMAP_LINEAR:
253 if (fxMesa->haveTwoTMUs) {
254 ti->mmMode = GR_MIPMAP_NEAREST;
255 ti->LODblend = FXTRUE;
256 }
257 else {
258 ti->mmMode = GR_MIPMAP_NEAREST_DITHER;
259 ti->LODblend = FXFALSE;
260 }
261 ti->minFilt = GR_TEXTUREFILTER_BILINEAR;
262 break;
263 default:
264 break;
265 }
266 fxTexInvalidate(ctx, tObj);
267 break;
268
269 case GL_TEXTURE_MAG_FILTER:
270 switch (param) {
271 case GL_NEAREST:
272 ti->maxFilt = GR_TEXTUREFILTER_POINT_SAMPLED;
273 break;
274 case GL_LINEAR:
275 ti->maxFilt = GR_TEXTUREFILTER_BILINEAR;
276 break;
277 default:
278 break;
279 }
280 fxTexInvalidate(ctx, tObj);
281 break;
282
283 case GL_TEXTURE_WRAP_S:
284 switch (param) {
285 case GL_CLAMP:
286 ti->sClamp = GR_TEXTURECLAMP_CLAMP;
287 break;
288 case GL_REPEAT:
289 ti->sClamp = GR_TEXTURECLAMP_WRAP;
290 break;
291 default:
292 break;
293 }
294 fxMesa->new_state |= FX_NEW_TEXTURING;
295 break;
296
297 case GL_TEXTURE_WRAP_T:
298 switch (param) {
299 case GL_CLAMP:
300 ti->tClamp = GR_TEXTURECLAMP_CLAMP;
301 break;
302 case GL_REPEAT:
303 ti->tClamp = GR_TEXTURECLAMP_WRAP;
304 break;
305 default:
306 break;
307 }
308 fxMesa->new_state |= FX_NEW_TEXTURING;
309 break;
310
311 case GL_TEXTURE_BORDER_COLOR:
312 /* TO DO */
313 break;
314
315 case GL_TEXTURE_MIN_LOD:
316 /* TO DO */
317 break;
318 case GL_TEXTURE_MAX_LOD:
319 /* TO DO */
320 break;
321 case GL_TEXTURE_BASE_LEVEL:
322 fxTexInvalidate(ctx, tObj);
323 break;
324 case GL_TEXTURE_MAX_LEVEL:
325 fxTexInvalidate(ctx, tObj);
326 break;
327
328 default:
329 break;
330 }
331 }
332
333 void
334 fxDDTexDel(GLcontext * ctx, struct gl_texture_object *tObj)
335 {
336 fxMesaContext fxMesa = FX_CONTEXT(ctx);
337 tfxTexInfo *ti = fxTMGetTexInfo(tObj);
338
339 if (MESA_VERBOSE & VERBOSE_DRIVER) {
340 fprintf(stderr, "fxmesa: fxDDTexDel(%d,%p)\n", tObj->Name, ti);
341 }
342
343 if (!ti)
344 return;
345
346 fxTMFreeTexture(fxMesa, tObj);
347
348 FREE(ti);
349 tObj->DriverData = NULL;
350 }
351
352
353
354 /*
355 * Convert a gl_color_table texture palette to Glide's format.
356 */
357 static void
358 convertPalette(FxU32 data[256], const struct gl_color_table *table)
359 {
360 const GLubyte *tableUB = (const GLubyte *) table->Table;
361 GLint width = table->Size;
362 FxU32 r, g, b, a;
363 GLint i;
364
365 ASSERT(!table->FloatTable);
366
367 switch (table->Format) {
368 case GL_INTENSITY:
369 for (i = 0; i < width; i++) {
370 r = tableUB[i];
371 g = tableUB[i];
372 b = tableUB[i];
373 a = tableUB[i];
374 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
375 }
376 break;
377 case GL_LUMINANCE:
378 for (i = 0; i < width; i++) {
379 r = tableUB[i];
380 g = tableUB[i];
381 b = tableUB[i];
382 a = 255;
383 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
384 }
385 break;
386 case GL_ALPHA:
387 for (i = 0; i < width; i++) {
388 r = g = b = 255;
389 a = tableUB[i];
390 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
391 }
392 break;
393 case GL_LUMINANCE_ALPHA:
394 for (i = 0; i < width; i++) {
395 r = g = b = tableUB[i * 2 + 0];
396 a = tableUB[i * 2 + 1];
397 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
398 }
399 break;
400 case GL_RGB:
401 for (i = 0; i < width; i++) {
402 r = tableUB[i * 3 + 0];
403 g = tableUB[i * 3 + 1];
404 b = tableUB[i * 3 + 2];
405 a = 255;
406 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
407 }
408 break;
409 case GL_RGBA:
410 for (i = 0; i < width; i++) {
411 r = tableUB[i * 4 + 0];
412 g = tableUB[i * 4 + 1];
413 b = tableUB[i * 4 + 2];
414 a = tableUB[i * 4 + 3];
415 data[i] = (a << 24) | (r << 16) | (g << 8) | b;
416 }
417 break;
418 }
419 }
420
421
422 void
423 fxDDTexPalette(GLcontext * ctx, struct gl_texture_object *tObj)
424 {
425 fxMesaContext fxMesa = FX_CONTEXT(ctx);
426
427 if (tObj) {
428 /* per-texture palette */
429 tfxTexInfo *ti;
430 if (MESA_VERBOSE & VERBOSE_DRIVER) {
431 fprintf(stderr, "fxmesa: fxDDTexPalette(%d,%x)\n",
432 tObj->Name, (GLuint) tObj->DriverData);
433 }
434 if (!tObj->DriverData)
435 tObj->DriverData = fxAllocTexObjData(fxMesa);
436 ti = fxTMGetTexInfo(tObj);
437 convertPalette(ti->palette.data, &tObj->Palette);
438 fxTexInvalidate(ctx, tObj);
439 }
440 else {
441 /* global texture palette */
442 if (MESA_VERBOSE & VERBOSE_DRIVER) {
443 fprintf(stderr, "fxmesa: fxDDTexPalette(global)\n");
444 }
445 convertPalette(fxMesa->glbPalette.data, &ctx->Texture.Palette);
446 fxMesa->new_state |= FX_NEW_TEXTURING;
447 }
448 }
449
450
451 void
452 fxDDTexUseGlbPalette(GLcontext * ctx, GLboolean state)
453 {
454 fxMesaContext fxMesa = FX_CONTEXT(ctx);
455
456 if (MESA_VERBOSE & VERBOSE_DRIVER) {
457 fprintf(stderr, "fxmesa: fxDDTexUseGlbPalette(%d)\n", state);
458 }
459
460 if (state) {
461 fxMesa->haveGlobalPaletteTexture = 1;
462
463 FX_grTexDownloadTable(GR_TMU0, GR_TEXTABLE_PALETTE,
464 &(fxMesa->glbPalette));
465 if (fxMesa->haveTwoTMUs)
466 FX_grTexDownloadTable(GR_TMU1, GR_TEXTABLE_PALETTE,
467 &(fxMesa->glbPalette));
468 }
469 else {
470 fxMesa->haveGlobalPaletteTexture = 0;
471
472 if ((ctx->Texture.Unit[0]._Current == ctx->Texture.Unit[0].Current2D) &&
473 (ctx->Texture.Unit[0]._Current != NULL)) {
474 struct gl_texture_object *tObj = ctx->Texture.Unit[0]._Current;
475
476 if (!tObj->DriverData)
477 tObj->DriverData = fxAllocTexObjData(fxMesa);
478
479 fxTexInvalidate(ctx, tObj);
480 }
481 }
482 }
483
484
485 static int
486 logbase2(int n)
487 {
488 GLint i = 1;
489 GLint log2 = 0;
490
491 if (n < 0) {
492 return -1;
493 }
494
495 while (n > i) {
496 i *= 2;
497 log2++;
498 }
499 if (i != n) {
500 return -1;
501 }
502 else {
503 return log2;
504 }
505 }
506
507 /* Need different versions for different cpus.
508 */
509 #define INT_TRICK(l2) (0x800000 * l2)
510
511
512 int
513 fxTexGetInfo(int w, int h, GrLOD_t * lodlevel, GrAspectRatio_t * ar,
514 float *sscale, float *tscale,
515 int *i_sscale, int *i_tscale, int *wscale, int *hscale)
516 {
517
518 static GrLOD_t lod[9] = { GR_LOD_256, GR_LOD_128, GR_LOD_64, GR_LOD_32,
519 GR_LOD_16, GR_LOD_8, GR_LOD_4, GR_LOD_2, GR_LOD_1
520 };
521
522 int logw, logh, ws, hs;
523 GrLOD_t l;
524 GrAspectRatio_t aspectratio;
525 float s, t;
526 int is, it;
527
528 logw = logbase2(w);
529 logh = logbase2(h);
530
531 switch (logw - logh) {
532 case 0:
533 aspectratio = GR_ASPECT_1x1;
534 l = lod[8 - logw];
535 s = t = 256.0f;
536 is = it = INT_TRICK(8);
537 ws = hs = 1;
538 break;
539 case 1:
540 aspectratio = GR_ASPECT_2x1;
541 l = lod[8 - logw];
542 s = 256.0f;
543 t = 128.0f;
544 is = INT_TRICK(8);
545 it = INT_TRICK(7);
546 ws = 1;
547 hs = 1;
548 break;
549 case 2:
550 aspectratio = GR_ASPECT_4x1;
551 l = lod[8 - logw];
552 s = 256.0f;
553 t = 64.0f;
554 is = INT_TRICK(8);
555 it = INT_TRICK(6);
556 ws = 1;
557 hs = 1;
558 break;
559 case 3:
560 aspectratio = GR_ASPECT_8x1;
561 l = lod[8 - logw];
562 s = 256.0f;
563 t = 32.0f;
564 is = INT_TRICK(8);
565 it = INT_TRICK(5);
566 ws = 1;
567 hs = 1;
568 break;
569 case 4:
570 aspectratio = GR_ASPECT_8x1;
571 l = lod[8 - logw];
572 s = 256.0f;
573 t = 32.0f;
574 is = INT_TRICK(8);
575 it = INT_TRICK(5);
576 ws = 1;
577 hs = 2;
578 break;
579 case 5:
580 aspectratio = GR_ASPECT_8x1;
581 l = lod[8 - logw];
582 s = 256.0f;
583 t = 32.0f;
584 is = INT_TRICK(8);
585 it = INT_TRICK(5);
586 ws = 1;
587 hs = 4;
588 break;
589 case 6:
590 aspectratio = GR_ASPECT_8x1;
591 l = lod[8 - logw];
592 s = 256.0f;
593 t = 32.0f;
594 is = INT_TRICK(8);
595 it = INT_TRICK(5);
596 ws = 1;
597 hs = 8;
598 break;
599 case 7:
600 aspectratio = GR_ASPECT_8x1;
601 l = lod[8 - logw];
602 s = 256.0f;
603 t = 32.0f;
604 is = INT_TRICK(8);
605 it = INT_TRICK(5);
606 ws = 1;
607 hs = 16;
608 break;
609 case 8:
610 aspectratio = GR_ASPECT_8x1;
611 l = lod[8 - logw];
612 s = 256.0f;
613 t = 32.0f;
614 is = INT_TRICK(8);
615 it = INT_TRICK(5);
616 ws = 1;
617 hs = 32;
618 break;
619 case -1:
620 aspectratio = GR_ASPECT_1x2;
621 l = lod[8 - logh];
622 s = 128.0f;
623 t = 256.0f;
624 is = INT_TRICK(7);
625 it = INT_TRICK(8);
626 ws = 1;
627 hs = 1;
628 break;
629 case -2:
630 aspectratio = GR_ASPECT_1x4;
631 l = lod[8 - logh];
632 s = 64.0f;
633 t = 256.0f;
634 is = INT_TRICK(6);
635 it = INT_TRICK(8);
636 ws = 1;
637 hs = 1;
638 break;
639 case -3:
640 aspectratio = GR_ASPECT_1x8;
641 l = lod[8 - logh];
642 s = 32.0f;
643 t = 256.0f;
644 is = INT_TRICK(5);
645 it = INT_TRICK(8);
646 ws = 1;
647 hs = 1;
648 break;
649 case -4:
650 aspectratio = GR_ASPECT_1x8;
651 l = lod[8 - logh];
652 s = 32.0f;
653 t = 256.0f;
654 is = INT_TRICK(5);
655 it = INT_TRICK(8);
656 ws = 2;
657 hs = 1;
658 break;
659 case -5:
660 aspectratio = GR_ASPECT_1x8;
661 l = lod[8 - logh];
662 s = 32.0f;
663 t = 256.0f;
664 is = INT_TRICK(5);
665 it = INT_TRICK(8);
666 ws = 4;
667 hs = 1;
668 break;
669 case -6:
670 aspectratio = GR_ASPECT_1x8;
671 l = lod[8 - logh];
672 s = 32.0f;
673 t = 256.0f;
674 is = INT_TRICK(5);
675 it = INT_TRICK(8);
676 ws = 8;
677 hs = 1;
678 break;
679 case -7:
680 aspectratio = GR_ASPECT_1x8;
681 l = lod[8 - logh];
682 s = 32.0f;
683 t = 256.0f;
684 is = INT_TRICK(5);
685 it = INT_TRICK(8);
686 ws = 16;
687 hs = 1;
688 break;
689 case -8:
690 aspectratio = GR_ASPECT_1x8;
691 l = lod[8 - logh];
692 s = 32.0f;
693 t = 256.0f;
694 is = INT_TRICK(5);
695 it = INT_TRICK(8);
696 ws = 32;
697 hs = 1;
698 break;
699 default:
700 return 0;
701 break;
702 }
703
704 if (lodlevel)
705 (*lodlevel) = l;
706
707 if (ar)
708 (*ar) = aspectratio;
709
710 if (sscale)
711 (*sscale) = s;
712
713 if (tscale)
714 (*tscale) = t;
715
716 if (wscale)
717 (*wscale) = ws;
718
719 if (hscale)
720 (*hscale) = hs;
721
722 if (i_sscale)
723 *i_sscale = is;
724
725 if (i_tscale)
726 *i_tscale = it;
727
728
729 return 1;
730 }
731
732 /*
733 * Given an OpenGL internal texture format, return the corresponding
734 * Glide internal texture format and base texture format.
735 */
736 void
737 fxTexGetFormat(GLenum glformat, GrTextureFormat_t * tfmt, GLint * ifmt)
738 {
739 switch (glformat) {
740 case 1:
741 case GL_LUMINANCE:
742 case GL_LUMINANCE4:
743 case GL_LUMINANCE8:
744 case GL_LUMINANCE12:
745 case GL_LUMINANCE16:
746 if (tfmt)
747 (*tfmt) = GR_TEXFMT_INTENSITY_8;
748 if (ifmt)
749 (*ifmt) = GL_LUMINANCE;
750 break;
751 case 2:
752 case GL_LUMINANCE_ALPHA:
753 case GL_LUMINANCE4_ALPHA4:
754 case GL_LUMINANCE6_ALPHA2:
755 case GL_LUMINANCE8_ALPHA8:
756 case GL_LUMINANCE12_ALPHA4:
757 case GL_LUMINANCE12_ALPHA12:
758 case GL_LUMINANCE16_ALPHA16:
759 if (tfmt)
760 (*tfmt) = GR_TEXFMT_ALPHA_INTENSITY_88;
761 if (ifmt)
762 (*ifmt) = GL_LUMINANCE_ALPHA;
763 break;
764 case GL_INTENSITY:
765 case GL_INTENSITY4:
766 case GL_INTENSITY8:
767 case GL_INTENSITY12:
768 case GL_INTENSITY16:
769 if (tfmt)
770 (*tfmt) = GR_TEXFMT_ALPHA_8;
771 if (ifmt)
772 (*ifmt) = GL_INTENSITY;
773 break;
774 case GL_ALPHA:
775 case GL_ALPHA4:
776 case GL_ALPHA8:
777 case GL_ALPHA12:
778 case GL_ALPHA16:
779 if (tfmt)
780 (*tfmt) = GR_TEXFMT_ALPHA_8;
781 if (ifmt)
782 (*ifmt) = GL_ALPHA;
783 break;
784 case 3:
785 case GL_RGB:
786 case GL_R3_G3_B2:
787 case GL_RGB4:
788 case GL_RGB5:
789 case GL_RGB8:
790 case GL_RGB10:
791 case GL_RGB12:
792 case GL_RGB16:
793 if (tfmt)
794 (*tfmt) = GR_TEXFMT_RGB_565;
795 if (ifmt)
796 (*ifmt) = GL_RGB;
797 break;
798 case 4:
799 case GL_RGBA:
800 case GL_RGBA2:
801 case GL_RGBA4:
802 case GL_RGBA8:
803 case GL_RGB10_A2:
804 case GL_RGBA12:
805 case GL_RGBA16:
806 if (tfmt)
807 (*tfmt) = GR_TEXFMT_ARGB_4444;
808 if (ifmt)
809 (*ifmt) = GL_RGBA;
810 break;
811 case GL_RGB5_A1:
812 if (tfmt)
813 (*tfmt) = GR_TEXFMT_ARGB_1555;
814 if (ifmt)
815 (*ifmt) = GL_RGBA;
816 break;
817 case GL_COLOR_INDEX:
818 case GL_COLOR_INDEX1_EXT:
819 case GL_COLOR_INDEX2_EXT:
820 case GL_COLOR_INDEX4_EXT:
821 case GL_COLOR_INDEX8_EXT:
822 case GL_COLOR_INDEX12_EXT:
823 case GL_COLOR_INDEX16_EXT:
824 if (tfmt)
825 (*tfmt) = GR_TEXFMT_P_8;
826 if (ifmt)
827 (*ifmt) = GL_RGBA; /* XXX why is this RGBA? */
828 break;
829 default:
830 fprintf(stderr,
831 "fx Driver: unsupported internalFormat (0x%x) in fxTexGetFormat()\n",
832 glformat);
833 fxCloseHardware();
834 exit(-1);
835 break;
836 }
837 }
838
839 static GLboolean
840 fxIsTexSupported(GLenum target, GLint internalFormat,
841 const struct gl_texture_image *image)
842 {
843 if (target != GL_TEXTURE_2D)
844 return GL_FALSE;
845
846 if (!fxTexGetInfo
847 (image->Width, image->Height, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
848 NULL)) return GL_FALSE;
849
850 if (image->Border > 0)
851 return GL_FALSE;
852
853 return GL_TRUE;
854 }
855
856
857 /**********************************************************************/
858 /**** NEW TEXTURE IMAGE FUNCTIONS ****/
859 /**********************************************************************/
860
861 /* Texel-fetch functions for software texturing and glGetTexImage().
862 * We should have been able to use some "standard" fetch functions (which
863 * may get defined in texutil.c) but we have to account for scaled texture
864 * images on tdfx hardware (the 8:1 aspect ratio limit).
865 * Hence, we need special functions here.
866 */
867
868 static void
869 fetch_intensity8(const struct gl_texture_image *texImage,
870 GLint i, GLint j, GLint k, GLvoid * texelOut)
871 {
872 GLchan *rgba = (GLchan *) texelOut;
873 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
874 const GLubyte *texel;
875
876 i = i * mml->wScale;
877 j = j * mml->hScale;
878
879 texel = ((GLubyte *) texImage->Data) + j * mml->width + i;
880 rgba[RCOMP] = *texel;
881 rgba[GCOMP] = *texel;
882 rgba[BCOMP] = *texel;
883 rgba[ACOMP] = *texel;
884 }
885
886
887 static void
888 fetch_luminance8(const struct gl_texture_image *texImage,
889 GLint i, GLint j, GLint k, GLvoid * texelOut)
890 {
891 GLchan *rgba = (GLchan *) texelOut;
892 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
893 const GLubyte *texel;
894
895 i = i * mml->wScale;
896 j = j * mml->hScale;
897
898 texel = ((GLubyte *) texImage->Data) + j * mml->width + i;
899 rgba[RCOMP] = *texel;
900 rgba[GCOMP] = *texel;
901 rgba[BCOMP] = *texel;
902 rgba[ACOMP] = 255;
903 }
904
905
906 static void
907 fetch_alpha8(const struct gl_texture_image *texImage,
908 GLint i, GLint j, GLint k, GLvoid * texelOut)
909 {
910 GLchan *rgba = (GLchan *) texelOut;
911 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
912 const GLubyte *texel;
913
914 i = i * mml->wScale;
915 j = j * mml->hScale;
916 i = i * mml->width / texImage->Width;
917 j = j * mml->height / texImage->Height;
918
919 texel = ((GLubyte *) texImage->Data) + j * mml->width + i;
920 rgba[RCOMP] = 255;
921 rgba[GCOMP] = 255;
922 rgba[BCOMP] = 255;
923 rgba[ACOMP] = *texel;
924 }
925
926
927 static void
928 fetch_index8(const struct gl_texture_image *texImage,
929 GLint i, GLint j, GLint k, GLvoid * texelOut)
930 {
931 /* XXX todo */
932 }
933
934
935 static void
936 fetch_luminance8_alpha8(const struct gl_texture_image *texImage,
937 GLint i, GLint j, GLint k, GLvoid * texelOut)
938 {
939 GLchan *rgba = (GLchan *) texelOut;
940 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
941 const GLubyte *texel;
942
943 i = i * mml->wScale;
944 j = j * mml->hScale;
945
946 texel = ((GLubyte *) texImage->Data) + (j * mml->width + i) * 2;
947 rgba[RCOMP] = texel[0];
948 rgba[GCOMP] = texel[0];
949 rgba[BCOMP] = texel[0];
950 rgba[ACOMP] = texel[1];
951 }
952
953
954 static void
955 fetch_r5g6b5(const struct gl_texture_image *texImage,
956 GLint i, GLint j, GLint k, GLvoid * texelOut)
957 {
958 GLchan *rgba = (GLchan *) texelOut;
959 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
960 const GLushort *texel;
961
962 i = i * mml->wScale;
963 j = j * mml->hScale;
964
965 texel = ((GLushort *) texImage->Data) + j * mml->width + i;
966 rgba[RCOMP] = (((*texel) >> 11) & 0x1f) * 255 / 31;
967 rgba[GCOMP] = (((*texel) >> 5) & 0x3f) * 255 / 63;
968 rgba[BCOMP] = (((*texel) >> 0) & 0x1f) * 255 / 31;
969 rgba[ACOMP] = 255;
970 }
971
972
973 static void
974 fetch_r4g4b4a4(const struct gl_texture_image *texImage,
975 GLint i, GLint j, GLint k, GLvoid * texelOut)
976 {
977 GLchan *rgba = (GLchan *) texelOut;
978 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
979 const GLushort *texel;
980
981 i = i * mml->wScale;
982 j = j * mml->hScale;
983
984 texel = ((GLushort *) texImage->Data) + j * mml->width + i;
985 rgba[RCOMP] = (((*texel) >> 12) & 0xf) * 255 / 15;
986 rgba[GCOMP] = (((*texel) >> 8) & 0xf) * 255 / 15;
987 rgba[BCOMP] = (((*texel) >> 4) & 0xf) * 255 / 15;
988 rgba[ACOMP] = (((*texel) >> 0) & 0xf) * 255 / 15;
989 }
990
991
992 static void
993 fetch_r5g5b5a1(const struct gl_texture_image *texImage,
994 GLint i, GLint j, GLint k, GLvoid * texelOut)
995 {
996 GLchan *rgba = (GLchan *) texelOut;
997 const tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
998 const GLushort *texel;
999
1000 i = i * mml->wScale;
1001 j = j * mml->hScale;
1002
1003 texel = ((GLushort *) texImage->Data) + j * mml->width + i;
1004 rgba[RCOMP] = (((*texel) >> 11) & 0x1f) * 255 / 31;
1005 rgba[GCOMP] = (((*texel) >> 6) & 0x1f) * 255 / 31;
1006 rgba[BCOMP] = (((*texel) >> 1) & 0x1f) * 255 / 31;
1007 rgba[ACOMP] = (((*texel) >> 0) & 0x01) * 255;
1008 }
1009
1010
1011 static void
1012 PrintTexture(int w, int h, int c, const GLubyte * data)
1013 {
1014 int i, j;
1015 for (i = 0; i < h; i++) {
1016 for (j = 0; j < w; j++) {
1017 if (c == 2)
1018 printf("%02x %02x ", data[0], data[1]);
1019 else if (c == 3)
1020 printf("%02x %02x %02x ", data[0], data[1], data[2]);
1021 data += c;
1022 }
1023 printf("\n");
1024 }
1025 }
1026
1027
1028 const struct gl_texture_format *
1029 fxDDChooseTextureFormat( GLcontext *ctx, GLint internalFormat,
1030 GLenum srcFormat, GLenum srcType )
1031 {
1032 switch (internalFormat) {
1033 case GL_INTENSITY:
1034 case GL_INTENSITY4:
1035 case GL_INTENSITY8:
1036 case GL_INTENSITY12:
1037 case GL_INTENSITY16:
1038 return &_mesa_texformat_i8;
1039 case 1:
1040 case GL_LUMINANCE:
1041 case GL_LUMINANCE4:
1042 case GL_LUMINANCE8:
1043 case GL_LUMINANCE12:
1044 case GL_LUMINANCE16:
1045 return &_mesa_texformat_l8;
1046 case GL_ALPHA:
1047 case GL_ALPHA4:
1048 case GL_ALPHA8:
1049 case GL_ALPHA12:
1050 case GL_ALPHA16:
1051 return &_mesa_texformat_a8;
1052 case GL_COLOR_INDEX:
1053 case GL_COLOR_INDEX1_EXT:
1054 case GL_COLOR_INDEX2_EXT:
1055 case GL_COLOR_INDEX4_EXT:
1056 case GL_COLOR_INDEX8_EXT:
1057 case GL_COLOR_INDEX12_EXT:
1058 case GL_COLOR_INDEX16_EXT:
1059 return &_mesa_texformat_ci8;
1060 case 2:
1061 case GL_LUMINANCE_ALPHA:
1062 case GL_LUMINANCE4_ALPHA4:
1063 case GL_LUMINANCE6_ALPHA2:
1064 case GL_LUMINANCE8_ALPHA8:
1065 case GL_LUMINANCE12_ALPHA4:
1066 case GL_LUMINANCE12_ALPHA12:
1067 case GL_LUMINANCE16_ALPHA16:
1068 return &_mesa_texformat_al88;
1069 case 3:
1070 case GL_RGB:
1071 case GL_R3_G3_B2:
1072 case GL_RGB4:
1073 case GL_RGB5:
1074 case GL_RGB8:
1075 case GL_RGB10:
1076 case GL_RGB12:
1077 case GL_RGB16:
1078 return &_mesa_texformat_rgb565;
1079 case 4:
1080 case GL_RGBA:
1081 case GL_RGBA2:
1082 case GL_RGBA4:
1083 case GL_RGBA8:
1084 case GL_RGB10_A2:
1085 case GL_RGBA12:
1086 case GL_RGBA16:
1087 return &_mesa_texformat_argb4444;
1088 case GL_RGB5_A1:
1089 return &_mesa_texformat_argb1555;
1090 default:
1091 _mesa_problem(NULL, "unexpected format in fxDDChooseTextureFormat");
1092 return NULL;
1093 }
1094 }
1095
1096
1097 static GrTextureFormat_t
1098 fxGlideFormat(GLint mesaFormat)
1099 {
1100 switch (mesaFormat) {
1101 case MESA_FORMAT_I8:
1102 return GR_TEXFMT_ALPHA_8;
1103 case MESA_FORMAT_A8:
1104 return GR_TEXFMT_ALPHA_8;
1105 case MESA_FORMAT_L8:
1106 return GR_TEXFMT_INTENSITY_8;
1107 case MESA_FORMAT_CI8:
1108 return GR_TEXFMT_P_8;
1109 case MESA_FORMAT_AL88:
1110 return GR_TEXFMT_ALPHA_INTENSITY_88;
1111 case MESA_FORMAT_RGB565:
1112 return GR_TEXFMT_RGB_565;
1113 case MESA_FORMAT_ARGB4444:
1114 return GR_TEXFMT_ARGB_4444;
1115 case MESA_FORMAT_ARGB1555:
1116 return GR_TEXFMT_ARGB_1555;
1117 default:
1118 _mesa_problem(NULL, "Unexpected format in fxGlideFormat");
1119 return 0;
1120 }
1121 }
1122
1123
1124 static FetchTexelFunc
1125 fxFetchFunction(GLint mesaFormat)
1126 {
1127 switch (mesaFormat) {
1128 case MESA_FORMAT_I8:
1129 return fetch_intensity8;
1130 case MESA_FORMAT_A8:
1131 return fetch_alpha8;
1132 case MESA_FORMAT_L8:
1133 return fetch_luminance8;
1134 case MESA_FORMAT_CI8:
1135 return fetch_index8;
1136 case MESA_FORMAT_AL88:
1137 return fetch_luminance8_alpha8;
1138 case MESA_FORMAT_RGB565:
1139 return fetch_r5g6b5;
1140 case MESA_FORMAT_ARGB4444:
1141 return fetch_r4g4b4a4;
1142 case MESA_FORMAT_ARGB1555:
1143 return fetch_r5g5b5a1;
1144 default:
1145 _mesa_problem(NULL, "Unexpected format in fxGlideFormat");
1146 return NULL;
1147 }
1148 }
1149
1150 void
1151 fxDDTexImage2D(GLcontext * ctx, GLenum target, GLint level,
1152 GLint internalFormat, GLint width, GLint height, GLint border,
1153 GLenum format, GLenum type, const GLvoid * pixels,
1154 const struct gl_pixelstore_attrib *packing,
1155 struct gl_texture_object *texObj,
1156 struct gl_texture_image *texImage)
1157 {
1158 fxMesaContext fxMesa = (fxMesaContext) ctx->DriverCtx;
1159 GrTextureFormat_t gldformat;
1160 tfxTexInfo *ti;
1161 tfxMipMapLevel *mml = FX_MIPMAP_DATA(texImage);
1162 GLint texelBytes;
1163
1164 if (!fxIsTexSupported(target, internalFormat, texImage)) {
1165 _mesa_problem(NULL, "fx Driver: unsupported texture in fxDDTexImg()\n");
1166 return;
1167 }
1168
1169 if (!texObj->DriverData)
1170 texObj->DriverData = fxAllocTexObjData(fxMesa);
1171 ti = fxTMGetTexInfo(texObj);
1172
1173 if (!mml) {
1174 texImage->DriverData = MALLOC(sizeof(tfxMipMapLevel));
1175 mml = FX_MIPMAP_DATA(texImage);
1176 }
1177
1178 fxTexGetFormat(internalFormat, &gldformat, NULL);
1179
1180 fxTexGetInfo(width, height, NULL, NULL, NULL, NULL,
1181 NULL, NULL, &mml->wScale, &mml->hScale);
1182
1183 mml->width = width * mml->wScale;
1184 mml->height = height * mml->hScale;
1185
1186 if (mml->wScale != 1 || mml->hScale != 1) {
1187 /* rescale image to overcome 1:8 aspect limitation */
1188
1189 /* XXX TO-DO! */
1190 }
1191
1192
1193 /*** This code basically comes from _mesa_store_teximage2d() ***/
1194
1195 /* choose the texture format */
1196 assert(ctx->Driver.ChooseTextureFormat);
1197 texImage->TexFormat = (*ctx->Driver.ChooseTextureFormat)(ctx,
1198 internalFormat, format, type);
1199 assert(texImage->TexFormat);
1200
1201 texelBytes = texImage->TexFormat->TexelBytes;
1202
1203 /* allocate memory */
1204 texImage->Data = MALLOC(mml->width * mml->height * texelBytes);
1205 if (!texImage->Data) {
1206 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage2D");
1207 return;
1208 }
1209
1210 /* unpack image, apply transfer ops and store in texImage->Data */
1211 _mesa_transfer_teximage(ctx, 2, _mesa_base_tex_format(ctx, internalFormat),
1212 texImage->TexFormat, texImage->Data,
1213 width, height, 1, 0, 0, 0,
1214 texImage->Width * texelBytes,
1215 0, /* dstImageStride */
1216 format, type, pixels, packing);
1217
1218
1219 mml->glideFormat = fxGlideFormat(texImage->TexFormat->MesaFormat);
1220 texImage->FetchTexel = fxFetchFunction(texImage->TexFormat->MesaFormat);
1221
1222 fxTexInvalidate(ctx, texObj);
1223
1224 if (ti->validated && ti->isInTM) {
1225 /*printf("reloadmipmaplevels\n"); */
1226 fxTMReloadMipMapLevel(fxMesa, texObj, level);
1227 }
1228 else {
1229 /*printf("invalidate2\n"); */
1230 fxTexInvalidate(ctx, texObj);
1231 }
1232 }
1233
1234
1235 void
1236 fxDDTexSubImage2D(GLcontext * ctx, GLenum target, GLint level,
1237 GLint xoffset, GLint yoffset,
1238 GLsizei width, GLsizei height,
1239 GLenum format, GLenum type, const GLvoid * pixels,
1240 const struct gl_pixelstore_attrib *packing,
1241 struct gl_texture_object *texObj,
1242 struct gl_texture_image *texImage)
1243 {
1244 fxMesaContext fxMesa = (fxMesaContext) ctx->DriverCtx;
1245 tfxTexInfo *ti;
1246 tfxMipMapLevel *mml;
1247
1248 if (!texObj->DriverData) {
1249 _mesa_problem(ctx, "problem in fxDDTexSubImage2D");
1250 return;
1251 }
1252
1253 ti = fxTMGetTexInfo(texObj);
1254 assert(ti);
1255 mml = FX_MIPMAP_DATA(texImage);
1256 assert(mml);
1257
1258 assert(texImage->Data); /* must have an existing texture image! */
1259 assert(texImage->Format);
1260
1261 #if 000
1262 /* XXX redo all of this */
1263
1264
1265 /* check for image rescale */
1266 if (ctx->_ImageTransferState ||
1267 texImage->Width != mml->width ||
1268 texImage->Height != mml->height) {
1269 success = GL_FALSE;
1270 }
1271 else {
1272 success = _mesa_convert_texsubimage2d(texImage->TexFormat->MesaFormat,
1273 xoffset, yoffset,
1274 width, height,
1275 mml->width,
1276 format, type, packing,
1277 pixels, texImage->Data);
1278 }
1279
1280 if (!success) {
1281 /* Incoming image might need scale/bias or is in an uncommon format
1282 * that _mesa_convert_texsubimage() can't deal with. Convert it to
1283 * a simpler format now.
1284 */
1285
1286 GLenum simpleFormat = texImage->TexFormat->BaseFormat;
1287 GLint comps = _mesa_components_in_format(simpleFormat);
1288 GLubyte *tempImage;
1289 GLboolean success;
1290
1291 tempImage = MALLOC(width * height * comps * sizeof(GLubyte));
1292
1293 /* Apply pixel transfer ops and convert image format to something
1294 * simple (format = simpleFormat, type = CHAN_TYPE).
1295 */
1296 #if 0
1297 /* XXX this has to be rewritten! */
1298 _mesa_transfer_teximage(ctx, 2, /* dimensions */
1299 simpleFormat, /* base int format */
1300 simpleFormat, /* dest format */
1301 tempImage, /* dest addr */
1302 width, height, 1, /* src size */
1303 0, 0, 0, /* dst offsets */
1304 width * comps, /* dstRowStride */
1305 0, /* dstImageStride */
1306 format, type, pixels, packing /* src info */ );
1307 #endif
1308
1309 if (texImage->Width != mml->width || texImage->Height != mml->height) {
1310 /* Upscale image to accomodate Glide's image aspect limitations. */
1311 const GLint wScale = mml->width / texImage->Width;
1312 const GLint hScale = mml->height / texImage->Height;
1313 const GLint newWidth = width * wScale;
1314 const GLint newHeight = height *hScale;
1315 GLvoid *rescaledImage = MALLOC(newWidth * newHeight
1316 * texImage->TexFormat->TexelBytes);
1317 assert(mml->width >= texImage->Width);
1318 assert(mml->height >= texImage->Height);
1319 if (!rescaledImage) {
1320 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexSubImage2D");
1321 return;
1322 }
1323 _mesa_rescale_teximage2d(texImage->TexFormat,
1324 width, height, newWidth, newHeight,
1325 tempImage, rescaledImage);
1326 success = _mesa_convert_texsubimage2d(texImage->TexFormat->MesaFormat,
1327 xoffset * wScale, yoffset * hScale,
1328 newWidth, newHeight,
1329 mml->width, /* destImageWidth */
1330 simpleFormat, /* source format */
1331 CHAN_TYPE, /* source type */
1332 &_mesa_native_packing,
1333 rescaledImage, texImage->Data);
1334 FREE(rescaledImage);
1335 }
1336 else {
1337 success = _mesa_convert_texsubimage2d(texImage->TexFormat->MesaFormat,
1338 xoffset, yoffset,
1339 width, height,
1340 mml->width,
1341 simpleFormat, CHAN_TYPE,
1342 &_mesa_native_packing,
1343 tempImage, texImage->Data);
1344 }
1345 /* the conversion had better of worked! */
1346 assert(success);
1347 FREE(tempImage);
1348 }
1349 #endif
1350
1351
1352 if (ti->validated && ti->isInTM)
1353 fxTMReloadMipMapLevel(fxMesa, texObj, level);
1354 else
1355 fxTexInvalidate(ctx, texObj);
1356 }
1357
1358
1359 #else
1360
1361
1362 /*
1363 * Need this to provide at least one external definition.
1364 */
1365
1366 extern int gl_fx_dummy_function_ddtex(void);
1367 int
1368 gl_fx_dummy_function_ddtex(void)
1369 {
1370 return 0;
1371 }
1372
1373 #endif /* FX */