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
10
11
12 #include "globalincs/pstypes.h"
13 #include "bmpman/bmpman.h"
14 #include "ddsutils/ddsutils.h"
15 #include "tgautils/tgautils.h"
16 #include "pngutils/pngutils.h"
17 #include "jpgutils/jpgutils.h"
18 #include "pcxutils/pcxutils.h"
19 #include "graphics/gropengltexture.h"
20 #include "graphics/gropenglextension.h"
21 #include "globalincs/systemvars.h"
22 #include "anim/animplay.h"
23 #include "anim/packunpack.h"
24 #include "cmdline/cmdline.h"
25
26 #define BMPMAN_INTERNAL
27 #include "bmpman/bm_internal.h"
28
29
30
is_power_of_two(int w,int h)31 static inline int is_power_of_two(int w, int h)
32 {
33 // NOTE: OpenGL texture code has a min tex size of 16 (currently), so we need to be at least
34 // the min size here to qualify as power-of-2 and not get resized later on
35 return ( ((w >= GL_min_texture_width) && !(w & (w-1))) && ((h >= GL_min_texture_height) && !(h & (h-1))) );
36 }
37
get_num_mipmap_levels(int w,int h)38 int get_num_mipmap_levels(int w, int h)
39 {
40 int size, levels = 0;
41
42 // make sure we can and should generate mipmaps before trying to use them
43 if ( !Cmdline_mipmap || !Is_Extension_Enabled(OGL_SGIS_GENERATE_MIPMAP) )
44 return 1;
45
46 size = MAX(w, h);
47
48 while (size > 0) {
49 size >>= 1;
50 levels++;
51 }
52
53 return (levels > 1) ? levels : 1;
54 }
55
56 /**
57 * Anything API specific to freeing bm data
58 */
gr_opengl_bm_free_data(int n,bool release)59 void gr_opengl_bm_free_data(int n, bool release)
60 {
61 Assert( (n >= 0) && (n < MAX_BITMAPS) );
62
63 // might as well free up the on card texture data too in order to get rid
64 // of old interface stuff but don't free USER types, unless it's a total release,
65 // since we can reuse ANI slots for faster and less resource intensive rendering
66 if ( release || (bm_bitmaps[n].type != BM_TYPE_USER) )
67 opengl_free_texture_slot( n );
68
69 if ( (bm_bitmaps[n].type == BM_TYPE_RENDER_TARGET_STATIC) || (bm_bitmaps[n].type == BM_TYPE_RENDER_TARGET_DYNAMIC) )
70 opengl_kill_render_target( n );
71 }
72
73 /**
74 * API specifics for creating a user bitmap
75 */
gr_opengl_bm_create(int n)76 void gr_opengl_bm_create(int n)
77 {
78 Assert( (n >= 0) && (n < MAX_BITMAPS) );
79 }
80
81 // Load an image and validate it while retrieving information for later use
82 // Input: type = current BM_TYPE_*
83 // n = location in bm_bitmaps[]
84 // filename = name of the current file
85 // img_cfp = already open CFILE handle, if available
86 //
87 // Output: w = bmp width
88 // h = bmp height
89 // bpp = bmp bits per pixel
90 // c_type = output for an updated BM_TYPE_*
91 // mm_lvl = number of mipmap levels for the image
92 // size = size of the data contained in the image
gr_opengl_bm_load(ubyte type,int n,const char * filename,CFILE * img_cfp,int * w,int * h,int * bpp,ubyte * c_type,int * mm_lvl,int * size)93 int gr_opengl_bm_load(ubyte type, int n, const char *filename, CFILE *img_cfp, int *w, int *h, int *bpp, ubyte *c_type, int *mm_lvl, int *size)
94 {
95 int dds_ct;
96
97 if (type == BM_TYPE_DDS) {
98 int dds_error = dds_read_header( filename, img_cfp, w, h, bpp, &dds_ct, mm_lvl, size );
99
100 if (dds_error != DDS_ERROR_NONE) {
101 mprintf(("DDS ERROR: Couldn't open '%s' -- %s\n", filename, dds_error_string(dds_error)));
102 return -1;
103 }
104
105 switch (dds_ct) {
106 case DDS_DXT1:
107 *c_type = BM_TYPE_DXT1;
108 break;
109
110 case DDS_DXT3:
111 *c_type = BM_TYPE_DXT3;
112 break;
113
114 case DDS_DXT5:
115 *c_type = BM_TYPE_DXT5;
116 break;
117
118 case DDS_UNCOMPRESSED:
119 *c_type = BM_TYPE_DDS;
120 break;
121
122 case DDS_CUBEMAP_DXT1:
123 *c_type = BM_TYPE_CUBEMAP_DXT1;
124 break;
125
126 case DDS_CUBEMAP_DXT3:
127 *c_type = BM_TYPE_CUBEMAP_DXT3;
128 break;
129
130 case DDS_CUBEMAP_DXT5:
131 *c_type = BM_TYPE_CUBEMAP_DXT5;
132 break;
133
134 case DDS_CUBEMAP_UNCOMPRESSED:
135 *c_type = BM_TYPE_CUBEMAP_DDS;
136 break;
137
138 default:
139 Error(LOCATION, "Bad DDS file compression! Not using DXT1,3,5: %s", filename);
140 return -1;
141 }
142 }
143 // if its a tga file
144 else if (type == BM_TYPE_TGA) {
145 int tga_error = targa_read_header( filename, img_cfp, w, h, bpp, NULL );
146 if ( tga_error != TARGA_ERROR_NONE ) {
147 mprintf(( "tga: Couldn't open '%s'\n", filename ));
148 return -1;
149 }
150 }
151 // if its a png file
152 else if (type == BM_TYPE_PNG) {
153 int png_error=png_read_header( filename, img_cfp, w, h, bpp, NULL );
154 if ( png_error != PNG_ERROR_NONE ) {
155 mprintf(( "png: Couldn't open '%s'\n", filename ));
156 return -1;
157 }
158 }
159 // if its a jpg file
160 else if (type == BM_TYPE_JPG) {
161 int jpg_error=jpeg_read_header( filename, img_cfp, w, h, bpp, NULL );
162 if ( jpg_error != JPEG_ERROR_NONE ) {
163 mprintf(( "jpg: Couldn't open '%s'\n", filename ));
164 return -1;
165 }
166 }
167 // if its a pcx file
168 else if (type == BM_TYPE_PCX) {
169 int pcx_error = pcx_read_header( filename, img_cfp, w, h, bpp, NULL );
170 if ( pcx_error != PCX_ERROR_NONE ) {
171 mprintf(( "pcx: Couldn't open '%s'\n", filename ));
172 return -1;
173 }
174 } else {
175 Assert( 0 );
176
177 return -1;
178 }
179
180 return 0;
181 }
182
183 /**
184 * API specific init instructions
185 */
gr_opengl_bm_init(int n)186 void gr_opengl_bm_init(int n)
187 {
188 Assert( (n >= 0) && (n < MAX_BITMAPS) );
189 }
190
191 /**
192 * Specific instructions for setting up the start of a page-in session
193 */
gr_opengl_bm_page_in_start()194 void gr_opengl_bm_page_in_start()
195 {
196 opengl_preload_init();
197 }
198
199 extern void bm_clean_slot(int n);
200 extern int opengl_compress_image( ubyte **out_data, ubyte *in_data, int width, int height, int alpha = 1, int num_mipmaps = 1 );
201
opengl_bm_lock_ani_compress(int handle,int bitmapnum,bitmap_entry * be,bitmap * bmp,ubyte bpp,ubyte flags)202 static int opengl_bm_lock_ani_compress( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags )
203 {
204 anim *the_anim;
205 anim_instance *the_anim_instance;
206 bitmap *bm;
207 ubyte *frame_data;
208 int size, i;
209 int first_frame, nframes;
210 ubyte *compressed_data = NULL;
211 int out_size = 0;
212 int alpha = 0;
213 int num_mipmaps = 1;
214
215 first_frame = be->info.ani.first_frame;
216 nframes = bm_bitmaps[first_frame].info.ani.num_frames;
217
218 // bpp can always be 24-bit since we don't do images with alpha here
219 bpp = 24;
220
221 alpha = (bpp == 32);
222
223 if ( (the_anim = anim_load(bm_bitmaps[first_frame].filename, bm_bitmaps[first_frame].dir_type)) == NULL ) {
224 nprintf(("BMPMAN", "Error opening %s in bm_lock\n", be->filename));
225 return 1;
226 }
227
228 if ( (the_anim_instance = init_anim_instance(the_anim, bpp)) == NULL ) {
229 nprintf(("BMPMAN", "Error opening %s in bm_lock\n", be->filename));
230 anim_free(the_anim);
231 return 1;
232 }
233
234 int can_drop_frames = 0;
235
236 if ( the_anim->total_frames != bm_bitmaps[first_frame].info.ani.num_frames ) {
237 can_drop_frames = 1;
238 }
239
240 bm = &bm_bitmaps[first_frame].bm;
241 size = bm->w * bm->h * (bpp >> 3);
242
243 num_mipmaps = get_num_mipmap_levels( bm->w, bm->h );
244 Assert( num_mipmaps > 0 );
245
246 nprintf(("BMPMAN", "Attempting to compress '%s' with %d frames, original size %.3fM ... ", bm_bitmaps[first_frame].filename, nframes, ((float)(size*nframes)/1024.0f)/1024.0f));
247
248 for ( i=0; i<nframes; i++ ) {
249 be = &bm_bitmaps[first_frame+i];
250 bmp = &bm_bitmaps[first_frame+i].bm;
251
252 // Unload any existing data
253 bm_clean_slot( first_frame+i );
254
255 bmp->flags = 0;
256
257 bmp->bpp = bpp;
258
259 frame_data = anim_get_next_raw_buffer(the_anim_instance, 0, 0, bmp->bpp);
260
261 Assert( frame_data != NULL );
262
263 out_size = opengl_compress_image(&compressed_data, frame_data, bmp->w, bmp->h, alpha, num_mipmaps);
264
265 if (out_size == 0) {
266 if (compressed_data != NULL) {
267 vm_free(compressed_data);
268 compressed_data = NULL;
269 }
270
271 nprintf(("BMPMAN", "compression failed!!\n"));
272
273 return 1;
274 }
275
276 Assert( compressed_data != NULL );
277
278 bmp->data = (ptr_u)compressed_data;
279 bmp->bpp = (alpha) ? 32 : 24;
280 bmp->palette = NULL;
281 be->comp_type = (alpha) ? BM_TYPE_DXT5 : BM_TYPE_DXT1;
282 be->mem_taken = out_size;
283 be->num_mipmaps = num_mipmaps;
284
285 bm_update_memory_used( first_frame + i, out_size );
286
287 // Skip a frame
288 if ( (i < nframes-1) && can_drop_frames ) {
289 frame_data = anim_get_next_raw_buffer(the_anim_instance, 0, 0, bm->bpp);
290 }
291
292 compressed_data = NULL;
293 }
294
295 if (num_mipmaps > 1) {
296 nprintf(("BMPMAN", "new size is %.3fM with %d mipmap levels.\n", ((float)(out_size*nframes)/1024.0f)/1024.0f, num_mipmaps));
297 } else {
298 nprintf(("BMPMAN", "new size is %.3fM.\n", ((float)(out_size*nframes)/1024.0f)/1024.0f));
299 }
300
301 free_anim_instance(the_anim_instance);
302 anim_free(the_anim);
303
304 return 0;
305 }
306
opengl_bm_lock_compress(int handle,int bitmapnum,bitmap_entry * be,bitmap * bmp,int bpp,int flags)307 static int opengl_bm_lock_compress( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, int bpp, int flags )
308 {
309 ubyte *data = NULL;
310 ubyte *compressed_data = NULL;
311 int error_code = 1;
312 int out_size = 0;
313 int byte_size = 0;
314 int alpha = 1;
315 int num_mipmaps = 1;
316
317 // don't use for EFFs, if they were wanting to be DDS then they should already be that way
318 if ( be->type == BM_TYPE_EFF ) {
319 return 1;
320 }
321
322 Assert( (be->type == BM_TYPE_PCX) ||
323 (be->type == BM_TYPE_TGA) ||
324 (be->type == BM_TYPE_PNG) ||
325 (be->type == BM_TYPE_JPG) );
326
327 Assert( !(flags & BMP_AABITMAP) );
328
329 // PCX need to be loaded as 32-bit
330 if ( (be->type == BM_TYPE_PCX) && (bpp == 16) )
331 bpp = 32;
332
333 bm_clean_slot( bitmapnum );
334
335 byte_size = (bpp >> 3);
336 Assert( (byte_size == 3) || (byte_size == 4) );
337 alpha = (byte_size != 3);
338
339 data = (ubyte*)vm_malloc(bmp->w * bmp->h * byte_size);
340
341 Assert( data != NULL );
342
343 if ( data == NULL )
344 return 1;
345
346 // read in bitmap data
347 if (be->type == BM_TYPE_PCX) {
348 Assert( bpp == 32 );
349 error_code = pcx_read_bitmap( be->filename, data, NULL, byte_size );
350 } else if (be->type == BM_TYPE_TGA) {
351 error_code = targa_read_bitmap( be->filename, data, NULL, byte_size );
352 } else if (be->type == BM_TYPE_PNG) {
353 error_code = png_read_bitmap( be->filename, data, NULL, byte_size );
354 } else if (be->type == BM_TYPE_JPG) {
355 error_code = jpeg_read_bitmap( be->filename, data, NULL, byte_size );
356 } else {
357 Assert( 0 );
358 }
359
360 nprintf(("BMPMAN", "Attempting to compress '%s', original size %.3fM ... ", be->filename, ((float)(bmp->w * bmp->h * byte_size)/1024.0f)/1024.0f));
361
362 // NOTE: this assumes that the *_ERROR_NONE #define's are going to be 0
363 if (error_code) {
364 if (data != NULL) {
365 vm_free(data);
366 data = NULL;
367 }
368
369 nprintf(("BMPMAN", "initial read failed!!\n"));
370
371 return 1;
372 }
373
374 num_mipmaps = get_num_mipmap_levels( bmp->w, bmp->h );
375 Assert( num_mipmaps > 0 );
376
377 // now for the attempt to compress the data
378 out_size = opengl_compress_image(&compressed_data, data, bmp->w, bmp->h, alpha, num_mipmaps);
379
380 if (data != NULL) {
381 vm_free(data);
382 data = NULL;
383 }
384
385 if (out_size == 0) {
386 if (compressed_data != NULL) {
387 vm_free(compressed_data);
388 compressed_data = NULL;
389 }
390
391 nprintf(("BMPMAN", "compression failed!!\n"));
392
393 return 1;
394 }
395
396 Assert( compressed_data != NULL );
397
398 if (num_mipmaps > 1) {
399 nprintf(("BMPMAN", "new size is %.3fM with %d mipmap levels.\n", ((float)out_size/1024.0f)/1024.0f, num_mipmaps));
400 } else {
401 nprintf(("BMPMAN", "new size is %.3fM.\n", ((float)out_size/1024.0f)/1024.0f));
402 }
403
404 bmp->data = (ptr_u)compressed_data;
405 bmp->bpp = (alpha) ? (ubyte)32 : (ubyte)24;
406 bmp->palette = NULL;
407 be->comp_type = (alpha) ? BM_TYPE_DXT5 : BM_TYPE_DXT1;
408 be->mem_taken = out_size;
409 be->num_mipmaps = num_mipmaps;
410
411 bm_update_memory_used( bitmapnum, out_size );
412
413 return 0;
414 }
415
416 extern bool opengl_texture_slot_valid(int n, int handle);
417
418 /**
419 * Lock an image files data into memory
420 */
gr_opengl_bm_lock(const char * filename,int handle,int bitmapnum,ubyte bpp,ubyte flags,bool nodebug)421 int gr_opengl_bm_lock( const char *filename, int handle, int bitmapnum, ubyte bpp, ubyte flags, bool nodebug)
422 {
423 ubyte c_type = BM_TYPE_NONE;
424 ubyte true_bpp;
425 int try_compress = 0;
426
427 bitmap_entry *be = &bm_bitmaps[bitmapnum];
428 bitmap *bmp = &be->bm;
429
430 Assert( !Is_standalone );
431
432 if (bmp->true_bpp > bpp)
433 true_bpp = bmp->true_bpp;
434 else
435 true_bpp = bpp;
436
437 // don't do a bpp check here since it could be different in OGL - taylor
438 if ( (bmp->data == 0) && !opengl_texture_slot_valid(bitmapnum, handle) ) {
439 Assert(be->ref_count == 1);
440
441 if (be->type != BM_TYPE_USER && !nodebug) {
442 if (bmp->data == 0)
443 nprintf (("BmpMan", "Loading %s for the first time.\n", be->filename));
444 }
445
446 if ( !Bm_paging ) {
447 if (be->type != BM_TYPE_USER && !nodebug)
448 nprintf(("Paging", "Loading %s (%dx%dx%d)\n", be->filename, bmp->w, bmp->h, true_bpp));
449 }
450
451 // select proper format
452 if (flags & BMP_AABITMAP)
453 BM_SELECT_ALPHA_TEX_FORMAT();
454 else if (flags & BMP_TEX_ANY)
455 BM_SELECT_TEX_FORMAT();
456 else
457 BM_SELECT_SCREEN_FORMAT();
458
459 // make sure we use the real graphic type for EFFs
460 if ( be->type == BM_TYPE_EFF ) {
461 c_type = be->info.ani.eff.type;
462 } else {
463 c_type = be->type;
464 }
465
466 try_compress = (Cmdline_img2dds && Texture_compression_available && is_power_of_two(bmp->w, bmp->h) && !(flags & BMP_AABITMAP) && !Is_standalone);
467
468 switch ( c_type )
469 {
470 case BM_TYPE_PCX:
471 if (try_compress && (true_bpp >= 16)) {
472 if ( !opengl_bm_lock_compress(handle, bitmapnum, be, bmp, true_bpp, flags) ) {
473 break;
474 }
475 }
476
477 bm_lock_pcx( handle, bitmapnum, be, bmp, true_bpp, flags );
478 break;
479
480 case BM_TYPE_ANI:
481 if (try_compress && (true_bpp >= 16) && !(flags & BMP_TEX_XPARENT)) {
482 if ( !opengl_bm_lock_ani_compress(handle, bitmapnum, be, bmp, true_bpp, flags) ) {
483 break;
484 }
485 }
486
487 bm_lock_ani( handle, bitmapnum, be, bmp, true_bpp, flags );
488 break;
489
490 case BM_TYPE_TGA:
491 if (try_compress && (true_bpp >= 24)) {
492 if ( !opengl_bm_lock_compress(handle, bitmapnum, be, bmp, true_bpp, flags) ) {
493 break;
494 }
495 }
496
497 bm_lock_tga( handle, bitmapnum, be, bmp, true_bpp, flags );
498 break;
499
500 case BM_TYPE_PNG:
501 //libpng handles compression with zlib
502 bm_lock_png( handle, bitmapnum, be, bmp, true_bpp, flags );
503 break;
504
505 case BM_TYPE_JPG:
506 if (try_compress) {
507 if ( !opengl_bm_lock_compress(handle, bitmapnum, be, bmp, true_bpp, flags) ) {
508 break;
509 }
510 }
511
512 bm_lock_jpg( handle, bitmapnum, be, bmp, true_bpp, flags );
513 break;
514
515 case BM_TYPE_DDS:
516 case BM_TYPE_DXT1:
517 case BM_TYPE_DXT3:
518 case BM_TYPE_DXT5:
519 case BM_TYPE_CUBEMAP_DDS:
520 case BM_TYPE_CUBEMAP_DXT1:
521 case BM_TYPE_CUBEMAP_DXT3:
522 case BM_TYPE_CUBEMAP_DXT5:
523 bm_lock_dds( handle, bitmapnum, be, bmp, true_bpp, flags );
524 break;
525
526 case BM_TYPE_USER:
527 bm_lock_user( handle, bitmapnum, be, bmp, true_bpp, flags );
528 break;
529
530 default:
531 Warning(LOCATION, "Unsupported type in bm_lock -- %d\n", c_type );
532 return -1;
533 }
534
535 // always go back to screen format
536 BM_SELECT_SCREEN_FORMAT();
537
538 // make sure we actually did something
539 if ( !(bmp->data) )
540 return -1;
541 }
542
543 return 0;
544 }
545
gr_opengl_bm_save_render_target(int n)546 void gr_opengl_bm_save_render_target(int n)
547 {
548 Assert( (n >= 0) && (n < MAX_BITMAPS) );
549
550 if ( !Is_Extension_Enabled(OGL_EXT_FRAMEBUFFER_OBJECT) || Cmdline_no_fbo ) {
551 return;
552 }
553
554 bitmap_entry *be = &bm_bitmaps[n];
555 bitmap *bmp = &be->bm;
556
557 int rc = opengl_export_image(n, bmp->w, bmp->h, (bmp->true_bpp == 32), be->num_mipmaps, (ubyte*)bmp->data);
558
559 if (rc != be->mem_taken) {
560 Int3();
561 return;
562 }
563
564 if (Cmdline_save_render_targets) {
565 dds_save_image(bmp->w, bmp->h, bmp->true_bpp, be->num_mipmaps, (ubyte*)bmp->data, (bmp->flags & BMP_FLAG_CUBEMAP));
566 }
567 }
568
gr_opengl_bm_make_render_target(int n,int * width,int * height,ubyte * bpp,int * mm_lvl,int flags)569 int gr_opengl_bm_make_render_target(int n, int *width, int *height, ubyte *bpp, int *mm_lvl, int flags)
570 {
571 Assert( (n >= 0) && (n < MAX_BITMAPS) );
572
573 if ( !Is_Extension_Enabled(OGL_EXT_FRAMEBUFFER_OBJECT) || Cmdline_no_fbo ) {
574 return 0;
575 }
576
577 if ( (flags & BMP_FLAG_CUBEMAP) && !Is_Extension_Enabled(OGL_ARB_TEXTURE_CUBE_MAP) ) {
578 return 0;
579 }
580
581 if ( (flags & BMP_FLAG_CUBEMAP) && (*width != *height) ) {
582 MIN(*width, *height) = MAX(*width, *height);
583 }
584
585 // Only enforce power of two size if not supported
586 if (!(Is_Extension_Enabled(OGL_ARB_TEXTURE_NON_POWER_OF_TWO)))
587 {
588 Assert( is_power_of_two(*width, *height) );
589 }
590
591 if ( opengl_make_render_target(bm_bitmaps[n].handle, n, width, height, bpp, mm_lvl, flags) ) {
592 return 1;
593 }
594
595 return 0;
596 }
597
gr_opengl_bm_set_render_target(int n,int face)598 int gr_opengl_bm_set_render_target(int n, int face)
599 {
600 if ( !Is_Extension_Enabled(OGL_EXT_FRAMEBUFFER_OBJECT) || Cmdline_no_fbo ) {
601 return 0;
602 }
603
604 if (n == -1) {
605 opengl_set_render_target(-1);
606 return 1;
607 }
608
609 Assert( (n >= 0) && (n < MAX_BITMAPS) );
610
611 int is_static = (bm_bitmaps[n].type == BM_TYPE_RENDER_TARGET_STATIC);
612
613 if ( opengl_set_render_target(n, face, is_static) ) {
614 return 1;
615 }
616
617 return 0;
618 }
619