Files
fs2open.github.com/code/graphics/grd3dbmpman.cpp
T

1374 lines
34 KiB
C++

/*
* 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.
*
*/
// D3D8 includes
#include <d3d8.h>
#include <d3dx8.h>
#include <ctype.h>
#include "globalincs/pstypes.h"
#include "pcxutils/pcxutils.h"
#include "bmpman/bmpman.h"
#include "palman/palman.h"
#include "graphics/2d.h"
#include "anim/animplay.h"
#include "io/timer.h"
#include "globalincs/systemvars.h"
#include "io/key.h"
#include "anim/packunpack.h"
#include "graphics/grinternal.h"
#include "graphics/grd3dinternal.h"
#include "ship/ship.h"
#include "debugconsole/dbugfile.h"
#include "bmpman/bmpman.h"
#include "Cmdline/cmdline.h"
#include "cfile/cfile.h"
#include "graphics/grd3dbmpman.h"
extern bitmap_entry bm_bitmaps[];
D3DBitmapData d3d_bitmap_entry[MAX_BITMAPS];
// keep this defined to use per-ship nondarkening pixels
#define BMPMAN_SPECIAL_NONDARK
int bm_d3d_inited = 0;
int bm_d3d_next_handle = 1;
// 16 bit pixel formats
//extern int Bm_pixel_format;
int bm_d3d_get_next_handle()
{
int n = bm_d3d_next_handle;
bm_d3d_next_handle++;
if ( bm_d3d_next_handle > 30000 ) {
bm_d3d_next_handle = 1;
}
return n;
}
// Frees a bitmaps data if it should, and
// Returns true if bitmap n can free it's data.
static void bm_d3d_free_data(int n)
{
bitmap_entry *be;
bitmap *bmp;
Assert( n >= 0 && n < MAX_BITMAPS );
be = &bm_bitmaps[n];
bmp = &be->bm;
if(d3d_bitmap_entry[n].tinterface != NULL) {
d3d_bitmap_entry[n].tinterface->Release();
d3d_bitmap_entry[n].tinterface = NULL;
}
// If there isn't a bitmap in this structure, don't
// do anything but clear out the bitmap info
if ( be->type==BM_TYPE_NONE)
goto SkipFree;
// If this bitmap doesn't have any data to free, skip
// the freeing it part of this.
if ( bmp->data == 0 )
goto SkipFree;
// Don't free up memory for user defined bitmaps, since
// BmpMan isn't the one in charge of allocating/deallocing them.
if ( ( be->type==BM_TYPE_USER ) )
goto SkipFree;
// Free up the data now!
// mprintf(( "Bitmap %d freed %d bytes\n", n, bm_bitmaps[n].data_size ));
if(bmp->data != NULL) {
free((void *)bmp->data);
}
SkipFree:
// Clear out & reset the bitmap data structure
bmp->flags = 0;
bmp->bpp = 0;
bmp->data = 0;
bmp->palette = NULL;
}
static void *bm_d3d_malloc( int n, int size )
{
Assert( n >= 0 && n < MAX_BITMAPS );
// mprintf(( "Bitmap %d allocated %d bytes\n", n, size ));
return malloc(size);
}
void bm_d3d_close()
{
if ( bm_d3d_inited ) {
for (int i=0; i<MAX_BITMAPS; i++ ) {
bm_d3d_free_data(i); // clears flags, bbp, data, etc
}
bm_d3d_inited = 0;
}
}
void bm_d3d_init()
{
if (!bm_d3d_inited) {
bm_d3d_inited = 1;
atexit(bm_d3d_close);
}
for (int i = 0; i < MAX_BITMAPS; i++ ) {
bm_bitmaps[i].filename[0] = '\0';
bm_bitmaps[i].type = BM_TYPE_NONE;
bm_bitmaps[i].info.user.data = NULL;
bm_bitmaps[i].bm.data = 0;
bm_bitmaps[i].bm.palette = NULL;
d3d_bitmap_entry[i].tinterface = NULL;
bm_d3d_free_data(i); // clears flags, bbp, data, etc
}
}
void bm_d3d_get_frame_usage(int *ntotal, int *nnew)
{
}
// Creates a bitmap that exists in RAM somewhere, instead
// of coming from a disk file. You pass in a pointer to a
// block of 32 (or 8)-bit-per-pixel data. Right now, the only
// bpp you can pass in is 32 or 8. On success, it returns the
// bitmap number. You cannot free that RAM until bm_release
// is called on that bitmap.
int bm_d3d_create( int bpp, int w, int h, void * data, int flags )
{
Assert(bm_d3d_inited);
int i, n, first_slot = MAX_BITMAPS;
if(bpp == 8){
Assert(flags & BMP_AABITMAP);
} else {
Assert(bpp == 16);
}
for (i = MAX_BITMAPS-1; i >= 0; i-- ) {
if ( bm_bitmaps[i].type == BM_TYPE_NONE ) {
first_slot = i;
break;
}
}
Assert(d3d_bitmap_entry[i].tinterface == NULL);
n = first_slot;
Assert( n > -1 );
// Out of bitmap slots
if ( n == -1 ) return -1;
memset( &bm_bitmaps[n], 0, sizeof(bitmap_entry) );
sprintf( bm_bitmaps[n].filename, "TMP%dx%d", w, h );
bm_bitmaps[n].type = BM_TYPE_USER;
bm_bitmaps[n].palette_checksum = 0;
bm_bitmaps[n].bm.w = (short) w;
bm_bitmaps[n].bm.h = (short) h;
bm_bitmaps[n].bm.rowsize = (short) w;
bm_bitmaps[n].bm.bpp = (unsigned char) bpp;
bm_bitmaps[n].bm.flags = 0;
bm_bitmaps[n].bm.flags |= flags;
bm_bitmaps[n].bm.data = 0;
bm_bitmaps[n].bm.palette = NULL;
bm_bitmaps[n].info.user.bpp = ubyte(bpp);
bm_bitmaps[n].info.user.data = data;
bm_bitmaps[n].info.user.flags = ubyte(flags);
bm_bitmaps[n].handle = bm_get_next_handle()*MAX_BITMAPS + n;
bm_bitmaps[n].last_used = -1;
d3d_bitmap_entry[n].tinterface = NULL;
return bm_bitmaps[n].handle;
}
// sub helper function. Given a raw filename and an extension, try and find the bitmap
// returns -1 if it could not be found
// 0 if it was found as a file
// 1 if it already exists, fills in handle
int Bm_d3d_ignore_duplicates = 0;
int bm_d3d_load_sub(char *real_filename, const char *ext, int *handle)
{
int i;
char filename[MAX_FILENAME_LEN] = "";
strcpy( filename, real_filename );
strcat( filename, ext );
for (i=0; i<(int)strlen(filename); i++ ){
filename[i] = char(tolower(filename[i]));
}
// try to find given filename to see if it has been loaded before
if(!Bm_d3d_ignore_duplicates){
for (i = 0; i < MAX_BITMAPS; i++) {
if ( (bm_bitmaps[i].type != BM_TYPE_NONE) && !stricmp(filename, bm_bitmaps[i].filename) ) {
nprintf (("BmpMan", "Found bitmap %s -- number %d\n", filename, i));
*handle = bm_bitmaps[i].handle;
return 1;
}
}
}
// try and find the file
CFILE *test = cfopen(filename, "rb");
if(test != NULL){
cfclose(test);
return 0;
}
// could not be found
return -1;
}
// This loads a bitmap so we can draw with it later.
// It returns a negative number if it couldn't load
// the bitmap. On success, it returns the bitmap
// number. Function doesn't acutally load the data, only
// width, height, and possibly flags.
int bm_d3d_load( char * real_filename )
{
Assert(bm_d3d_inited);
int i, n;
int w, h;
char filename[MAX_FILENAME_LEN];
// nice little trick for keeping standalone memory usage way low - always return a bogus bitmap
if(Game_mode & GM_STANDALONE_SERVER){
strcpy(filename,"test128");
}
// make sure no one passed an extension
strcpy( filename, real_filename );
char *p = strchr( filename, '.' );
if ( p ) {
mprintf(( "Someone passed an extension to bm_load for file '%s'\n", real_filename ));
//Int3();
*p = 0;
}
// Lets find out what type it is
int type = BM_TYPE_NONE;
{
bool found = false;
//int handle = -1;
const int NUM_TYPES = 4;
const int type_list[NUM_TYPES] = {BM_TYPE_TGA, BM_TYPE_JPG, BM_TYPE_DDS, BM_TYPE_PCX};
const char *ext_list[NUM_TYPES] = {".tga", ".jpg", ".dds", ".pcx"};
// Only load TGA and JPG if given flag, support DDS and PCX by default
int i = Cmdline_jpgtga ? 0 : 2; // 2 Means start with DDS and fall back to PCX
for(; i < NUM_TYPES; i++) {
int handle = 0;
int result = bm_d3d_load_sub(filename, ext_list[i], &handle);
// Image is already loaded
if(result == 1) {
return handle;
}
// File was found
if(result == 0) {
strcat(filename, ext_list[i]);
type = type_list[i];
found = true;
break;
}
}
// No match was found
if(found == false) {
return -1;
}
}
Assert(type != BM_TYPE_NONE);
// Find an open slot
int first_slot = MAX_BITMAPS;
for (i = 0; i < MAX_BITMAPS; i++) {
if ( (bm_bitmaps[i].type == BM_TYPE_NONE) ){
first_slot = i;
break;
}
}
Assert(d3d_bitmap_entry[i].tinterface == NULL);
n = first_slot;
Assert( n < MAX_BITMAPS );
if ( n == MAX_BITMAPS ) return -1;
if(type == BM_TYPE_PCX) {
int pcx_error=pcx_read_header( filename, &w, &h, NULL );
if ( pcx_error != PCX_ERROR_NONE ) {
DBUGFILE_OUTPUT_1("bm_pcx: Cant load %s",filename);
mprintf(( "bm_pcx: Couldn't open '%s'\n", filename ));
return -1;
}
} else {
// Let D3DX handle this
if(d3d_read_header_d3dx( filename, type, &w, &h) == false) {
DBUGFILE_OUTPUT_1("not bm_pcx: Cant load %s",filename);
mprintf(( "not bm_pcx: Couldn't open '%s'\n", filename ));
return -1;
}
}
// ensure fields are cleared out from previous bitmap
memset( &bm_bitmaps[n], 0, sizeof(bitmap_entry) );
// Mark the slot as filled, because cf_read might load a new bitmap
// into this slot.
bm_bitmaps[n].type = (ubyte) type;
Assert ( strlen(filename) < MAX_FILENAME_LEN );
strncpy(bm_bitmaps[n].filename, filename, MAX_FILENAME_LEN-1 );
bm_bitmaps[n].bm.w = short(w);
bm_bitmaps[n].bm.rowsize = short(w);
bm_bitmaps[n].bm.h = short(h);
bm_bitmaps[n].bm.bpp = 0;
bm_bitmaps[n].bm.flags = 0;
bm_bitmaps[n].bm.data = 0;
bm_bitmaps[n].bm.palette = NULL;
bm_bitmaps[n].palette_checksum = 0;
bm_bitmaps[n].handle = bm_get_next_handle()*MAX_BITMAPS + n;
bm_bitmaps[n].last_used = -1;
d3d_bitmap_entry[n].tinterface = NULL;
return bm_bitmaps[n].handle;
}
// special load function. basically allows you to load a bitmap which already exists (by filename).
// this is useful because in some cases we need to have a bitmap which is locked in screen format
// _and_ texture format, such as pilot pics and squad logos
int bm_d3d_load_duplicate(char *filename)
{
int ret;
// ignore duplicates
Bm_d3d_ignore_duplicates = 1;
// load
ret = bm_load(filename);
// back to normal
Bm_d3d_ignore_duplicates = 0;
return ret;
}
static int find_block_of(int n)
{
int i, cnt, nstart;
cnt=0;
nstart = 0;
for (i=0; i<MAX_BITMAPS; i++ ) {
if ( bm_bitmaps[i].type == BM_TYPE_NONE ) {
if (cnt==0) nstart = i;
cnt++;
} else
cnt=0;
if ( cnt == n ) return nstart;
}
// Error( LOCATION, "Couldn't find block of %d frames\n", n );
return -1;
}
// ------------------------------------------------------------------
// bm_load_animation()
//
// input: filename => filename of animation
// nframes => OUTPUT parameter: number of frames in the animation
// fps => OUTPUT/OPTIONAL parameter: intended fps for the animation
//
// returns: bitmap number of first frame in the animation
//
int bm_d3d_load_animation( char *real_filename, int *nframes, int *fps, int can_drop_frames, int dir_type)
{
Assert(bm_d3d_inited);
int i, n;
anim the_anim;
CFILE *fp;
char filename[MAX_FILENAME_LEN];
strcpy( filename, real_filename );
char *p = strchr( filename, '.' );
if ( p ) {
mprintf(( "Someone passed an extension to bm_load_animation for file '%s'\n", real_filename ));
//Int3();
*p = 0;
}
strcat( filename, ".ani" );
if ( (fp = cfopen(filename, "rb", CFILE_NORMAL, dir_type)) == NULL ) {
return -1;
}
int reduced = 0;
#ifndef NDEBUG
// for debug of ANI sizes
strcpy(the_anim.name, real_filename);
#endif
anim_read_header(&the_anim, fp);
cfclose(fp);
*nframes = the_anim.total_frames;
if ( fps != NULL ) {
if ( reduced ) {
*fps = the_anim.fps / 2;
} else {
*fps = the_anim.fps;
}
}
// first check to see if this ani already has it's frames loaded
for (i = 0; i < MAX_BITMAPS; i++) {
if ( (bm_bitmaps[i].type == BM_TYPE_ANI) && !stricmp(filename, bm_bitmaps[i].filename) ) {
break;
}
}
if ( i < MAX_BITMAPS ) {
Assert(bm_bitmaps[i].info.ani.num_frames == *nframes);
return bm_bitmaps[i].handle;
}
n = find_block_of(*nframes);
if(n < 0){
return -1;
}
int first_handle = bm_get_next_handle();
Assert ( strlen(filename) < MAX_FILENAME_LEN );
for ( i = 0; i < *nframes; i++ ) {
memset( &bm_bitmaps[n+i], 0, sizeof(bitmap_entry) );
bm_bitmaps[n+i].info.ani.first_frame = n;
bm_bitmaps[n+i].info.ani.num_frames = ubyte(the_anim.total_frames);
bm_bitmaps[n+i].info.ani.fps = ubyte(the_anim.fps);
bm_bitmaps[n+i].bm.w = short(the_anim.width);
bm_bitmaps[n+i].bm.rowsize = short(the_anim.width);
bm_bitmaps[n+i].bm.h = short(the_anim.height);
if ( reduced ) {
bm_bitmaps[n+i].bm.w /= 2;
bm_bitmaps[n+i].bm.rowsize /= 2;
bm_bitmaps[n+i].bm.h /= 2;
}
bm_bitmaps[n+i].bm.flags = 0;
bm_bitmaps[n+i].bm.bpp = 0;
bm_bitmaps[n+i].bm.data = 0;
bm_bitmaps[n+i].bm.palette = NULL;
bm_bitmaps[n+i].type = BM_TYPE_ANI;
bm_bitmaps[n+i].palette_checksum = 0;
bm_bitmaps[n+i].handle = first_handle*MAX_BITMAPS + n+i;
bm_bitmaps[n+i].last_used = -1;
if ( i == 0 ) {
sprintf( bm_bitmaps[n+i].filename, "%s", filename );
} else {
sprintf( bm_bitmaps[n+i].filename, "%s[%d]", filename, i );
}
}
return bm_bitmaps[n].handle;
}
// Gets info. w,h,or flags,nframes or fps can be NULL if you don't care.
void bm_d3d_get_info( int handle, int *w, int * h, ubyte * flags, int *nframes, int *fps, bitmap_section_info **sections )
{
Assert(bm_d3d_inited);
bitmap * bmp;
if (sections != NULL) *sections = NULL;
int bitmapnum = handle % MAX_BITMAPS;
Assert( bm_bitmaps[bitmapnum].handle == handle ); // INVALID BITMAP HANDLE!
if ( (bm_bitmaps[bitmapnum].type == BM_TYPE_NONE) || (bm_bitmaps[bitmapnum].handle != handle) ) {
if (w) *w = 0;
if (h) *h = 0;
if (flags) *flags = 0;
if (nframes) *nframes=0;
if (fps) *fps=0;
return;
}
bmp = &(bm_bitmaps[bitmapnum].bm);
if (w) *w = bmp->w;
if (h) *h = bmp->h;
if (flags) *flags = bmp->flags;
if ( bm_bitmaps[bitmapnum].type == BM_TYPE_ANI ) {
if (nframes) {
*nframes = bm_bitmaps[bitmapnum].info.ani.num_frames;
}
if (fps) {
*fps= bm_bitmaps[bitmapnum].info.ani.fps;
}
} else {
if (nframes) {
*nframes = 1;
}
if (fps) {
*fps= 0;
}
}
}
uint bm_d3d_get_signature( int handle )
{
Assert(bm_d3d_inited);
int bitmapnum = handle % MAX_BITMAPS;
Assert( bm_bitmaps[bitmapnum].handle == handle ); // INVALID BITMAP HANDLE
return bm_bitmaps[bitmapnum].signature;
}
static void bm_d3d_convert_format( int bitmapnum, bitmap *bmp, ubyte bpp, ubyte flags )
{
int idx;
if(flags & BMP_AABITMAP){
// Assert(bmp->bpp == 8);
} else {
Assert(bmp->bpp == 16);
}
// maybe swizzle to be an xparent texture
if(!(bmp->flags & BMP_TEX_XPARENT) && (flags & BMP_TEX_XPARENT)) {
for(idx=0; idx<bmp->w*bmp->h; idx++){
// if the pixel is transparent
if ( ((ushort*)bmp->data)[idx] == Gr_t_green.mask) {
((ushort*)bmp->data)[idx] = 0;
}
}
bmp->flags |= BMP_TEX_XPARENT;
}
}
void bm_d3d_lock_pcx( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
{
ubyte *data, *palette;
ubyte pal[768];
palette = NULL;
// Unload any existing data
bm_d3d_free_data( bitmapnum );
// allocate bitmap data
if(bpp == 16 && Cmdline_pcx32) {
bpp = 32;
}
data = (ubyte *)bm_d3d_malloc(bitmapnum, bmp->w * bmp->h * (bpp / 8));
bmp->data = (uint)data;
memset( data, 0, bmp->w * bmp->h * (bpp / 8));
bmp->bpp = bpp;
if(bpp == 8){
palette = pal;
bmp->palette = gr_palette;
} else {
bmp->palette = NULL;
}
Assert( &be->bm == bmp );
// some sanity checks on flags
Assert(!((flags & BMP_AABITMAP) && (flags & BMP_TEX_ANY))); // no aabitmap textures
Assert(!((flags & BMP_TEX_XPARENT) && (flags & BMP_TEX_NONDARK))); // can't be a transparent texture and a nondarkening texture
if(bpp == 8){
int pcx_error=pcx_read_bitmap_8bpp( be->filename, data, palette );
if ( pcx_error != PCX_ERROR_NONE ) {
//return -1;
}
} else {
int pcx_error;
if(bpp == 16){
// load types
Assert(!(flags & BMP_AABITMAP));
Assert(!(flags & BMP_TEX_NONDARK));
pcx_error = pcx_read_bitmap_16bpp( be->filename, data );
bm_d3d_convert_format( bitmapnum, bmp, bpp, flags );
} else {
pcx_error = pcx_read_bitmap_32( be->filename, data );
}
Assert(pcx_error == PCX_ERROR_NONE );
}
}
void bm_d3d_lock_ani( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
{
anim *the_anim;
anim_instance *the_anim_instance;
bitmap *bm;
ubyte *frame_data;
int size, i;
int first_frame, nframes;
first_frame = be->info.ani.first_frame;
nframes = bm_bitmaps[first_frame].info.ani.num_frames;
if ( (the_anim = anim_load(bm_bitmaps[first_frame].filename)) == NULL ) {
// Error(LOCATION, "Error opening %s in bm_lock\n", be->filename);
}
if ( (the_anim_instance = init_anim_instance(the_anim, bpp)) == NULL ) {
// Error(LOCATION, "Error opening %s in bm_lock\n", be->filename);
anim_free(the_anim);
}
int can_drop_frames = 0;
if ( the_anim->total_frames != bm_bitmaps[first_frame].info.ani.num_frames ) {
can_drop_frames = 1;
}
bm = &bm_bitmaps[first_frame].bm;
size = bm->w * bm->h * bpp / 8;
for ( i=0; i<nframes; i++ ) {
be = &bm_bitmaps[first_frame+i];
bm = &bm_bitmaps[first_frame+i].bm;
// Unload any existing data
bm_d3d_free_data( first_frame+i );
bm->flags = 0;
bm->bpp = bpp;
bm->data = (uint)bm_d3d_malloc(first_frame + i, size);
frame_data = anim_get_next_raw_buffer(the_anim_instance, 0 ,flags & BMP_AABITMAP ? 1 : 0, bm->bpp);
if ( frame_data == NULL ) {
// Error(LOCATION,"Fatal error locking .ani file: %s\n", be->filename);
}
ubyte *dptr, *sptr;
sptr = frame_data;
dptr = (ubyte *)bm->data;
if ( (bm->w!=the_anim->width) || (bm->h!=the_anim->height) ) {
// Scale it down
// Int3(); // not ready yet - should only be ingame
// 8 bit
if(bpp == 8){
int w,h;
fix u, utmp, v, du, dv;
u = v = 0;
du = ( the_anim->width*F1_0 ) / bm->w;
dv = ( the_anim->height*F1_0 ) / bm->h;
for (h = 0; h < bm->h; h++) {
ubyte *drow = &dptr[bm->w * h];
ubyte *srow = &sptr[f2i(v)*the_anim->width];
utmp = u;
for (w = 0; w < bm->w; w++) {
*drow++ = srow[f2i(utmp)];
utmp += du;
}
v += dv;
}
}
// 16 bpp
else {
int w,h;
fix u, utmp, v, du, dv;
u = v = 0;
du = ( the_anim->width*F1_0 ) / bm->w;
dv = ( the_anim->height*F1_0 ) / bm->h;
for (h = 0; h < bm->h; h++) {
ushort *drow = &((ushort*)dptr)[bm->w * h];
ushort *srow = &((ushort*)sptr)[f2i(v)*the_anim->width];
utmp = u;
for (w = 0; w < bm->w; w++) {
*drow++ = srow[f2i(utmp)];
utmp += du;
}
v += dv;
}
}
} else {
// 1-to-1 mapping
memcpy(dptr, sptr, size);
}
bm_d3d_convert_format( first_frame+i, bm, bpp, flags );
// Skip a frame
if ( (i < nframes-1) && can_drop_frames ) {
frame_data = anim_get_next_raw_buffer(the_anim_instance, 0, flags & BMP_AABITMAP ? 1 : 0, bm->bpp);
}
//mprintf(( "Checksum = %d\n", be->palette_checksum ));
}
free_anim_instance(the_anim_instance);
anim_free(the_anim);
}
void bm_d3d_lock_user( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
{
// Unload any existing data
bm_d3d_free_data( bitmapnum );
switch( be->info.user.bpp ) {
case 16: // user 16 bit bitmap
bmp->bpp = bpp;
bmp->flags = be->info.user.flags;
bmp->data = (uint)be->info.user.data;
break;
case 8: // Going from 8 bpp to something (probably only for aabitmaps)
// Assert(flags & BMP_AABITMAP);
bmp->bpp = bpp;
bmp->flags = be->info.user.flags;
bmp->data = (uint)be->info.user.data;
break;
}
bm_d3d_convert_format( bitmapnum, bmp, bpp, flags );
}
bool d3d_lock_and_set_internal_texture(int stage, int handle, ubyte bpp, ubyte flags, float *u_scale, float *v_scale )
{
int bitmapnum = handle % MAX_BITMAPS;
Assert( bm_bitmaps[bitmapnum].handle == handle ); // INVALID BITMAP HANDLE
int valid_type =
bm_bitmaps[bitmapnum].type == BM_TYPE_TGA ||
bm_bitmaps[bitmapnum].type == BM_TYPE_DDS ||
bm_bitmaps[bitmapnum].type == BM_TYPE_JPG;
if(valid_type)
{
// There is no internal texture
bm_d3d_lock(handle, bpp, flags );
bm_unlock(handle);
if(u_scale) *u_scale = d3d_bitmap_entry[bitmapnum].uscale;
if(v_scale) *v_scale = d3d_bitmap_entry[bitmapnum].vscale;
d3d_SetTexture(stage, d3d_bitmap_entry[bitmapnum].tinterface);
return true;
}
if(Cmdline_d3d_lesstmem && bm_bitmaps[bitmapnum].type == BM_TYPE_PCX)
{
if(d3d_bitmap_entry[bitmapnum].tinterface == NULL)
{
// Trick the normal functions into getting the info we need
tcache_slot_d3d t;
t.bitmap_id = -1;
d3d_create_texture(handle, flags, &t, 0);
d3d_bitmap_entry[bitmapnum].uscale = t.u_scale;
d3d_bitmap_entry[bitmapnum].vscale = t.v_scale;
d3d_bitmap_entry[bitmapnum].tinterface = t.d3d8_thandle;
free((void *) bm_bitmaps[bitmapnum].bm.data);
bm_bitmaps[bitmapnum].bm.data = NULL;
}
if(u_scale) *u_scale = d3d_bitmap_entry[bitmapnum].uscale;
if(v_scale) *v_scale = d3d_bitmap_entry[bitmapnum].vscale;
d3d_SetTexture(stage, d3d_bitmap_entry[bitmapnum].tinterface);
return true;
}
return false;
}
// This locks down a bitmap and returns a pointer to a bitmap
// that can be accessed until you call bm_unlock. Only lock
// a bitmap when you need it! This will convert it into the
// appropriate format also.
bitmap * bm_d3d_lock( int handle, ubyte bpp, ubyte flags )
{
Assert(bm_d3d_inited);
bitmap *bmp;
bitmap_entry *be;
int bitmapnum = handle % MAX_BITMAPS;
Assert( bm_bitmaps[bitmapnum].handle == handle ); // INVALID BITMAP HANDLE
// if we're on a standalone server, aways for it to lock to 8 bits
if(Is_standalone){
bpp = 8;
flags = 0;
}
// otherwise do it as normal
else if(flags & BMP_AABITMAP){
// Assert( bpp == 8 );
}
else {
Assert( bpp == 16 );
}
be = &bm_bitmaps[bitmapnum];
bmp = &be->bm;
// If you hit this assert, chances are that someone freed the
// wrong bitmap and now someone is trying to use that bitmap.
// See John.
Assert( be->type != BM_TYPE_NONE );
// Increment ref count for bitmap since lock was made on it.
Assert(be->ref_count >= 0);
be->ref_count++; // Lock it before we page in data; this prevents a callback from freeing this
// as it gets read in
// Mark this bitmap as used this frame
// if bitmap hasn't been loaded yet, then load it from disk
// reread the bitmap from disk under certain conditions
if(be->type > BM_TYPE_32_BIT_FORMATS)
{
if(d3d_bitmap_entry[bitmapnum].tinterface == NULL)
{
switch ( be->type ) {
// We'll let D3DX handle this
case BM_TYPE_JPG:
case BM_TYPE_DDS:
case BM_TYPE_TGA:
{
d3d_bitmap_entry[bitmapnum].tinterface =
(IDirect3DBaseTexture8 *) d3d_lock_d3dx_types(be->filename, be->type, flags, bitmapnum);
break;
}
default:
Warning(LOCATION, "Unsupported type in bm_lock -- %d\n", be->type );
return NULL;
}
}
}
/*
else if (be->type == BM_TYPE_PCX)
{
if(d3d_bitmap_entry[bitmapnum].tinterface == NULL)
{
// select proper format
if(flags & BMP_AABITMAP){
BM_SELECT_ALPHA_TEX_FORMAT();
} else if(flags & BMP_TEX_ANY){
BM_SELECT_TEX_FORMAT();
} else {
BM_SELECT_SCREEN_FORMAT();
}
bm_d3d_lock_pcx( handle, bitmapnum, be, bmp, bpp, flags );
}
}
*/
else if ( (bmp->data == 0) || (bpp != bmp->bpp && bmp->bpp != 32)) {
Assert(be->ref_count == 1);
if ( be->type != BM_TYPE_USER ) {
if ( bmp->data == 0 ) {
nprintf (("BmpMan","Loading %s for the first time.\n", be->filename));
} else if ( bpp != bmp->bpp ) {
nprintf (("BmpMan","Reloading %s from bitdepth %d to bitdepth %d\n", be->filename, bmp->bpp, bpp));
}
}
// select proper format
if(flags & BMP_AABITMAP){
BM_SELECT_ALPHA_TEX_FORMAT();
} else if(flags & BMP_TEX_ANY){
BM_SELECT_TEX_FORMAT();
} else {
BM_SELECT_SCREEN_FORMAT();
}
switch ( be->type ) {
case BM_TYPE_PCX:
bm_d3d_lock_pcx( handle, bitmapnum, be, bmp, bpp, flags );
break;
case BM_TYPE_ANI:
bm_d3d_lock_ani( handle, bitmapnum, be, bmp, bpp, flags );
break;
case BM_TYPE_USER:
bm_d3d_lock_user( handle, bitmapnum, be, bmp, bpp, flags );
break;
default:
Warning(LOCATION, "Unsupported type in bm_lock -- %d\n", be->type );
return NULL;
}
// always go back to screen format
BM_SELECT_SCREEN_FORMAT();
}
if ( be->type == BM_TYPE_ANI ) {
int i,first = bm_bitmaps[bitmapnum].info.ani.first_frame;
for ( i=0; i< bm_bitmaps[first].info.ani.num_frames; i++ ) {
// Mark all the bitmaps in this bitmap or animation as recently used
bm_bitmaps[first+i].last_used = timer_get_milliseconds();
// Mark all the bitmaps in this bitmap or animation as used for the usage tracker.
bm_bitmaps[first+i].used_flags = flags;
}
} else {
// Mark all the bitmaps in this bitmap or animation as recently used
be->last_used = timer_get_milliseconds();
// Mark all the bitmaps in this bitmap or animation as used for the usage tracker.
be->used_flags = flags;
}
return bmp;
}
// Unlocks a bitmap
//
// Decrements the ref_count member of the bitmap_entry struct. A bitmap can only be unloaded
// when the ref_count is 0.
//
void bm_d3d_unlock( int handle )
{
Assert(bm_d3d_inited);
bitmap_entry *be;
bitmap *bmp;
int bitmapnum = handle % MAX_BITMAPS;
Assert( bm_bitmaps[bitmapnum].handle == handle ); // INVALID BITMAP HANDLE
Assert(bitmapnum >= 0 && bitmapnum < MAX_BITMAPS);
be = &bm_bitmaps[bitmapnum];
bmp = &be->bm;
be->ref_count--;
Assert(be->ref_count >= 0); // Trying to unlock data more times than lock was called!!!
}
void bm_d3d_get_palette(int handle, ubyte *pal, char *name)
{
char *filename;
int w,h;
int n= handle % MAX_BITMAPS;
Assert( bm_bitmaps[n].handle == handle ); // INVALID BITMAP HANDLE
filename = bm_bitmaps[n].filename;
if (name) {
strcpy( name, filename );
}
int pcx_error=pcx_read_header( filename, &w, &h, pal );
if ( pcx_error != PCX_ERROR_NONE ){
// Error(LOCATION, "Couldn't open '%s'\n", filename );
}
}
// --------------------------------------------------------------------------------------
// bm_release() - unloads the bitmap's data and entire slot, so bitmap 'n' won't be valid anymore
//
// parameters: n => index into bm_bitmaps ( index returned from bm_load() or bm_create() )
//
// returns: nothing
void bm_d3d_release(int handle)
{
bitmap_entry *be;
int n = handle % MAX_BITMAPS;
Assert(n >= 0 && n < MAX_BITMAPS);
be = &bm_bitmaps[n];
if ( bm_bitmaps[n].type == BM_TYPE_NONE ) {
return; // Already been released?
}
if ( bm_bitmaps[n].type != BM_TYPE_USER ) {
return;
}
Assert( be->handle == handle ); // INVALID BITMAP HANDLE
// If it is locked, cannot free it.
if (be->ref_count != 0) {
nprintf(("BmpMan", "tried to unload %s that has a lock count of %d.. not unloading\n", be->filename, be->ref_count));
return;
}
bm_d3d_free_data(n);
if ( bm_bitmaps[n].type == BM_TYPE_USER ) {
bm_bitmaps[n].info.user.data = NULL;
bm_bitmaps[n].info.user.bpp = 0;
}
bm_bitmaps[n].type = BM_TYPE_NONE;
// Fill in bogus structures!
// For debugging:
strcpy( bm_bitmaps[n].filename, "IVE_BEEN_RELEASED!" );
bm_bitmaps[n].signature = 0xDEADBEEF; // a unique signature identifying the data
bm_bitmaps[n].palette_checksum = 0xDEADBEEF; // checksum used to be sure bitmap is in current palette
// bookeeping
bm_bitmaps[n].ref_count = -1; // Number of locks on bitmap. Can't unload unless ref_count is 0.
// Bitmap info
bm_bitmaps[n].bm.w = bm_bitmaps[n].bm.h = -1;
// Stuff needed for animations
// Stuff needed for user bitmaps
memset( &bm_bitmaps[n].info, 0, sizeof(bm_extra_info) );
bm_bitmaps[n].handle = -1;
}
// --------------------------------------------------------------------------------------
// bm_unload() - unloads the data, but not the bitmap info.
//
// parameters: n => index into bm_bitmaps ( index returned from bm_load() or bm_create() )
//
// returns: 0 => unload failed
// 1 => unload successful
//
int bm_d3d_unload( int handle )
{
bitmap_entry *be;
bitmap *bmp;
int n = handle % MAX_BITMAPS;
Assert(n >= 0 && n < MAX_BITMAPS);
be = &bm_bitmaps[n];
bmp = &be->bm;
if ( be->type == BM_TYPE_NONE ) {
return 0; // Already been released
}
Assert( be->handle == handle ); // INVALID BITMAP HANDLE!
if(stricmp("2_Credits.jpg", be->filename) == 0)
{
mprintf(("unloading 2_Credits.jpg\n"));
}
// If it is locked, cannot free it.
if (be->ref_count != 0) {
nprintf(("BmpMan", "tried to unload %s that has a lock count of %d.. not unloading\n", be->filename, be->ref_count));
return 0;
}
nprintf(("BmpMan", "unloading %s. %dx%dx%d\n", be->filename, bmp->w, bmp->h, bmp->bpp));
bm_d3d_free_data(n); // clears flags, bbp, data, etc
return 1;
}
// unload all used bitmaps
void bm_d3d_unload_all()
{
for (int i = 0; i < MAX_BITMAPS; i++) {
if ( bm_bitmaps[i].type != BM_TYPE_NONE ) {
bm_unload(bm_bitmaps[i].handle);
}
}
}
// Marks a texture as being used for this level
void bm_d3d_page_in_texture( int bitmapnum, int nframes )
{
int n = bitmapnum % MAX_BITMAPS;
for (int i=0; i<nframes;i++ ) {
bm_bitmaps[n+i].preloaded = 1;
if ( D3D_enabled ) {
bm_bitmaps[n+i].used_flags = BMP_TEX_OTHER;
} else {
bm_bitmaps[n+i].used_flags = 0;
}
}
}
// Marks a texture as being used for this level
// If num_frames is passed, assume this is an animation
void bm_d3d_page_in_nondarkening_texture( int bitmapnum, int nframes )
{
int n = bitmapnum % MAX_BITMAPS;
for (int i=0; i<nframes;i++ ) {
bm_bitmaps[n+i].preloaded = 4;
if ( D3D_enabled ) {
bm_bitmaps[n+i].used_flags = BMP_TEX_NONDARK;
} else {
bm_bitmaps[n+i].used_flags = 0;
}
}
}
// marks a texture as being a transparent textyre used for this level
// Marks a texture as being used for this level
// If num_frames is passed, assume this is an animation
void bm_d3d_page_in_xparent_texture( int bitmapnum, int nframes)
{
int n = bitmapnum % MAX_BITMAPS;
for (int i=0; i<nframes;i++ ) {
bm_bitmaps[n+i].preloaded = 3;
if ( D3D_enabled ) {
bm_bitmaps[n+i].used_flags = BMP_TEX_XPARENT;
} else {
bm_bitmaps[n+i].used_flags = 0;
}
}
}
// Marks an aabitmap as being used for this level
void bm_d3d_page_in_aabitmap( int bitmapnum, int nframes )
{
int n = bitmapnum % MAX_BITMAPS;
for (int i=0; i<nframes;i++ ) {
bm_bitmaps[n+i].preloaded = 2;
if ( D3D_enabled ) {
bm_bitmaps[n+i].used_flags = BMP_AABITMAP;
} else {
bm_bitmaps[n+i].used_flags = 0;
}
}
}
// Tell the bitmap manager to start keeping track of what bitmaps are used where.
void bm_d3d_page_in_start()
{
// Mark all as inited
for (int i = 0; i < MAX_BITMAPS; i++) {
if ( bm_bitmaps[i].type != BM_TYPE_NONE ) {
bm_unload(bm_bitmaps[i].handle);
}
bm_bitmaps[i].preloaded = 0;
bm_bitmaps[i].used_flags = 0;
}
GlobalD3DVars::lpD3DDevice->ResourceManagerDiscardBytes(0);
}
void bm_d3d_page_in_stop()
{
int ship_info_index;
extern void gr_d3d_preload_init();
extern int gr_d3d_preload(int bitmap_num, int is_aabitmap );
int i;
nprintf(( "BmpInfo","BMPMAN: Loading all used bitmaps.\n" ));
// Load all the ones that are supposed to be loaded for this level.
int n = 0;
int d3d_preloading = 1;
gr_d3d_preload_init();
for (i = 0; i < MAX_BITMAPS; i++) {
if ( bm_bitmaps[i].type != BM_TYPE_NONE ) {
if ( bm_bitmaps[i].preloaded ) {
#ifdef BMPMAN_SPECIAL_NONDARK
// if this is a texture, check to see if a ship uses it
ship_info_index = ship_get_texture(bm_bitmaps[i].handle);
// use the colors from this ship
if((ship_info_index >= 0) && (Ship_info[ship_info_index].num_nondark_colors > 0)){
// mprintf(("Using custom pixels for %s\n", Ship_info[ship_info_index].name));
palman_set_nondarkening(Ship_info[ship_info_index].nondark_colors, Ship_info[ship_info_index].num_nondark_colors);
}
// use the colors from the default table
else {
// mprintf(("Using default pixels\n"));
palman_set_nondarkening(Palman_non_darkening_default, Palman_num_nondarkening_default);
}
#endif
// if preloaded == 3, load it as an xparent texture
if(bm_bitmaps[i].used_flags == BMP_AABITMAP){
bm_lock( bm_bitmaps[i].handle, 8, bm_bitmaps[i].used_flags );
} else {
bm_lock( bm_bitmaps[i].handle, 16, bm_bitmaps[i].used_flags );
}
bm_unlock( bm_bitmaps[i].handle );
if ( d3d_preloading ) {
if ( !gr_d3d_preload(bm_bitmaps[i].handle, (bm_bitmaps[i].preloaded==2) ) ) {
mprintf(( "Out of VRAM. Done preloading.\n" ));
d3d_preloading = 0;
}
}
n++;
}
}
game_busy();
}
nprintf(( "BmpInfo","BMPMAN: Loaded %d bitmaps that are marked as used for this level.\n", n ));
int total_bitmaps = 0;
for (i = 0; i < MAX_BITMAPS; i++) {
if ( bm_bitmaps[i].type != BM_TYPE_NONE ) {
total_bitmaps++;
}
if ( bm_bitmaps[i].type == BM_TYPE_USER ) {
mprintf(( "User bitmap '%s'\n", bm_bitmaps[i].filename ));
}
}
mprintf(( "Bmpman: %d/%d bitmap slots in use.\n", total_bitmaps, MAX_BITMAPS ));
}
int bm_d3d_get_cache_slot( int bitmap_id, int separate_ani_frames )
{
int n = bitmap_id % MAX_BITMAPS;
Assert( bm_bitmaps[n].handle == bitmap_id ); // INVALID BITMAP HANDLE
bitmap_entry *be = &bm_bitmaps[n];
if ( (!separate_ani_frames) && (be->type == BM_TYPE_ANI) ) {
return be->info.ani.first_frame;
}
return n;
}
// get the rgba components of a pixel, any of the parameters can be NULL
void bm_d3d_get_components(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a)
{
extern int D3D_32bit;
int bit_32 = 0;
// pick a byte size - 32 bits only if 32 bit mode d3d and screen format
if(D3D_32bit && (Gr_current_red == &Gr_red)){
bit_32 = 1;
}
if(r != NULL){
if(bit_32){
*r = ubyte(( (*((uint*)pixel) & Gr_current_red->mask)>>Gr_current_red->shift)*Gr_current_red->scale);
} else {
*r = ubyte(( ( ((ushort*)pixel)[0] & Gr_current_red->mask)>>Gr_current_red->shift)*Gr_current_red->scale);
}
}
if(g != NULL){
if(bit_32){
*g = ubyte(( (*((uint*)pixel) & Gr_current_green->mask) >>Gr_current_green->shift)*Gr_current_green->scale);
} else {
*g = ubyte(( ( ((ushort*)pixel)[0] & Gr_current_green->mask) >>Gr_current_green->shift)*Gr_current_green->scale);
}
}
if(b != NULL){
if(bit_32){
*b = ubyte(( (*((uint*)pixel) & Gr_current_blue->mask)>>Gr_current_blue->shift)*Gr_current_blue->scale);
} else {
*b = ubyte(( ( ((ushort*)pixel)[0] & Gr_current_blue->mask)>>Gr_current_blue->shift)*Gr_current_blue->scale);
}
}
// get the alpha value - this is never asked for
if(a != NULL){
*a = 1;
Assert(!bit_32);
if(!( ((ushort*)pixel)[0] & 0x8000)){
*a = 0;
}
}
}
// given a bitmap and a section, return the size (w, h)
void bm_d3d_get_section_size(int bitmapnum, int sx, int sy, int *w, int *h)
{
int bw, bh;
// bogus input?
Assert((w != NULL) && (h != NULL));
if((w == NULL) || (h == NULL)){
return;
}
// get bitmap info
bm_get_info(bitmapnum, &bw, &bh, NULL, NULL, NULL, NULL);
// determine the width and height of this section
*w = bw;
*h = bh;
}