1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 1998-2000 by DooM Legacy Team.
5 // Copyright (C) 1999-2021 by Sonic Team Junior.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file  r_draw.c
12 /// \brief span / column drawer functions, for 8bpp and 16bpp
13 ///        All drawing to the view buffer is accomplished in this file.
14 ///        The other refresh files only know about ccordinates,
15 ///        not the architecture of the frame buffer.
16 ///        The frame buffer is a linear one, and we need only the base address.
17 
18 #include "doomdef.h"
19 #include "doomstat.h"
20 #include "r_local.h"
21 #include "st_stuff.h" // need ST_HEIGHT
22 #include "i_video.h"
23 #include "v_video.h"
24 #include "m_misc.h"
25 #include "w_wad.h"
26 #include "z_zone.h"
27 #include "console.h" // Until buffering gets finished
28 
29 #ifdef HWRENDER
30 #include "hardware/hw_main.h"
31 #endif
32 
33 // ==========================================================================
34 //                     COMMON DATA FOR 8bpp AND 16bpp
35 // ==========================================================================
36 
37 /**	\brief view info
38 */
39 INT32 viewwidth, scaledviewwidth, viewheight, viewwindowx, viewwindowy;
40 
41 /**	\brief pointer to the start of each line of the screen,
42 */
43 UINT8 *ylookup[MAXVIDHEIGHT*4];
44 
45 /**	\brief pointer to the start of each line of the screen, for view1 (splitscreen)
46 */
47 UINT8 *ylookup1[MAXVIDHEIGHT*4];
48 
49 /**	\brief pointer to the start of each line of the screen, for view2 (splitscreen)
50 */
51 UINT8 *ylookup2[MAXVIDHEIGHT*4];
52 
53 /**	\brief  x byte offset for columns inside the viewwindow,
54 	so the first column starts at (SCRWIDTH - VIEWWIDTH)/2
55 */
56 INT32 columnofs[MAXVIDWIDTH*4];
57 
58 UINT8 *topleft;
59 
60 // =========================================================================
61 //                      COLUMN DRAWING CODE STUFF
62 // =========================================================================
63 
64 lighttable_t *dc_colormap;
65 INT32 dc_x = 0, dc_yl = 0, dc_yh = 0;
66 
67 fixed_t dc_iscale, dc_texturemid;
68 UINT8 dc_hires; // under MSVC boolean is a byte, while on other systems, it a bit,
69                // soo lets make it a byte on all system for the ASM code
70 UINT8 *dc_source;
71 
72 // -----------------------
73 // translucency stuff here
74 // -----------------------
75 #define NUMTRANSTABLES 9 // how many translucency tables are used
76 
77 UINT8 *transtables; // translucency tables
78 UINT8 *blendtables[NUMBLENDMAPS];
79 
80 /**	\brief R_DrawTransColumn uses this
81 */
82 UINT8 *dc_transmap; // one of the translucency tables
83 
84 // ----------------------
85 // translation stuff here
86 // ----------------------
87 
88 
89 /**	\brief R_DrawTranslatedColumn uses this
90 */
91 UINT8 *dc_translation;
92 
93 struct r_lightlist_s *dc_lightlist = NULL;
94 INT32 dc_numlights = 0, dc_maxlights, dc_texheight;
95 
96 // =========================================================================
97 //                      SPAN DRAWING CODE STUFF
98 // =========================================================================
99 
100 INT32 ds_y, ds_x1, ds_x2;
101 lighttable_t *ds_colormap;
102 lighttable_t *ds_translation; // Lactozilla: Sprite splat drawer
103 
104 fixed_t ds_xfrac, ds_yfrac, ds_xstep, ds_ystep;
105 INT32 ds_waterofs, ds_bgofs;
106 
107 UINT16 ds_flatwidth, ds_flatheight;
108 boolean ds_powersoftwo;
109 
110 UINT8 *ds_source; // points to the start of a flat
111 UINT8 *ds_transmap; // one of the translucency tables
112 
113 // Vectors for Software's tilted slope drawers
114 floatv3_t *ds_su, *ds_sv, *ds_sz;
115 floatv3_t *ds_sup, *ds_svp, *ds_szp;
116 float focallengthf, zeroheight;
117 
118 /**	\brief Variable flat sizes
119 */
120 
121 UINT32 nflatxshift, nflatyshift, nflatshiftup, nflatmask;
122 
123 // =========================================================================
124 //                   TRANSLATION COLORMAP CODE
125 // =========================================================================
126 
127 #define DEFAULT_TT_CACHE_INDEX MAXSKINS
128 #define BOSS_TT_CACHE_INDEX (MAXSKINS + 1)
129 #define METALSONIC_TT_CACHE_INDEX (MAXSKINS + 2)
130 #define ALLWHITE_TT_CACHE_INDEX (MAXSKINS + 3)
131 #define RAINBOW_TT_CACHE_INDEX (MAXSKINS + 4)
132 #define BLINK_TT_CACHE_INDEX (MAXSKINS + 5)
133 #define DASHMODE_TT_CACHE_INDEX (MAXSKINS + 6)
134 #define DEFAULT_STARTTRANSCOLOR 96
135 #define NUM_PALETTE_ENTRIES 256
136 
137 static UINT8 **translationtablecache[MAXSKINS + 7] = {NULL};
138 UINT8 skincolor_modified[MAXSKINCOLORS];
139 
SkinToCacheIndex(INT32 skinnum)140 static INT32 SkinToCacheIndex(INT32 skinnum)
141 {
142 	switch (skinnum)
143 	{
144 		case TC_DEFAULT:    return DEFAULT_TT_CACHE_INDEX;
145 		case TC_BOSS:       return BOSS_TT_CACHE_INDEX;
146 		case TC_METALSONIC: return METALSONIC_TT_CACHE_INDEX;
147 		case TC_ALLWHITE:   return ALLWHITE_TT_CACHE_INDEX;
148 		case TC_RAINBOW:    return RAINBOW_TT_CACHE_INDEX;
149 		case TC_BLINK:      return BLINK_TT_CACHE_INDEX;
150 		case TC_DASHMODE:   return DASHMODE_TT_CACHE_INDEX;
151 		     default:       break;
152 	}
153 
154 	return skinnum;
155 }
156 
CacheIndexToSkin(INT32 ttc)157 static INT32 CacheIndexToSkin(INT32 ttc)
158 {
159 	switch (ttc)
160 	{
161 		case DEFAULT_TT_CACHE_INDEX:    return TC_DEFAULT;
162 		case BOSS_TT_CACHE_INDEX:       return TC_BOSS;
163 		case METALSONIC_TT_CACHE_INDEX: return TC_METALSONIC;
164 		case ALLWHITE_TT_CACHE_INDEX:   return TC_ALLWHITE;
165 		case RAINBOW_TT_CACHE_INDEX:    return TC_RAINBOW;
166 		case BLINK_TT_CACHE_INDEX:      return TC_BLINK;
167 		case DASHMODE_TT_CACHE_INDEX:   return TC_DASHMODE;
168 		     default:                   break;
169 	}
170 
171 	return ttc;
172 }
173 
174 CV_PossibleValue_t Color_cons_t[MAXSKINCOLORS+1];
175 
176 /** \brief Initializes the translucency tables used by the Software renderer.
177 */
R_InitTranslucencyTables(void)178 void R_InitTranslucencyTables(void)
179 {
180 	// Load here the transparency lookup tables 'TRANSx0'
181 	// NOTE: the TRANSx0 resources MUST BE aligned on 64k for the asm
182 	// optimised code (in other words, transtables pointer low word is 0)
183 	transtables = Z_MallocAlign(NUMTRANSTABLES*0x10000, PU_STATIC,
184 		NULL, 16);
185 
186 	W_ReadLump(W_GetNumForName("TRANS10"), transtables);
187 	W_ReadLump(W_GetNumForName("TRANS20"), transtables+0x10000);
188 	W_ReadLump(W_GetNumForName("TRANS30"), transtables+0x20000);
189 	W_ReadLump(W_GetNumForName("TRANS40"), transtables+0x30000);
190 	W_ReadLump(W_GetNumForName("TRANS50"), transtables+0x40000);
191 	W_ReadLump(W_GetNumForName("TRANS60"), transtables+0x50000);
192 	W_ReadLump(W_GetNumForName("TRANS70"), transtables+0x60000);
193 	W_ReadLump(W_GetNumForName("TRANS80"), transtables+0x70000);
194 	W_ReadLump(W_GetNumForName("TRANS90"), transtables+0x80000);
195 
196 	R_GenerateBlendTables();
197 }
198 
199 static colorlookup_t transtab_lut;
200 
BlendTab_Translucent(UINT8 * table,int style,UINT8 blendamt)201 static void BlendTab_Translucent(UINT8 *table, int style, UINT8 blendamt)
202 {
203 	INT16 bg, fg;
204 
205 	if (table == NULL)
206 		I_Error("BlendTab_Translucent: input table was NULL!");
207 
208 	for (bg = 0; bg < 0xFF; bg++)
209 	{
210 		for (fg = 0; fg < 0xFF; fg++)
211 		{
212 			RGBA_t backrgba = V_GetMasterColor(bg);
213 			RGBA_t frontrgba = V_GetMasterColor(fg);
214 			RGBA_t result;
215 
216 			result.rgba = ASTBlendPixel(backrgba, frontrgba, style, 0xFF);
217 			result.rgba = ASTBlendPixel(result, frontrgba, AST_TRANSLUCENT, blendamt);
218 
219 			table[((bg * 0x100) + fg)] = GetColorLUT(&transtab_lut, result.s.red, result.s.green, result.s.blue);
220 		}
221 	}
222 }
223 
BlendTab_Subtractive(UINT8 * table,int style,UINT8 blendamt)224 static void BlendTab_Subtractive(UINT8 *table, int style, UINT8 blendamt)
225 {
226 	INT16 bg, fg;
227 
228 	if (table == NULL)
229 		I_Error("BlendTab_Subtractive: input table was NULL!");
230 
231 	if (blendamt == 0xFF)
232 	{
233 		memset(table, GetColorLUT(&transtab_lut, 0, 0, 0), 0x10000);
234 		return;
235 	}
236 
237 	for (bg = 0; bg < 0xFF; bg++)
238 	{
239 		for (fg = 0; fg < 0xFF; fg++)
240 		{
241 			RGBA_t backrgba = V_GetMasterColor(bg);
242 			RGBA_t frontrgba = V_GetMasterColor(fg);
243 			RGBA_t result;
244 
245 			result.rgba = ASTBlendPixel(backrgba, frontrgba, style, 0xFF);
246 			result.s.red = max(0, result.s.red - blendamt);
247 			result.s.green = max(0, result.s.green - blendamt);
248 			result.s.blue = max(0, result.s.blue - blendamt);
249 
250 			table[((bg * 0x100) + fg)] = GetColorLUT(&transtab_lut, result.s.red, result.s.green, result.s.blue);
251 		}
252 	}
253 }
254 
BlendTab_Modulative(UINT8 * table)255 static void BlendTab_Modulative(UINT8 *table)
256 {
257 	INT16 bg, fg;
258 
259 	if (table == NULL)
260 		I_Error("BlendTab_Modulative: input table was NULL!");
261 
262 	for (bg = 0; bg < 0xFF; bg++)
263 	{
264 		for (fg = 0; fg < 0xFF; fg++)
265 		{
266 			RGBA_t backrgba = V_GetMasterColor(bg);
267 			RGBA_t frontrgba = V_GetMasterColor(fg);
268 			RGBA_t result;
269 			result.rgba = ASTBlendPixel(backrgba, frontrgba, AST_MODULATE, 0);
270 			table[((bg * 0x100) + fg)] = GetColorLUT(&transtab_lut, result.s.red, result.s.green, result.s.blue);
271 		}
272 	}
273 }
274 
275 static INT32 BlendTab_Count[NUMBLENDMAPS] =
276 {
277 	NUMTRANSTABLES+1, // blendtab_add
278 	NUMTRANSTABLES+1, // blendtab_subtract
279 	NUMTRANSTABLES+1, // blendtab_reversesubtract
280 	1                 // blendtab_modulate
281 };
282 
283 static INT32 BlendTab_FromStyle[] =
284 {
285 	0,                        // AST_COPY
286 	0,                        // AST_TRANSLUCENT
287 	blendtab_add,             // AST_ADD
288 	blendtab_subtract,        // AST_SUBTRACT
289 	blendtab_reversesubtract, // AST_REVERSESUBTRACT
290 	blendtab_modulate,        // AST_MODULATE
291 	0                         // AST_OVERLAY
292 };
293 
BlendTab_GenerateMaps(INT32 tab,INT32 style,void (* genfunc)(UINT8 *,int,UINT8))294 static void BlendTab_GenerateMaps(INT32 tab, INT32 style, void (*genfunc)(UINT8 *, int, UINT8))
295 {
296 	INT32 i = 0, num = BlendTab_Count[tab];
297 	const float amtmul = (256.0f / (float)(NUMTRANSTABLES + 1));
298 	for (; i < num; i++)
299 	{
300 		const size_t offs = (0x10000 * i);
301 		const UINT16 alpha = min(amtmul * i, 0xFF);
302 		genfunc(blendtables[tab] + offs, style, alpha);
303 	}
304 }
305 
R_GenerateBlendTables(void)306 void R_GenerateBlendTables(void)
307 {
308 	INT32 i;
309 
310 	for (i = 0; i < NUMBLENDMAPS; i++)
311 		blendtables[i] = Z_MallocAlign(BlendTab_Count[i] * 0x10000, PU_STATIC, NULL, 16);
312 
313 	InitColorLUT(&transtab_lut, pMasterPalette, false);
314 
315 	// Additive
316 	BlendTab_GenerateMaps(blendtab_add, AST_ADD, BlendTab_Translucent);
317 
318 	// Subtractive
319 #if 1
320 	BlendTab_GenerateMaps(blendtab_subtract, AST_SUBTRACT, BlendTab_Subtractive);
321 #else
322 	BlendTab_GenerateMaps(blendtab_subtract, AST_SUBTRACT, BlendTab_Translucent);
323 #endif
324 
325 	// Reverse subtractive
326 	BlendTab_GenerateMaps(blendtab_reversesubtract, AST_REVERSESUBTRACT, BlendTab_Translucent);
327 
328 	// Modulative blending only requires a single table
329 	BlendTab_Modulative(blendtables[blendtab_modulate]);
330 }
331 
332 #define ClipBlendLevel(style, trans) max(min((trans), BlendTab_Count[BlendTab_FromStyle[style]]-1), 0)
333 #define ClipTransLevel(trans) max(min((trans), NUMTRANSMAPS-2), 0)
334 
R_GetTranslucencyTable(INT32 alphalevel)335 UINT8 *R_GetTranslucencyTable(INT32 alphalevel)
336 {
337 	return transtables + (ClipTransLevel(alphalevel-1) << FF_TRANSSHIFT);
338 }
339 
R_GetBlendTable(int style,INT32 alphalevel)340 UINT8 *R_GetBlendTable(int style, INT32 alphalevel)
341 {
342 	size_t offs;
343 
344 	if (style == AST_COPY || style == AST_OVERLAY)
345 		return NULL;
346 
347 	offs = (ClipBlendLevel(style, alphalevel) << FF_TRANSSHIFT);
348 
349 	// Lactozilla: Returns the equivalent to AST_TRANSLUCENT
350 	// if no alpha style matches any of the blend tables.
351 	switch (style)
352 	{
353 		case AST_ADD:
354 			return blendtables[blendtab_add] + offs;
355 		case AST_SUBTRACT:
356 			return blendtables[blendtab_subtract] + offs;
357 		case AST_REVERSESUBTRACT:
358 			return blendtables[blendtab_reversesubtract] + offs;
359 		case AST_MODULATE:
360 			return blendtables[blendtab_modulate];
361 		default:
362 			break;
363 	}
364 
365 	// Return a normal translucency table
366 	if (--alphalevel >= 0)
367 		return transtables + (ClipTransLevel(alphalevel) << FF_TRANSSHIFT);
368 	else
369 		return NULL;
370 }
371 
R_BlendLevelVisible(INT32 blendmode,INT32 alphalevel)372 boolean R_BlendLevelVisible(INT32 blendmode, INT32 alphalevel)
373 {
374 	if (blendmode == AST_COPY || blendmode == AST_SUBTRACT || blendmode == AST_MODULATE || blendmode == AST_OVERLAY)
375 		return true;
376 
377 	return (alphalevel < BlendTab_Count[BlendTab_FromStyle[blendmode]]);
378 }
379 
380 // Define for getting accurate color brightness readings according to how the human eye sees them.
381 // https://en.wikipedia.org/wiki/Relative_luminance
382 // 0.2126 to red
383 // 0.7152 to green
384 // 0.0722 to blue
385 // (See this same define in hw_md2.c!)
386 #define SETBRIGHTNESS(brightness,r,g,b) \
387 	brightness = (UINT8)(((1063*((UINT16)r)/5000) + (3576*((UINT16)g)/5000) + (361*((UINT16)b)/5000)) / 3)
388 
389 /** \brief	Generates the rainbow colourmaps that are used when a player has the invincibility power... stolen from kart, with permission
390 
391 	\param	dest_colormap	colormap to populate
392 	\param	skincolor		translation color
393 */
R_RainbowColormap(UINT8 * dest_colormap,UINT16 skincolor)394 static void R_RainbowColormap(UINT8 *dest_colormap, UINT16 skincolor)
395 {
396 	INT32 i;
397 	RGBA_t color;
398 	UINT8 brightness;
399 	INT32 j;
400 	UINT8 colorbrightnesses[16];
401 	UINT16 brightdif;
402 	INT32 temp;
403 
404 	// first generate the brightness of all the colours of that skincolour
405 	for (i = 0; i < 16; i++)
406 	{
407 		color = V_GetColor(skincolors[skincolor].ramp[i]);
408 		SETBRIGHTNESS(colorbrightnesses[i], color.s.red, color.s.green, color.s.blue);
409 	}
410 
411 	// next, for every colour in the palette, choose the transcolor that has the closest brightness
412 	for (i = 0; i < NUM_PALETTE_ENTRIES; i++)
413 	{
414 		if (i == 0 || i == 31) // pure black and pure white don't change
415 		{
416 			dest_colormap[i] = (UINT8)i;
417 			continue;
418 		}
419 		color = V_GetColor(i);
420 		SETBRIGHTNESS(brightness, color.s.red, color.s.green, color.s.blue);
421 		brightdif = 256;
422 		for (j = 0; j < 16; j++)
423 		{
424 			temp = abs((INT16)brightness - (INT16)colorbrightnesses[j]);
425 			if (temp < brightdif)
426 			{
427 				brightdif = (UINT16)temp;
428 				dest_colormap[i] = skincolors[skincolor].ramp[j];
429 			}
430 		}
431 	}
432 }
433 
434 #undef SETBRIGHTNESS
435 
436 /**	\brief	Generates a translation colormap.
437 
438 	\param	dest_colormap	colormap to populate
439 	\param	skinnum		skin number, or a translation mode
440 	\param	color		translation color
441 
442 	\return	void
443 */
R_GenerateTranslationColormap(UINT8 * dest_colormap,INT32 skinnum,UINT16 color)444 static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, UINT16 color)
445 {
446 	INT32 i, starttranscolor, skinramplength;
447 
448 	// Handle a couple of simple special cases
449 	if (skinnum < TC_DEFAULT)
450 	{
451 		switch (skinnum)
452 		{
453 			case TC_ALLWHITE:
454 				memset(dest_colormap, 0, NUM_PALETTE_ENTRIES * sizeof(UINT8));
455 				return;
456 			case TC_RAINBOW:
457 				if (color >= numskincolors)
458 					I_Error("Invalid skin color #%hu.", (UINT16)color);
459 				if (color != SKINCOLOR_NONE)
460 				{
461 					R_RainbowColormap(dest_colormap, color);
462 					return;
463 				}
464 				break;
465 			case TC_BLINK:
466 				if (color >= numskincolors)
467 					I_Error("Invalid skin color #%hu.", (UINT16)color);
468 				if (color != SKINCOLOR_NONE)
469 				{
470 					memset(dest_colormap, skincolors[color].ramp[3], NUM_PALETTE_ENTRIES * sizeof(UINT8));
471 					return;
472 				}
473 				break;
474 			default:
475 				break;
476 		}
477 
478 		for (i = 0; i < NUM_PALETTE_ENTRIES; i++)
479 			dest_colormap[i] = (UINT8)i;
480 
481 		// White!
482 		if (skinnum == TC_BOSS)
483 		{
484 			for (i = 0; i < 16; i++)
485 				dest_colormap[31-i] = i;
486 		}
487 		else if (skinnum == TC_METALSONIC)
488 		{
489 			for (i = 0; i < 6; i++)
490 			{
491 				dest_colormap[skincolors[SKINCOLOR_BLUE].ramp[12-i]] = skincolors[SKINCOLOR_BLUE].ramp[i];
492 			}
493 			dest_colormap[159] = dest_colormap[253] = dest_colormap[254] = 0;
494 			for (i = 0; i < 16; i++)
495 				dest_colormap[96+i] = dest_colormap[skincolors[SKINCOLOR_COBALT].ramp[i]];
496 		}
497 		else if (skinnum == TC_DASHMODE) // This is a long one, because MotorRoach basically hand-picked the indices
498 		{
499 			// greens -> ketchups
500 			dest_colormap[96] = dest_colormap[97] = 48;
501 			dest_colormap[98] = 49;
502 			dest_colormap[99] = 51;
503 			dest_colormap[100] = 52;
504 			dest_colormap[101] = dest_colormap[102] = 54;
505 			dest_colormap[103] = 34;
506 			dest_colormap[104] = 37;
507 			dest_colormap[105] = 39;
508 			dest_colormap[106] = 41;
509 			for (i = 0; i < 5; i++)
510 				dest_colormap[107 + i] = 43 + i;
511 
512 			// reds -> steel blues
513 			dest_colormap[32] = 146;
514 			dest_colormap[33] = 147;
515 			dest_colormap[34] = dest_colormap[35] = 170;
516 			dest_colormap[36] = 171;
517 			dest_colormap[37] = dest_colormap[38] = 172;
518 			dest_colormap[39] = dest_colormap[40] = dest_colormap[41] = 173;
519 			dest_colormap[42] = dest_colormap[43] = dest_colormap[44] = 174;
520 			dest_colormap[45] = dest_colormap[46] = dest_colormap[47] = 175;
521 			dest_colormap[71] = 139;
522 
523 			// steel blues -> oranges
524 			dest_colormap[170] = 52;
525 			dest_colormap[171] = 54;
526 			dest_colormap[172] = 56;
527 			dest_colormap[173] = 42;
528 			dest_colormap[174] = 45;
529 			dest_colormap[175] = 47;
530 		}
531 		return;
532 	}
533 	else if (color == SKINCOLOR_NONE)
534 	{
535 		for (i = 0; i < NUM_PALETTE_ENTRIES; i++)
536 			dest_colormap[i] = (UINT8)i;
537 		return;
538 	}
539 
540 	if (color >= numskincolors)
541 		I_Error("Invalid skin color #%hu.", (UINT16)color);
542 
543 	if (skinnum < 0 && skinnum > TC_DEFAULT)
544 		I_Error("Invalid translation colormap index %d.", skinnum);
545 
546 	starttranscolor = (skinnum != TC_DEFAULT) ? skins[skinnum].starttranscolor : DEFAULT_STARTTRANSCOLOR;
547 
548 	if (starttranscolor >= NUM_PALETTE_ENTRIES)
549 		I_Error("Invalid startcolor #%d.", starttranscolor);
550 
551 	// Fill in the entries of the palette that are fixed
552 	for (i = 0; i < starttranscolor; i++)
553 		dest_colormap[i] = (UINT8)i;
554 
555 	i = starttranscolor + 16;
556 	if (i < NUM_PALETTE_ENTRIES)
557 	{
558 		for (i = (UINT8)i; i < NUM_PALETTE_ENTRIES; i++)
559 			dest_colormap[i] = (UINT8)i;
560 		skinramplength = 16;
561 	}
562 	else
563 		skinramplength = i - NUM_PALETTE_ENTRIES; // shouldn't this be NUM_PALETTE_ENTRIES - starttranscolor?
564 
565 	// Build the translated ramp
566 	for (i = 0; i < skinramplength; i++)
567 		dest_colormap[starttranscolor + i] = (UINT8)skincolors[color].ramp[i];
568 }
569 
570 
571 /**	\brief	Retrieves a translation colormap from the cache.
572 
573 	\param	skinnum	number of skin, TC_DEFAULT or TC_BOSS
574 	\param	color	translation color
575 	\param	flags	set GTC_CACHE to use the cache
576 
577 	\return	Colormap. If not cached, caller should Z_Free.
578 */
R_GetTranslationColormap(INT32 skinnum,skincolornum_t color,UINT8 flags)579 UINT8* R_GetTranslationColormap(INT32 skinnum, skincolornum_t color, UINT8 flags)
580 {
581 	UINT8* ret;
582 	INT32 skintableindex = SkinToCacheIndex(skinnum); // Adjust if we want the default colormap
583 	INT32 i;
584 
585 	if (flags & GTC_CACHE)
586 	{
587 		// Allocate table for skin if necessary
588 		if (!translationtablecache[skintableindex])
589 			translationtablecache[skintableindex] = Z_Calloc(MAXSKINCOLORS * sizeof(UINT8**), PU_STATIC, NULL);
590 
591 		// Get colormap
592 		ret = translationtablecache[skintableindex][color];
593 
594 		// Rebuild the cache if necessary
595 		if (skincolor_modified[color])
596 		{
597 			for (i = 0; i < (INT32)(sizeof(translationtablecache) / sizeof(translationtablecache[0])); i++)
598 				if (translationtablecache[i] && translationtablecache[i][color])
599 					R_GenerateTranslationColormap(translationtablecache[i][color], CacheIndexToSkin(i), color);
600 
601 			skincolor_modified[color] = false;
602 		}
603 	}
604 	else ret = NULL;
605 
606 	// Generate the colormap if necessary
607 	if (!ret)
608 	{
609 		ret = Z_MallocAlign(NUM_PALETTE_ENTRIES, (flags & GTC_CACHE) ? PU_LEVEL : PU_STATIC, NULL, 8);
610 		R_GenerateTranslationColormap(ret, skinnum, color);
611 
612 		// Cache the colormap if desired
613 		if (flags & GTC_CACHE)
614 			translationtablecache[skintableindex][color] = ret;
615 	}
616 
617 	return ret;
618 }
619 
620 /**	\brief	Flushes cache of translation colormaps.
621 
622 	Flushes cache of translation colormaps, but doesn't actually free the
623 	colormaps themselves. These are freed when PU_LEVEL blocks are purged,
624 	at or before which point, this function should be called.
625 
626 	\return	void
627 */
R_FlushTranslationColormapCache(void)628 void R_FlushTranslationColormapCache(void)
629 {
630 	INT32 i;
631 
632 	for (i = 0; i < (INT32)(sizeof(translationtablecache) / sizeof(translationtablecache[0])); i++)
633 		if (translationtablecache[i])
634 			memset(translationtablecache[i], 0, MAXSKINCOLORS * sizeof(UINT8**));
635 }
636 
R_GetColorByName(const char * name)637 UINT16 R_GetColorByName(const char *name)
638 {
639 	UINT16 color = (UINT16)atoi(name);
640 	if (color > 0 && color < numskincolors)
641 		return color;
642 	for (color = 1; color < numskincolors; color++)
643 		if (!stricmp(skincolors[color].name, name))
644 			return color;
645 	return SKINCOLOR_NONE;
646 }
647 
R_GetSuperColorByName(const char * name)648 UINT16 R_GetSuperColorByName(const char *name)
649 {
650 	UINT16 i, color = SKINCOLOR_NONE;
651 	char *realname = Z_Malloc(MAXCOLORNAME+1, PU_STATIC, NULL);
652 	snprintf(realname, MAXCOLORNAME+1, "Super %s 1", name);
653 	for (i = 1; i < numskincolors; i++)
654 		if (!stricmp(skincolors[i].name, realname)) {
655 			color = i;
656 			break;
657 		}
658 	Z_Free(realname);
659 	return color;
660 }
661 
662 // ==========================================================================
663 //               COMMON DRAWER FOR 8 AND 16 BIT COLOR MODES
664 // ==========================================================================
665 
666 // in a perfect world, all routines would be compatible for either mode,
667 // and optimised enough
668 //
669 // in reality, the few routines that can work for either mode, are
670 // put here
671 
672 /**	\brief	The R_InitViewBuffer function
673 
674 	Creates lookup tables for getting the framebuffer address
675 	of a pixel to draw.
676 
677 	\param	width	witdh of buffer
678 	\param	height	hieght of buffer
679 
680 	\return	void
681 
682 
683 */
684 
R_InitViewBuffer(INT32 width,INT32 height)685 void R_InitViewBuffer(INT32 width, INT32 height)
686 {
687 	INT32 i, bytesperpixel = vid.bpp;
688 
689 	if (width > MAXVIDWIDTH)
690 		width = MAXVIDWIDTH;
691 	if (height > MAXVIDHEIGHT)
692 		height = MAXVIDHEIGHT;
693 	if (bytesperpixel < 1 || bytesperpixel > 4)
694 		I_Error("R_InitViewBuffer: wrong bytesperpixel value %d\n", bytesperpixel);
695 
696 	// Handle resize, e.g. smaller view windows with border and/or status bar.
697 	viewwindowx = (vid.width - width) >> 1;
698 
699 	// Column offset for those columns of the view window, but relative to the entire screen
700 	for (i = 0; i < width; i++)
701 		columnofs[i] = (viewwindowx + i) * bytesperpixel;
702 
703 	// Same with base row offset.
704 	if (width == vid.width)
705 		viewwindowy = 0;
706 	else
707 		viewwindowy = (vid.height - height) >> 1;
708 
709 	// Precalculate all row offsets.
710 	for (i = 0; i < height; i++)
711 	{
712 		ylookup[i] = ylookup1[i] = screens[0] + (i+viewwindowy)*vid.width*bytesperpixel;
713 		ylookup2[i] = screens[0] + (i+(vid.height>>1))*vid.width*bytesperpixel; // for splitscreen
714 	}
715 }
716 
717 /**	\brief viewborder patches lump numbers
718 */
719 lumpnum_t viewborderlump[8];
720 
721 /**	\brief Store the lumpnumber of the viewborder patches
722 */
723 
R_InitViewBorder(void)724 void R_InitViewBorder(void)
725 {
726 	viewborderlump[BRDR_T] = W_GetNumForName("brdr_t");
727 	viewborderlump[BRDR_B] = W_GetNumForName("brdr_b");
728 	viewborderlump[BRDR_L] = W_GetNumForName("brdr_l");
729 	viewborderlump[BRDR_R] = W_GetNumForName("brdr_r");
730 	viewborderlump[BRDR_TL] = W_GetNumForName("brdr_tl");
731 	viewborderlump[BRDR_BL] = W_GetNumForName("brdr_bl");
732 	viewborderlump[BRDR_TR] = W_GetNumForName("brdr_tr");
733 	viewborderlump[BRDR_BR] = W_GetNumForName("brdr_br");
734 }
735 
736 #if 0
737 /**	\brief R_FillBackScreen
738 
739 	Fills the back screen with a pattern for variable screen sizes
740 	Also draws a beveled edge.
741 */
742 void R_FillBackScreen(void)
743 {
744 	UINT8 *src, *dest;
745 	patch_t *patch;
746 	INT32 x, y, step, boff;
747 
748 	// quickfix, don't cache lumps in both modes
749 	if (rendermode != render_soft)
750 		return;
751 
752 	// draw pattern around the status bar too (when hires),
753 	// so return only when in full-screen without status bar.
754 	if (scaledviewwidth == vid.width && viewheight == vid.height)
755 		return;
756 
757 	src = scr_borderpatch;
758 	dest = screens[1];
759 
760 	for (y = 0; y < vid.height; y++)
761 	{
762 		for (x = 0; x < vid.width/128; x++)
763 		{
764 			M_Memcpy (dest, src+((y&127)<<7), 128);
765 			dest += 128;
766 		}
767 
768 		if (vid.width&127)
769 		{
770 			M_Memcpy(dest, src+((y&127)<<7), vid.width&127);
771 			dest += (vid.width&127);
772 		}
773 	}
774 
775 	// don't draw the borders when viewwidth is full vid.width.
776 	if (scaledviewwidth == vid.width)
777 		return;
778 
779 	step = 8;
780 	boff = 8;
781 
782 	patch = W_CacheLumpNum(viewborderlump[BRDR_T], PU_CACHE);
783 	for (x = 0; x < scaledviewwidth; x += step)
784 		V_DrawPatch(viewwindowx + x, viewwindowy - boff, 1, patch);
785 
786 	patch = W_CacheLumpNum(viewborderlump[BRDR_B], PU_CACHE);
787 	for (x = 0; x < scaledviewwidth; x += step)
788 		V_DrawPatch(viewwindowx + x, viewwindowy + viewheight, 1, patch);
789 
790 	patch = W_CacheLumpNum(viewborderlump[BRDR_L], PU_CACHE);
791 	for (y = 0; y < viewheight; y += step)
792 		V_DrawPatch(viewwindowx - boff, viewwindowy + y, 1, patch);
793 
794 	patch = W_CacheLumpNum(viewborderlump[BRDR_R],PU_CACHE);
795 	for (y = 0; y < viewheight; y += step)
796 		V_DrawPatch(viewwindowx + scaledviewwidth, viewwindowy + y, 1,
797 			patch);
798 
799 	// Draw beveled corners.
800 	V_DrawPatch(viewwindowx - boff, viewwindowy - boff, 1,
801 		W_CacheLumpNum(viewborderlump[BRDR_TL], PU_CACHE));
802 	V_DrawPatch(viewwindowx + scaledviewwidth, viewwindowy - boff, 1,
803 		W_CacheLumpNum(viewborderlump[BRDR_TR], PU_CACHE));
804 	V_DrawPatch(viewwindowx - boff, viewwindowy + viewheight, 1,
805 		W_CacheLumpNum(viewborderlump[BRDR_BL], PU_CACHE));
806 	V_DrawPatch(viewwindowx + scaledviewwidth, viewwindowy + viewheight, 1,
807 		W_CacheLumpNum(viewborderlump[BRDR_BR], PU_CACHE));
808 }
809 #endif
810 
811 /**	\brief	The R_VideoErase function
812 
813 	Copy a screen buffer.
814 
815 	\param	ofs	offest from buffer
816 	\param	count	bytes to erase
817 
818 	\return	void
819 
820 
821 */
R_VideoErase(size_t ofs,INT32 count)822 void R_VideoErase(size_t ofs, INT32 count)
823 {
824 	// LFB copy.
825 	// This might not be a good idea if memcpy
826 	//  is not optimal, e.g. byte by byte on
827 	//  a 32bit CPU, as GNU GCC/Linux libc did
828 	//  at one point.
829 	M_Memcpy(screens[0] + ofs, screens[1] + ofs, count);
830 }
831 
832 #if 0
833 /**	\brief The R_DrawViewBorder
834 
835   Draws the border around the view
836 	for different size windows?
837 */
838 void R_DrawViewBorder(void)
839 {
840 	INT32 top, side, ofs;
841 
842 	if (rendermode == render_none)
843 		return;
844 #ifdef HWRENDER
845 	if (rendermode != render_soft)
846 	{
847 		HWR_DrawViewBorder(0);
848 		return;
849 	}
850 	else
851 #endif
852 
853 #ifdef DEBUG
854 	fprintf(stderr,"RDVB: vidwidth %d vidheight %d scaledviewwidth %d viewheight %d\n",
855 		vid.width, vid.height, scaledviewwidth, viewheight);
856 #endif
857 
858 	if (scaledviewwidth == vid.width)
859 		return;
860 
861 	top = (vid.height - viewheight)>>1;
862 	side = (vid.width - scaledviewwidth)>>1;
863 
864 	// copy top and one line of left side
865 	R_VideoErase(0, top*vid.width+side);
866 
867 	// copy one line of right side and bottom
868 	ofs = (viewheight+top)*vid.width - side;
869 	R_VideoErase(ofs, top*vid.width + side);
870 
871 	// copy sides using wraparound
872 	ofs = top*vid.width + vid.width-side;
873 	side <<= 1;
874 
875     // simpler using our VID_Blit routine
876 	VID_BlitLinearScreen(screens[1] + ofs, screens[0] + ofs, side, viewheight - 1,
877 		vid.width, vid.width);
878 }
879 #endif
880 
881 // ==========================================================================
882 //                   INCLUDE 8bpp DRAWING CODE HERE
883 // ==========================================================================
884 
885 #include "r_draw8.c"
886 #include "r_draw8_npo2.c"
887 
888 // ==========================================================================
889 //                   INCLUDE 16bpp DRAWING CODE HERE
890 // ==========================================================================
891 
892 #ifdef HIGHCOLOR
893 #include "r_draw16.c"
894 #endif
895