/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ #include "anim/packunpack.h" #include "bmpman/bmpman.h" #include "graphics/2d.h" #include "anim/animplay.h" const int packer_code = PACKER_CODE; const int transparent_code = 254; void anim_check_for_palette_change(anim_instance *instance) { if ( instance->parent->screen_sig != gr_screen.signature ) { instance->parent->screen_sig = gr_screen.signature; anim_set_palette(instance->parent); } } anim_instance *init_anim_instance(anim *ptr, int bpp) { anim_instance *inst; if (!ptr) { Int3(); return NULL; } if ( ptr->flags & ANF_STREAMED ) { if ( ptr->file_offset < 0 ) { Int3(); return NULL; } } else { if ( !ptr->data ) { Int3(); return NULL; } } ptr->instance_count++; inst = (anim_instance *) vm_malloc(sizeof(anim_instance)); Assert(inst); memset(inst, 0, sizeof(anim_instance)); inst->frame_num = -1; inst->last_frame_num = -1; inst->parent = ptr; inst->data = ptr->data; inst->file_offset = ptr->file_offset; inst->stop_now = FALSE; inst->aa_color = NULL; inst->frame = (ubyte *) vm_malloc(inst->parent->width * inst->parent->height * (bpp >> 3)); Assert( inst->frame != NULL ); memset( inst->frame, 0, inst->parent->width * inst->parent->height * (bpp >> 3) ); return inst; } void free_anim_instance(anim_instance *inst) { Assert(inst->frame); vm_free(inst->frame); inst->frame = NULL; inst->parent->instance_count--; inst->parent = NULL; inst->data = NULL; inst->file_offset = -1; vm_free(inst); } int anim_get_next_frame(anim_instance *inst) { int bm, bitmap_flags; int aabitmap = 0; int bpp = 16; if ( anim_instance_is_streamed(inst) ) { if ( inst->file_offset <= 0 ) { return -1; } } else { if (!inst->data) return -1; } inst->frame_num++; if (inst->frame_num >= inst->parent->total_frames) { inst->data = NULL; inst->file_offset = inst->parent->file_offset; return -1; } bitmap_flags = 0; bpp = 16; if(inst->aa_color != NULL){ bitmap_flags |= BMP_AABITMAP; aabitmap = 1; bpp = 8; } anim_check_for_palette_change(inst); BM_SELECT_TEX_FORMAT(); if ( anim_instance_is_streamed(inst) ) { inst->file_offset = unpack_frame_from_file(inst, inst->frame, inst->parent->width*inst->parent->height, inst->parent->palette_translation, aabitmap, bpp); } else { inst->data = unpack_frame(inst, inst->data, inst->frame, inst->parent->width*inst->parent->height, inst->parent->palette_translation, aabitmap, bpp); } bm = bm_create(bpp, inst->parent->width, inst->parent->height, inst->frame, bitmap_flags); bm_unload(bm); return bm; } ubyte *anim_get_next_raw_buffer(anim_instance *inst, int xlate_pal, int aabitmap, int bpp) { if ( anim_instance_is_streamed(inst) ) { if ( inst->file_offset < 0 ) { return NULL; } } else { if (!inst->data){ return NULL; } } inst->frame_num++; if (inst->frame_num >= inst->parent->total_frames) { inst->data = NULL; inst->file_offset = inst->parent->file_offset; return NULL; } anim_check_for_palette_change(inst); if ( anim_instance_is_streamed(inst) ) { if ( xlate_pal ){ inst->file_offset = unpack_frame_from_file(inst, inst->frame, inst->parent->width*inst->parent->height, inst->parent->palette_translation, aabitmap, bpp); } else { inst->file_offset = unpack_frame_from_file(inst, inst->frame, inst->parent->width*inst->parent->height, NULL, aabitmap, bpp); } } else { if ( xlate_pal ){ inst->data = unpack_frame(inst, inst->data, inst->frame, inst->parent->width*inst->parent->height, inst->parent->palette_translation, aabitmap, bpp); } else { inst->data = unpack_frame(inst, inst->data, inst->frame, inst->parent->width*inst->parent->height, NULL, aabitmap, bpp); } } return inst->frame; } /** * @brief Pack key frame * * @param frame Frame pixel data to pack * @param save Memory to store packed data to * @param size Number of bytes to pack * @param max Maximum number of packed bytes (size of buffer) * @param compress_type Compress type * @return Actual number of bytes data packed to or -1 if error */ int pack_key_frame(ubyte *frame, ubyte *save, long size, long max, int compress_type) { int last = -32768, count = 0; long packed_size = 1; switch ( compress_type ) { case PACKING_METHOD_RLE_KEY: *save++ = PACKING_METHOD_RLE_KEY; while (size--) { if (*frame != last || count > 255) { if (packed_size + 3 >= max) return -1; if (count < 3) { if (last == packer_code) { *save++ = (ubyte)packer_code; *save++ = (ubyte)(count - 1); packed_size += 2; } else while (count--) { *save++ = (ubyte)last; packed_size++; } } else { *save++ = (ubyte)packer_code; *save++ = (ubyte)(count - 1); *save++ = (ubyte)last; packed_size += 3; } count = 0; last = *frame; } count++; frame++; } if (packed_size + 3 >= max) return -1; if (count < 3) { if (last == packer_code) { *save++ = (ubyte)packer_code; *save++ = (ubyte)(count - 1); packed_size += 2; } else while (count--) { *save++ = (ubyte)last; packed_size++; } } else { *save++ = (ubyte)packer_code; *save++ = (ubyte)(count - 1); *save++ = (ubyte)last; packed_size += 3; } break; case PACKING_METHOD_STD_RLE_KEY: { ubyte *dest_start; int i; dest_start = save; count = 1; last = *frame++; *save++ = PACKING_METHOD_STD_RLE_KEY; for (i=1; i < size; i++ ) { if ( *frame != last ) { if ( count ) { if (packed_size + 2 >= max) return -1; if ( (count == 1) && !(last & STD_RLE_CODE) ) { *save++ = (ubyte)last; packed_size++; Assert( last != STD_RLE_CODE ); } else { count |= STD_RLE_CODE; *save++ = (ubyte)count; *save++ = (ubyte)last; packed_size += 2; } } last = *frame; count = 0; } count++; frame++; if ( count == 127 ) { count |= STD_RLE_CODE; *save++ = (ubyte)count; *save++ = (ubyte)last; packed_size += 2; count = 0; } } // end for if (count) { if (packed_size + 2 >= max) return -1; if ( (count == 1) && !(last & STD_RLE_CODE) ) { *save++ = (ubyte)last; packed_size++; Assert( last != STD_RLE_CODE ); } else { count |= STD_RLE_CODE; *save++ = (ubyte)count; *save++ = (ubyte)last; packed_size += 2; } } Assert(packed_size == (save-dest_start) ); return packed_size; } default: Assert(0); return -1; break; } // end switch return packed_size; } /** * @brief Pack frame * * @param frame Frame pixel data to pack * @param frame2 Previous frame's pixel data * @param save Memory to store packed data to * @param size Number of bytes to pack * @param max Maximum number of packed bytes (size of buffer) * @param compress_type Compress type * @return Actual number of bytes data packed to or -1 if error */ int pack_frame(ubyte *frame, ubyte *frame2, ubyte *save, long size, long max, int compress_type) { int pixel, last = -32768, count = 0, i; long packed_size = 1; switch ( compress_type ) { case PACKING_METHOD_RLE: // Hoffoss RLE regular frame *save++ = PACKING_METHOD_RLE; while (size--) { if (*frame != *frame2++) pixel = *frame; else pixel = transparent_code; if (pixel != last || count > 255) { if (packed_size + 3 >= max) return -1; if (count < 3) { if (last == packer_code) { *save++ = (ubyte)packer_code; *save++ = (ubyte)(count - 1); packed_size += 2; } else while (count--) { *save++ = (ubyte)last; packed_size++; } } else { *save++ = (ubyte)packer_code; *save++ = (ubyte)(count - 1); *save++ = (ubyte)last; packed_size += 3; } count = 0; last = pixel; } frame++; count++; } if (packed_size + 3 >= max) return -1; if (count < 3) { if (last == packer_code) { *save++ = (ubyte)packer_code; *save++ = (ubyte)(count - 1); packed_size += 2; } else while (count--) { *save++ = (ubyte)last; packed_size++; } } else { *save++ = (ubyte)(packer_code); *save++ = (ubyte)(count - 1); *save++ = (ubyte)(last); packed_size += 3; } break; case PACKING_METHOD_STD_RLE: { // high bit count regular RLE frame ubyte *dest_start; dest_start = save; count = 1; if (*frame++ != *frame2++) last = *frame; else last = transparent_code; *save++ = PACKING_METHOD_STD_RLE; for (i=1; i < size; i++ ) { if (*frame != *frame2++) pixel = *frame; else pixel = transparent_code; if ( pixel != last ) { if ( count ) { if (packed_size + 2 >= max) return -1; if ( (count == 1) && !(last & STD_RLE_CODE) ) { *save++ = (ubyte)last; packed_size++; Assert( last != STD_RLE_CODE ); } else { count |= STD_RLE_CODE; *save++ = (ubyte)count; *save++ = (ubyte)last; packed_size += 2; } } last = pixel; count = 0; } count++; frame++; if ( count == 127 ) { count |= STD_RLE_CODE; *save++ = (ubyte)count; *save++ = (ubyte)last; packed_size += 2; count = 0; } } // end for if (count) { if (packed_size + 2 >= max) return -1; if ( (count == 1) && !(last & STD_RLE_CODE) ) { *save++ = (ubyte)last; packed_size++; Assert( last != STD_RLE_CODE ); } else { count |= STD_RLE_CODE; *save++ = (ubyte)count; *save++ = (ubyte)last; packed_size += 2; } } Assert(packed_size == (save-dest_start) ); return packed_size; } default: Assert(0); return -1; break; } // end switch return packed_size; } /** * @brief Convert a 24 bit value to a 16 bit value * @param bit_24 24 bit value * @param bit_16 16 bit value (output) */ void convert_24_to_16(int bit_24, ushort *bit_16) { ubyte *pixel = (ubyte*)&bit_24; ubyte alpha = 1; bm_set_components((ubyte*)bit_16, (ubyte*)&pixel[0], (ubyte*)&pixel[1], (ubyte*)&pixel[2], &alpha); } /** * @brief Unpack a pixel given the passed index and the anim_instance's palette * @return Bytes stuffed */ int unpack_pixel(anim_instance *ai, ubyte *data, ubyte pix, int aabitmap, int bpp) { int bit_24; ushort bit_16 = 0; ubyte bit_8 = 0; ubyte al = 0; ubyte r, g, b; int pixel_size = (bpp / 8); anim *a = ai->parent; Assert(a); // if this is an aabitmap, don't run through the palette if(aabitmap){ switch(bpp){ case 16 : bit_16 = (ushort)pix; break; case 8: bit_8 = pix; break; default: Int3(); } } else { // if the pixel value is 255, or is the xparent color, make it so if(((a->palette[pix*3] == a->xparent_r) && (a->palette[pix*3+1] == a->xparent_g) && (a->palette[pix*3+2] == a->xparent_b)) ){ if (pixel_size > 2) { bit_24 = 0; } else { r = b = 0; g = 255; bm_set_components((ubyte*)&bit_16, &r, &g, &b, &al); } } else { if (pixel_size > 2) { ubyte pixel[4]; pixel[0] = ai->parent->palette[pix * 3 + 2]; pixel[1] = ai->parent->palette[pix * 3 + 1]; pixel[2] = ai->parent->palette[pix * 3]; pixel[3] = 255; memcpy(&bit_24, pixel, sizeof(int)); if (pixel_size == 4) { bit_24 = INTEL_INT(bit_24); } } else { // stuff the 24 bit value memcpy(&bit_24, &ai->parent->palette[pix * 3], 3); //-V512 // convert to 16 bit convert_24_to_16(bit_24, &bit_16); } } } // stuff the pixel switch (bpp) { case 32: case 24: memcpy(data, &bit_24, pixel_size); break; case 16: memcpy(data, &bit_16, pixel_size); break; case 8: *data = bit_8; break; default: Int3(); return 0; } return pixel_size; } /** * @brief Unpack a pixel given the passed index and the anim_instance's palette * @return Bytes stuffed */ int unpack_pixel_count(anim_instance *ai, ubyte *data, ubyte pix, int count = 0, int aabitmap = 0, int bpp = 8) { int bit_24; int idx; ubyte al = 0; ushort bit_16 = 0; ubyte bit_8 = 0; anim *a = ai->parent; int pixel_size = (bpp / 8); ubyte r, g, b; Assert(a); // if this is an aabitmap, don't run through the palette if(aabitmap){ switch(bpp){ case 16 : bit_16 = (ushort)pix; break; case 8 : bit_8 = pix; break; default : Int3(); } } else { // if the pixel value is 255, or is the xparent color, make it so if(((a->palette[pix*3] == a->xparent_r) && (a->palette[pix*3+1] == a->xparent_g) && (a->palette[pix*3+2] == a->xparent_b)) ){ if (pixel_size > 2) { bit_24 = 0; } else { r = b = 0; g = 255; bm_set_components((ubyte*)&bit_16, &r, &g, &b, &al); } } else { if (pixel_size > 2) { ubyte pixel[4]; pixel[0] = ai->parent->palette[pix * 3 + 2]; pixel[1] = ai->parent->palette[pix * 3 + 1]; pixel[2] = ai->parent->palette[pix * 3]; pixel[3] = 255; memcpy(&bit_24, pixel, sizeof(int)); if (pixel_size == 4) { bit_24 = INTEL_INT(bit_24); } } else { // stuff the 24 bit value memcpy(&bit_24, &ai->parent->palette[pix * 3], 3); //-V512 // convert to 16 bit convert_24_to_16(bit_24, &bit_16); } } } // stuff the pixel for (idx=0; idx 0) { value = *ptr++; if (value != packer_code) { if ( xlate_pal ){ stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); } else { stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); } frame += stuffed; size--; } else { count = *ptr++; if (count < 2){ value = packer_code; } else { value = *ptr++; } if (++count > size){ count = size; } if ( xlate_pal ){ stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); } else { stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); } frame += stuffed; size -= count; } } } else if ( *ptr == PACKING_METHOD_STD_RLE_KEY) { // key frame, with high bit as count ptr++; while (size > 0) { value = *ptr++; if ( !(value & STD_RLE_CODE) ) { if ( xlate_pal ){ stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); } else { stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); } frame += stuffed; size--; } else { count = value & (~STD_RLE_CODE); value = *ptr++; if (count > size) count = size; size -= count; Assert(size >= 0); if ( xlate_pal ){ stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); } else { stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); } frame += stuffed; } } } else if (*ptr == PACKING_METHOD_RLE) { // normal frame, Hoffoss's RLE format ptr++; while (size > 0) { value = *ptr++; if (value != packer_code) { if (value != transparent_code) { if ( xlate_pal ){ stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); } else { stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); } } else { // temporary pixel stuffed = pixel_size; } frame += stuffed; size--; } else { count = *ptr++; if (count < 2){ value = packer_code; } else { value = *ptr++; } if (++count > size){ count = size; } size -= count; Assert(size >= 0); if (value != transparent_code ) { if ( xlate_pal ) { stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); } else { stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); } } else { stuffed = count * pixel_size; } frame += stuffed; } } } else if ( *ptr == PACKING_METHOD_STD_RLE) { // normal frame, with high bit as count ptr++; while (size > 0) { value = *ptr++; if ( !(value & STD_RLE_CODE) ) { if (value != transparent_code) { if ( xlate_pal ){ stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); } else { stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); } } else { stuffed = pixel_size; } frame += stuffed; size--; } else { count = value & (~STD_RLE_CODE); value = *ptr++; if (count > size) count = size; size -= count; Assert(size >= 0); if (value != transparent_code) { if ( xlate_pal ){ stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); } else { stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); } } else { stuffed = pixel_size * count; } frame += stuffed; } } } else { // unknown packing method return NULL; } return ptr; } /** * @brief Unpack frame from file * * @param ai Animation instance * @param frame Where to store unpacked data to * @param size Total number of unpacked pixels requested * @param pal_translate Color translation lookup table (NULL if no palette translation desired) * @param aabitmap * @param bpp */ int unpack_frame_from_file(anim_instance *ai, ubyte *frame, int size, ubyte *pal_translate, int aabitmap, int bpp) { int xlate_pal, value, count = 0; int offset = 0; int stuffed; int pixel_size = (bpp / 8); if ( pal_translate == NULL ) { xlate_pal = 0; } else { xlate_pal = 1; } if (anim_instance_get_byte(ai,offset) == PACKING_METHOD_RLE_KEY) { // key frame, Hoffoss's RLE format offset++; while (size > 0) { value = anim_instance_get_byte(ai,offset); offset++; if (value != packer_code) { if ( xlate_pal ){ stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); } else { stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); } frame += stuffed; size--; } else { count = anim_instance_get_byte(ai,offset); offset++; if (count < 2) { value = packer_code; } else { value = anim_instance_get_byte(ai,offset); offset++; } if (++count > size){ count = size; } if ( xlate_pal ){ stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); } else { stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); } frame += stuffed; size -= count; } } } else if ( anim_instance_get_byte(ai,offset) == PACKING_METHOD_STD_RLE_KEY) { // key frame, with high bit as count offset++; while (size > 0) { value = anim_instance_get_byte(ai,offset); offset++; if ( !(value & STD_RLE_CODE) ) { if ( xlate_pal ){ stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); } else { stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); } frame += stuffed; size--; } else { count = value & (~STD_RLE_CODE); value = anim_instance_get_byte(ai,offset); offset++; if (count > size) count = size; size -= count; Assert(size >= 0); if ( xlate_pal ){ stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); } else { stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); } frame += stuffed; } } } else if (anim_instance_get_byte(ai,offset) == PACKING_METHOD_RLE) { // normal frame, Hoffoss's RLE format offset++; while (size > 0) { value = anim_instance_get_byte(ai,offset); offset++; if (value != packer_code) { if (value != transparent_code) { if ( xlate_pal ){ stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); } else { stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); } } else { stuffed = pixel_size; } frame += stuffed; size--; } else { count = anim_instance_get_byte(ai,offset); offset++; if (count < 2) { value = packer_code; } else { value = anim_instance_get_byte(ai,offset); offset++; } if (++count > size){ count = size; } size -= count; Assert(size >= 0); if (value != transparent_code ) { if ( xlate_pal ) { stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); } else { stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); } } else { stuffed = pixel_size * count; } frame += stuffed; } } } else if ( anim_instance_get_byte(ai,offset) ) { // normal frame, with high bit as count offset++; while (size > 0) { value = anim_instance_get_byte(ai,offset); offset++; if ( !(value & STD_RLE_CODE) ) { if (value != transparent_code) { if ( xlate_pal ){ stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); } else { stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); } } else { stuffed = pixel_size; } frame += stuffed; size--; } else { count = value & (~STD_RLE_CODE); value = anim_instance_get_byte(ai,offset); offset++; if (count > size) count = size; size -= count; Assert(size >= 0); if (value != transparent_code) { if ( xlate_pal ){ stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); } else { stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); } } else { stuffed = pixel_size * count; } frame += stuffed; } } } else { // unknown packing method return -1; } return ai->file_offset + offset; } /** * @brief Set animation palette * @todo Actually convert the frame data to correct palette at this point */ void anim_set_palette(anim *ptr) { int i, xparent_found = 0; // create the palette translation look-up table for ( i = 0; i < 256; i++ ) { ptr->palette_translation[i] = (ubyte)i; } if ( xparent_found ) { ptr->flags |= ANF_XPARENT; } else { ptr->flags &= ~ANF_XPARENT; } }