1 /*
2 ** v_draw.cpp
3 ** Draw patches and blocks to a canvas
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2008 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34 
35 // #define NO_SWRENDER 	// set this if you want to exclude the software renderer. Without software renderer the base implementations of DrawTextureV and FillSimplePoly need to be disabled because they depend on it.
36 
37 #include <stdio.h>
38 #include <stdarg.h>
39 
40 #include "doomtype.h"
41 #include "v_video.h"
42 #include "m_swap.h"
43 #include "r_defs.h"
44 #include "r_utility.h"
45 #ifndef NO_SWRENDER
46 #include "r_draw.h"
47 #include "r_main.h"
48 #include "r_things.h"
49 #endif
50 #include "r_data/r_translate.h"
51 #include "doomstat.h"
52 #include "v_palette.h"
53 #include "gi.h"
54 #include "g_level.h"
55 #include "st_stuff.h"
56 #include "sbar.h"
57 
58 #include "i_system.h"
59 #include "i_video.h"
60 #include "templates.h"
61 #include "d_net.h"
62 #include "colormatcher.h"
63 #include "r_data/colormaps.h"
64 
65 // [RH] Stretch values to make a 320x200 image best fit the screen
66 // without using fractional steppings
67 int CleanXfac, CleanYfac;
68 
69 // [RH] Effective screen sizes that the above scale values give you
70 int CleanWidth, CleanHeight;
71 
72 // Above minus 1 (or 1, if they are already 1)
73 int CleanXfac_1, CleanYfac_1, CleanWidth_1, CleanHeight_1;
74 
75 // FillSimplePoly uses this
76 extern "C" short spanend[MAXHEIGHT];
77 
78 CVAR (Bool, hud_scale, false, CVAR_ARCHIVE);
79 
80 // For routines that take RGB colors, cache the previous lookup in case there
81 // are several repetitions with the same color.
82 static int LastPal = -1;
83 static uint32 LastRGB;
84 
85 
PalFromRGB(uint32 rgb)86 static int PalFromRGB(uint32 rgb)
87 {
88 	if (LastPal >= 0 && LastRGB == rgb)
89 	{
90 		return LastPal;
91 	}
92 	// Quick check for black and white.
93 	if (rgb == MAKEARGB(255,0,0,0))
94 	{
95 		LastPal = GPalette.BlackIndex;
96 	}
97 	else if (rgb == MAKEARGB(255,255,255,255))
98 	{
99 		LastPal = GPalette.WhiteIndex;
100 	}
101 	else
102 	{
103 		LastPal = ColorMatcher.Pick(RPART(rgb), GPART(rgb), BPART(rgb));
104 	}
105 	LastRGB = rgb;
106 	return LastPal;
107 }
108 
DrawTexture(FTexture * img,double x,double y,int tags_first,...)109 void STACK_ARGS DCanvas::DrawTexture (FTexture *img, double x, double y, int tags_first, ...)
110 {
111 	va_list tags;
112 	va_start(tags, tags_first);
113 	DrawTextureV(img, x, y, tags_first, tags);
114 }
115 
DrawTextureV(FTexture * img,double x,double y,uint32 tag,va_list tags)116 void STACK_ARGS DCanvas::DrawTextureV(FTexture *img, double x, double y, uint32 tag, va_list tags)
117 {
118 #ifndef NO_SWRENDER
119 	FTexture::Span unmaskedSpan[2];
120 	const FTexture::Span **spanptr, *spans;
121 	static short bottomclipper[MAXWIDTH], topclipper[MAXWIDTH];
122 
123 	DrawParms parms;
124 
125 	if (!ParseDrawTextureTags(img, x, y, tag, tags, &parms, false))
126 	{
127 		return;
128 	}
129 
130 	if (parms.masked)
131 	{
132 		spanptr = &spans;
133 	}
134 	else
135 	{
136 		spanptr = NULL;
137 	}
138 
139 	if (APART(parms.colorOverlay) != 0)
140 	{
141 		// The software renderer cannot invert the source without inverting the overlay
142 		// too. That means if the source is inverted, we need to do the reverse of what
143 		// the invert overlay flag says to do.
144 		INTBOOL invertoverlay = (parms.style.Flags & STYLEF_InvertOverlay);
145 
146 		if (parms.style.Flags & STYLEF_InvertSource)
147 		{
148 			invertoverlay = !invertoverlay;
149 		}
150 		if (invertoverlay)
151 		{
152 			parms.colorOverlay = PalEntry(parms.colorOverlay).InverseColor();
153 		}
154 		// Note that this overrides DTA_Translation in software, but not in hardware.
155 		FDynamicColormap *colormap = GetSpecialLights(MAKERGB(255,255,255),
156 			parms.colorOverlay & MAKEARGB(0,255,255,255), 0);
157 		parms.translation = &colormap->Maps[(APART(parms.colorOverlay)*NUMCOLORMAPS/255)*256];
158 	}
159 
160 	if (parms.translation != NULL)
161 	{
162 		dc_colormap = (lighttable_t *)parms.translation;
163 	}
164 	else
165 	{
166 		dc_colormap = identitymap;
167 	}
168 
169 	fixedcolormap = dc_colormap;
170 	ESPSResult mode = R_SetPatchStyle (parms.style, parms.alpha, 0, parms.fillcolor);
171 
172 	BYTE *destorgsave = dc_destorg;
173 	dc_destorg = screen->GetBuffer();
174 
175 	double x0 = parms.x - parms.left * parms.destwidth / parms.texwidth;
176 	double y0 = parms.y - parms.top * parms.destheight / parms.texheight;
177 
178 	if (mode != DontDraw)
179 	{
180 		const BYTE *pixels;
181 		int stop4;
182 
183 		if (spanptr == NULL)
184 		{ // Create a single span for forced unmasked images
185 			spans = unmaskedSpan;
186 			unmaskedSpan[0].TopOffset = 0;
187 			unmaskedSpan[0].Length = img->GetHeight();
188 			unmaskedSpan[1].TopOffset = 0;
189 			unmaskedSpan[1].Length = 0;
190 		}
191 
192 		fixed_t centeryback = centeryfrac;
193 		centeryfrac = 0;
194 
195 		sprtopscreen = FLOAT2FIXED(y0);
196 		// There is not enough precision in the drawing routines to keep the full
197 		// precision for y0. :(
198 		sprtopscreen &= ~(FRACUNIT - 1);
199 
200 		double yscale = parms.destheight / img->GetHeight();
201 		double iyscale = 1 / yscale;
202 
203 		spryscale = FLOAT2FIXED(yscale);
204 		assert(spryscale > 2);
205 #if 0
206 		// Fix precision errors that are noticeable at some resolutions
207 		if ((y0 + parms.destheight) > (y0 + yscale * img->GetHeight()))
208 		{
209 			spryscale++;
210 		}
211 #endif
212 
213 		sprflipvert = false;
214 		//dc_iscale = FLOAT2FIXED(iyscale);
215 		//dc_texturemid = FLOAT2FIXED((-y0) * iyscale);
216 		//dc_iscale = 0xffffffffu / (unsigned)spryscale;
217 		dc_iscale = DivScale32(1, spryscale);
218 		dc_texturemid = FixedMul(-sprtopscreen, dc_iscale) + FixedMul(centeryfrac-FRACUNIT, dc_iscale);
219 		fixed_t frac = 0;
220 		double xiscale = img->GetWidth() / parms.destwidth;
221 		double x2 = x0 + parms.destwidth;
222 
223 		if (bottomclipper[0] != parms.dclip)
224 		{
225 			clearbufshort(bottomclipper, screen->GetWidth(), (short)parms.dclip);
226 		}
227 		if (parms.uclip != 0)
228 		{
229 			if (topclipper[0] != parms.uclip)
230 			{
231 				clearbufshort(topclipper, screen->GetWidth(), (short)parms.uclip);
232 			}
233 			mceilingclip = topclipper;
234 		}
235 		else
236 		{
237 			mceilingclip = zeroarray;
238 		}
239 		mfloorclip = bottomclipper;
240 
241 		if (parms.flipX)
242 		{
243 			frac = (img->GetWidth() << FRACBITS) - 1;
244 			xiscale = -xiscale;
245 		}
246 
247 		if (parms.windowleft > 0 || parms.windowright < parms.texwidth)
248 		{
249 			double xscale = parms.destwidth / parms.texwidth;
250 			x0 += parms.windowleft * xscale;
251 			frac += FLOAT2FIXED(parms.windowleft);
252 			x2 -= (parms.texwidth - parms.windowright) * xscale;
253 		}
254 		if (x0 < parms.lclip)
255 		{
256 			frac += FLOAT2FIXED((parms.lclip - x0) * xiscale);
257 			x0 = parms.lclip;
258 		}
259 		if (x2 > parms.rclip)
260 		{
261 			x2 = parms.rclip;
262 		}
263 
264 		// Drawing short output ought to fit in the data cache well enough
265 		// if we draw one column at a time, so do that, since it's simpler.
266 		if (parms.destheight < 32 || (parms.dclip - parms.uclip) < 32)
267 		{
268 			mode = DoDraw0;
269 		}
270 
271 		dc_x = int(x0);
272 		int x2_i = int(x2);
273 		fixed_t xiscale_i = FLOAT2FIXED(xiscale);
274 
275 		if (mode == DoDraw0)
276 		{
277 			// One column at a time
278 			stop4 = dc_x;
279 		}
280 		else	 // DoDraw1`
281 		{
282 			// Up to four columns at a time
283 			stop4 = x2_i & ~3;
284 		}
285 
286 		if (dc_x < x2_i)
287 		{
288 			while ((dc_x < stop4) && (dc_x & 3))
289 			{
290 				pixels = img->GetColumn(frac >> FRACBITS, spanptr);
291 				R_DrawMaskedColumn(pixels, spans);
292 				dc_x++;
293 				frac += xiscale_i;
294 			}
295 
296 			while (dc_x < stop4)
297 			{
298 				rt_initcols();
299 				for (int zz = 4; zz; --zz)
300 				{
301 					pixels = img->GetColumn(frac >> FRACBITS, spanptr);
302 					R_DrawMaskedColumnHoriz(pixels, spans);
303 					dc_x++;
304 					frac += xiscale_i;
305 				}
306 				rt_draw4cols(dc_x - 4);
307 			}
308 
309 			while (dc_x < x2_i)
310 			{
311 				pixels = img->GetColumn(frac >> FRACBITS, spanptr);
312 				R_DrawMaskedColumn(pixels, spans);
313 				dc_x++;
314 				frac += xiscale_i;
315 			}
316 		}
317 		centeryfrac = centeryback;
318 	}
319 	R_FinishSetPatchStyle ();
320 
321 	dc_destorg = destorgsave;
322 
323 	if (ticdup != 0 && menuactive == MENU_Off)
324 	{
325 		NetUpdate();
326 	}
327 #endif
328 }
329 
ParseDrawTextureTags(FTexture * img,double x,double y,DWORD tag,va_list tags,DrawParms * parms,bool hw) const330 bool DCanvas::ParseDrawTextureTags (FTexture *img, double x, double y, DWORD tag, va_list tags, DrawParms *parms, bool hw) const
331 {
332 	INTBOOL boolval;
333 	int intval;
334 	bool translationset = false;
335 	bool virtBottom;
336 	bool fillcolorset = false;
337 
338 	if (img == NULL || img->UseType == FTexture::TEX_Null)
339 	{
340 		va_end(tags);
341 		return false;
342 	}
343 
344 	// Do some sanity checks on the coordinates.
345 	if (x < -16383 || x > 16383 || y < -16383 || y > 16383)
346 	{
347 		va_end(tags);
348 		return false;
349 	}
350 
351 	virtBottom = false;
352 
353 	parms->texwidth = img->GetScaledWidthDouble();
354 	parms->texheight = img->GetScaledHeightDouble();
355 
356 	parms->windowleft = 0;
357 	parms->windowright = parms->texwidth;
358 	parms->dclip = this->GetHeight();
359 	parms->uclip = 0;
360 	parms->lclip = 0;
361 	parms->rclip = this->GetWidth();
362 	parms->destwidth = parms->windowright;
363 	parms->destheight = parms->texheight;
364 	parms->top = img->GetScaledTopOffset();
365 	parms->left = img->GetScaledLeftOffset();
366 	parms->alpha = FRACUNIT;
367 	parms->fillcolor = -1;
368 	parms->remap = NULL;
369 	parms->translation = NULL;
370 	parms->colorOverlay = 0;
371 	parms->alphaChannel = false;
372 	parms->flipX = false;
373 	parms->shadowAlpha = 0;
374 	parms->shadowColor = 0;
375 	parms->virtWidth = this->GetWidth();
376 	parms->virtHeight = this->GetHeight();
377 	parms->keepratio = false;
378 	parms->style.BlendOp = 255;		// Dummy "not set" value
379 	parms->masked = true;
380 	parms->bilinear = false;
381 	parms->specialcolormap = NULL;
382 	parms->colormapstyle = NULL;
383 
384 	parms->x = x;
385 	parms->y = y;
386 
387 	// Parse the tag list for attributes. (For floating point attributes,
388 	// consider that the C ABI dictates that all floats be promoted to
389 	// doubles when passed as function arguments.)
390 	while (tag != TAG_DONE)
391 	{
392 		va_list *more_p;
393 		DWORD data;
394 
395 		switch (tag)
396 		{
397 		case TAG_IGNORE:
398 		default:
399 			data = va_arg(tags, DWORD);
400 			break;
401 
402 		case TAG_MORE:
403 			more_p = va_arg(tags, va_list *);
404 			va_end (tags);
405 #ifndef NO_VA_COPY
406 			va_copy (tags, *more_p);
407 #else
408 			tags = *more_p;
409 #endif
410 			break;
411 
412 		case DTA_DestWidth:
413 			parms->destwidth = va_arg(tags, int);
414 			break;
415 
416 		case DTA_DestWidthF:
417 			parms->destwidth = va_arg(tags, double);
418 			break;
419 
420 		case DTA_DestHeight:
421 			parms->destheight = va_arg(tags, int);
422 			break;
423 
424 		case DTA_DestHeightF:
425 			parms->destheight = va_arg(tags, double);
426 			break;
427 
428 		case DTA_Clean:
429 			boolval = va_arg(tags, INTBOOL);
430 			if (boolval)
431 			{
432 				parms->x = (parms->x - 160.0) * CleanXfac + (Width * 0.5);
433 				parms->y = (parms->y - 100.0) * CleanYfac + (Height * 0.5);
434 				parms->destwidth = parms->texwidth * CleanXfac;
435 				parms->destheight = parms->texheight * CleanYfac;
436 			}
437 			break;
438 
439 		case DTA_CleanNoMove:
440 			boolval = va_arg(tags, INTBOOL);
441 			if (boolval)
442 			{
443 				parms->destwidth = parms->texwidth * CleanXfac;
444 				parms->destheight = parms->texheight * CleanYfac;
445 			}
446 			break;
447 
448 		case DTA_CleanNoMove_1:
449 			boolval = va_arg(tags, INTBOOL);
450 			if (boolval)
451 			{
452 				parms->destwidth = parms->texwidth * CleanXfac_1;
453 				parms->destheight = parms->texheight * CleanYfac_1;
454 			}
455 			break;
456 
457 		case DTA_320x200:
458 			boolval = va_arg(tags, INTBOOL);
459 			if (boolval)
460 			{
461 				parms->virtWidth = 320;
462 				parms->virtHeight = 200;
463 			}
464 			break;
465 
466 		case DTA_Bottom320x200:
467 			boolval = va_arg(tags, INTBOOL);
468 			if (boolval)
469 			{
470 				parms->virtWidth = 320;
471 				parms->virtHeight = 200;
472 			}
473 			virtBottom = true;
474 			break;
475 
476 		case DTA_HUDRules:
477 			{
478 				bool xright = parms->x < 0;
479 				bool ybot = parms->y < 0;
480 				intval = va_arg(tags, int);
481 
482 				if (hud_scale)
483 				{
484 					parms->x *= CleanXfac;
485 					if (intval == HUD_HorizCenter)
486 						parms->x += Width * 0.5;
487 					else if (xright)
488 						parms->x = Width + parms->x;
489 					parms->y *= CleanYfac;
490 					if (ybot)
491 						parms->y = Height + parms->y;
492 					parms->destwidth = parms->texwidth * CleanXfac;
493 					parms->destheight = parms->texheight * CleanYfac;
494 				}
495 				else
496 				{
497 					if (intval == HUD_HorizCenter)
498 						parms->x += Width * 0.5;
499 					else if (xright)
500 						parms->x = Width + parms->x;
501 					if (ybot)
502 						parms->y = Height + parms->y;
503 				}
504 			}
505 			break;
506 
507 		case DTA_VirtualWidth:
508 			parms->virtWidth = va_arg(tags, int);
509 			break;
510 
511 		case DTA_VirtualWidthF:
512 			parms->virtWidth = va_arg(tags, double);
513 			break;
514 
515 		case DTA_VirtualHeight:
516 			parms->virtHeight = va_arg(tags, int);
517 			break;
518 
519 		case DTA_VirtualHeightF:
520 			parms->virtHeight = va_arg(tags, double);
521 			break;
522 
523 		case DTA_Fullscreen:
524 			boolval = va_arg(tags, INTBOOL);
525 			if (boolval)
526 			{
527 				parms->x = parms->y = 0;
528 				parms->virtWidth = img->GetScaledWidthDouble();
529 				parms->virtHeight = img->GetScaledHeightDouble();
530 			}
531 			break;
532 
533 		case DTA_Alpha:
534 			parms->alpha = MIN<fixed_t>(FRACUNIT, va_arg (tags, fixed_t));
535 			break;
536 
537 		case DTA_AlphaChannel:
538 			parms->alphaChannel = va_arg(tags, INTBOOL);
539 			break;
540 
541 		case DTA_FillColor:
542 			parms->fillcolor = va_arg(tags, uint32);
543 			fillcolorset = true;
544 			break;
545 
546 		case DTA_Translation:
547 			parms->remap = va_arg(tags, FRemapTable *);
548 			if (parms->remap != NULL && parms->remap->Inactive)
549 			{ // If it's inactive, pretend we were passed NULL instead.
550 				parms->remap = NULL;
551 			}
552 			break;
553 
554 		case DTA_ColorOverlay:
555 			parms->colorOverlay = va_arg(tags, DWORD);
556 			break;
557 
558 		case DTA_FlipX:
559 			parms->flipX = va_arg(tags, INTBOOL);
560 			break;
561 
562 		case DTA_TopOffset:
563 			parms->top = va_arg(tags, int);
564 			break;
565 
566 		case DTA_TopOffsetF:
567 			parms->top = va_arg(tags, double);
568 			break;
569 
570 		case DTA_LeftOffset:
571 			parms->left = va_arg(tags, int);
572 			break;
573 
574 		case DTA_LeftOffsetF:
575 			parms->left = va_arg(tags, double);
576 			break;
577 
578 		case DTA_CenterOffset:
579 			if (va_arg(tags, int))
580 			{
581 				parms->left = parms->texwidth * 0.5;
582 				parms->top = parms->texheight * 0.5;
583 			}
584 			break;
585 
586 		case DTA_CenterBottomOffset:
587 			if (va_arg(tags, int))
588 			{
589 				parms->left = parms->texwidth * 0.5;
590 				parms->top = parms->texheight;
591 			}
592 			break;
593 
594 		case DTA_WindowLeft:
595 			parms->windowleft = va_arg(tags, int);
596 			break;
597 
598 		case DTA_WindowLeftF:
599 			parms->windowleft = va_arg(tags, double);
600 			break;
601 
602 		case DTA_WindowRight:
603 			parms->windowright = va_arg(tags, int);
604 			break;
605 
606 		case DTA_WindowRightF:
607 			parms->windowright = va_arg(tags, double);
608 			break;
609 
610 		case DTA_ClipTop:
611 			parms->uclip = va_arg(tags, int);
612 			if (parms->uclip < 0)
613 			{
614 				parms->uclip = 0;
615 			}
616 			break;
617 
618 		case DTA_ClipBottom:
619 			parms->dclip = va_arg(tags, int);
620 			if (parms->dclip > this->GetHeight())
621 			{
622 				parms->dclip = this->GetHeight();
623 			}
624 			break;
625 
626 		case DTA_ClipLeft:
627 			parms->lclip = va_arg(tags, int);
628 			if (parms->lclip < 0)
629 			{
630 				parms->lclip = 0;
631 			}
632 			break;
633 
634 		case DTA_ClipRight:
635 			parms->rclip = va_arg(tags, int);
636 			if (parms->rclip > this->GetWidth())
637 			{
638 				parms->rclip = this->GetWidth();
639 			}
640 			break;
641 
642 		case DTA_ShadowAlpha:
643 			parms->shadowAlpha = MIN<fixed_t>(FRACUNIT, va_arg (tags, fixed_t));
644 			break;
645 
646 		case DTA_ShadowColor:
647 			parms->shadowColor = va_arg(tags, int);
648 			break;
649 
650 		case DTA_Shadow:
651 			boolval = va_arg(tags, INTBOOL);
652 			if (boolval)
653 			{
654 				parms->shadowAlpha = FRACUNIT/2;
655 				parms->shadowColor = 0;
656 			}
657 			else
658 			{
659 				parms->shadowAlpha = 0;
660 			}
661 			break;
662 
663 		case DTA_Masked:
664 			parms->masked = va_arg(tags, INTBOOL);
665 			break;
666 
667 		case DTA_BilinearFilter:
668 			parms->bilinear = va_arg(tags, INTBOOL);
669 			break;
670 
671 		case DTA_KeepRatio:
672 			// I think this is a terribly misleading name, since it actually turns
673 			// *off* aspect ratio correction.
674 			parms->keepratio = va_arg(tags, INTBOOL);
675 			break;
676 
677 		case DTA_RenderStyle:
678 			parms->style.AsDWORD = va_arg(tags, DWORD);
679 			break;
680 
681 		case DTA_SpecialColormap:
682 			parms->specialcolormap = va_arg(tags, FSpecialColormap *);
683 			break;
684 
685 		case DTA_ColormapStyle:
686 			parms->colormapstyle = va_arg(tags, FColormapStyle *);
687 			break;
688 		}
689 		tag = va_arg(tags, DWORD);
690 	}
691 	va_end (tags);
692 
693 	if (parms->uclip >= parms->dclip || parms->lclip >= parms->rclip)
694 	{
695 		return false;
696 	}
697 
698 	if (parms->virtWidth != Width || parms->virtHeight != Height)
699 	{
700 		VirtualToRealCoords(parms->x, parms->y, parms->destwidth, parms->destheight,
701 			parms->virtWidth, parms->virtHeight, virtBottom, !parms->keepratio);
702 	}
703 
704 	if (parms->destwidth <= 0 || parms->destheight <= 0)
705 	{
706 		return false;
707 	}
708 
709 	if (parms->remap != NULL)
710 	{
711 		parms->translation = parms->remap->Remap;
712 	}
713 
714 	if (parms->style.BlendOp == 255)
715 	{
716 		if (fillcolorset)
717 		{
718 			if (parms->alphaChannel)
719 			{
720 				parms->style = STYLE_Shaded;
721 			}
722 			else if (parms->alpha < FRACUNIT)
723 			{
724 				parms->style = STYLE_TranslucentStencil;
725 			}
726 			else
727 			{
728 				parms->style = STYLE_Stencil;
729 			}
730 		}
731 		else if (parms->alpha < FRACUNIT)
732 		{
733 			parms->style = STYLE_Translucent;
734 		}
735 		else
736 		{
737 			parms->style = STYLE_Normal;
738 		}
739 	}
740 	return true;
741 }
742 
VirtualToRealCoords(double & x,double & y,double & w,double & h,double vwidth,double vheight,bool vbottom,bool handleaspect) const743 void DCanvas::VirtualToRealCoords(double &x, double &y, double &w, double &h,
744 	double vwidth, double vheight, bool vbottom, bool handleaspect) const
745 {
746 	int myratio = handleaspect ? CheckRatio (Width, Height) : 0;
747 	double right = x + w;
748 	double bottom = y + h;
749 
750 	if (myratio != 0 && myratio != 4)
751 	{ // The target surface is either 16:9 or 16:10, so expand the
752 	  // specified virtual size to avoid undesired stretching of the
753 	  // image. Does not handle non-4:3 virtual sizes. I'll worry about
754 	  // those if somebody expresses a desire to use them.
755 		x = (x - vwidth * 0.5) * Width * 960 / (vwidth * BaseRatioSizes[myratio][0]) + Width * 0.5;
756 		w = (right - vwidth * 0.5) * Width * 960 / (vwidth * BaseRatioSizes[myratio][0]) + Width * 0.5 - x;
757 	}
758 	else
759 	{
760 		x = x * Width / vwidth;
761 		w = right * Width / vwidth - x;
762 	}
763 	if (myratio == 4)
764 	{ // The target surface is 5:4
765 		y = (y - vheight * 0.5) * Height * 600 / (vheight * BaseRatioSizes[myratio][1]) + Height * 0.5;
766 		h = (bottom - vheight * 0.5) * Height * 600 / (vheight * BaseRatioSizes[myratio][1]) + Height * 0.5 - y;
767 		if (vbottom)
768 		{
769 			y += (Height - Height * BaseRatioSizes[myratio][3] / 48.0) * 0.5;
770 		}
771 	}
772 	else
773 	{
774 		y = y * Height / vheight;
775 		h = bottom * Height / vheight - y;
776 	}
777 }
778 
VirtualToRealCoordsFixed(fixed_t & x,fixed_t & y,fixed_t & w,fixed_t & h,int vwidth,int vheight,bool vbottom,bool handleaspect) const779 void DCanvas::VirtualToRealCoordsFixed(fixed_t &x, fixed_t &y, fixed_t &w, fixed_t &h,
780 	int vwidth, int vheight, bool vbottom, bool handleaspect) const
781 {
782 	double dx, dy, dw, dh;
783 
784 	dx = FIXED2FLOAT(x);
785 	dy = FIXED2FLOAT(y);
786 	dw = FIXED2FLOAT(w);
787 	dh = FIXED2FLOAT(h);
788 	VirtualToRealCoords(dx, dy, dw, dh, vwidth, vheight, vbottom, handleaspect);
789 	x = FLOAT2FIXED(dx);
790 	y = FLOAT2FIXED(dy);
791 	w = FLOAT2FIXED(dw);
792 	h = FLOAT2FIXED(dh);
793 }
794 
VirtualToRealCoordsInt(int & x,int & y,int & w,int & h,int vwidth,int vheight,bool vbottom,bool handleaspect) const795 void DCanvas::VirtualToRealCoordsInt(int &x, int &y, int &w, int &h,
796 	int vwidth, int vheight, bool vbottom, bool handleaspect) const
797 {
798 	double dx, dy, dw, dh;
799 
800 	dx = x;
801 	dy = y;
802 	dw = w;
803 	dh = h;
804 	VirtualToRealCoords(dx, dy, dw, dh, vwidth, vheight, vbottom, handleaspect);
805 	x = int(dx + 0.5);
806 	y = int(dy + 0.5);
807 	w = int(dx + dw + 0.5) - x;
808 	h = int(dy + dh + 0.5) - y;
809 }
810 
FillBorder(FTexture * img)811 void DCanvas::FillBorder (FTexture *img)
812 {
813 	int myratio = CheckRatio (Width, Height);
814 	if (myratio == 0)
815 	{ // This is a 4:3 display, so no border to show
816 		return;
817 	}
818 	int bordtop, bordbottom, bordleft, bordright, bord;
819 	if (myratio & 4)
820 	{ // Screen is taller than it is wide
821 		bordleft = bordright = 0;
822 		bord = Height - Height * BaseRatioSizes[myratio][3] / 48;
823 		bordtop = bord / 2;
824 		bordbottom = bord - bordtop;
825 	}
826 	else
827 	{ // Screen is wider than it is tall
828 		bordtop = bordbottom = 0;
829 		bord = Width - Width * BaseRatioSizes[myratio][3] / 48;
830 		bordleft = bord / 2;
831 		bordright = bord - bordleft;
832 	}
833 
834 	if (img != NULL)
835 	{
836 		FlatFill (0, 0, Width, bordtop, img);									// Top
837 		FlatFill (0, bordtop, bordleft, Height - bordbottom, img);				// Left
838 		FlatFill (Width - bordright, bordtop, Width, Height - bordbottom, img);	// Right
839 		FlatFill (0, Height - bordbottom, Width, Height, img);					// Bottom
840 	}
841 	else
842 	{
843 		Clear (0, 0, Width, bordtop, GPalette.BlackIndex, 0);									// Top
844 		Clear (0, bordtop, bordleft, Height - bordbottom, GPalette.BlackIndex, 0);				// Left
845 		Clear (Width - bordright, bordtop, Width, Height - bordbottom, GPalette.BlackIndex, 0);	// Right
846 		Clear (0, Height - bordbottom, Width, Height, GPalette.BlackIndex, 0);					// Bottom
847 	}
848 }
849 
PUTTRANSDOT(int xx,int yy,int basecolor,int level)850 void DCanvas::PUTTRANSDOT (int xx, int yy, int basecolor, int level)
851 {
852 	static int oldyy;
853 	static int oldyyshifted;
854 
855 #if 0
856 	if(xx < 32)
857 		cc += 7-(xx>>2);
858 	else if(xx > (finit_width - 32))
859 		cc += 7-((finit_width-xx) >> 2);
860 //	if(cc==oldcc) //make sure that we don't double fade the corners.
861 //	{
862 		if(yy < 32)
863 			cc += 7-(yy>>2);
864 		else if(yy > (finit_height - 32))
865 			cc += 7-((finit_height-yy) >> 2);
866 //	}
867 	if(cc > cm && cm != NULL)
868 	{
869 		cc = cm;
870 	}
871 	else if(cc > oldcc+6) // don't let the color escape from the fade table...
872 	{
873 		cc=oldcc+6;
874 	}
875 #endif
876 	if (yy == oldyy+1)
877 	{
878 		oldyy++;
879 		oldyyshifted += GetPitch();
880 	}
881 	else if (yy == oldyy-1)
882 	{
883 		oldyy--;
884 		oldyyshifted -= GetPitch();
885 	}
886 	else if (yy != oldyy)
887 	{
888 		oldyy = yy;
889 		oldyyshifted = yy * GetPitch();
890 	}
891 
892 	BYTE *spot = GetBuffer() + oldyyshifted + xx;
893 	DWORD *bg2rgb = Col2RGB8[1+level];
894 	DWORD *fg2rgb = Col2RGB8[63-level];
895 	DWORD fg = fg2rgb[basecolor];
896 	DWORD bg = bg2rgb[*spot];
897 	bg = (fg+bg) | 0x1f07c1f;
898 	*spot = RGB32k.All[bg&(bg>>15)];
899 }
900 
DrawLine(int x0,int y0,int x1,int y1,int palColor,uint32 realcolor)901 void DCanvas::DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32 realcolor)
902 //void DrawTransWuLine (int x0, int y0, int x1, int y1, BYTE palColor)
903 {
904 	const int WeightingScale = 0;
905 	const int WEIGHTBITS = 6;
906 	const int WEIGHTSHIFT = 16-WEIGHTBITS;
907 	const int NUMWEIGHTS = (1<<WEIGHTBITS);
908 	const int WEIGHTMASK = (NUMWEIGHTS-1);
909 
910 	if (palColor < 0)
911 	{
912 		palColor = PalFromRGB(realcolor);
913 	}
914 
915 	Lock();
916 	int deltaX, deltaY, xDir;
917 
918 	if (y0 > y1)
919 	{
920 		int temp = y0; y0 = y1; y1 = temp;
921 		temp = x0; x0 = x1; x1 = temp;
922 	}
923 
924 	PUTTRANSDOT (x0, y0, palColor, 0);
925 
926 	if ((deltaX = x1 - x0) >= 0)
927 	{
928 		xDir = 1;
929 	}
930 	else
931 	{
932 		xDir = -1;
933 		deltaX = -deltaX;
934 	}
935 
936 	if ((deltaY = y1 - y0) == 0)
937 	{ // horizontal line
938 		if (x0 > x1)
939 		{
940 			swapvalues (x0, x1);
941 		}
942 		memset (GetBuffer() + y0*GetPitch() + x0, palColor, deltaX+1);
943 	}
944 	else if (deltaX == 0)
945 	{ // vertical line
946 		BYTE *spot = GetBuffer() + y0*GetPitch() + x0;
947 		int pitch = GetPitch ();
948 		do
949 		{
950 			*spot = palColor;
951 			spot += pitch;
952 		} while (--deltaY != 0);
953 	}
954 	else if (deltaX == deltaY)
955 	{ // diagonal line.
956 		BYTE *spot = GetBuffer() + y0*GetPitch() + x0;
957 		int advance = GetPitch() + xDir;
958 		do
959 		{
960 			*spot = palColor;
961 			spot += advance;
962 		} while (--deltaY != 0);
963 	}
964 	else
965 	{
966 		// line is not horizontal, diagonal, or vertical
967 		fixed_t errorAcc = 0;
968 
969 		if (deltaY > deltaX)
970 		{ // y-major line
971 			fixed_t errorAdj = (((unsigned)deltaX << 16) / (unsigned)deltaY) & 0xffff;
972 			if (xDir < 0)
973 			{
974 				if (WeightingScale == 0)
975 				{
976 					while (--deltaY)
977 					{
978 						errorAcc += errorAdj;
979 						y0++;
980 						int weighting = (errorAcc >> WEIGHTSHIFT) & WEIGHTMASK;
981 						PUTTRANSDOT (x0 - (errorAcc >> 16), y0, palColor, weighting);
982 						PUTTRANSDOT (x0 - (errorAcc >> 16) - 1, y0,
983 								palColor, WEIGHTMASK - weighting);
984 					}
985 				}
986 				else
987 				{
988 					while (--deltaY)
989 					{
990 						errorAcc += errorAdj;
991 						y0++;
992 						int weighting = ((errorAcc * WeightingScale) >> (WEIGHTSHIFT+8)) & WEIGHTMASK;
993 						PUTTRANSDOT (x0 - (errorAcc >> 16), y0, palColor, weighting);
994 						PUTTRANSDOT (x0 - (errorAcc >> 16) - 1, y0,
995 								palColor, WEIGHTMASK - weighting);
996 					}
997 				}
998 			}
999 			else
1000 			{
1001 				if (WeightingScale == 0)
1002 				{
1003 					while (--deltaY)
1004 					{
1005 						errorAcc += errorAdj;
1006 						y0++;
1007 						int weighting = (errorAcc >> WEIGHTSHIFT) & WEIGHTMASK;
1008 						PUTTRANSDOT (x0 + (errorAcc >> 16), y0, palColor, weighting);
1009 						PUTTRANSDOT (x0 + (errorAcc >> 16) + xDir, y0,
1010 								palColor, WEIGHTMASK - weighting);
1011 					}
1012 				}
1013 				else
1014 				{
1015 					while (--deltaY)
1016 					{
1017 						errorAcc += errorAdj;
1018 						y0++;
1019 						int weighting = ((errorAcc * WeightingScale) >> (WEIGHTSHIFT+8)) & WEIGHTMASK;
1020 						PUTTRANSDOT (x0 + (errorAcc >> 16), y0, palColor, weighting);
1021 						PUTTRANSDOT (x0 + (errorAcc >> 16) + xDir, y0,
1022 								palColor, WEIGHTMASK - weighting);
1023 					}
1024 				}
1025 			}
1026 		}
1027 		else
1028 		{ // x-major line
1029 			fixed_t errorAdj = (((DWORD) deltaY << 16) / (DWORD) deltaX) & 0xffff;
1030 
1031 			if (WeightingScale == 0)
1032 			{
1033 				while (--deltaX)
1034 				{
1035 					errorAcc += errorAdj;
1036 					x0 += xDir;
1037 					int weighting = (errorAcc >> WEIGHTSHIFT) & WEIGHTMASK;
1038 					PUTTRANSDOT (x0, y0 + (errorAcc >> 16), palColor, weighting);
1039 					PUTTRANSDOT (x0, y0 + (errorAcc >> 16) + 1,
1040 							palColor, WEIGHTMASK - weighting);
1041 				}
1042 			}
1043 			else
1044 			{
1045 				while (--deltaX)
1046 				{
1047 					errorAcc += errorAdj;
1048 					x0 += xDir;
1049 					int weighting = ((errorAcc * WeightingScale) >> (WEIGHTSHIFT+8)) & WEIGHTMASK;
1050 					PUTTRANSDOT (x0, y0 + (errorAcc >> 16), palColor, weighting);
1051 					PUTTRANSDOT (x0, y0 + (errorAcc >> 16) + 1,
1052 							palColor, WEIGHTMASK - weighting);
1053 				}
1054 			}
1055 		}
1056 		PUTTRANSDOT (x1, y1, palColor, 0);
1057 	}
1058 	Unlock();
1059 }
1060 
DrawPixel(int x,int y,int palColor,uint32 realcolor)1061 void DCanvas::DrawPixel(int x, int y, int palColor, uint32 realcolor)
1062 {
1063 	if (palColor < 0)
1064 	{
1065 		palColor = PalFromRGB(realcolor);
1066 	}
1067 
1068 	Buffer[Pitch * y + x] = (BYTE)palColor;
1069 }
1070 
1071 //==========================================================================
1072 //
1073 // DCanvas :: Clear
1074 //
1075 // Set an area to a specified color.
1076 //
1077 //==========================================================================
1078 
Clear(int left,int top,int right,int bottom,int palcolor,uint32 color)1079 void DCanvas::Clear (int left, int top, int right, int bottom, int palcolor, uint32 color)
1080 {
1081 	int x, y;
1082 	BYTE *dest;
1083 
1084 	if (left == right || top == bottom)
1085 	{
1086 		return;
1087 	}
1088 
1089 	assert(left < right);
1090 	assert(top < bottom);
1091 
1092 	if (left >= Width || right <= 0 || top >= Height || bottom <= 0)
1093 	{
1094 		return;
1095 	}
1096 	left = MAX(0,left);
1097 	right = MIN(Width,right);
1098 	top = MAX(0,top);
1099 	bottom = MIN(Height,bottom);
1100 
1101 	if (palcolor < 0)
1102 	{
1103 		if (APART(color) != 255)
1104 		{
1105 			Dim(color, APART(color)/255.f, left, top, right - left, bottom - top);
1106 			return;
1107 		}
1108 
1109 		palcolor = PalFromRGB(color);
1110 	}
1111 
1112 	dest = Buffer + top * Pitch + left;
1113 	x = right - left;
1114 	for (y = top; y < bottom; y++)
1115 	{
1116 		memset(dest, palcolor, x);
1117 		dest += Pitch;
1118 	}
1119 }
1120 
1121 //==========================================================================
1122 //
1123 // DCanvas :: FillSimplePoly
1124 //
1125 // Fills a simple polygon with a texture. Here, "simple" means that a
1126 // horizontal scanline at any vertical position within the polygon will
1127 // not cross it more than twice.
1128 //
1129 // The originx, originy, scale, and rotation parameters specify
1130 // transformation of the filling texture, not of the points.
1131 //
1132 // The points must be specified in clockwise order.
1133 //
1134 //==========================================================================
1135 
FillSimplePoly(FTexture * tex,FVector2 * points,int npoints,double originx,double originy,double scalex,double scaley,angle_t rotation,FDynamicColormap * colormap,int lightlevel)1136 void DCanvas::FillSimplePoly(FTexture *tex, FVector2 *points, int npoints,
1137 	double originx, double originy, double scalex, double scaley, angle_t rotation,
1138 	FDynamicColormap *colormap, int lightlevel)
1139 {
1140 #ifndef NO_SWRENDER
1141 	// Use an equation similar to player sprites to determine shade
1142 	fixed_t shade = LIGHT2SHADE(lightlevel) - 12*FRACUNIT;
1143 	float topy, boty, leftx, rightx;
1144 	int toppt, botpt, pt1, pt2;
1145 	int i;
1146 	int y1, y2, y;
1147 	fixed_t x;
1148 	double rot = rotation * M_PI / double(1u << 31);
1149 	bool dorotate = rot != 0;
1150 	double cosrot, sinrot;
1151 
1152 	if (--npoints < 2 || Buffer == NULL)
1153 	{ // not a polygon or we're not locked
1154 		return;
1155 	}
1156 
1157 	// Find the extents of the polygon, in particular the highest and lowest points.
1158 	for (botpt = toppt = 0, boty = topy = points[0].Y, leftx = rightx = points[0].X, i = 1; i <= npoints; ++i)
1159 	{
1160 		if (points[i].Y < topy)
1161 		{
1162 			topy = points[i].Y;
1163 			toppt = i;
1164 		}
1165 		if (points[i].Y > boty)
1166 		{
1167 			boty = points[i].Y;
1168 			botpt = i;
1169 		}
1170 		if (points[i].X < leftx)
1171 		{
1172 			leftx = points[i].X;
1173 		}
1174 		if (points[i].X > rightx)
1175 		{
1176 			rightx = points[i].X;
1177 		}
1178 	}
1179 	if (topy >= Height ||		// off the bottom of the screen
1180 		boty <= 0 ||			// off the top of the screen
1181 		leftx >= Width ||		// off the right of the screen
1182 		rightx <= 0)			// off the left of the screen
1183 	{
1184 		return;
1185 	}
1186 
1187 	scalex /= FIXED2FLOAT(tex->xScale);
1188 	scaley /= FIXED2FLOAT(tex->yScale);
1189 
1190 	cosrot = cos(rot);
1191 	sinrot = sin(rot);
1192 
1193 	// Setup constant texture mapping parameters.
1194 	R_SetupSpanBits(tex);
1195 	R_SetSpanColormap(colormap != NULL ? &colormap->Maps[clamp(shade >> FRACBITS, 0, NUMCOLORMAPS-1) * 256] : identitymap);
1196 	R_SetSpanSource(tex->GetPixels());
1197 	scalex = double(1u << (32 - ds_xbits)) / scalex;
1198 	scaley = double(1u << (32 - ds_ybits)) / scaley;
1199 	ds_xstep = xs_RoundToInt(cosrot * scalex);
1200 	ds_ystep = xs_RoundToInt(sinrot * scaley);
1201 
1202 	// Travel down the right edge and create an outline of that edge.
1203 	pt1 = toppt;
1204 	pt2 = toppt + 1;	if (pt2 > npoints) pt2 = 0;
1205 	y1 = xs_RoundToInt(points[pt1].Y + 0.5f);
1206 	do
1207 	{
1208 		x = FLOAT2FIXED(points[pt1].X + 0.5f);
1209 		y2 = xs_RoundToInt(points[pt2].Y + 0.5f);
1210 		if (y1 >= y2 || (y1 < 0 && y2 < 0) || (y1 >= Height && y2 >= Height))
1211 		{
1212 		}
1213 		else
1214 		{
1215 			fixed_t xinc = FLOAT2FIXED((points[pt2].X - points[pt1].X) / (points[pt2].Y - points[pt1].Y));
1216 			int y3 = MIN(y2, Height);
1217 			if (y1 < 0)
1218 			{
1219 				x += xinc * -y1;
1220 				y1 = 0;
1221 			}
1222 			for (y = y1; y < y3; ++y)
1223 			{
1224 				spanend[y] = clamp<short>(x >> FRACBITS, -1, Width);
1225 				x += xinc;
1226 			}
1227 		}
1228 		y1 = y2;
1229 		pt1 = pt2;
1230 		pt2++;			if (pt2 > npoints) pt2 = 0;
1231 	} while (pt1 != botpt);
1232 
1233 	// Travel down the left edge and fill it in.
1234 	pt1 = toppt;
1235 	pt2 = toppt - 1;	if (pt2 < 0) pt2 = npoints;
1236 	y1 = xs_RoundToInt(points[pt1].Y + 0.5f);
1237 	do
1238 	{
1239 		x = FLOAT2FIXED(points[pt1].X + 0.5f);
1240 		y2 = xs_RoundToInt(points[pt2].Y + 0.5f);
1241 		if (y1 >= y2 || (y1 < 0 && y2 < 0) || (y1 >= Height && y2 >= Height))
1242 		{
1243 		}
1244 		else
1245 		{
1246 			fixed_t xinc = FLOAT2FIXED((points[pt2].X - points[pt1].X) / (points[pt2].Y - points[pt1].Y));
1247 			int y3 = MIN(y2, Height);
1248 			if (y1 < 0)
1249 			{
1250 				x += xinc * -y1;
1251 				y1 = 0;
1252 			}
1253 			for (y = y1; y < y3; ++y)
1254 			{
1255 				int x1 = x >> FRACBITS;
1256 				int x2 = spanend[y];
1257 				if (x2 > x1 && x2 > 0 && x1 < Width)
1258 				{
1259 					x1 = MAX(x1, 0);
1260 					x2 = MIN(x2, Width);
1261 #if 0
1262 					memset(this->Buffer + y * this->Pitch + x1, (int)tex, x2 - x1);
1263 #else
1264 					ds_y = y;
1265 					ds_x1 = x1;
1266 					ds_x2 = x2 - 1;
1267 
1268 					TVector2<double> tex(x1 - originx, y - originy);
1269 					if (dorotate)
1270 					{
1271 						double t = tex.X;
1272 						tex.X = t * cosrot - tex.Y * sinrot;
1273 						tex.Y = tex.Y * cosrot + t * sinrot;
1274 					}
1275 					ds_xfrac = xs_RoundToInt(tex.X * scalex);
1276 					ds_yfrac = xs_RoundToInt(tex.Y * scaley);
1277 
1278 					R_DrawSpan();
1279 #endif
1280 				}
1281 				x += xinc;
1282 			}
1283 		}
1284 		y1 = y2;
1285 		pt1 = pt2;
1286 		pt2--;			if (pt2 < 0) pt2 = npoints;
1287 	} while (pt1 != botpt);
1288 #endif
1289 }
1290 
1291 
1292 /********************************/
1293 /*								*/
1294 /* Other miscellaneous routines */
1295 /*								*/
1296 /********************************/
1297 
1298 
1299 //
1300 // V_DrawBlock
1301 // Draw a linear block of pixels into the view buffer.
1302 //
DrawBlock(int x,int y,int _width,int _height,const BYTE * src) const1303 void DCanvas::DrawBlock (int x, int y, int _width, int _height, const BYTE *src) const
1304 {
1305 	int srcpitch = _width;
1306 	int destpitch;
1307 	BYTE *dest;
1308 
1309 	if (ClipBox (x, y, _width, _height, src, srcpitch))
1310 	{
1311 		return;		// Nothing to draw
1312 	}
1313 
1314 	destpitch = Pitch;
1315 	dest = Buffer + y*Pitch + x;
1316 
1317 	do
1318 	{
1319 		memcpy (dest, src, _width);
1320 		src += srcpitch;
1321 		dest += destpitch;
1322 	} while (--_height);
1323 }
1324 
1325 //
1326 // V_GetBlock
1327 // Gets a linear block of pixels from the view buffer.
1328 //
GetBlock(int x,int y,int _width,int _height,BYTE * dest) const1329 void DCanvas::GetBlock (int x, int y, int _width, int _height, BYTE *dest) const
1330 {
1331 	const BYTE *src;
1332 
1333 #ifdef RANGECHECK
1334 	if (x<0
1335 		||x+_width > Width
1336 		|| y<0
1337 		|| y+_height>Height)
1338 	{
1339 		I_Error ("Bad V_GetBlock");
1340 	}
1341 #endif
1342 
1343 	src = Buffer + y*Pitch + x;
1344 
1345 	while (_height--)
1346 	{
1347 		memcpy (dest, src, _width);
1348 		src += Pitch;
1349 		dest += _width;
1350 	}
1351 }
1352 
1353 // Returns true if the box was completely clipped. False otherwise.
ClipBox(int & x,int & y,int & w,int & h,const BYTE * & src,const int srcpitch) const1354 bool DCanvas::ClipBox (int &x, int &y, int &w, int &h, const BYTE *&src, const int srcpitch) const
1355 {
1356 	if (x >= Width || y >= Height || x+w <= 0 || y+h <= 0)
1357 	{ // Completely clipped off screen
1358 		return true;
1359 	}
1360 	if (x < 0)				// clip left edge
1361 	{
1362 		src -= x;
1363 		w += x;
1364 		x = 0;
1365 	}
1366 	if (x+w > Width)		// clip right edge
1367 	{
1368 		w = Width - x;
1369 	}
1370 	if (y < 0)				// clip top edge
1371 	{
1372 		src -= y*srcpitch;
1373 		h += y;
1374 		y = 0;
1375 	}
1376 	if (y+h > Height)		// clip bottom edge
1377 	{
1378 		h = Height - y;
1379 	}
1380 	return false;
1381 }
1382 
1383 //==========================================================================
1384 //
1385 // V_SetBorderNeedRefresh
1386 //
1387 // Flag the border as in need of updating. (Probably because something that
1388 // was on top of it has changed.
1389 //
1390 //==========================================================================
1391 
V_SetBorderNeedRefresh()1392 void V_SetBorderNeedRefresh()
1393 {
1394 	if (screen != NULL)
1395 	{
1396 		BorderNeedRefresh = screen->GetPageCount();
1397 	}
1398 }
1399 
1400 //==========================================================================
1401 //
1402 // V_DrawFrame
1403 //
1404 // Draw a frame around the specified area using the view border
1405 // frame graphics. The border is drawn outside the area, not in it.
1406 //
1407 //==========================================================================
1408 
V_DrawFrame(int left,int top,int width,int height)1409 void V_DrawFrame (int left, int top, int width, int height)
1410 {
1411 	FTexture *p;
1412 	const gameborder_t *border = &gameinfo.Border;
1413 	// Sanity check for incomplete gameinfo
1414 	if (border == NULL)
1415 		return;
1416 	int offset = border->offset;
1417 	int right = left + width;
1418 	int bottom = top + height;
1419 
1420 	// Draw top and bottom sides.
1421 	p = TexMan[border->t];
1422 	screen->FlatFill(left, top - p->GetHeight(), right, top, p, true);
1423 	p = TexMan[border->b];
1424 	screen->FlatFill(left, bottom, right, bottom + p->GetHeight(), p, true);
1425 
1426 	// Draw left and right sides.
1427 	p = TexMan[border->l];
1428 	screen->FlatFill(left - p->GetWidth(), top, left, bottom, p, true);
1429 	p = TexMan[border->r];
1430 	screen->FlatFill(right, top, right + p->GetWidth(), bottom, p, true);
1431 
1432 	// Draw beveled corners.
1433 	screen->DrawTexture (TexMan[border->tl], left-offset, top-offset, TAG_DONE);
1434 	screen->DrawTexture (TexMan[border->tr], left+width, top-offset, TAG_DONE);
1435 	screen->DrawTexture (TexMan[border->bl], left-offset, top+height, TAG_DONE);
1436 	screen->DrawTexture (TexMan[border->br], left+width, top+height, TAG_DONE);
1437 }
1438 
1439 //==========================================================================
1440 //
1441 // V_DrawBorder
1442 //
1443 //==========================================================================
1444 
V_DrawBorder(int x1,int y1,int x2,int y2)1445 void V_DrawBorder (int x1, int y1, int x2, int y2)
1446 {
1447 	FTextureID picnum;
1448 
1449 	if (level.info != NULL && level.info->BorderTexture.Len() != 0)
1450 	{
1451 		picnum = TexMan.CheckForTexture (level.info->BorderTexture, FTexture::TEX_Flat);
1452 	}
1453 	else
1454 	{
1455 		picnum = TexMan.CheckForTexture (gameinfo.BorderFlat, FTexture::TEX_Flat);
1456 	}
1457 
1458 	if (picnum.isValid())
1459 	{
1460 		screen->FlatFill (x1, y1, x2, y2, TexMan(picnum));
1461 	}
1462 	else
1463 	{
1464 		screen->Clear (x1, y1, x2, y2, 0, 0);
1465 	}
1466 }
1467 
1468 //==========================================================================
1469 //
1470 // R_DrawViewBorder
1471 //
1472 // Draws the border around the view for different size windows
1473 //
1474 //==========================================================================
1475 
1476 int BorderNeedRefresh;
1477 
1478 
V_DrawViewBorder(void)1479 static void V_DrawViewBorder (void)
1480 {
1481 	// [RH] Redraw the status bar if SCREENWIDTH > status bar width.
1482 	// Will draw borders around itself, too.
1483 	if (SCREENWIDTH > 320)
1484 	{
1485 		ST_SetNeedRefresh();
1486 	}
1487 
1488 	if (viewwidth == SCREENWIDTH)
1489 	{
1490 		return;
1491 	}
1492 
1493 	V_DrawBorder (0, 0, SCREENWIDTH, viewwindowy);
1494 	V_DrawBorder (0, viewwindowy, viewwindowx, viewheight + viewwindowy);
1495 	V_DrawBorder (viewwindowx + viewwidth, viewwindowy, SCREENWIDTH, viewheight + viewwindowy);
1496 	V_DrawBorder (0, viewwindowy + viewheight, SCREENWIDTH, ST_Y);
1497 
1498 	V_DrawFrame (viewwindowx, viewwindowy, viewwidth, viewheight);
1499 	V_MarkRect (0, 0, SCREENWIDTH, ST_Y);
1500 }
1501 
1502 //==========================================================================
1503 //
1504 // R_DrawTopBorder
1505 //
1506 // Draws the top border around the view for different size windows
1507 //
1508 //==========================================================================
1509 
V_DrawTopBorder()1510 static void V_DrawTopBorder ()
1511 {
1512 	FTexture *p;
1513 	int offset;
1514 
1515 	if (viewwidth == SCREENWIDTH)
1516 		return;
1517 
1518 	offset = gameinfo.Border.offset;
1519 
1520 	if (viewwindowy < 34)
1521 	{
1522 		V_DrawBorder (0, 0, viewwindowx, 34);
1523 		V_DrawBorder (viewwindowx, 0, viewwindowx + viewwidth, viewwindowy);
1524 		V_DrawBorder (viewwindowx + viewwidth, 0, SCREENWIDTH, 34);
1525 		p = TexMan(gameinfo.Border.t);
1526 		screen->FlatFill(viewwindowx, viewwindowy - p->GetHeight(),
1527 						 viewwindowx + viewwidth, viewwindowy, p, true);
1528 
1529 		p = TexMan(gameinfo.Border.l);
1530 		screen->FlatFill(viewwindowx - p->GetWidth(), viewwindowy,
1531 						 viewwindowx, 35, p, true);
1532 		p = TexMan(gameinfo.Border.r);
1533 		screen->FlatFill(viewwindowx + viewwidth, viewwindowy,
1534 						 viewwindowx + viewwidth + p->GetWidth(), 35, p, true);
1535 
1536 		p = TexMan(gameinfo.Border.tl);
1537 		screen->DrawTexture (p, viewwindowx - offset, viewwindowy - offset, TAG_DONE);
1538 
1539 		p = TexMan(gameinfo.Border.tr);
1540 		screen->DrawTexture (p, viewwindowx + viewwidth, viewwindowy - offset, TAG_DONE);
1541 	}
1542 	else
1543 	{
1544 		V_DrawBorder (0, 0, SCREENWIDTH, 34);
1545 	}
1546 }
1547 
1548 //==========================================================================
1549 //
1550 // R_RefreshViewBorder
1551 //
1552 // Draws the border around the player view, if needed.
1553 //
1554 //==========================================================================
1555 
V_RefreshViewBorder()1556 void V_RefreshViewBorder ()
1557 {
1558 	if (setblocks < 10)
1559 	{
1560 		if (BorderNeedRefresh)
1561 		{
1562 			BorderNeedRefresh--;
1563 			if (BorderTopRefresh)
1564 			{
1565 				BorderTopRefresh--;
1566 			}
1567 			V_DrawViewBorder();
1568 		}
1569 		else if (BorderTopRefresh)
1570 		{
1571 			BorderTopRefresh--;
1572 			V_DrawTopBorder();
1573 		}
1574 	}
1575 }
1576 
1577