1 /*
2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
3 *
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
6 * source.
7 *
8 */
9 #ifndef NDEBUG
10 #define BMPMAN_NDEBUG
11 #endif
12
13 #define WIN32_LEAN_AND_MEAN
14 #define BMPMAN_INTERNAL
15
16 #include "anim/animplay.h"
17 #include "anim/packunpack.h"
18 #include "bmpman/bm_internal.h"
19 #include "ddsutils/ddsutils.h"
20 #include "debugconsole/console.h"
21 #include "globalincs/systemvars.h"
22 #include "graphics/2d.h"
23 #include "graphics/matrix.h"
24 #include "io/key.h"
25 #include "io/timer.h"
26 #include "jpgutils/jpgutils.h"
27 #include "network/multiutil.h"
28 #include "parse/parselo.h"
29 #include "pcxutils/pcxutils.h"
30 #include "pngutils/pngutils.h"
31 #include "ship/ship.h"
32 #include "tgautils/tgautils.h"
33 #include "tracing/Monitor.h"
34 #include "tracing/tracing.h"
35
36 #include <cctype>
37 #include <climits>
38 #include <iomanip>
39 #include <memory>
40
41 // --------------------------------------------------------------------------------------------------------------------
42 // Private macros.
43
44 /**
45 * @todo upgrade this to an inline funciton, taking bitmap_entry and const char* as arguments
46 */
47 #define EFF_FILENAME_CHECK { if ( be->type == BM_TYPE_EFF ) strcpy_s( filename, be->info.ani.eff.filename ); else strcpy_s( filename, be->filename ); }
48 // --------------------------------------------------------------------------------------------------------------------
49 // Monitor variables
50 MONITOR(NumBitmapPage)
51 MONITOR(SizeBitmapPage)
52
53 // --------------------------------------------------------------------------------------------------------------------
54 // Definition of public variables (declared as extern in bmpman.h).
55 int ENVMAP = -1;
56
57 size_t bm_texture_ram = 0;
58 int Bm_paging = 0;
59
60 // Extension type lists
61 const BM_TYPE bm_type_list[] = { BM_TYPE_DDS, BM_TYPE_TGA, BM_TYPE_PNG, BM_TYPE_JPG, BM_TYPE_PCX };
62 const char *bm_ext_list[] = { ".dds", ".tga", ".png", ".jpg", ".pcx" };
63 const int BM_NUM_TYPES = sizeof(bm_type_list) / sizeof(bm_type_list[0]);
64
65 const BM_TYPE bm_ani_type_list[] = { BM_TYPE_EFF, BM_TYPE_ANI, BM_TYPE_PNG };
66 // NOTE: it would be better to have apng files use the .apng extension
67 // However there's lots of assumptions throughout the codebase that file extensions are only
68 // three chars long. If this is ever changed the the .apng extension could be used
69 const char *bm_ani_ext_list[] = { ".eff", ".ani", ".png" };
70 const int BM_ANI_NUM_TYPES = sizeof(bm_ani_type_list) / sizeof(bm_ani_type_list[0]);
71
72 void(*bm_set_components)(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a) = NULL;
73
74 // --------------------------------------------------------------------------------------------------------------------
75 // Definition of public variables (declared as extern in bm_internal.h).
76 SCP_vector<std::array<bitmap_slot, BM_BLOCK_SIZE>> bm_blocks;
77
78 // --------------------------------------------------------------------------------------------------------------------
79 // Definition of private variables at file scope (static).
80 static bool bm_inited = false;
81 static uint Bm_next_signature = 0x1234;
82 static int Bm_low_mem = 0;
83
84 SCP_map<int,ubyte*> bm_lookup_cache;
85
86 /**
87 * How much RAM bmpman can use for textures.
88 *
89 * @details Set to <1 to make it use all it wants.
90 *
91 * @note was initialized to 16*1024*1024 at some point to "use only 16MB for textures"
92 */
93 static int Bm_max_ram = 0;
94
95 static int Bm_ignore_duplicates = 0;
96 static int Bm_ignore_load_count = 0;
97
98 // This needs to be declared somewhere and bm_internal.h has no own source file
99 gr_bitmap_info::~gr_bitmap_info() = default;
100
101 /**
102 * Finds if a bitmap entry contains an animation
103 */
bm_is_anim(bitmap_entry * entry)104 static bool bm_is_anim(bitmap_entry* entry)
105 {
106 return ((entry->type == BM_TYPE_ANI) ||
107 (entry->type == BM_TYPE_EFF) ||
108 (entry->type == BM_TYPE_PNG && entry->info.ani.apng.is_apng));
109 }
110
bm_get_slot(int handle,bool separate_ani_frames)111 bitmap_slot* bm_get_slot(int handle, bool separate_ani_frames) {
112 Assertion(handle >= 0, "Invalid handle %d passed to bm_get_slot!", handle);
113
114 // The lower 16-bit contain the index in the bitmap block
115 auto index = handle & 0xFFFF;
116 // The upper 16-bit contain the number of the used block
117 auto block_index = handle >> 16;
118
119 Assertion(block_index >= 0 && block_index < (int) bm_blocks.size(),
120 "Bitmap handle %d has an invalid block number %d!",
121 handle,
122 block_index);
123 Assertion(index >= 0 && index < (int) bm_blocks[block_index].size(),
124 "Bitmap handle %d has an invalid block index %d!",
125 handle,
126 index);
127
128 auto slot = &bm_blocks[block_index][index];
129 auto entry = &slot->entry;
130
131 if (separate_ani_frames || !bm_is_anim(entry)) {
132 // Not an animation or we don't want the first frame so we are done now
133 return slot;
134 }
135
136 // Get the entry of the first frame by calling ourself again but this time we enforce separate ani frames (since we
137 // already know that this handle is the correct one)
138 return bm_get_slot(entry->info.ani.first_frame, true);
139 }
140
141 // --------------------------------------------------------------------------------------------------------------------
142 // Declaration of private functions and templates(declared as static type func(type param);)
143
bitmap_lookup(int bitmap_num)144 bitmap_lookup::bitmap_lookup(int bitmap_num):
145 Bitmap_data(NULL)
146 {
147 if ( !bm_is_valid(bitmap_num) ) return;
148
149 Num_channels = 3;
150
151 if ( bm_has_alpha_channel(bitmap_num) ) {
152 Num_channels = 4;
153 }
154
155 bitmap_entry *be = bm_get_entry(bitmap_num);
156
157 Width = be->bm.w;
158 Height = be->bm.h;
159
160
161 auto cache_search = bm_lookup_cache.find(bitmap_num);
162 if (cache_search == bm_lookup_cache.end()) {
163 Bitmap_data = (ubyte*)vm_malloc(Width * Height * Num_channels * sizeof(ubyte));
164
165 gr_get_bitmap_from_texture((void*)Bitmap_data, bitmap_num);
166 bm_lookup_cache.insert({bitmap_num, Bitmap_data});
167 } else {
168 Bitmap_data = cache_search->second;
169 }
170 }
171
~bitmap_lookup()172 bitmap_lookup::~bitmap_lookup()
173 {
174 }
175
valid()176 bool bitmap_lookup::valid()
177 {
178 return Bitmap_data != NULL;
179 }
180
map_texture_address(float address)181 float bitmap_lookup::map_texture_address(float address)
182 {
183 // assume we're just wrapping
184 return address - floorf(address);
185 }
186
get_channel_alpha(float u,float v)187 float bitmap_lookup::get_channel_alpha(float u, float v)
188 {
189 Assert( Bitmap_data != NULL );
190
191 int x = fl2i(map_texture_address(u) * (Width-1));
192 int y = fl2i(map_texture_address(v) * (Height-1));
193
194 return i2fl(Bitmap_data[(y*Width + x)*Num_channels + 3]) / 255.0f;
195 }
196
clear_bm_lookup_cache()197 void clear_bm_lookup_cache() {
198 for(auto &iter: bm_lookup_cache) {
199 free(iter.second);
200 }
201 bm_lookup_cache.clear();
202 }
203
204 /**
205 * Converts the bitmap referenced by bmp to the type specified by flags
206 */
207 static void bm_convert_format(bitmap *bmp, ushort flags);
208
209 /**
210 * Frees a bitmap's data if it can
211 */
212 static void bm_free_data(bitmap_slot* n, bool release = false);
213
214 /**
215 * A special version of bm_free_data() that can be safely used in gr_*_texture
216 * to save system memory once textures have been transfered to API memory
217 * it doesn't restore the slot to a pristine state, it only releases the data
218 *
219 * @attention: THIS SHOULD ONLY BE USED FROM bm_unload_fast()!!!
220 */
221 static void bm_free_data_fast(int handle);
222
223 /**
224 * Given a raw filename and an extension set, try and find the bitmap
225 * that isn't already loaded and may exist somewhere on the disk
226 *
227 * @returns -1 if it could not be found,
228 * @returns index into ext_list[] if it was found as a file, fills img_cfg if available
229 */
230 static int bm_load_sub_slow(const char *real_filename, const int num_ext, const char **ext_list, CFILE **img_cfp = NULL, int dir_type = CF_TYPE_ANY);
231
232 /**
233 * Given a raw filename, try and find a bitmap that's already loaded
234 *
235 * @returns 0 if it could not be found,
236 * @returns 1 if it already exists, fills in handle
237 */
238 static int bm_load_sub_fast(const char *real_filename, int *handle, int dir_type = CF_TYPE_ANY, bool animated_type = false);
239
240 /**
241 * @brief Finds a start handle to a block of contiguous bitmap slots
242 *
243 * This will return a bitmap handle which has space for n slots.
244 *
245 * @param n How many slots the bitmap needs
246 * @param start_block At which block the search should start. Do not use. This is only for internal purposes.
247 *
248 * @returns -1 if the block could not be found
249 * @returns the handle of the block ?
250 */
251 static int find_block_of(int n, int start_block = 0);
252
253
get_handle(int block,int index)254 static int get_handle(int block, int index) {
255 Assertion(block >= 0, "Negative block values are not allowed!");
256 Assertion(index >= 0, "Negative index values are not allowed!");
257
258 return (uint32_t) block << 16 | (uint16_t) index;
259 }
260
allocate_new_block()261 static void allocate_new_block() {
262 bm_blocks.emplace_back();
263 auto& new_block = bm_blocks.back();
264
265 for (auto& slot : new_block) {
266 auto& entry = slot.entry;
267
268 entry.filename[0] = '\0';
269 entry.type = BM_TYPE_NONE;
270 entry.comp_type = BM_TYPE_NONE;
271 entry.dir_type = CF_TYPE_ANY;
272 entry.info.user.data = nullptr;
273 entry.mem_taken = 0;
274 entry.bm.data = 0;
275 entry.bm.palette = nullptr;
276 entry.info.ani.eff.type = BM_TYPE_NONE;
277 entry.info.ani.eff.filename[0] = '\0';
278 #ifdef BMPMAN_NDEBUG
279 entry.data_size = 0;
280 entry.used_count = 0;
281 entry.used_last_frame = 0;
282 entry.used_this_frame = 0;
283 #endif
284 entry.load_count = 0;
285
286 gr_bm_init(&slot);
287
288 // clears flags, bbp, data, etc
289 bm_free_data(&slot);
290 }
291 }
292
293 // --------------------------------------------------------------------------------------------------------------------
294 // Macro-defined functions
295
296 DCF(bm_frag, "Shows BmpMan fragmentation") {
297 if (dc_optional_string_either("help", "--help")) {
298 dc_printf("Displays a graphic showing the BmpMan fragmentation. Color key:\n");
299 dc_printf("\tGray : NONE\n");
300 dc_printf("\tRed : PCXn");
301 dc_printf("\tGreen : USER, TGA, PNG, DDS, other\n");
302 dc_printf("\tBlue : ANI, EFF\n\n");
303
304 dc_printf("Once done reviewing the graphic, press any key to return to the console\n");
305 return;
306 }
307
308 gr_clear();
309
310 int x = 0, y = 0;
311 int xs = 2, ys = 2;
312 int w = 4, h = 4;
313
314 for (auto& block : bm_blocks) {
315 for (size_t i = 0; i < BM_BLOCK_SIZE; ++i) {
316 switch (block[i].entry.type) {
317 case BM_TYPE_NONE:
318 gr_set_color(128, 128, 128);
319 break;
320 case BM_TYPE_PCX:
321 gr_set_color(255, 0, 0);
322 break;
323 case BM_TYPE_USER:
324 case BM_TYPE_TGA:
325 case BM_TYPE_PNG:
326 case BM_TYPE_DDS:
327 gr_set_color(0, 255, 0);
328 break;
329 case BM_TYPE_ANI:
330 case BM_TYPE_EFF:
331 gr_set_color(0, 0, 255);
332 break;
333 default:
334 gr_set_color(0, 255, 0);
335 break;
336 }
337
338 gr_rect(x + xs, y + ys, w, h);
339 x += w + xs + xs;
340 if (x > 639) {
341 x = 0;
342 y += h + ys + ys;
343 }
344 }
345 }
346
347 gr_flip();
348 key_getch();
349 }
350
351 DCF(bm_used, "Shows BmpMan Slot Usage") {
352 if (dc_optional_string_either("help", "--help")) {
353 dc_printf("Displays used bmpman slots usage with a breakdown per filetype\n\n");
354 return;
355 }
356
357 int none = 0, pcx = 0, user = 0, tga = 0, png = 0; int jpg = 0, dds = 0, ani = 0;
358 int eff = 0, eff_dds = 0, eff_tga = 0, eff_png = 0, eff_jpg = 0, eff_pcx = 0;
359 int render_target_dynamic = 0, render_target_static = 0;
360
361 for (auto& block : bm_blocks) {
362 for (size_t i = 0; i < BM_BLOCK_SIZE; ++i) {
363 switch (block[i].entry.type) {
364 case BM_TYPE_NONE:
365 none++;
366 break;
367 case BM_TYPE_PCX:
368 pcx++;
369 break;
370 case BM_TYPE_USER:
371 user++;
372 break;
373 case BM_TYPE_TGA:
374 tga++;
375 break;
376 case BM_TYPE_PNG:
377 // TODO distinguish png(static) from apng
378 png++;
379 break;
380 case BM_TYPE_JPG:
381 jpg++;
382 break;
383 case BM_TYPE_DDS:
384 dds++;
385 break;
386 case BM_TYPE_ANI:
387 ani++;
388 break;
389 case BM_TYPE_EFF:
390 eff++;
391 switch (block[i].entry.info.ani.eff.type) {
392 case BM_TYPE_DDS:
393 eff_dds++;
394 break;
395 case BM_TYPE_TGA:
396 eff_tga++;
397 break;
398 case BM_TYPE_PNG:
399 eff_png++;
400 break;
401 case BM_TYPE_JPG:
402 eff_jpg++;
403 break;
404 case BM_TYPE_PCX:
405 eff_pcx++;
406 break;
407 default:
408 Warning(LOCATION, "Unhandled EFF image type (%i), get a coder!", block[i].entry.info.ani.eff.type);
409 break;
410 }
411 break;
412 case BM_TYPE_RENDER_TARGET_STATIC:
413 render_target_static++;
414 break;
415 case BM_TYPE_RENDER_TARGET_DYNAMIC:
416 render_target_dynamic++;
417 break;
418 default:
419 Warning(LOCATION, "Unhandled image type (%i), get a coder!", block[i].entry.type);
420 break;
421 }
422 }
423 }
424
425 SCP_stringstream text;
426 text << "BmpMan Used Slots\n";
427 text << " " << std::dec << std::setw(4) << std::setfill('0') << pcx << ", PCX\n";
428 text << " " << std::dec << std::setw(4) << std::setfill('0') << user << ", User\n";
429 text << " " << std::dec << std::setw(4) << std::setfill('0') << tga << ", TGA\n";
430 text << " " << std::dec << std::setw(4) << std::setfill('0') << png << ", PNG\n";
431 text << " " << std::dec << std::setw(4) << std::setfill('0') << jpg << ", JPG\n";
432 text << " " << std::dec << std::setw(4) << std::setfill('0') << dds << ", DDS\n";
433 text << " " << std::dec << std::setw(4) << std::setfill('0') << ani << ", ANI\n";
434 text << " " << std::dec << std::setw(4) << std::setfill('0') << eff << ", EFF\n";
435 text << " " << std::dec << std::setw(4) << std::setfill('0') << eff_dds << ", EFF/DDS\n";
436 text << " " << std::dec << std::setw(4) << std::setfill('0') << eff_tga << ", EFF/TGA\n";
437 text << " " << std::dec << std::setw(4) << std::setfill('0') << eff_png << ", EFF/PNG\n";
438 text << " " << std::dec << std::setw(4) << std::setfill('0') << eff_jpg << ", EFF/JPG\n";
439 text << " " << std::dec << std::setw(4) << std::setfill('0') << eff_pcx << ", EFF/PCX\n";
440 text << " " << std::dec << std::setw(4) << std::setfill('0') << render_target_static << ", Render/Static\n";
441 text << " " << std::dec << std::setw(4) << std::setfill('0') << render_target_dynamic << ", Render/Dynamic\n";
442 text << " " << std::dec << std::setw(4) << std::setfill('0') << bmpman_count_bitmaps() << "/" << bmpman_count_available_slots() << ", Total\n";
443 text << "\n";
444
445 // TODO consider converting 1's to monospace to make debug console output prettier
446 mprintf(("%s", text.str().c_str())); // log for ease for copying data
447 dc_printf("%s", text.str().c_str()); // instant gratification
448 }
449
450
451 DCF(bmpman, "Shows/changes bitmap caching parameters and usage") {
452 if (dc_optional_string_either("help", "--help")) {
453 dc_printf("Usage: BmpMan [arg]\nWhere arg can be any of the following:\n");
454 dc_printf("\tflush Unloads all bitmaps.\n");
455 dc_printf("\tram [x] Sets max mem usage to x MB. (Set to 0 to have no limit.)\n");
456 dc_printf("\t? Displays status of Bitmap manager.\n");
457 return;
458 }
459
460 if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
461 dc_printf("Total RAM usage: " SIZE_T_ARG " bytes\n", bm_texture_ram);
462
463 if (Bm_max_ram > 1024 * 1024) {
464 dc_printf("\tMax RAM allowed: %.1f MB\n", i2fl(Bm_max_ram) / (1024.0f*1024.0f));
465 } else if (Bm_max_ram > 1024) {
466 dc_printf("\tMax RAM allowed: %.1f KB\n", i2fl(Bm_max_ram) / (1024.0f));
467 } else if (Bm_max_ram > 0) {
468 dc_printf("\tMax RAM allowed: %d bytes\n", Bm_max_ram);
469 } else {
470 dc_printf("\tNo RAM limit\n");
471 }
472 return;
473 }
474
475
476 if (dc_optional_string("flush")) {
477 dc_printf("Total RAM usage before flush: " SIZE_T_ARG " bytes\n", bm_texture_ram);
478 for (auto& block : bm_blocks) {
479 for (size_t i = 0; i < BM_BLOCK_SIZE; ++i) {
480 if (block[i].entry.type != BM_TYPE_NONE) {
481 bm_free_data(&block[i]);
482 }
483 }
484 }
485 dc_printf("Total RAM after flush: " SIZE_T_ARG " bytes\n", bm_texture_ram);
486 } else if (dc_optional_string("ram")) {
487 dc_stuff_int(&Bm_max_ram);
488
489 if (Bm_max_ram > 0) {
490 dc_printf("BmpMan limited to %i, MB's\n", Bm_max_ram);
491 Bm_max_ram *= 1024 * 1024;
492 } else if (Bm_max_ram == 0) {
493 dc_printf("!!BmpMan memory is unlimited!!\n");
494 } else {
495 dc_printf("Illegal value. Must be non-negative.");
496 }
497 } else {
498 dc_printf("<BmpMan> No argument given\n");
499 }
500 }
501
502 DCF(bmpslots, "Writes bitmap slot info to fs2_open.log") {
503 if (dc_optional_string_either("help", "--help")) {
504 dc_printf("Usage: bmpslots\n");
505 dc_printf("\tWrites bitmap slot info to fs2_open.log\n");
506 return;
507 }
508 bm_print_bitmaps();
509 }
510
511 // --------------------------------------------------------------------------------------------------------------------
512 // Definition of all functions, in alphabetical order
bm_close()513 void bm_close() {
514 if (bm_inited) {
515 for (auto& block : bm_blocks) {
516 for (auto& slot : block) {
517 bm_free_data(&slot); // clears flags, bbp, data, etc
518
519 if (slot.gr_info != nullptr) {
520 // free graphics data
521 delete slot.gr_info;
522 slot.gr_info = nullptr;
523 }
524 }
525 }
526 bm_blocks.clear();
527 bm_inited = false;
528 }
529 }
530
bm_create(int bpp,int w,int h,void * data,int flags)531 int bm_create(int bpp, int w, int h, void *data, int flags) {
532 if (bpp == 8) {
533 Assert(flags & BMP_AABITMAP);
534 } else {
535 Assert((bpp == 16) || (bpp == 24) || (bpp == 32));
536 }
537
538 Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
539
540 int n = find_block_of(1);
541
542 // Out of bitmap slots
543 if (n == -1)
544 return -1;
545
546 // make sure that we have valid data
547 if (data == NULL) {
548 Int3();
549 return -1;
550 }
551
552 auto entry = bm_get_entry(n);
553
554 memset(entry, 0, sizeof(bitmap_entry));
555
556 sprintf_safe(entry->filename, "TMP%dx%d+%d", w, h, bpp);
557 entry->type = BM_TYPE_USER;
558 entry->comp_type = BM_TYPE_NONE;
559
560 entry->bm.w = (short)w;
561 entry->bm.h = (short)h;
562 entry->bm.rowsize = (short)w;
563 entry->bm.bpp = (ubyte)bpp;
564 entry->bm.true_bpp = (ubyte)bpp;
565 entry->bm.flags = (ubyte)flags;
566 entry->bm.data = 0;
567 entry->bm.palette = nullptr;
568
569 entry->info.user.bpp = (ubyte)bpp;
570 entry->info.user.data = data;
571 entry->info.user.flags = (ubyte)flags;
572
573 entry->signature = Bm_next_signature++;
574
575 entry->handle = n;
576 entry->mem_taken = (w * h * (bpp >> 3));
577
578 entry->load_count++;
579
580 bm_update_memory_used(n, (int)entry->mem_taken);
581
582 gr_bm_create(bm_get_slot(n));
583
584 return n;
585 }
586
bm_convert_format(bitmap * bmp,ushort flags)587 void bm_convert_format(bitmap *bmp, ushort flags) {
588 int idx;
589
590 // no transparency for 24 bpp images
591 if (!(flags & BMP_AABITMAP) && (bmp->bpp == 24))
592 return;
593
594 if (Is_standalone) {
595 Assert(bmp->bpp == 8);
596 return;
597 } else {
598 if (flags & BMP_AABITMAP)
599 Assert(bmp->bpp == 8);
600 else
601 Assert((bmp->bpp == 16) || (bmp->bpp == 32));
602 }
603
604 // maybe swizzle to be an xparent texture
605 if (!(bmp->flags & BMP_TEX_XPARENT) && (flags & BMP_TEX_XPARENT)) {
606 for (idx = 0; idx<bmp->w*bmp->h; idx++) {
607 // if the pixel is transparent
608 if (((ushort*)bmp->data)[idx] == Gr_t_green.mask) {
609 ((ushort*)bmp->data)[idx] = 0;
610 }
611 }
612
613 bmp->flags |= BMP_TEX_XPARENT;
614 }
615 }
616
bm_free_data(bitmap_slot * bs,bool release)617 void bm_free_data(bitmap_slot* bs, bool release)
618 {
619 bitmap *bmp;
620
621 auto be = &bs->entry;
622 bmp = &be->bm;
623
624 gr_bm_free_data(bs, release);
625
626 // If there isn't a bitmap in this structure, don't
627 // do anything but clear out the bitmap info
628 if (be->type==BM_TYPE_NONE)
629 goto SkipFree;
630
631 // Don't free up memory for user defined bitmaps, since
632 // BmpMan isn't the one in charge of allocating/deallocing them.
633 if (be->type==BM_TYPE_USER) {
634 #ifdef BMPMAN_NDEBUG
635 if ( be->data_size != 0 )
636 bm_texture_ram -= be->data_size;
637 #endif
638 goto SkipFree;
639 }
640
641 // If this bitmap doesn't have any data to free, skip
642 // the freeing it part of this.
643 if (bmp->data == 0) {
644 #ifdef BMPMAN_NDEBUG
645 if ( be->data_size != 0 )
646 bm_texture_ram -= be->data_size;
647 #endif
648 goto SkipFree;
649 }
650
651 // Free up the data now!
652 #ifdef BMPMAN_NDEBUG
653 bm_texture_ram -= be->data_size;
654 #endif
655 vm_free((void *)bmp->data);
656
657 // reset the load_count to at least 1, don't do this in SkipFree though
658 // since the real count ends up wrong
659 be->load_count = 1;
660
661 SkipFree:
662
663 // Clear out & reset the bitmap data structure
664 bmp->flags = 0;
665 bmp->bpp = 0;
666 bmp->data = 0;
667 bmp->palette = NULL;
668 #ifdef BMPMAN_NDEBUG
669 be->data_size = 0;
670 #endif
671 be->signature = Bm_next_signature++;
672 }
673
bm_free_data_fast(int handle)674 void bm_free_data_fast(int handle)
675 {
676 bitmap_entry *be;
677 bitmap *bmp;
678
679 be = bm_get_entry(handle);
680 bmp = &be->bm;
681
682 // If there isn't a bitmap in this structure, don't
683 // do anything but clear out the bitmap info
684 if (be->type == BM_TYPE_NONE)
685 return;
686
687 // Don't free up memory for user defined bitmaps, since
688 // BmpMan isn't the one in charge of allocating/deallocing them.
689 if (be->type == BM_TYPE_USER) {
690 #ifdef BMPMAN_NDEBUG
691 if ( be->data_size != 0 )
692 bm_texture_ram -= be->data_size;
693 #endif
694 return;
695 }
696
697 // If this bitmap doesn't have any data to free, skip
698 // the freeing it part of this.
699 if (bmp->data == 0) {
700 #ifdef BMPMAN_NDEBUG
701 if ( be->data_size != 0 ) {
702 bm_texture_ram -= be->data_size;
703 be->data_size = 0;
704 }
705 #endif
706 return;
707 }
708
709 // Free up the data now!
710 #ifdef BMPMAN_NDEBUG
711 bm_texture_ram -= be->data_size;
712 be->data_size = 0;
713 #endif
714 vm_free((void *)bmp->data);
715 bmp->data = 0;
716 }
717
bm_get_anim_frame(const int frame1_handle,float elapsed_time,const float divisor,const bool loop)718 int bm_get_anim_frame(const int frame1_handle, float elapsed_time, const float divisor, const bool loop)
719 {
720 bitmap_entry *be = bm_get_entry(frame1_handle);
721
722 if (be->info.ani.num_frames <= 1) {
723 // this is a still image
724 return 0;
725 }
726 int last_frame = be->info.ani.num_frames - 1;
727 bitmap_entry *last_framep = bm_get_entry(frame1_handle + last_frame);
728
729 if (elapsed_time < 0.0f) {
730 elapsed_time = 0.0f;
731 }
732
733 int frame = 0;
734 // variable frame delay animations
735 if (be->info.ani.apng.is_apng == true) {
736 if (divisor > 0.0f) {
737 // scale to get the real elapsed time
738 elapsed_time = elapsed_time / divisor * last_framep->info.ani.apng.frame_delay;
739 }
740
741 if (loop == true) {
742 elapsed_time = fmod(elapsed_time, last_framep->info.ani.apng.frame_delay);
743 }
744
745 int i = frame1_handle;
746 for ( ; i < (frame1_handle + be->info.ani.num_frames); ++i) {
747 // see bm_lock_apng for precalculated incremental delay for each frame
748 if (elapsed_time <= bm_get_entry(i)->info.ani.apng.frame_delay) {
749 break;
750 }
751 }
752 frame = i - frame1_handle;
753 }
754 // fixed frame delay animations; simpler
755 else {
756 if (divisor > 0.0f) {
757 // scale to get the real elapsed time
758 frame = fl2i(elapsed_time / divisor * be->info.ani.num_frames);
759 }
760 else {
761 frame = fl2i(elapsed_time * i2fl(be->info.ani.fps));
762 }
763
764 if (loop == true) {
765 frame %= be->info.ani.num_frames;
766 }
767 }
768 // note; this also makes non-looping anims hold on their last frame
769 CLAMP(frame, 0, last_frame);
770
771 return frame;
772 }
773
bm_get_components(ubyte * pixel,ubyte * r,ubyte * g,ubyte * b,ubyte * a)774 void bm_get_components(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a) {
775 int bit_32 = 0;
776
777 if ((gr_screen.bits_per_pixel == 32) && (Gr_current_red == &Gr_red)) {
778 bit_32 = 1;
779 }
780
781 if (r != NULL) {
782 if (bit_32) {
783 *r = ubyte(((*((unsigned int*)pixel) & Gr_current_red->mask) >> Gr_current_red->shift)*Gr_current_red->scale);
784 } else {
785 *r = ubyte(((((unsigned short*)pixel)[0] & Gr_current_red->mask) >> Gr_current_red->shift)*Gr_current_red->scale);
786 }
787 }
788
789 if (g != NULL) {
790 if (bit_32) {
791 *g = ubyte(((*((unsigned int*)pixel) & Gr_current_green->mask) >> Gr_current_green->shift)*Gr_current_green->scale);
792 } else {
793 *g = ubyte(((((unsigned short*)pixel)[0] & Gr_current_green->mask) >> Gr_current_green->shift)*Gr_current_green->scale);
794 }
795 }
796
797 if (b != NULL) {
798 if (bit_32) {
799 *b = ubyte(((*((unsigned int*)pixel) & Gr_current_blue->mask) >> Gr_current_blue->shift)*Gr_current_blue->scale);
800 } else {
801 *b = ubyte(((((unsigned short*)pixel)[0] & Gr_current_blue->mask) >> Gr_current_blue->shift)*Gr_current_blue->scale);
802 }
803 }
804
805 // get the alpha value
806 if (a != NULL) {
807 *a = 1;
808
809 Assert(!bit_32);
810 if (!(((unsigned short*)pixel)[0] & 0x8000)) {
811 *a = 0;
812 }
813 }
814 }
815
bm_get_filename(int handle)816 const char *bm_get_filename(int handle) {
817 auto entry = bm_get_entry(handle);
818 return entry->filename;
819 }
820
bm_get_filename(int bitmapnum,char * filename)821 void bm_get_filename(int bitmapnum, char *filename) {
822 if (!bm_is_valid(bitmapnum)) {
823 strcpy(filename, "");
824 return;
825 }
826
827 // return filename
828 strcpy(filename, bm_get_entry(bitmapnum)->filename);
829 }
830
bm_get_frame_usage(int * ntotal,int * nnew)831 void bm_get_frame_usage(int *ntotal, int *nnew) {
832 #ifdef BMPMAN_NDEBUG
833 *ntotal = 0;
834 *nnew = 0;
835
836 for (auto& block : bm_blocks) {
837 for (auto& slot : block) {
838 auto& entry = slot.entry;
839 if ((entry.type != BM_TYPE_NONE) && (entry.used_this_frame)) {
840 if (!entry.used_last_frame) {
841 *nnew += (int)entry.mem_taken;
842 }
843 *ntotal += (int)entry.mem_taken;
844 }
845 entry.used_last_frame = entry.used_this_frame;
846 entry.used_this_frame = 0;
847 }
848 }
849 #endif
850 }
851
bm_get_info(int handle,int * w,int * h,ushort * flags,int * nframes,int * fps)852 int bm_get_info(int handle, int *w, int * h, ushort* flags, int *nframes, int *fps) {
853 bitmap * bmp;
854
855 if (!bm_inited) return -1;
856
857 auto entry = bm_get_entry(handle);
858
859 Assertion(entry->handle == handle, "Invalid bitmap handle %d passed to bm_get_info().\nThis might be due to an invalid animation somewhere else.\n", handle); // INVALID BITMAP HANDLE!
860
861 if ((entry->type == BM_TYPE_NONE) || (entry->handle != handle)) {
862 if (w) *w = 0;
863 if (h) *h = 0;
864 if (flags) *flags = 0;
865 if (nframes) *nframes = 0;
866 if (fps) *fps = 0;
867 return -1;
868 }
869
870 bmp = &(entry->bm);
871
872 if (w) *w = bmp->w;
873 if (h) *h = bmp->h;
874 if (flags) *flags = bmp->flags;
875
876 if (bm_is_anim(entry)) {
877 if (nframes) {
878 *nframes = entry->info.ani.num_frames;
879 }
880 if (fps) {
881 *fps = entry->info.ani.fps;
882 }
883
884 return bm_get_entry(entry->info.ani.first_frame)->handle;
885 } else {
886 if (nframes) {
887 *nframes = 1;
888 }
889 if (fps) {
890 *fps = 0;
891 }
892
893 return handle;
894 }
895 }
896
bm_get_num_mipmaps(int num)897 int bm_get_num_mipmaps(int num) {
898 auto entry = bm_get_entry(num);
899
900 if (entry->num_mipmaps == 0)
901 return 1;
902
903 return entry->num_mipmaps;
904 }
905
bm_get_palette(int handle,ubyte * pal,char * name)906 void bm_get_palette(int handle, ubyte *pal, char *name) {
907 int w, h;
908
909 const char* filename = bm_get_entry(handle)->filename;
910
911 if (name) {
912 strcpy(name, filename);
913 }
914
915 int pcx_error = pcx_read_header(filename, NULL, &w, &h, NULL, pal);
916 if (pcx_error != PCX_ERROR_NONE) {
917 // Error(LOCATION, "Couldn't open '%s'\n", filename );
918 }
919 }
920
bm_get_tcache_type(int num)921 int bm_get_tcache_type(int num) {
922 if (bm_is_compressed(num))
923 return TCACHE_TYPE_COMPRESSED;
924
925 return TCACHE_TYPE_NORMAL;
926 }
927
bm_get_type(int handle)928 BM_TYPE bm_get_type(int handle) {
929 Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
930
931 return bm_get_entry(handle)->type;
932 }
933
bm_has_alpha_channel(int handle)934 bool bm_has_alpha_channel(int handle) {
935 auto entry = bm_get_entry(handle);
936
937 // assume that PCX never has a real alpha channel (it may be 32-bit, but without any alpha)
938 if (entry->type == BM_TYPE_PCX)
939 return 0;
940
941 return (entry->bm.true_bpp == 32);
942 }
943
bm_init()944 void bm_init() {
945 Assertion(!bm_inited, "bmpman cannot be initialized more than once!");
946
947 // Allocate one block by default
948 allocate_new_block();
949
950 bm_inited = true;
951 }
952
bm_is_compressed(int num)953 int bm_is_compressed(int num) {
954 auto entry = bm_get_entry(num);
955 BM_TYPE type = BM_TYPE_NONE;
956
957 //duh
958 if (!Use_compressed_textures)
959 return 0;
960
961 type = entry->comp_type;
962
963 switch (type) {
964 case BM_TYPE_NONE:
965 case BM_TYPE_DDS:
966 return 0;
967
968 case BM_TYPE_DXT1:
969 return DDS_DXT1;
970
971 case BM_TYPE_DXT3:
972 return DDS_DXT3;
973
974 case BM_TYPE_DXT5:
975 return DDS_DXT5;
976
977 case BM_TYPE_BC7:
978 return DDS_BC7;
979
980 case BM_TYPE_CUBEMAP_DXT1:
981 return DDS_CUBEMAP_DXT1;
982
983 case BM_TYPE_CUBEMAP_DXT3:
984 return DDS_CUBEMAP_DXT3;
985
986 case BM_TYPE_CUBEMAP_DXT5:
987 return DDS_CUBEMAP_DXT5;
988
989 default:
990 return 0;
991 }
992 }
993
bm_is_render_target(int bitmap_id)994 int bm_is_render_target(int bitmap_id) {
995 auto entry = bm_get_entry(bitmap_id);
996
997 if (!((entry->type == BM_TYPE_RENDER_TARGET_STATIC) || (entry->type == BM_TYPE_RENDER_TARGET_DYNAMIC))) {
998 return 0;
999 }
1000
1001 return entry->type;
1002 }
1003
bm_is_valid(int handle)1004 int bm_is_valid(int handle) {
1005 // Ensure that certain known false or out of range handles are quickly returned as invalid,
1006 // prior to utilising the handle in a way which leads to memory access outside bm_bitmaps[]
1007 if (!bm_inited) return 0;
1008 if (handle < 0) return 0;
1009
1010 auto entry = bm_get_entry(handle);
1011
1012 return entry->handle == handle;
1013 }
1014
1015
1016 // Load an image and validate it while retrieving information for later use
1017 // Input: type = current BM_TYPE_*
1018 // n = location in bm_bitmaps[]
1019 // filename = name of the current file
1020 // img_cfp = already open CFILE handle, if available
1021 //
1022 // Output: w = bmp width
1023 // h = bmp height
1024 // bpp = bmp bits per pixel
1025 // c_type = output for an updated BM_TYPE_*
1026 // mm_lvl = number of mipmap levels for the image
1027 // size = size of the data contained in the image
bm_load_info(BM_TYPE type,const char * filename,CFILE * img_cfp,int * w,int * h,int * bpp,BM_TYPE * c_type,int * mm_lvl,size_t * size)1028 static int bm_load_info(BM_TYPE type, const char *filename, CFILE *img_cfp, int *w, int *h, int *bpp, BM_TYPE *c_type, int *mm_lvl, size_t *size)
1029 {
1030 int dds_ct;
1031
1032 if (type == BM_TYPE_DDS) {
1033 int dds_error = dds_read_header(filename, img_cfp, w, h, bpp, &dds_ct, mm_lvl, size);
1034
1035 if (dds_error != DDS_ERROR_NONE) {
1036 mprintf(("DDS ERROR: Couldn't open '%s' -- %s\n", filename, dds_error_string(dds_error)));
1037 return -1;
1038 }
1039
1040 switch (dds_ct) {
1041 case DDS_DXT1:
1042 *c_type = BM_TYPE_DXT1;
1043 break;
1044
1045 case DDS_DXT3:
1046 *c_type = BM_TYPE_DXT3;
1047 break;
1048
1049 case DDS_DXT5:
1050 *c_type = BM_TYPE_DXT5;
1051 break;
1052
1053 case DDS_BC7:
1054 *c_type = BM_TYPE_BC7;
1055 break;
1056
1057 case DDS_UNCOMPRESSED:
1058 *c_type = BM_TYPE_DDS;
1059 break;
1060
1061 case DDS_CUBEMAP_DXT1:
1062 *c_type = BM_TYPE_CUBEMAP_DXT1;
1063 break;
1064
1065 case DDS_CUBEMAP_DXT3:
1066 *c_type = BM_TYPE_CUBEMAP_DXT3;
1067 break;
1068
1069 case DDS_CUBEMAP_DXT5:
1070 *c_type = BM_TYPE_CUBEMAP_DXT5;
1071 break;
1072
1073 case DDS_CUBEMAP_UNCOMPRESSED:
1074 *c_type = BM_TYPE_CUBEMAP_DDS;
1075 break;
1076
1077 default:
1078 Error(LOCATION, "Bad DDS file compression! Not using DXT1,3,5: %s", filename);
1079 return -1;
1080 }
1081 }
1082 // if its a tga file
1083 else if (type == BM_TYPE_TGA) {
1084 int tga_error = targa_read_header(filename, img_cfp, w, h, bpp, NULL);
1085 if (tga_error != TARGA_ERROR_NONE) {
1086 mprintf(("tga: Couldn't open '%s'\n", filename));
1087 return -1;
1088 }
1089 }
1090 // if its a png file
1091 else if (type == BM_TYPE_PNG) {
1092 int png_error = png_read_header(filename, img_cfp, w, h, bpp, NULL);
1093 if (png_error != PNG_ERROR_NONE) {
1094 mprintf(("png: Couldn't open '%s'\n", filename));
1095 return -1;
1096 }
1097 }
1098 // if its a jpg file
1099 else if (type == BM_TYPE_JPG) {
1100 int jpg_error = jpeg_read_header(filename, img_cfp, w, h, bpp, NULL);
1101 if (jpg_error != JPEG_ERROR_NONE) {
1102 mprintf(("jpg: Couldn't open '%s'\n", filename));
1103 return -1;
1104 }
1105 }
1106 // if its a pcx file
1107 else if (type == BM_TYPE_PCX) {
1108 int pcx_error = pcx_read_header(filename, img_cfp, w, h, bpp, NULL);
1109 if (pcx_error != PCX_ERROR_NONE) {
1110 mprintf(("pcx: Couldn't open '%s'\n", filename));
1111 return -1;
1112 }
1113 }
1114 else {
1115 UNREACHABLE("Unknown file type specified! This is probably a coding error.");
1116
1117 return -1;
1118 }
1119
1120 return 0;
1121 }
1122
bm_load(const char * real_filename)1123 int bm_load(const char *real_filename) {
1124 int free_slot = -1;
1125 int w, h, bpp = 8;
1126 int rc = 0;
1127 size_t bm_size = 0;
1128 int mm_lvl = 0;
1129 char filename[MAX_FILENAME_LEN];
1130 BM_TYPE type = BM_TYPE_NONE;
1131 BM_TYPE c_type = BM_TYPE_NONE;
1132 CFILE *img_cfp = NULL;
1133 int handle = -1;
1134
1135 Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
1136
1137 // if no file was passed then get out now
1138 if ((real_filename == NULL) || (strlen(real_filename) <= 0))
1139 return -1;
1140
1141 // make sure no one passed an extension
1142 memset(filename, 0, MAX_FILENAME_LEN);
1143 strncpy(filename, real_filename, MAX_FILENAME_LEN - 1);
1144 char *p = strrchr(filename, '.');
1145 if (p) {
1146 mprintf(("Someone passed an extension to bm_load for file '%s'\n", real_filename));
1147 *p = 0;
1148 }
1149
1150 // If we are standalone server keep replacing the 'right_bracket' (right side help bracket) as the filename
1151 // should keep the game happy while loading only single pcx file which the needs to be present in any case
1152 if (Is_standalone) {
1153 char standalone_filename[MAX_FILENAME_LEN] = "right_bracket";
1154 strcpy_s(filename, standalone_filename);
1155 }
1156
1157 // safety catch for strcat...
1158 // MAX_FILENAME_LEN-5 == '.' plus 3 letter ext plus NULL terminator
1159 if (strlen(filename) > MAX_FILENAME_LEN - 5) {
1160 Warning(LOCATION, "Passed filename, '%s', is too long to support an extension!!\n\nMaximum length, minus the extension, is %i characters.\n", filename, MAX_FILENAME_LEN - 5);
1161 return -1;
1162 }
1163
1164 // Lets find out what type it is
1165 {
1166 // see if it's already loaded (checks for any type with filename)
1167 if (bm_load_sub_fast(filename, &handle))
1168 return handle;
1169
1170 // if we are still here then we need to fall back to a file-based search
1171 int rval = bm_load_sub_slow(filename, BM_NUM_TYPES, bm_ext_list, &img_cfp);
1172
1173 if (rval < 0)
1174 return -1;
1175
1176 strcat_s(filename, bm_ext_list[rval]);
1177 type = bm_type_list[rval];
1178 }
1179
1180 Assert(type != BM_TYPE_NONE);
1181
1182 // Find an open slot
1183 free_slot = find_block_of(1);
1184
1185 if (free_slot < 0) {
1186 Assertion(free_slot < 0, "Could not find free BMPMAN slot for bitmap: %s", real_filename);
1187 if (img_cfp != nullptr)
1188 cfclose(img_cfp);
1189 return -1;
1190 }
1191
1192 rc = bm_load_info(type, filename, img_cfp, &w, &h, &bpp, &c_type, &mm_lvl, &bm_size);
1193
1194 if (rc != 0) {
1195 if (img_cfp != nullptr)
1196 cfclose(img_cfp);
1197 return -1;
1198 }
1199
1200 if ((bm_size <= 0) && (w) && (h) && (bpp))
1201 bm_size = (w * h * (bpp >> 3));
1202
1203
1204 handle = free_slot;
1205
1206 auto entry = bm_get_entry(handle);
1207
1208 // ensure fields are cleared out from previous bitmap
1209 memset(entry, 0, sizeof(bitmap_entry));
1210
1211 // Mark the slot as filled, because cf_read might load a new bitmap
1212 // into this slot.
1213 strcpy_s(entry->filename, filename);
1214 entry->type = type;
1215 entry->comp_type = c_type;
1216 entry->signature = Bm_next_signature++;
1217 entry->bm.w = (short)w;
1218 entry->bm.rowsize = (short)w;
1219 entry->bm.h = (short)h;
1220 entry->bm.bpp = 0;
1221 entry->bm.true_bpp = (ubyte)bpp;
1222 entry->bm.flags = 0;
1223 entry->bm.data = 0;
1224 entry->bm.palette = nullptr;
1225 entry->num_mipmaps = mm_lvl;
1226 entry->mem_taken = (size_t)bm_size;
1227 entry->dir_type = CF_TYPE_ANY;
1228 entry->handle = handle;
1229
1230 entry->load_count++;
1231
1232 if (img_cfp != nullptr)
1233 cfclose(img_cfp);
1234
1235 return handle;
1236 }
1237
bm_load(const SCP_string & filename)1238 int bm_load(const SCP_string& filename) {
1239 return bm_load(filename.c_str());
1240 }
1241
bm_load_and_parse_eff(const char * filename,int dir_type,int * nframes,int * nfps,int * key,BM_TYPE * type)1242 bool bm_load_and_parse_eff(const char *filename, int dir_type, int *nframes, int *nfps, int *key, BM_TYPE *type) {
1243 int frames = 0, fps = 30, keyframe = 0;
1244 char ext[8];
1245 BM_TYPE c_type = BM_TYPE_NONE;
1246 char file_text[1024];
1247 char file_text_raw[1024];
1248
1249 memset(ext, 0, sizeof(ext));
1250 memset(file_text, 0, sizeof(file_text));
1251 memset(file_text_raw, 0, sizeof(file_text_raw));
1252
1253 // pause anything that may happen to be parsing right now
1254 pause_parse();
1255
1256 try
1257 {
1258 // now start parsing the EFF
1259 read_file_text(filename, dir_type, file_text, file_text_raw);
1260 reset_parse(file_text);
1261
1262 required_string("$Type:");
1263 stuff_string(ext, F_NAME, sizeof(ext));
1264
1265 required_string("$Frames:");
1266 stuff_int(&frames);
1267
1268 if (optional_string("$FPS:"))
1269 stuff_int(&fps);
1270
1271 if (optional_string("$Keyframe:"))
1272 stuff_int(&keyframe);
1273 }
1274 catch (const parse::ParseException& e)
1275 {
1276 mprintf(("BMPMAN: Unable to parse '%s'! Error message = %s.\n", filename, e.what()));
1277 unpause_parse();
1278 return false;
1279 }
1280
1281 // done with EFF so unpause parsing so whatever can continue
1282 unpause_parse();
1283
1284 if (!stricmp(NOX("dds"), ext)) {
1285 c_type = BM_TYPE_DDS;
1286 } else if (!stricmp(NOX("tga"), ext)) {
1287 c_type = BM_TYPE_TGA;
1288 } else if (!stricmp(NOX("png"), ext)) {
1289 c_type = BM_TYPE_PNG;
1290 } else if (!stricmp(NOX("jpg"), ext)) {
1291 c_type = BM_TYPE_JPG;
1292 } else if (!stricmp(NOX("pcx"), ext)) {
1293 c_type = BM_TYPE_PCX;
1294 } else {
1295 mprintf(("BMPMAN: Unknown file type in EFF parse!\n"));
1296 return false;
1297 }
1298
1299 // did we do anything?
1300 if (c_type == BM_TYPE_NONE || frames == 0) {
1301 mprintf(("BMPMAN: EFF parse ERROR!\n"));
1302 return false;
1303 }
1304
1305 if (type)
1306 *type = c_type;
1307
1308 if (nframes)
1309 *nframes = frames;
1310
1311 if (nfps)
1312 *nfps = fps;
1313
1314 if (key)
1315 *key = keyframe;
1316
1317 return true;
1318 }
1319
1320 /**
1321 * Lock an image files data into memory
1322 */
bm_load_image_data(int handle,int bpp,ushort flags,bool nodebug)1323 static int bm_load_image_data(int handle, int bpp, ushort flags, bool nodebug)
1324 {
1325 BM_TYPE c_type = BM_TYPE_NONE;
1326 int true_bpp;
1327
1328 auto bs = bm_get_slot(handle);
1329 auto be = &bs->entry;
1330 bitmap *bmp = &be->bm;
1331
1332 if (bmp->true_bpp > bpp)
1333 true_bpp = bmp->true_bpp;
1334 else
1335 true_bpp = bpp;
1336
1337 // don't do a bpp check here since it could be different in OGL - taylor
1338 if (bmp->data == 0) {
1339 Assert(be->ref_count == 1);
1340
1341 if (be->type != BM_TYPE_USER && !nodebug) {
1342 if (bmp->data == 0)
1343 nprintf(("BmpMan", "Loading %s for the first time.\n", be->filename));
1344 }
1345
1346 if (!Bm_paging) {
1347 if (be->type != BM_TYPE_USER && !nodebug)
1348 nprintf(("Paging", "Loading %s (%dx%dx%d)\n", be->filename, bmp->w, bmp->h, true_bpp));
1349 }
1350
1351 // select proper format
1352 if (flags & BMP_AABITMAP)
1353 BM_SELECT_ALPHA_TEX_FORMAT();
1354 else if (flags & BMP_TEX_ANY)
1355 BM_SELECT_TEX_FORMAT();
1356 else
1357 BM_SELECT_SCREEN_FORMAT();
1358
1359 // make sure we use the real graphic type for EFFs
1360 if (be->type == BM_TYPE_EFF) {
1361 c_type = be->info.ani.eff.type;
1362 }
1363 else {
1364 c_type = be->type;
1365 }
1366
1367 switch (c_type)
1368 {
1369 case BM_TYPE_PCX:
1370 bm_lock_pcx(handle, bs, bmp, true_bpp, flags);
1371 break;
1372
1373 case BM_TYPE_ANI:
1374 bm_lock_ani(handle, bs, bmp, true_bpp, flags);
1375 break;
1376
1377 case BM_TYPE_TGA:
1378 bm_lock_tga(handle, bs, bmp, true_bpp, flags);
1379 break;
1380
1381 case BM_TYPE_PNG:
1382 //libpng handles compression with zlib
1383 if (be->info.ani.apng.is_apng == true) {
1384 bm_lock_apng( handle, bs, bmp, true_bpp, flags );
1385 }
1386 else {
1387 bm_lock_png( handle, bs, bmp, true_bpp, flags );
1388 }
1389 break;
1390
1391 case BM_TYPE_JPG:
1392 bm_lock_jpg(handle, bs, bmp, true_bpp, flags);
1393 break;
1394
1395 case BM_TYPE_DDS:
1396 case BM_TYPE_DXT1:
1397 case BM_TYPE_DXT3:
1398 case BM_TYPE_DXT5:
1399 case BM_TYPE_BC7:
1400 case BM_TYPE_CUBEMAP_DDS:
1401 case BM_TYPE_CUBEMAP_DXT1:
1402 case BM_TYPE_CUBEMAP_DXT3:
1403 case BM_TYPE_CUBEMAP_DXT5:
1404 bm_lock_dds(handle, bs, bmp, true_bpp, flags);
1405 break;
1406
1407 case BM_TYPE_USER:
1408 bm_lock_user(handle, bs, bmp, true_bpp, flags);
1409 break;
1410
1411 default:
1412 Warning(LOCATION, "Unsupported type in bm_lock -- %d\n", c_type);
1413 return -1;
1414 }
1415
1416 // always go back to screen format
1417 BM_SELECT_SCREEN_FORMAT();
1418
1419 // make sure we actually did something
1420 if (!(bmp->data))
1421 return -1;
1422 }
1423
1424 return 0;
1425 }
1426
bm_load_animation(const char * real_filename,int * nframes,int * fps,int * keyframe,float * total_time,bool can_drop_frames,int dir_type)1427 int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *keyframe, float *total_time, bool can_drop_frames, int dir_type) {
1428 int i, n;
1429 anim the_anim;
1430 CFILE *img_cfp = nullptr;
1431 char filename[MAX_FILENAME_LEN];
1432 int reduced = 0;
1433 int anim_fps = 0, anim_frames = 0, key = 0;
1434 float anim_total_time = 0.0f;
1435 int anim_width = 0, anim_height = 0;
1436 BM_TYPE type = BM_TYPE_NONE, eff_type = BM_TYPE_NONE, c_type = BM_TYPE_NONE;
1437 int bpp = 0, mm_lvl = 0;
1438 size_t img_size = 0;
1439 char clean_name[MAX_FILENAME_LEN];
1440
1441 Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
1442
1443 // set output param defaults before going any further
1444 if (nframes != nullptr)
1445 *nframes = 0;
1446
1447 if (fps != nullptr)
1448 *fps = 0;
1449
1450 if (keyframe != nullptr)
1451 *keyframe = 0;
1452
1453 if (total_time != nullptr)
1454 *total_time = 0.0f;
1455
1456 memset(filename, 0, MAX_FILENAME_LEN);
1457 strncpy(filename, real_filename, MAX_FILENAME_LEN - 1);
1458 char *p = strchr(filename, '.');
1459 if (p) {
1460 mprintf(("Someone passed an extension to bm_load_animation for file '%s'\n", real_filename));
1461 *p = 0;
1462 }
1463
1464 // If we are standalone server keep replacing the 'cursorweb' (mouse cursor) as the filename
1465 // should keep the game happy while loading only single ani file which the needs to be present in any case
1466 if (Is_standalone) {
1467 char standalone_filename[MAX_FILENAME_LEN] = "cursorweb";
1468 strcpy_s(filename, standalone_filename);
1469 }
1470
1471 // safety catch for strcat...
1472 // MAX_FILENAME_LEN-5 == '.' plus 3 letter ext plus NULL terminator
1473 if (strlen(filename) > MAX_FILENAME_LEN - 5) {
1474 Warning(LOCATION, "Passed filename, '%s', is too long to support an extension!!\n\nMaximum length, minus the extension, is %i characters.\n", filename, MAX_FILENAME_LEN - 5);
1475 return -1;
1476 }
1477
1478 // used later if EFF type
1479 strcpy_s(clean_name, filename);
1480
1481 // Lets find out what type it is
1482 {
1483 int handle = -1;
1484
1485 // do a search for any previously loaded files (looks at filename only)
1486 if (bm_load_sub_fast(filename, &handle, dir_type, true)) {
1487 auto entry = bm_get_entry(handle);
1488
1489 if (nframes)
1490 *nframes = entry->info.ani.num_frames;
1491
1492 if (fps)
1493 *fps = entry->info.ani.fps;
1494
1495 if (keyframe)
1496 *keyframe = entry->info.ani.keyframe;
1497
1498 if (total_time != nullptr)
1499 *total_time = entry->info.ani.total_time;
1500
1501 return handle;
1502 }
1503
1504 // if we are still here then we need to fall back to a file-based search
1505 int rval = bm_load_sub_slow(filename, BM_ANI_NUM_TYPES, bm_ani_ext_list, &img_cfp, dir_type);
1506
1507 if (rval < 0)
1508 return -1;
1509
1510 strcat_s(filename, bm_ani_ext_list[rval]);
1511 type = bm_ani_type_list[rval];
1512 }
1513
1514 // If we found an animation then there is an extra 5 char size limit to adhere to. We don't do this check earlier since it's only needed if we found an anim
1515 // an ANI needs about 5 extra characters to have the "[###]" frame designator
1516 // EFF/APNG need 5 extra characters for each frame filename too, which just happens to be the same length as the frame designator needed otherwise
1517 // MAX_FILENAME_LEN-10 == 5 character frame designator plus '.' plus 3 letter ext plus NULL terminator
1518 // we only check for -5 here since the filename should already have the extension on it, and it must have passed the previous check
1519 if (strlen(filename) > MAX_FILENAME_LEN - 5) {
1520 Warning(LOCATION, "Passed filename, '%s', is too long to support an extension and frames!!\n\nMaximum length for an ANI/EFF/APNG, minus the extension, is %i characters.\n", filename, MAX_FILENAME_LEN - 10);
1521 if (img_cfp != nullptr)
1522 cfclose(img_cfp);
1523 return -1;
1524 }
1525
1526 // it's an effect file, any readable image type with eff being txt
1527 if (type == BM_TYPE_EFF) {
1528 if (!bm_load_and_parse_eff(filename, dir_type, &anim_frames, &anim_fps, &key, &eff_type)) {
1529 mprintf(("BMPMAN: Error reading EFF\n"));
1530 if (img_cfp != nullptr)
1531 cfclose(img_cfp);
1532 return -1;
1533 } else {
1534 mprintf(("BMPMAN: Found EFF (%s) with %d frames at %d fps.\n", filename, anim_frames, anim_fps));
1535 }
1536 if (anim_fps == 0) {
1537 Error(LOCATION, "animation (%s) has invalid fps of 0, fix this!", filename);
1538 }
1539 anim_total_time = anim_frames / i2fl(anim_fps);
1540 }
1541 // regular ani file
1542 else if (type == BM_TYPE_ANI) {
1543 #ifndef NDEBUG
1544 // for debug of ANI sizes
1545 strcpy_s(the_anim.name, real_filename);
1546 #endif
1547 anim_read_header(&the_anim, img_cfp);
1548
1549 if (the_anim.width < 0 || the_anim.height < 0) {
1550 Error(LOCATION, "Ani file %s has a faulty header and cannot be loaded.", real_filename);
1551 }
1552
1553 anim_frames = the_anim.total_frames;
1554 anim_fps = the_anim.fps;
1555 if (anim_fps == 0) {
1556 Error(LOCATION, "animation (%s) has invalid fps of 0, fix this!", filename);
1557 }
1558 anim_total_time = anim_frames / i2fl(anim_fps);
1559 anim_width = the_anim.width;
1560 anim_height = the_anim.height;
1561 bpp = 8;
1562 img_size = (anim_width * anim_height * bpp);
1563 //we only care if there are 2 keyframes - first frame, other frame to jump to for ship/weapons
1564 //mainhall door anis hav every frame as keyframe, so we don't care
1565 //other anis only have the first frame
1566 if (the_anim.num_keys == 2) {
1567 the_anim.keys = (key_frame*)vm_malloc(sizeof(key_frame) * the_anim.num_keys);
1568 Assert(the_anim.keys != NULL);
1569
1570 for (i = 0; i<the_anim.num_keys; i++) {
1571 the_anim.keys[i].frame_num = 0;
1572 cfread(&the_anim.keys[i].frame_num, 2, 1, img_cfp);
1573 cfread(&the_anim.keys[i].offset, 4, 1, img_cfp);
1574 the_anim.keys[i].frame_num = INTEL_INT(the_anim.keys[i].frame_num); //-V570
1575 the_anim.keys[i].offset = INTEL_INT(the_anim.keys[i].offset); //-V570
1576 }
1577 //some retail anis have their keyframes reversed
1578 key = MAX(the_anim.keys[0].frame_num, the_anim.keys[1].frame_num);
1579
1580 vm_free(the_anim.keys);
1581 the_anim.keys = nullptr;
1582 }
1583 }
1584 else if (type == BM_TYPE_PNG) {
1585 nprintf(("apng", "Loading apng: %s\n", filename));
1586 try {
1587 apng::apng_ani the_apng = apng::apng_ani(filename);
1588 anim_frames = the_apng.nframes;
1589 anim_total_time = the_apng.anim_time;
1590 anim_fps = fl2i(i2fl(anim_frames) / the_apng.anim_time); // note; apng bails on loading if anim_time is <= 0.0f
1591 anim_width = the_apng.w;
1592 anim_height = the_apng.h;
1593 bpp = the_apng.bpp;
1594 img_size = the_apng.imgsize();
1595 }
1596 catch (const apng::ApngException& e) {
1597 mprintf(("Failed to load apng: %s\n", e.what() ));
1598 if (img_cfp != nullptr)
1599 cfclose(img_cfp);
1600 return -1;
1601 }
1602 }
1603 else {
1604 Warning(LOCATION, "Unsupported image type: %i", type);
1605 if (img_cfp != nullptr)
1606 cfclose(img_cfp);
1607 return -1;
1608 }
1609
1610 if ((can_drop_frames == true) && (type == BM_TYPE_ANI)) {
1611 if (Bm_low_mem == 1) {
1612 reduced = 1;
1613 anim_frames = (anim_frames + 1) / 2;
1614 anim_fps = (anim_fps / 2);
1615 } else if (Bm_low_mem == 2) {
1616 anim_frames = 1;
1617 }
1618 }
1619
1620
1621 n = find_block_of(anim_frames);
1622
1623 if (n < 0) {
1624 if (img_cfp != nullptr)
1625 cfclose(img_cfp);
1626
1627 return -1;
1628 }
1629
1630 auto first_entry = bm_get_entry(n);
1631 // if all images of the animation have the same size then we can use a texture array
1632 bool is_array = true;
1633
1634 for (i = 0; i < anim_frames; i++) {
1635 auto entry = bm_get_entry(n + i);
1636 memset(entry, 0, sizeof(bitmap_entry));
1637
1638 if (type == BM_TYPE_EFF) {
1639 entry->info.ani.eff.type = eff_type;
1640 sprintf_safe(entry->info.ani.eff.filename, "%s_%.4d", clean_name, i);
1641
1642 // bm_load_info() returns non-0 on failure
1643 if (bm_load_info(eff_type, entry->info.ani.eff.filename,
1644 nullptr, &anim_width, &anim_height, &bpp, &c_type, &mm_lvl, &img_size)) {
1645 // if we didn't get anything then bail out now
1646 if (i == 0) {
1647 Warning(LOCATION, "EFF: No frame images were found. EFF, %s, is invalid.\n", filename);
1648
1649 if (img_cfp != nullptr)
1650 cfclose(img_cfp);
1651
1652 return -1;
1653 }
1654
1655 Warning(LOCATION, "EFF: Unable to load all frames for '%s', stopping at #%d\n", filename, i);
1656
1657 // reset total frames to current
1658 anim_frames = i;
1659
1660 // update all previous frames with the new count
1661 for (int j = 0; j<anim_frames; j++)
1662 bm_get_entry(n + j)->info.ani.num_frames = anim_frames;
1663
1664 break;
1665 }
1666
1667 if ((img_size <= 0) && (anim_width) && (anim_height) && (bpp)) {
1668 img_size = (anim_width * anim_height * (bpp >> 3));
1669 }
1670 }
1671
1672 entry->info.ani.first_frame = n;
1673 entry->info.ani.num_frames = anim_frames;
1674 entry->info.ani.fps = (ubyte)anim_fps;
1675 entry->info.ani.keyframe = key;
1676 entry->info.ani.total_time = anim_total_time;
1677 entry->bm.w = (short)anim_width;
1678 entry->bm.rowsize = (short)anim_width;
1679 entry->bm.h = (short)anim_height;
1680 if (reduced) {
1681 entry->bm.w /= 2;
1682 entry->bm.rowsize /= 2;
1683 entry->bm.h /= 2;
1684 }
1685 entry->bm.flags = 0;
1686 entry->bm.bpp = 0;
1687 entry->bm.true_bpp = (ubyte)bpp;
1688 entry->bm.data = 0;
1689 entry->bm.palette = nullptr;
1690 entry->type = type;
1691 entry->comp_type = c_type;
1692 entry->signature = Bm_next_signature++;
1693 entry->handle = n + i;
1694 entry->num_mipmaps = mm_lvl;
1695 entry->mem_taken = (size_t)img_size;
1696 entry->dir_type = dir_type;
1697
1698 entry->load_count++;
1699
1700 if (i == 0) {
1701 sprintf_safe(entry->filename, "%s", filename);
1702 } else {
1703 if (type == BM_TYPE_PNG) {
1704 sprintf_safe(entry->filename, "%s_%04d", filename, i);
1705 }
1706 else {
1707 sprintf_safe(entry->filename, "%s[%d]", filename, i);
1708 }
1709 }
1710
1711 entry->info.ani.apng.frame_delay = 0.0f;
1712 if (type == BM_TYPE_PNG) {
1713 entry->info.ani.apng.is_apng = true;
1714 }
1715 else {
1716 entry->info.ani.apng.is_apng = false;
1717 }
1718
1719 if (first_entry->bm.w != entry->bm.w || first_entry->bm.h != entry->bm.h) {
1720 // We found a frame with a different size than the first frame -> this can't be used as a texture array
1721 is_array = false;
1722
1723 Warning(LOCATION, "Animation '%s' has images that are of different sizes (currently at frame %d)."
1724 "Performance could be improved by making all images the same size.", filename, i + 1);
1725 }
1726 if (first_entry->comp_type != entry->comp_type) {
1727 // Different compression type
1728 is_array = false;
1729
1730 Warning(LOCATION, "Animation '%s' has images that are of different compression formats (currently at frame %d)."
1731 "Performance could be improved by making all images the same compression format.", filename, i + 1);
1732 }
1733 if (first_entry->bm.true_bpp != entry->bm.true_bpp) {
1734 // We found a frame with an incompatible pixel format
1735 is_array = false;
1736
1737 Warning(LOCATION, "Animation '%s' has images that are of different pixel formats (currently at frame %d)."
1738 "Performance could be improved by making all images the same pixel format.", filename, i + 1);
1739 }
1740 if (first_entry->num_mipmaps != entry->num_mipmaps) {
1741 // We found a frame with a different number of mipmaps
1742 is_array = false;
1743
1744 Warning(LOCATION, "Animation '%s' has images that have a different number of mipmaps (currently at frame %d)."
1745 "Performance could be improved by giving all frames the same number of mipmaps.", filename, i + 1);
1746 }
1747 }
1748
1749 // Set array flag of first frame
1750 first_entry->info.ani.is_array = is_array;
1751
1752 if (nframes != nullptr)
1753 *nframes = anim_frames;
1754
1755 if (fps != nullptr)
1756 *fps = anim_fps;
1757
1758 if (img_cfp != nullptr)
1759 cfclose(img_cfp);
1760
1761 if (keyframe != nullptr)
1762 *keyframe = key;
1763
1764 if (total_time != nullptr)
1765 *total_time = anim_total_time;
1766
1767 return first_entry->handle;
1768 }
1769
bm_load_duplicate(const char * filename)1770 int bm_load_duplicate(const char *filename) {
1771 int ret;
1772
1773 // ignore duplicates
1774 Bm_ignore_duplicates = 1;
1775
1776 // load
1777 ret = bm_load(filename);
1778
1779 // back to normal
1780 Bm_ignore_duplicates = 0;
1781
1782 return ret;
1783 }
1784
bm_load_either(const char * filename,int * nframes,int * fps,int * keyframe,bool can_drop_frames,int dir_type)1785 int bm_load_either(const char *filename, int *nframes, int *fps, int *keyframe, bool can_drop_frames, int dir_type) {
1786 if (nframes != NULL)
1787 *nframes = 0;
1788 if (fps != NULL)
1789 *fps = 0;
1790 int tidx = bm_load_animation(filename, nframes, fps, keyframe, nullptr, can_drop_frames, dir_type);
1791 if (tidx == -1) {
1792 tidx = bm_load(filename);
1793 if (tidx != -1 && nframes != NULL)
1794 *nframes = 1;
1795 }
1796
1797 return tidx;
1798 }
1799
bm_load_sub_fast(const char * real_filename,int * handle,int dir_type,bool animated_type)1800 int bm_load_sub_fast(const char *real_filename, int *handle, int dir_type, bool animated_type) {
1801 if (Bm_ignore_duplicates)
1802 return 0;
1803
1804 for (auto& block : bm_blocks) {
1805 for (auto& slot : block) {
1806 auto& entry = slot.entry;
1807 if (entry.type == BM_TYPE_NONE)
1808 continue;
1809
1810 if (entry.dir_type != dir_type)
1811 continue;
1812
1813 bool animated = bm_is_anim(&entry);
1814
1815 if (animated_type && !animated)
1816 continue;
1817 else if (!animated_type && animated)
1818 continue;
1819
1820 if (!strextcmp(real_filename, entry.filename)) {
1821 entry.load_count++;
1822 *handle = entry.handle;
1823 return 1;
1824 }
1825 }
1826 }
1827
1828 // not found to be loaded already
1829 return 0;
1830 }
1831
bm_load_sub_slow(const char * real_filename,const int num_ext,const char ** ext_list,CFILE ** img_cfp,int dir_type)1832 int bm_load_sub_slow(const char *real_filename, const int num_ext, const char **ext_list, CFILE **img_cfp, int dir_type) {
1833 auto res = cf_find_file_location_ext(real_filename, num_ext, ext_list, dir_type, false);
1834
1835 // could not be found, or is invalid for some reason
1836 if (!res.found)
1837 return -1;
1838
1839 CFILE *test = cfopen_special(res, "rb", dir_type);
1840
1841 if (test != NULL) {
1842 if (img_cfp != NULL)
1843 *img_cfp = test;
1844
1845 return res.extension_index;
1846 }
1847
1848 // umm, that's not good...
1849 Warning(LOCATION, "Could not open file %s!", real_filename);
1850 return -1;
1851 }
1852
bm_lock(int handle,int bpp,ushort flags,bool nodebug)1853 bitmap * bm_lock(int handle, int bpp, ushort flags, bool nodebug) {
1854 bitmap *bmp;
1855
1856 Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
1857
1858 auto be = bm_get_entry(handle);
1859
1860 // to fix a couple of OGL bpp passes, force 8bit on AABITMAP - taylor
1861 if (flags & BMP_AABITMAP)
1862 bpp = 8;
1863
1864 // if we're on a standalone server, aways for it to lock to 8 bits
1865 if (Is_standalone) {
1866 bpp = 8;
1867 flags = 0;
1868 }
1869 // otherwise do it as normal
1870 else {
1871 if (flags & BMP_AABITMAP) {
1872 Assert(bpp == 8);
1873 } else if ((flags & BMP_TEX_NONCOMP) && (!(flags & BMP_TEX_COMP))) {
1874 Assert(bpp >= 16); // cheating but bpp passed isn't what we normally end up with
1875 } else if ((flags & BMP_TEX_DXT1) || (flags & BMP_TEX_DXT3) || (flags & BMP_TEX_DXT5) || (flags & BMP_TEX_BC7)) {
1876 Assert(bpp >= 16); // cheating but bpp passed isn't what we normally end up with
1877 } else if (flags & BMP_TEX_CUBEMAP) {
1878 Assert((be->type == BM_TYPE_CUBEMAP_DDS) ||
1879 (be->type == BM_TYPE_CUBEMAP_DXT1) ||
1880 (be->type == BM_TYPE_CUBEMAP_DXT3) ||
1881 (be->type == BM_TYPE_CUBEMAP_DXT5));
1882 Assert(bpp >= 16);
1883 } else {
1884 Assert(0); //?
1885 }
1886 }
1887
1888 bmp = &be->bm;
1889
1890 // If you hit this assert, chances are that someone freed the
1891 // wrong bitmap and now someone is trying to use that bitmap.
1892 // See John.
1893 Assert(be->type != BM_TYPE_NONE);
1894
1895 // Increment ref count for bitmap since lock was made on it.
1896 Assert(be->ref_count >= 0);
1897 be->ref_count++; // Lock it before we page in data; this prevents a callback from freeing this
1898 // as it gets read in
1899
1900 // Mark this bitmap as used this frame
1901 #ifdef BMPMAN_NDEBUG
1902 if (be->used_this_frame < 255) {
1903 be->used_this_frame++;
1904 }
1905 #endif
1906
1907 // read the file data
1908 if (bm_load_image_data(handle, bpp, flags, nodebug) == -1) {
1909 // oops, this isn't good - reset and return NULL
1910 bm_unlock( handle );
1911 bm_unload( handle );
1912
1913 return NULL;
1914 }
1915
1916 if (!gr_bm_data(handle, bmp)) {
1917 // graphics subsystem failed, reset and return NULL
1918 bm_unlock( handle );
1919 bm_unload( handle );
1920
1921 return NULL;
1922 }
1923
1924 MONITOR_INC(NumBitmapPage, 1);
1925 MONITOR_INC(SizeBitmapPage, bmp->w*bmp->h);
1926
1927 if (bm_is_anim(be) == true) {
1928 int i;
1929 auto first = be->info.ani.first_frame;
1930
1931 auto frames = bm_get_entry(first)->info.ani.num_frames;
1932 for (i = 0; i< frames; i++) {
1933 // Mark all the bitmaps in this bitmap or animation as recently used
1934 auto frame_entry = bm_get_entry(first + i);
1935
1936 #ifdef BMPMAN_NDEBUG
1937 // Mark all the bitmaps in this bitmap or animation as used for the usage tracker.
1938 frame_entry->used_count++;
1939 #endif
1940
1941 frame_entry->used_flags = flags;
1942 }
1943 } else {
1944 #ifdef BMPMAN_NDEBUG
1945 // Mark all the bitmaps in this bitmap or animation as used for the usage tracker.
1946 be->used_count++;
1947 #endif
1948 be->used_flags = flags;
1949 }
1950
1951 return bmp;
1952 }
1953
bm_lock_ani(int,bitmap_slot * bs,bitmap *,int bpp,ushort flags)1954 void bm_lock_ani(int /*handle*/, bitmap_slot *bs, bitmap* /*bmp*/, int bpp, ushort flags) {
1955 anim *the_anim;
1956 anim_instance *the_anim_instance;
1957 bitmap *bm;
1958 ubyte *frame_data;
1959 int size, i;
1960 int first_frame, nframes;
1961
1962 auto be = &bs->entry;
1963 first_frame = be->info.ani.first_frame;
1964
1965 auto first_entry = bm_get_entry(first_frame);
1966
1967 nframes = first_entry->info.ani.num_frames;
1968
1969 if ((the_anim = anim_load(first_entry->filename, first_entry->dir_type)) == nullptr) {
1970 nprintf(("BMPMAN", "Error opening %s in bm_lock\n", be->filename));
1971 return;
1972 }
1973
1974 if ((the_anim_instance = init_anim_instance(the_anim, bpp)) == nullptr) {
1975 nprintf(("BMPMAN", "Error opening %s in bm_lock\n", be->filename));
1976 anim_free(the_anim);
1977 return;
1978 }
1979
1980 int can_drop_frames = 0;
1981
1982 if (the_anim->total_frames != first_entry->info.ani.num_frames) {
1983 can_drop_frames = 1;
1984 }
1985
1986 bm = &first_entry->bm;
1987 size = bm->w * bm->h * (bpp >> 3);
1988 be->mem_taken = (size_t)size;
1989
1990 Assert(size > 0);
1991
1992 for (i = 0; i<nframes; i++) {
1993 auto slot = bm_get_slot(first_frame + i);
1994 be = &slot->entry;
1995 bm = &be->bm;
1996
1997 // Unload any existing data
1998 bm_free_data(slot);
1999
2000 bm->flags = 0;
2001
2002 // briefing editor in Fred2 uses aabitmaps (ani's) - force to 8 bit
2003 bm->bpp = Is_standalone ? (ubyte)8 : bpp;
2004
2005 bm->data = (ptr_u)bm_malloc(first_frame + i, size);
2006
2007 frame_data = anim_get_next_raw_buffer(the_anim_instance, 0, flags & BMP_AABITMAP ? 1 : 0, bm->bpp);
2008
2009 ubyte *dptr, *sptr;
2010
2011 sptr = frame_data;
2012 dptr = (ubyte *)bm->data;
2013
2014 if ((bm->w != the_anim->width) || (bm->h != the_anim->height)) {
2015 // Scale it down
2016 // 8 bit
2017 if (bpp == 8) {
2018 int w, h;
2019 fix u, utmp, v, du, dv;
2020
2021 u = v = 0;
2022
2023 du = (the_anim->width*F1_0) / bm->w;
2024 dv = (the_anim->height*F1_0) / bm->h;
2025
2026 for (h = 0; h < bm->h; h++) {
2027 ubyte *drow = &dptr[bm->w * h];
2028 ubyte *srow = &sptr[f2i(v)*the_anim->width];
2029
2030 utmp = u;
2031
2032 for (w = 0; w < bm->w; w++) {
2033 *drow++ = srow[f2i(utmp)];
2034 utmp += du;
2035 }
2036 v += dv;
2037 }
2038 }
2039 // 16 bpp
2040 else {
2041 int w, h;
2042 fix u, utmp, v, du, dv;
2043
2044 u = v = 0;
2045
2046 du = (the_anim->width*F1_0) / bm->w;
2047 dv = (the_anim->height*F1_0) / bm->h;
2048
2049 for (h = 0; h < bm->h; h++) {
2050 unsigned short *drow = &((unsigned short*)dptr)[bm->w * h];
2051 unsigned short *srow = &((unsigned short*)sptr)[f2i(v)*the_anim->width];
2052
2053 utmp = u;
2054
2055 for (w = 0; w < bm->w; w++) {
2056 *drow++ = srow[f2i(utmp)];
2057 utmp += du;
2058 }
2059 v += dv;
2060 }
2061 }
2062 } else {
2063 // 1-to-1 mapping
2064 memcpy(dptr, sptr, size);
2065 }
2066
2067 bm_convert_format(bm, flags);
2068
2069 // Skip a frame
2070 if ((i < nframes - 1) && can_drop_frames) {
2071 frame_data = anim_get_next_raw_buffer(the_anim_instance, 0, flags & BMP_AABITMAP ? 1 : 0, bm->bpp);
2072 }
2073 }
2074
2075 free_anim_instance(the_anim_instance);
2076 anim_free(the_anim);
2077 }
2078
2079
bm_lock_apng(int,bitmap_slot * bs,bitmap * bmp,int bpp,ushort)2080 void bm_lock_apng(int /*handle*/, bitmap_slot *bs, bitmap *bmp, int bpp, ushort /*flags*/) {
2081 auto be = &bs->entry;
2082 int first_frame = be->info.ani.first_frame;
2083 auto first_entry = bm_get_entry(first_frame);
2084 int nframes = first_entry->info.ani.num_frames;
2085
2086 std::unique_ptr<apng::apng_ani> the_apng;
2087 try {
2088 the_apng.reset(new apng::apng_ani(first_entry->filename));
2089 }
2090 catch (const apng::ApngException& e) {
2091 Warning(LOCATION, "Failed to load apng: %s", e.what());
2092 return;
2093 }
2094
2095 float cumulative_frame_delay = 0.0f;
2096 for (int i = 0; i<nframes; i++) {
2097 auto slot = bm_get_slot(first_frame + i);
2098 be = &slot->entry;
2099 bitmap* bm = &be->bm;
2100
2101 // Unload any existing data
2102 bm_free_data(slot);
2103
2104 ubyte* data = static_cast<ubyte*>(bm_malloc(first_frame + i, be->mem_taken));
2105 try {
2106 // this is a reasonably expensive operation
2107 // especially when run on all frames at once
2108 // i.e. try to preload the apng before using it
2109 the_apng->next_frame();
2110 }
2111 catch (const apng::ApngException& e) {
2112 Warning(LOCATION, "Failed to get next apng frame: %s", e.what());
2113 bm_release(first_frame);
2114 return;
2115 }
2116 memcpy(data, the_apng->frame.data.data(), be->mem_taken);
2117
2118 bm->data = reinterpret_cast<ptr_u>(data);
2119 bm->palette = nullptr;
2120 bm->bpp = bpp;
2121 bm->flags = 0;
2122 cumulative_frame_delay += the_apng->frame.delay;
2123 be->info.ani.apng.frame_delay = cumulative_frame_delay;
2124
2125 nprintf(("apng", "locking apng frame: %s (%i|%i|%i) (%f) " SIZE_T_ARG "\n", be->filename, bpp, bmp->bpp, bm->true_bpp, be->info.ani.apng.frame_delay, be->mem_taken));
2126 }
2127 }
2128
2129
bm_lock_dds(int handle,bitmap_slot * bs,bitmap * bmp,int,ushort)2130 void bm_lock_dds(int handle, bitmap_slot *bs, bitmap *bmp, int /*bpp*/, ushort /*flags*/) {
2131 ubyte *data = NULL;
2132 int error;
2133 ubyte dds_bpp = 0;
2134 char filename[MAX_FILENAME_LEN];
2135
2136 auto be = &bs->entry;
2137
2138 // free any existing data
2139 bm_free_data(bs);
2140
2141 Assert(be->mem_taken > 0);
2142 Assert(&be->bm == bmp);
2143
2144 data = (ubyte*)bm_malloc(handle, be->mem_taken);
2145
2146 if (data == NULL)
2147 return;
2148
2149 memset(data, 0, be->mem_taken);
2150
2151 // make sure we are using the correct filename in the case of an EFF.
2152 // this will populate filename[] whether it's EFF or not
2153 EFF_FILENAME_CHECK;
2154
2155 error = dds_read_bitmap(filename, data, &dds_bpp, be->dir_type);
2156
2157 #if BYTE_ORDER == BIG_ENDIAN
2158 // same as with TGA, we need to byte swap 16 & 32-bit, uncompressed, DDS images
2159 if ((be->comp_type == BM_TYPE_DDS) || (be->comp_type == BM_TYPE_CUBEMAP_DDS)) {
2160 size_t i = 0;
2161
2162 if (dds_bpp == 32) {
2163 unsigned int *swap_tmp;
2164
2165 for (i = 0; i < be->mem_taken; i += 4) {
2166 swap_tmp = (unsigned int *)(data + i);
2167 *swap_tmp = INTEL_INT(*swap_tmp);
2168 }
2169 } else if (dds_bpp == 16) {
2170 unsigned short *swap_tmp;
2171
2172 for (i = 0; i < be->mem_taken; i += 2) {
2173 swap_tmp = (unsigned short *)(data + i);
2174 *swap_tmp = INTEL_SHORT(*swap_tmp);
2175 }
2176 }
2177 }
2178 #endif
2179
2180 bmp->bpp = dds_bpp;
2181 bmp->data = (ptr_u)data;
2182 bmp->flags = 0;
2183
2184 if (error != DDS_ERROR_NONE) {
2185 bm_free_data(bs);
2186 return;
2187 }
2188
2189 #ifdef BMPMAN_NDEBUG
2190 Assert(be->data_size > 0);
2191 #endif
2192 }
2193
bm_lock_jpg(int handle,bitmap_slot * bs,bitmap * bmp,int bpp,ushort)2194 void bm_lock_jpg(int handle, bitmap_slot *bs, bitmap *bmp, int bpp, ushort /*flags*/) {
2195 ubyte *data = NULL;
2196 int d_size = 0;
2197 int jpg_error = JPEG_ERROR_INVALID;
2198 char filename[MAX_FILENAME_LEN];
2199
2200 auto be = &bs->entry;
2201
2202 // Unload any existing data
2203 bm_free_data(bs);
2204
2205 // JPEG actually only support 24 bits per pixel so we enforce that here
2206 bpp = 24;
2207
2208 d_size = (bpp >> 3);
2209
2210 // allocate bitmap data
2211 Assert(be->mem_taken > 0);
2212 data = (ubyte*)bm_malloc(handle, be->mem_taken);
2213
2214 if (data == NULL)
2215 return;
2216
2217 memset(data, 0, be->mem_taken);
2218
2219 bmp->bpp = bpp;
2220 bmp->data = (ptr_u)data;
2221 bmp->palette = NULL;
2222
2223 Assert(&be->bm == bmp);
2224
2225 // make sure we are using the correct filename in the case of an EFF.
2226 // this will populate filename[] whether it's EFF or not
2227 EFF_FILENAME_CHECK;
2228
2229 jpg_error = jpeg_read_bitmap(filename, data, NULL, d_size, be->dir_type);
2230
2231 if (jpg_error != JPEG_ERROR_NONE) {
2232 bm_free_data(bs);
2233 return;
2234 }
2235
2236 #ifdef BMPMAN_NDEBUG
2237 Assert(be->data_size > 0);
2238 #endif
2239 }
2240
bm_lock_pcx(int handle,bitmap_slot * bs,bitmap * bmp,int bpp,ushort flags)2241 void bm_lock_pcx(int handle, bitmap_slot *bs, bitmap *bmp, int bpp, ushort flags) {
2242 ubyte *data;
2243 int pcx_error;
2244 char filename[MAX_FILENAME_LEN];
2245
2246 auto be = &bs->entry;
2247
2248 // Unload any existing data
2249 bm_free_data(bs);
2250
2251 be->mem_taken = (bmp->w * bmp->h * (bpp >> 3));
2252 data = (ubyte *)bm_malloc(handle, be->mem_taken);
2253 bmp->bpp = bpp;
2254 bmp->data = (ptr_u)data;
2255 bmp->palette = NULL;
2256 memset(data, 0, be->mem_taken);
2257
2258 Assert(&be->bm == bmp);
2259 #ifdef BMPMAN_NDEBUG
2260 Assert(be->data_size > 0);
2261 #endif
2262
2263 // some sanity checks on flags
2264 Assert(!((flags & BMP_AABITMAP) && (flags & BMP_TEX_ANY))); // no aabitmap textures
2265
2266 // make sure we are using the correct filename in the case of an EFF.
2267 // this will populate filename[] whether it's EFF or not
2268 EFF_FILENAME_CHECK;
2269
2270 pcx_error = pcx_read_bitmap(filename, data, NULL, (bpp >> 3), (flags & BMP_AABITMAP), (flags & BMP_MASK_BITMAP) != 0, be->dir_type);
2271
2272 if (pcx_error != PCX_ERROR_NONE) {
2273 mprintf(("Couldn't load PCX!!! (%s)\n", filename));
2274 return;
2275 }
2276
2277 #ifdef BMPMAN_NDEBUG
2278 Assert(be->data_size > 0);
2279 #endif
2280
2281 bmp->flags = 0;
2282
2283 bm_convert_format(bmp, flags);
2284 }
2285
bm_lock_png(int handle,bitmap_slot * bs,bitmap * bmp,int,ushort)2286 void bm_lock_png(int handle, bitmap_slot *bs, bitmap *bmp, int /*bpp*/, ushort /*flags*/) {
2287 ubyte *data = NULL;
2288 //assume 32 bit - libpng should expand everything
2289 int d_size;
2290 int png_error = PNG_ERROR_INVALID;
2291 char filename[MAX_FILENAME_LEN];
2292
2293 auto be = &bs->entry;
2294
2295 // Unload any existing data
2296 bm_free_data(bs);
2297
2298 // allocate bitmap data
2299 Assert(bmp->w * bmp->h > 0);
2300
2301 //if it's not 32-bit, we expand when we read it
2302 bmp->bpp = 32;
2303 d_size = bmp->bpp >> 3;
2304 //we waste memory if it turns out to be 24-bit, but the way this whole thing works is dodgy anyway
2305 data = (ubyte*)bm_malloc(handle, bmp->w * bmp->h * d_size);
2306 if (data == NULL)
2307 return;
2308 memset(data, 0, bmp->w * bmp->h * d_size);
2309 bmp->data = (ptr_u)data;
2310 bmp->palette = NULL;
2311
2312 Assert(&be->bm == bmp);
2313
2314 // make sure we are using the correct filename in the case of an EFF.
2315 // this will populate filename[] whether it's EFF or not
2316 EFF_FILENAME_CHECK;
2317
2318 //bmp->bpp gets set correctly in here after reading into memory
2319 png_error = png_read_bitmap(filename, data, &bmp->bpp, d_size, be->dir_type);
2320
2321 if (png_error != PNG_ERROR_NONE) {
2322 bm_free_data(bs);
2323 return;
2324 }
2325
2326 #ifdef BMPMAN_NDEBUG
2327 Assert(be->data_size > 0);
2328 #endif
2329 }
2330
bm_lock_tga(int handle,bitmap_slot * bs,bitmap * bmp,int bpp,ushort flags)2331 void bm_lock_tga(int handle, bitmap_slot *bs, bitmap *bmp, int bpp, ushort flags) {
2332 ubyte *data = NULL;
2333 int byte_size;
2334 char filename[MAX_FILENAME_LEN];
2335
2336 auto be = &bs->entry;
2337
2338 // Unload any existing data
2339 bm_free_data(bs);
2340
2341 bpp = be->bm.true_bpp;
2342
2343 if (Is_standalone) {
2344 Assert(bpp == 8);
2345 } else {
2346 Assert((bpp == 16) || (bpp == 24) || (bpp == 32));
2347 }
2348
2349 // allocate bitmap data
2350 byte_size = (bpp >> 3);
2351
2352 Assert(byte_size);
2353 Assert(be->mem_taken > 0);
2354
2355 data = (ubyte*)bm_malloc(handle, static_cast<size_t>(bmp->w * bmp->h * byte_size));
2356
2357 if (data) {
2358 memset(data, 0, be->mem_taken);
2359 } else {
2360 return;
2361 }
2362
2363 bmp->bpp = bpp;
2364 bmp->data = (ptr_u)data;
2365 bmp->palette = NULL;
2366
2367 Assert(&be->bm == bmp);
2368 #ifdef BMPMAN_NDEBUG
2369 Assert(be->data_size > 0);
2370 #endif
2371
2372 int tga_error;
2373
2374 // make sure we are using the correct filename in the case of an EFF.
2375 // this will populate filename[] whether it's EFF or not
2376 EFF_FILENAME_CHECK;
2377
2378 tga_error = targa_read_bitmap(filename, data, nullptr, byte_size, be->dir_type);
2379
2380 if (tga_error != TARGA_ERROR_NONE) {
2381 bm_free_data(bs);
2382 return;
2383 }
2384
2385 bmp->flags = 0;
2386
2387 bm_convert_format(bmp, flags);
2388 }
2389
bm_lock_user(int,bitmap_slot * bs,bitmap * bmp,int bpp,ushort flags)2390 void bm_lock_user(int /*handle*/, bitmap_slot *bs, bitmap *bmp, int bpp, ushort flags) {
2391 auto be = &bs->entry;
2392
2393 // Unload any existing data
2394 bm_free_data(bs);
2395
2396 if ((bpp != be->info.user.bpp) && !(flags & BMP_AABITMAP))
2397 bpp = be->info.user.bpp;
2398
2399 switch (bpp) {
2400 case 32: // user 32-bit bitmap
2401 bmp->bpp = bpp;
2402 bmp->flags = be->info.user.flags;
2403 bmp->data = (ptr_u)be->info.user.data;
2404 break;
2405
2406 case 24: // user 24-bit bitmap
2407 bmp->bpp = bpp;
2408 bmp->flags = be->info.user.flags;
2409 bmp->data = (ptr_u)be->info.user.data;
2410 break;
2411
2412 case 16: // user 16 bit bitmap
2413 bmp->bpp = bpp;
2414 bmp->flags = be->info.user.flags;
2415 bmp->data = (ptr_u)be->info.user.data;
2416 break;
2417
2418 case 8: // Going from 8 bpp to something (probably only for aabitmaps)
2419 Assert(flags & BMP_AABITMAP);
2420 bmp->bpp = bpp;
2421 bmp->flags = be->info.user.flags;
2422 bmp->data = (ptr_u)be->info.user.data;
2423 break;
2424
2425 default:
2426 Error(LOCATION, "Unhandled user bitmap conversion from %d to %d bpp", be->info.user.bpp, bmp->bpp);
2427 break;
2428 }
2429
2430 bm_convert_format(bmp, flags);
2431 }
2432
bm_make_render_target(int width,int height,int flags)2433 int bm_make_render_target(int width, int height, int flags) {
2434 int mm_lvl = 0;
2435 // final w and h may be different from passed width and height
2436 int w = width, h = height;
2437 int bpp = 32;
2438 int size = 0;
2439
2440 Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
2441
2442 // Find an open slot (starting from the end)
2443 int n = find_block_of(1);
2444
2445 // Out of bitmap slots
2446 if (n == -1)
2447 return -1;
2448
2449 if (!gr_bm_make_render_target(n, &w, &h, &bpp, &mm_lvl, flags))
2450 return -1;
2451
2452 Assert(mm_lvl > 0);
2453
2454 if (flags & BMP_FLAG_RENDER_TARGET_STATIC) {
2455 // data size
2456 size = (w * h * (bpp >> 3));
2457
2458 if (mm_lvl > 1) {
2459 // include size of all mipmap levels (there should be a full chain)
2460 size += (size / 3) - 1;
2461 }
2462
2463 // make sure to count all faces if a cubemap
2464 if (flags & BMP_FLAG_CUBEMAP) {
2465 size *= 6;
2466 }
2467 }
2468
2469 auto entry = bm_get_entry(n);
2470 // ensure fields are cleared out from previous bitmap
2471 memset(entry, 0, sizeof(bitmap_entry));
2472
2473 entry->type = (flags & BMP_FLAG_RENDER_TARGET_STATIC) ? BM_TYPE_RENDER_TARGET_STATIC : BM_TYPE_RENDER_TARGET_DYNAMIC;
2474 entry->signature = Bm_next_signature++;
2475 sprintf_safe(entry->filename, "RT_%dx%d+%d", w, h, bpp);
2476 entry->bm.w = (short)w;
2477 entry->bm.h = (short)h;
2478 entry->bm.rowsize = (short)w;
2479 entry->bm.bpp = bpp;
2480 entry->bm.true_bpp = bpp;
2481 entry->bm.flags = (ubyte)flags;
2482 entry->bm.data = 0;
2483 entry->bm.palette = nullptr;
2484 entry->num_mipmaps = mm_lvl;
2485 entry->mem_taken = (size_t)size;
2486 entry->dir_type = CF_TYPE_ANY;
2487
2488 entry->handle = n;
2489
2490 if (entry->mem_taken) {
2491 entry->bm.data = (ptr_u)bm_malloc(n, entry->mem_taken);
2492 }
2493
2494 return entry->handle;
2495 }
2496
bm_malloc(int n,size_t size)2497 void *bm_malloc(int n, size_t size) {
2498 if (size == 0)
2499 return nullptr;
2500
2501 #ifdef BMPMAN_NDEBUG
2502 auto entry = bm_get_entry(n);
2503 Assert(entry->data_size == 0);
2504 entry->data_size += size;
2505 bm_texture_ram += size;
2506 #endif
2507
2508 return vm_malloc(size);
2509 }
2510
bm_page_in_aabitmap(int handle,int nframes)2511 void bm_page_in_aabitmap(int handle, int nframes) {
2512 int i;
2513
2514 if (handle == -1)
2515 return;
2516
2517 Assert(bm_get_entry(handle)->handle == handle);
2518
2519 for (i = 0; i<nframes; i++) {
2520 auto frame_entry = bm_get_entry(handle + i);
2521
2522 frame_entry->preloaded = 2;
2523 frame_entry->preload_count++;
2524 frame_entry->used_flags = BMP_AABITMAP;
2525 }
2526 }
2527
bm_page_in_start()2528 void bm_page_in_start() {
2529 Bm_paging = 1;
2530
2531 // Mark all as inited
2532 for (auto& block : bm_blocks) {
2533 for (auto& slot : block) {
2534 auto& entry = slot.entry;
2535
2536 entry.preloaded = 0;
2537 entry.preload_count = 0;
2538 #ifdef BMPMAN_NDEBUG
2539 entry.used_count = 0;
2540 #endif
2541 entry.used_flags = 0;
2542 }
2543 }
2544
2545 gr_bm_page_in_start();
2546 }
2547
bm_page_in_stop()2548 void bm_page_in_stop() {
2549 TRACE_SCOPE(tracing::PageInStop);
2550
2551 #ifndef NDEBUG
2552 char busy_text[60];
2553 #endif
2554
2555 nprintf(("BmpInfo", "BMPMAN: Loading all used bitmaps.\n"));
2556
2557 // Load all the ones that are supposed to be loaded for this level.
2558 int n = 0;
2559
2560 int bm_preloading = 1;
2561
2562 for (auto& block : bm_blocks) {
2563 for (auto& slot : block) {
2564 auto& entry = slot.entry;
2565
2566 if ((entry.type != BM_TYPE_NONE) && (entry.type != BM_TYPE_RENDER_TARGET_DYNAMIC)
2567 && (entry.type != BM_TYPE_RENDER_TARGET_STATIC)) {
2568 if (entry.preloaded) {
2569 TRACE_SCOPE(tracing::PageInSingleBitmap);
2570 if (bm_preloading) {
2571 if (!gr_preload(entry.handle, (entry.preloaded == 2))) {
2572 mprintf(("Out of VRAM. Done preloading.\n"));
2573 bm_preloading = 0;
2574 }
2575 } else {
2576 bm_lock(entry.handle, (entry.used_flags == BMP_AABITMAP) ? 8 : 16, entry.used_flags);
2577 if (entry.ref_count >= 1) {
2578 bm_unlock(entry.handle);
2579 }
2580 }
2581
2582 n++;
2583
2584 multi_send_anti_timeout_ping();
2585
2586 if ((entry.info.ani.first_frame == 0) || (entry.info.ani.first_frame == entry.handle)) {
2587 #ifndef NDEBUG
2588 memset(busy_text, 0, sizeof(busy_text));
2589
2590 strcat_s(busy_text, "** BmpMan: ");
2591 strcat_s(busy_text, entry.filename);
2592 strcat_s(busy_text, " **");
2593
2594 game_busy(busy_text);
2595 #else
2596 game_busy();
2597 #endif
2598 }
2599 } else {
2600 bm_unload_fast(entry.handle);
2601 }
2602 }
2603 }
2604 }
2605
2606 nprintf(("BmpInfo", "BMPMAN: Loaded %d bitmaps that are marked as used for this level.\n", n));
2607
2608 #ifndef NDEBUG
2609 int total_bitmaps = 0;
2610 int total_slots = 0;
2611 for (auto& block : bm_blocks) {
2612 total_slots += BM_BLOCK_SIZE;
2613 for (auto& slot : block) {
2614 auto& entry = slot.entry;
2615
2616 if (entry.type != BM_TYPE_NONE) {
2617 total_bitmaps++;
2618 }
2619 if (entry.type == BM_TYPE_USER) {
2620 mprintf(("User bitmap '%s'\n", entry.filename));
2621 }
2622 }
2623 }
2624
2625 mprintf(("Bmpman: %d/%d bitmap slots in use.\n", total_bitmaps, total_slots));
2626 #endif
2627
2628 Bm_paging = 0;
2629 }
2630
bm_page_in_texture(int bitmapnum,int nframes)2631 void bm_page_in_texture(int bitmapnum, int nframes) {
2632 int i;
2633
2634 if (bitmapnum < 0)
2635 return;
2636
2637 auto entry = bm_get_entry(bitmapnum);
2638
2639 if (nframes <= 0) {
2640 if (bm_is_anim(entry) == true)
2641 nframes = entry->info.ani.num_frames;
2642 else
2643 nframes = 1;
2644 }
2645
2646 for (i = 0; i < nframes; i++) {
2647 auto frame_entry = bm_get_entry(bitmapnum + i);
2648
2649 frame_entry->preloaded = 1;
2650
2651 frame_entry->preload_count++;
2652
2653 frame_entry->used_flags = BMP_TEX_OTHER;
2654
2655 //check if its compressed
2656 switch (frame_entry->comp_type) {
2657 case BM_TYPE_NONE:
2658 continue;
2659
2660 case BM_TYPE_DXT1:
2661 frame_entry->used_flags = BMP_TEX_DXT1;
2662 continue;
2663
2664 case BM_TYPE_DXT3:
2665 frame_entry->used_flags = BMP_TEX_DXT3;
2666 continue;
2667
2668 case BM_TYPE_DXT5:
2669 frame_entry->used_flags = BMP_TEX_DXT5;
2670 continue;
2671
2672 case BM_TYPE_BC7:
2673 frame_entry->used_flags = BMP_TEX_BC7;
2674 continue;
2675
2676 case BM_TYPE_CUBEMAP_DXT1:
2677 case BM_TYPE_CUBEMAP_DXT3:
2678 case BM_TYPE_CUBEMAP_DXT5:
2679 frame_entry->used_flags = BMP_TEX_CUBEMAP;
2680 continue;
2681
2682 default:
2683 continue;
2684 }
2685 }
2686 }
2687
bm_page_in_xparent_texture(int bitmapnum,int nframes)2688 void bm_page_in_xparent_texture(int bitmapnum, int nframes) {
2689 int i;
2690
2691 for (i = 0; i < nframes; i++) {
2692 auto entry = bm_get_entry(bitmapnum + i);
2693
2694 entry->preloaded = 3;
2695
2696 entry->preload_count++;
2697
2698 entry->used_flags = BMP_TEX_XPARENT;
2699
2700 //check if its compressed
2701 switch (entry->comp_type) {
2702 case BM_TYPE_NONE:
2703 continue;
2704
2705 case BM_TYPE_DXT1:
2706 entry->used_flags = BMP_TEX_DXT1;
2707 continue;
2708
2709 case BM_TYPE_DXT3:
2710 entry->used_flags = BMP_TEX_DXT3;
2711 continue;
2712
2713 case BM_TYPE_DXT5:
2714 entry->used_flags = BMP_TEX_DXT5;
2715 continue;
2716
2717 case BM_TYPE_BC7:
2718 entry->used_flags = BMP_TEX_BC7;
2719 continue;
2720
2721 case BM_TYPE_CUBEMAP_DXT1:
2722 case BM_TYPE_CUBEMAP_DXT3:
2723 case BM_TYPE_CUBEMAP_DXT5:
2724 entry->used_flags = BMP_TEX_CUBEMAP;
2725 continue;
2726
2727 default:
2728 continue;
2729 }
2730 }
2731 }
2732
bm_print_bitmaps()2733 void bm_print_bitmaps() {
2734 #ifdef BMPMAN_NDEBUG
2735 for (auto& block : bm_blocks) {
2736 for (auto& slot : block) {
2737 auto& entry = slot.entry;
2738
2739 if (entry.type != BM_TYPE_NONE) {
2740 if (entry.data_size) {
2741 nprintf(("BMP DEBUG", "BMPMAN = num: %d, name: %s, handle: %d - (%s) size: %.3fM\n", entry.handle, entry.filename, entry.handle, entry.data_size
2742 ? NOX(
2743 "*LOCKED*") : NOX(""), ((float) entry.data_size / 1024.0f) / 1024.0f));
2744 } else {
2745 nprintf(("BMP DEBUG", "BMPMAN = num: %d, name: %s, handle: %d\n", entry.handle, entry.filename, entry.handle));
2746 }
2747 }
2748 }
2749 }
2750
2751 nprintf(("BMP DEBUG", "BMPMAN = LOCKED memory usage: %.3fM\n", ((float) bm_texture_ram / 1024.0f) / 1024.0f));
2752 #endif
2753 }
2754
bm_release(int handle,int clear_render_targets)2755 int bm_release(int handle, int clear_render_targets) {
2756 Assert(handle >= 0);
2757
2758 bitmap_entry *be;
2759
2760 be = bm_get_entry(handle);
2761
2762 if (be->type == BM_TYPE_NONE) {
2763 return 0; // Already been released?
2764 }
2765
2766 Assertion(be->handle == handle, "Invalid bitmap handle number %d (expected %d) for %s passed to bm_release()\n", be->handle, handle, be->filename);
2767
2768 if (!clear_render_targets && ((be->type == BM_TYPE_RENDER_TARGET_STATIC) || (be->type == BM_TYPE_RENDER_TARGET_DYNAMIC))) {
2769 nprintf(("BmpMan", "Tried to release a render target!\n"));
2770 return 0;
2771 }
2772
2773 // If it is locked, cannot free it.
2774 if (be->ref_count != 0) {
2775 nprintf(("BmpMan", "Tried to release %s that has a lock count of %d.. not releasing\n", be->filename, be->ref_count));
2776 return 0;
2777 }
2778
2779 // kind of like ref_count except it gets around the lock/unlock usage problem
2780 // this gets set for each bm_load() call so we can make sure and not unload it
2781 // from memory, even if we *can*, until it's really not needed anymore
2782 if (be->load_count > 0)
2783 be->load_count--;
2784
2785 if (be->load_count != 0) {
2786 nprintf(("BmpMan", "Tried to release %s that has a load count of %d.. not releasing\n", be->filename, be->load_count + 1));
2787 return 0;
2788 }
2789
2790 if (be->type != BM_TYPE_USER) {
2791 nprintf(("BmpMan", "Releasing bitmap %s with handle %i\n", be->filename, handle));
2792 }
2793
2794 // be sure that all frames of an ani are unloaded - taylor
2795 if (bm_is_anim(be)) {
2796 int i, first = be->info.ani.first_frame;
2797 int total = bm_get_entry(first)->info.ani.num_frames;
2798
2799 // The graphics system requires that the bitmap information for the whole animation is still valid when the data
2800 // is freed so we first free all the data and then clear the bitmaps data
2801 for (i = 0; i < total; i++) {
2802 bm_free_data(bm_get_slot(first + i), true);
2803 }
2804
2805 for (i = 0; i < total; i++) {
2806 auto entry = bm_get_entry(first + i);
2807
2808 memset(entry, 0, sizeof(bitmap_entry));
2809
2810 entry->type = BM_TYPE_NONE;
2811 entry->comp_type = BM_TYPE_NONE;
2812 entry->dir_type = CF_TYPE_ANY;
2813 // Fill in bogus structures!
2814
2815 // For debugging:
2816 strcpy_s(entry->filename, "IVE_BEEN_RELEASED!");
2817 entry->signature = 0xDEADBEEF; // a unique signature identifying the data
2818
2819 // bookeeping
2820 entry->ref_count = -1; // Number of locks on bitmap. Can't unload unless ref_count is 0.
2821
2822 // Stuff needed for animations
2823 entry->info.ani.first_frame = -1;
2824
2825 entry->handle = -1;
2826 }
2827 } else {
2828 auto slot = bm_get_slot(handle);
2829 auto entry = &slot->entry;
2830
2831 bm_free_data(slot, true); // clears flags, bbp, data, etc
2832
2833
2834 memset(entry, 0, sizeof(bitmap_entry));
2835
2836 entry->type = BM_TYPE_NONE;
2837 entry->comp_type = BM_TYPE_NONE;
2838 entry->dir_type = CF_TYPE_ANY;
2839 // Fill in bogus structures!
2840
2841 // For debugging:
2842 strcpy_s(entry->filename, "IVE_BEEN_RELEASED!");
2843 entry->signature = 0xDEADBEEF; // a unique signature identifying the data
2844
2845 // bookeeping
2846 entry->ref_count = -1; // Number of locks on bitmap. Can't unload unless ref_count is 0.
2847
2848 // Stuff needed for animations
2849 entry->info.ani.first_frame = -1;
2850
2851 entry->handle = -1;
2852 }
2853
2854 return 1;
2855 }
2856
bm_release_rendertarget(int handle)2857 bool bm_release_rendertarget(int handle) {
2858 Assert(handle >= 0);
2859
2860 bitmap_entry* be = bm_get_entry(handle);
2861
2862 if (be->type == BM_TYPE_NONE) {
2863 return false; // Already been released?
2864 }
2865
2866 Assertion(be->handle == handle, "Invalid bitmap handle number %d (expected %d) for %s passed to bm_release_rendertarget()\n", be->handle, handle, be->filename);
2867 Assertion(!bm_is_anim(be), "Cannot release a render target of an animation (bitmap handle number %d for %s)!\n", be->handle, be->filename);
2868
2869 if (!((be->type == BM_TYPE_RENDER_TARGET_STATIC) || (be->type == BM_TYPE_RENDER_TARGET_DYNAMIC))) {
2870 nprintf(("BmpMan", "Tried to release a render target of a non-rendered bitmap!\n"));
2871 return false;
2872 }
2873
2874 gr_bm_free_data(bm_get_slot(handle), false);
2875
2876 return true;
2877 }
2878
bm_reload(int bitmap_handle,const char * filename)2879 int bm_reload(int bitmap_handle, const char* filename) {
2880 Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
2881
2882 // if no file was passed then get out now
2883 if ((filename == NULL) || (strlen(filename) <= 0))
2884 return -1;
2885
2886 auto entry = bm_get_entry(bitmap_handle);
2887
2888 if (entry->type == BM_TYPE_NONE)
2889 return -1;
2890
2891 if (entry->ref_count) {
2892 nprintf(("BmpMan", "Trying to reload a bitmap that is still locked. Filename: %s, ref_count: %d", entry->filename, entry->ref_count));
2893 return -1;
2894 }
2895
2896 strcpy_s(entry->filename, filename);
2897 return bitmap_handle;
2898 }
2899
BM_SELECT_ALPHA_TEX_FORMAT()2900 void BM_SELECT_ALPHA_TEX_FORMAT() {
2901 Gr_current_red = &Gr_ta_red;
2902 Gr_current_green = &Gr_ta_green;
2903 Gr_current_blue = &Gr_ta_blue;
2904 Gr_current_alpha = &Gr_ta_alpha;
2905
2906 // setup pointers
2907 // should always assume that 16-bit is the default request
2908 bm_set_components = bm_set_components_argb_16_tex;
2909 }
2910
BM_SELECT_SCREEN_FORMAT()2911 void BM_SELECT_SCREEN_FORMAT() {
2912 Gr_current_red = &Gr_red;
2913 Gr_current_green = &Gr_green;
2914 Gr_current_blue = &Gr_blue;
2915 Gr_current_alpha = &Gr_alpha;
2916
2917 // setup pointers
2918 // should always assume that 16-bit is the default request
2919 bm_set_components = bm_set_components_argb_16_screen;
2920 }
2921
BM_SELECT_TEX_FORMAT()2922 void BM_SELECT_TEX_FORMAT() {
2923 Gr_current_red = &Gr_t_red;
2924 Gr_current_green = &Gr_t_green;
2925 Gr_current_blue = &Gr_t_blue;
2926 Gr_current_alpha = &Gr_t_alpha;
2927
2928 // setup pointers
2929 // should always assume that 16-bit is the default request
2930 bm_set_components = bm_set_components_argb_16_tex;
2931 }
2932
bm_set_components_argb_16_screen(ubyte * pixel,ubyte * rv,ubyte * gv,ubyte * bv,ubyte * av)2933 void bm_set_components_argb_16_screen(ubyte *pixel, ubyte *rv, ubyte *gv, ubyte *bv, ubyte *av) {
2934 if (*av == 0) {
2935 *((unsigned short*)pixel) = (unsigned short)Gr_current_green->mask;
2936 return;
2937 }
2938
2939 *((unsigned short*)pixel) = (unsigned short)(((int)*rv / Gr_current_red->scale) << Gr_current_red->shift);
2940 *((unsigned short*)pixel) |= (unsigned short)(((int)*gv / Gr_current_green->scale) << Gr_current_green->shift);
2941 *((unsigned short*)pixel) |= (unsigned short)(((int)*bv / Gr_current_blue->scale) << Gr_current_blue->shift);
2942 }
2943
bm_set_components_argb_16_tex(ubyte * pixel,ubyte * rv,ubyte * gv,ubyte * bv,ubyte * av)2944 void bm_set_components_argb_16_tex(ubyte *pixel, ubyte *rv, ubyte *gv, ubyte *bv, ubyte *av) {
2945 if (*av == 0) {
2946 *((unsigned short*)pixel) = 0;
2947 return;
2948 }
2949
2950 *((unsigned short*)pixel) = (unsigned short)(((int)*rv / Gr_current_red->scale) << Gr_current_red->shift);
2951 *((unsigned short*)pixel) |= (unsigned short)(((int)*gv / Gr_current_green->scale) << Gr_current_green->shift);
2952 *((unsigned short*)pixel) |= (unsigned short)(((int)*bv / Gr_current_blue->scale) << Gr_current_blue->shift);
2953 *((unsigned short*)pixel) |= (unsigned short)(Gr_current_alpha->mask);
2954 }
2955
bm_set_low_mem(int mode)2956 void bm_set_low_mem(int mode) {
2957 Assert((mode >= 0) && (mode <= 2));
2958
2959 CLAMP(mode, 0, 2);
2960 Bm_low_mem = mode;
2961 }
2962
bm_set_render_target(int handle,int face)2963 bool bm_set_render_target(int handle, int face) {
2964 GR_DEBUG_SCOPE("Set render target");
2965
2966 auto entry = handle >= 0 ? bm_get_entry(handle) : nullptr;
2967
2968 if (handle >= 0) {
2969 if ((entry->type != BM_TYPE_RENDER_TARGET_STATIC) && (entry->type != BM_TYPE_RENDER_TARGET_DYNAMIC)) {
2970 // odds are that someone passed a normal texture created with bm_load()
2971 mprintf(("Trying to set invalid bitmap (handle: %i) as render target!\n", handle));
2972 return false;
2973 }
2974 }
2975
2976 if (gr_bm_set_render_target(handle, face)) {
2977 if (gr_screen.rendering_to_texture == -1) {
2978 //if we are moving from the back buffer to a texture save whatever the current settings are
2979 gr_screen.save_max_w = gr_screen.max_w;
2980 gr_screen.save_max_h = gr_screen.max_h;
2981
2982 gr_screen.save_max_w_unscaled = gr_screen.max_w_unscaled;
2983 gr_screen.save_max_h_unscaled = gr_screen.max_h_unscaled;
2984
2985 gr_screen.save_max_w_unscaled_zoomed = gr_screen.max_w_unscaled_zoomed;
2986 gr_screen.save_max_h_unscaled_zoomed = gr_screen.max_h_unscaled_zoomed;
2987
2988 gr_screen.save_center_w = gr_screen.center_w;
2989 gr_screen.save_center_h = gr_screen.center_h;
2990
2991 gr_screen.save_center_offset_x = gr_screen.center_offset_x;
2992 gr_screen.save_center_offset_y = gr_screen.center_offset_y;
2993 }
2994
2995 if (handle < 0) {
2996 gr_screen.max_w = gr_screen.save_max_w;
2997 gr_screen.max_h = gr_screen.save_max_h;
2998
2999 gr_screen.max_w_unscaled = gr_screen.save_max_w_unscaled;
3000 gr_screen.max_h_unscaled = gr_screen.save_max_h_unscaled;
3001
3002 gr_screen.max_w_unscaled_zoomed = gr_screen.save_max_w_unscaled_zoomed;
3003 gr_screen.max_h_unscaled_zoomed = gr_screen.save_max_h_unscaled_zoomed;
3004
3005 gr_screen.center_w = gr_screen.save_center_w;
3006 gr_screen.center_h = gr_screen.save_center_h;
3007
3008 gr_screen.center_offset_x = gr_screen.save_center_offset_x;
3009 gr_screen.center_offset_y = gr_screen.save_center_offset_y;
3010 } else {
3011 gr_screen.max_w = entry->bm.w;
3012 gr_screen.max_h = entry->bm.h;
3013
3014 gr_screen.max_w_unscaled = entry->bm.w;
3015 gr_screen.max_h_unscaled = entry->bm.h;
3016
3017 gr_screen.max_w_unscaled_zoomed = entry->bm.w;
3018 gr_screen.max_h_unscaled_zoomed = entry->bm.h;
3019
3020 gr_screen.center_w = entry->bm.w;
3021 gr_screen.center_h = entry->bm.h;
3022
3023 gr_screen.center_offset_x = 0;
3024 gr_screen.center_offset_y = 0;
3025 }
3026
3027 gr_screen.rendering_to_face = face;
3028 gr_screen.rendering_to_texture = handle;
3029
3030 gr_reset_clip();
3031
3032 gr_setup_viewport();
3033
3034 return true;
3035 }
3036
3037 return false;
3038 }
3039
bm_unload(int handle,int clear_render_targets,bool nodebug)3040 int bm_unload(int handle, int clear_render_targets, bool nodebug) {
3041 bitmap_entry *be;
3042 bitmap *bmp;
3043
3044 if (handle == -1) {
3045 return -1;
3046 }
3047
3048 be = bm_get_entry(handle);
3049 bmp = &be->bm;
3050
3051 if (!clear_render_targets && ((be->type == BM_TYPE_RENDER_TARGET_STATIC) || (be->type == BM_TYPE_RENDER_TARGET_DYNAMIC))) {
3052 return -1;
3053 }
3054
3055 if (be->type == BM_TYPE_NONE) {
3056 return -1; // Already been released
3057 }
3058
3059 Assert(be->handle == handle); // INVALID BITMAP HANDLE!
3060
3061 // If it is locked, cannot free it.
3062 if (be->ref_count != 0 && !nodebug) {
3063 nprintf(("BmpMan", "Tried to unload %s that has a lock count of %d.. not unloading\n", be->filename, be->ref_count));
3064 return 0;
3065 }
3066
3067 // kind of like ref_count except it gets around the lock/unlock usage problem
3068 // this gets set for each bm_load() call so we can make sure and not unload it
3069 // from memory, even if we *can*, until it's really not needed anymore
3070 if (!Bm_ignore_load_count) {
3071 if (be->load_count > 0)
3072 be->load_count--;
3073
3074 if (be->load_count != 0 && !nodebug) {
3075 nprintf(("BmpMan", "Tried to unload %s that has a load count of %d.. not unloading\n", be->filename, be->load_count + 1));
3076 return 0;
3077 }
3078 }
3079
3080 // be sure that all frames of an ani are unloaded - taylor
3081 if (bm_is_anim(be) == true) {
3082 int i, first = be->info.ani.first_frame;
3083
3084 auto first_entry = bm_get_entry(first);
3085
3086 // for the unload all case, don't try to unload every frame of every frame
3087 // all additional frames automatically get unloaded with the first one
3088 if ((handle != be->info.ani.first_frame) && (first_entry->bm.data == 0))
3089 return 1;
3090
3091 for (i = 0; i < first_entry->info.ani.num_frames; i++) {
3092 if (!nodebug)
3093 nprintf(("BmpMan", "Unloading %s frame %d. %dx%dx%d\n", be->filename, i, bmp->w, bmp->h, bmp->bpp));
3094 bm_free_data(bm_get_slot(first + i)); // clears flags, bbp, data, etc
3095 }
3096 } else {
3097 if (!nodebug)
3098 nprintf(("BmpMan", "Unloading %s. %dx%dx%d\n", be->filename, bmp->w, bmp->h, bmp->bpp));
3099 bm_free_data(bm_get_slot(handle)); // clears flags, bbp, data, etc
3100 }
3101
3102 return 1;
3103 }
3104
bm_unload_all()3105 void bm_unload_all() {
3106 // since bm_unload_all() should only be called from game_shutdown() it should be
3107 // safe to ignore load_count's and unload anyway
3108 Bm_ignore_load_count = 1;
3109 for(auto& block : bm_blocks) {
3110 for (auto& slot : block) {
3111 auto& entry = slot.entry;
3112
3113 if (entry.type != BM_TYPE_NONE) {
3114 bm_unload(entry.handle, 1);
3115 }
3116 }
3117 }
3118
3119 Bm_ignore_load_count = 0;
3120 }
3121
bm_unload_fast(int handle,int clear_render_targets)3122 int bm_unload_fast(int handle, int clear_render_targets) {
3123 bitmap_entry *be;
3124 bitmap *bmp;
3125
3126 be = bm_get_entry(handle);
3127 bmp = &be->bm;
3128
3129 if (be->type == BM_TYPE_NONE) {
3130 return -1; // Already been released
3131 }
3132
3133 if (be->type == BM_TYPE_USER) {
3134 return -1;
3135 }
3136
3137 if (!clear_render_targets && ((be->type == BM_TYPE_RENDER_TARGET_STATIC) || (be->type == BM_TYPE_RENDER_TARGET_DYNAMIC))) {
3138 return -1;
3139 }
3140
3141 // If it is locked, cannot free it.
3142 if (be->ref_count != 0) {
3143 nprintf(("BmpMan", "Tried to unload_fast %s that has a lock count of %d.. not unloading\n", be->filename, be->ref_count));
3144 return 0;
3145 }
3146
3147 Assert(be->handle == handle); // INVALID BITMAP HANDLE!
3148
3149 // unlike bm_unload(), we handle each frame of an animation separately, for safer use in the graphics API
3150 nprintf(("BmpMan", "Fast-unloading %s. %dx%dx%d\n", be->filename, bmp->w, bmp->h, bmp->bpp));
3151 bm_free_data_fast(handle); // clears flags, bbp, data, etc
3152
3153 return 1;
3154 }
3155
bm_unlock(int handle)3156 void bm_unlock(int handle) {
3157 bitmap_entry *be;
3158
3159 Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
3160
3161 be = bm_get_entry(handle);
3162
3163 be->ref_count--;
3164 Assert(be->ref_count >= 0); // Trying to unlock data more times than lock was called!!!
3165 }
3166
bm_update_memory_used(int n,size_t size)3167 void bm_update_memory_used(int n, size_t size)
3168 {
3169 #ifdef BMPMAN_NDEBUG
3170 auto entry = bm_get_entry(n);
3171 Assert( entry->data_size == 0 );
3172 entry->data_size += size;
3173 bm_texture_ram += size;
3174 #endif
3175 }
3176
find_block_of(int n,int start_block)3177 static int find_block_of(int n, int start_block)
3178 {
3179 Assertion(n < (int) BM_BLOCK_SIZE, "Can not allocate bitmap block with %d slots! Block size is only "
3180 SIZE_T_ARG
3181 "!", n, BM_BLOCK_SIZE);
3182
3183 int nstart = 0;
3184
3185 if (n < 1) {
3186 Int3();
3187 return -1;
3188 }
3189
3190 int block_idx;
3191 for (block_idx = start_block; block_idx < (int)bm_blocks.size(); ++block_idx) {
3192 auto& block = bm_blocks[block_idx];
3193
3194 auto entry_idx = 0;
3195 auto cnt = 0;
3196 for (auto& slot : block) {
3197 auto& entry = slot.entry;
3198
3199 if (entry.type == BM_TYPE_NONE) {
3200 if (cnt == 0) {
3201 nstart = get_handle(block_idx, entry_idx);
3202 }
3203
3204 cnt++;
3205 } else {
3206 cnt = 0;
3207 }
3208
3209 if (cnt == n) {
3210 return nstart;
3211 }
3212
3213 ++entry_idx;
3214 }
3215 }
3216
3217 // If we are here it means that we could not find a block to store the bitmap blocks in.
3218 // In that case we just allocate a new block and try to allocate the block in that
3219 allocate_new_block();
3220
3221 // This call is sure to succeed since we now have a contiguous block of size BM_BLOCK_SIZE and n must be less than that
3222 return find_block_of(n, block_idx);
3223 }
3224
bm_is_texture_array(const int handle)3225 bool bm_is_texture_array(const int handle) {
3226 auto entry = bm_get_entry(handle);
3227
3228 if (!bm_is_anim(entry)) {
3229 return true;
3230 }
3231
3232 // This will return the index of the first animation frame
3233 return entry->info.ani.is_array;
3234 }
3235
bm_get_base_frame(const int handle,int * num_frames)3236 int bm_get_base_frame(const int handle, int* num_frames) {
3237 if (handle == -1) {
3238 if (num_frames != nullptr) {
3239 *num_frames = 0;
3240 }
3241 return -1;
3242 }
3243
3244 // If the bitmap is no animation then num_frames will still be at least 1
3245 auto animation_begin = bm_get_info(handle, nullptr, nullptr, nullptr, num_frames);
3246
3247 if (animation_begin < 0) {
3248 // Some kind of error
3249 return -1;
3250 }
3251
3252 if (!bm_is_texture_array(animation_begin)) {
3253 // If this is not a texture array then we just treat it like a single frame animation
3254 animation_begin = handle;
3255 if (num_frames != nullptr) {
3256 *num_frames = 1;
3257 }
3258 }
3259 return animation_begin;
3260 }
bm_get_array_index(const int handle)3261 int bm_get_array_index(const int handle) {
3262 return handle - bm_get_base_frame(handle, nullptr);
3263 }
3264
bmpman_count_bitmaps()3265 int bmpman_count_bitmaps() {
3266 if (!bm_inited)
3267 return -1;
3268
3269 int total_bitmaps = 0;
3270 for(auto& block : bm_blocks) {
3271 for (auto& slot : block) {
3272 auto& entry = slot.entry;
3273
3274 if (entry.type != BM_TYPE_NONE) {
3275 total_bitmaps++;
3276 }
3277 }
3278 }
3279
3280 return total_bitmaps;
3281 }
3282
bmpman_count_available_slots()3283 int bmpman_count_available_slots() {
3284 if (!bm_inited)
3285 return -1;
3286
3287 return (int) (bm_blocks.size() * BM_BLOCK_SIZE);
3288 }
3289
bm_validate_filename(const SCP_string & file,bool single_frame,bool animation)3290 bool bm_validate_filename(const SCP_string& file, bool single_frame, bool animation) {
3291 Assertion(single_frame || animation, "At least one of single_frame or animation must be true!");
3292
3293 if (!VALID_FNAME(file.c_str())) {
3294 return false;
3295 }
3296
3297 int handle = -1;
3298 if (single_frame && animation) {
3299 // Caller has indicated that single frames and animations are valid
3300 handle = bm_load_either(file.c_str());
3301 } else if (single_frame) {
3302 handle = bm_load(file);
3303 } else {
3304 handle = bm_load_animation(file.c_str());
3305 }
3306 if (handle != -1) {
3307 bm_release(handle);
3308 return true;
3309 }
3310 return false;
3311 }
bm_to_sdl_surface(int handle)3312 SDL_Surface* bm_to_sdl_surface(int handle) {
3313 Assertion(bm_is_valid(handle), "%d is no valid bitmap handle!", handle);
3314
3315 int w;
3316 int h;
3317
3318 bm_get_info(handle, &w, &h, nullptr, nullptr);
3319 Uint32 rmask, gmask, bmask, amask;
3320
3321 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
3322 rmask = 0x0000ff00;
3323 gmask = 0x00ff0000;
3324 bmask = 0xff000000;
3325 amask = 0x000000ff;
3326 #else
3327 rmask = 0x00ff0000;
3328 gmask = 0x0000ff00;
3329 bmask = 0x000000ff;
3330 amask = 0xff000000;
3331 #endif
3332
3333 SDL_Surface* bitmapSurface = SDL_CreateRGBSurface(0, w, h, 32, rmask, gmask, bmask, amask);
3334 if (SDL_LockSurface(bitmapSurface) < 0) {
3335 return nullptr;
3336 }
3337 bitmap* bmp = bm_lock(handle, 32, BMP_TEX_XPARENT);
3338
3339 memcpy(bitmapSurface->pixels, reinterpret_cast<void*>(bmp->data), static_cast<size_t>(w * h * 4));
3340
3341 bm_unlock(handle);
3342 SDL_UnlockSurface(bitmapSurface);
3343
3344 return bitmapSurface;
3345
3346 }
3347