2 * Mesa 3-D graphics library
5 * Copyright (C) 1999-2008 Brian Paul All Rights Reserved.
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:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
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.
27 * \file texcompress_fxt1.c
28 * GL_3DFX_texture_compression_FXT1 support.
37 #include "mfeatures.h"
39 #include "texcompress.h"
40 #include "texcompress_fxt1.h"
42 #include "swrast/s_context.h"
45 #if FEATURE_texture_fxt1
49 fxt1_encode (GLuint width
, GLuint height
, GLint comps
,
50 const void *source
, GLint srcRowStride
,
51 void *dest
, GLint destRowStride
);
54 fxt1_decode_1 (const void *texture
, GLint stride
,
55 GLint i
, GLint j
, GLubyte
*rgba
);
59 * Store user's image in rgb_fxt1 format.
62 _mesa_texstore_rgb_fxt1(TEXSTORE_PARAMS
)
64 const GLubyte
*pixels
;
67 const GLint texWidth
= dstRowStride
* 8 / 16; /* a bit of a hack */
68 const GLubyte
*tempImage
= NULL
;
70 ASSERT(dstFormat
== MESA_FORMAT_RGB_FXT1
);
71 ASSERT(dstXoffset
% 8 == 0);
72 ASSERT(dstYoffset
% 4 == 0);
73 ASSERT(dstZoffset
== 0);
76 if (srcFormat
!= GL_RGB
||
77 srcType
!= GL_UNSIGNED_BYTE
||
78 ctx
->_ImageTransferState
||
79 srcPacking
->RowLength
!= srcWidth
||
80 srcPacking
->SwapBytes
) {
81 /* convert image to RGB/GLubyte */
82 tempImage
= _mesa_make_temp_ubyte_image(ctx
, dims
,
84 _mesa_get_format_base_format(dstFormat
),
85 srcWidth
, srcHeight
, srcDepth
,
86 srcFormat
, srcType
, srcAddr
,
89 return GL_FALSE
; /* out of memory */
91 srcRowStride
= 3 * srcWidth
;
95 pixels
= _mesa_image_address2d(srcPacking
, srcAddr
, srcWidth
, srcHeight
,
96 srcFormat
, srcType
, 0, 0);
98 srcRowStride
= _mesa_image_row_stride(srcPacking
, srcWidth
, srcFormat
,
99 srcType
) / sizeof(GLubyte
);
102 dst
= _mesa_compressed_image_address(dstXoffset
, dstYoffset
, 0,
104 texWidth
, dstSlices
[0]);
106 fxt1_encode(srcWidth
, srcHeight
, 3, pixels
, srcRowStride
,
110 free((void*) tempImage
);
117 * Store user's image in rgba_fxt1 format.
120 _mesa_texstore_rgba_fxt1(TEXSTORE_PARAMS
)
122 const GLubyte
*pixels
;
125 GLint texWidth
= dstRowStride
* 8 / 16; /* a bit of a hack */
126 const GLubyte
*tempImage
= NULL
;
128 ASSERT(dstFormat
== MESA_FORMAT_RGBA_FXT1
);
129 ASSERT(dstXoffset
% 8 == 0);
130 ASSERT(dstYoffset
% 4 == 0);
131 ASSERT(dstZoffset
== 0);
134 if (srcFormat
!= GL_RGBA
||
135 srcType
!= GL_UNSIGNED_BYTE
||
136 ctx
->_ImageTransferState
||
137 srcPacking
->SwapBytes
) {
138 /* convert image to RGBA/GLubyte */
139 tempImage
= _mesa_make_temp_ubyte_image(ctx
, dims
,
141 _mesa_get_format_base_format(dstFormat
),
142 srcWidth
, srcHeight
, srcDepth
,
143 srcFormat
, srcType
, srcAddr
,
146 return GL_FALSE
; /* out of memory */
148 srcRowStride
= 4 * srcWidth
;
152 pixels
= _mesa_image_address2d(srcPacking
, srcAddr
, srcWidth
, srcHeight
,
153 srcFormat
, srcType
, 0, 0);
155 srcRowStride
= _mesa_image_row_stride(srcPacking
, srcWidth
, srcFormat
,
156 srcType
) / sizeof(GLubyte
);
159 dst
= _mesa_compressed_image_address(dstXoffset
, dstYoffset
, 0,
161 texWidth
, dstSlices
[0]);
163 fxt1_encode(srcWidth
, srcHeight
, 4, pixels
, srcRowStride
,
167 free((void*) tempImage
);
174 _mesa_fetch_texel_2d_f_rgba_fxt1( const struct swrast_texture_image
*texImage
,
175 GLint i
, GLint j
, GLint k
, GLfloat
*texel
)
177 /* just sample as GLubyte and convert to float here */
180 fxt1_decode_1(texImage
->Base
.Data
, texImage
->Base
.RowStride
, i
, j
, rgba
);
181 texel
[RCOMP
] = UBYTE_TO_FLOAT(rgba
[RCOMP
]);
182 texel
[GCOMP
] = UBYTE_TO_FLOAT(rgba
[GCOMP
]);
183 texel
[BCOMP
] = UBYTE_TO_FLOAT(rgba
[BCOMP
]);
184 texel
[ACOMP
] = UBYTE_TO_FLOAT(rgba
[ACOMP
]);
189 _mesa_fetch_texel_2d_f_rgb_fxt1( const struct swrast_texture_image
*texImage
,
190 GLint i
, GLint j
, GLint k
, GLfloat
*texel
)
192 /* just sample as GLubyte and convert to float here */
195 fxt1_decode_1(texImage
->Base
.Data
, texImage
->Base
.RowStride
, i
, j
, rgba
);
196 texel
[RCOMP
] = UBYTE_TO_FLOAT(rgba
[RCOMP
]);
197 texel
[GCOMP
] = UBYTE_TO_FLOAT(rgba
[GCOMP
]);
198 texel
[BCOMP
] = UBYTE_TO_FLOAT(rgba
[BCOMP
]);
204 /***************************************************************************\
207 * The encoder was built by reversing the decoder,
208 * and is vaguely based on Texus2 by 3dfx. Note that this code
209 * is merely a proof of concept, since it is highly UNoptimized;
210 * moreover, it is sub-optimal due to initial conditions passed
211 * to Lloyd's algorithm (the interpolation modes are even worse).
212 \***************************************************************************/
215 #define MAX_COMP 4 /* ever needed maximum number of components in texel */
216 #define MAX_VECT 4 /* ever needed maximum number of base vectors to find */
217 #define N_TEXELS 32 /* number of texels in a block (always 32) */
218 #define LL_N_REP 50 /* number of iterations in lloyd's vq */
219 #define LL_RMS_D 10 /* fault tolerance (maximum delta) */
220 #define LL_RMS_E 255 /* fault tolerance (maximum error) */
221 #define ALPHA_TS 2 /* alpha threshold: (255 - ALPHA_TS) deemed opaque */
222 #define ISTBLACK(v) (*((GLuint *)(v)) == 0)
226 * Define a 64-bit unsigned integer type and macros
230 #define FX64_NATIVE 1
232 typedef uint64_t Fx64
;
234 #define FX64_MOV32(a, b) a = b
235 #define FX64_OR32(a, b) a |= b
236 #define FX64_SHL(a, c) a <<= c
240 #define FX64_NATIVE 0
246 #define FX64_MOV32(a, b) a.lo = b
247 #define FX64_OR32(a, b) a.lo |= b
249 #define FX64_SHL(a, c) \
252 a.hi = a.lo << ((c) - 32); \
255 a.hi = (a.hi << (c)) | (a.lo >> (32 - (c))); \
263 #define F(i) (GLfloat)1 /* can be used to obtain an oblong metric: 0.30 / 0.59 / 0.11 */
264 #define SAFECDOT 1 /* for paranoids */
266 #define MAKEIVEC(NV, NC, IV, B, V0, V1) \
268 /* compute interpolation vector */ \
272 for (i = 0; i < NC; i++) { \
273 IV[i] = (V1[i] - V0[i]) * F(i); \
274 d2 += IV[i] * IV[i]; \
276 rd2 = (GLfloat)NV / d2; \
278 for (i = 0; i < NC; i++) { \
280 B -= IV[i] * V0[i]; \
283 B = B * rd2 + 0.5f; \
286 #define CALCCDOT(TEXEL, NV, NC, IV, B, V)\
288 GLfloat dot = 0.0F; \
289 for (i = 0; i < NC; i++) { \
290 dot += V[i] * IV[i]; \
292 TEXEL = (GLint)(dot + B); \
296 } else if (TEXEL > NV) { \
304 fxt1_bestcol (GLfloat vec
[][MAX_COMP
], GLint nv
,
305 GLubyte input
[MAX_COMP
], GLint nc
)
307 GLint i
, j
, best
= -1;
308 GLfloat err
= 1e9
; /* big enough */
310 for (j
= 0; j
< nv
; j
++) {
312 for (i
= 0; i
< nc
; i
++) {
313 e
+= (vec
[j
][i
] - input
[i
]) * (vec
[j
][i
] - input
[i
]);
326 fxt1_worst (GLfloat vec
[MAX_COMP
],
327 GLubyte input
[N_TEXELS
][MAX_COMP
], GLint nc
, GLint n
)
329 GLint i
, k
, worst
= -1;
330 GLfloat err
= -1.0F
; /* small enough */
332 for (k
= 0; k
< n
; k
++) {
334 for (i
= 0; i
< nc
; i
++) {
335 e
+= (vec
[i
] - input
[k
][i
]) * (vec
[i
] - input
[k
][i
]);
348 fxt1_variance (GLdouble variance
[MAX_COMP
],
349 GLubyte input
[N_TEXELS
][MAX_COMP
], GLint nc
, GLint n
)
351 GLint i
, k
, best
= 0;
353 GLdouble var
, maxvar
= -1; /* small enough */
354 GLdouble teenth
= 1.0 / n
;
356 for (i
= 0; i
< nc
; i
++) {
358 for (k
= 0; k
< n
; k
++) {
359 GLint t
= input
[k
][i
];
363 var
= sx2
* teenth
- sx
* sx
* teenth
* teenth
;
378 fxt1_choose (GLfloat vec
[][MAX_COMP
], GLint nv
,
379 GLubyte input
[N_TEXELS
][MAX_COMP
], GLint nc
, GLint n
)
382 /* Choose colors from a grid.
386 for (j
= 0; j
< nv
; j
++) {
387 GLint m
= j
* (n
- 1) / (nv
- 1);
388 for (i
= 0; i
< nc
; i
++) {
389 vec
[j
][i
] = input
[m
][i
];
393 /* Our solution here is to find the darkest and brightest colors in
394 * the 8x4 tile and use those as the two representative colors.
395 * There are probably better algorithms to use (histogram-based).
398 GLint minSum
= 2000; /* big enough */
399 GLint maxSum
= -1; /* small enough */
400 GLint minCol
= 0; /* phoudoin: silent compiler! */
401 GLint maxCol
= 0; /* phoudoin: silent compiler! */
411 memset(hist
, 0, sizeof(hist
));
413 for (k
= 0; k
< n
; k
++) {
417 for (i
= 0; i
< nc
; i
++) {
422 for (l
= 0; l
< n
; l
++) {
431 } else if (hist
[l
].key
== key
) {
447 for (j
= 0; j
< lenh
; j
++) {
448 for (i
= 0; i
< nc
; i
++) {
449 vec
[j
][i
] = (GLfloat
)input
[hist
[j
].idx
][i
];
452 for (; j
< nv
; j
++) {
453 for (i
= 0; i
< nc
; i
++) {
454 vec
[j
][i
] = vec
[0][i
];
460 for (j
= 0; j
< nv
; j
++) {
461 for (i
= 0; i
< nc
; i
++) {
462 vec
[j
][i
] = ((nv
- 1 - j
) * input
[minCol
][i
] + j
* input
[maxCol
][i
] + (nv
- 1) / 2) / (GLfloat
)(nv
- 1);
472 fxt1_lloyd (GLfloat vec
[][MAX_COMP
], GLint nv
,
473 GLubyte input
[N_TEXELS
][MAX_COMP
], GLint nc
, GLint n
)
475 /* Use the generalized lloyd's algorithm for VQ:
476 * find 4 color vectors.
478 * for each sample color
479 * sort to nearest vector.
481 * replace each vector with the centroid of its matching colors.
483 * repeat until RMS doesn't improve.
485 * if a color vector has no samples, or becomes the same as another
486 * vector, replace it with the color which is farthest from a sample.
488 * vec[][MAX_COMP] initial vectors and resulting colors
489 * nv number of resulting colors required
490 * input[N_TEXELS][MAX_COMP] input texels
491 * nc number of components in input / vec
492 * n number of input samples
495 GLint sum
[MAX_VECT
][MAX_COMP
]; /* used to accumulate closest texels */
496 GLint cnt
[MAX_VECT
]; /* how many times a certain vector was chosen */
497 GLfloat error
, lasterror
= 1e9
;
502 for (rep
= 0; rep
< LL_N_REP
; rep
++) {
503 /* reset sums & counters */
504 for (j
= 0; j
< nv
; j
++) {
505 for (i
= 0; i
< nc
; i
++) {
512 /* scan whole block */
513 for (k
= 0; k
< n
; k
++) {
516 GLfloat err
= 1e9
; /* big enough */
517 /* determine best vector */
518 for (j
= 0; j
< nv
; j
++) {
519 GLfloat e
= (vec
[j
][0] - input
[k
][0]) * (vec
[j
][0] - input
[k
][0]) +
520 (vec
[j
][1] - input
[k
][1]) * (vec
[j
][1] - input
[k
][1]) +
521 (vec
[j
][2] - input
[k
][2]) * (vec
[j
][2] - input
[k
][2]);
523 e
+= (vec
[j
][3] - input
[k
][3]) * (vec
[j
][3] - input
[k
][3]);
531 GLint best
= fxt1_bestcol(vec
, nv
, input
[k
], nc
, &err
);
534 /* add in closest color */
535 for (i
= 0; i
< nc
; i
++) {
536 sum
[best
][i
] += input
[k
][i
];
538 /* mark this vector as used */
540 /* accumulate error */
545 if ((error
< LL_RMS_E
) ||
546 ((error
< lasterror
) && ((lasterror
- error
) < LL_RMS_D
))) {
547 return !0; /* good match */
551 /* move each vector to the barycenter of its closest colors */
552 for (j
= 0; j
< nv
; j
++) {
554 GLfloat div
= 1.0F
/ cnt
[j
];
555 for (i
= 0; i
< nc
; i
++) {
556 vec
[j
][i
] = div
* sum
[j
][i
];
559 /* this vec has no samples or is identical with a previous vec */
560 GLint worst
= fxt1_worst(vec
[j
], input
, nc
, n
);
561 for (i
= 0; i
< nc
; i
++) {
562 vec
[j
][i
] = input
[worst
][i
];
568 return 0; /* could not converge fast enough */
573 fxt1_quantize_CHROMA (GLuint
*cc
,
574 GLubyte input
[N_TEXELS
][MAX_COMP
])
576 const GLint n_vect
= 4; /* 4 base vectors to find */
577 const GLint n_comp
= 3; /* 3 components: R, G, B */
578 GLfloat vec
[MAX_VECT
][MAX_COMP
];
580 Fx64 hi
; /* high quadword */
581 GLuint lohi
, lolo
; /* low quadword: hi dword, lo dword */
583 if (fxt1_choose(vec
, n_vect
, input
, n_comp
, N_TEXELS
) != 0) {
584 fxt1_lloyd(vec
, n_vect
, input
, n_comp
, N_TEXELS
);
587 FX64_MOV32(hi
, 4); /* cc-chroma = "010" + unused bit */
588 for (j
= n_vect
- 1; j
>= 0; j
--) {
589 for (i
= 0; i
< n_comp
; i
++) {
592 FX64_OR32(hi
, (GLuint
)(vec
[j
][i
] / 8.0F
));
595 ((Fx64
*)cc
)[1] = hi
;
598 /* right microtile */
599 for (k
= N_TEXELS
- 1; k
>= N_TEXELS
/2; k
--) {
601 lohi
|= fxt1_bestcol(vec
, n_vect
, input
[k
], n_comp
);
604 for (; k
>= 0; k
--) {
606 lolo
|= fxt1_bestcol(vec
, n_vect
, input
[k
], n_comp
);
614 fxt1_quantize_ALPHA0 (GLuint
*cc
,
615 GLubyte input
[N_TEXELS
][MAX_COMP
],
616 GLubyte reord
[N_TEXELS
][MAX_COMP
], GLint n
)
618 const GLint n_vect
= 3; /* 3 base vectors to find */
619 const GLint n_comp
= 4; /* 4 components: R, G, B, A */
620 GLfloat vec
[MAX_VECT
][MAX_COMP
];
622 Fx64 hi
; /* high quadword */
623 GLuint lohi
, lolo
; /* low quadword: hi dword, lo dword */
625 /* the last vector indicates zero */
626 for (i
= 0; i
< n_comp
; i
++) {
630 /* the first n texels in reord are guaranteed to be non-zero */
631 if (fxt1_choose(vec
, n_vect
, reord
, n_comp
, n
) != 0) {
632 fxt1_lloyd(vec
, n_vect
, reord
, n_comp
, n
);
635 FX64_MOV32(hi
, 6); /* alpha = "011" + lerp = 0 */
636 for (j
= n_vect
- 1; j
>= 0; j
--) {
639 FX64_OR32(hi
, (GLuint
)(vec
[j
][ACOMP
] / 8.0F
));
641 for (j
= n_vect
- 1; j
>= 0; j
--) {
642 for (i
= 0; i
< n_comp
- 1; i
++) {
645 FX64_OR32(hi
, (GLuint
)(vec
[j
][i
] / 8.0F
));
648 ((Fx64
*)cc
)[1] = hi
;
651 /* right microtile */
652 for (k
= N_TEXELS
- 1; k
>= N_TEXELS
/2; k
--) {
654 lohi
|= fxt1_bestcol(vec
, n_vect
+ 1, input
[k
], n_comp
);
657 for (; k
>= 0; k
--) {
659 lolo
|= fxt1_bestcol(vec
, n_vect
+ 1, input
[k
], n_comp
);
667 fxt1_quantize_ALPHA1 (GLuint
*cc
,
668 GLubyte input
[N_TEXELS
][MAX_COMP
])
670 const GLint n_vect
= 3; /* highest vector number in each microtile */
671 const GLint n_comp
= 4; /* 4 components: R, G, B, A */
672 GLfloat vec
[1 + 1 + 1][MAX_COMP
]; /* 1.5 extrema for each sub-block */
673 GLfloat b
, iv
[MAX_COMP
]; /* interpolation vector */
675 Fx64 hi
; /* high quadword */
676 GLuint lohi
, lolo
; /* low quadword: hi dword, lo dword */
680 GLint minColL
= 0, maxColL
= 0;
681 GLint minColR
= 0, maxColR
= 0;
682 GLint sumL
= 0, sumR
= 0;
684 /* Our solution here is to find the darkest and brightest colors in
685 * the 4x4 tile and use those as the two representative colors.
686 * There are probably better algorithms to use (histogram-based).
689 while ((minColL
== maxColL
) && nn_comp
) {
690 minSum
= 2000; /* big enough */
691 maxSum
= -1; /* small enough */
692 for (k
= 0; k
< N_TEXELS
/ 2; k
++) {
694 for (i
= 0; i
< nn_comp
; i
++) {
712 while ((minColR
== maxColR
) && nn_comp
) {
713 minSum
= 2000; /* big enough */
714 maxSum
= -1; /* small enough */
715 for (k
= N_TEXELS
/ 2; k
< N_TEXELS
; k
++) {
717 for (i
= 0; i
< nn_comp
; i
++) {
734 /* choose the common vector (yuck!) */
737 GLint v1
= 0, v2
= 0;
738 GLfloat err
= 1e9
; /* big enough */
739 GLfloat tv
[2 * 2][MAX_COMP
]; /* 2 extrema for each sub-block */
740 for (i
= 0; i
< n_comp
; i
++) {
741 tv
[0][i
] = input
[minColL
][i
];
742 tv
[1][i
] = input
[maxColL
][i
];
743 tv
[2][i
] = input
[minColR
][i
];
744 tv
[3][i
] = input
[maxColR
][i
];
746 for (j1
= 0; j1
< 2; j1
++) {
747 for (j2
= 2; j2
< 4; j2
++) {
749 for (i
= 0; i
< n_comp
; i
++) {
750 e
+= (tv
[j1
][i
] - tv
[j2
][i
]) * (tv
[j1
][i
] - tv
[j2
][i
]);
759 for (i
= 0; i
< n_comp
; i
++) {
760 vec
[0][i
] = tv
[1 - v1
][i
];
761 vec
[1][i
] = (tv
[v1
][i
] * sumL
+ tv
[v2
][i
] * sumR
) / (sumL
+ sumR
);
762 vec
[2][i
] = tv
[5 - v2
][i
];
768 if (minColL
!= maxColL
) {
769 /* compute interpolation vector */
770 MAKEIVEC(n_vect
, n_comp
, iv
, b
, vec
[0], vec
[1]);
774 for (k
= N_TEXELS
/ 2 - 1; k
>= 0; k
--) {
776 /* interpolate color */
777 CALCCDOT(texel
, n_vect
, n_comp
, iv
, b
, input
[k
]);
786 /* right microtile */
788 if (minColR
!= maxColR
) {
789 /* compute interpolation vector */
790 MAKEIVEC(n_vect
, n_comp
, iv
, b
, vec
[2], vec
[1]);
794 for (k
= N_TEXELS
- 1; k
>= N_TEXELS
/ 2; k
--) {
796 /* interpolate color */
797 CALCCDOT(texel
, n_vect
, n_comp
, iv
, b
, input
[k
]);
806 FX64_MOV32(hi
, 7); /* alpha = "011" + lerp = 1 */
807 for (j
= n_vect
- 1; j
>= 0; j
--) {
810 FX64_OR32(hi
, (GLuint
)(vec
[j
][ACOMP
] / 8.0F
));
812 for (j
= n_vect
- 1; j
>= 0; j
--) {
813 for (i
= 0; i
< n_comp
- 1; i
++) {
816 FX64_OR32(hi
, (GLuint
)(vec
[j
][i
] / 8.0F
));
819 ((Fx64
*)cc
)[1] = hi
;
824 fxt1_quantize_HI (GLuint
*cc
,
825 GLubyte input
[N_TEXELS
][MAX_COMP
],
826 GLubyte reord
[N_TEXELS
][MAX_COMP
], GLint n
)
828 const GLint n_vect
= 6; /* highest vector number */
829 const GLint n_comp
= 3; /* 3 components: R, G, B */
830 GLfloat b
= 0.0F
; /* phoudoin: silent compiler! */
831 GLfloat iv
[MAX_COMP
]; /* interpolation vector */
833 GLuint hihi
; /* high quadword: hi dword */
835 GLint minSum
= 2000; /* big enough */
836 GLint maxSum
= -1; /* small enough */
837 GLint minCol
= 0; /* phoudoin: silent compiler! */
838 GLint maxCol
= 0; /* phoudoin: silent compiler! */
840 /* Our solution here is to find the darkest and brightest colors in
841 * the 8x4 tile and use those as the two representative colors.
842 * There are probably better algorithms to use (histogram-based).
844 for (k
= 0; k
< n
; k
++) {
846 for (i
= 0; i
< n_comp
; i
++) {
859 hihi
= 0; /* cc-hi = "00" */
860 for (i
= 0; i
< n_comp
; i
++) {
863 hihi
|= reord
[maxCol
][i
] >> 3;
865 for (i
= 0; i
< n_comp
; i
++) {
868 hihi
|= reord
[minCol
][i
] >> 3;
871 cc
[0] = cc
[1] = cc
[2] = 0;
873 /* compute interpolation vector */
874 if (minCol
!= maxCol
) {
875 MAKEIVEC(n_vect
, n_comp
, iv
, b
, reord
[minCol
], reord
[maxCol
]);
879 for (k
= N_TEXELS
- 1; k
>= 0; k
--) {
881 GLuint
*kk
= (GLuint
*)((char *)cc
+ t
/ 8);
882 GLint texel
= n_vect
+ 1; /* transparent black */
884 if (!ISTBLACK(input
[k
])) {
885 if (minCol
!= maxCol
) {
886 /* interpolate color */
887 CALCCDOT(texel
, n_vect
, n_comp
, iv
, b
, input
[k
]);
889 kk
[0] |= texel
<< (t
& 7);
893 kk
[0] |= texel
<< (t
& 7);
900 fxt1_quantize_MIXED1 (GLuint
*cc
,
901 GLubyte input
[N_TEXELS
][MAX_COMP
])
903 const GLint n_vect
= 2; /* highest vector number in each microtile */
904 const GLint n_comp
= 3; /* 3 components: R, G, B */
905 GLubyte vec
[2 * 2][MAX_COMP
]; /* 2 extrema for each sub-block */
906 GLfloat b
, iv
[MAX_COMP
]; /* interpolation vector */
908 Fx64 hi
; /* high quadword */
909 GLuint lohi
, lolo
; /* low quadword: hi dword, lo dword */
913 GLint minColL
= 0, maxColL
= -1;
914 GLint minColR
= 0, maxColR
= -1;
916 /* Our solution here is to find the darkest and brightest colors in
917 * the 4x4 tile and use those as the two representative colors.
918 * There are probably better algorithms to use (histogram-based).
920 minSum
= 2000; /* big enough */
921 maxSum
= -1; /* small enough */
922 for (k
= 0; k
< N_TEXELS
/ 2; k
++) {
923 if (!ISTBLACK(input
[k
])) {
925 for (i
= 0; i
< n_comp
; i
++) {
938 minSum
= 2000; /* big enough */
939 maxSum
= -1; /* small enough */
940 for (; k
< N_TEXELS
; k
++) {
941 if (!ISTBLACK(input
[k
])) {
943 for (i
= 0; i
< n_comp
; i
++) {
959 /* all transparent black */
961 for (i
= 0; i
< n_comp
; i
++) {
967 for (i
= 0; i
< n_comp
; i
++) {
968 vec
[0][i
] = input
[minColL
][i
];
969 vec
[1][i
] = input
[maxColL
][i
];
971 if (minColL
!= maxColL
) {
972 /* compute interpolation vector */
973 MAKEIVEC(n_vect
, n_comp
, iv
, b
, vec
[0], vec
[1]);
977 for (k
= N_TEXELS
/ 2 - 1; k
>= 0; k
--) {
978 GLint texel
= n_vect
+ 1; /* transparent black */
979 if (!ISTBLACK(input
[k
])) {
980 /* interpolate color */
981 CALCCDOT(texel
, n_vect
, n_comp
, iv
, b
, input
[k
]);
991 /* right microtile */
993 /* all transparent black */
995 for (i
= 0; i
< n_comp
; i
++) {
1001 for (i
= 0; i
< n_comp
; i
++) {
1002 vec
[2][i
] = input
[minColR
][i
];
1003 vec
[3][i
] = input
[maxColR
][i
];
1005 if (minColR
!= maxColR
) {
1006 /* compute interpolation vector */
1007 MAKEIVEC(n_vect
, n_comp
, iv
, b
, vec
[2], vec
[3]);
1011 for (k
= N_TEXELS
- 1; k
>= N_TEXELS
/ 2; k
--) {
1012 GLint texel
= n_vect
+ 1; /* transparent black */
1013 if (!ISTBLACK(input
[k
])) {
1014 /* interpolate color */
1015 CALCCDOT(texel
, n_vect
, n_comp
, iv
, b
, input
[k
]);
1025 FX64_MOV32(hi
, 9 | (vec
[3][GCOMP
] & 4) | ((vec
[1][GCOMP
] >> 1) & 2)); /* chroma = "1" */
1026 for (j
= 2 * 2 - 1; j
>= 0; j
--) {
1027 for (i
= 0; i
< n_comp
; i
++) {
1030 FX64_OR32(hi
, vec
[j
][i
] >> 3);
1033 ((Fx64
*)cc
)[1] = hi
;
1038 fxt1_quantize_MIXED0 (GLuint
*cc
,
1039 GLubyte input
[N_TEXELS
][MAX_COMP
])
1041 const GLint n_vect
= 3; /* highest vector number in each microtile */
1042 const GLint n_comp
= 3; /* 3 components: R, G, B */
1043 GLubyte vec
[2 * 2][MAX_COMP
]; /* 2 extrema for each sub-block */
1044 GLfloat b
, iv
[MAX_COMP
]; /* interpolation vector */
1046 Fx64 hi
; /* high quadword */
1047 GLuint lohi
, lolo
; /* low quadword: hi dword, lo dword */
1049 GLint minColL
= 0, maxColL
= 0;
1050 GLint minColR
= 0, maxColR
= 0;
1055 /* Our solution here is to find the darkest and brightest colors in
1056 * the 4x4 tile and use those as the two representative colors.
1057 * There are probably better algorithms to use (histogram-based).
1059 minSum
= 2000; /* big enough */
1060 maxSum
= -1; /* small enough */
1061 for (k
= 0; k
< N_TEXELS
/ 2; k
++) {
1063 for (i
= 0; i
< n_comp
; i
++) {
1075 minSum
= 2000; /* big enough */
1076 maxSum
= -1; /* small enough */
1077 for (; k
< N_TEXELS
; k
++) {
1079 for (i
= 0; i
< n_comp
; i
++) {
1094 GLint maxVarL
= fxt1_variance(NULL
, input
, n_comp
, N_TEXELS
/ 2);
1095 GLint maxVarR
= fxt1_variance(NULL
, &input
[N_TEXELS
/ 2], n_comp
, N_TEXELS
/ 2);
1097 /* Scan the channel with max variance for lo & hi
1098 * and use those as the two representative colors.
1100 minVal
= 2000; /* big enough */
1101 maxVal
= -1; /* small enough */
1102 for (k
= 0; k
< N_TEXELS
/ 2; k
++) {
1103 GLint t
= input
[k
][maxVarL
];
1113 minVal
= 2000; /* big enough */
1114 maxVal
= -1; /* small enough */
1115 for (; k
< N_TEXELS
; k
++) {
1116 GLint t
= input
[k
][maxVarR
];
1128 /* left microtile */
1130 for (i
= 0; i
< n_comp
; i
++) {
1131 vec
[0][i
] = input
[minColL
][i
];
1132 vec
[1][i
] = input
[maxColL
][i
];
1134 if (minColL
!= maxColL
) {
1135 /* compute interpolation vector */
1136 MAKEIVEC(n_vect
, n_comp
, iv
, b
, vec
[0], vec
[1]);
1140 for (k
= N_TEXELS
/ 2 - 1; k
>= 0; k
--) {
1142 /* interpolate color */
1143 CALCCDOT(texel
, n_vect
, n_comp
, iv
, b
, input
[k
]);
1149 /* funky encoding for LSB of green */
1150 if ((GLint
)((lolo
>> 1) & 1) != (((vec
[1][GCOMP
] ^ vec
[0][GCOMP
]) >> 2) & 1)) {
1151 for (i
= 0; i
< n_comp
; i
++) {
1152 vec
[1][i
] = input
[minColL
][i
];
1153 vec
[0][i
] = input
[maxColL
][i
];
1161 /* right microtile */
1163 for (i
= 0; i
< n_comp
; i
++) {
1164 vec
[2][i
] = input
[minColR
][i
];
1165 vec
[3][i
] = input
[maxColR
][i
];
1167 if (minColR
!= maxColR
) {
1168 /* compute interpolation vector */
1169 MAKEIVEC(n_vect
, n_comp
, iv
, b
, vec
[2], vec
[3]);
1173 for (k
= N_TEXELS
- 1; k
>= N_TEXELS
/ 2; k
--) {
1175 /* interpolate color */
1176 CALCCDOT(texel
, n_vect
, n_comp
, iv
, b
, input
[k
]);
1182 /* funky encoding for LSB of green */
1183 if ((GLint
)((lohi
>> 1) & 1) != (((vec
[3][GCOMP
] ^ vec
[2][GCOMP
]) >> 2) & 1)) {
1184 for (i
= 0; i
< n_comp
; i
++) {
1185 vec
[3][i
] = input
[minColR
][i
];
1186 vec
[2][i
] = input
[maxColR
][i
];
1194 FX64_MOV32(hi
, 8 | (vec
[3][GCOMP
] & 4) | ((vec
[1][GCOMP
] >> 1) & 2)); /* chroma = "1" */
1195 for (j
= 2 * 2 - 1; j
>= 0; j
--) {
1196 for (i
= 0; i
< n_comp
; i
++) {
1199 FX64_OR32(hi
, vec
[j
][i
] >> 3);
1202 ((Fx64
*)cc
)[1] = hi
;
1207 fxt1_quantize (GLuint
*cc
, const GLubyte
*lines
[], GLint comps
)
1210 GLubyte reord
[N_TEXELS
][MAX_COMP
];
1212 GLubyte input
[N_TEXELS
][MAX_COMP
];
1216 /* make the whole block opaque */
1217 memset(input
, -1, sizeof(input
));
1220 /* 8 texels each line */
1221 for (l
= 0; l
< 4; l
++) {
1222 for (k
= 0; k
< 4; k
++) {
1223 for (i
= 0; i
< comps
; i
++) {
1224 input
[k
+ l
* 4][i
] = *lines
[l
]++;
1227 for (; k
< 8; k
++) {
1228 for (i
= 0; i
< comps
; i
++) {
1229 input
[k
+ l
* 4 + 12][i
] = *lines
[l
]++;
1235 * 00, 01, 02, 03, 08, 09, 0a, 0b
1236 * 10, 11, 12, 13, 18, 19, 1a, 1b
1237 * 04, 05, 06, 07, 0c, 0d, 0e, 0f
1238 * 14, 15, 16, 17, 1c, 1d, 1e, 1f
1242 * stupidity flows forth from this
1247 /* skip all transparent black texels */
1249 for (k
= 0; k
< N_TEXELS
; k
++) {
1250 /* test all components against 0 */
1251 if (!ISTBLACK(input
[k
])) {
1252 /* texel is not transparent black */
1253 COPY_4UBV(reord
[l
], input
[k
]);
1254 if (reord
[l
][ACOMP
] < (255 - ALPHA_TS
)) {
1255 /* non-opaque texel */
1265 fxt1_quantize_ALPHA0(cc
, input
, reord
, l
);
1266 } else if (l
== 0) {
1267 cc
[0] = cc
[1] = cc
[2] = -1;
1269 } else if (l
< N_TEXELS
) {
1270 fxt1_quantize_HI(cc
, input
, reord
, l
);
1272 fxt1_quantize_CHROMA(cc
, input
);
1274 (void)fxt1_quantize_ALPHA1
;
1275 (void)fxt1_quantize_MIXED1
;
1276 (void)fxt1_quantize_MIXED0
;
1279 fxt1_quantize_ALPHA1(cc
, input
);
1280 } else if (l
== 0) {
1281 cc
[0] = cc
[1] = cc
[2] = ~0u;
1283 } else if (l
< N_TEXELS
) {
1284 fxt1_quantize_MIXED1(cc
, input
);
1286 fxt1_quantize_MIXED0(cc
, input
);
1288 (void)fxt1_quantize_ALPHA0
;
1289 (void)fxt1_quantize_HI
;
1290 (void)fxt1_quantize_CHROMA
;
1297 * Upscale an image by replication, not (typical) stretching.
1298 * We use this when the image width or height is less than a
1299 * certain size (4, 8) and we need to upscale an image.
1302 upscale_teximage2d(GLsizei inWidth
, GLsizei inHeight
,
1303 GLsizei outWidth
, GLsizei outHeight
,
1304 GLint comps
, const GLubyte
*src
, GLint srcRowStride
,
1309 ASSERT(outWidth
>= inWidth
);
1310 ASSERT(outHeight
>= inHeight
);
1312 ASSERT(inWidth
== 1 || inWidth
== 2 || inHeight
== 1 || inHeight
== 2);
1313 ASSERT((outWidth
& 3) == 0);
1314 ASSERT((outHeight
& 3) == 0);
1317 for (i
= 0; i
< outHeight
; i
++) {
1318 const GLint ii
= i
% inHeight
;
1319 for (j
= 0; j
< outWidth
; j
++) {
1320 const GLint jj
= j
% inWidth
;
1321 for (k
= 0; k
< comps
; k
++) {
1322 dest
[(i
* outWidth
+ j
) * comps
+ k
]
1323 = src
[ii
* srcRowStride
+ jj
* comps
+ k
];
1331 fxt1_encode (GLuint width
, GLuint height
, GLint comps
,
1332 const void *source
, GLint srcRowStride
,
1333 void *dest
, GLint destRowStride
)
1336 const GLubyte
*data
;
1337 GLuint
*encoded
= (GLuint
*)dest
;
1338 void *newSource
= NULL
;
1340 assert(comps
== 3 || comps
== 4);
1342 /* Replicate image if width is not M8 or height is not M4 */
1343 if ((width
& 7) | (height
& 3)) {
1344 GLint newWidth
= (width
+ 7) & ~7;
1345 GLint newHeight
= (height
+ 3) & ~3;
1346 newSource
= malloc(comps
* newWidth
* newHeight
* sizeof(GLubyte
));
1348 GET_CURRENT_CONTEXT(ctx
);
1349 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "texture compression");
1352 upscale_teximage2d(width
, height
, newWidth
, newHeight
,
1353 comps
, (const GLubyte
*) source
,
1354 srcRowStride
, (GLubyte
*) newSource
);
1358 srcRowStride
= comps
* newWidth
;
1361 data
= (const GLubyte
*) source
;
1362 destRowStride
= (destRowStride
- width
* 2) / 4;
1363 for (y
= 0; y
< height
; y
+= 4) {
1364 GLuint offs
= 0 + (y
+ 0) * srcRowStride
;
1365 for (x
= 0; x
< width
; x
+= 8) {
1366 const GLubyte
*lines
[4];
1367 lines
[0] = &data
[offs
];
1368 lines
[1] = lines
[0] + srcRowStride
;
1369 lines
[2] = lines
[1] + srcRowStride
;
1370 lines
[3] = lines
[2] + srcRowStride
;
1372 fxt1_quantize(encoded
, lines
, comps
);
1373 /* 128 bits per 8x4 block */
1376 encoded
+= destRowStride
;
1380 if (newSource
!= NULL
) {
1386 /***************************************************************************\
1389 * The decoder is based on GL_3DFX_texture_compression_FXT1
1390 * specification and serves as a concept for the encoder.
1391 \***************************************************************************/
1394 /* lookup table for scaling 5 bit colors up to 8 bits */
1395 static const GLubyte _rgb_scale_5
[] = {
1396 0, 8, 16, 25, 33, 41, 49, 58,
1397 66, 74, 82, 90, 99, 107, 115, 123,
1398 132, 140, 148, 156, 165, 173, 181, 189,
1399 197, 206, 214, 222, 230, 239, 247, 255
1402 /* lookup table for scaling 6 bit colors up to 8 bits */
1403 static const GLubyte _rgb_scale_6
[] = {
1404 0, 4, 8, 12, 16, 20, 24, 28,
1405 32, 36, 40, 45, 49, 53, 57, 61,
1406 65, 69, 73, 77, 81, 85, 89, 93,
1407 97, 101, 105, 109, 113, 117, 121, 125,
1408 130, 134, 138, 142, 146, 150, 154, 158,
1409 162, 166, 170, 174, 178, 182, 186, 190,
1410 194, 198, 202, 206, 210, 215, 219, 223,
1411 227, 231, 235, 239, 243, 247, 251, 255
1415 #define CC_SEL(cc, which) (((GLuint *)(cc))[(which) / 32] >> ((which) & 31))
1416 #define UP5(c) _rgb_scale_5[(c) & 31]
1417 #define UP6(c, b) _rgb_scale_6[(((c) & 31) << 1) | ((b) & 1)]
1418 #define LERP(n, t, c0, c1) (((n) - (t)) * (c0) + (t) * (c1) + (n) / 2) / (n)
1422 fxt1_decode_1HI (const GLubyte
*code
, GLint t
, GLubyte
*rgba
)
1427 cc
= (const GLuint
*)(code
+ t
/ 8);
1428 t
= (cc
[0] >> (t
& 7)) & 7;
1431 rgba
[RCOMP
] = rgba
[GCOMP
] = rgba
[BCOMP
] = rgba
[ACOMP
] = 0;
1434 cc
= (const GLuint
*)(code
+ 12);
1436 b
= UP5(CC_SEL(cc
, 0));
1437 g
= UP5(CC_SEL(cc
, 5));
1438 r
= UP5(CC_SEL(cc
, 10));
1439 } else if (t
== 6) {
1440 b
= UP5(CC_SEL(cc
, 15));
1441 g
= UP5(CC_SEL(cc
, 20));
1442 r
= UP5(CC_SEL(cc
, 25));
1444 b
= LERP(6, t
, UP5(CC_SEL(cc
, 0)), UP5(CC_SEL(cc
, 15)));
1445 g
= LERP(6, t
, UP5(CC_SEL(cc
, 5)), UP5(CC_SEL(cc
, 20)));
1446 r
= LERP(6, t
, UP5(CC_SEL(cc
, 10)), UP5(CC_SEL(cc
, 25)));
1457 fxt1_decode_1CHROMA (const GLubyte
*code
, GLint t
, GLubyte
*rgba
)
1462 cc
= (const GLuint
*)code
;
1467 t
= (cc
[0] >> (t
* 2)) & 3;
1470 cc
= (const GLuint
*)(code
+ 8 + t
/ 8);
1471 kk
= cc
[0] >> (t
& 7);
1472 rgba
[BCOMP
] = UP5(kk
);
1473 rgba
[GCOMP
] = UP5(kk
>> 5);
1474 rgba
[RCOMP
] = UP5(kk
>> 10);
1480 fxt1_decode_1MIXED (const GLubyte
*code
, GLint t
, GLubyte
*rgba
)
1486 cc
= (const GLuint
*)code
;
1489 t
= (cc
[1] >> (t
* 2)) & 3;
1491 col
[0][BCOMP
] = (*(const GLuint
*)(code
+ 11)) >> 6;
1492 col
[0][GCOMP
] = CC_SEL(cc
, 99);
1493 col
[0][RCOMP
] = CC_SEL(cc
, 104);
1495 col
[1][BCOMP
] = CC_SEL(cc
, 109);
1496 col
[1][GCOMP
] = CC_SEL(cc
, 114);
1497 col
[1][RCOMP
] = CC_SEL(cc
, 119);
1498 glsb
= CC_SEL(cc
, 126);
1499 selb
= CC_SEL(cc
, 33);
1501 t
= (cc
[0] >> (t
* 2)) & 3;
1503 col
[0][BCOMP
] = CC_SEL(cc
, 64);
1504 col
[0][GCOMP
] = CC_SEL(cc
, 69);
1505 col
[0][RCOMP
] = CC_SEL(cc
, 74);
1507 col
[1][BCOMP
] = CC_SEL(cc
, 79);
1508 col
[1][GCOMP
] = CC_SEL(cc
, 84);
1509 col
[1][RCOMP
] = CC_SEL(cc
, 89);
1510 glsb
= CC_SEL(cc
, 125);
1511 selb
= CC_SEL(cc
, 1);
1514 if (CC_SEL(cc
, 124) & 1) {
1519 rgba
[RCOMP
] = rgba
[BCOMP
] = rgba
[GCOMP
] = rgba
[ACOMP
] = 0;
1523 b
= UP5(col
[0][BCOMP
]);
1524 g
= UP5(col
[0][GCOMP
]);
1525 r
= UP5(col
[0][RCOMP
]);
1526 } else if (t
== 2) {
1527 b
= UP5(col
[1][BCOMP
]);
1528 g
= UP6(col
[1][GCOMP
], glsb
);
1529 r
= UP5(col
[1][RCOMP
]);
1531 b
= (UP5(col
[0][BCOMP
]) + UP5(col
[1][BCOMP
])) / 2;
1532 g
= (UP5(col
[0][GCOMP
]) + UP6(col
[1][GCOMP
], glsb
)) / 2;
1533 r
= (UP5(col
[0][RCOMP
]) + UP5(col
[1][RCOMP
])) / 2;
1544 b
= UP5(col
[0][BCOMP
]);
1545 g
= UP6(col
[0][GCOMP
], glsb
^ selb
);
1546 r
= UP5(col
[0][RCOMP
]);
1547 } else if (t
== 3) {
1548 b
= UP5(col
[1][BCOMP
]);
1549 g
= UP6(col
[1][GCOMP
], glsb
);
1550 r
= UP5(col
[1][RCOMP
]);
1552 b
= LERP(3, t
, UP5(col
[0][BCOMP
]), UP5(col
[1][BCOMP
]));
1553 g
= LERP(3, t
, UP6(col
[0][GCOMP
], glsb
^ selb
),
1554 UP6(col
[1][GCOMP
], glsb
));
1555 r
= LERP(3, t
, UP5(col
[0][RCOMP
]), UP5(col
[1][RCOMP
]));
1566 fxt1_decode_1ALPHA (const GLubyte
*code
, GLint t
, GLubyte
*rgba
)
1571 cc
= (const GLuint
*)code
;
1572 if (CC_SEL(cc
, 124) & 1) {
1578 t
= (cc
[1] >> (t
* 2)) & 3;
1580 col0
[BCOMP
] = (*(const GLuint
*)(code
+ 11)) >> 6;
1581 col0
[GCOMP
] = CC_SEL(cc
, 99);
1582 col0
[RCOMP
] = CC_SEL(cc
, 104);
1583 col0
[ACOMP
] = CC_SEL(cc
, 119);
1585 t
= (cc
[0] >> (t
* 2)) & 3;
1587 col0
[BCOMP
] = CC_SEL(cc
, 64);
1588 col0
[GCOMP
] = CC_SEL(cc
, 69);
1589 col0
[RCOMP
] = CC_SEL(cc
, 74);
1590 col0
[ACOMP
] = CC_SEL(cc
, 109);
1594 b
= UP5(col0
[BCOMP
]);
1595 g
= UP5(col0
[GCOMP
]);
1596 r
= UP5(col0
[RCOMP
]);
1597 a
= UP5(col0
[ACOMP
]);
1598 } else if (t
== 3) {
1599 b
= UP5(CC_SEL(cc
, 79));
1600 g
= UP5(CC_SEL(cc
, 84));
1601 r
= UP5(CC_SEL(cc
, 89));
1602 a
= UP5(CC_SEL(cc
, 114));
1604 b
= LERP(3, t
, UP5(col0
[BCOMP
]), UP5(CC_SEL(cc
, 79)));
1605 g
= LERP(3, t
, UP5(col0
[GCOMP
]), UP5(CC_SEL(cc
, 84)));
1606 r
= LERP(3, t
, UP5(col0
[RCOMP
]), UP5(CC_SEL(cc
, 89)));
1607 a
= LERP(3, t
, UP5(col0
[ACOMP
]), UP5(CC_SEL(cc
, 114)));
1616 t
= (cc
[0] >> (t
* 2)) & 3;
1623 cc
= (const GLuint
*)code
;
1624 a
= UP5(cc
[3] >> (t
* 5 + 13));
1626 cc
= (const GLuint
*)(code
+ 8 + t
/ 8);
1627 kk
= cc
[0] >> (t
& 7);
1641 fxt1_decode_1 (const void *texture
, GLint stride
, /* in pixels */
1642 GLint i
, GLint j
, GLubyte
*rgba
)
1644 static void (*decode_1
[]) (const GLubyte
*, GLint
, GLubyte
*) = {
1645 fxt1_decode_1HI
, /* cc-high = "00?" */
1646 fxt1_decode_1HI
, /* cc-high = "00?" */
1647 fxt1_decode_1CHROMA
, /* cc-chroma = "010" */
1648 fxt1_decode_1ALPHA
, /* alpha = "011" */
1649 fxt1_decode_1MIXED
, /* mixed = "1??" */
1650 fxt1_decode_1MIXED
, /* mixed = "1??" */
1651 fxt1_decode_1MIXED
, /* mixed = "1??" */
1652 fxt1_decode_1MIXED
/* mixed = "1??" */
1655 const GLubyte
*code
= (const GLubyte
*)texture
+
1656 ((j
/ 4) * (stride
/ 8) + (i
/ 8)) * 16;
1657 GLint mode
= CC_SEL(code
, 125);
1665 decode_1
[mode
](code
, t
, rgba
);
1669 #endif /* FEATURE_texture_fxt1 */