1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: v_palette.cpp 4686 2014-03-25 15:52:15Z dr_sean $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 2006-2014 by The Odamex Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // DESCRIPTION:
20 //	V_PALETTE
21 //
22 //-----------------------------------------------------------------------------
23 
24 
25 #include <cstring>
26 #include <math.h>
27 #include <cstddef>
28 
29 #include "doomstat.h"
30 #include "v_video.h"
31 #include "v_gamma.h"
32 #include "m_alloc.h"
33 #include "r_main.h"		// For lighting constants
34 #include "w_wad.h"
35 #include "z_zone.h"
36 #include "i_video.h"
37 #include "c_dispatch.h"
38 #include "g_level.h"
39 #include "st_stuff.h"
40 
41 /* Reimplement old way of doing red/gold colors, from Chocolate Doom - ML */
42 
43 // Palette indices.
44 // For damage/bonus red-/gold-shifts
45 #define STARTREDPALS		1
46 #define STARTBONUSPALS		9
47 #define NUMREDPALS			8
48 #define NUMBONUSPALS		4
49 // Radiation suit, green shift.
50 #define RADIATIONPAL		13
51 
52 EXTERN_CVAR(gammalevel)
53 EXTERN_CVAR(vid_gammatype)
54 EXTERN_CVAR(r_painintensity)
55 EXTERN_CVAR(sv_allowredscreen)
56 
57 void BuildColoredLights (byte *maps, int lr, int lg, int lb, int fr, int fg, int fb);
58 static void DoBlending (argb_t *from, argb_t *to, unsigned count, int tor, int tog, int tob, int toa);
59 void V_ForceBlend (int blendr, int blendg, int blendb, int blenda);
60 
61 dyncolormap_t NormalLight;
62 
63 static int lu_palette;
64 static int current_palette;
65 static float current_blend[4];
66 
67 palette_t DefPal;
68 palette_t *FirstPal;
69 
70 argb_t IndexedPalette[256];
71 
72 
73 /* Current color blending values */
74 int		BlendR, BlendG, BlendB, BlendA;
75 
translationref_t()76 translationref_t::translationref_t() : m_table(NULL), m_player_id(-1)
77 {
78 }
79 
translationref_t(const translationref_t & other)80 translationref_t::translationref_t(const translationref_t &other) : m_table(other.m_table), m_player_id(other.m_player_id)
81 {
82 }
83 
translationref_t(const byte * table)84 translationref_t::translationref_t(const byte *table) : m_table(table), m_player_id(-1)
85 {
86 }
87 
translationref_t(const byte * table,const int player_id)88 translationref_t::translationref_t(const byte *table, const int player_id) : m_table(table), m_player_id(player_id)
89 {
90 }
91 
shaderef_t()92 shaderef_t::shaderef_t() : m_colors(NULL), m_mapnum(-1), m_colormap(NULL), m_shademap(NULL)
93 {
94 }
95 
shaderef_t(const shaderef_t & other)96 shaderef_t::shaderef_t(const shaderef_t &other)
97 	: m_colors(other.m_colors), m_mapnum(other.m_mapnum),
98 	  m_colormap(other.m_colormap), m_shademap(other.m_shademap), m_dyncolormap(other.m_dyncolormap)
99 {
100 }
101 
shaderef_t(const shademap_t * const colors,const int mapnum)102 shaderef_t::shaderef_t(const shademap_t * const colors, const int mapnum) : m_colors(colors), m_mapnum(mapnum)
103 {
104 #if DEBUG
105 	// NOTE(jsd): Arbitrary value picked here because we don't record the max number of colormaps for dynamic ones... or do we?
106 	if (m_mapnum >= 8192)
107 	{
108 		char tmp[100];
109 		sprintf_s(tmp, "32bpp: shaderef_t::shaderef_t() called with mapnum = %d, which looks too large", m_mapnum);
110 		throw CFatalError(tmp);
111 	}
112 #endif
113 
114 	if (m_colors != NULL)
115 	{
116 		if (m_colors->colormap != NULL)
117 			m_colormap = m_colors->colormap + (256 * m_mapnum);
118 		else
119 			m_colormap = NULL;
120 
121 		if (m_colors->shademap != NULL)
122 			m_shademap = m_colors->shademap + (256 * m_mapnum);
123 		else
124 			m_shademap = NULL;
125 
126 		// Detect if the colormap is dynamic:
127 		m_dyncolormap = NULL;
128 
129 		extern palette_t DefPal;
130 		if (m_colors != &(DefPal.maps))
131 		{
132 			// Find the dynamic colormap by the `m_colors` pointer:
133 			extern dyncolormap_t NormalLight;
134 			dyncolormap_t *colormap = &NormalLight;
135 
136 			do
137 			{
138 				if (m_colors == colormap->maps.m_colors)
139 				{
140 					m_dyncolormap = colormap;
141 					break;
142 				}
143 				colormap = colormap->next;
144 			} while (colormap);
145 		}
146 	}
147 	else
148 	{
149 		m_colormap = NULL;
150 		m_shademap = NULL;
151 		m_dyncolormap = NULL;
152 	}
153 }
154 
155 /**************************/
156 /* Gamma correction stuff */
157 /**************************/
158 
159 static DoomGammaStrategy doomgammastrat;
160 static ZDoomGammaStrategy zdoomgammastrat;
161 GammaStrategy* gammastrat = &doomgammastrat;
162 
CVAR_FUNC_IMPL(vid_gammatype)163 CVAR_FUNC_IMPL(vid_gammatype)
164 {
165 	if (vid_gammatype == GAMMA_ZDOOM)
166 		gammastrat = &zdoomgammastrat;
167 	else
168 		gammastrat = &doomgammastrat;
169 
170 	gammalevel.Set(gammalevel);
171 }
172 
173 byte newgamma[256];
174 static bool gamma_initialized = false;
175 
V_UpdateGammaLevel(float level)176 static void V_UpdateGammaLevel(float level)
177 {
178 	static float lastgammalevel = 0.0f;
179 	static int lasttype = -1;			// ensure this gets set up the first time
180 	int type = vid_gammatype;
181 
182 	if (lastgammalevel != level || lasttype != type)
183 	{
184 		// Only recalculate the gamma table if the new gamma
185 		// value is different from the old one.
186 
187 		lastgammalevel = level;
188 		lasttype = type;
189 
190 		gammastrat->generateGammaTable(newgamma, level);
191 		GammaAdjustPalettes();
192 
193 		if (!screen)
194 			return;
195 		if (screen->is8bit())
196 			V_ForceBlend(BlendR, BlendG, BlendB, BlendA);
197 		else
198 			RefreshPalettes();
199 	}
200 }
201 
CVAR_FUNC_IMPL(gammalevel)202 CVAR_FUNC_IMPL(gammalevel)
203 {
204 	float sanitized_var = clamp(var.value(), gammastrat->min(), gammastrat->max());
205 	if (var == sanitized_var)
206 		V_UpdateGammaLevel(var);
207 	else
208 		var.Set(sanitized_var);
209 }
210 
BEGIN_COMMAND(bumpgamma)211 BEGIN_COMMAND(bumpgamma)
212 {
213 	gammalevel.Set(gammastrat->increment(gammalevel));
214 
215 	if (gammalevel.value() == gammastrat->min())
216 	    Printf (PRINT_HIGH, "Gamma correction off\n");
217 	else
218 	    Printf (PRINT_HIGH, "Gamma correction level %g\n", gammalevel.value());
219 }
END_COMMAND(bumpgamma)220 END_COMMAND(bumpgamma)
221 
222 
223 // [Russell] - Restore original screen palette from current gamma level
224 void V_RestoreScreenPalette(void)
225 {
226     if (screen && screen->is8bit())
227 		V_ForceBlend(BlendR, BlendG, BlendB, BlendA);
228 }
229 
230 /****************************/
231 /* Palette management stuff */
232 /****************************/
233 
InternalCreatePalette(palette_t * palette,const char * name,byte * colors,unsigned numcolors,unsigned flags)234 bool InternalCreatePalette (palette_t *palette, const char *name, byte *colors,
235 							unsigned numcolors, unsigned flags)
236 {
237 	unsigned i;
238 
239 	if (numcolors > 256)
240 		numcolors = 256;
241 	else if (numcolors == 0)
242 		return false;
243 
244 	strncpy (palette->name.name, name, 8);
245 	palette->flags = flags;
246 	palette->usecount = 1;
247 	palette->maps.colormap = NULL;
248 	palette->maps.shademap = NULL;
249 
250 	M_Free(palette->basecolors);
251 
252 	palette->basecolors = (argb_t *)Malloc (numcolors * 2 * sizeof(argb_t));
253 	palette->colors = palette->basecolors + numcolors;
254 	palette->numcolors = numcolors;
255 
256 	if (numcolors == 1)
257 		palette->shadeshift = 0;
258 	else if (numcolors <= 2)
259 		palette->shadeshift = 1;
260 	else if (numcolors <= 4)
261 		palette->shadeshift = 2;
262 	else if (numcolors <= 8)
263 		palette->shadeshift = 3;
264 	else if (numcolors <= 16)
265 		palette->shadeshift = 4;
266 	else if (numcolors <= 32)
267 		palette->shadeshift = 5;
268 	else if (numcolors <= 64)
269 		palette->shadeshift = 6;
270 	else if (numcolors <= 128)
271 		palette->shadeshift = 7;
272 	else
273 		palette->shadeshift = 8;
274 
275 	for (i = 0; i < numcolors; i++, colors += 3)
276 		palette->basecolors[i] = MAKERGB(colors[0],colors[1],colors[2]);
277 
278 	GammaAdjustPalette (palette);
279 
280 	return true;
281 }
282 
InitPalettes(const char * name)283 palette_t *InitPalettes (const char *name)
284 {
285 	byte *colors;
286 
287 	//if (DefPal.usecount)
288 	//	return &DefPal;
289 
290 	current_palette = -1;
291 	current_blend[0] = current_blend[1] = current_blend[2] = current_blend[3] = 255.0f;
292 
293     lu_palette = W_GetNumForName ("PLAYPAL");
294 
295 	if ( (colors = (byte *)W_CacheLumpName (name, PU_CACHE)) )
296 		if (InternalCreatePalette (&DefPal, name, colors, 256,
297 									PALETTEF_SHADE|PALETTEF_BLEND|PALETTEF_DEFAULT)) {
298 			return &DefPal;
299 		}
300 	return NULL;
301 }
302 
GetDefaultPalette(void)303 palette_t *GetDefaultPalette (void)
304 {
305 	return &DefPal;
306 }
307 
308 // MakePalette()
309 //	input: colors: ptr to 256 3-byte RGB values
310 //		   flags:  the flags for the new palette
311 //
MakePalette(byte * colors,char * name,unsigned flags)312 palette_t *MakePalette (byte *colors, char *name, unsigned flags)
313 {
314 	palette_t *pal;
315 
316 	pal = (palette_t *)Malloc (sizeof (palette_t));
317 
318 	if (InternalCreatePalette (pal, name, colors, 256, flags)) {
319 		pal->next = FirstPal;
320 		pal->prev = NULL;
321 		FirstPal = pal;
322 
323 		return pal;
324 	} else {
325 		M_Free(pal);
326 		return NULL;
327 	}
328 }
329 
330 // LoadPalette()
331 //	input: name:  the name of the palette lump
332 //		   flags: the flags for the palette
333 //
334 //	This function will try and find an already loaded
335 //	palette and return that if possible.
LoadPalette(char * name,unsigned flags)336 palette_t *LoadPalette (char *name, unsigned flags)
337 {
338 	palette_t *pal;
339 
340 	if (!(pal = FindPalette (name, flags))) {
341 		// Palette doesn't already exist. Create a new one.
342 		byte *colors = (byte *)W_CacheLumpName (name, PU_CACHE);
343 
344 		pal = MakePalette (colors, name, flags);
345 	} else {
346 		pal->usecount++;
347 	}
348 	return pal;
349 }
350 
351 // LoadAttachedPalette()
352 //	input: name:  the name of a graphic whose palette should be loaded
353 //		   type:  the type of graphic whose palette is being requested
354 //		   flags: the flags for the palette
355 //
356 //	This function looks through the PALETTES lump for a palette
357 //	associated with the given graphic and returns that if possible.
358 palette_t *LoadAttachedPalette (char *name, int type, unsigned flags);
359 
360 // FreePalette()
361 //	input: palette: the palette to free
362 //
363 //	This function decrements the palette's usecount and frees it
364 //	when it hits zero.
FreePalette(palette_t * palette)365 void FreePalette (palette_t *palette)
366 {
367 	if (!(--palette->usecount)) {
368 		if (!(palette->flags & PALETTEF_DEFAULT)) {
369 			if (!palette->prev)
370 				FirstPal = palette->next;
371 			else
372 				palette->prev->next = palette->next;
373 
374 			M_Free(palette->basecolors);
375 
376 			M_Free(palette->colormapsbase);
377 
378 			M_Free(palette);
379 		}
380 	}
381 }
382 
383 
FindPalette(char * name,unsigned flags)384 palette_t *FindPalette (char *name, unsigned flags)
385 {
386 	palette_t *pal = FirstPal;
387 	union {
388 		char	s[9];
389 		int		x[2];
390 	} name8;
391 
392 	int			v1;
393 	int			v2;
394 
395 	// make the name into two integers for easy compares
396 	strncpy (name8.s,name,8);
397 
398 	v1 = name8.x[0];
399 	v2 = name8.x[1];
400 
401 	while (pal) {
402 		if (pal->name.nameint[0] == v1 && pal->name.nameint[1] == v2) {
403 			if ((flags == (unsigned)~0) || (flags == pal->flags))
404 				return pal;
405 		}
406 		pal = pal->next;
407 	}
408 	return NULL;
409 }
410 
411 
412 // This is based (loosely) on the ColorShiftPalette()
413 // function from the dcolors.c file in the Doom utilities.
DoBlending(argb_t * from,argb_t * to,unsigned count,int tor,int tog,int tob,int toa)414 static void DoBlending (argb_t *from, argb_t *to, unsigned count, int tor, int tog, int tob, int toa)
415 {
416 	if (toa == 0)
417 	{
418 		if (from != to)
419 			memcpy(to, from, count * sizeof(argb_t));
420 	}
421 	else
422 	{
423 		for (unsigned i = 0; i < count; i++)
424 		{
425 			int r = RPART(*from);
426 			int g = GPART(*from);
427 			int b = BPART(*from);
428 			from++;
429 			int dr = tor - r;
430 			int dg = tog - g;
431 			int db = tob - b;
432 			*to++ = MAKERGB (r + ((dr*toa)>>8),
433 							 g + ((dg*toa)>>8),
434 							 b + ((db*toa)>>8));
435 		}
436 	}
437 }
438 
DoBlendingWithGamma(DWORD * from,DWORD * to,unsigned count,int tor,int tog,int tob,int toa)439 static void DoBlendingWithGamma (DWORD *from, DWORD *to, unsigned count, int tor, int tog, int tob, int toa)
440 {
441 	for (unsigned i = 0; i < count; i++)
442 	{
443 		int r = RPART(*from);
444 		int g = GPART(*from);
445 		int b = BPART(*from);
446 		from++;
447 		int dr = tor - r;
448 		int dg = tog - g;
449 		int db = tob - b;
450 		*to++ = MAKERGB (newgamma[r + ((dr*toa)>>8)],
451 						 newgamma[g + ((dg*toa)>>8)],
452 						 newgamma[b + ((db*toa)>>8)]);
453 	}
454 }
455 
lightScale(float a)456 static const float lightScale(float a)
457 {
458 	// NOTE(jsd): Revised inverse logarithmic scale; near-perfect match to COLORMAP lump's scale
459 	// 1 - ((Exp[1] - Exp[a*2 - 1]) / (Exp[1] - Exp[-1]))
460 	static float e1 = exp(1.0f);
461 	static float e1sube0 = e1 - exp(-1.0f);
462 
463 	float newa = clamp(1.0f - (e1 - (float)exp(a * 2.0f - 1.0f)) / e1sube0, 0.0f, 1.0f);
464 	return newa;
465 }
466 
BuildLightRamp(shademap_t & maps)467 void BuildLightRamp (shademap_t &maps)
468 {
469 	int l;
470 	// Build light ramp:
471 	for (l = 0; l < 256; ++l)
472 	{
473 		int a = (int)(255 * lightScale(l / 255.0f));
474 		maps.ramp[l] = a;
475 	}
476 }
477 
BuildDefaultColorAndShademap(palette_t * pal,shademap_t & maps)478 void BuildDefaultColorAndShademap(palette_t *pal, shademap_t &maps)
479 {
480 	const int numcolors = 256;
481 	BuildLightRamp(maps);
482 
483 	// [SL] Modified algorithm from RF_BuildLights in dcolors.c
484 	// from Doom Utilities. Now accomodates fading to non-black colors.
485 
486 	const argb_t* palette = pal->basecolors;
487 	argb_t fadecolor = level.fadeto;
488 
489 	palindex_t* colormap = maps.colormap;
490 	argb_t* shademap = maps.shademap;
491 
492 	for (int i = 0; i < NUMCOLORMAPS; i++, colormap += numcolors, shademap += numcolors)
493 	{
494 		for (int c = 0; c < numcolors; c++)
495 		{
496 			unsigned int r = (RPART(palette[c]) * (NUMCOLORMAPS - i) + RPART(fadecolor) * i
497 					+ NUMCOLORMAPS/2) / NUMCOLORMAPS;
498 			unsigned int g = (GPART(palette[c]) * (NUMCOLORMAPS - i) + GPART(fadecolor) * i
499 					+ NUMCOLORMAPS/2) / NUMCOLORMAPS;
500 			unsigned int b = (BPART(palette[c]) * (NUMCOLORMAPS - i) + BPART(fadecolor) * i
501 					+ NUMCOLORMAPS/2) / NUMCOLORMAPS;
502 
503 			colormap[c] = BestColor(palette, r, g, b, numcolors);
504 			shademap[c] = MAKERGB(newgamma[r], newgamma[g], newgamma[b]);
505 		}
506 	}
507 
508 	// build special maps (e.g. invulnerability)
509 	for (int c = 0; c < numcolors; c++)
510 	{
511 		int grayint = (int)(255.0f * clamp(1.0f -
512 						(RPART(palette[c]) * 0.00116796875f +
513 						 GPART(palette[c]) * 0.00229296875f +
514 			 			 BPART(palette[c]) * 0.0005625f), 0.0f, 1.0f));
515 
516 		colormap[c] = BestColor(palette, grayint, grayint, grayint, numcolors);
517 		shademap[c] = MAKERGB(newgamma[grayint], newgamma[grayint], newgamma[grayint]);
518 	}
519 }
520 
BuildDefaultShademap(palette_t * pal,shademap_t & maps)521 void BuildDefaultShademap(palette_t *pal, shademap_t &maps)
522 {
523 	const int numcolors = 256;
524 	BuildLightRamp(maps);
525 
526 	// [SL] Modified algorithm from RF_BuildLights in dcolors.c
527 	// from Doom Utilities. Now accomodates fading to non-black colors.
528 
529 	const argb_t* palette = pal->basecolors;
530 	argb_t fadecolor = level.fadeto;
531 
532 	argb_t* shademap = maps.shademap;
533 
534 	for (int i = 0; i < NUMCOLORMAPS; i++, shademap += numcolors)
535 	{
536 		for (int c = 0; c < numcolors; c++)
537 		{
538 			unsigned int r = (RPART(palette[c]) * (NUMCOLORMAPS - i) + RPART(fadecolor) * i
539 					+ NUMCOLORMAPS/2) / NUMCOLORMAPS;
540 			unsigned int g = (GPART(palette[c]) * (NUMCOLORMAPS - i) + GPART(fadecolor) * i
541 					+ NUMCOLORMAPS/2) / NUMCOLORMAPS;
542 			unsigned int b = (BPART(palette[c]) * (NUMCOLORMAPS - i) + BPART(fadecolor) * i
543 					+ NUMCOLORMAPS/2) / NUMCOLORMAPS;
544 
545 			shademap[c] = MAKERGB(newgamma[r], newgamma[g], newgamma[b]);
546 		}
547 	}
548 
549 	// build special maps (e.g. invulnerability)
550 	for (int c = 0; c < numcolors; c++)
551 	{
552 		int grayint = (int)(255.0f * clamp(1.0f -
553 						(RPART(palette[c]) * 0.00116796875f +
554 						 GPART(palette[c]) * 0.00229296875f +
555 			 			 BPART(palette[c]) * 0.0005625f), 0.0f, 1.0f));
556 
557 		shademap[c] = MAKERGB(newgamma[grayint], newgamma[grayint], newgamma[grayint]);
558 	}
559 }
560 
RefreshPalette(palette_t * pal)561 void RefreshPalette (palette_t *pal)
562 {
563 	if (pal->flags & PALETTEF_SHADE)
564 	{
565 		if (pal->maps.colormap && pal->maps.colormap - pal->colormapsbase >= 256) {
566 			M_Free(pal->maps.colormap);
567 		}
568 		pal->colormapsbase = (byte *)Realloc (pal->colormapsbase, (NUMCOLORMAPS + 1) * 256 + 255);
569 		pal->maps.colormap = (byte *)(((ptrdiff_t)(pal->colormapsbase) + 255) & ~0xff);
570 		pal->maps.shademap = (argb_t *)Realloc (pal->maps.shademap, (NUMCOLORMAPS + 1)*256*sizeof(argb_t) + 255);
571 
572 		BuildDefaultColorAndShademap(pal, pal->maps);
573 	}
574 
575 	if (pal == &DefPal)
576 	{
577 		NormalLight.maps = shaderef_t(&DefPal.maps, 0);
578 		NormalLight.color = MAKERGB(255,255,255);
579 		NormalLight.fade = level.fadeto;
580 	}
581 }
582 
RefreshPalettes(void)583 void RefreshPalettes (void)
584 {
585 	palette_t *pal = FirstPal;
586 
587 	RefreshPalette (&DefPal);
588 	while (pal) {
589 		RefreshPalette (pal);
590 		pal = pal->next;
591 	}
592 }
593 
594 
GammaAdjustPalette(palette_t * pal)595 void GammaAdjustPalette (palette_t *pal)
596 {
597 	unsigned i, color;
598 
599 	if (!(pal->colors && pal->basecolors))
600 		return;
601 
602 	if (!gamma_initialized)
603 		V_UpdateGammaLevel(gammalevel);
604 
605 	for (i = 0; i < pal->numcolors; i++)
606 	{
607 		color = pal->basecolors[i];
608 		pal->colors[i] = MAKERGB(newgamma[RPART(color)], newgamma[GPART(color)], newgamma[BPART(color)]);
609 	}
610 }
611 
GammaAdjustPalettes(void)612 void GammaAdjustPalettes (void)
613 {
614 	palette_t *pal = FirstPal;
615 
616 	GammaAdjustPalette(&DefPal);
617 	while (pal)
618 	{
619 		GammaAdjustPalette(pal);
620 		pal = pal->next;
621 	}
622 }
623 
624 //
625 // V_AddBlend
626 //
627 // [RH] This is from Q2.
628 //
V_AddBlend(float r,float g,float b,float a,float * v_blend)629 void V_AddBlend(float r, float g, float b, float a, float* v_blend)
630 {
631 	float a2, a3;
632 
633 	if (a <= 0.0f)
634 		return;
635 	a2 = v_blend[3] + (1.0f - v_blend[3]) * a;	// new total alpha
636 	a3 = v_blend[3] / a2;		// fraction of color from old
637 
638 	v_blend[0] = v_blend[0] * a3 + r*(1.0f - a3);
639 	v_blend[1] = v_blend[1] * a3 + g*(1.0f - a3);
640 	v_blend[2] = v_blend[2] * a3 + b*(1.0f - a3);
641 	v_blend[3] = a2;
642 }
643 
V_SetBlend(int blendr,int blendg,int blendb,int blenda)644 void V_SetBlend (int blendr, int blendg, int blendb, int blenda)
645 {
646 	// Don't do anything if the new blend is the same as the old
647 	if ((blenda == 0 && BlendA == 0) ||
648 		(blendr == BlendR &&
649 		 blendg == BlendG &&
650 		 blendb == BlendB &&
651 		 blenda == BlendA))
652 		return;
653 
654 	V_ForceBlend(blendr, blendg, blendb, blenda);
655 }
656 
V_ForceBlend(int blendr,int blendg,int blendb,int blenda)657 void V_ForceBlend (int blendr, int blendg, int blendb, int blenda)
658 {
659 	BlendR = blendr;
660 	BlendG = blendg;
661 	BlendB = blendb;
662 	BlendA = blenda;
663 
664 	// blend the palette for 8-bit mode
665 	// shademap_t::shade takes care of blending
666 	// [SL] actually, an alpha overlay is drawn on top of the rendered screen
667 	// in R_RenderPlayerView
668 	if (screen->is8bit())
669 	{
670 		DoBlending(DefPal.colors, IndexedPalette, DefPal.numcolors,
671 					newgamma[BlendR], newgamma[BlendG], newgamma[BlendB], BlendA);
672 		I_SetPalette(IndexedPalette);
673 	}
674 }
675 
BEGIN_COMMAND(testblend)676 BEGIN_COMMAND (testblend)
677 {
678 	int color;
679 	float amt;
680 
681 	if (argc < 3)
682 	{
683 		Printf (PRINT_HIGH, "testblend <color> <amount>\n");
684 	}
685 	else
686 	{
687 		std::string colorstring = V_GetColorStringByName (argv[1]);
688 
689 		if (colorstring.length())
690 			color = V_GetColorFromString (NULL, colorstring.c_str());
691 		else
692 			color = V_GetColorFromString (NULL, argv[1]);
693 
694 		amt = (float)atof (argv[2]);
695 		if (amt > 1.0f)
696 			amt = 1.0f;
697 		else if (amt < 0.0f)
698 			amt = 0.0f;
699 		//V_SetBlend (RPART(color), GPART(color), BPART(color), (int)(amt * 256.0f));
700 		BaseBlendR = RPART(color);
701 		BaseBlendG = GPART(color);
702 		BaseBlendB = BPART(color);
703 		BaseBlendA = amt;
704 	}
705 }
706 END_COMMAND (testblend)
707 
BEGIN_COMMAND(testfade)708 BEGIN_COMMAND (testfade)
709 {
710 
711 	int color;
712 
713 	if (argc < 2)
714 	{
715 		Printf (PRINT_HIGH, "testfade <color>\n");
716 	}
717 	else
718 	{
719 		std::string colorstring = V_GetColorStringByName (argv[1]);
720 		if (colorstring.length())
721 			color = V_GetColorFromString (NULL, colorstring.c_str());
722 		else
723 			color = V_GetColorFromString (NULL, argv[1]);
724 
725 		level.fadeto = color;
726 		RefreshPalettes();
727 		NormalLight.maps = shaderef_t(&DefPal.maps, 0);
728 	}
729 }
END_COMMAND(testfade)730 END_COMMAND (testfade)
731 
732 /****** Colorspace Conversion Functions ******/
733 
734 // Code from http://www.cs.rit.edu/~yxv4997/t_convert.html
735 
736 // r,g,b values are from 0 to 1
737 // h = [0,360], s = [0,1], v = [0,1]
738 //				if s == 0, then h = -1 (undefined)
739 
740 // Green Doom guy colors:
741 // RGB - 0: {    .46  1 .429 } 7: {    .254 .571 .206 } 15: {    .0317 .0794 .0159 }
742 // HSV - 0: { 116.743 .571 1 } 7: { 112.110 .639 .571 } 15: { 105.071  .800 .0794 }
743 void RGBtoHSV (float r, float g, float b, float *h, float *s, float *v)
744 {
745 	float min, max, delta, foo;
746 
747 	if (r == g && g == b) {
748 		*h = 0;
749 		*s = 0;
750 		*v = r;
751 		return;
752 	}
753 
754 	foo = r < g ? r : g;
755 	min = (foo < b) ? foo : b;
756 	foo = r > g ? r : g;
757 	max = (foo > b) ? foo : b;
758 
759 	*v = max;									// v
760 
761 	delta = max - min;
762 
763 	*s = delta / max;							// s
764 
765 	if (r == max)
766 		*h = (g - b) / delta;					// between yellow & magenta
767 	else if (g == max)
768 		*h = 2 + (b - r) / delta;				// between cyan & yellow
769 	else
770 		*h = 4 + (r - g) / delta;				// between magenta & cyan
771 
772 	*h *= 60;									// degrees
773 	if (*h < 0)
774 		*h += 360;
775 }
776 
HSVtoRGB(float * r,float * g,float * b,float h,float s,float v)777 void HSVtoRGB (float *r, float *g, float *b, float h, float s, float v)
778 {
779 	int i;
780 	float f, p, q, t;
781 
782 	if (s == 0) {
783 		// achromatic (grey)
784 		*r = *g = *b = v;
785 		return;
786 	}
787 
788 	h /= 60;									// sector 0 to 5
789 	i = (int)floor (h);
790 	f = h - i;									// factorial part of h
791 	p = v * (1 - s);
792 	q = v * (1 - s * f);
793 	t = v * (1 - s * (1 - f));
794 
795 	switch (i) {
796 		case 0:		*r = v; *g = t; *b = p; break;
797 		case 1:		*r = q; *g = v; *b = p; break;
798 		case 2:		*r = p; *g = v; *b = t; break;
799 		case 3:		*r = p; *g = q; *b = v; break;
800 		case 4:		*r = t; *g = p; *b = v; break;
801 		default:	*r = v; *g = p; *b = q; break;
802 	}
803 }
804 
805 /****** Colored Lighting Stuffs (Sorry, 8-bit only) ******/
806 
807 // Builds NUMCOLORMAPS colormaps lit with the specified color
BuildColoredLights(shademap_t * maps,int lr,int lg,int lb,int r,int g,int b)808 void BuildColoredLights (shademap_t *maps, int lr, int lg, int lb, int r, int g, int b)
809 {
810 	unsigned int l,c;
811 	byte	*color;
812 	argb_t  *shade;
813 
814 	// The default palette is assumed to contain the maps for white light.
815 	if (!maps)
816 		return;
817 
818 	BuildLightRamp(*maps);
819 
820 	// build normal (but colored) light mappings
821 	for (l = 0; l < NUMCOLORMAPS; l++) {
822 		byte a = maps->ramp[l * 255 / NUMCOLORMAPS];
823 
824 		// Write directly to the shademap for blending:
825 		argb_t *colors = maps->shademap + (256 * l);
826 		DoBlending (DefPal.basecolors, colors, DefPal.numcolors, r, g, b, a);
827 
828 		// Build the colormap and shademap:
829 		color = maps->colormap + 256*l;
830 		shade = maps->shademap + 256*l;
831 		for (c = 0; c < 256; c++) {
832 			shade[c] = MAKERGB(
833 				newgamma[(RPART(colors[c])*lr)/255],
834 				newgamma[(GPART(colors[c])*lg)/255],
835 				newgamma[(BPART(colors[c])*lb)/255]
836 			);
837 			color[c] = BestColor(
838 				DefPal.basecolors,
839 				RPART(shade[c]),
840 				GPART(shade[c]),
841 				BPART(shade[c]),
842 				256
843 			);
844 		}
845 	}
846 }
847 
GetSpecialLights(int lr,int lg,int lb,int fr,int fg,int fb)848 dyncolormap_t *GetSpecialLights (int lr, int lg, int lb, int fr, int fg, int fb)
849 {
850 	unsigned int color = MAKERGB (lr, lg, lb);
851 	unsigned int fade = MAKERGB (fr, fg, fb);
852 	dyncolormap_t *colormap = &NormalLight;
853 
854 	// Bah! Simple linear search because I want to get this done.
855 	while (colormap) {
856 		if (color == colormap->color && fade == colormap->fade)
857 			return colormap;
858 		else
859 			colormap = colormap->next;
860 	}
861 
862 	// Not found. Create it.
863 	colormap = (dyncolormap_t *)Z_Malloc (sizeof(*colormap), PU_LEVEL, 0);
864 	shademap_t *maps = new shademap_t();
865 	maps->colormap = (byte *)Z_Malloc (NUMCOLORMAPS*256*sizeof(byte)+3+255, PU_LEVEL, 0);
866 	maps->colormap = (byte *)(((ptrdiff_t)maps->colormap + 255) & ~0xff);
867 	maps->shademap = (argb_t *)Z_Malloc (NUMCOLORMAPS*256*sizeof(argb_t)+3+255, PU_LEVEL, 0);
868 	maps->shademap = (argb_t *)(((ptrdiff_t)maps->shademap + 255) & ~0xff);
869 
870 	colormap->maps = shaderef_t(maps, 0);
871 	colormap->color = color;
872 	colormap->fade = fade;
873 	colormap->next = NormalLight.next;
874 	NormalLight.next = colormap;
875 
876 	BuildColoredLights (maps, lr, lg, lb, fr, fg, fb);
877 
878 	return colormap;
879 }
880 
BEGIN_COMMAND(testcolor)881 BEGIN_COMMAND (testcolor)
882 {
883 	int color;
884 
885 	if (argc < 2)
886 	{
887 		Printf (PRINT_HIGH, "testcolor <color>\n");
888 	}
889 	else
890 	{
891 		std::string colorstring = V_GetColorStringByName (argv[1]);
892 
893 		if (colorstring.length())
894 			color = V_GetColorFromString (NULL, colorstring.c_str());
895 		else
896 			color = V_GetColorFromString (NULL, argv[1]);
897 
898 		BuildColoredLights ((shademap_t *)NormalLight.maps.map(), RPART(color), GPART(color), BPART(color),
899 			RPART(level.fadeto), GPART(level.fadeto), BPART(level.fadeto));
900 	}
901 }
END_COMMAND(testcolor)902 END_COMMAND (testcolor)
903 
904 //
905 // V_DoPaletteEffects
906 //
907 // Handles changing the palette or the BlendR/G/B/A globals based on damage
908 // the player has taken, any power-ups, or environment such as deep water.
909 //
910 void V_DoPaletteEffects()
911 {
912 	player_t* plyr = &displayplayer();
913 
914 	if (screen->is8bit())
915 	{
916 		int		palette;
917 
918 		float cnt = (float)plyr->damagecount;
919 		if (!multiplayer || sv_allowredscreen)
920 			cnt *= r_painintensity;
921 
922 		// slowly fade the berzerk out
923 		if (plyr->powers[pw_strength])
924 			cnt = MAX(cnt, 12.0f - float(plyr->powers[pw_strength] >> 6));
925 
926 		if (cnt > 0.0f)
927 		{
928 			palette = ((int)cnt + 7) >> 3;
929 
930 			if (gamemode == retail_chex)
931 				palette = RADIATIONPAL;
932 			else
933 			{
934 				if (palette >= NUMREDPALS)
935 					palette = NUMREDPALS-1;
936 
937 				palette += STARTREDPALS;
938 
939 				if (palette < 0)
940 					palette = 0;
941 			}
942 		}
943 		else if (plyr->bonuscount)
944 		{
945 			palette = (plyr->bonuscount+7)>>3;
946 
947 			if (palette >= NUMBONUSPALS)
948 				palette = NUMBONUSPALS-1;
949 
950 			palette += STARTBONUSPALS;
951 		}
952 		else if (plyr->powers[pw_ironfeet] > 4*32 || plyr->powers[pw_ironfeet] & 8)
953 			palette = RADIATIONPAL;
954 		else
955 			palette = 0;
956 
957 		if (palette != current_palette)
958 		{
959 			current_palette = palette;
960 			byte* pal = (byte *)W_CacheLumpNum(lu_palette, PU_CACHE) + palette * 768;
961 			I_SetOldPalette(pal);
962 		}
963 	}
964 	else
965 	{
966 		float blend[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
967 
968 		V_AddBlend(BaseBlendR / 255.0f, BaseBlendG / 255.0f, BaseBlendB / 255.0f, BaseBlendA, blend);
969 		V_AddBlend(plyr->BlendR, plyr->BlendG, plyr->BlendB, plyr->BlendA, blend);
970 
971 		// red tint for pain / berzerk power
972 		if (plyr->damagecount || plyr->powers[pw_strength])
973 		{
974 			float amount = (float)plyr->damagecount;
975 			if (!multiplayer || sv_allowredscreen)
976 				amount *= r_painintensity;
977 
978 			// slowly fade the berzerk out
979 			if (plyr->powers[pw_strength])
980 				amount = MAX(amount, 12.0f - float(plyr->powers[pw_strength]) / 64.0f);
981 
982 			if (amount > 0.0f)
983 			{
984 				amount = MIN(amount, 56.0f);
985 				float alpha = (amount + 8.0f) / 72.0f;
986 
987 				static const float red = 255.0f / 255.0f;
988 				static const float green = 0.0f;
989 				static const float blue = 0.0f;
990 				V_AddBlend(red, green, blue, alpha, blend);
991 			}
992 		}
993 
994 		// yellow tint for item pickup
995 		if (plyr->bonuscount)
996 		{
997 			float amount = (float)plyr->bonuscount;
998 			if (amount > 0.0f)
999 			{
1000 				amount = MIN(amount, 24.0f);
1001 				float alpha = (amount + 8.0f) / 64.0f;
1002 
1003 				static const float red = 215.0f / 255.0f;
1004 				static const float green = 186.0f / 255.0f;
1005 				static const float blue = 69.0f / 255.0f;
1006 				V_AddBlend(red, green, blue, alpha, blend);
1007 			}
1008 		}
1009 
1010 		// green tint for radiation suit
1011 		if (plyr->powers[pw_ironfeet] > 4*32 || plyr->powers[pw_ironfeet] & 8)
1012 		{
1013 			static const float alpha = 1.0f / 8.0f;
1014 			static const float red = 0.0f;
1015 			static const float green = 255.0f / 255.0f;
1016 			static const float blue = 0.0f;
1017 			V_AddBlend(red, green, blue, alpha, blend);
1018 		}
1019 
1020 		memcpy(current_blend, blend, sizeof(blend));
1021 
1022 		V_SetBlend ((int)(blend[0] * 255.0f), (int)(blend[1] * 255.0f),
1023 					(int)(blend[2] * 255.0f), (int)(blend[3] * 256.0f));
1024 	}
1025 }
1026 
1027 VERSION_CONTROL (v_palette_cpp, "$Id: v_palette.cpp 4686 2014-03-25 15:52:15Z dr_sean $")
1028 
1029