1 | /* $Id: write.c,v 1.2 2001-09-05 14:30:48 bird Exp $ */
|
---|
2 | /*
|
---|
3 | ** THIS SOFTWARE IS SUBJECT TO COPYRIGHT PROTECTION AND IS OFFERED ONLY
|
---|
4 | ** PURSUANT TO THE 3DFX GLIDE GENERAL PUBLIC LICENSE. THERE IS NO RIGHT
|
---|
5 | ** TO USE THE GLIDE TRADEMARK WITHOUT PRIOR WRITTEN PERMISSION OF 3DFX
|
---|
6 | ** INTERACTIVE, INC. A COPY OF THIS LICENSE MAY BE OBTAINED FROM THE
|
---|
7 | ** DISTRIBUTOR OR BY CONTACTING 3DFX INTERACTIVE INC(info@3dfx.com).
|
---|
8 | ** THIS PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
|
---|
9 | ** EXPRESSED OR IMPLIED. SEE THE 3DFX GLIDE GENERAL PUBLIC LICENSE FOR A
|
---|
10 | ** FULL TEXT OF THE NON-WARRANTY PROVISIONS.
|
---|
11 | **
|
---|
12 | ** USE, DUPLICATION OR DISCLOSURE BY THE GOVERNMENT IS SUBJECT TO
|
---|
13 | ** RESTRICTIONS AS SET FORTH IN SUBDIVISION (C)(1)(II) OF THE RIGHTS IN
|
---|
14 | ** TECHNICAL DATA AND COMPUTER SOFTWARE CLAUSE AT DFARS 252.227-7013,
|
---|
15 | ** AND/OR IN SIMILAR OR SUCCESSOR CLAUSES IN THE FAR, DOD OR NASA FAR
|
---|
16 | ** SUPPLEMENT. UNPUBLISHED RIGHTS RESERVED UNDER THE COPYRIGHT LAWS OF
|
---|
17 | ** THE UNITED STATES.
|
---|
18 | **
|
---|
19 | ** COPYRIGHT 3DFX INTERACTIVE, INC. 1999, ALL RIGHTS RESERVED
|
---|
20 | */
|
---|
21 |
|
---|
22 | #include <stdio.h>
|
---|
23 | #include <stdlib.h>
|
---|
24 | #include <string.h>
|
---|
25 | #include <math.h>
|
---|
26 |
|
---|
27 | #include "texusint.h"
|
---|
28 |
|
---|
29 | /*
|
---|
30 | * The only two file formats we can write are: 3df and tga.
|
---|
31 | */
|
---|
32 | static char *Version = "1.1";
|
---|
33 | static char* aspect_names[] = { "8 1", "4 1", "2 1", "1 1",
|
---|
34 | "1 2", "1 4", "1 8" };
|
---|
35 |
|
---|
36 | /*************************************** tga files ****************************/
|
---|
37 | /*
|
---|
38 | * Write a tga file from an ARGB8888 mipmap.
|
---|
39 | */
|
---|
40 | static FxBool
|
---|
41 | txWriteTGA( FILE *stream, TxMip *txMip)
|
---|
42 | {
|
---|
43 |
|
---|
44 | struct {
|
---|
45 | FxU8 IDLength;
|
---|
46 | FxU8 ColorMapType;
|
---|
47 | FxU8 ImgType;
|
---|
48 | FxU8 CMapStartLo;
|
---|
49 | FxU8 CMapStartHi;
|
---|
50 | FxU8 CMapLengthLo;
|
---|
51 | FxU8 CMapLengthHi;
|
---|
52 | FxU8 CMapDepth;
|
---|
53 | FxU8 XOffSetLo;
|
---|
54 | FxU8 XOffSetHi;
|
---|
55 | FxU8 YOffSetLo;
|
---|
56 | FxU8 YOffSetHi;
|
---|
57 | FxU8 WidthLo;
|
---|
58 | FxU8 WidthHi;
|
---|
59 | FxU8 HeightLo;
|
---|
60 | FxU8 HeightHi;
|
---|
61 | FxU8 PixelDepth;
|
---|
62 | FxU8 ImageDescriptor;
|
---|
63 | } tgaHeader;
|
---|
64 |
|
---|
65 | FxU8 *data, *p;
|
---|
66 | FxU32 *data32;
|
---|
67 | int outW, outH, w, h, i;
|
---|
68 |
|
---|
69 | if (txMip->format != GR_TEXFMT_ARGB_8888) {
|
---|
70 | txPanic("TGA Image: Write: input format must be ARGB8888.");
|
---|
71 | }
|
---|
72 | if ( stream == NULL ) {
|
---|
73 | txPanic("Bad file handle");
|
---|
74 | return FXFALSE;
|
---|
75 | }
|
---|
76 |
|
---|
77 | outW = txMip->width;
|
---|
78 | outH = txMip->height;
|
---|
79 | if (txMip->depth > 1) outW += outW/2;
|
---|
80 |
|
---|
81 | tgaHeader.IDLength = 0;
|
---|
82 | tgaHeader.ColorMapType = 0;
|
---|
83 | tgaHeader.ImgType = 0x2;
|
---|
84 | tgaHeader.CMapStartLo = 0;
|
---|
85 | tgaHeader.CMapStartHi = 0;
|
---|
86 | tgaHeader.CMapLengthLo = 0;
|
---|
87 | tgaHeader.CMapLengthHi = 0;
|
---|
88 | tgaHeader.CMapDepth = 0;
|
---|
89 | tgaHeader.XOffSetLo = 0;
|
---|
90 | tgaHeader.XOffSetHi = 0;
|
---|
91 | tgaHeader.YOffSetLo = 0;
|
---|
92 | tgaHeader.YOffSetHi = 0;
|
---|
93 | tgaHeader.WidthHi = (FxU8)((outW >> 8) & 0xFF);
|
---|
94 | tgaHeader.WidthLo = (FxU8) (outW & 0xFF);
|
---|
95 | tgaHeader.HeightHi = (FxU8)((outH >> 8) & 0xFF);
|
---|
96 | tgaHeader.HeightLo = (FxU8) (outH & 0xFF);
|
---|
97 | tgaHeader.PixelDepth = 32;
|
---|
98 | tgaHeader.ImageDescriptor = 0x20; // image always right side up.
|
---|
99 |
|
---|
100 |
|
---|
101 | if ( fwrite( &tgaHeader, 1, 18, stream ) != 18 ) {
|
---|
102 | txPanic("TGA Header stream write error");
|
---|
103 | return FXFALSE;
|
---|
104 | }
|
---|
105 |
|
---|
106 | /*
|
---|
107 | * Allocate memory to hold all mipmaps, and copy the mipmaps.
|
---|
108 | */
|
---|
109 | p = data = txMalloc(outW * outH * 4);
|
---|
110 | memset(data, 0, outW * outH * 4);
|
---|
111 |
|
---|
112 | /* Copy level 0 into malloc'd area */
|
---|
113 | txRectCopy( data, outW * 4, txMip->data[0], txMip->width * 4,
|
---|
114 | txMip->width * 4, txMip->height);
|
---|
115 |
|
---|
116 | p += (txMip->width * 4);
|
---|
117 |
|
---|
118 | /* Copy the rest of the levels to the right of level 0 */
|
---|
119 | w = txMip->width;
|
---|
120 | h = txMip->height;
|
---|
121 | for (i=1; i< txMip->depth; i++) {
|
---|
122 | // printf("Copying: level = %d\n", i);
|
---|
123 | if (w > 1) w >>= 1;
|
---|
124 | if (h > 1) h >>= 1;
|
---|
125 | txRectCopy(p, outW * 4, txMip->data[i], w * 4, w * 4, h);
|
---|
126 | p += ( h * outW * 4);
|
---|
127 | }
|
---|
128 |
|
---|
129 | /* Write out the data */
|
---|
130 | data32 = (FxU32 *) data;
|
---|
131 | for (i=outW*outH; i; i--) {
|
---|
132 | putc(((*data32 ) & 0xff) , stream);
|
---|
133 | putc(((*data32 >> 8) & 0xff) , stream);
|
---|
134 | putc(((*data32 >> 16) & 0xff) , stream);
|
---|
135 | putc(((*data32 >> 24) & 0xff) , stream);
|
---|
136 | data32++;
|
---|
137 | }
|
---|
138 | return FXTRUE;
|
---|
139 | }
|
---|
140 |
|
---|
141 |
|
---|
142 | /*************************************** 3df files ****************************/
|
---|
143 | /* Write word, msb first */
|
---|
144 |
|
---|
145 | static FxBool
|
---|
146 | _txWrite16 (FILE *stream, FxU16 data)
|
---|
147 | {
|
---|
148 | FxU8 byte[2];
|
---|
149 |
|
---|
150 | byte[0] = (FxU8) ((data >> 8) & 0xFF);
|
---|
151 | byte[1] = (FxU8) ((data ) & 0xFF);
|
---|
152 |
|
---|
153 | return (fwrite (byte, 2, 1, stream) != 1) ? FXFALSE : FXTRUE;
|
---|
154 | }
|
---|
155 |
|
---|
156 | /* Write long word, msb first */
|
---|
157 |
|
---|
158 | static FxBool
|
---|
159 | _txWrite32 (FILE *stream, FxU32 data)
|
---|
160 | {
|
---|
161 | FxU8 byte[4];
|
---|
162 |
|
---|
163 | byte[0] = (FxU8) ((data >> 24) & 0xFF);
|
---|
164 | byte[1] = (FxU8) ((data >> 16) & 0xFF);
|
---|
165 | byte[2] = (FxU8) ((data >> 8) & 0xFF);
|
---|
166 | byte[3] = (FxU8) ((data ) & 0xFF);
|
---|
167 |
|
---|
168 | return (fwrite (byte, 4, 1, stream) != 1) ? FXFALSE : FXTRUE;
|
---|
169 | }
|
---|
170 |
|
---|
171 | /* Write NCC table */
|
---|
172 | static FxBool
|
---|
173 | _txWrite3dfNCCTable (FILE *stream, FxU32 *yab)
|
---|
174 | {
|
---|
175 | int i;
|
---|
176 |
|
---|
177 | for (i = 0; i < 16; i++)
|
---|
178 | if (!_txWrite16 (stream, (FxU16) (yab[i] & 0x00ff))) return FXFALSE;
|
---|
179 |
|
---|
180 | for (i = 0; i < 12; i++)
|
---|
181 | if (!_txWrite16 (stream, (FxU16) (yab[16+i] & 0xffff))) return FXFALSE;
|
---|
182 |
|
---|
183 | for (i = 0; i < 12; i++)
|
---|
184 | if (!_txWrite16 (stream, (FxU16) (yab[28+i] & 0xffff))) return FXFALSE;
|
---|
185 |
|
---|
186 | return FXTRUE;
|
---|
187 | }
|
---|
188 |
|
---|
189 | static FxBool
|
---|
190 | _txWrite3dfPalTable (FILE *stream, FxU32 *pal)
|
---|
191 | {
|
---|
192 | int i;
|
---|
193 |
|
---|
194 | for (i=0; i<256; i++) {
|
---|
195 | if (!_txWrite32 (stream, pal[i])) return FXFALSE;
|
---|
196 | }
|
---|
197 | return FXTRUE;
|
---|
198 | }
|
---|
199 |
|
---|
200 | static FxBool
|
---|
201 | txWrite3df (FILE *stream, TxMip *txMip)
|
---|
202 | {
|
---|
203 | FxU32 i;
|
---|
204 | FxU32 n_pixels;
|
---|
205 | int small_lod, large_lod, aspect;
|
---|
206 |
|
---|
207 | /* Write out the header */
|
---|
208 | large_lod = (txMip->width > txMip->height) ? txMip->width : txMip->height;
|
---|
209 | small_lod = large_lod >> (txMip->depth - 1);
|
---|
210 | aspect = txAspectRatio(txMip->width, txMip->height);
|
---|
211 |
|
---|
212 | // printf("Format = %d\n", txMip->format);
|
---|
213 | // printf("Format = %s\n", Format_Name[txMip->format]);
|
---|
214 |
|
---|
215 | // printf("Writing header...\n");
|
---|
216 | if (EOF == fprintf (stream,
|
---|
217 | "3df v%s\n%s\nlod range: %d %d\naspect ratio: %s\n",
|
---|
218 | Version,
|
---|
219 | Format_Name[txMip->format],
|
---|
220 | small_lod,
|
---|
221 | large_lod,
|
---|
222 | aspect_names[aspect])) return FXFALSE;
|
---|
223 |
|
---|
224 | /* write out ncc table if necessary */
|
---|
225 | // printf("Writing NCC...\n");
|
---|
226 | if ((txMip->format == GR_TEXFMT_YIQ_422) ||
|
---|
227 | (txMip->format == GR_TEXFMT_AYIQ_8422)) {
|
---|
228 | if (!_txWrite3dfNCCTable (stream, txMip->pal)) return FXFALSE;
|
---|
229 | }
|
---|
230 |
|
---|
231 | else if ((txMip->format == GR_TEXFMT_P_8) ||
|
---|
232 | (txMip->format == GR_TEXFMT_AP_88)) {
|
---|
233 | if (!_txWrite3dfPalTable (stream, txMip->pal)) return FXFALSE;
|
---|
234 | }
|
---|
235 |
|
---|
236 |
|
---|
237 | /* write out mipmap image data */
|
---|
238 | // printf("Writing mipmaps (%d bytes)...\n", txMip->size);
|
---|
239 | if (txMip->format < GR_TEXFMT_16BIT) {
|
---|
240 | n_pixels = txMip->size;
|
---|
241 | if (n_pixels != fwrite (txMip->data[0], 1, n_pixels, stream)) {
|
---|
242 | printf("Bad Bad Bad!\n");
|
---|
243 | return FXFALSE;
|
---|
244 | }
|
---|
245 | }
|
---|
246 | else if (txMip->format < GR_TEXFMT_32BIT) {
|
---|
247 | FxU16* data = (FxU16 *) txMip->data[0];
|
---|
248 | n_pixels = txMip->size >> 1;
|
---|
249 |
|
---|
250 | for (i = 0; i < n_pixels; i ++)
|
---|
251 | if (FXFALSE == _txWrite16 (stream, *data++)) return FXFALSE;
|
---|
252 | }
|
---|
253 | else {
|
---|
254 | FxU32* data = (FxU32*) txMip->data[0];
|
---|
255 | n_pixels = txMip->size >> 2;
|
---|
256 |
|
---|
257 | for (i = 0; i < n_pixels; i ++)
|
---|
258 | if (FXFALSE == _txWrite32 (stream, *data++)) return FXFALSE;
|
---|
259 | }
|
---|
260 | return FXTRUE;
|
---|
261 | }
|
---|
262 |
|
---|
263 | void
|
---|
264 | txMipWrite(TxMip *txMip, char *file, char *ext, int split)
|
---|
265 | {
|
---|
266 | int tgaformat;
|
---|
267 | FILE *stream;
|
---|
268 | char filename[128];
|
---|
269 | int i, w, h;
|
---|
270 | TxMip splitImg;
|
---|
271 |
|
---|
272 | if ((txMip->width & (txMip->width - 1)) ||
|
---|
273 | (txMip->height & (txMip->height - 1))) {
|
---|
274 | txPanic("txMipWrite: size not power of 2!");
|
---|
275 | }
|
---|
276 |
|
---|
277 | if (strcmp(ext, ".tga") && strcmp(ext, ".3df")) {
|
---|
278 | txPanic("txMipWrite: Bad output format");
|
---|
279 | }
|
---|
280 |
|
---|
281 | tgaformat = (strcmp(ext, ".tga") == 0);
|
---|
282 | if (tgaformat && (txMip->format != GR_TEXFMT_ARGB_8888)) {
|
---|
283 | txPanic("txMipWrite: TGA format must be ARGB_8888");
|
---|
284 | }
|
---|
285 |
|
---|
286 | /* If not split, write out a single file */
|
---|
287 | if (!split) {
|
---|
288 | strcpy(filename, file);
|
---|
289 | strcat(filename, ext);
|
---|
290 | if( txVerbose )
|
---|
291 | {
|
---|
292 | printf("Writing file \"%s\" (format: %s)\n",
|
---|
293 | filename, Format_Name[txMip->format]);
|
---|
294 | }
|
---|
295 | stream = fopen(filename, "wb");
|
---|
296 | if (stream == NULL) {
|
---|
297 | txPanic("Unable to open output file.");
|
---|
298 | }
|
---|
299 | if (tgaformat) {
|
---|
300 | if (!txWriteTGA( stream, txMip)) {
|
---|
301 | txPanic("txMipWrite: Write failed.");
|
---|
302 | }
|
---|
303 | } else {
|
---|
304 | if (!txWrite3df( stream, txMip)) {
|
---|
305 | txPanic("txMipWrite: Write failed.");
|
---|
306 | }
|
---|
307 | }
|
---|
308 | fclose(stream);
|
---|
309 | return;
|
---|
310 | }
|
---|
311 |
|
---|
312 | /* Otherwise, we need to write out separate output files */
|
---|
313 | w = txMip->width;
|
---|
314 | h = txMip->height;
|
---|
315 |
|
---|
316 | for (i=0; i<txMip->depth; i++) {
|
---|
317 | char suffix[2];
|
---|
318 |
|
---|
319 | splitImg = *txMip; // Copy everything first, including palette.
|
---|
320 |
|
---|
321 | // Then change stuff.
|
---|
322 | splitImg.format = txMip->format;
|
---|
323 | splitImg.width = w;
|
---|
324 | splitImg.height = h;
|
---|
325 | splitImg.size = w * h * GR_TEXFMT_SIZE( txMip->format );
|
---|
326 | splitImg.depth = 1;
|
---|
327 | splitImg.data[0] = txMip->data[i];
|
---|
328 |
|
---|
329 | // manufacture a new name.
|
---|
330 | suffix[0] = '0' + i;
|
---|
331 | suffix[1] = 0;
|
---|
332 | strcpy(filename, file);
|
---|
333 | strcat(filename, suffix);
|
---|
334 | strcat(filename, ext);
|
---|
335 | stream = fopen(filename, "wb");
|
---|
336 | if (stream == NULL) {
|
---|
337 | txPanic("Unable to open output file.");
|
---|
338 | }
|
---|
339 | if (tgaformat) {
|
---|
340 | if (!txWriteTGA( stream, &splitImg)) {
|
---|
341 | txPanic("txMipWrite: Write failed.");
|
---|
342 | }
|
---|
343 | } else {
|
---|
344 | if (!txWrite3df( stream, &splitImg)) {
|
---|
345 | txPanic("txMipWrite: Write failed.");
|
---|
346 | }
|
---|
347 | }
|
---|
348 | fclose(stream);
|
---|
349 | if (w > 1) w >>= 1;
|
---|
350 | if (h > 1) h >>= 1;
|
---|
351 | }
|
---|
352 | }
|
---|
353 |
|
---|
354 | FxBool txWrite( Gu3dfInfo *info, FILE *fp, FxU32 flags )
|
---|
355 | {
|
---|
356 | TxMip mip;
|
---|
357 |
|
---|
358 | mip.format = info->header.format;
|
---|
359 | mip.width = info->header.width;
|
---|
360 | mip.height = info->header.height;
|
---|
361 | #ifdef GLIDE3
|
---|
362 | mip.depth = info->header.large_lod - info->header.small_lod + 1;
|
---|
363 | #else
|
---|
364 | mip.depth = info->header.small_lod - info->header.large_lod + 1;
|
---|
365 | #endif
|
---|
366 | mip.size = info->mem_required;
|
---|
367 | mip.data[0] = info->data;
|
---|
368 | if( mip.format == GR_TEXFMT_P_8 || mip.format == GR_TEXFMT_AP_88 )
|
---|
369 | {
|
---|
370 | memcpy( mip.pal, info->table.palette.data, sizeof( FxU32 ) * 256 );
|
---|
371 | }
|
---|
372 | if( mip.format == GR_TEXFMT_YIQ_422 || mip.format == GR_TEXFMT_AYIQ_8422 )
|
---|
373 | {
|
---|
374 | txNccToPal( mip.pal, &info->table.nccTable);
|
---|
375 | }
|
---|
376 | switch( flags & TX_WRITE_MASK )
|
---|
377 | {
|
---|
378 | case TX_WRITE_3DF:
|
---|
379 | if( !txWrite3df( fp, &mip ) )
|
---|
380 | return FXFALSE;
|
---|
381 | break;
|
---|
382 | case TX_WRITE_TGA:
|
---|
383 | if( mip.format == GR_TEXFMT_YIQ_422 ||
|
---|
384 | mip.format == GR_TEXFMT_AYIQ_8422 )
|
---|
385 | {
|
---|
386 | txPanic( "Don't know how to write NCC textures\n" );
|
---|
387 | }
|
---|
388 | if( !txWriteTGA( fp, &mip ) )
|
---|
389 | return FXFALSE;
|
---|
390 | break;
|
---|
391 | default:
|
---|
392 | txPanic( "Unknown texture write format" );
|
---|
393 | break;
|
---|
394 | }
|
---|
395 | return FXTRUE;
|
---|
396 | }
|
---|