2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/AC/convert.cpp $
15 * C module for the conversion of standard animation files to our own ANIM format.
16 * This is where all the real code for this application is located really.
19 * Revision 1.2 2002/06/09 04:41:15 relnev
20 * added copyright header
22 * Revision 1.1.1.1 2002/05/03 03:28:11 root
26 * 2 10/23/98 6:03p Dave
28 * 1 10/23/98 5:34p Dave
30 * 3 10/22/98 6:14p Dave
31 * Optimized some #includes in Anim folder. Put in the beginnings of
32 * parse/localization support for externalized strings and tstrings.tbl
34 * 2 10/07/98 10:52a Dave
37 * 1 10/07/98 10:48a Dave
39 * 12 6/23/98 2:52p Hoffoss
40 * Changed code so AC compiles once again.
42 * 11 7/20/97 6:57p Lawrance
43 * supporting new RLE format
45 * 10 6/25/97 11:57a Lawrance
48 * 9 6/25/97 11:49a Lawrance
49 * don't assert if PCX read fails, print warning
51 * 8 5/21/97 2:26p Lawrance
52 * bug fix: not using correct header size
54 * 7 5/21/97 11:06a Lawrance
55 * enabling a user-defined transparent value
57 * 6 5/19/97 3:21p Lawrance
58 * add fps parm, version num to anim header
60 * 5 2/25/97 5:18p Lawrance
61 * force_key_frame now numbering from 1 for PCXs as well as AVIs
63 * 4 2/20/97 1:47p Lawrance
64 * adding dots when making frames
66 * 3 2/20/97 10:30a Hoffoss
67 * Added in support for forcing a key frame.
69 * 2 2/19/97 9:26p Lawrance
70 * added force_key_frame global
72 * 1 2/19/97 7:14p Lawrance
74 * 14 2/19/97 4:21p Lawrance
75 * took out unnecessary #include BmpMan.h
77 * 13 2/19/97 3:59p Lawrance
78 * using pcxutils to load bitmaps, not bmpman
80 * 12 2/17/97 2:59p Lawrance
81 * integrating into game
83 * 11 2/17/97 3:02p Hoffoss
84 * Added headers to files, and implemented key frame dialog stuff.
86 * 10 2/14/97 10:38p Lawrance
89 * 9 2/14/97 9:47p Hoffoss
92 * 8 2/14/97 8:44p Lawrance
93 * fixed bug with saving header
95 * 7 2/14/97 8:04p Hoffoss
98 * 6 2/14/97 7:44p Lawrance
99 * fixed some bugs in loading an AVI
101 * 5 2/14/97 7:33p Lawrance
102 * added convert_avi_to_anim() function
104 * 4 2/14/97 5:38p Hoffoss
105 * Changes to get AnimCoverter project to compile and link.
107 * 3 2/14/97 3:28p Hoffoss
108 * Wrote functions to save an anim file.
110 * 2 2/13/97 5:55p Lawrance
111 * reading AVI / decompressing AVI functions in
116 #pragma warning(disable: 4201)
119 #include <vfw.h> /* Video For Windows header file */
122 #include "pcxutils.h"
123 #include "animplay.h"
124 #include "packunpack.h"
126 #define AVI_STREAM_F_USED ( 1 << 0 )
128 typedef struct AVI_STREAM_TYPE {
134 int min_compressed_buffer_size;
137 //ubyte pal_translation[256]; // palette translation look-up table
141 // Internal function prototypes
142 int AVI_stream_open(char* filename);
143 void AVI_stream_close();
144 int AVI_stream_get_frame(ubyte* buffer, int frame_number);
145 void AVI_decompress_RLE8(ubyte* src, ubyte* dest, int w, int h);
148 static AVI_STREAM_TYPE AVI_stream;
149 static int AVI_stream_inited = 0;
152 char *anim_save_filename;
153 ubyte *cur_frame, *last_frame;
154 ubyte anim_buffer[ANIM_BUFFER_MAX];
157 int total_key_frames;
161 int Use_custom_xparent_color;
162 rgb_triple Xparent_color;
164 int Compression_type; // what kind of RLE compression is going to be used
165 int Key_frame_compression, Regular_frame_compression;
167 key_frame *first_frame;
168 FILE *anim_fp = NULL;
171 // AVI_stream_init() is called only to clear USED flag of the AVI_stream structure
172 // and reset the current frame.
174 // This does not need to be called explicity, since it will be called by AVI_stream_open()
177 void AVI_stream_init()
179 AVI_stream.flags &= ~AVI_STREAM_F_USED;
180 AVI_stream.current_frame = 0;
181 AVI_stream_inited = 1;
183 AVIFileInit(); // opens AVIFile library
186 // AVI_stream_open() will open the AVI file and prepare it for reading, but will not
187 // store any of the frame data.
189 // returns: 0 ==> success
190 // !0 ==> could not open the AVI stream
192 // The filename is expected to be an absolute pathname (or file in the current working directory)
194 int AVI_stream_open(char* filename)
196 if ( !AVI_stream_inited )
202 AVISTREAMINFO avi_stream_info;
204 Assert( !(AVI_stream.flags & AVI_STREAM_F_USED) );
207 hr = AVIFileOpen(&pfile, filename, OF_SHARE_DENY_WRITE, 0);
209 // nprintf(("Warning", "AVI ==> Unable to open %s", filename));
213 strcpy(AVI_stream.filename, filename);
215 // Get a handle to the video stream within the AVI file
216 hr = AVIFileGetStream(pfile, &pstream, streamtypeVIDEO, 0);
218 //nprintf(("Warning", "AVI ==> Unable to open video stream in %s", filename));
222 // Store the pointer to stream, since we'll need it later to read from disk
223 AVI_stream.pstream = pstream;
224 AVI_stream.pfile = pfile;
226 // Get information on the stream
227 hr = AVIStreamInfo( pstream, &avi_stream_info, sizeof(AVISTREAMINFO) );
229 //nprintf(("Warning", "AVI ==> Unable to retreive stream info in %s", filename));
236 int start_sample = AVIStreamStart(pstream);
237 Assert( start_sample == 0 );
239 int end_sample = AVIStreamEnd(pstream);
240 Assert( end_sample >= start_sample );
242 // store the number of frames in the AVI_info[] structure
243 AVI_stream.num_frames = end_sample; // start sample must be 0
244 Assert(AVI_stream.num_frames == AVIStreamLength(pstream) );
247 // Get information on the stream
248 hr = AVIStreamInfo( pstream, &avi_stream_info, sizeof(AVISTREAMINFO) );
250 //nprintf(("Warning", "AVI ==> Unable to retreive stream info in %s", filename));
254 buffer_size = avi_stream_info.dwSuggestedBufferSize;
255 Assert( buffer_size > 0 );
256 AVI_stream.min_compressed_buffer_size = buffer_size;
258 // determine the format of the AVI image data
259 ubyte* format_buffer;
260 long format_buffer_size;
261 BITMAPINFO* bitmap_info;
263 hr = AVIStreamFormatSize(pstream, 0, &format_buffer_size);
264 Assert( format_buffer_size > 0 );
266 format_buffer = (ubyte*) malloc(format_buffer_size);
267 Assert(format_buffer != NULL); // format_buffer is free'ed when AVI is free'ed, since memory is used by b_info member in AVI_info[] structure
269 hr = AVIStreamReadFormat(pstream, 0, format_buffer, &format_buffer_size);
270 bitmap_info = (BITMAPINFO*)format_buffer;
273 switch ( bitmap_info->bmiHeader.biCompression ) {
282 AVI_stream.w = bitmap_info->bmiHeader.biWidth;
283 AVI_stream.h = bitmap_info->bmiHeader.biHeight;
284 AVI_stream.bpp = bitmap_info->bmiHeader.biBitCount;
286 // create the palette translation look-up table
288 // Transparency: If the palette color is full green, then treat as transparent
291 pal = (RGBQUAD*)(bitmap_info->bmiColors);
293 // Store the palette in the AVI stream structure
294 for ( int i = 0; i < 256; i++ ) {
295 AVI_stream.palette[i*3] = pal[i].rgbRed;
296 AVI_stream.palette[i*3+1] = pal[i].rgbGreen;
297 AVI_stream.palette[i*3+2] = pal[i].rgbBlue;
301 // memcpy(AVI_stream.palette, pal, 256*3);
304 int transparent_found = 0;
305 for ( i = 0; i < 256; i++ ) {
307 //nprintf(("AVI", "AVI ==> R: %d G: %d B: %d\n", pal[i].rgbRed, pal[i].rgbGreen, pal[i].rgbBlue));
308 if ( pal[i].rgbRed < 5 && pal[i].rgbGreen > 250 && pal[i].rgbBlue < 5 ) {
309 avi_stream->pal_translation[i] = TRANSPARENT_INDEX;
310 break; // found transparent, continue in j for loop, since don't need check any more
313 avi_stream->pal_translation[i] = palette_find( pal[i].rgbRed, pal[i].rgbGreen, pal[i].rgbBlue );
316 for ( j = i+1; j < 256; j++ ) {
317 avi_stream->pal_translation[j] = palette_find( pal[j].rgbRed, pal[j].rgbGreen, pal[j].rgbBlue );
323 // set the flag to used, so to make sure we only process one AVI stream at a time
324 AVI_stream.flags |= AVI_STREAM_F_USED;
331 // AVI_stream_close() should be called when you are finished reading all the frames of an AVI
333 void AVI_stream_close()
335 // Assert( AVI_stream.flags & AVI_STREAM_F_USED);
337 AVIStreamRelease(AVI_stream.pstream); // closes the video stream
338 AVIFileRelease(AVI_stream.pfile); // closes the file
339 AVI_stream.flags &= ~AVI_STREAM_F_USED; // clear the used flag
341 AVIFileExit(); // releases AVIFile library
342 AVI_stream_inited = 0;
349 // AVI_stream_get_next_frame() will take the next RLE'd AVI frame and return the
350 // uncompressed data in the buffer pointer supplied as a parameter. The caller is
351 // responsible for allocating the memory before-hand (the memory required is easily
352 // calculated by looking at the w and h members in AVI_stream).
354 // returns: 0 ==> success
357 int AVI_stream_get_frame(ubyte* buffer, int frame_number)
359 if ( frame_number > AVI_stream.num_frames ) {
364 Assert( (frame_number - 1) >= 0 );
366 ubyte* compressed_frame = (ubyte*)malloc(AVI_stream.min_compressed_buffer_size);
367 Assert( compressed_frame != NULL );
370 long num_samples_used;
372 AVIStreamRead( AVI_stream.pstream, frame_number-1, 1, compressed_frame, AVI_stream.min_compressed_buffer_size, &num_bytes_used, &num_samples_used);
373 Assert(num_samples_used == 1);
375 AVI_decompress_RLE8(compressed_frame, buffer, AVI_stream.w, AVI_stream.h);
377 free( compressed_frame );
384 // -------------------------------------------------------------------------------------------------
385 // AVI_decompress_RLE8() will decompress the data pointed to by src, and store in dest.
387 // NOTE: 1. memory for dest must be already allocated before calling function
390 void AVI_decompress_RLE8(ubyte* src, ubyte* dest, int w, int h)
396 Assert( src != NULL);
397 Assert( dest != NULL);
405 int size_src = w * h + 1;
408 int height_offset = scan_line * w;
410 while ( src_index < size_src ) {
412 count = src[src_index];
414 if ( count == 0 ) { // control code follows
416 control_code = src[src_index];
417 if ( control_code == 1 ) {
419 // nprintf(("AVI","AVI ==> Reached end of compressed image\n"));
422 else if ( control_code == 0 ) {
425 height_offset = scan_line * w; // only need to calc once per scanline
427 //nprintf(("AVI","AVI ==> Reached end of line in compressed image\n"));
429 else if ( control_code == 2 ) {
435 for ( i = 0; i < control_code; i++ ) {
437 dest[height_offset + dest_index] = src[src_index];
441 // run must end on a word boundry
442 if ( control_code & 1 )
448 run = src[src_index];
449 // nprintf(("AVI","AVI ==> Got %d pixel run of %d\n", src[src_index], count));
450 memset( &dest[height_offset + dest_index], run, count );
458 int save_anim_header()
460 int i, new_format_id = 0;
464 anim_fp = fopen(anim_save_filename, "r+b");
466 if (!fwrite(&new_format_id, 2, 1, anim_fp))
468 if (!fwrite(&Anim.version, 2, 1, anim_fp))
470 if (!fwrite(&Anim.fps, 2, 1, anim_fp))
472 if (!fwrite(&Anim.xparent_r, 1, 1, anim_fp))
474 if (!fwrite(&Anim.xparent_g, 1, 1, anim_fp))
476 if (!fwrite(&Anim.xparent_b, 1, 1, anim_fp))
478 if (!fwrite(&Anim.width, 2, 1, anim_fp))
480 if (!fwrite(&Anim.height, 2, 1, anim_fp))
482 if (!fwrite(&Anim.total_frames, 2, 1, anim_fp))
484 if (!fwrite(&Anim.packer_code, 1, 1, anim_fp))
486 if (fwrite(&Anim.palette, 3, 256, anim_fp) != 256)
488 if (!fwrite(&total_key_frames, 2, 1, anim_fp))
491 for (i=0; i<Anim.num_keys; i++) {
492 if (!fwrite(&Anim.keys[i].frame_num, 2, 1, anim_fp))
495 if (!fwrite(&Anim.keys[i].offset, 4, 1, anim_fp))
499 if (!fwrite(&anim_offset, 4, 1, anim_fp))
505 // This function allocates a linked list of key frame headers.
506 // It is responsible for determining which frames in an anim
507 // should be key frames.
508 int allocate_key_frames(int total_frames)
510 int count = 0, frame = 1, rate = key_frame_rate, last_frame;
515 while (frame <= total_frames) {
520 if (force_key_frame >= 0)
524 Anim.keys = (key_frame *) malloc(count * sizeof(key_frame));
527 frame = last_frame = 1;
528 while (frame <= total_frames) {
529 if ((force_key_frame > last_frame) && (force_key_frame < frame))
530 Anim.keys[count++].frame_num = force_key_frame;
532 Anim.keys[count++].frame_num = frame;
536 if (force_key_frame > last_frame)
537 Anim.keys[count++].frame_num = force_key_frame;
539 Anim.num_keys = count;
540 return count; // number of key frames
543 int anim_save_init(char *file, int width, int height, int frames)
546 anim_save_filename = file;
547 anim_fp = fopen(file, "wb");
551 Anim.version = ANIM_VERSION;
552 Anim.fps = Default_fps;
554 Anim.height = height;
555 Anim.packer_code = PACKER_CODE;
556 Anim.xparent_r = Xparent_color.r;
557 Anim.xparent_g = Xparent_color.g;
558 Anim.xparent_b = Xparent_color.b;
559 Anim.total_frames = frames;
562 total_key_frames = allocate_key_frames(frames);
563 fseek(anim_fp, ANIM_HEADER_SIZE + total_key_frames * 6, SEEK_SET);
565 switch ( Compression_type ) {
566 case CUSTOM_DELTA_RLE:
567 Key_frame_compression = PACKING_METHOD_RLE_KEY;
568 Regular_frame_compression = PACKING_METHOD_RLE;
572 Key_frame_compression = PACKING_METHOD_STD_RLE_KEY;
573 Regular_frame_compression = PACKING_METHOD_STD_RLE;
585 int anim_save_frame()
589 key_frame *keyp = NULL;
593 Assert(cur_frame_num <= Anim.total_frames);
595 for (i=0; i<Anim.num_keys; i++)
596 if (Anim.keys[i].frame_num == cur_frame_num) {
597 keyp = &Anim.keys[i];
602 fprintf(stdout, "*");
604 keyp->offset = anim_offset;
605 size = pack_key_frame(cur_frame, anim_buffer, Anim.width * Anim.height, ANIM_BUFFER_MAX, Key_frame_compression);
608 fprintf(stdout, ".");
610 size = pack_frame(cur_frame, last_frame, anim_buffer, Anim.width * Anim.height, ANIM_BUFFER_MAX, Regular_frame_compression);
616 if ((int) fwrite(anim_buffer, 1, size, anim_fp) != size)
621 cur_frame = last_frame;
627 // converts an avi file to an anim file
629 // returns: 0 ==> success
632 int convert_avi_to_anim(char* filename)
634 char ani_filename[255];
636 int rc, i, xparent_pal_index;
637 int avi_stream_opened = 0;
639 rc = AVI_stream_open(filename);
641 // could not open the AVI stream
644 avi_stream_opened = 1;
646 Assert(AVI_stream.bpp == 8);
647 cur_frame = (ubyte*) malloc(AVI_stream.w * AVI_stream.h);
648 last_frame = (ubyte*) malloc(AVI_stream.w * AVI_stream.h);
649 Assert(cur_frame && last_frame);
651 strcpy(ani_filename, AVI_stream.filename);
652 strcpy(ani_filename + strlen(ani_filename) - 3, "ani");
654 memcpy(Anim.palette, AVI_stream.palette, 768);
656 if (Use_custom_xparent_color) {
657 // Need to look at pixel in top-left
658 rc = AVI_stream_get_frame(cur_frame, 1);
659 xparent_pal_index = cur_frame[0];
660 Xparent_color.r = Anim.palette[xparent_pal_index * 3];
661 Xparent_color.g = Anim.palette[xparent_pal_index * 3 + 1];
662 Xparent_color.b = Anim.palette[xparent_pal_index * 3 + 2];
666 Xparent_color.g = 255;
670 rc = anim_save_init(ani_filename, AVI_stream.w, AVI_stream.h, AVI_stream.num_frames);
674 for ( i=1; i <= AVI_stream.num_frames; i++ ) {
675 // get uncompressed frame from the AVI
676 rc = AVI_stream_get_frame(cur_frame, i);
680 // pass to the anim compression
681 rc = anim_save_frame();
686 rc = save_anim_header();
693 // done with the AVI.. close the stream
694 if ( avi_stream_opened )
702 fprintf(stdout,"\n");
707 int convert_frames_to_anim(char *filename)
709 int first_frame, frame, pos, width, height, xparent_pal_index, r = -1;
710 char ani_filename[255], name[255], temp[8];
714 Assert(strlen(filename) < 254);
715 strcpy(name, filename);
716 strcpy(ani_filename, filename);
717 strcpy(ani_filename + strlen(ani_filename) - 8, ".ani");
718 pos = strlen(name) - 8;
719 frame = first_frame = atoi(&name[pos]);
720 force_key_frame -= frame;
723 fp = fopen(name, "rb");
728 sprintf(temp, "%04.4d", frame);
729 strncpy(&name[pos], temp, 4);
732 fp = fopen(name, "rb");
736 rc = pcx_read_header(filename, &width, &height, NULL);
737 if (rc != PCX_ERROR_NONE) {
738 fprintf(stdout, "An error reading the PCX file %s. It may not exist.\n", filename);
742 cur_frame = (ubyte *) malloc(width * height);
743 last_frame = (ubyte *) malloc(width * height);
745 rc = pcx_read_bitmap_8bpp(filename, cur_frame, Anim.palette);
746 if (rc != PCX_ERROR_NONE) {
747 fprintf(stdout, "An error reading the PCX file %s. It may not exist.\n", filename);
751 if (Use_custom_xparent_color) {
752 // Need to look at pixel in top-left
753 xparent_pal_index = ((ubyte *) cur_frame)[0];
754 Xparent_color.r = Anim.palette[xparent_pal_index * 3];
755 Xparent_color.g = Anim.palette[xparent_pal_index * 3 + 1];
756 Xparent_color.b = Anim.palette[xparent_pal_index * 3 + 2];
760 Xparent_color.g = 255;
764 if (anim_save_init(ani_filename, width, height, frame - first_frame))
767 while (first_frame < frame) {
768 sprintf(temp, "%04.4d", first_frame);
769 strncpy(&name[pos], temp, 4);
770 rc = pcx_read_bitmap_8bpp(name, cur_frame, Anim.palette);
771 if (rc != PCX_ERROR_NONE)
774 if (anim_save_frame())
780 if (save_anim_header())
789 fprintf(stdout, "\n");