1 //----------------------------------------------------------------------------
2 // EDGE Generalised Image Handling
3 //----------------------------------------------------------------------------
4 //
5 // Copyright (c) 1999-2009 The EDGE Team.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 // Based on the DOOM source code, released by Id Software under the
20 // following copyright:
21 //
22 // Copyright (C) 1993-1996 by id Software, Inc.
23 //
24 //----------------------------------------------------------------------------
25 //
26 // -AJA- 2000/06/25: Began this image generalisation, based on Erik
27 // Sandberg's w_textur.c/h code.
28 //
29 // TODO HERE:
30 // - faster search methods.
31 // - do some optimisation
32 //
33
34 #include "i_defs.h"
35 #include "i_defs_gl.h"
36
37 #include <limits.h>
38 #include <list>
39
40 #include "epi/endianess.h"
41 #include "epi/file.h"
42 #include "epi/filesystem.h"
43 #include "epi/file_memory.h"
44
45 #include "epi/image_data.h"
46 #include "epi/image_hq2x.h"
47 #include "epi/image_png.h"
48 #include "epi/image_jpeg.h"
49 #include "epi/image_tga.h"
50
51 #include "dm_data.h"
52 #include "dm_defs.h"
53 #include "dm_state.h"
54 #include "e_search.h"
55 #include "e_main.h"
56 #include "m_argv.h"
57 #include "m_misc.h"
58 #include "p_local.h"
59 #include "r_defs.h"
60 #include "r_gldefs.h"
61 #include "r_image.h"
62 #include "r_sky.h"
63 #include "r_texgl.h"
64 #include "r_colormap.h"
65 #include "w_texture.h"
66 #include "w_wad.h"
67 #include "z_zone.h"
68
69
70 // LIGHTING DEBUGGING
71 // #define MAKE_TEXTURES_WHITE 1
72
73 extern epi::image_data_c *ReadAsEpiBlock(image_c *rim);
74
75 extern epi::file_c *OpenUserFileOrLump(imagedef_c *def);
76
77 extern void CloseUserFileOrLump(imagedef_c *def, epi::file_c *f);
78
79 // FIXME: duplicated in r_doomtex
80 #define DUMMY_X 16
81 #define DUMMY_Y 16
82
83
84 extern void DeleteSkyTextures(void);
85 extern void DeleteColourmapTextures(void);
86
87
88 //
89 // This structure is for "cached" images (i.e. ready to be used for
90 // rendering), and is the non-opaque version of cached_image_t. A
91 // single structure is used for all image modes (Block and OGL).
92 //
93 // Note: multiple modes and/or multiple mips of the same image_c can
94 // indeed be present in the cache list at any one time.
95 //
96 typedef struct cached_image_s
97 {
98 // parent image
99 image_c *parent;
100
101 // colormap used for translated image, normally NULL
102 const colourmap_c *trans_map;
103
104 // general hue of image (skewed towards pure colors)
105 rgbcol_t hue;
106
107 // texture identifier within GL
108 GLuint tex_id;
109 }
110 cached_image_t;
111
112
113 typedef std::list<image_c *> real_image_container_c;
114
115
do_Lookup(real_image_container_c & bucket,const char * name,int source_type=-1)116 static image_c *do_Lookup(real_image_container_c& bucket, const char *name,
117 int source_type = -1
118 /* use -2 to prevent USER override */)
119 {
120 // for a normal lookup, we want USER images to override
121 if (source_type == -1)
122 {
123 image_c *rim = do_Lookup(bucket, name, IMSRC_User); // recursion
124 if (rim)
125 return rim;
126 }
127
128 real_image_container_c::reverse_iterator it;
129
130 // search backwards, we want newer image to override older ones
131 for (it = bucket.rbegin(); it != bucket.rend(); it++)
132 {
133 image_c *rim = *it;
134
135 if (source_type >= 0 && source_type != (int)rim->source_type)
136 continue;
137
138 if (stricmp(name, rim->name) == 0)
139 return rim;
140 }
141
142 return NULL; // not found
143 }
144
do_Animate(real_image_container_c & bucket)145 static void do_Animate(real_image_container_c& bucket)
146 {
147 real_image_container_c::iterator it;
148
149 for (it = bucket.begin(); it != bucket.end(); it++)
150 {
151 image_c *rim = *it;
152
153 if (rim->anim.speed == 0) // not animated ?
154 continue;
155
156 SYS_ASSERT(rim->anim.count > 0);
157
158 rim->anim.count--;
159
160 if (rim->anim.count == 0 && rim->anim.cur->anim.next)
161 {
162 rim->anim.cur = rim->anim.cur->anim.next;
163 rim->anim.count = rim->anim.speed;
164 }
165 }
166 }
167
168 #if 0
169 static void do_DebugDump(real_image_container_c& bucket)
170 {
171 L_WriteDebug("{\n");
172
173 real_image_container_c::iterator it;
174
175 for (it = bucket.begin(); it != bucket.end(); it++)
176 {
177 image_c *rim = *it;
178
179 L_WriteDebug(" [%s] type %d: %dx%d < %dx%d\n",
180 rim->name, rim->source_type,
181 rim->actual_w, rim->actual_h,
182 rim->total_w, rim->total_h);
183 }
184
185 L_WriteDebug("}\n");
186 }
187 #endif
188
189 // mipmapping enabled ?
190 // 0 off, 1 bilinear, 2 trilinear
191 int var_mipmapping = 1;
192
193 int var_smoothing = 1;
194
195 bool var_dithering = false;
196
197 int hq2x_scaling = 1;
198
199
200 // total set of images
201 static real_image_container_c real_graphics;
202 static real_image_container_c real_textures;
203 static real_image_container_c real_flats;
204 static real_image_container_c real_sprites;
205
206
207 const image_c *skyflatimage;
208
209 static const image_c *dummy_sprite;
210 static const image_c *dummy_skin;
211 static const image_c *dummy_hom[2];
212
213
214 // image cache (actually a ring structure)
215 static std::list<cached_image_t *> image_cache;
216
217
218 // tiny ring helpers
InsertAtTail(cached_image_t * rc)219 static inline void InsertAtTail(cached_image_t *rc)
220 {
221 image_cache.push_back(rc);
222
223 #if 0 // OLD WAY
224 SYS_ASSERT(rc != &imagecachehead);
225
226 rc->prev = imagecachehead.prev;
227 rc->next = &imagecachehead;
228
229 rc->prev->next = rc;
230 rc->next->prev = rc;
231 #endif
232 }
Unlink(cached_image_t * rc)233 static inline void Unlink(cached_image_t *rc)
234 {
235 // FIXME: Unlink
236 #if 0
237 SYS_ASSERT(rc != &imagecachehead);
238
239 rc->prev->next = rc->next;
240 rc->next->prev = rc->prev;
241 #endif
242 }
243
244
245
246 //----------------------------------------------------------------------------
247 //
248 // IMAGE CREATION
249 //
250
image_c()251 image_c::image_c() : actual_w(0), actual_h(0), total_w(0), total_h(0),
252 source_type(IMSRC_Dummy),
253 source_palette(-1),
254 cache()
255 {
256 strcpy(name, "_UNINIT_");
257
258 memset(&source, 0, sizeof(source));
259 memset(&anim, 0, sizeof(anim));
260 }
261
~image_c()262 image_c::~image_c()
263 {
264 /* TODO: image_c destructor */
265 }
266
267
NewImage(int width,int height,int opacity=OPAC_Unknown)268 static image_c *NewImage(int width, int height, int opacity = OPAC_Unknown)
269 {
270 image_c *rim = new image_c;
271
272 rim->actual_w = width;
273 rim->actual_h = height;
274 rim->total_w = W_MakeValidSize(width);
275 rim->total_h = W_MakeValidSize(height);
276 rim->offset_x = rim->offset_y = 0;
277 rim->scale_x = rim->scale_y = 1.0f;
278 rim->opacity = opacity;
279
280 // set initial animation info
281 rim->anim.cur = rim;
282 rim->anim.next = NULL;
283 rim->anim.count = rim->anim.speed = 0;
284
285 return rim;
286 }
287
288
CreateDummyImage(const char * name,rgbcol_t fg,rgbcol_t bg)289 static image_c *CreateDummyImage(const char *name, rgbcol_t fg, rgbcol_t bg)
290 {
291 image_c *rim;
292
293 rim = NewImage(DUMMY_X, DUMMY_Y, (bg == TRANS_PIXEL) ? OPAC_Masked : OPAC_Solid);
294
295 strcpy(rim->name, name);
296
297 rim->source_type = IMSRC_Dummy;
298 rim->source_palette = -1;
299
300 rim->source.dummy.fg = fg;
301 rim->source.dummy.bg = bg;
302
303 return rim;
304 }
305
306
AddImageGraphic(const char * name,image_source_e type,int lump,real_image_container_c & container,const image_c * replaces=NULL)307 static image_c *AddImageGraphic(const char *name, image_source_e type, int lump,
308 real_image_container_c& container,
309 const image_c *replaces = NULL)
310 {
311 /* also used for Sprites and TX/HI stuff */
312
313 int lump_len = W_LumpLength(lump);
314
315 epi::file_c *f = W_OpenLump(lump);
316 SYS_ASSERT(f);
317
318 byte buffer[32];
319
320 f->Read(buffer, sizeof(buffer));
321
322 f->Seek(0, epi::file_c::SEEKPOINT_START);
323
324 // determine info, and whether it is PNG or DOOM_PATCH
325 int width=0, height=0;
326 int offset_x=0, offset_y=0;
327
328 bool is_png = false;
329 bool solid = false;
330
331 if (epi::PNG_IsDataPNG(buffer, lump_len))
332 {
333 is_png = true;
334
335 if (! PNG_GetInfo(f, &width, &height, &solid) ||
336 width <= 0 or height <= 0)
337 {
338 I_Error("Error scanning PNG image in '%s' lump\n", W_GetLumpName(lump));
339 }
340
341 // close it
342 delete f;
343 }
344 else // DOOM PATCH format
345 {
346 patch_t *pat = (patch_t *) buffer;
347
348 width = EPI_LE_S16(pat->width);
349 height = EPI_LE_S16(pat->height);
350 offset_x = EPI_LE_S16(pat->leftoffset);
351 offset_y = EPI_LE_S16(pat->topoffset);
352
353 delete f;
354
355 // do some basic checks
356 // !!! FIXME: identify lump types in wad code.
357 if (width <= 0 || width > 2048 ||
358 height <= 0 || height > 512 ||
359 ABS(offset_x) > 2048 || ABS(offset_y) > 1024)
360 {
361 // check for Heretic/Hexen images, which are raw 320x200
362 if (lump_len == 320*200 && type == IMSRC_Graphic)
363 {
364 image_c *rim = NewImage(320, 200, OPAC_Solid);
365 strcpy(rim->name, name);
366
367 rim->source_type = IMSRC_Raw320x200;
368 rim->source.flat.lump = lump;
369 rim->source_palette = W_GetPaletteForLump(lump);
370 return rim;
371 }
372
373 if (lump_len == 64*64 || lump_len == 64*65 || lump_len == 64*128)
374 I_Warning("Graphic '%s' seems to be a flat.\n", name);
375 else
376 I_Warning("Graphic '%s' does not seem to be a graphic.\n", name);
377
378 return NULL;
379 }
380 }
381
382 // create new image
383 image_c *rim = NewImage(width, height, solid ? OPAC_Solid : OPAC_Unknown);
384
385 rim->offset_x = offset_x;
386 rim->offset_y = offset_y;
387
388 strcpy(rim->name, name);
389
390 rim->source_type = type;
391 rim->source.graphic.lump = lump;
392 rim->source.graphic.is_png = is_png;
393 rim->source_palette = W_GetPaletteForLump(lump);
394
395 if (replaces)
396 {
397 rim->scale_x = replaces->actual_w / (float)width;
398 rim->scale_y = replaces->actual_h / (float)height;
399
400 if (is_png && replaces->source_type == IMSRC_Sprite)
401 {
402 rim->offset_x = replaces->offset_x;
403 rim->offset_y = replaces->offset_y;
404 }
405 }
406
407 container.push_back(rim);
408
409 return rim;
410 }
411
412
AddImageTexture(const char * name,texturedef_t * tdef)413 static image_c *AddImageTexture(const char *name, texturedef_t *tdef)
414 {
415 image_c *rim;
416
417 rim = NewImage(tdef->width, tdef->height);
418
419 strcpy(rim->name, name);
420
421 if (tdef->scale_x) rim->scale_x = 8.0 / tdef->scale_x;
422 if (tdef->scale_y) rim->scale_y = 8.0 / tdef->scale_y;
423
424 rim->source_type = IMSRC_Texture;
425 rim->source.texture.tdef = tdef;
426 rim->source_palette = tdef->palette_lump;
427
428 real_textures.push_back(rim);
429
430 return rim;
431 }
432
AddImageFlat(const char * name,int lump)433 static image_c *AddImageFlat(const char *name, int lump)
434 {
435 image_c *rim;
436 int len, size;
437
438 len = W_LumpLength(lump);
439
440 switch (len)
441 {
442 case 64 * 64: size = 64; break;
443
444 // support for odd-size Heretic flats
445 case 64 * 65: size = 64; break;
446
447 // support for odd-size Hexen flats
448 case 64 * 128: size = 64; break;
449
450 // -- EDGE feature: bigger than normal flats --
451 case 128 * 128: size = 128; break;
452 case 256 * 256: size = 256; break;
453 case 512 * 512: size = 512; break;
454 case 1024 * 1024: size = 1024; break;
455
456 default:
457 return NULL;
458 }
459
460 rim = NewImage(size, size, OPAC_Solid);
461
462 strcpy(rim->name, name);
463
464 rim->source_type = IMSRC_Flat;
465 rim->source.flat.lump = lump;
466 rim->source_palette = W_GetPaletteForLump(lump);
467
468 real_flats.push_back(rim);
469
470 return rim;
471 }
472
473
AddImageUser(imagedef_c * def)474 static image_c *AddImageUser(imagedef_c *def)
475 {
476 int w, h;
477 bool solid;
478
479 switch (def->type)
480 {
481 case IMGDT_Colour:
482 w = h = 8;
483 solid = true;
484 break;
485
486 case IMGDT_Builtin:
487 //!!!!! (detail_level == 2) ? 512 : 256;
488 w = 256;
489 h = 256;
490 solid = false;
491 break;
492
493 case IMGDT_File:
494 case IMGDT_Lump:
495 {
496 const char *basename = def->info.c_str();
497
498 epi::file_c *f = OpenUserFileOrLump(def);
499
500 if (! f)
501 {
502 I_Warning("Unable to add image %s: %s\n",
503 (def->type == IMGDT_Lump) ? "lump" : "file", basename);
504 return NULL;
505 }
506
507 bool got_info;
508
509 if (def->format == LIF_JPEG)
510 got_info = epi::JPEG_GetInfo(f, &w, &h, &solid);
511 else if (def->format == LIF_TGA)
512 got_info = epi::TGA_GetInfo(f, &w, &h, &solid);
513 else
514 got_info = epi::PNG_GetInfo(f, &w, &h, &solid);
515
516 if (! got_info)
517 I_Error("Error occurred scanning image: %s\n", basename);
518
519 CloseUserFileOrLump(def, f);
520 #if 1
521 L_WriteDebug("GETINFO [%s] : size %dx%d\n", def->name.c_str(), w, h);
522 #endif
523 }
524 break;
525
526 default:
527 I_Error("AddImageUser: Coding error, unknown type %d\n", def->type);
528 return NULL; /* NOT REACHED */
529 }
530
531 image_c *rim = NewImage(w, h, solid ? OPAC_Solid : OPAC_Unknown);
532
533 rim->offset_x = def->x_offset;
534 rim->offset_y = def->y_offset;
535
536 rim->scale_x = def->scale * def->aspect;
537 rim->scale_y = def->scale;
538
539 strcpy(rim->name, def->name.c_str());
540
541 /* FIX NAME : replace space with '_' */
542 for (int i = 0; rim->name[i]; i++)
543 if (rim->name[i] == ' ')
544 rim->name[i] = '_';
545
546 rim->source_type = IMSRC_User;
547 rim->source.user.def = def;
548
549 if (def->special & IMGSP_Crosshair)
550 {
551 float dy = (200.0f - rim->actual_h * rim->scale_y) / 2.0f - WEAPONTOP;
552
553 rim->offset_y += int(dy / rim->scale_y);
554 }
555
556 switch (def->belong)
557 {
558 case INS_Graphic: real_graphics.push_back(rim); break;
559 case INS_Texture: real_textures.push_back(rim); break;
560 case INS_Flat: real_flats. push_back(rim); break;
561 case INS_Sprite: real_sprites. push_back(rim); break;
562
563 default:
564 I_Error("INTERNAL ERROR: Bad belong value: %d\n", def->belong);
565 }
566
567 return rim;
568 }
569
570
571 //
572 // Used to fill in the image array with flats from the WAD. The set
573 // of lumps is those that occurred between F_START and F_END in each
574 // existing wad file, with duplicates set to -1.
575 //
576 // NOTE: should only be called once, as it assumes none of the flats
577 // in the list have names colliding with existing flat images.
578 //
W_ImageCreateFlats(int * lumps,int number)579 void W_ImageCreateFlats(int *lumps, int number)
580 {
581 int i;
582
583 SYS_ASSERT(lumps);
584
585 for (i=0; i < number; i++)
586 {
587 if (lumps[i] < 0)
588 continue;
589
590 AddImageFlat(W_GetLumpName(lumps[i]), lumps[i]);
591 }
592 }
593
594
595 //
596 // Used to fill in the image array with textures from the WAD. The
597 // list of texture definitions comes from each TEXTURE1/2 lump in each
598 // existing wad file, with duplicates set to NULL.
599 //
600 // NOTE: should only be called once, as it assumes none of the
601 // textures in the list have names colliding with existing texture
602 // images.
603 //
W_ImageCreateTextures(struct texturedef_s ** defs,int number)604 void W_ImageCreateTextures(struct texturedef_s ** defs, int number)
605 {
606 int i;
607
608 SYS_ASSERT(defs);
609
610 for (i=0; i < number; i++)
611 {
612 if (defs[i] == NULL)
613 continue;
614
615 AddImageTexture(defs[i]->name, defs[i]);
616 }
617 }
618
619
620 //
621 // Used to fill in the image array with sprites from the WAD. The
622 // lumps come from those occurring between S_START and S_END markers
623 // in each existing wad.
624 //
625 // NOTE: it is assumed that each new sprite is unique i.e. the name
626 // does not collide with any existing sprite image.
627 //
W_ImageCreateSprite(const char * name,int lump,bool is_weapon)628 const image_c *W_ImageCreateSprite(const char *name, int lump, bool is_weapon)
629 {
630 SYS_ASSERT(lump >= 0);
631
632 image_c *rim = AddImageGraphic(name, IMSRC_Sprite, lump, real_sprites);
633 if (! rim)
634 return NULL;
635
636 // adjust sprite offsets so that (0,0) is normal
637 if (is_weapon)
638 {
639 rim->offset_x += (320 / 2 - rim->actual_w / 2); // loss of accuracy
640 rim->offset_y += (200 - 32 - rim->actual_h);
641 }
642 else
643 {
644 rim->offset_x -= rim->actual_w / 2; // loss of accuracy
645 rim->offset_y -= rim->actual_h;
646 }
647
648 return rim;
649 }
650
651
652 //
653 // Add the images defined in IMAGES.DDF.
654 //
W_ImageCreateUser(void)655 void W_ImageCreateUser(void)
656 {
657 for (int i = 0; i < imagedefs.GetSize(); i++)
658 {
659 imagedef_c* def = imagedefs[i];
660
661 if (def)
662 AddImageUser(def);
663
664 E_LocalProgress(i, imagedefs.GetSize());
665 }
666
667 #if 0
668 L_WriteDebug("Textures -----------------------------\n");
669 do_DebugDump(real_textures);
670
671 L_WriteDebug("Flats ------------------------------\n");
672 do_DebugDump(real_flats);
673
674 L_WriteDebug("Sprites ------------------------------\n");
675 do_DebugDump(real_sprites);
676
677 L_WriteDebug("Graphics ------------------------------\n");
678 do_DebugDump(real_graphics);
679 #endif
680 }
681
682
W_ImageAddTX(int lump,const char * name,bool hires)683 void W_ImageAddTX(int lump, const char *name, bool hires)
684 {
685 if (hires)
686 {
687 const image_c *rim = do_Lookup(real_textures, name, -2);
688 if (rim && rim->source_type != IMSRC_User)
689 {
690 AddImageGraphic(name, IMSRC_TX_HI, lump, real_textures, rim);
691 return;
692 }
693
694 rim = do_Lookup(real_flats, name, -2);
695 if (rim && rim->source_type != IMSRC_User)
696 {
697 AddImageGraphic(name, IMSRC_TX_HI, lump, real_flats, rim);
698 return;
699 }
700
701 rim = do_Lookup(real_sprites, name, -2);
702 if (rim && rim->source_type != IMSRC_User)
703 {
704 AddImageGraphic(name, IMSRC_TX_HI, lump, real_sprites, rim);
705 return;
706 }
707
708 // we do it this way to force the original graphic to be loaded
709 rim = W_ImageLookup(name, INS_Graphic, ILF_Exact|ILF_Null);
710
711 if (rim && rim->source_type != IMSRC_User)
712 {
713 AddImageGraphic(name, IMSRC_TX_HI, lump, real_graphics, rim);
714 return;
715 }
716
717 I_Warning("HIRES replacement '%s' has no counterpart.\n", name);
718 }
719
720 AddImageGraphic(name, IMSRC_TX_HI, lump, real_textures);
721 }
722
723
724 //
725 // Only used during sprite initialisation. The returned array of
726 // images is guaranteed to be sorted by name.
727 //
728 // Use delete[] to free the returned array.
729 //
W_ImageGetUserSprites(int * count)730 const image_c ** W_ImageGetUserSprites(int *count)
731 {
732 // count number of user sprites
733 (*count) = 0;
734
735 real_image_container_c::iterator it;
736
737 for (it = real_sprites.begin(); it != real_sprites.end(); it++)
738 {
739 image_c *rim = *it;
740
741 if (rim->source_type == IMSRC_User)
742 (*count) += 1;
743 }
744
745 if (*count == 0)
746 {
747 L_WriteDebug("W_ImageGetUserSprites(count = %d)\n", *count);
748 return NULL;
749 }
750
751 const image_c ** array = new const image_c *[*count];
752 int pos = 0;
753
754 for (it = real_sprites.begin(); it != real_sprites.end(); it++)
755 {
756 image_c *rim = *it;
757
758 if (rim->source_type == IMSRC_User)
759 array[pos++] = rim;
760 }
761
762 #define CMP(a, b) (strcmp(W_ImageGetName(a), W_ImageGetName(b)) < 0)
763 QSORT(const image_c *, array, (*count), CUTOFF);
764 #undef CMP
765
766 #if 0 // DEBUGGING
767 L_WriteDebug("W_ImageGetUserSprites(count = %d)\n", *count);
768 L_WriteDebug("{\n");
769
770 for (pos = 0; pos < *count; pos++)
771 L_WriteDebug(" %p = [%s] %dx%d\n", array[pos], W_ImageGetName(array[pos]),
772 array[pos]->actual_w, array[pos]->actual_h);
773
774 L_WriteDebug("}\n");
775 #endif
776
777 return array;
778 }
779
780
781 //----------------------------------------------------------------------------
782 //
783 // IMAGE LOADING / UNLOADING
784 //
785
786 // TODO: make methods of image_c class
IM_ShouldClamp(const image_c * rim)787 static bool IM_ShouldClamp(const image_c *rim)
788 {
789 switch (rim->source_type)
790 {
791 case IMSRC_Graphic:
792 case IMSRC_Raw320x200:
793 case IMSRC_Sprite:
794 return true;
795
796 case IMSRC_User:
797 switch (rim->source.user.def->belong)
798 {
799 case INS_Graphic:
800 case INS_Sprite:
801 return true;
802
803 default:
804 return false;
805 }
806
807 default:
808 return false;
809 }
810 }
811
IM_ShouldMipmap(image_c * rim)812 static bool IM_ShouldMipmap(image_c *rim)
813 {
814 // the "SKY" check here is a hack...
815 if (strnicmp(rim->name, "SKY", 3) == 0)
816 return false;
817
818 switch (rim->source_type)
819 {
820 case IMSRC_Texture:
821 case IMSRC_Flat:
822 case IMSRC_TX_HI:
823 return true;
824
825 case IMSRC_User:
826 switch (rim->source.user.def->belong)
827 {
828 case INS_Texture:
829 case INS_Flat:
830 return true;
831
832 default:
833 return false;
834 }
835
836 default:
837 return false;
838 }
839 }
840
IM_ShouldSmooth(image_c * rim)841 static bool IM_ShouldSmooth(image_c *rim)
842 {
843 // the "SKY" check here is a hack...
844 if (strnicmp(rim->name, "SKY", 3) == 0)
845 return true;
846
847 // TODO: more smooth options
848
849 return var_smoothing ? true : false;
850 }
851
IM_ShouldHQ2X(image_c * rim)852 static bool IM_ShouldHQ2X(image_c *rim)
853 {
854 // Note: no need to check IMSRC_User, since those images are
855 // always PNG or JPEG (etc) and never palettised, hence
856 // the Hq2x scaling would never apply.
857
858 if (hq2x_scaling == 0)
859 return false;
860
861 if (hq2x_scaling >= 3)
862 return true;
863
864 switch (rim->source_type)
865 {
866 case IMSRC_Graphic:
867 case IMSRC_Raw320x200:
868 // UI elements
869 return true;
870 #if 0
871 case IMSRC_Texture:
872 // the "SKY" check here is a hack...
873 if (strnicmp(rim->name, "SKY", 3) == 0)
874 return true;
875 break;
876 #endif
877 case IMSRC_Sprite:
878 if (hq2x_scaling >= 2)
879 return true;
880 break;
881
882 default:
883 break;
884 }
885
886 return false;
887 }
888
IM_PixelLimit(image_c * rim)889 static int IM_PixelLimit(image_c *rim)
890 {
891 if (IM_ShouldMipmap(rim))
892 return 65536 * (1 << (2 * detail_level));
893
894 return (1 << 24);
895 }
896
897
LoadImageOGL(image_c * rim,const colourmap_c * trans)898 static GLuint LoadImageOGL(image_c *rim, const colourmap_c *trans)
899 {
900 bool clamp = IM_ShouldClamp(rim);
901 bool mip = IM_ShouldMipmap(rim);
902 bool smooth = IM_ShouldSmooth(rim);
903
904 int max_pix = IM_PixelLimit(rim);
905
906 if (rim->source_type == IMSRC_User)
907 {
908 if (rim->source.user.def->special & IMGSP_Clamp)
909 clamp = true;
910
911 if (rim->source.user.def->special & IMGSP_Mip)
912 mip = true;
913 else if (rim->source.user.def->special & IMGSP_NoMip)
914 mip = false;
915
916 if (rim->source.user.def->special & IMGSP_Smooth)
917 smooth = true;
918 else if (rim->source.user.def->special & IMGSP_NoSmooth)
919 smooth = false;
920 }
921
922
923 const byte *what_palette = (const byte *) &playpal_data[0];
924 bool what_pal_cached = false;
925
926 static byte trans_pal[256 * 3];
927
928 if (trans != NULL)
929 {
930 // Note: we don't care about source_palette here. It's likely that
931 // the translation table itself would not match the other palette,
932 // and so we would still end up with messed up colours.
933
934 R_TranslatePalette(trans_pal, what_palette, trans);
935 what_palette = trans_pal;
936 }
937 else if (rim->source_palette >= 0)
938 {
939 what_palette = (const byte *) W_CacheLumpNum(rim->source_palette);
940 what_pal_cached = true;
941 }
942
943
944 epi::image_data_c *tmp_img = ReadAsEpiBlock(rim);
945
946 if (rim->opacity == OPAC_Unknown)
947 rim->opacity = R_DetermineOpacity(tmp_img);
948
949 if ((tmp_img->bpp == 1) && IM_ShouldHQ2X(rim))
950 {
951 bool solid = (rim->opacity == OPAC_Solid);
952
953 epi::Hq2x::Setup(what_palette, solid ? -1 : TRANS_PIXEL);
954
955 epi::image_data_c *scaled_img =
956 epi::Hq2x::Convert(tmp_img, solid, false /* invert */);
957
958 delete tmp_img;
959 tmp_img = scaled_img;
960 }
961 else if (tmp_img->bpp == 1)
962 {
963 epi::image_data_c *rgb_img =
964 R_PalettisedToRGB(tmp_img, what_palette, rim->opacity);
965
966 delete tmp_img;
967 tmp_img = rgb_img;
968 }
969 else if (tmp_img->bpp >= 3 && trans != NULL)
970 {
971 if (trans == font_whiten_map)
972 tmp_img->Whiten();
973 else
974 R_PaletteRemapRGBA(tmp_img, what_palette, (const byte *) &playpal_data[0]);
975 }
976
977
978 GLuint tex_id = R_UploadTexture(tmp_img,
979 (clamp ? UPL_Clamp : 0) |
980 (mip ? UPL_MipMap : 0) |
981 (smooth ? UPL_Smooth : 0) |
982 ((rim->opacity == OPAC_Masked) ? UPL_Thresh : 0), max_pix);
983
984 delete tmp_img;
985
986 if (what_pal_cached)
987 W_DoneWithLump(what_palette);
988
989 return tex_id;
990 }
991
992
993
994
995 #if 0
996 static
997 void UnloadImageOGL(cached_image_t *rc, image_c *rim)
998 {
999 glDeleteTextures(1, &rc->tex_id);
1000
1001 for (unsigned int i = 0; i < rim->cache.size(); i++)
1002 {
1003 if (rim->cache[i] == rc)
1004 {
1005 rim->cache[i] = NULL;
1006 return;
1007 }
1008 }
1009
1010 I_Error("INTERNAL ERROR: UnloadImageOGL: no such RC in cache !\n");
1011 }
1012
1013
1014 //
1015 // UnloadImage
1016 //
1017 // Unloads a cached image from the cache list and frees all resources.
1018 // Mainly just a switch to more specialised image unloaders.
1019 //
1020 static void UnloadImage(cached_image_t *rc)
1021 {
1022 image_c *rim = rc->parent;
1023
1024 SYS_ASSERT(rc);
1025 SYS_ASSERT(rc != &imagecachehead);
1026 SYS_ASSERT(rim);
1027
1028 // unlink from the cache list
1029 Unlink(rc);
1030
1031 UnloadImageOGL(rc, rim);
1032
1033 delete rc;
1034 }
1035 #endif
1036
1037
1038 //----------------------------------------------------------------------------
1039 // IMAGE LOOKUP
1040 //----------------------------------------------------------------------------
1041
1042 //
1043 // BackupTexture
1044 //
BackupTexture(const char * tex_name,int flags)1045 static const image_c *BackupTexture(const char *tex_name, int flags)
1046 {
1047 const image_c *rim;
1048
1049 // backup plan: try a flat with the same name
1050 if (! (flags & ILF_Exact))
1051 {
1052 rim = do_Lookup(real_flats, tex_name);
1053 if (rim)
1054 return rim;
1055 }
1056
1057 if (flags & ILF_Null)
1058 return NULL;
1059
1060 M_WarnError("Unknown texture found in level: '%s'\n", tex_name);
1061
1062 image_c *dummy;
1063
1064 if (strnicmp(tex_name, "SKY", 3) == 0)
1065 dummy = CreateDummyImage(tex_name, 0x0000AA, 0x55AADD);
1066 else
1067 dummy = CreateDummyImage(tex_name, 0xAA5511, 0x663300);
1068
1069 // keep dummy texture so that future lookups will succeed
1070 real_textures.push_back(dummy);
1071 return dummy;
1072 }
1073
1074 //
1075 // BackupFlat
1076 //
BackupFlat(const char * flat_name,int flags)1077 static const image_c *BackupFlat(const char *flat_name, int flags)
1078 {
1079 const image_c *rim;
1080
1081 // backup plan 1: if lump exists and is right size, add it.
1082 if (! (flags & ILF_NoNew))
1083 {
1084 int i = W_CheckNumForName(flat_name);
1085
1086 if (i >= 0)
1087 {
1088 rim = AddImageFlat(flat_name, i);
1089 if (rim)
1090 return rim;
1091 }
1092 }
1093
1094 // backup plan 2: Texture with the same name ?
1095 if (! (flags & ILF_Exact))
1096 {
1097 rim = do_Lookup(real_textures, flat_name);
1098 if (rim)
1099 return rim;
1100 }
1101
1102 if (flags & ILF_Null)
1103 return NULL;
1104
1105 M_WarnError("Unknown flat found in level: '%s'\n", flat_name);
1106
1107 image_c *dummy = CreateDummyImage(flat_name, 0x11AA11, 0x115511);
1108
1109 // keep dummy flat so that future lookups will succeed
1110 real_flats.push_back(dummy);
1111 return dummy;
1112 }
1113
1114 //
1115 // BackupGraphic
1116 //
BackupGraphic(const char * gfx_name,int flags)1117 static const image_c *BackupGraphic(const char *gfx_name, int flags)
1118 {
1119 const image_c *rim;
1120
1121 // backup plan 1: look for sprites and heretic-background
1122 if ((flags & (ILF_Exact | ILF_Font)) == 0)
1123 {
1124 rim = do_Lookup(real_graphics, gfx_name, IMSRC_Raw320x200);
1125 if (rim)
1126 return rim;
1127
1128 rim = do_Lookup(real_sprites, gfx_name);
1129 if (rim)
1130 return rim;
1131 }
1132
1133 // not already loaded ? Check if lump exists in wad, if so add it.
1134 if (! (flags & ILF_NoNew))
1135 {
1136 int i = W_CheckNumForName_GFX(gfx_name);
1137
1138 if (i >= 0)
1139 {
1140 rim = AddImageGraphic(gfx_name, IMSRC_Graphic, i, real_graphics);
1141 if (rim)
1142 return rim;
1143 }
1144 }
1145
1146 if (flags & ILF_Null)
1147 return NULL;
1148
1149 M_WarnError("Unknown graphic: '%s'\n", gfx_name);
1150
1151 image_c *dummy;
1152
1153 if (flags & ILF_Font)
1154 dummy = CreateDummyImage(gfx_name, 0xFFFFFF, TRANS_PIXEL);
1155 else
1156 dummy = CreateDummyImage(gfx_name, 0xFF0000, TRANS_PIXEL);
1157
1158 // keep dummy graphic so that future lookups will succeed
1159 real_graphics.push_back(dummy);
1160 return dummy;
1161 }
1162
1163
BackupSprite(const char * spr_name,int flags)1164 static const image_c *BackupSprite(const char *spr_name, int flags)
1165 {
1166 if (flags & ILF_Null)
1167 return NULL;
1168
1169 return W_ImageForDummySprite();
1170 }
1171
1172
W_ImageLookup(const char * name,image_namespace_e type,int flags)1173 const image_c *W_ImageLookup(const char *name, image_namespace_e type, int flags)
1174 {
1175 //
1176 // Note: search must be case insensitive.
1177 //
1178
1179 // "NoTexture" marker.
1180 if (!name || !name[0] || name[0] == '-')
1181 return NULL;
1182
1183 // "Sky" marker.
1184 if (type == INS_Flat &&
1185 (stricmp(name, "F_SKY1") == 0 ||
1186 stricmp(name, "F_SKY") == 0))
1187 {
1188 return skyflatimage;
1189 }
1190
1191 // compatibility hack (first texture in IWAD is a dummy)
1192 if (type == INS_Texture &&
1193 ( (stricmp(name, "AASTINKY") == 0) ||
1194 (stricmp(name, "AASHITTY") == 0) ||
1195 (stricmp(name, "BADPATCH") == 0) ||
1196 (stricmp(name, "ABADONE") == 0)))
1197 {
1198 return NULL;
1199 }
1200
1201 const image_c *rim;
1202
1203 if (type == INS_Texture)
1204 {
1205 rim = do_Lookup(real_textures, name);
1206 return rim ? rim : BackupTexture(name, flags);
1207 }
1208 if (type == INS_Flat)
1209 {
1210 rim = do_Lookup(real_flats, name);
1211 return rim ? rim : BackupFlat(name, flags);
1212 }
1213 if (type == INS_Sprite)
1214 {
1215 rim = do_Lookup(real_sprites, name);
1216 return rim ? rim : BackupSprite(name, flags);
1217 }
1218
1219 /* INS_Graphic */
1220
1221 rim = do_Lookup(real_graphics, name);
1222 return rim ? rim : BackupGraphic(name, flags);
1223 }
1224
1225
W_ImageForDummySprite(void)1226 const image_c *W_ImageForDummySprite(void)
1227 {
1228 return dummy_sprite;
1229 }
1230
W_ImageForDummySkin(void)1231 const image_c *W_ImageForDummySkin(void)
1232 {
1233 return dummy_skin;
1234 }
1235
W_ImageForHOMDetect(void)1236 const image_c *W_ImageForHOMDetect(void)
1237 {
1238 return dummy_hom[(framecount & 0x10) ? 1 : 0];
1239 }
1240
1241
W_ImageParseSaveString(char type,const char * name)1242 const image_c *W_ImageParseSaveString(char type, const char *name)
1243 {
1244 // Used by the savegame code.
1245
1246 // this name represents the sky (historical reasons)
1247 if (type == 'd' && stricmp(name, "DUMMY__2") == 0)
1248 {
1249 return skyflatimage;
1250 }
1251
1252 switch (type)
1253 {
1254 case 'K':
1255 return skyflatimage;
1256
1257 case 'F':
1258 return W_ImageLookup(name, INS_Flat);
1259
1260 case 'P':
1261 return W_ImageLookup(name, INS_Graphic);
1262
1263 case 'S':
1264 return W_ImageLookup(name, INS_Sprite);
1265
1266 default:
1267 I_Warning("W_ImageParseSaveString: unknown type '%c'\n", type);
1268 /* FALL THROUGH */
1269
1270 case 'd': /* dummy */
1271 case 'T':
1272 return W_ImageLookup(name, INS_Texture);
1273 }
1274
1275 return NULL; /* NOT REACHED */
1276 }
1277
1278
W_ImageMakeSaveString(const image_c * image,char * type,char * namebuf)1279 void W_ImageMakeSaveString(const image_c *image, char *type, char *namebuf)
1280 {
1281 // Used by the savegame code
1282
1283 if (image == skyflatimage)
1284 {
1285 *type = 'K';
1286 strcpy(namebuf, "F_SKY1");
1287 return;
1288 }
1289
1290 const image_c *rim = (const image_c *) image;
1291
1292 strcpy(namebuf, rim->name);
1293
1294 /* handle User images (convert to a more general type) */
1295 if (rim->source_type == IMSRC_User)
1296 {
1297 switch (rim->source.user.def->belong)
1298 {
1299 case INS_Texture: (*type) = 'T'; return;
1300 case INS_Flat: (*type) = 'F'; return;
1301 case INS_Sprite: (*type) = 'S'; return;
1302
1303 default: /* INS_Graphic */
1304 (*type) = 'P';
1305 return;
1306 }
1307 }
1308
1309 switch (rim->source_type)
1310 {
1311 case IMSRC_Raw320x200:
1312 case IMSRC_Graphic: (*type) = 'P'; return;
1313
1314 case IMSRC_TX_HI:
1315 case IMSRC_Texture: (*type) = 'T'; return;
1316
1317 case IMSRC_Flat: (*type) = 'F'; return;
1318
1319 case IMSRC_Sprite: (*type) = 'S'; return;
1320
1321 case IMSRC_Dummy: (*type) = 'd'; return;
1322
1323 default:
1324 I_Error("W_ImageMakeSaveString: bad type %d\n", rim->source_type);
1325 break;
1326 }
1327 }
1328
1329
W_ImageGetName(const image_c * image)1330 const char *W_ImageGetName(const image_c *image)
1331 {
1332 const image_c *rim;
1333
1334 rim = (const image_c *) image;
1335
1336 return rim->name;
1337 }
1338
1339
1340 //----------------------------------------------------------------------------
1341 //
1342 // IMAGE USAGE
1343 //
1344
ImageCacheOGL(image_c * rim,const colourmap_c * trans)1345 static cached_image_t *ImageCacheOGL(image_c *rim,
1346 const colourmap_c *trans)
1347 {
1348 // check if image + translation is already cached
1349
1350 int free_slot = -1;
1351
1352 cached_image_t *rc = NULL;
1353
1354 for (int i = 0; i < (int)rim->cache.size(); i++)
1355 {
1356 rc = rim->cache[i];
1357
1358 if (! rc)
1359 {
1360 free_slot = i;
1361 continue;
1362 }
1363
1364 if (rc->trans_map == trans)
1365 break;
1366
1367 rc = NULL;
1368 }
1369
1370 if (! rc)
1371 {
1372 // add entry into cache
1373 rc = new cached_image_t;
1374
1375 rc->parent = rim;
1376 rc->trans_map = trans;
1377 rc->hue = RGB_NO_VALUE;
1378 rc->tex_id = 0;
1379
1380 InsertAtTail(rc);
1381
1382 if (free_slot >= 0)
1383 rim->cache[free_slot] = rc;
1384 else
1385 rim->cache.push_back(rc);
1386 }
1387
1388 SYS_ASSERT(rc);
1389
1390 #if 0 // REMOVE
1391 if (rc->invalidated)
1392 {
1393 SYS_ASSERT(rc->tex_id != 0);
1394
1395 glDeleteTextures(1, &rc->tex_id);
1396
1397 rc->tex_id = 0;
1398 rc->invalidated = false;
1399 }
1400 #endif
1401
1402 if (rc->tex_id == 0)
1403 {
1404 // load image into cache
1405 rc->tex_id = LoadImageOGL(rim, trans);
1406 }
1407
1408 return rc;
1409 }
1410
1411
1412 //
1413 // The top-level routine for caching in an image. Mainly just a
1414 // switch to more specialised routines.
1415 //
W_ImageCache(const image_c * image,bool anim,const colourmap_c * trans)1416 GLuint W_ImageCache(const image_c *image, bool anim,
1417 const colourmap_c *trans)
1418 {
1419 // Intentional Const Override
1420 image_c *rim = (image_c *) image;
1421
1422 // handle animations
1423 if (anim)
1424 rim = rim->anim.cur;
1425
1426 cached_image_t *rc = ImageCacheOGL(rim, trans);
1427
1428 SYS_ASSERT(rc->parent);
1429
1430 return rc->tex_id;
1431 }
1432
1433
1434 #if 0
1435 rgbcol_t W_ImageGetHue(const image_c *img)
1436 {
1437 SYS_ASSERT(c);
1438
1439 // Intentional Const Override
1440 cached_image_t *rc = ((cached_image_t *) c) - 1;
1441
1442 SYS_ASSERT(rc->parent);
1443
1444 return rc->hue;
1445 }
1446 #endif
1447
1448
W_ImagePreCache(const image_c * image)1449 void W_ImagePreCache(const image_c *image)
1450 {
1451 W_ImageCache(image, false);
1452
1453 // Intentional Const Override
1454 image_c *rim = (image_c *) image;
1455
1456 // pre-cache alternative images for switches too
1457 if (strlen(rim->name) >= 4 &&
1458 (strnicmp(rim->name, "SW1", 3) == 0 ||
1459 strnicmp(rim->name, "SW2", 3) == 0 ))
1460 {
1461 char alt_name[16];
1462
1463 strcpy(alt_name, rim->name);
1464 alt_name[2] = (alt_name[2] == '1') ? '2' : '1';
1465
1466 image_c *alt = do_Lookup(real_textures, alt_name);
1467
1468 if (alt) W_ImageCache(alt, false);
1469 }
1470 }
1471
1472
1473 //----------------------------------------------------------------------------
1474
W_CreateDummyImages(void)1475 static void W_CreateDummyImages(void)
1476 {
1477 dummy_sprite = CreateDummyImage("DUMMY_SPRITE", 0xFFFF00, TRANS_PIXEL);
1478 dummy_skin = CreateDummyImage("DUMMY_SKIN", 0xFF77FF, 0x993399);
1479
1480 skyflatimage = CreateDummyImage("DUMMY_SKY", 0x0000AA, 0x55AADD);
1481
1482 dummy_hom[0] = CreateDummyImage("DUMMY_HOM1", 0xFF3333, 0x000000);
1483 dummy_hom[1] = CreateDummyImage("DUMMY_HOM2", 0x000000, 0xFF3333);
1484
1485 // make the dummy sprite easier to see
1486 {
1487 // Intentional Const Override
1488 image_c *dsp = (image_c *) dummy_sprite;
1489
1490 dsp->scale_x = 3.0f;
1491 dsp->scale_y = 3.0f;
1492 }
1493 }
1494
1495
1496 //
1497 // Initialises the image system.
1498 //
W_InitImages(void)1499 bool W_InitImages(void)
1500 {
1501 // check options
1502 if (M_CheckParm("-nosmoothing"))
1503 var_smoothing = 0;
1504 else if (M_CheckParm("-smoothing"))
1505 var_smoothing = 1;
1506
1507 if (M_CheckParm("-nomipmap"))
1508 var_mipmapping = 0;
1509 else if (M_CheckParm("-mipmap"))
1510 var_mipmapping = 1;
1511 else if (M_CheckParm("-trilinear"))
1512 var_mipmapping = 2;
1513
1514 M_CheckBooleanParm("dither", &var_dithering, false);
1515
1516 W_CreateDummyImages();
1517
1518 return true;
1519 }
1520
1521
1522 //
1523 // Animate all the images.
1524 //
W_UpdateImageAnims(void)1525 void W_UpdateImageAnims(void)
1526 {
1527 do_Animate(real_graphics);
1528 do_Animate(real_textures);
1529 do_Animate(real_flats);
1530 }
1531
1532
W_DeleteAllImages(void)1533 void W_DeleteAllImages(void)
1534 {
1535 std::list<cached_image_t *>::iterator CI;
1536
1537 for (CI = image_cache.begin(); CI != image_cache.end(); CI++)
1538 {
1539 cached_image_t *rc = *CI;
1540 SYS_ASSERT(rc);
1541
1542 if (rc->tex_id != 0)
1543 {
1544 glDeleteTextures(1, &rc->tex_id);
1545 rc->tex_id = 0;
1546 }
1547 }
1548
1549 DeleteSkyTextures();
1550 DeleteColourmapTextures();
1551 }
1552
1553
1554 //
1555 // W_AnimateImageSet
1556 //
1557 // Sets up the images so they will animate properly. Array is
1558 // allowed to contain NULL entries.
1559 //
1560 // NOTE: modifies the input array of images.
1561 //
W_AnimateImageSet(const image_c ** images,int number,int speed)1562 void W_AnimateImageSet(const image_c ** images, int number, int speed)
1563 {
1564 int i, total;
1565 image_c *rim, *other;
1566
1567 SYS_ASSERT(images);
1568 SYS_ASSERT(speed > 0);
1569
1570 // ignore images that are already animating
1571 for (i=0, total=0; i < number; i++)
1572 {
1573 // Intentional Const Override
1574 rim = (image_c *) images[i];
1575
1576 if (! rim)
1577 continue;
1578
1579 if (rim->anim.speed > 0)
1580 continue;
1581
1582 images[total++] = images[i];
1583 }
1584
1585 // anything left to animate ?
1586 if (total < 2)
1587 return;
1588
1589 for (i=0; i < total; i++)
1590 {
1591 // Intentional Const Override
1592 rim = (image_c *) images[i];
1593 other = (image_c *) images[(i+1) % total];
1594
1595 rim->anim.next = other;
1596 rim->anim.speed = rim->anim.count = speed;
1597 }
1598 }
1599
1600 //--- editor settings ---
1601 // vi:ts=4:sw=4:noexpandtab
1602