mirror of
https://github.com/kevinbentley/Descent3.git
synced 2025-01-22 11:28:56 +00:00
1551b240d3
``` git grep -l mem_malloc | xargs perl -i -lpe 's{\((\w+) \*\*\)mem_malloc\((\S+) \* sizeof\(\1 \*\)\)}{mem_rmalloc<$1 *>($2)}' ```
1557 lines
43 KiB
C++
1557 lines
43 KiB
C++
/*
|
|
* Descent 3
|
|
* Copyright (C) 2024 Parallax Software
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
--- HISTORICAL COMMENTS FOLLOW ---
|
|
|
|
* $Logfile: /DescentIII/Main/bitmap/bitmain.cpp $
|
|
* $Revision: 70 $
|
|
* $Date: 5/10/00 5:09p $
|
|
* $Author: Matt $
|
|
*
|
|
* Bitmap handling functions
|
|
*
|
|
* $Log: /DescentIII/Main/bitmap/bitmain.cpp $
|
|
*
|
|
* 70 5/10/00 5:09p Matt
|
|
* Added casts so code would compile under Visual C++.
|
|
*
|
|
* 69 5/10/00 4:34p Jeff
|
|
* handle buggy tablefile editors that put the full path name to a
|
|
* primative instead of just the filename
|
|
*
|
|
* 68 4/19/00 5:29p Matt
|
|
* From Duane for 1.4
|
|
* Added Mac-only error handling
|
|
* Added checks & asserts for error return values
|
|
*
|
|
* 67 4/04/00 6:09p Matt
|
|
* Fixed two problems in the hash code: the hash values were
|
|
* case-sensitive, which caused misses with mixed-case filenames, and hash
|
|
* table nodes were not freed when a bitmap was freed.
|
|
*
|
|
* 66 3/20/00 12:23p Matt
|
|
* Merge of Duane's post-1.3 changes.
|
|
* Small optimization
|
|
* Error check
|
|
*
|
|
* 65 2/06/00 3:41a Jason
|
|
* fixed memory overrun error in bm_ChangeEndName function
|
|
*
|
|
* 64 10/21/99 9:27p Jeff
|
|
* B.A. Macintosh code merge
|
|
*
|
|
* 63 8/10/99 5:10p Jeff
|
|
* delete the hash table on bitmap lib shutdown...just to be clean
|
|
*
|
|
* 62 7/28/99 5:18p Kevin
|
|
* Mac merge fixes
|
|
*
|
|
* 61 7/28/99 3:37p Kevin
|
|
* Mac!
|
|
*
|
|
* 60 4/29/99 4:59p Jason
|
|
* fixed some preuploaded texture problems
|
|
*
|
|
* 59 4/23/99 10:45p Jeff
|
|
* fixed crash with freeing hash table, set it to NULL to prevent double
|
|
* free
|
|
*
|
|
* 58 4/21/99 11:05a Kevin
|
|
* new ps_rand and ps_srand to replace rand & srand
|
|
*
|
|
* 57 4/16/99 11:51p Jeff
|
|
* renamed strcmpi to stricmp to be linux nice
|
|
*
|
|
* 56 4/15/99 2:56p Kevin
|
|
* Removed unneeded mprintf and unused variable
|
|
*
|
|
* 55 4/15/99 12:36p Kevin
|
|
* Changed bm_FindBitmapName to use a hash table.
|
|
*
|
|
* 54 4/14/99 1:07a Jeff
|
|
* fixed case mismatched #includes
|
|
*
|
|
* 53 3/23/99 10:24a Samir
|
|
* NULL pointer in bm_DestroyChunkedBitmap.
|
|
*
|
|
* 52 2/12/99 12:14p Jason
|
|
* fixed memory scaling problem
|
|
*
|
|
* 51 1/19/99 11:17a Jason
|
|
* fixed another scaling problem
|
|
*
|
|
* 50 1/14/99 2:26p Jason
|
|
* made data tracking more reliable
|
|
*
|
|
* 49 1/04/99 6:19p Jason
|
|
* fixed bitmap format problem
|
|
*
|
|
* 48 12/21/98 11:47a Jason
|
|
* fixed bitmap problem with 4444 mipping
|
|
*
|
|
* 47 12/21/98 11:21a Josh
|
|
* FROM JASON: Fixed format/mip problem
|
|
*
|
|
* 46 11/30/98 5:50p Jason
|
|
* added 4444 bitmap support
|
|
*
|
|
* 45 10/21/98 4:36p Jason
|
|
* more changes for renderering speedups
|
|
*
|
|
* 44 10/19/98 4:22p Jason
|
|
* more fixes for Beta5
|
|
*
|
|
* 43 10/08/98 2:27p Jason
|
|
* sped up table file loading
|
|
*
|
|
* 42 9/25/98 12:01p Samir
|
|
* for ndebug builds, removed the return immediately case in
|
|
* bm_AllocLoadFileBitmap.
|
|
*
|
|
* 41 8/21/98 5:14p Jason
|
|
* made better memory use of primitives
|
|
*
|
|
* 40 7/14/98 10:58a Jason
|
|
* fixed transparency bug in error texture
|
|
*
|
|
* 39 5/27/98 5:17p Jason
|
|
* fixed some bugs for the E3 Demo
|
|
*
|
|
* 38 5/20/98 5:43p Jason
|
|
* incremental checkin for bumpmapping
|
|
*
|
|
* 37 5/07/98 3:30p Jeff
|
|
* optimized CreateChunkBitmap a bit
|
|
*
|
|
* 36 5/05/98 1:01p Jeff
|
|
* improved on Chunk bitmaps
|
|
*
|
|
* 35 4/23/98 6:38p Jason
|
|
* made bitmaps use 1555 format
|
|
*
|
|
* 34 4/22/98 12:10p Chris
|
|
* Fixed path length problems
|
|
*
|
|
* 33 4/03/98 12:23p Jason
|
|
* dealt with overlay types being loaded from disk more than once
|
|
*
|
|
* 32 3/31/98 3:48p Jason
|
|
* added memory lib
|
|
*
|
|
* 31 3/23/98 4:42p Jason
|
|
* took out dumb ifdef
|
|
*
|
|
* 30 3/19/98 3:18p Samir
|
|
* enforce constant char* arguments when needed. done in CFILE and bitmap
|
|
* libraries as well as ddio.
|
|
*
|
|
* 29 3/17/98 4:33p Jason
|
|
* Added size changing to bitmaps/textures
|
|
*
|
|
* 28 2/17/98 4:57p Jason
|
|
* upped bitmap counts
|
|
*
|
|
* 27 2/13/98 7:23p Brent
|
|
* fixed mipping bug
|
|
*
|
|
* 26 2/12/98 1:32p Jason
|
|
* got mipmapping working
|
|
*
|
|
* 25 2/11/98 7:08p Jason
|
|
* took out dumb mprintf
|
|
*
|
|
* 24 2/09/98 3:38p Jason
|
|
* fixed potential problem with overlay bitmaps
|
|
*
|
|
* 23 2/06/98 7:20p Jason
|
|
* made duplicate bitmaps replace themselves in memory
|
|
*
|
|
* 22 1/26/98 4:32p Jason
|
|
* took out some goofy mprintfs
|
|
*
|
|
* 21 1/16/98 3:14p Samir
|
|
* Now store pixel width and height of chunked bitmap.
|
|
*
|
|
* 20 1/16/98 11:46a Samir
|
|
* Added functions to allocate and destroy chunked bitmaps.
|
|
*
|
|
* 19 1/14/98 12:45p Jeff
|
|
* Added a 'clear bitmap' function. Clears to transparent color.
|
|
*
|
|
* 18 12/23/97 6:32p Mark
|
|
* fixed problem with trying to save a bitmap that hasn't been paged in.
|
|
*
|
|
* 17 12/19/97 5:59p Jason
|
|
* sped up bitmap loading
|
|
*
|
|
* 16 12/19/97 3:36p Jason
|
|
* bug fixes for bitmap paging stuff
|
|
*
|
|
* 15 12/19/97 2:46p Jason
|
|
* implemented bitmap paging routines
|
|
*
|
|
* 14 12/18/97 4:03p Jason
|
|
* added error checking for bitmaps initting
|
|
*
|
|
* 13 11/11/97 1:07p Jason
|
|
* fixed multiple names problem in bitmap naming
|
|
*
|
|
* 12 11/10/97 4:53p Jason
|
|
* added warning for names that are too long
|
|
*
|
|
* 11 10/16/97 4:55p Jason
|
|
* fixed stupid off-by-one bug
|
|
*
|
|
* 10 10/16/97 10:48a Jason
|
|
* added bm_set_priority
|
|
*
|
|
* 9 10/15/97 5:20p Jason
|
|
* did a HUGE overhaul of the bitmap system
|
|
*
|
|
* 8 9/17/97 10:48a Jason
|
|
* added cache_slot variable
|
|
*
|
|
* 7 9/16/97 5:04p Matt
|
|
* Changed conditional for debug code
|
|
*
|
|
* 6 9/12/97 6:06p Samir
|
|
* Added function to get bitmap pixel.
|
|
*
|
|
* 5 9/10/97 12:13p Samir
|
|
* Added function to check if a pixel is transparent.
|
|
*
|
|
* 4 9/02/97 11:18a Jason
|
|
* got rid of compiler warnings
|
|
*
|
|
* 3 8/29/97 1:22p Jason
|
|
* added tga screenshots
|
|
*
|
|
* 2 8/05/97 10:18a Jason
|
|
* added lightmap system
|
|
*
|
|
* 46 6/24/97 12:41p Jason
|
|
* checked in for safety
|
|
*
|
|
* 45 6/06/97 3:57p Jason
|
|
* implemented changes for small textures and automatic tmap2 recognition
|
|
*
|
|
* 44 6/03/97 12:19p Jason
|
|
* cleaned up the bitmap library a bit
|
|
*
|
|
* 43 5/19/97 5:10p Jason
|
|
* changes for our new abstracted renderer code
|
|
*
|
|
* 42 5/12/97 11:41a Jason
|
|
* made game work (default) to 16bit no mip maps mode
|
|
* Saves us alot of memory
|
|
*
|
|
* 41 5/08/97 1:16p Jason
|
|
* made ChangeEndName work with device independent calls
|
|
*
|
|
* 40 5/02/97 5:22p Jason
|
|
* added bm_rowsize to return bytes per row
|
|
*
|
|
* 39 5/01/97 4:37p Jason
|
|
* made bitmaps clear their settings when alloced (bug fixed)
|
|
*
|
|
* 38 4/30/97 3:15p Jason
|
|
* changes to support both 8bit and 16bit rendering in software
|
|
*
|
|
* 37 4/25/97 3:31p Jason
|
|
* implemented better memory management for vclips and bitmaps
|
|
*
|
|
* 36 4/8/97 5:23 PM Jeremy
|
|
* #ifdef editor around calls to decrement bitmap_memory_used by checking
|
|
* _msize of an array location. _msize is microsoft specific, not cross
|
|
* platform, but this is only used by the editor anyway, thus the ifdef
|
|
* editor.
|
|
*
|
|
* 35 3/31/97 7:18p Jason
|
|
* made bitmaps easier on memory if not in editor mode
|
|
*
|
|
* 34 3/28/97 12:22p Jason
|
|
* implemented memory sharing scheme between 8 bit bitmaps
|
|
*
|
|
* 33 3/13/97 1:05p Matt
|
|
* Fixed extra-stupid bug
|
|
*
|
|
* 32 3/13/97 12:35p Matt
|
|
* Look for error1.ogf in current directory (explicitely)
|
|
*
|
|
* 31 3/03/97 6:20p Matt
|
|
* Changed cfile functions to use D3 naming convention
|
|
*
|
|
* $NoKeywords: $
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <cctype>
|
|
#include <filesystem>
|
|
|
|
#include "cfile.h"
|
|
#include "bitmap.h"
|
|
#include "pserror.h"
|
|
#include "log.h"
|
|
#include "iff.h"
|
|
#include "ddio.h"
|
|
#include "lightmap.h"
|
|
#include "bumpmap.h"
|
|
#include "mem.h"
|
|
#include "psrand.h"
|
|
#include "grdefs.h"
|
|
|
|
#define BM_FILETYPE_TGA 1
|
|
#define BM_FILETYPE_PCX 2
|
|
#define BM_FILETYPE_IFF 3
|
|
int Num_of_bitmaps = 0;
|
|
bms_bitmap GameBitmaps[MAX_BITMAPS];
|
|
uint32_t Bitmap_memory_used = 0;
|
|
uint8_t Bitmaps_initted = 0;
|
|
/* modify these lines to establish data type */
|
|
typedef bms_bitmap *bm_T; /* type of item to be stored */
|
|
typedef int bm_hashTableIndex; /* index into hash table */
|
|
#define compEQ(a, b) (stricmp((a)->name, (b)->name) == 0)
|
|
struct bm_Node {
|
|
struct bm_Node *next; /* next bm_Node */
|
|
bm_T data; /* data stored in bm_Node */
|
|
};
|
|
static bm_Node *bm_findNode(bm_T data);
|
|
static void bm_deleteNode(bm_T data);
|
|
static bm_Node *bm_insertNode(bm_T data);
|
|
static bm_hashTableIndex bm_hash(bm_T data);
|
|
static bm_Node **bm_hashTable = NULL;
|
|
static const int bm_hashTableSize = (MAX_BITMAPS / 2);
|
|
static void bm_InitHashTable();
|
|
static void bm_DeleteHashTable();
|
|
static int bm_TestName(const char *src);
|
|
|
|
void bm_InitHashTable() {
|
|
bm_hashTable = mem_rmalloc<bm_Node *>(bm_hashTableSize);
|
|
for (int a = 0; a < bm_hashTableSize; a++) {
|
|
bm_hashTable[a] = NULL;
|
|
}
|
|
}
|
|
void bm_DeleteHashTable() {
|
|
if (bm_hashTable) {
|
|
int idx;
|
|
for (idx = 0; idx < bm_hashTableSize; idx++) {
|
|
if (bm_hashTable[idx]) {
|
|
bm_Node *curr, *next;
|
|
curr = bm_hashTable[idx];
|
|
while (curr) {
|
|
next = curr->next;
|
|
mem_free(curr);
|
|
curr = next;
|
|
}
|
|
bm_hashTable[idx] = NULL;
|
|
}
|
|
}
|
|
|
|
mem_free(bm_hashTable);
|
|
bm_hashTable = NULL;
|
|
}
|
|
}
|
|
|
|
bm_hashTableIndex bm_hash(bm_T data) {
|
|
/***********************************
|
|
* hash function applied to data *
|
|
***********************************/
|
|
char *p = data->name;
|
|
uint32_t hval = strlen(p);
|
|
|
|
while (*p) {
|
|
hval += tolower(*p);
|
|
p++;
|
|
}
|
|
|
|
return (hval % (MAX_BITMAPS / 2));
|
|
}
|
|
bm_Node *bm_insertNode(bm_T data) {
|
|
bm_Node *p, *p0;
|
|
bm_hashTableIndex bucket;
|
|
/************************************************
|
|
* allocate bm_Node for data and insert in table *
|
|
************************************************/
|
|
/* insert bm_Node at beginning of list */
|
|
bucket = bm_hash(data);
|
|
if ((p = mem_rmalloc<bm_Node>()) == 0) {
|
|
exit(1);
|
|
}
|
|
p0 = bm_hashTable[bucket];
|
|
bm_hashTable[bucket] = p;
|
|
p->next = p0;
|
|
p->data = data;
|
|
return p;
|
|
}
|
|
void bm_deleteNode(bm_T data) {
|
|
bm_Node *p0, *p;
|
|
bm_hashTableIndex bucket;
|
|
/********************************************
|
|
* delete bm_Node containing data from table *
|
|
********************************************/
|
|
/* find bm_Node */
|
|
p0 = 0;
|
|
bucket = bm_hash(data);
|
|
p = bm_hashTable[bucket];
|
|
while (p && !compEQ(p->data, data)) {
|
|
p0 = p;
|
|
p = p->next;
|
|
}
|
|
if (!p)
|
|
return;
|
|
/* p designates bm_Node to delete, remove it from list */
|
|
if (p0)
|
|
/* not first bm_Node, p0 points to previous bm_Node */
|
|
p0->next = p->next;
|
|
else
|
|
/* first bm_Node on chain */
|
|
bm_hashTable[bucket] = p->next;
|
|
mem_free(p);
|
|
}
|
|
bm_Node *bm_findNode(bm_T data) {
|
|
bm_Node *p;
|
|
if (!bm_hashTable)
|
|
return NULL;
|
|
/*******************************
|
|
* find bm_Node containing data *
|
|
*******************************/
|
|
p = bm_hashTable[bm_hash(data)];
|
|
while (p && !compEQ(p->data, data))
|
|
p = p->next;
|
|
return p;
|
|
}
|
|
// simply frees up a bitmap
|
|
void bm_FreeBitmapMain(int handle);
|
|
// Sets all the bitmaps to unused
|
|
void bm_InitBitmaps() {
|
|
int i, ret;
|
|
bm_InitHashTable();
|
|
Bitmaps_initted = 1;
|
|
for (i = 0; i < MAX_BITMAPS; i++) {
|
|
GameBitmaps[i].used = 0;
|
|
GameBitmaps[i].data16 = NULL;
|
|
GameBitmaps[i].format = BITMAP_FORMAT_STANDARD;
|
|
GameBitmaps[i].cache_slot = -1;
|
|
GameBitmaps[i].flags = 0;
|
|
}
|
|
int bm = bm_AllocBitmap(128, 128, 0);
|
|
ASSERT(bm == BAD_BITMAP_HANDLE);
|
|
if (cfexist(".\\error1.ogf"))
|
|
ret = bm_AllocLoadFileBitmap(".\\error1.ogf", 0);
|
|
else
|
|
ret = -1;
|
|
if (ret == -1)
|
|
bm_MakeBad(bm);
|
|
else {
|
|
bm_ScaleBitmapToBitmap(bm, ret);
|
|
bm_FreeBitmap(ret);
|
|
}
|
|
|
|
// bm_GenerateMipMaps (bm);
|
|
atexit(bm_ShutdownBitmaps);
|
|
// Initialize lightmaps and bumpmaps
|
|
lm_InitLightmaps();
|
|
bump_InitBumpmaps();
|
|
}
|
|
void bm_ShutdownBitmaps(void) {
|
|
int i;
|
|
LOG_DEBUG << "Freeing all bitmap memory.";
|
|
bm_FreeBitmapMain(0);
|
|
for (i = 0; i < MAX_BITMAPS; i++) {
|
|
while (GameBitmaps[i].used > 0)
|
|
bm_FreeBitmap(i);
|
|
}
|
|
bm_DeleteHashTable();
|
|
}
|
|
int bm_AllocateMemoryForIndex(int n, int w, int h, int add_mem) {
|
|
// If no go on the malloc, bail out with -1
|
|
|
|
int size = (w * h * 2) + (add_mem) + 2;
|
|
GameBitmaps[n].data16 = (uint16_t *)mem_malloc(size);
|
|
if (!GameBitmaps[n].data16) {
|
|
Int3(); // Ran out of memory!
|
|
return -1;
|
|
}
|
|
Bitmap_memory_used += (size);
|
|
memset(GameBitmaps[n].data16, 0xAA, size);
|
|
|
|
GameBitmaps[n].format = BITMAP_FORMAT_STANDARD;
|
|
GameBitmaps[n].width = w;
|
|
GameBitmaps[n].height = h;
|
|
GameBitmaps[n].flags = BF_CHANGED | BF_BRAND_NEW;
|
|
|
|
#ifdef USE_OPENGL
|
|
for (int tmp = w; tmp > 0; tmp = tmp >> 1)
|
|
GameBitmaps[n].mip_levels++;
|
|
#else
|
|
GameBitmaps[n].mip_levels = NUM_MIP_LEVELS;
|
|
#endif
|
|
|
|
Num_of_bitmaps++;
|
|
return n;
|
|
}
|
|
// Allocs a bitmap of w x h size
|
|
// If add_mem is nonzero, adds that to the amount alloced
|
|
// Returns bitmap handle if successful, -1 if otherwise
|
|
int bm_AllocBitmap(int w, int h, int add_mem) {
|
|
int n = 0, i;
|
|
if (!Bitmaps_initted) {
|
|
LOG_ERROR << "Bitmaps not initted!!!";
|
|
Int3();
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < MAX_BITMAPS; i++) {
|
|
if (GameBitmaps[i].used == 0) {
|
|
n = i;
|
|
break;
|
|
}
|
|
}
|
|
// If we can't find a free slot in which to alloc, bail out
|
|
if (i == MAX_BITMAPS) {
|
|
LOG_ERROR << "ERROR! Couldn't find a free bitmap to alloc!";
|
|
Int3();
|
|
return -1;
|
|
}
|
|
memset(&GameBitmaps[n], 0, sizeof(bms_bitmap));
|
|
int ret = bm_AllocateMemoryForIndex(n, w, h, add_mem);
|
|
if (ret >= 0) {
|
|
GameBitmaps[n].used = 1;
|
|
GameBitmaps[n].cache_slot = -1;
|
|
}
|
|
return ret;
|
|
}
|
|
// Just like bm_AllocBitmap but doesn't actually allocate memory. Useful for paging!
|
|
int bm_AllocNoMemBitmap(int w, int h) {
|
|
int n = 0, i;
|
|
if (!Bitmaps_initted) {
|
|
LOG_ERROR << "Bitmaps not initted!!!";
|
|
Int3();
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < MAX_BITMAPS; i++) {
|
|
if (GameBitmaps[i].used == 0) {
|
|
n = i;
|
|
break;
|
|
}
|
|
}
|
|
// If we can't find a free slot in which to alloc, bail out
|
|
if (i == MAX_BITMAPS) {
|
|
LOG_ERROR << "Couldn't find a free bitmap to alloc!";
|
|
Int3();
|
|
return -1;
|
|
}
|
|
// If no go on the malloc, bail out with -1
|
|
|
|
memset(&GameBitmaps[n], 0, sizeof(bms_bitmap));
|
|
|
|
GameBitmaps[n].width = w;
|
|
GameBitmaps[n].height = h;
|
|
GameBitmaps[n].used = 1;
|
|
GameBitmaps[n].format = BITMAP_FORMAT_STANDARD;
|
|
GameBitmaps[n].flags = BF_NOT_RESIDENT | BF_CHANGED | BF_BRAND_NEW;
|
|
GameBitmaps[n].cache_slot = -1;
|
|
|
|
Num_of_bitmaps++;
|
|
return n;
|
|
}
|
|
// Searches thru all bitmaps for a specific name, returns -1 if not found
|
|
// or index of bitmap with name
|
|
int bm_FindBitmapName(const char *name) {
|
|
int num_counted = 0;
|
|
|
|
bms_bitmap fbmp;
|
|
strcpy(fbmp.name, name);
|
|
bm_Node *fnode = bm_findNode(&fbmp);
|
|
if (fnode) {
|
|
// mprintf(0,"Hash table found bitmap %d\n",fnode->data - GameBitmaps);
|
|
return fnode->data - GameBitmaps;
|
|
} else
|
|
return -1;
|
|
}
|
|
// Given a handle, frees the bitmap memory and flags this bitmap as unused
|
|
void bm_FreeBitmap(int handle) {
|
|
if (!Bitmaps_initted) {
|
|
LOG_ERROR << "Bitmaps not initted!!!";
|
|
Int3();
|
|
return;
|
|
}
|
|
if (handle == BAD_BITMAP_HANDLE)
|
|
return;
|
|
if (GameBitmaps[handle].used < 1)
|
|
return;
|
|
GameBitmaps[handle].used--;
|
|
if (GameBitmaps[handle].used == 0) {
|
|
bm_FreeBitmapMain(handle);
|
|
}
|
|
}
|
|
// simply frees up a bitmap
|
|
void bm_FreeBitmapData(int handle) {
|
|
if (GameBitmaps[handle].data16 != NULL && !(GameBitmaps[handle].flags & BF_NOT_RESIDENT)) {
|
|
if (!(GameBitmaps[handle].flags & BF_NOT_RESIDENT)) {
|
|
int mem_used = (GameBitmaps[handle].width * GameBitmaps[handle].height * 2);
|
|
Bitmap_memory_used -= mem_used;
|
|
if (GameBitmaps[handle].flags & BF_MIPMAPPED)
|
|
Bitmap_memory_used -= (mem_used / 3);
|
|
}
|
|
mem_free(GameBitmaps[handle].data16);
|
|
}
|
|
GameBitmaps[handle].cache_slot = -1;
|
|
GameBitmaps[handle].flags |= BF_NOT_RESIDENT;
|
|
if (GameBitmaps[handle].flags & BF_MIPMAPPED)
|
|
GameBitmaps[handle].flags |= BF_WANTS_MIP;
|
|
GameBitmaps[handle].data16 = NULL;
|
|
}
|
|
// simply frees up a bitmap
|
|
void bm_FreeBitmapMain(int handle) {
|
|
bm_deleteNode(&GameBitmaps[handle]);
|
|
bm_FreeBitmapData(handle);
|
|
GameBitmaps[handle].data16 = NULL;
|
|
GameBitmaps[handle].cache_slot = -1;
|
|
GameBitmaps[handle].used = 0;
|
|
Num_of_bitmaps--;
|
|
}
|
|
// Returns -1 if this name is not already in use, else index of bitmap that is using name
|
|
int bm_TestName(const char *src) {
|
|
uint32_t i, limit;
|
|
|
|
char namedest[256];
|
|
char path[256], ext[256], filename[256];
|
|
|
|
ddio_SplitPath(src, path, filename, ext);
|
|
limit = BITMAP_NAME_LEN - 7;
|
|
// Make sure we don't go over our name length limit
|
|
strncpy(namedest, filename, limit);
|
|
namedest[limit] = 0;
|
|
|
|
int cur_len = strlen(namedest);
|
|
|
|
// Now, make sure there are no other bitmaps with this name
|
|
strcat(namedest, ".ogf");
|
|
if ((i = bm_FindBitmapName(namedest)) == -1)
|
|
return -1;
|
|
return i;
|
|
}
|
|
// gets the filename from a path, plus appends our .ogf extension
|
|
void bm_ChangeEndName(const char *src, char *dest) {
|
|
uint32_t i, limit;
|
|
int last = -1;
|
|
int curnum = -1;
|
|
char namedest[256 + 16];
|
|
char path[256], ext[256], filename[256];
|
|
|
|
ddio_SplitPath(src, path, filename, ext);
|
|
limit = BITMAP_NAME_LEN - 7;
|
|
// Make sure we don't go over our name length limit
|
|
filename[limit] = 0;
|
|
Start:
|
|
if (curnum != -1)
|
|
snprintf(namedest, sizeof(namedest), "%s%d", filename, curnum);
|
|
else
|
|
strcpy(namedest, filename);
|
|
// Now, make sure there are no other bitmaps with this name
|
|
strcat(namedest, ".ogf");
|
|
if ((i = bm_FindBitmapName(namedest)) != -1) {
|
|
curnum++;
|
|
// This name is already taken. Try same name with different number
|
|
// appended
|
|
goto Start;
|
|
}
|
|
strcpy(dest, namedest);
|
|
}
|
|
// Checks to see if this file is a kind we can read
|
|
int bm_GetFileType(CFILE *infile, const char *dest) {
|
|
char iffcheck[4];
|
|
int i;
|
|
// First, check if it is a PCX
|
|
i = strlen(dest);
|
|
if (dest[i - 4] == '.' && (dest[i - 3] == 'p' || dest[i - 3] == 'P') && (dest[i - 2] == 'c' || dest[i - 2] == 'C') &&
|
|
(dest[i - 1] == 'x' || dest[i - 1] == 'X'))
|
|
return BM_FILETYPE_PCX;
|
|
// How about an IFF?
|
|
for (i = 0; i < 4; i++)
|
|
iffcheck[i] = cf_ReadByte(infile);
|
|
cfseek(infile, 0, SEEK_SET);
|
|
if (!strncmp("FORM", iffcheck, 4))
|
|
return BM_FILETYPE_IFF;
|
|
// Lastly, just default to possible TGA or OGF
|
|
return BM_FILETYPE_TGA;
|
|
}
|
|
// Allocs and loads a bitmap
|
|
// Returns the handle of the loaded bitmap
|
|
// Returns -1 if something is wrong
|
|
// If mipped is non-zero, allocs extra space for mips and computes them
|
|
int bm_AllocLoadFileBitmap(const char *fname, int mipped, int format) {
|
|
CFILE *infile;
|
|
if (!Bitmaps_initted) {
|
|
LOG_ERROR << "Bitmaps not initted!!!";
|
|
Int3();
|
|
return -1;
|
|
}
|
|
|
|
char name[BITMAP_NAME_LEN];
|
|
int n, src_bm = 0;
|
|
int old_used;
|
|
int overlay = 0;
|
|
|
|
char *filename = (char *)fname;
|
|
|
|
// due to a bug in some people's tablefile editors, we got to make sure there is
|
|
// no path if there shouldn't be
|
|
if (!cfexist(filename)) {
|
|
// generate a possible new filename
|
|
char *end_ptr, *start_ptr;
|
|
start_ptr = (char *)fname;
|
|
end_ptr = start_ptr + strlen(start_ptr) - 1;
|
|
while ((end_ptr >= start_ptr) && (*end_ptr != '\\'))
|
|
end_ptr--;
|
|
if (end_ptr < start_ptr) {
|
|
LOG_ERROR.printf("Unable to find bitmap %s", fname);
|
|
return -1;
|
|
}
|
|
ASSERT(*end_ptr == '\\');
|
|
end_ptr++;
|
|
filename = end_ptr;
|
|
LOG_DEBUG.printf("Couldn't find %s, so gonna try %s", fname, filename);
|
|
}
|
|
|
|
// Check to see if this bitmap is already in memory, if so, just return that
|
|
// bitmaps handle
|
|
if ((n = bm_TestName(filename)) != -1) {
|
|
LOG_DEBUG.printf("Found duplicate bitmap %s.", GameBitmaps[n].name);
|
|
old_used = GameBitmaps[n].used;
|
|
GameBitmaps[n].used = 1;
|
|
bm_FreeBitmap(n);
|
|
GameBitmaps[n].used = old_used + 1;
|
|
|
|
overlay = 1;
|
|
}
|
|
if (!overlay)
|
|
bm_ChangeEndName(filename, name);
|
|
else {
|
|
strcpy(name, GameBitmaps[n].name);
|
|
}
|
|
if (strlen(name) > 33) {
|
|
LOG_FATAL << "This bitmaps name is too long, try shortening it and retry!";
|
|
return -1;
|
|
}
|
|
// Try to open the file. If we can't load it from the network if possible
|
|
infile = (CFILE *)cfopen(filename, "rb");
|
|
if (!infile) {
|
|
LOG_ERROR.printf("bm_AllocLoadFileBitmap: Can't open file named %s.", filename);
|
|
|
|
return BAD_BITMAP_HANDLE; // return the bad texture
|
|
}
|
|
// Check to see if this is IFF. If so, call the IFF reader. If not,
|
|
// rewind the file and read as a TGA
|
|
|
|
int filetype = bm_GetFileType(infile, filename);
|
|
|
|
switch (filetype) {
|
|
case BM_FILETYPE_IFF:
|
|
src_bm = bm_iff_alloc_file(infile);
|
|
break;
|
|
case BM_FILETYPE_TGA:
|
|
// reads a tga or an outrage graphics file (ogf)
|
|
src_bm = bm_tga_alloc_file(infile, name, format);
|
|
break;
|
|
case BM_FILETYPE_PCX:
|
|
src_bm = bm_pcx_alloc_file(infile);
|
|
break;
|
|
default:
|
|
Int3(); // Can't read this type
|
|
break;
|
|
}
|
|
cfclose(infile);
|
|
if (src_bm < 0) {
|
|
LOG_ERROR.printf("Couldn't load %s.", filename);
|
|
return -1;
|
|
}
|
|
|
|
// Allocate space for our bitmap.
|
|
if (mipped) {
|
|
if ((bm_mipped(src_bm)) == 0) // If we want a mipped but we don't have one
|
|
{
|
|
int w = bm_w(src_bm, 0);
|
|
int h = bm_h(src_bm, 0);
|
|
|
|
if (overlay) {
|
|
bm_AllocateMemoryForIndex(n, w, h, mipped * (((w * h * 2) / 3)));
|
|
GameBitmaps[n].format = GameBitmaps[src_bm].format;
|
|
} else {
|
|
n = bm_AllocBitmap(w, h, mipped * (((w * h * 2) / 3)));
|
|
GameBitmaps[n].format = GameBitmaps[src_bm].format;
|
|
}
|
|
|
|
ASSERT(n >= 0);
|
|
bm_ScaleBitmapToBitmap(n, src_bm);
|
|
bm_FreeBitmap(src_bm);
|
|
|
|
bm_GenerateMipMaps(n);
|
|
} else {
|
|
if (!overlay)
|
|
n = src_bm;
|
|
else {
|
|
int w = bm_w(src_bm, 0);
|
|
int h = bm_h(src_bm, 0);
|
|
bm_AllocateMemoryForIndex(n, w, h, mipped * (((w * h * 2) / 3)));
|
|
GameBitmaps[n].flags |= BF_MIPMAPPED;
|
|
GameBitmaps[n].format = GameBitmaps[src_bm].format;
|
|
bm_ScaleBitmapToBitmap(n, src_bm);
|
|
bm_FreeBitmap(src_bm);
|
|
}
|
|
}
|
|
} else // If we don't want a mipped
|
|
{
|
|
if ((bm_mipped(src_bm)) == 0) {
|
|
if (!overlay)
|
|
n = src_bm;
|
|
else {
|
|
int w = bm_w(src_bm, 0);
|
|
int h = bm_h(src_bm, 0);
|
|
bm_AllocateMemoryForIndex(n, w, h, mipped * (((w * h * 2) / 3)));
|
|
GameBitmaps[n].format = GameBitmaps[src_bm].format;
|
|
bm_ScaleBitmapToBitmap(n, src_bm);
|
|
bm_FreeBitmap(src_bm);
|
|
}
|
|
} else // And this is already mipped
|
|
{
|
|
int w = bm_w(src_bm, 0);
|
|
int h = bm_h(src_bm, 0);
|
|
uint16_t *src_data, *dest_data;
|
|
|
|
if (overlay) {
|
|
bm_AllocateMemoryForIndex(n, w, h, mipped * (((w * h * 2) / 3)));
|
|
GameBitmaps[n].format = GameBitmaps[src_bm].format;
|
|
} else {
|
|
n = bm_AllocBitmap(w, h, mipped * (((w * h * 2) / 3)));
|
|
GameBitmaps[n].format = GameBitmaps[src_bm].format;
|
|
}
|
|
ASSERT(n >= 0);
|
|
src_data = (uint16_t *)bm_data(src_bm, 0);
|
|
dest_data = (uint16_t *)bm_data(n, 0);
|
|
memcpy(dest_data, src_data, w * h * 2);
|
|
|
|
bm_FreeBitmap(src_bm);
|
|
}
|
|
}
|
|
|
|
strcpy(GameBitmaps[n].name, name);
|
|
|
|
// Insert into the hash table!
|
|
bm_insertNode(&GameBitmaps[n]);
|
|
return n; // We made it!
|
|
}
|
|
// Allocs and loads a bitmap but doesn't actually load texel data!
|
|
// Returns the handle of the loaded bitmap
|
|
// Returns -1 if something is wrong
|
|
int bm_AllocLoadFileNoMemBitmap(const char *fname, int mipped, int format) {
|
|
if (!Bitmaps_initted) {
|
|
LOG_ERROR << "Bitmaps not initted!!!";
|
|
Int3();
|
|
return -1;
|
|
}
|
|
|
|
char name[BITMAP_NAME_LEN];
|
|
int n;
|
|
char *filename = (char *)fname;
|
|
|
|
// due to a bug in some people's tablefile editors, we got to make sure there is
|
|
// no path if there shouldn't be
|
|
if (!cfexist(filename)) {
|
|
// generate a possible new filename
|
|
char *end_ptr, *start_ptr;
|
|
start_ptr = (char *)fname;
|
|
end_ptr = start_ptr + strlen(start_ptr) - 1;
|
|
while ((end_ptr >= start_ptr) && (*end_ptr != '\\'))
|
|
end_ptr--;
|
|
if (end_ptr < start_ptr) {
|
|
LOG_ERROR.printf("Unable to find bitmap %s", fname);
|
|
return -1;
|
|
}
|
|
ASSERT(*end_ptr == '\\');
|
|
end_ptr++;
|
|
filename = end_ptr;
|
|
LOG_DEBUG.printf("Couldn't find %s, so gonna try %s", fname, filename);
|
|
}
|
|
|
|
// Check to see if this bitmap is already in memory, if so, just return that
|
|
// bitmaps handle
|
|
if ((n = bm_TestName(filename)) != -1) {
|
|
GameBitmaps[n].used++;
|
|
LOG_DEBUG.printf("Found duplicate bitmap %s.", GameBitmaps[n].name);
|
|
return n;
|
|
}
|
|
|
|
bm_ChangeEndName(filename, name);
|
|
if (strlen(name) > 33) {
|
|
LOG_ERROR << "This bitmaps name is too long, try shortening it and retry!";
|
|
return -1;
|
|
}
|
|
n = bm_AllocNoMemBitmap(1, 1);
|
|
strcpy(GameBitmaps[n].name, name);
|
|
if (mipped)
|
|
GameBitmaps[n].flags |= BF_WANTS_MIP;
|
|
if (format == BITMAP_FORMAT_4444)
|
|
GameBitmaps[n].flags |= BF_WANTS_4444;
|
|
|
|
return n; // We made it!
|
|
}
|
|
// Allocs and loads a bitmap from an open file
|
|
// Returns the handle of the loaded bitmap
|
|
// Returns -1 if something is wrong
|
|
// If mipped is non-zero, allocs extra space for mips and computes them
|
|
int bm_AllocLoadBitmap(CFILE *infile, int mipped, int format) {
|
|
char name[BITMAP_NAME_LEN];
|
|
int n, src_bm;
|
|
if (!Bitmaps_initted) {
|
|
LOG_ERROR << "Bitmaps not initted!!!";
|
|
Int3();
|
|
return -1;
|
|
}
|
|
|
|
// Assume this is an ogf
|
|
|
|
src_bm = bm_tga_alloc_file(infile, name, format);
|
|
if (src_bm < 0) {
|
|
LOG_ERROR.printf("Couldn't load %s.", name);
|
|
return -1;
|
|
}
|
|
|
|
// Allocate space for our bitmap
|
|
if (mipped) {
|
|
if ((bm_mipped(src_bm)) == 0) // If we want a mipped but we don't have one
|
|
{
|
|
int w = bm_w(src_bm, 0);
|
|
int h = bm_h(src_bm, 0);
|
|
|
|
n = bm_AllocBitmap(w, h, mipped * (((w * h * 2) / 3)));
|
|
|
|
ASSERT(n >= 0);
|
|
GameBitmaps[n].format = GameBitmaps[src_bm].format;
|
|
bm_ScaleBitmapToBitmap(n, src_bm);
|
|
bm_FreeBitmap(src_bm);
|
|
|
|
bm_GenerateMipMaps(n);
|
|
} else
|
|
n = src_bm;
|
|
} else // If we don't want a mipped
|
|
{
|
|
if ((bm_mipped(src_bm)) == 0)
|
|
n = src_bm;
|
|
else // And this is already mipped
|
|
{
|
|
int w = bm_w(src_bm, 0);
|
|
int h = bm_h(src_bm, 0);
|
|
uint16_t *src_data, *dest_data;
|
|
|
|
n = bm_AllocBitmap(w, h, mipped * (((w * h * 2) / 3)));
|
|
ASSERT(n >= 0);
|
|
src_data = (uint16_t *)bm_data(src_bm, 0);
|
|
dest_data = (uint16_t *)bm_data(n, 0);
|
|
memcpy(dest_data, src_data, w * h * 2);
|
|
|
|
bm_FreeBitmap(src_bm);
|
|
}
|
|
}
|
|
strcpy(GameBitmaps[n].name, name);
|
|
return n; // We made it!
|
|
}
|
|
// Given a handle, makes a big random shape to let you know you are screwed.
|
|
void bm_MakeBad(int handle) {
|
|
int i, t, limit;
|
|
uint16_t *dest;
|
|
ASSERT(GameBitmaps[handle].used);
|
|
if (handle != BAD_BITMAP_HANDLE)
|
|
Int3(); // hmm, you're assigning a random bitmap to something other than
|
|
// the default.
|
|
if (bm_mipped(handle))
|
|
limit = bm_miplevels(handle);
|
|
else
|
|
limit = 1;
|
|
for (i = 0; i < limit; i++) {
|
|
dest = bm_data(handle, i);
|
|
|
|
for (t = 0; t < bm_h(handle, i) * bm_w(handle, i); t++)
|
|
*dest++ = (OPAQUE_FLAG | ps_rand());
|
|
}
|
|
}
|
|
|
|
// Saves a bitmap to an open file. Saves the bitmap as an OUTRAGE_COMPRESSED_OGF.
|
|
// Returns -1 if something is wrong.
|
|
int bm_SaveBitmap(CFILE *fp, int handle) {
|
|
uint8_t dumbbyte = 0, image_type = OUTRAGE_1555_COMPRESSED_MIPPED, pixsize = 32, desc = 8 + 32;
|
|
int i, done = 0;
|
|
int num_mips;
|
|
LOG_DEBUG.printf("Saving bitmap %s...", GameBitmaps[handle].name);
|
|
if (!GameBitmaps[handle].used) {
|
|
LOG_ERROR << "bm_SaveBitmap: Trying to save a bitmap that isn't used!";
|
|
Int3();
|
|
return -1;
|
|
}
|
|
if (handle == BAD_BITMAP_HANDLE) {
|
|
Int3(); // Hey, you shouldn't be saving this bitmap!
|
|
return -1;
|
|
}
|
|
if (GameBitmaps[handle].format == BITMAP_FORMAT_4444)
|
|
image_type = OUTRAGE_4444_COMPRESSED_MIPPED;
|
|
if (!fp) {
|
|
LOG_ERROR << "bm_SaveBitmap: Trying to save a bitmap to a closed file!";
|
|
Int3();
|
|
return -1;
|
|
}
|
|
cf_WriteByte(fp, dumbbyte);
|
|
cf_WriteByte(fp, dumbbyte);
|
|
cf_WriteByte(fp, image_type);
|
|
cf_WriteString(fp, GameBitmaps[handle].name);
|
|
|
|
if ((bm_mipped(handle)))
|
|
num_mips = bm_miplevels(handle);
|
|
else
|
|
num_mips = 1;
|
|
cf_WriteByte(fp, num_mips);
|
|
for (i = 0; i < 9; i++)
|
|
cf_WriteByte(fp, 0);
|
|
cf_WriteShort(fp, GameBitmaps[handle].width);
|
|
cf_WriteShort(fp, GameBitmaps[handle].height);
|
|
cf_WriteByte(fp, pixsize);
|
|
cf_WriteByte(fp, desc);
|
|
|
|
// write compressed info
|
|
for (int m = 0; m < num_mips; m++) {
|
|
int curptr = 0;
|
|
int total = bm_w(handle, m) * bm_h(handle, m);
|
|
uint16_t *src_data = (uint16_t *)bm_data(handle, m);
|
|
done = 0;
|
|
while (!done) {
|
|
if (curptr == total) {
|
|
done = 1;
|
|
continue;
|
|
}
|
|
ASSERT(curptr < total);
|
|
uint16_t curpix = src_data[curptr];
|
|
uint8_t count = 1;
|
|
while (src_data[curptr + count] == curpix && count < 250 && (curptr + count) < total)
|
|
count++;
|
|
if (count == 1) {
|
|
cf_WriteByte(fp, 0);
|
|
cf_WriteShort(fp, curpix);
|
|
} else {
|
|
cf_WriteByte(fp, count);
|
|
cf_WriteShort(fp, curpix);
|
|
}
|
|
curptr += count;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
// Makes sure a bitmap is in memory...if not it attempts to page it in
|
|
int bm_MakeBitmapResident(int handle) {
|
|
if (GameBitmaps[handle].flags & BF_NOT_RESIDENT) {
|
|
if (bm_page_in_file(handle) > 0) // DAJ -1FIX
|
|
{
|
|
GameBitmaps[handle].flags &= ~BF_NOT_RESIDENT;
|
|
GameBitmaps[handle].flags |= BF_CHANGED | BF_BRAND_NEW;
|
|
} else {
|
|
LOG_ERROR.printf("Error paging in bitmap %s!", GameBitmaps[handle].name);
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
// Saves a bitmap to a file. Saves the bitmap as an OUTRAGE_COMPRESSED_OGF.
|
|
// Returns -1 if something is wrong.
|
|
int bm_SaveFileBitmap(const std::filesystem::path &filename, int handle) {
|
|
int ret;
|
|
CFILE *fp;
|
|
if (!GameBitmaps[handle].used) {
|
|
LOG_ERROR << "Trying to save a bitmap that isn't used!";
|
|
Int3();
|
|
return -1;
|
|
}
|
|
if (handle == BAD_BITMAP_HANDLE) {
|
|
Int3(); // Hey, you shouldn't be saving this bitmap!
|
|
return -1;
|
|
}
|
|
if (!bm_MakeBitmapResident(handle)) {
|
|
LOG_ERROR << "There was a problem paging this bitmap in!";
|
|
return -1;
|
|
}
|
|
fp = (CFILE *)cfopen(filename, "wb");
|
|
|
|
if (!fp) {
|
|
LOG_ERROR << "Trying to save a bitmap to a closed file!";
|
|
Int3();
|
|
return -1;
|
|
}
|
|
ret = bm_SaveBitmap(fp, handle);
|
|
|
|
cfclose(fp);
|
|
return ret;
|
|
}
|
|
// returns a bitmaps width (based on miplevel), else -1 if something is wrong
|
|
int bm_w(int handle, int miplevel) {
|
|
int w;
|
|
if (!GameBitmaps[handle].used) {
|
|
Int3();
|
|
return -1;
|
|
}
|
|
if (GameBitmaps[handle].flags & BF_NOT_RESIDENT)
|
|
if (!bm_MakeBitmapResident(handle))
|
|
return 0;
|
|
if (!(GameBitmaps[handle].flags & BF_MIPMAPPED))
|
|
miplevel = 0;
|
|
w = GameBitmaps[handle].width;
|
|
w >>= miplevel;
|
|
return (w);
|
|
}
|
|
// returns a bitmaps height (based on miplevel), else -1 if something is wrong
|
|
int bm_h(int handle, int miplevel) {
|
|
int h;
|
|
if (!GameBitmaps[handle].used) {
|
|
Int3();
|
|
return -1;
|
|
}
|
|
// If this bitmap is not page in, do so!
|
|
if (GameBitmaps[handle].flags & BF_NOT_RESIDENT)
|
|
if (!bm_MakeBitmapResident(handle))
|
|
return 0;
|
|
if (!(GameBitmaps[handle].flags & BF_MIPMAPPED))
|
|
miplevel = 0;
|
|
h = GameBitmaps[handle].height;
|
|
h >>= miplevel;
|
|
return (h);
|
|
}
|
|
|
|
// returns the number of levels of mips a bitmap should have
|
|
int bm_miplevels(int handle) {
|
|
int levels = 0;
|
|
if (!GameBitmaps[handle].used) {
|
|
Int3();
|
|
return -1;
|
|
}
|
|
if (GameBitmaps[handle].mip_levels)
|
|
return GameBitmaps[handle].mip_levels;
|
|
|
|
// If this bitmap is not page in, do so!
|
|
if (GameBitmaps[handle].flags & BF_NOT_RESIDENT)
|
|
if (!bm_MakeBitmapResident(handle))
|
|
return 0;
|
|
if (GameBitmaps[handle].flags & BF_MIPMAPPED) {
|
|
for (int tmp = GameBitmaps[handle].width; tmp > 0; tmp = tmp >> 1) {
|
|
levels++;
|
|
}
|
|
return levels;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// returns a bitmaps mipped status, else -1 if something is wrong
|
|
int bm_mipped(int handle) {
|
|
if (!GameBitmaps[handle].used) {
|
|
Int3();
|
|
return -1;
|
|
}
|
|
// If this bitmap is not page in, do so!
|
|
if (GameBitmaps[handle].flags & BF_NOT_RESIDENT)
|
|
if (!bm_MakeBitmapResident(handle))
|
|
return 0;
|
|
if (GameBitmaps[handle].flags & BF_MIPMAPPED)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
// returns a bitmaps data (based on given miplevel), else NULL if something is wrong
|
|
uint16_t *bm_data(int handle, int miplevel) {
|
|
uint16_t *d;
|
|
int i;
|
|
if (!GameBitmaps[handle].used) {
|
|
Int3();
|
|
return NULL;
|
|
}
|
|
|
|
// If this bitmap is not page in, do so!
|
|
if (GameBitmaps[handle].flags & BF_NOT_RESIDENT)
|
|
if (!bm_MakeBitmapResident(handle))
|
|
return NULL;
|
|
|
|
d = GameBitmaps[handle].data16;
|
|
for (i = 0; i < miplevel; i++) {
|
|
d += (GameBitmaps[handle].width >> i) * (GameBitmaps[handle].height >> i);
|
|
}
|
|
return (d);
|
|
}
|
|
// Given a source bitmap, generates mipmaps for it
|
|
void bm_GenerateMipMaps(int handle) {
|
|
int width = bm_w(handle, 0);
|
|
int height = bm_h(handle, 0);
|
|
int jump = 2; // how many pixels to jump in x and y on source
|
|
|
|
// mprintf(0,"We got a mipper! %d \n",handle);
|
|
|
|
ASSERT(GameBitmaps[handle].used);
|
|
GameBitmaps[handle].flags |= BF_MIPMAPPED;
|
|
|
|
int levels = bm_miplevels(handle);
|
|
|
|
uint16_t *destdata;
|
|
for (int miplevel = 1; miplevel < levels; miplevel++) {
|
|
width /= 2;
|
|
height /= 2;
|
|
|
|
for (int i = 0; i < height; i++) {
|
|
int adjheight = (i * jump) * bm_w(handle, miplevel - 1); // find our y offset
|
|
|
|
for (int t = 0; t < width; t++) {
|
|
int adjwidth = (t * jump); // find our x offset
|
|
uint16_t *srcptr = bm_data(handle, miplevel - 1);
|
|
srcptr += (adjheight + adjwidth);
|
|
|
|
int rsum, gsum, bsum, asum;
|
|
rsum = gsum = bsum = asum = 0;
|
|
uint16_t destpix = 0;
|
|
if (GameBitmaps[handle].format == BITMAP_FORMAT_1555) {
|
|
for (int y = 0; y < 2; y++)
|
|
for (int x = 0; x < 2; x++) {
|
|
uint16_t pix = srcptr[y * bm_w(handle, miplevel - 1) + x];
|
|
int r = (pix >> 10) & 0x1f;
|
|
int g = (pix >> 5) & 0x1f;
|
|
int b = (pix & 0x1f);
|
|
|
|
if (!(pix & OPAQUE_FLAG))
|
|
asum++;
|
|
else {
|
|
rsum += r;
|
|
gsum += g;
|
|
bsum += b;
|
|
}
|
|
}
|
|
// Average our source pixels
|
|
rsum /= 4;
|
|
gsum /= 4;
|
|
bsum /= 4;
|
|
destpix = OPAQUE_FLAG | (rsum << 10) | (gsum << 5) | bsum;
|
|
|
|
if (asum > 2)
|
|
destpix = NEW_TRANSPARENT_COLOR;
|
|
} else if (GameBitmaps[handle].format == BITMAP_FORMAT_4444) {
|
|
for (int y = 0; y < 2; y++)
|
|
for (int x = 0; x < 2; x++) {
|
|
uint16_t pix = srcptr[y * bm_w(handle, miplevel - 1) + x];
|
|
int a = (pix >> 12) & 0x0f;
|
|
int r = (pix >> 8) & 0x0f;
|
|
int g = (pix >> 4) & 0x0f;
|
|
int b = (pix & 0x0f);
|
|
|
|
asum += a;
|
|
rsum += r;
|
|
gsum += g;
|
|
bsum += b;
|
|
}
|
|
// Average our source pixels
|
|
rsum /= 4;
|
|
gsum /= 4;
|
|
bsum /= 4;
|
|
asum /= 4;
|
|
destpix = (asum << 12) | (rsum << 8) | (gsum << 4) | bsum;
|
|
} else {
|
|
Int3();
|
|
}
|
|
destdata = bm_data(handle, miplevel);
|
|
int sw = bm_w(handle, miplevel);
|
|
destdata[(i * sw) + t] = destpix;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Gets bits per pixel for a particular bitmap
|
|
// As of 12/30/96 always returns 16
|
|
int bm_bpp(int handle) { return BPP_16; }
|
|
// Given two bitmaps, scales the data from src to the size of dest
|
|
// Not a particularly fast implementation
|
|
void bm_ScaleBitmapToBitmap(int dest, int src) {
|
|
uint16_t *dp = bm_data(dest, 0);
|
|
uint16_t *sp = bm_data(src, 0);
|
|
ASSERT(GameBitmaps[dest].used && dp);
|
|
ASSERT(GameBitmaps[src].used && sp);
|
|
int smipped = bm_mipped(src);
|
|
int dmipped = bm_mipped(dest);
|
|
int limit;
|
|
ASSERT(smipped == dmipped);
|
|
ASSERT(GameBitmaps[dest].format == GameBitmaps[src].format);
|
|
|
|
int sw = bm_w(src, 0);
|
|
int sh = bm_h(src, 0);
|
|
int dw = bm_w(dest, 0);
|
|
int dh = bm_h(dest, 0);
|
|
int i, t;
|
|
uint16_t *sdata;
|
|
uint16_t *ddata;
|
|
if (sw == dw && sh == dh) {
|
|
if (smipped)
|
|
limit = bm_miplevels(src);
|
|
else
|
|
limit = 1;
|
|
for (i = 0; i < limit; i++) {
|
|
sdata = (uint16_t *)bm_data(src, i);
|
|
ddata = (uint16_t *)bm_data(dest, i);
|
|
dw = bm_w(dest, i);
|
|
dh = bm_h(dest, i);
|
|
memcpy(ddata, sdata, dw * dh * sizeof(uint16_t));
|
|
}
|
|
return;
|
|
}
|
|
if (smipped)
|
|
limit = bm_miplevels(src);
|
|
else
|
|
limit = 1;
|
|
for (int m = 0; m < limit; m++) {
|
|
sw = bm_w(src, m);
|
|
sh = bm_h(src, m);
|
|
dw = bm_w(dest, m);
|
|
dh = bm_h(dest, m);
|
|
|
|
sdata = (uint16_t *)bm_data(src, m);
|
|
ddata = (uint16_t *)bm_data(dest, m);
|
|
// These are our interpolant variables
|
|
float xstep = (float)sw / (float)dw;
|
|
float ystep = (float)sh / (float)dh;
|
|
float xoff = 0;
|
|
float yoff = 0;
|
|
for (i = 0; i < dh; i++, yoff += ystep) {
|
|
for (xoff = 0, t = 0; t < dw; t++, xoff += xstep) {
|
|
ddata[i * dw + t] = sdata[(int)yoff * sw + (int)xoff];
|
|
}
|
|
}
|
|
}
|
|
GameBitmaps[dest].flags |= BF_CHANGED;
|
|
}
|
|
// given a handle and a miplevel, returns the bytes per bitmap row
|
|
int bm_rowsize(int handle, int miplevel) {
|
|
int w;
|
|
ASSERT(GameBitmaps[handle].used > 0);
|
|
w = bm_w(handle, miplevel);
|
|
w *= 2;
|
|
return w;
|
|
}
|
|
// a function to determine if a pixel in a bitmap is transparent
|
|
bool bm_pixel_transparent(int bm_handle, int x, int y) {
|
|
if ((bm_data(bm_handle, 0)) == NULL)
|
|
return 0; // only check 16bit stuff
|
|
uint16_t *data = bm_data(bm_handle, 0);
|
|
data = data + (bm_w(bm_handle, 0) * y) + x;
|
|
if (GameBitmaps[bm_handle].format == BITMAP_FORMAT_4444) {
|
|
int pix = *data;
|
|
if (!(pix >> 12))
|
|
return true;
|
|
} else {
|
|
if (!(*data & OPAQUE_FLAG))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
// a function to determine if a pixel in a bitmap is transparent
|
|
uint16_t bm_pixel(int bm_handle, int x, int y) {
|
|
if ((bm_data(bm_handle, 0)) == NULL)
|
|
return 0; // only check 16bit stuff
|
|
uint16_t *data = bm_data(bm_handle, 0);
|
|
data = data + (bm_w(bm_handle, 0) * y) + x;
|
|
return *data;
|
|
}
|
|
// Goes through the bitmap and sees if there is any transparency...if so, flag it!
|
|
int bm_SetBitmapIfTransparent(int handle) {
|
|
if ((bm_data(handle, 0)) == NULL)
|
|
return 0; // only check 16bit stuff
|
|
int w = bm_w(handle, 0);
|
|
int h = bm_h(handle, 0);
|
|
uint16_t *data = bm_data(handle, 0);
|
|
if (GameBitmaps[handle].format == BITMAP_FORMAT_4444) {
|
|
for (int i = 0; i < w * h; i++) {
|
|
int pix = data[i] >> 12;
|
|
if (!pix) {
|
|
GameBitmaps[handle].flags |= BF_TRANSPARENT;
|
|
return 1;
|
|
}
|
|
}
|
|
} else {
|
|
for (int i = 0; i < w * h; i++) {
|
|
if (!(data[i] & OPAQUE_FLAG)) {
|
|
GameBitmaps[handle].flags |= BF_TRANSPARENT;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// clears bitmap
|
|
void bm_ClearBitmap(int handle) {
|
|
int dx, dy;
|
|
// DAJ int rowsize_w = bm_rowsize(handle,0) >> 1;
|
|
uint16_t *bmpdata = bm_data(handle, 0);
|
|
int w = bm_w(handle, 0);
|
|
int h = bm_h(handle, 0);
|
|
for (dy = 0; dy < h; dy++) {
|
|
for (dx = 0; dx < w; dx++)
|
|
bmpdata[dx] = NEW_TRANSPARENT_COLOR;
|
|
|
|
bmpdata += w;
|
|
}
|
|
}
|
|
// Given a bitmap handle, breaks a bitmap into smaller pieces.
|
|
// Note, this routine isn't terrible fast or efficient, and you
|
|
// must free the bitmaps returned to you in the bm_array
|
|
bool bm_CreateChunkedBitmap(int bm_handle, chunked_bitmap *chunk) {
|
|
int i;
|
|
int *bm_array;
|
|
int bw = bm_w(bm_handle, 0);
|
|
int bh = bm_h(bm_handle, 0);
|
|
// determine optimal size of the square bitmaps
|
|
float fopt = 128.0f;
|
|
int iopt;
|
|
// find the smallest dimension and base off that
|
|
int smallest = std::min(bw, bh);
|
|
if (smallest <= 32)
|
|
fopt = 32;
|
|
else if (smallest <= 64)
|
|
fopt = 64;
|
|
else
|
|
fopt = 128;
|
|
iopt = (int)fopt;
|
|
// Get how many pieces we need across and down
|
|
float temp = bw / fopt;
|
|
int how_many_across = temp;
|
|
if ((temp - how_many_across) > 0)
|
|
how_many_across++;
|
|
temp = bh / fopt;
|
|
int how_many_down = temp;
|
|
if ((temp - how_many_down) > 0)
|
|
how_many_down++;
|
|
ASSERT(how_many_across > 0);
|
|
ASSERT(how_many_down > 0);
|
|
// Allocate memory to hold our list of pieces
|
|
bm_array = (int *)mem_malloc(how_many_down * how_many_across * sizeof(int));
|
|
ASSERT(bm_array);
|
|
for (i = 0; i < how_many_down * how_many_across; i++) {
|
|
bm_array[i] = bm_AllocBitmap(iopt, iopt, 0);
|
|
ASSERT(bm_array[i] > -1);
|
|
// Fill our new pieces with transparency
|
|
bm_ClearBitmap(bm_array[i]);
|
|
}
|
|
// Now go through our big bitmap and partition it into pieces
|
|
uint16_t *src_data = bm_data(bm_handle, 0);
|
|
uint16_t *sdata;
|
|
uint16_t *ddata;
|
|
int shift;
|
|
switch (iopt) {
|
|
case 32:
|
|
shift = 5;
|
|
break;
|
|
case 64:
|
|
shift = 6;
|
|
break;
|
|
case 128:
|
|
shift = 7;
|
|
break;
|
|
default:
|
|
Int3(); // Get Jeff
|
|
break;
|
|
}
|
|
int maxx, maxy;
|
|
int windex, hindex;
|
|
int s_y, s_x, d_y, d_x;
|
|
for (hindex = 0; hindex < how_many_down; hindex++) {
|
|
for (windex = 0; windex < how_many_across; windex++) {
|
|
// loop through the chunks
|
|
// find end x and y
|
|
if (windex < how_many_across - 1)
|
|
maxx = iopt;
|
|
else
|
|
maxx = bw - (windex << shift);
|
|
if (hindex < how_many_down - 1)
|
|
maxy = iopt;
|
|
else
|
|
maxy = bh - (hindex << shift);
|
|
// find the starting source x and y
|
|
s_x = (windex << shift);
|
|
s_y = (hindex << shift);
|
|
// get the pointers pointing to the right spot
|
|
ddata = bm_data(bm_array[hindex * how_many_across + windex], 0);
|
|
sdata = &src_data[s_y * bw + s_x];
|
|
// copy the data
|
|
for (d_y = 0; d_y < maxy; d_y++) {
|
|
for (d_x = 0; d_x < maxx; d_x++) {
|
|
ddata[d_x] = sdata[d_x];
|
|
} // end for d_x
|
|
sdata += bw;
|
|
ddata += iopt;
|
|
} // end for d_y
|
|
} // end for windex
|
|
} // end for hindex
|
|
// Sirrah, we're done!
|
|
// Tell the calling function how many pieces we have in x and y
|
|
chunk->pw = bw;
|
|
chunk->ph = bh;
|
|
chunk->w = how_many_across;
|
|
chunk->h = how_many_down;
|
|
chunk->bm_array = bm_array;
|
|
return true;
|
|
}
|
|
// destroys a chunked bitmap.
|
|
void bm_DestroyChunkedBitmap(chunked_bitmap *chunk) {
|
|
int i, num_bmps = chunk->w * chunk->h;
|
|
for (i = 0; i < num_bmps; i++)
|
|
bm_FreeBitmap(chunk->bm_array[i]);
|
|
mem_free(chunk->bm_array);
|
|
chunk->bm_array = NULL;
|
|
}
|
|
// Changes the size of a bitmap to a new size
|
|
void bm_ChangeSize(int handle, int new_w, int new_h) {
|
|
int mipped = bm_mipped(handle);
|
|
int n;
|
|
int mem_used = (GameBitmaps[handle].width * GameBitmaps[handle].height * 2);
|
|
n = bm_AllocBitmap(new_w, new_h, mipped * ((new_w * new_h * 2) / 3));
|
|
ASSERT(n >= 0);
|
|
if (mipped)
|
|
GameBitmaps[n].flags |= BF_MIPMAPPED;
|
|
if (GameBitmaps[handle].format == BITMAP_FORMAT_4444)
|
|
GameBitmaps[n].format = BITMAP_FORMAT_4444;
|
|
|
|
bm_ScaleBitmapToBitmap(n, handle);
|
|
Bitmap_memory_used -= mem_used + (mipped * (mem_used / 3));
|
|
mem_free(GameBitmaps[handle].data16);
|
|
GameBitmaps[handle].data16 = GameBitmaps[n].data16;
|
|
GameBitmaps[handle].width = new_w;
|
|
GameBitmaps[handle].height = new_h;
|
|
GameBitmaps[n].used = 0;
|
|
Num_of_bitmaps--;
|
|
}
|
|
// Returns the format of this bitmap
|
|
int bm_format(int handle) {
|
|
if (!GameBitmaps[handle].used) {
|
|
Int3();
|
|
return -1;
|
|
}
|
|
if (GameBitmaps[handle].flags & BF_NOT_RESIDENT)
|
|
if (!bm_MakeBitmapResident(handle))
|
|
return 0;
|
|
return (GameBitmaps[handle].format);
|
|
}
|