1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: r_draw.cpp 4715 2014-03-29 15:58:53Z 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 //		The actual span/column drawing functions.
21 //		Here find the main potential for optimization,
22 //		 e.g. inline assembly, different algorithms.
23 //
24 //-----------------------------------------------------------------------------
25 
26 #include <stddef.h>
27 #include <assert.h>
28 #include <algorithm>
29 
30 #include <SDL.h>
31 #if (SDL_VERSION > SDL_VERSIONNUM(1, 2, 7))
32 #include "SDL_cpuinfo.h"
33 #endif
34 #include "r_intrin.h"
35 
36 #include "m_alloc.h"
37 #include "doomdef.h"
38 #include "i_system.h"
39 #include "z_zone.h"
40 #include "w_wad.h"
41 #include "r_local.h"
42 #include "v_video.h"
43 #include "doomstat.h"
44 #include "st_stuff.h"
45 
46 #include "gi.h"
47 
48 #undef RANGECHECK
49 
50 // status bar height at bottom of screen
51 // [RH] status bar position at bottom of screen
52 extern	int		ST_Y;
53 
54 //
55 // All drawing to the view buffer is accomplished in this file.
56 // The other refresh files only know about ccordinates,
57 //	not the architecture of the frame buffer.
58 // Conveniently, the frame buffer is a linear one,
59 //	and we need only the base address,
60 //	and the total size == width*height*depth/8.,
61 //
62 
63 extern "C" {
64 drawcolumn_t dcol;
65 drawspan_t dspan;
66 }
67 
68 
69 
70 byte*			viewimage;
71 extern "C" {
72 int 			viewwidth;
73 int 			viewheight;
74 }
75 int 			scaledviewwidth;
76 int 			viewwindowx;
77 int 			viewwindowy;
78 byte**			ylookup;
79 int* 			columnofs;
80 
81 extern "C" {
82 int				realviewwidth;		// [RH] Physical width of view window
83 int				realviewheight;		// [RH] Physical height of view window
84 int				detailxshift;		// [RH] X shift for horizontal detail level
85 int				detailyshift;		// [RH] Y shift for vertical detail level
86 }
87 
88 // [RH] Pointers to the different column drawers.
89 //		These get changed depending on the current
90 //		screen depth.
91 void (*R_DrawColumn)(void);
92 void (*R_DrawColumnHoriz)(void);
93 void (*R_DrawFuzzColumn)(void);
94 void (*R_DrawTranslucentColumn)(void);
95 void (*R_DrawTranslatedColumn)(void);
96 void (*R_DrawSpan)(void);
97 void (*R_DrawSlopeSpan)(void);
98 void (*R_FillColumn)(void);
99 void (*R_FillSpan)(void);
100 void (*R_FillTranslucentSpan)(void);
101 void (*rt_copy1col) (int hx, int sx, int yl, int yh);
102 void (*rt_copy4cols) (int sx, int yl, int yh);
103 void (*rt_map1col) (int hx, int sx, int yl, int yh);
104 void (*rt_map4cols) (int sx, int yl, int yh);
105 void (*rt_lucent1col) (int hx, int sx, int yl, int yh);
106 void (*rt_lucent4cols) (int sx, int yl, int yh);
107 void (*rt_tlate1col) (int hx, int sx, int yl, int yh);
108 void (*rt_tlate4cols) (int sx, int yl, int yh);
109 void (*rt_tlatelucent1col) (int hx, int sx, int yl, int yh);
110 void (*rt_tlatelucent4cols) (int sx, int yl, int yh);
111 
112 // Possibly vectorized functions:
113 void (*R_DrawSpanD)(void);
114 void (*R_DrawSlopeSpanD)(void);
115 void (*r_dimpatchD)(const DCanvas *const cvs, argb_t color, int alpha, int x1, int y1, int w, int h);
116 
117 // ============================================================================
118 //
119 // Fuzz Table
120 //
121 // Framebuffer postprocessing.
122 // Creates a fuzzy image by copying pixels
123 // from adjacent ones to left and right.
124 // Used with an all black colormap, this
125 // could create the SHADOW effect,
126 // i.e. spectres and invisible players.
127 //
128 // ============================================================================
129 
130 #define FUZZTABLE	64		// [RH] FUZZTABLE changed from 50 to 64
131 #define FUZZOFF		(screen->pitch)
132 
133 static int fuzzoffset[FUZZTABLE];
134 static int fuzzpos = 0;
135 
136 static const signed char fuzzinit[FUZZTABLE] = {
137 	1,-1, 1,-1, 1, 1,-1, 1,
138 	1,-1, 1, 1, 1,-1, 1, 1,
139 	1,-1,-1,-1,-1, 1,-1,-1,
140 	1, 1, 1, 1,-1, 1,-1, 1,
141 	1,-1,-1, 1, 1,-1,-1,-1,
142    -1, 1, 1, 1, 1,-1, 1, 1,
143    -1, 1, 1, 1,-1, 1, 1, 1,
144    -1, 1, 1,-1, 1, 1,-1, 1
145 };
146 
R_InitFuzzTable(void)147 void R_InitFuzzTable (void)
148 {
149 	int i;
150 	int fuzzoff;
151 
152 	screen->Lock ();
153 	fuzzoff = FUZZOFF << detailyshift;
154 	screen->Unlock ();
155 
156 	for (i = 0; i < FUZZTABLE; i++)
157 		fuzzoffset[i] = fuzzinit[i] * fuzzoff;
158 }
159 
160 
161 // ============================================================================
162 //
163 // Translucency Table
164 //
165 // ============================================================================
166 
167 /*
168 [RH] This translucency algorithm is based on DOSDoom 0.65's, but uses
169 a 32k RGB table instead of an 8k one. At least on my machine, it's
170 slightly faster (probably because it uses only one shift instead of
171 two), and it looks considerably less green at the ends of the
172 translucency range. The extra size doesn't appear to be an issue.
173 
174 The following note is from DOSDoom 0.65:
175 
176 New translucency algorithm, by Erik Sandberg:
177 
178 Basically, we compute the red, green and blue values for each pixel, and
179 then use a RGB table to check which one of the palette colours that best
180 represents those RGB values. The RGB table is 8k big, with 4 R-bits,
181 5 G-bits and 4 B-bits. A 4k table gives a bit too bad precision, and a 32k
182 table takes up more memory and results in more cache misses, so an 8k
183 table seemed to be quite ultimate.
184 
185 The computation of the RGB for each pixel is accelerated by using two
186 1k tables for each translucency level.
187 The xth element of one of these tables contains the r, g and b values for
188 the colour x, weighted for the current translucency level (for example,
189 the weighted rgb values for background colour at 75% translucency are 1/4
190 of the original rgb values). The rgb values are stored as three
191 low-precision fixed point values, packed into one long per colour:
192 Bit 0-4:   Frac part of blue  (5 bits)
193 Bit 5-8:   Int  part of blue  (4 bits)
194 Bit 9-13:  Frac part of red   (5 bits)
195 Bit 14-17: Int  part of red   (4 bits)
196 Bit 18-22: Frac part of green (5 bits)
197 Bit 23-27: Int  part of green (5 bits)
198 Bit 28-31: All zeros          (4 bits)
199 
200 The point of this format is that the two colours now can be added, and
201 then be converted to a RGB table index very easily: First, we just set
202 all the frac bits and the four upper zero bits to 1. It's now possible
203 to get the RGB table index by anding the current value >> 5 with the
204 current value >> 19. When asm-optimised, this should be the fastest
205 algorithm that uses RGB tables.
206 */
207 
208 
209 // ============================================================================
210 //
211 // Indexed-color Translation Table
212 //
213 // Used to draw player sprites with the green colorramp mapped to others.
214 // Could be used with different translation tables, e.g. the lighter colored
215 // version of the BaronOfHell, the HellKnight, uses identical sprites, kinda
216 // brightened up.
217 //
218 // ============================================================================
219 
220 byte* translationtables;
221 argb_t translationRGB[MAXPLAYERS+1][16];
222 byte *Ranges;
223 static byte *translationtablesmem = NULL;
224 
225 //
226 // R_InitTranslationTables
227 //
228 // Creates the translation tables to map
229 //	the green color ramp to gray, brown, red.
230 // Assumes a given structure of the PLAYPAL.
231 // Could be read from a lump instead.
232 //
R_InitTranslationTables(void)233 void R_InitTranslationTables (void)
234 {
235 	static const char ranges[23][8] = {
236 		"CRBRICK",
237 		"CRTAN",
238 		"CRGRAY",
239 		"CRGREEN",
240 		"CRBROWN",
241 		"CRGOLD",
242 		"CRRED",
243 		"CRBLUE2",
244 		{ 'C','R','O','R','A','N','G','E' },
245 		"CRGRAY", // "White"
246 		{ 'C','R','Y','E','L','L','O','W' },
247 		"CRRED", // "Untranslated"
248 		"CRGRAY", // "Black"
249 		"CRBLUE",
250 		"CRTAN", // "Cream"
251 		"CRGREEN", // "Olive"
252 		"CRGREEN", // "Dark Green"
253 		"CRRED", // "Dark Red"
254 		"CRBROWN", // "Dark Brown"
255 		"CRRED", // "Purple"
256 		"CRGRAY", // "Dark Gray"
257 		"CRBLUE" // "Cyan"
258 	};
259 
260     R_FreeTranslationTables();
261 
262 	translationtablesmem = new byte[256*(MAXPLAYERS+3+22)+255]; // denis - fixme - magic numbers?
263 
264 	// [Toke - fix13]
265 	// denis - cleaned this up somewhat
266 	translationtables = (byte *)(((ptrdiff_t)translationtablesmem + 255) & ~255);
267 
268 	// [RH] Each player now gets their own translation table
269 	//		(soon to be palettes). These are set up during
270 	//		netgame arbitration and as-needed rather than
271 	//		in here. We do, however load some text translation
272 	//		tables from our PWAD (ala BOOM).
273 
274 	for (int i = 0; i < 256; i++)
275 		translationtables[i] = i;
276 
277 	// Set up default translationRGB tables:
278 	palette_t *pal = GetDefaultPalette();
279 	for (int i = 0; i < MAXPLAYERS; ++i)
280 	{
281 		for (int j = 0x70; j < 0x80; ++j)
282 			translationRGB[i][j - 0x70] = pal->basecolors[j];
283 	}
284 
285 	for (int i = 1; i < MAXPLAYERS+3; i++)
286 		memcpy (translationtables + i*256, translationtables, 256);
287 
288 	// create translation tables for dehacked patches that expect them
289 	for (int i = 0x70; i < 0x80; i++) {
290 		// map green ramp to gray, brown, red
291 		translationtables[i+(MAXPLAYERS+0)*256] = 0x60 + (i&0xf);
292 		translationtables[i+(MAXPLAYERS+1)*256] = 0x40 + (i&0xf);
293 		translationtables[i+(MAXPLAYERS+2)*256] = 0x20 + (i&0xf);
294 	}
295 
296 	Ranges = translationtables + (MAXPLAYERS+3)*256;
297 	for (int i = 0; i < 22; i++)
298 		W_ReadLump (W_GetNumForName (ranges[i]), Ranges + 256 * i);
299 
300 }
301 
R_FreeTranslationTables(void)302 void R_FreeTranslationTables (void)
303 {
304     delete[] translationtablesmem;
305     translationtablesmem = NULL;
306 }
307 
308 // [Nes] Vanilla player translation table.
R_BuildClassicPlayerTranslation(int player,int color)309 void R_BuildClassicPlayerTranslation (int player, int color)
310 {
311 	palette_t *pal = GetDefaultPalette();
312 	int i;
313 
314 	if (color == 1) // Indigo
315 		for (i = 0x70; i < 0x80; i++)
316 		{
317 			translationtables[i+(player * 256)] = 0x60 + (i&0xf);
318 			translationRGB[player][i - 0x70] = pal->basecolors[translationtables[i+(player * 256)]];
319 		}
320 	else if (color == 2) // Brown
321 		for (i = 0x70; i < 0x80; i++)
322 		{
323 			translationtables[i+(player * 256)] = 0x40 + (i&0xf);
324 			translationRGB[player][i - 0x70] = pal->basecolors[translationtables[i+(player * 256)]];
325 		}
326 	else if (color == 3) // Red
327 		for (i = 0x70; i < 0x80; i++)
328 		{
329 			translationtables[i+(player * 256)] = 0x20 + (i&0xf);
330 			translationRGB[player][i - 0x70] = pal->basecolors[translationtables[i+(player * 256)]];
331 		}
332 }
333 
R_CopyTranslationRGB(int fromplayer,int toplayer)334 void R_CopyTranslationRGB (int fromplayer, int toplayer)
335 {
336 	for (int i = 0x70; i < 0x80; ++i)
337 	{
338 		translationRGB[toplayer][i - 0x70] = translationRGB[fromplayer][i - 0x70];
339 		translationtables[i+(toplayer * 256)] = translationtables[i+(fromplayer * 256)];
340 	}
341 }
342 
343 // [RH] Create a player's translation table based on
344 //		a given mid-range color.
R_BuildPlayerTranslation(int player,int color)345 void R_BuildPlayerTranslation (int player, int color)
346 {
347 	palette_t *pal = GetDefaultPalette();
348 	byte *table = &translationtables[player * 256];
349 	int i;
350 	float r = (float)RPART(color) / 255.0f;
351 	float g = (float)GPART(color) / 255.0f;
352 	float b = (float)BPART(color) / 255.0f;
353 	float h, s, v;
354 	float sdelta, vdelta;
355 
356 	RGBtoHSV (r, g, b, &h, &s, &v);
357 
358 	s -= 0.23f;
359 	if (s < 0.0f)
360 		s = 0.0f;
361 	sdelta = 0.014375f;
362 
363 	v += 0.1f;
364 	if (v > 1.0f)
365 		v = 1.0f;
366 	vdelta = -0.05882f;
367 
368 	for (i = 0x70; i < 0x80; i++) {
369 		HSVtoRGB (&r, &g, &b, h, s, v);
370 
371 		// Set up RGB values for 32bpp translation:
372 		translationRGB[player][i - 0x70] = MAKERGB(
373 			(int)(r * 255.0f),
374 			(int)(g * 255.0f),
375 			(int)(b * 255.0f)
376 		);
377 
378 		table[i] = BestColor (pal->basecolors,
379 							  (int)(r * 255.0f),
380 							  (int)(g * 255.0f),
381 							  (int)(b * 255.0f),
382 							  pal->numcolors);
383 		s += sdelta;
384 		if (s > 1.0f) {
385 			s = 1.0f;
386 			sdelta = 0.0f;
387 		}
388 
389 		v += vdelta;
390 		if (v < 0.0f) {
391 			v = 0.0f;
392 			vdelta = 0.0f;
393 		}
394 	}
395 }
396 
397 
398 // ============================================================================
399 //
400 // Spans
401 //
402 // With DOOM style restrictions on view orientation,
403 // the floors and ceilings consist of horizontal slices
404 // or spans with constant z depth.
405 // However, rotation around the world z axis is possible,
406 // thus this mapping, while simpler and faster than
407 // perspective correct texture mapping, has to traverse
408 // the texture at an angle in all but a few cases.
409 // In consequence, flats are not stored by column (like walls),
410 // and the inner loop has to step in texture space u and v.
411 //
412 // ============================================================================
413 
414 
415 // ============================================================================
416 //
417 // Generic Drawers
418 //
419 // Templated versions of column and span drawing functions
420 //
421 // ============================================================================
422 
423 //
424 // R_BlankColumn
425 //
426 // [SL] - Does nothing (obviously). Used when a column drawing function
427 // pointer should not draw anything.
428 //
R_BlankColumn()429 void R_BlankColumn()
430 {
431 }
432 
433 //
434 // R_BlankSpan
435 //
436 // [SL] - Does nothing (obviously). Used when a span drawing function
437 // pointer should not draw anything.
438 //
R_BlankSpan()439 void R_BlankSpan()
440 {
441 }
442 
443 //
444 // R_FillColumnGeneric
445 //
446 // Templated version of a function to fill a column with a solid color.
447 // The data type of the destination pixels and a color-remapping functor
448 // are passed as template parameters.
449 //
450 template<typename PIXEL_T, typename COLORFUNC>
R_FillColumnGeneric(PIXEL_T * dest,const drawcolumn_t & drawcolumn)451 static forceinline void R_FillColumnGeneric(PIXEL_T* dest, const drawcolumn_t& drawcolumn)
452 {
453 #ifdef RANGECHECK
454 	if (drawcolumn.x >= screen->width || drawcolumn.yl < 0 || drawcolumn.yh >= screen->height)
455 	{
456 		Printf (PRINT_HIGH, "R_FillColumn: %i to %i at %i\n", drawcolumn.yl, drawcolumn.yh, drawcolumn.x);
457 		return;
458 	}
459 #endif
460 
461 	int color = drawcolumn.color;
462 	int pitch = drawcolumn.pitch / sizeof(PIXEL_T);
463 	int count = drawcolumn.yh - drawcolumn.yl + 1;
464 	if (count <= 0)
465 		return;
466 
467 	COLORFUNC colorfunc(drawcolumn);
468 
469 	do {
470 		colorfunc(color, dest);
471 		dest += pitch;
472 	} while (--count);
473 }
474 
475 
476 //
477 // R_DrawColumnGeneric
478 //
479 // A column is a vertical slice/span from a wall texture that,
480 // given the DOOM style restrictions on the view orientation,
481 // will always have constant z depth.
482 // Thus a special case loop for very fast rendering can
483 // be used. It has also been used with Wolfenstein 3D.
484 //
485 // Templated version of a column mapping function.
486 // The data type of the destination pixels and a color-remapping functor
487 // are passed as template parameters.
488 //
489 template<typename PIXEL_T, typename COLORFUNC>
R_DrawColumnGeneric(PIXEL_T * dest,const drawcolumn_t & drawcolumn)490 static forceinline void R_DrawColumnGeneric(PIXEL_T* dest, const drawcolumn_t& drawcolumn)
491 {
492 #ifdef RANGECHECK
493 	if (drawcolumn.x >= screen->width || drawcolumn.yl < 0 || drawcolumn.yh >= screen->height)
494 	{
495 		Printf (PRINT_HIGH, "R_DrawColumn: %i to %i at %i\n", drawcolumn.yl, drawcolumn.yh, drawcolumn.x);
496 		return;
497 	}
498 #endif
499 
500 	palindex_t* source = drawcolumn.source;
501 	int pitch = drawcolumn.pitch / sizeof(PIXEL_T);
502 	int count = drawcolumn.yh - drawcolumn.yl + 1;
503 	if (count <= 0)
504 		return;
505 
506 	const fixed_t fracstep = drawcolumn.iscale;
507 	fixed_t frac = drawcolumn.texturefrac;
508 
509 	const int texheight = drawcolumn.textureheight;
510 	const int mask = (texheight >> FRACBITS) - 1;
511 
512 	COLORFUNC colorfunc(drawcolumn);
513 
514 	// [SL] Properly tile textures whose heights are not a power-of-2,
515 	// avoiding a tutti-frutti effect.  From Eternity Engine.
516 	if (texheight & (texheight - 1))
517 	{
518 		// texture height is NOT a power-of-2
519 		// just do a simple blit to the dest buffer (I'm lazy)
520 
521 		if (frac < 0)
522 			while ((frac += texheight) < 0);
523 		else
524 			while (frac >= texheight)
525 				frac -= texheight;
526 
527 		while (count--)
528 		{
529 			colorfunc(source[frac >> FRACBITS], dest);
530 			dest += pitch;
531 			if ((frac += fracstep) >= texheight)
532 				frac -= texheight;
533 		}
534 	}
535 	else
536 	{
537 		// texture height is a power-of-2
538 		// do some loop unrolling
539 		while (count >= 8)
540 		{
541 			colorfunc(source[(frac >> FRACBITS) & mask], dest);
542 			dest += pitch; frac += fracstep;
543 			colorfunc(source[(frac >> FRACBITS) & mask], dest);
544 			dest += pitch; frac += fracstep;
545 			colorfunc(source[(frac >> FRACBITS) & mask], dest);
546 			dest += pitch; frac += fracstep;
547 			colorfunc(source[(frac >> FRACBITS) & mask], dest);
548 			dest += pitch; frac += fracstep;
549 			colorfunc(source[(frac >> FRACBITS) & mask], dest);
550 			dest += pitch; frac += fracstep;
551 			colorfunc(source[(frac >> FRACBITS) & mask], dest);
552 			dest += pitch; frac += fracstep;
553 			colorfunc(source[(frac >> FRACBITS) & mask], dest);
554 			dest += pitch; frac += fracstep;
555 			colorfunc(source[(frac >> FRACBITS) & mask], dest);
556 			dest += pitch; frac += fracstep;
557 			count -= 8;
558 		}
559 
560 		if (count & 1)
561 		{
562 			colorfunc(source[(frac >> FRACBITS) & mask], dest);
563 			dest += pitch; frac += fracstep;
564 		}
565 
566 		if (count & 2)
567 		{
568 			colorfunc(source[(frac >> FRACBITS) & mask], dest);
569 			dest += pitch; frac += fracstep;
570 			colorfunc(source[(frac >> FRACBITS) & mask], dest);
571 			dest += pitch; frac += fracstep;
572 		}
573 
574 		if (count & 4)
575 		{
576 			colorfunc(source[(frac >> FRACBITS) & mask], dest);
577 			dest += pitch; frac += fracstep;
578 			colorfunc(source[(frac >> FRACBITS) & mask], dest);
579 			dest += pitch; frac += fracstep;
580 			colorfunc(source[(frac >> FRACBITS) & mask], dest);
581 			dest += pitch; frac += fracstep;
582 			colorfunc(source[(frac >> FRACBITS) & mask], dest);
583 			dest += pitch; frac += fracstep;
584 		}
585 	}
586 }
587 
588 
589 //
590 // R_FillSpanGeneric
591 //
592 // Templated version of a function to fill a span with a solid color.
593 // The data type of the destination pixels and a color-remapping functor
594 // are passed as template parameters.
595 //
596 template<typename PIXEL_T, typename COLORFUNC>
R_FillSpanGeneric(PIXEL_T * dest,const drawspan_t & drawspan)597 static forceinline void R_FillSpanGeneric(PIXEL_T* dest, const drawspan_t& drawspan)
598 {
599 #ifdef RANGECHECK
600 	if (drawspan.x2 < drawspan.x1 || drawspan.x1 < 0 || drawspan.x2 >= viewwidth ||
601 		drawspan.y >= viewheight || drawspan.y < 0)
602 	{
603 		Printf(PRINT_HIGH, "R_FillSpan: %i to %i at %i", drawspan.x1, drawspan.x2, drawspan.y);
604 		return;
605 	}
606 #endif
607 
608 	int color = drawspan.color;
609 	int colsize = drawspan.colsize;
610 	int count = drawspan.x2 - drawspan.x1 + 1;
611 	if (count <= 0)
612 		return;
613 
614 	COLORFUNC colorfunc(drawspan);
615 
616 	do {
617 		colorfunc(color, dest);
618 		dest += colsize;
619 	} while (--count);
620 }
621 
622 
623 //
624 // R_DrawLevelSpanGeneric
625 //
626 // Templated version of a function to fill a horizontal span with a texture map.
627 // The data type of the destination pixels and a color-remapping functor
628 // are passed as template parameters.
629 //
630 template<typename PIXEL_T, typename COLORFUNC>
R_DrawLevelSpanGeneric(PIXEL_T * dest,const drawspan_t & drawspan)631 static forceinline void R_DrawLevelSpanGeneric(PIXEL_T* dest, const drawspan_t& drawspan)
632 {
633 #ifdef RANGECHECK
634 	if (drawspan.x2 < drawspan.x1 || drawspan.x1 < 0 || drawspan.x2 >= viewwidth ||
635 		drawspan.y >= viewheight || drawspan.y < 0)
636 	{
637 		Printf(PRINT_HIGH, "R_DrawLevelSpan: %i to %i at %i", drawspan.x1, drawspan.x2, drawspan.y);
638 		return;
639 	}
640 #endif
641 
642 	palindex_t* source = drawspan.source;
643 	int colsize = drawspan.colsize;
644 	int count = drawspan.x2 - drawspan.x1 + 1;
645 	if (count <= 0)
646 		return;
647 
648 	dsfixed_t xfrac = drawspan.xfrac;
649 	dsfixed_t yfrac = drawspan.yfrac;
650 	const dsfixed_t xstep = drawspan.xstep;
651 	const dsfixed_t ystep = drawspan.ystep;
652 
653 	COLORFUNC colorfunc(drawspan);
654 
655 	do {
656 		// Current texture index in u,v.
657 		const int spot = ((yfrac >> (32-6-6)) & (63*64)) + (xfrac >> (32-6));
658 
659 		// Lookup pixel from flat texture tile,
660 		//  re-index using light/colormap.
661 
662 		colorfunc(source[spot], dest);
663 		dest += colsize;
664 
665 		// Next step in u,v.
666 		xfrac += xstep;
667 		yfrac += ystep;
668 	} while (--count);
669 }
670 
671 
672 //
673 // R_DrawSlopedSpanGeneric
674 //
675 // Texture maps a sloped surface using affine texturemapping for each row of
676 // the span.  Not as pretty as a perfect texturemapping but should be much
677 // faster.
678 //
679 // Based on R_DrawSlope_8_64 from Eternity Engine, written by SoM/Quasar
680 //
681 // The data type of the destination pixels and a color-remapping functor
682 // are passed as template parameters.
683 //
684 template<typename PIXEL_T, typename COLORFUNC>
R_DrawSlopedSpanGeneric(PIXEL_T * dest,const drawspan_t & drawspan)685 static forceinline void R_DrawSlopedSpanGeneric(PIXEL_T* dest, const drawspan_t& drawspan)
686 {
687 #ifdef RANGECHECK
688 	if (drawspan.x2 < drawspan.x1 || drawspan.x1 < 0 || drawspan.x2 >= viewwidth ||
689 		drawspan.y >= viewheight || drawspan.y < 0)
690 	{
691 		Printf(PRINT_HIGH, "R_DrawSlopedSpan: %i to %i at %i", drawspan.x1, drawspan.x2, drawspan.y);
692 		return;
693 	}
694 #endif
695 
696 	palindex_t* source = drawspan.source;
697 	int colsize = drawspan.colsize;
698 	int count = drawspan.x2 - drawspan.x1 + 1;
699 	if (count <= 0)
700 		return;
701 
702 	float iu = drawspan.iu, iv = drawspan.iv;
703 	const float ius = drawspan.iustep, ivs = drawspan.ivstep;
704 	float id = drawspan.id, ids = drawspan.idstep;
705 
706 	int ltindex = 0;
707 
708 	shaderef_t colormap;
709 	COLORFUNC colorfunc(drawspan);
710 
711 	while (count >= SPANJUMP)
712 	{
713 		const float mulstart = 65536.0f / id;
714 		id += ids * SPANJUMP;
715 		const float mulend = 65536.0f / id;
716 
717 		const float ustart = iu * mulstart;
718 		const float vstart = iv * mulstart;
719 
720 		fixed_t ufrac = (fixed_t)ustart;
721 		fixed_t vfrac = (fixed_t)vstart;
722 
723 		iu += ius * SPANJUMP;
724 		iv += ivs * SPANJUMP;
725 
726 		const float uend = iu * mulend;
727 		const float vend = iv * mulend;
728 
729 		fixed_t ustep = (fixed_t)((uend - ustart) * INTERPSTEP);
730 		fixed_t vstep = (fixed_t)((vend - vstart) * INTERPSTEP);
731 
732 		int incount = SPANJUMP;
733 		while (incount--)
734 		{
735 			colormap = drawspan.slopelighting[ltindex++];
736 
737 			const int spot = ((vfrac >> 10) & 0xFC0) | ((ufrac >> 16) & 63);
738 			colorfunc(source[spot], dest);
739 			dest += colsize;
740 			ufrac += ustep;
741 			vfrac += vstep;
742 		}
743 
744 		count -= SPANJUMP;
745 	}
746 
747 	if (count > 0)
748 	{
749 		const float mulstart = 65536.0f / id;
750 		id += ids * count;
751 		const float mulend = 65536.0f / id;
752 
753 		const float ustart = iu * mulstart;
754 		const float vstart = iv * mulstart;
755 
756 		fixed_t ufrac = (fixed_t)ustart;
757 		fixed_t vfrac = (fixed_t)vstart;
758 
759 		iu += ius * count;
760 		iv += ivs * count;
761 
762 		const float uend = iu * mulend;
763 		const float vend = iv * mulend;
764 
765 		fixed_t ustep = (fixed_t)((uend - ustart) / count);
766 		fixed_t vstep = (fixed_t)((vend - vstart) / count);
767 
768 		int incount = count;
769 		while (incount--)
770 		{
771 			colormap = drawspan.slopelighting[ltindex++];
772 
773 			const int spot = ((vfrac >> 10) & 0xFC0) | ((ufrac >> 16) & 63);
774 			colorfunc(source[spot], dest);
775 			dest += colsize;
776 			ufrac += ustep;
777 			vfrac += vstep;
778 		}
779 	}
780 }
781 
782 
783 /************************************/
784 /*									*/
785 /* Palettized drawers (C versions)	*/
786 /*									*/
787 /************************************/
788 
789 // ----------------------------------------------------------------------------
790 //
791 // 8bpp color remapping functors
792 //
793 // These functors provide a variety of ways to manipulate a source pixel
794 // color (given by 8bpp palette index) and write the result to the destination
795 // buffer.
796 //
797 // The functors are instantiated with a shaderef_t* parameter (typically
798 // dcol.colormap or ds_colormap) that will be used to shade the pixel.
799 //
800 // ----------------------------------------------------------------------------
801 
802 class PaletteFunc
803 {
804 public:
PaletteFunc(const drawcolumn_t & drawcolumn)805 	PaletteFunc(const drawcolumn_t& drawcolumn) { }
PaletteFunc(const drawspan_t & drawspan)806 	PaletteFunc(const drawspan_t& drawspan) { }
807 
operator ()(byte c,palindex_t * dest) const808 	forceinline void operator()(byte c, palindex_t* dest) const
809 	{
810 		*dest = c;
811 	}
812 };
813 
814 class PaletteColormapFunc
815 {
816 public:
PaletteColormapFunc(const drawcolumn_t & drawcolumn)817 	PaletteColormapFunc(const drawcolumn_t& drawcolumn) :
818 			colormap(drawcolumn.colormap) { }
PaletteColormapFunc(const drawspan_t & drawspan)819 	PaletteColormapFunc(const drawspan_t& drawspan) :
820 			colormap(drawspan.colormap) { }
821 
operator ()(byte c,palindex_t * dest) const822 	forceinline void operator()(byte c, palindex_t* dest) const
823 	{
824 		*dest = colormap.index(c);
825 	}
826 
827 private:
828 	const shaderef_t& colormap;
829 };
830 
831 class PaletteFuzzyFunc
832 {
833 public:
PaletteFuzzyFunc(const drawcolumn_t & drawcolum)834 	PaletteFuzzyFunc(const drawcolumn_t& drawcolum) :
835 			colormap(&GetDefaultPalette()->maps, 6) { }
836 
operator ()(byte c,palindex_t * dest) const837 	forceinline void operator()(byte c, palindex_t* dest) const
838 	{
839 		*dest = colormap.index(dest[fuzzoffset[fuzzpos]]);
840 		fuzzpos = (fuzzpos + 1) & (FUZZTABLE - 1);
841 	}
842 
843 private:
844 	shaderef_t colormap;
845 };
846 
847 class PaletteTranslucentColormapFunc
848 {
849 public:
PaletteTranslucentColormapFunc(const drawcolumn_t & drawcolumn)850 	PaletteTranslucentColormapFunc(const drawcolumn_t& drawcolumn) :
851 			colormap(drawcolumn.colormap)
852 	{
853 		calculate_alpha(drawcolumn.translevel);
854 	}
855 
PaletteTranslucentColormapFunc(const drawspan_t & drawspan)856 	PaletteTranslucentColormapFunc(const drawspan_t& drawspan) :
857 			colormap(drawspan.colormap)
858 	{
859 		calculate_alpha(drawspan.translevel);
860 	}
861 
operator ()(byte c,palindex_t * dest) const862 	forceinline void operator()(byte c, palindex_t* dest) const
863 	{
864 		const palindex_t fg = colormap.index(c);
865 		const palindex_t bg = *dest;
866 
867 		*dest = rt_blend2<palindex_t>(bg, bga, fg, fga);
868 	}
869 
870 private:
calculate_alpha(fixed_t translevel)871 	void calculate_alpha(fixed_t translevel)
872 	{
873 		fga = (translevel & ~0x03FF) >> 8;
874 		bga = 255 - fga;
875 	}
876 
877 	const shaderef_t& colormap;
878 	int fga, bga;
879 };
880 
881 class PaletteTranslatedColormapFunc
882 {
883 public:
PaletteTranslatedColormapFunc(const drawcolumn_t & drawcolumn)884 	PaletteTranslatedColormapFunc(const drawcolumn_t& drawcolumn) :
885 			colormap(drawcolumn.colormap), translation(drawcolumn.translation) { }
886 
operator ()(byte c,palindex_t * dest) const887 	forceinline void operator()(byte c, palindex_t* dest) const
888 	{
889 		*dest = colormap.index(translation.tlate(c));
890 	}
891 
892 private:
893 	const shaderef_t& colormap;
894 	const translationref_t& translation;
895 };
896 
897 class PaletteTranslatedTranslucentColormapFunc
898 {
899 public:
PaletteTranslatedTranslucentColormapFunc(const drawcolumn_t & drawcolumn)900 	PaletteTranslatedTranslucentColormapFunc(const drawcolumn_t& drawcolumn) :
901 			tlatefunc(drawcolumn), translation(drawcolumn.translation) { }
902 
operator ()(byte c,palindex_t * dest) const903 	forceinline void operator()(byte c, palindex_t* dest) const
904 	{
905 		tlatefunc(translation.tlate(c), dest);
906 	}
907 
908 private:
909 	PaletteTranslucentColormapFunc tlatefunc;
910 	const translationref_t& translation;
911 };
912 
913 class PaletteSlopeColormapFunc
914 {
915 public:
PaletteSlopeColormapFunc(const drawspan_t & drawspan)916 	PaletteSlopeColormapFunc(const drawspan_t& drawspan) :
917 			colormap(drawspan.slopelighting) { }
918 
operator ()(byte c,palindex_t * dest)919 	forceinline void operator()(byte c, palindex_t* dest)
920 	{
921 		*dest = colormap->index(c);
922 		colormap++;
923 	}
924 
925 private:
926 	const shaderef_t* colormap;
927 };
928 
929 
930 // ----------------------------------------------------------------------------
931 //
932 // 8bpp color column drawing wrappers
933 //
934 // ----------------------------------------------------------------------------
935 
936 #define FB_COLDEST_P ((palindex_t*)(ylookup[dcol.yl] + columnofs[dcol.x]))
937 
938 //
939 // R_FillColumnP
940 //
941 // Fills a column in the 8bpp palettized screen buffer with a solid color,
942 // determined by dcol.color. Performs no shading.
943 //
R_FillColumnP()944 void R_FillColumnP()
945 {
946 	R_FillColumnGeneric<palindex_t, PaletteFunc>(FB_COLDEST_P, dcol);
947 }
948 
949 //
950 // R_DrawColumnP
951 //
952 // Renders a column to the 8bpp palettized screen buffer from the source buffer
953 // dcol.source and scaled by dcol.iscale. Shading is performed using dcol.colormap.
954 //
R_DrawColumnP()955 void R_DrawColumnP()
956 {
957 	R_DrawColumnGeneric<palindex_t, PaletteColormapFunc>(FB_COLDEST_P, dcol);
958 }
959 
960 //
961 // R_StretchColumnP
962 //
963 // Renders a column to the 8bpp palettized screen buffer from the source buffer
964 // dcol.source and scaled by dcol.iscale. Performs no shading.
965 //
R_StretchColumnP()966 void R_StretchColumnP()
967 {
968 	R_DrawColumnGeneric<palindex_t, PaletteFunc>(FB_COLDEST_P, dcol);
969 }
970 
971 //
972 // R_DrawFuzzColumnP
973 //
974 // Alters a column in the 8bpp palettized screen buffer using Doom's partial
975 // invisibility effect, which shades the column and rearranges the ordering
976 // the pixels to create distortion. Shading is performed using colormap 6.
977 //
R_DrawFuzzColumnP()978 void R_DrawFuzzColumnP()
979 {
980 	// adjust the borders (prevent buffer over/under-reads)
981 	if (dcol.yl <= 0)
982 		dcol.yl = 1;
983 	if (dcol.yh >= viewheight - 1)
984 		dcol.yh = viewheight - 2;
985 
986 	R_FillColumnGeneric<palindex_t, PaletteFuzzyFunc>(FB_COLDEST_P, dcol);
987 
988 	fuzzpos = (fuzzpos + 3) & (FUZZTABLE - 1);
989 }
990 
991 //
992 // R_DrawTranslucentColumnP
993 //
994 // Renders a translucent column to the 8bpp palettized screen buffer from the
995 // source buffer dcol.source and scaled by dcol.iscale. The amount of
996 // translucency is controlled by dcol.translevel. Shading is performed using
997 // dcol.colormap.
998 //
R_DrawTranslucentColumnP()999 void R_DrawTranslucentColumnP()
1000 {
1001 	R_DrawColumnGeneric<palindex_t, PaletteTranslucentColormapFunc>(FB_COLDEST_P, dcol);
1002 }
1003 
1004 //
1005 // R_DrawTranslatedColumnP
1006 //
1007 // Renders a column to the 8bpp palettized screen buffer with color-remapping
1008 // from the source buffer dcol.source and scaled by dcol.iscale. The translation
1009 // table is supplied by dcol.translation. Shading is performed using dcol.colormap.
1010 //
R_DrawTranslatedColumnP()1011 void R_DrawTranslatedColumnP()
1012 {
1013 	R_DrawColumnGeneric<palindex_t, PaletteTranslatedColormapFunc>(FB_COLDEST_P, dcol);
1014 }
1015 
1016 //
1017 // R_DrawTlatedLucentColumnP
1018 //
1019 // Renders a translucent column to the 8bpp palettized screen buffer with
1020 // color-remapping from the source buffer dcol.source and scaled by dcol.iscale.
1021 // The translation table is supplied by dcol.translation and the amount of
1022 // translucency is controlled by dcol.translevel. Shading is performed using
1023 // dcol.colormap.
1024 //
R_DrawTlatedLucentColumnP()1025 void R_DrawTlatedLucentColumnP()
1026 {
1027 	R_DrawColumnGeneric<palindex_t, PaletteTranslatedTranslucentColormapFunc>(FB_COLDEST_P, dcol);
1028 }
1029 
1030 //
1031 // R_FillColumnHorizP
1032 //
1033 // Fills a column in an 8bpp palettized buffer dcol.temp with a solid color,
1034 // determined by dcol.color. Performs no shading.
1035 //
R_FillColumnHorizP()1036 void R_FillColumnHorizP()
1037 {
1038 	if (dcol.yl > dcol.yh)
1039 		return;
1040 
1041 	const int x = dcol.x & 3;
1042 	unsigned int **span = &dc_ctspan[x];
1043 
1044 	(*span)[0] = dcol.yl;
1045 	(*span)[1] = dcol.yh;
1046 	*span += 2;
1047 	palindex_t* dest = &dc_temp[x + 4*dcol.yl];
1048 
1049 	int oldpitch = dcol.pitch;
1050 	dcol.pitch = 4;
1051 
1052 	R_FillColumnGeneric<palindex_t, PaletteFunc>(dest, dcol);
1053 
1054 	dcol.pitch = oldpitch;
1055 }
1056 
1057 //
1058 // R_DrawColumnHorizP
1059 //
1060 // Renders a column to an 8bpp palettized buffer dcol.temp from the source buffer
1061 // dcol.source and scaled by dcol.iscale. The column is rendered to the buffer in
1062 // an interleaved format, writing to every 4th byte of the buffer. Performs
1063 // no shading.
1064 //
R_DrawColumnHorizP()1065 void R_DrawColumnHorizP()
1066 {
1067 	if (dcol.yl > dcol.yh)
1068 		return;
1069 
1070 	const int x = dcol.x & 3;
1071 	unsigned int **span = &dc_ctspan[x];
1072 
1073 	(*span)[0] = dcol.yl;
1074 	(*span)[1] = dcol.yh;
1075 	*span += 2;
1076 	palindex_t* dest = &dc_temp[x + 4*dcol.yl];
1077 
1078 	int oldpitch = dcol.pitch;
1079 	dcol.pitch = 4;
1080 
1081 	R_DrawColumnGeneric<palindex_t, PaletteFunc>(dest, dcol);
1082 
1083 	dcol.pitch = oldpitch;
1084 }
1085 
1086 
1087 // ----------------------------------------------------------------------------
1088 //
1089 // 8bpp color span drawing wrappers
1090 //
1091 // ----------------------------------------------------------------------------
1092 
1093 #define FB_SPANDEST_P ((palindex_t*)(ylookup[dspan.y] + columnofs[dspan.x1]))
1094 
1095 //
1096 // R_FillSpanP
1097 //
1098 // Fills a span in the 8bpp palettized screen buffer with a solid color,
1099 // determined by ds_color. Performs no shading.
1100 //
R_FillSpanP()1101 void R_FillSpanP()
1102 {
1103 	R_FillSpanGeneric<palindex_t, PaletteFunc>(FB_SPANDEST_P, dspan);
1104 }
1105 
1106 //
1107 // R_FillTranslucentSpanP
1108 //
1109 // Fills a span in the 8bpp palettized screen buffer with a solid color,
1110 // determined by ds_color using translucency. Shading is performed
1111 // using ds_colormap.
1112 //
R_FillTranslucentSpanP()1113 void R_FillTranslucentSpanP()
1114 {
1115 	R_FillSpanGeneric<palindex_t, PaletteTranslucentColormapFunc>(FB_SPANDEST_P, dspan);
1116 }
1117 
1118 //
1119 // R_DrawSpanP
1120 //
1121 // Renders a span for a level plane to the 8bpp palettized screen buffer from
1122 // the source buffer ds_source. Shading is performed using ds_colormap.
1123 //
R_DrawSpanP()1124 void R_DrawSpanP()
1125 {
1126 	R_DrawLevelSpanGeneric<palindex_t, PaletteColormapFunc>(FB_SPANDEST_P, dspan);
1127 }
1128 
1129 //
1130 // R_DrawSlopeSpanP
1131 //
1132 // Renders a span for a sloped plane to the 8bpp palettized screen buffer from
1133 // the source buffer ds_source. Shading is performed using ds_colormap.
1134 //
R_DrawSlopeSpanP()1135 void R_DrawSlopeSpanP()
1136 {
1137 	R_DrawSlopedSpanGeneric<palindex_t, PaletteSlopeColormapFunc>(FB_SPANDEST_P, dspan);
1138 }
1139 
1140 
1141 /****************************************/
1142 /*										*/
1143 /* [RH] ARGB8888 drawers (C versions)	*/
1144 /*										*/
1145 /****************************************/
1146 
1147 // ----------------------------------------------------------------------------
1148 //
1149 // 32bpp color remapping functors
1150 //
1151 // These functors provide a variety of ways to manipulate a source pixel
1152 // color (given by 8bpp palette index) and write the result to the destination
1153 // buffer.
1154 //
1155 // The functors are instantiated with a shaderef_t* parameter (typically
1156 // dcol.colormap or ds_colormap) that will be used to shade the pixel.
1157 //
1158 // ----------------------------------------------------------------------------
1159 
1160 class DirectFunc
1161 {
1162 public:
DirectFunc(const drawcolumn_t & drawcolumn)1163 	DirectFunc(const drawcolumn_t& drawcolumn) { }
DirectFunc(const drawspan_t & drawspan)1164 	DirectFunc(const drawspan_t& drawspan) { }
1165 
operator ()(byte c,argb_t * dest) const1166 	forceinline void operator()(byte c, argb_t* dest) const
1167 	{
1168 		*dest = basecolormap.shade(c);
1169 	}
1170 };
1171 
1172 class DirectColormapFunc
1173 {
1174 public:
DirectColormapFunc(const drawcolumn_t & drawcolumn)1175 	DirectColormapFunc(const drawcolumn_t& drawcolumn) :
1176 			colormap(drawcolumn.colormap) { }
DirectColormapFunc(const drawspan_t & drawspan)1177 	DirectColormapFunc(const drawspan_t& drawspan) :
1178 			colormap(drawspan.colormap) { }
1179 
operator ()(byte c,argb_t * dest) const1180 	forceinline void operator()(byte c, argb_t* dest) const
1181 	{
1182 		*dest = colormap.shade(c);
1183 	}
1184 
1185 private:
1186 	const shaderef_t& colormap;
1187 };
1188 
1189 class DirectFuzzyFunc
1190 {
1191 public:
DirectFuzzyFunc(const drawcolumn_t & drawcolumn)1192 	DirectFuzzyFunc(const drawcolumn_t& drawcolumn) { }
1193 
operator ()(byte c,argb_t * dest) const1194 	forceinline void operator()(byte c, argb_t* dest) const
1195 	{
1196 		argb_t work = dest[fuzzoffset[fuzzpos] >> 2];
1197 		*dest = work - ((work >> 2) & 0x3f3f3f);
1198 		fuzzpos = (fuzzpos + 1) & (FUZZTABLE - 1);
1199 	}
1200 };
1201 
1202 class DirectTranslucentColormapFunc
1203 {
1204 public:
DirectTranslucentColormapFunc(const drawcolumn_t & drawcolumn)1205 	DirectTranslucentColormapFunc(const drawcolumn_t& drawcolumn) :
1206 			colormap(drawcolumn.colormap)
1207 	{
1208 		calculate_alpha(drawcolumn.translevel);
1209 	}
1210 
DirectTranslucentColormapFunc(const drawspan_t & drawspan)1211 	DirectTranslucentColormapFunc(const drawspan_t& drawspan) :
1212 			colormap(drawspan.colormap)
1213 	{
1214 		calculate_alpha(drawspan.translevel);
1215 	}
1216 
operator ()(byte c,argb_t * dest) const1217 	forceinline void operator()(byte c, argb_t* dest) const
1218 	{
1219 		argb_t fg = colormap.shade(c);
1220 		argb_t bg = *dest;
1221 		*dest = alphablend2a(bg, bga, fg, fga);
1222 	}
1223 
1224 private:
calculate_alpha(fixed_t translevel)1225 	void calculate_alpha(fixed_t translevel)
1226 	{
1227 		fga = (translevel & ~0x03FF) >> 8;
1228 		bga = 255 - fga;
1229 	}
1230 
1231 	const shaderef_t& colormap;
1232 	int fga, bga;
1233 };
1234 
1235 class DirectTranslatedColormapFunc
1236 {
1237 public:
DirectTranslatedColormapFunc(const drawcolumn_t & drawcolumn)1238 	DirectTranslatedColormapFunc(const drawcolumn_t& drawcolumn) :
1239 			colormap(drawcolumn.colormap), translation(drawcolumn.translation) { }
1240 
operator ()(byte c,argb_t * dest) const1241 	forceinline void operator()(byte c, argb_t* dest) const
1242 	{
1243 		*dest = colormap.tlate(translation, c);
1244 	}
1245 
1246 private:
1247 	const shaderef_t& colormap;
1248 	const translationref_t& translation;
1249 };
1250 
1251 class DirectTranslatedTranslucentColormapFunc
1252 {
1253 public:
DirectTranslatedTranslucentColormapFunc(const drawcolumn_t & drawcolumn)1254 	DirectTranslatedTranslucentColormapFunc(const drawcolumn_t& drawcolumn) :
1255 			tlatefunc(drawcolumn), translation(drawcolumn.translation) { }
1256 
operator ()(byte c,argb_t * dest) const1257 	forceinline void operator()(byte c, argb_t* dest) const
1258 	{
1259 		tlatefunc(translation.tlate(c), dest);
1260 	}
1261 
1262 private:
1263 	DirectTranslucentColormapFunc tlatefunc;
1264 	const translationref_t& translation;
1265 };
1266 
1267 class DirectSlopeColormapFunc
1268 {
1269 public:
DirectSlopeColormapFunc(const drawspan_t & drawspan)1270 	DirectSlopeColormapFunc(const drawspan_t& drawspan) :
1271 			colormap(drawspan.slopelighting) { }
1272 
operator ()(byte c,argb_t * dest)1273 	forceinline void operator()(byte c, argb_t* dest)
1274 	{
1275 		*dest = colormap->shade(c);
1276 		colormap++;
1277 	}
1278 
1279 private:
1280 	const shaderef_t* colormap;
1281 };
1282 
1283 
1284 // ----------------------------------------------------------------------------
1285 //
1286 // 32bpp color drawing wrappers
1287 //
1288 // ----------------------------------------------------------------------------
1289 
1290 #define FB_COLDEST_D ((argb_t*)(ylookup[dcol.yl] + columnofs[dcol.x]))
1291 
1292 //
1293 // R_FillColumnD
1294 //
1295 // Fills a column in the 32bpp ARGB8888 screen buffer with a solid color,
1296 // determined by dcol.color. Performs no shading.
1297 //
R_FillColumnD()1298 void R_FillColumnD()
1299 {
1300 	R_FillColumnGeneric<argb_t, DirectFunc>(FB_COLDEST_D, dcol);
1301 }
1302 
1303 //
1304 // R_DrawColumnD
1305 //
1306 // Renders a column to the 32bpp ARGB8888 screen buffer from the source buffer
1307 // dcol.source and scaled by dcol.iscale. Shading is performed using dcol.colormap.
1308 //
R_DrawColumnD()1309 void R_DrawColumnD()
1310 {
1311 	R_DrawColumnGeneric<argb_t, DirectColormapFunc>(FB_COLDEST_D, dcol);
1312 }
1313 
1314 //
1315 // R_DrawFuzzColumnD
1316 //
1317 // Alters a column in the 32bpp ARGB8888 screen buffer using Doom's partial
1318 // invisibility effect, which shades the column and rearranges the ordering
1319 // the pixels to create distortion. Shading is performed using colormap 6.
1320 //
R_DrawFuzzColumnD()1321 void R_DrawFuzzColumnD()
1322 {
1323 	// adjust the borders (prevent buffer over/under-reads)
1324 	if (dcol.yl <= 0)
1325 		dcol.yl = 1;
1326 	if (dcol.yh >= viewheight - 1)
1327 		dcol.yh = viewheight - 2;
1328 
1329 	R_FillColumnGeneric<argb_t, DirectFuzzyFunc>(FB_COLDEST_D, dcol);
1330 
1331 	fuzzpos = (fuzzpos + 3) & (FUZZTABLE - 1);
1332 }
1333 
1334 //
1335 // R_DrawTranslucentColumnD
1336 //
1337 // Renders a translucent column to the 32bpp ARGB8888 screen buffer from the
1338 // source buffer dcol.source and scaled by dcol.iscale. The amount of
1339 // translucency is controlled by dcol.translevel. Shading is performed using
1340 // dcol.colormap.
1341 //
R_DrawTranslucentColumnD()1342 void R_DrawTranslucentColumnD()
1343 {
1344 	R_DrawColumnGeneric<argb_t, DirectTranslucentColormapFunc>(FB_COLDEST_D, dcol);
1345 }
1346 
1347 //
1348 // R_DrawTranslatedColumnD
1349 //
1350 // Renders a column to the 32bpp ARGB8888 screen buffer with color-remapping
1351 // from the source buffer dcol.source and scaled by dcol.iscale. The translation
1352 // table is supplied by dcol.translation. Shading is performed using dcol.colormap.
1353 //
R_DrawTranslatedColumnD()1354 void R_DrawTranslatedColumnD()
1355 {
1356 	R_DrawColumnGeneric<argb_t, DirectTranslatedColormapFunc>(FB_COLDEST_D, dcol);
1357 }
1358 
1359 //
1360 // R_DrawTlatedLucentColumnD
1361 //
1362 // Renders a translucent column to the 32bpp ARGB8888 screen buffer with
1363 // color-remapping from the source buffer dcol.source and scaled by dcol.iscale.
1364 // The translation table is supplied by dcol.translation and the amount of
1365 // translucency is controlled by dcol.translevel. Shading is performed using
1366 // dcol.colormap.
1367 //
R_DrawTlatedLucentColumnD()1368 void R_DrawTlatedLucentColumnD()
1369 {
1370 	R_DrawColumnGeneric<argb_t, DirectTranslatedTranslucentColormapFunc>(FB_COLDEST_D, dcol);
1371 }
1372 
1373 
1374 // ----------------------------------------------------------------------------
1375 //
1376 // 32bpp color span drawing wrappers
1377 //
1378 // ----------------------------------------------------------------------------
1379 
1380 #define FB_SPANDEST_D ((argb_t*)(ylookup[dspan.y] + columnofs[dspan.x1]))
1381 
1382 //
1383 // R_FillSpanD
1384 //
1385 // Fills a span in the 32bpp ARGB8888 screen buffer with a solid color,
1386 // determined by ds_color. Performs no shading.
1387 //
R_FillSpanD()1388 void R_FillSpanD()
1389 {
1390 	R_FillSpanGeneric<argb_t, DirectFunc>(FB_SPANDEST_D, dspan);
1391 }
1392 
1393 //
1394 // R_FillTranslucentSpanD
1395 //
1396 // Fills a span in the 32bpp ARGB8888 screen buffer with a solid color,
1397 // determined by ds_color using translucency. Shading is performed
1398 // using ds_colormap.
1399 //
R_FillTranslucentSpanD()1400 void R_FillTranslucentSpanD()
1401 {
1402 	R_FillSpanGeneric<argb_t, DirectTranslucentColormapFunc>(FB_SPANDEST_D, dspan);
1403 }
1404 
1405 //
1406 // R_DrawSpanD
1407 //
1408 // Renders a span for a level plane to the 32bpp ARGB8888 screen buffer from
1409 // the source buffer ds_source. Shading is performed using ds_colormap.
1410 //
R_DrawSpanD_c()1411 void R_DrawSpanD_c()
1412 {
1413 	R_DrawLevelSpanGeneric<argb_t, DirectColormapFunc>(FB_SPANDEST_D, dspan);
1414 }
1415 
1416 //
1417 // R_DrawSlopeSpanD
1418 //
1419 // Renders a span for a sloped plane to the 32bpp ARGB8888 screen buffer from
1420 // the source buffer ds_source. Shading is performed using ds_colormap.
1421 //
R_DrawSlopeSpanD_c()1422 void R_DrawSlopeSpanD_c()
1423 {
1424 	R_DrawSlopedSpanGeneric<argb_t, DirectSlopeColormapFunc>(FB_SPANDEST_D, dspan);
1425 }
1426 
1427 
1428 /****************************************************/
1429 
1430 //
1431 // R_InitBuffer
1432 // Creats lookup tables that avoid
1433 //  multiplies and other hazzles
1434 //  for getting the framebuffer address
1435 //  of a pixel to draw.
1436 //
R_InitBuffer(int width,int height)1437 void R_InitBuffer(int width, int height)
1438 {
1439 	int 		i;
1440 	byte		*buffer;
1441 	int			pitch;
1442 	int			xshift;
1443 
1444 	int windowwidth = width << detailxshift;
1445 	int windowheight = height << detailyshift;
1446 
1447 	// Handle resize,
1448 	//	e.g. smaller view windows
1449 	//	with border and/or status bar.
1450 	viewwindowx = (screen->width - windowwidth) >> 1;
1451 
1452 	// [RH] Adjust column offset according to bytes per pixel
1453 	//		and detail mode
1454 	xshift = detailxshift + (screen->is8bit() ? 0 : 2);
1455 
1456 	// Column offset. For windows
1457 	for (i = 0; i < width; i++)
1458 		columnofs[i] = (viewwindowx + i) << xshift;
1459 
1460 	// Same with base row offset.
1461 	if (windowwidth == screen->width)
1462 		viewwindowy = 0;
1463 	else
1464 		viewwindowy = (ST_Y - windowheight) >> 1;
1465 
1466 	screen->Lock();
1467 	buffer = screen->buffer;
1468 	pitch = screen->pitch;
1469 	screen->Unlock();
1470 
1471 	// Precalculate all row offsets.
1472 	for (i = 0; i < height; i++)
1473 		ylookup[i] = buffer + ((i << detailyshift) + viewwindowy) * pitch;
1474 }
1475 
1476 
R_DrawBorder(int x1,int y1,int x2,int y2)1477 void R_DrawBorder (int x1, int y1, int x2, int y2)
1478 {
1479 	int lump;
1480 
1481 	lump = W_CheckNumForName (gameinfo.borderFlat, ns_flats);
1482 	if (lump >= 0)
1483 	{
1484 		screen->FlatFill (x1 & ~63, y1, x2, y2,
1485 			(byte *)W_CacheLumpNum (lump, PU_CACHE));
1486 	}
1487 	else
1488 	{
1489 		screen->Clear (x1, y1, x2, y2, 0);
1490 	}
1491 }
1492 
1493 
1494 //
1495 // R_DrawViewBorder
1496 // Draws the border around the view
1497 //  for different size windows?
1498 //
1499 void V_MarkRect (int x, int y, int width, int height);
1500 
R_DrawViewBorder(void)1501 void R_DrawViewBorder (void)
1502 {
1503 	int x, y;
1504 	int offset, size;
1505 	gameborder_t *border;
1506 
1507 	if (realviewwidth == screen->width) {
1508 		return;
1509 	}
1510 
1511 	border = gameinfo.border;
1512 	offset = border->offset;
1513 	size = border->size;
1514 
1515 	R_DrawBorder (0, 0, screen->width, viewwindowy);
1516 	R_DrawBorder (0, viewwindowy, viewwindowx, realviewheight + viewwindowy);
1517 	R_DrawBorder (viewwindowx + realviewwidth, viewwindowy, screen->width, realviewheight + viewwindowy);
1518 	R_DrawBorder (0, viewwindowy + realviewheight, screen->width, ST_Y);
1519 
1520 	for (x = viewwindowx; x < viewwindowx + realviewwidth; x += size)
1521 	{
1522 		screen->DrawPatch (W_CachePatch (border->t),
1523 			x, viewwindowy - offset);
1524 		screen->DrawPatch (W_CachePatch (border->b),
1525 			x, viewwindowy + realviewheight);
1526 	}
1527 	for (y = viewwindowy; y < viewwindowy + realviewheight; y += size)
1528 	{
1529 		screen->DrawPatch (W_CachePatch (border->l),
1530 			viewwindowx - offset, y);
1531 		screen->DrawPatch (W_CachePatch (border->r),
1532 			viewwindowx + realviewwidth, y);
1533 	}
1534 	// Draw beveled edge.
1535 	screen->DrawPatch (W_CachePatch (border->tl),
1536 		viewwindowx-offset, viewwindowy-offset);
1537 
1538 	screen->DrawPatch (W_CachePatch (border->tr),
1539 		viewwindowx+realviewwidth, viewwindowy-offset);
1540 
1541 	screen->DrawPatch (W_CachePatch (border->bl),
1542 		viewwindowx-offset, viewwindowy+realviewheight);
1543 
1544 	screen->DrawPatch (W_CachePatch (border->br),
1545 		viewwindowx+realviewwidth, viewwindowy+realviewheight);
1546 
1547 	V_MarkRect (0, 0, screen->width, ST_Y);
1548 }
1549 
1550 // ============================================================================
1551 //
1552 // Horizontal and vertical pixel doubling functions
1553 //
1554 // ============================================================================
1555 
R_DoubleX8()1556 static void R_DoubleX8()
1557 {
1558 	int rowsize = realviewwidth >> 2;
1559 	int pitch = screen->pitch >> (2 - detailyshift);
1560 
1561 	unsigned int* line = (unsigned int*)(screen->buffer + viewwindowy * screen->pitch + viewwindowx);
1562 	for (int y = 0; y < viewheight; y++, line += pitch)
1563 	{
1564 		for (int x = 0; x < rowsize; x += 2)
1565 		{
1566 			unsigned int a = line[x + 0];
1567 			unsigned int b = line[x + 1];
1568 			a &= 0x00ff00ff;
1569 			b &= 0x00ff00ff;
1570 			line[x + 0] = a | (a << 8);
1571 			line[x + 1] = b | (b << 8);
1572 		}
1573 	}
1574 }
1575 
R_DoubleX32()1576 static void R_DoubleX32()
1577 {
1578 	int rowsize = realviewwidth;
1579 	int pitch = screen->pitch >> (2 - detailyshift);
1580 
1581 	argb_t* line = (argb_t*)(screen->buffer + viewwindowy * screen->pitch + viewwindowx);
1582 	for (int y = 0; y < viewheight; y++, line += pitch)
1583 	{
1584 		for (int x = 0; x < rowsize; x += 2)
1585 			line[x + 1] = line[x];
1586 	}
1587 }
1588 
R_DoubleY8()1589 static void R_DoubleY8()
1590 {
1591 	int rowsize = realviewwidth;
1592 	int pitch = screen->pitch;
1593 
1594 	byte* line = screen->buffer + viewwindowy * pitch + viewwindowx;
1595 
1596 	for (int y = 0; y < viewheight; y++, line += pitch << 1)
1597 		memcpy(line + pitch, line, rowsize);
1598 }
1599 
R_DoubleY32()1600 static void R_DoubleY32()
1601 {
1602 	int rowsize = realviewwidth << 2;
1603 	int pitch = screen->pitch;
1604 
1605 	byte* line = screen->buffer + viewwindowy * pitch + viewwindowx;
1606 
1607 	for (int y = 0; y < viewheight; y++, line += pitch << 1)
1608 		memcpy(line + pitch, line, rowsize);
1609 }
1610 
1611 
1612 // [RH] Double pixels in the view window horizontally
1613 //		and/or vertically (or not at all).
R_DetailDouble(void)1614 void R_DetailDouble (void)
1615 {
1616 	if (screen->is8bit())
1617 	{
1618 		if (detailxshift)
1619 			R_DoubleX8();
1620 		if (detailyshift)
1621 			R_DoubleY8();
1622 	}
1623 	else
1624 	{
1625 		if (detailxshift)
1626 			R_DoubleX32();
1627 		if (detailyshift)
1628 			R_DoubleY32();
1629 	}
1630 }
1631 
1632 
1633 enum r_optimize_kind {
1634 	OPTIMIZE_NONE,
1635 	OPTIMIZE_SSE2,
1636 	OPTIMIZE_MMX,
1637 	OPTIMIZE_ALTIVEC
1638 };
1639 
1640 static r_optimize_kind optimize_kind = OPTIMIZE_NONE;
1641 static std::vector<r_optimize_kind> optimizations_available;
1642 
get_optimization_name(r_optimize_kind kind)1643 static const char *get_optimization_name(r_optimize_kind kind)
1644 {
1645 	switch (kind)
1646 	{
1647 		case OPTIMIZE_SSE2:    return "sse2";
1648 		case OPTIMIZE_MMX:     return "mmx";
1649 		case OPTIMIZE_ALTIVEC: return "altivec";
1650 		case OPTIMIZE_NONE:
1651 		default:
1652 			return "none";
1653 	}
1654 }
1655 
get_optimization_name_list(const bool includeNone)1656 static std::string get_optimization_name_list(const bool includeNone)
1657 {
1658 	std::string str;
1659 	std::vector<r_optimize_kind>::const_iterator it = optimizations_available.begin();
1660 	if (!includeNone)
1661 		++it;
1662 
1663 	for (; it != optimizations_available.end(); ++it)
1664 	{
1665 		str.append(get_optimization_name(*it));
1666 		if (it+1 != optimizations_available.end())
1667 			str.append(", ");
1668 	}
1669 	return str;
1670 }
1671 
print_optimizations()1672 static void print_optimizations()
1673 {
1674 	Printf(PRINT_HIGH, "r_optimize detected \"%s\"\n", get_optimization_name_list(false).c_str());
1675 }
1676 
detect_optimizations()1677 static bool detect_optimizations()
1678 {
1679 	if (!optimizations_available.empty())
1680 		return false;
1681 
1682 	optimizations_available.clear();
1683 
1684 	// Start with default non-optimized:
1685 	optimizations_available.push_back(OPTIMIZE_NONE);
1686 
1687 	// Detect CPU features in ascending order of preference:
1688 	#ifdef __MMX__
1689 	#ifndef _XBOX // Until SDLx is updated
1690 	if (SDL_HasMMX())
1691 	#endif
1692 		optimizations_available.push_back(OPTIMIZE_MMX);
1693 	#endif
1694 	#ifdef __SSE2__
1695 	if (SDL_HasSSE2())
1696 		optimizations_available.push_back(OPTIMIZE_SSE2);
1697 	#endif
1698 	#ifdef __ALTIVEC__
1699 	if (SDL_HasAltiVec())
1700 		optimizations_available.push_back(OPTIMIZE_ALTIVEC);
1701 	#endif
1702 
1703 	return true;
1704 }
1705 
1706 //
1707 // R_IsOptimizationAvailable
1708 //
1709 // Returns true if Odamex was compiled with support for the optimization
1710 // and the current CPU also supports it.
1711 //
R_IsOptimizationAvailable(r_optimize_kind kind)1712 static bool R_IsOptimizationAvailable(r_optimize_kind kind)
1713 {
1714 	return std::find(optimizations_available.begin(), optimizations_available.end(), kind)
1715 			!= optimizations_available.end();
1716 }
1717 
1718 
CVAR_FUNC_IMPL(r_optimize)1719 CVAR_FUNC_IMPL(r_optimize)
1720 {
1721 	const char* val = var.cstring();
1722 
1723 	// Only print the detected list the first time:
1724 	if (detect_optimizations())
1725 		print_optimizations();
1726 
1727 	// Set the optimization based on availability:
1728 	if (stricmp(val, "none") == 0)
1729 		optimize_kind = OPTIMIZE_NONE;
1730 	else if (stricmp(val, "sse2") == 0 && R_IsOptimizationAvailable(OPTIMIZE_SSE2))
1731 		optimize_kind = OPTIMIZE_SSE2;
1732 	else if (stricmp(val, "mmx") == 0 && R_IsOptimizationAvailable(OPTIMIZE_MMX))
1733 		optimize_kind = OPTIMIZE_MMX;
1734 	else if (stricmp(val, "altivec") == 0 && R_IsOptimizationAvailable(OPTIMIZE_ALTIVEC))
1735 		optimize_kind = OPTIMIZE_ALTIVEC;
1736 	else if (stricmp(val, "detect") == 0)
1737 		// Default to the most preferred:
1738 		optimize_kind = optimizations_available.back();
1739 	else
1740 	{
1741 		Printf(PRINT_HIGH, "Invalid value for r_optimize. Availible options are \"%s, detect\"\n",
1742 				get_optimization_name_list(true).c_str());
1743 
1744 		// Restore the original setting:
1745 		var.Set(get_optimization_name(optimize_kind));
1746 		return;
1747 	}
1748 
1749 	const char* optimize_name = get_optimization_name(optimize_kind);
1750 	if (stricmp(val, optimize_name) != 0)
1751 	{
1752 		// update the cvar string
1753 		// this will trigger the callback to run a second time
1754 		Printf(PRINT_HIGH, "r_optimize set to \"%s\" based on availability\n", optimize_name);
1755 		var.Set(optimize_name);
1756 	}
1757 	else
1758 	{
1759 		// cvar string is current, now intialize the drawing function pointers
1760 		R_InitVectorizedDrawers();
1761 		R_InitColumnDrawers();
1762 	}
1763 }
1764 
1765 
1766 //
1767 // R_InitVectorizedDrawers
1768 //
1769 // Sets up the function pointers based on CPU optimization selected.
1770 //
R_InitVectorizedDrawers()1771 void R_InitVectorizedDrawers()
1772 {
1773 	if (optimize_kind == OPTIMIZE_NONE)
1774 	{
1775 		// [SL] set defaults to non-vectorized drawers
1776 		rtv_lucent4colsP        = rtv_lucent4cols_c;
1777 		rtv_lucent4colsD        = rtv_lucent4cols_c;
1778 		R_DrawSpanD				= R_DrawSpanD_c;
1779 		R_DrawSlopeSpanD		= R_DrawSlopeSpanD_c;
1780 		r_dimpatchD             = r_dimpatchD_c;
1781 	}
1782 	#ifdef __SSE2__
1783 	if (optimize_kind == OPTIMIZE_SSE2)
1784 	{
1785 		rtv_lucent4colsP        = rtv_lucent4cols_SSE2;
1786 		rtv_lucent4colsD        = rtv_lucent4cols_SSE2;
1787 		R_DrawSpanD				= R_DrawSpanD_SSE2;
1788 		R_DrawSlopeSpanD		= R_DrawSlopeSpanD_SSE2;
1789 		r_dimpatchD             = r_dimpatchD_SSE2;
1790 	}
1791 	#endif
1792 	#ifdef __MMX__
1793 	else if (optimize_kind == OPTIMIZE_MMX)
1794 	{
1795 		rtv_lucent4colsP        = rtv_lucent4cols_MMX;
1796 		rtv_lucent4colsD        = rtv_lucent4cols_MMX;
1797 		R_DrawSpanD				= R_DrawSpanD_c;		// TODO
1798 		R_DrawSlopeSpanD		= R_DrawSlopeSpanD_c;	// TODO
1799 		r_dimpatchD             = r_dimpatchD_MMX;
1800 	}
1801 	#endif
1802 	#ifdef __ALTIVEC__
1803 	else if (optimize_kind == OPTIMIZE_ALTIVEC)
1804 	{
1805 		rtv_lucent4colsP        = rtv_lucent4cols_c;    // TODO
1806 		rtv_lucent4colsD        = rtv_lucent4cols_c;    // TODO
1807 		R_DrawSpanD				= R_DrawSpanD_c;		// TODO
1808 		R_DrawSlopeSpanD		= R_DrawSlopeSpanD_c;	// TODO
1809 		r_dimpatchD             = r_dimpatchD_ALTIVEC;
1810 	}
1811 	#endif
1812 
1813 	// Check that all pointers are definitely assigned!
1814 	assert(rtv_lucent4colsP != NULL);
1815 	assert(rtv_lucent4colsD != NULL);
1816 	assert(R_DrawSpanD != NULL);
1817 	assert(R_DrawSlopeSpanD != NULL);
1818 	assert(r_dimpatchD != NULL);
1819 }
1820 
1821 // [RH] Initialize the column drawer pointers
R_InitColumnDrawers()1822 void R_InitColumnDrawers ()
1823 {
1824 	if (!screen)
1825 		return;
1826 
1827 	// NOTE(jsd): It's okay to use R_DrawColumnHorizP because it renders to a temp buffer first.
1828 	R_DrawColumnHoriz		= R_DrawColumnHorizP;
1829 
1830 	if (screen->is8bit())
1831 	{
1832 		R_DrawColumn			= R_DrawColumnP;
1833 		R_DrawFuzzColumn		= R_DrawFuzzColumnP;
1834 		R_DrawTranslucentColumn	= R_DrawTranslucentColumnP;
1835 		R_DrawTranslatedColumn	= R_DrawTranslatedColumnP;
1836 		R_DrawSlopeSpan			= R_DrawSlopeSpanP;
1837 		R_DrawSpan				= R_DrawSpanP;
1838 		R_FillColumn			= R_FillColumnP;
1839 		R_FillSpan				= R_FillSpanP;
1840 		R_FillTranslucentSpan	= R_FillTranslucentSpanP;
1841 
1842 		rt_copy1col				= rt_copy1colP;
1843 		rt_copy4cols			= rt_copy4colsP;
1844 		rt_map1col				= rt_map1colP;
1845 		rt_map4cols				= rt_map4colsP;
1846 		rt_lucent1col			= rt_lucent1colP;
1847 		rt_lucent4cols			= rt_lucent4colsP;
1848 		rt_tlate1col			= rt_tlate1colP;
1849 		rt_tlate4cols			= rt_tlate4colsP;
1850 		rt_tlatelucent1col		= rt_tlatelucent1colP;
1851 		rt_tlatelucent4cols		= rt_tlatelucent4colsP;
1852 	}
1853 	else
1854 	{
1855 		// 32bpp rendering functions:
1856 		R_DrawColumn			= R_DrawColumnD;
1857 		R_DrawFuzzColumn		= R_DrawFuzzColumnD;
1858 		R_DrawTranslucentColumn	= R_DrawTranslucentColumnD;
1859 		R_DrawTranslatedColumn	= R_DrawTranslatedColumnD;
1860 		R_DrawSlopeSpan			= R_DrawSlopeSpanD;
1861 		R_DrawSpan				= R_DrawSpanD;
1862 		R_FillColumn			= R_FillColumnD;
1863 		R_FillSpan				= R_FillSpanD;
1864 		R_FillTranslucentSpan	= R_FillTranslucentSpanD;
1865 
1866 		rt_copy1col				= rt_copy1colD;
1867 		rt_copy4cols			= rt_copy4colsD;
1868 		rt_map1col				= rt_map1colD;
1869 		rt_map4cols				= rt_map4colsD;
1870 		rt_lucent1col			= rt_lucent1colD;
1871 		rt_lucent4cols			= rt_lucent4colsD;
1872 		rt_tlate1col			= rt_tlate1colD;
1873 		rt_tlate4cols			= rt_tlate4colsD;
1874 		rt_tlatelucent1col		= rt_tlatelucent1colD;
1875 		rt_tlatelucent4cols		= rt_tlatelucent4colsD;
1876 	}
1877 }
1878 
1879 VERSION_CONTROL (r_draw_cpp, "$Id: r_draw.cpp 4715 2014-03-29 15:58:53Z dr_sean $")
1880 
1881