1 /*
2  *  Copyright (C) 2002-2013  The DOSBox Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 
20 #include <sys/types.h>
21 #include <assert.h>
22 #include <math.h>
23 
24 #include "dosbox.h"
25 #include "video.h"
26 #include "render.h"
27 #include "setup.h"
28 #include "control.h"
29 #include "mapper.h"
30 #include "cross.h"
31 #include "hardware.h"
32 #include "support.h"
33 
34 #include "render_scalers.h"
35 
36 Render_t render;
37 ScalerLineHandler_t RENDER_DrawLine;
38 
39 static void RENDER_CallBack( GFX_CallBackFunctions_t function );
40 
Check_Palette(void)41 static void Check_Palette(void) {
42 	/* Clean up any previous changed palette data */
43 	if (render.pal.changed) {
44 		memset(render.pal.modified, 0, sizeof(render.pal.modified));
45 		render.pal.changed = false;
46 	}
47 	if (render.pal.first>render.pal.last)
48 		return;
49 	Bitu i;
50 
51    for (i=render.pal.first;i<=render.pal.last;i++) {
52       Bit8u r=render.pal.rgb[i].red;
53       Bit8u g=render.pal.rgb[i].green;
54       Bit8u b=render.pal.rgb[i].blue;
55       Bit32u newPal = GFX_GetRGB(r,g,b);
56       if (newPal != render.pal.lut.b32[i]) {
57          render.pal.changed = true;
58          render.pal.modified[i] = 1;
59          render.pal.lut.b32[i] = newPal;
60       }
61    }
62 	/* Setup pal index to startup values */
63 	render.pal.first=256;
64 	render.pal.last=0;
65 }
66 
RENDER_SetPal(Bit8u entry,Bit8u red,Bit8u green,Bit8u blue)67 void RENDER_SetPal(Bit8u entry,Bit8u red,Bit8u green,Bit8u blue) {
68 	render.pal.rgb[entry].red=red;
69 	render.pal.rgb[entry].green=green;
70 	render.pal.rgb[entry].blue=blue;
71 	if (render.pal.first>entry) render.pal.first=entry;
72 	if (render.pal.last<entry) render.pal.last=entry;
73 }
74 
RENDER_EmptyLineHandler(const void * src)75 static void RENDER_EmptyLineHandler(const void * src) {
76 }
77 
RENDER_StartLineHandler(const void * s)78 static void RENDER_StartLineHandler(const void * s) {
79 	if (s) {
80 		const Bitu *src = (Bitu*)s;
81 		Bitu *cache = (Bitu*)(render.scale.cacheRead);
82 		for (Bits x=render.src.start;x>0;) {
83 			if (GCC_UNLIKELY(src[0] != cache[0])) {
84 				if (!GFX_StartUpdate( render.scale.outWrite, render.scale.outPitch )) {
85 					RENDER_DrawLine = RENDER_EmptyLineHandler;
86 					return;
87 				}
88 				render.scale.outWrite += render.scale.outPitch * Scaler_ChangedLines[0];
89 				RENDER_DrawLine = render.scale.lineHandler;
90 				RENDER_DrawLine( s );
91 				return;
92 			}
93 			x--; src++; cache++;
94 		}
95 	}
96 	render.scale.cacheRead += render.scale.cachePitch;
97 	Scaler_ChangedLines[0] += Scaler_Aspect[ render.scale.inLine ];
98 	render.scale.inLine++;
99 	render.scale.outLine++;
100 }
101 
RENDER_FinishLineHandler(const void * s)102 static void RENDER_FinishLineHandler(const void * s) {
103 	if (s) {
104 		const Bitu *src = (Bitu*)s;
105 		Bitu *cache = (Bitu*)(render.scale.cacheRead);
106       Bits x = render.src.start;
107       if(x > 0)memcpy(cache,src,x * sizeof(Bitu));
108 	}
109 	render.scale.cacheRead += render.scale.cachePitch;
110 }
111 
112 
RENDER_ClearCacheHandler(const void * src)113 static void RENDER_ClearCacheHandler(const void * src) {
114 	Bitu x, width;
115 	Bit32u *srcLine, *cacheLine;
116 	srcLine = (Bit32u *)src;
117 	cacheLine = (Bit32u *)render.scale.cacheRead;
118 	width = render.scale.cachePitch / 4;
119 	for (x=0;x<width;x++)
120 		cacheLine[x] = ~srcLine[x];
121 	render.scale.lineHandler( src );
122 }
123 
RENDER_StartUpdate(void)124 bool RENDER_StartUpdate(void) {
125 	if (GCC_UNLIKELY(render.updating))
126 		return false;
127 	if (GCC_UNLIKELY(!render.active))
128 		return false;
129 	if (GCC_UNLIKELY(render.frameskip.count<render.frameskip.max)) {
130 		render.frameskip.count++;
131 		return false;
132 	}
133 	render.frameskip.count=0;
134 	if (render.scale.inMode == scalerMode8) {
135 		Check_Palette();
136 	}
137 	render.scale.inLine = 0;
138 	render.scale.outLine = 0;
139 	render.scale.cacheRead = (Bit8u*)&scalerSourceCache;
140 	render.scale.outWrite = 0;
141 	render.scale.outPitch = 0;
142 	Scaler_ChangedLines[0] = 0;
143 	Scaler_ChangedLineIndex = 0;
144 	/* Clearing the cache will first process the line to make sure it's never the same */
145 	if (GCC_UNLIKELY( render.scale.clearCache) ) {
146 //		LOG_MSG("Clearing cache");
147 		//Will always have to update the screen with this one anyway, so let's update already
148 		if (GCC_UNLIKELY(!GFX_StartUpdate( render.scale.outWrite, render.scale.outPitch )))
149 			return false;
150 		render.fullFrame = true;
151 		render.scale.clearCache = false;
152 		RENDER_DrawLine = RENDER_ClearCacheHandler;
153 	} else {
154 		if (render.pal.changed) {
155 			/* Assume pal changes always do a full screen update anyway */
156 			if (GCC_UNLIKELY(!GFX_StartUpdate( render.scale.outWrite, render.scale.outPitch )))
157 				return false;
158 			RENDER_DrawLine = render.scale.linePalHandler;
159 			render.fullFrame = true;
160 		} else {
161 			RENDER_DrawLine = RENDER_StartLineHandler;
162          render.fullFrame = false;
163 		}
164 	}
165 	render.updating = true;
166 	return true;
167 }
168 
RENDER_Halt(void)169 static void RENDER_Halt( void ) {
170 	RENDER_DrawLine = RENDER_EmptyLineHandler;
171 	GFX_EndUpdate( 0 );
172 	render.updating=false;
173 	render.active=false;
174 }
175 
176 extern Bitu PIC_Ticks;
RENDER_EndUpdate(bool abort)177 void RENDER_EndUpdate( bool abort ) {
178 	if (GCC_UNLIKELY(!render.updating))
179 		return;
180 	RENDER_DrawLine = RENDER_EmptyLineHandler;
181 	if ( render.scale.outWrite ) {
182 		GFX_EndUpdate( abort? NULL : Scaler_ChangedLines );
183 		render.frameskip.hadSkip[render.frameskip.index] = 0;
184 	} else {
185 #if 0
186 		Bitu total = 0, i;
187 		render.frameskip.hadSkip[render.frameskip.index] = 1;
188 		for (i = 0;i<RENDER_SKIP_CACHE;i++)
189 			total += render.frameskip.hadSkip[i];
190 		LOG_MSG( "Skipped frame %d %d", PIC_Ticks, (total * 100) / RENDER_SKIP_CACHE );
191 #endif
192 	}
193 	render.frameskip.index = (render.frameskip.index + 1) & (RENDER_SKIP_CACHE - 1);
194 	render.updating=false;
195 }
196 
MakeAspectTable(Bitu skip,Bitu height,double scaley,Bitu miny)197 static Bitu MakeAspectTable(Bitu skip,Bitu height,double scaley,Bitu miny) {
198 	Bitu i;
199 	double lines=0;
200 	Bitu linesadded=0;
201 
202    memset(Scaler_Aspect,0,skip);
203 
204 	height += skip;
205 	for (i=skip;i<height;i++) {
206 		lines += scaley;
207 		if (lines >= miny) {
208 			Bitu templines = (Bitu)lines;
209 			lines -= templines;
210 			linesadded += templines;
211 			Scaler_Aspect[i] = templines;
212 		} else {
213 			Scaler_Aspect[i] = 0;
214 		}
215 	}
216 	return linesadded;
217 }
218 
219 
RENDER_Reset(void)220 static void RENDER_Reset( void ) {
221 	Bitu width=render.src.width;
222 	Bitu height=render.src.height;
223 	bool dblw=render.src.dblw;
224 	bool dblh=render.src.dblh;
225 
226 	double gfx_scalew;
227 	double gfx_scaleh;
228 
229 	Bitu gfx_flags, xscale, yscale;
230 	ScalerSimpleBlock_t		*simpleBlock = &ScaleNormal1x;
231 	ScalerComplexBlock_t	*complexBlock = 0;
232 	if (render.aspect) {
233 		if (render.src.ratio>1.0) {
234 			gfx_scalew = 1;
235 			gfx_scaleh = render.src.ratio;
236 		} else {
237 			gfx_scalew = (1/render.src.ratio);
238 			gfx_scaleh = 1;
239 		}
240 	} else {
241 		gfx_scalew = 1;
242 		gfx_scaleh = 1;
243 	}
244 	if ((dblh && dblw) || (render.scale.forced && !dblh && !dblw)) {
245 		/* Initialize always working defaults */
246       simpleBlock = &ScaleNormal1x;
247 		/* Maybe override them */
248 	} else if (dblw) {
249 		simpleBlock = &ScaleNormalDw;
250 	} else if (dblh) {
251 		simpleBlock = &ScaleNormalDh;
252 	} else  {
253 forcenormal:
254 		complexBlock = 0;
255 		simpleBlock = &ScaleNormal1x;
256 	}
257 	if (complexBlock) {
258 		goto forcenormal;
259 		gfx_flags = complexBlock->gfxFlags;
260 		xscale = complexBlock->xscale;
261 		yscale = complexBlock->yscale;
262 //		LOG_MSG("Scaler:%s",complexBlock->name);
263 	} else {
264 		gfx_flags = simpleBlock->gfxFlags;
265 		xscale = simpleBlock->xscale;
266 		yscale = simpleBlock->yscale;
267 //		LOG_MSG("Scaler:%s",simpleBlock->name);
268 	}
269 	switch (render.src.bpp) {
270 	case 8:
271 		render.src.start = ( render.src.width * 1) / sizeof(Bitu);
272 		if (gfx_flags & GFX_CAN_8)
273 			gfx_flags |= GFX_LOVE_8;
274 		else
275 			gfx_flags |= GFX_LOVE_32;
276 			break;
277 	case 15:
278 			render.src.start = ( render.src.width * 2) / sizeof(Bitu);
279 			gfx_flags |= GFX_LOVE_15;
280 			gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY;
281 			break;
282 	case 16:
283 			render.src.start = ( render.src.width * 2) / sizeof(Bitu);
284 			gfx_flags |= GFX_LOVE_16;
285 			gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY;
286 			break;
287 	case 32:
288 			render.src.start = ( render.src.width * 4) / sizeof(Bitu);
289 			gfx_flags |= GFX_LOVE_32;
290 			gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY;
291 			break;
292 	}
293 	gfx_flags=GFX_GetBestMode(gfx_flags);
294 	if (!gfx_flags) {
295 		if (!complexBlock && simpleBlock == &ScaleNormal1x)
296 			E_Exit("Failed to create a rendering output");
297 		else
298 			goto forcenormal;
299 	}
300 	width *= xscale;
301 	Bitu skip = complexBlock ? 1 : 0;
302 	if (gfx_flags & GFX_SCALING) {
303 		height = MakeAspectTable(skip, render.src.height, yscale, yscale );
304 	} else {
305 		if ((gfx_flags & GFX_CAN_RANDOM) && gfx_scaleh > 1) {
306 			gfx_scaleh *= yscale;
307 			height = MakeAspectTable( skip, render.src.height, gfx_scaleh, yscale );
308 		} else {
309 			gfx_flags &= ~GFX_CAN_RANDOM;		//Hardware surface when possible
310 			height = MakeAspectTable( skip, render.src.height, yscale, yscale);
311 		}
312 	}
313 /* Setup the scaler variables */
314 	gfx_flags=GFX_SetSize(width,height,gfx_flags,gfx_scalew,gfx_scaleh,&RENDER_CallBack);
315 	if (gfx_flags & GFX_CAN_32)
316 		render.scale.outMode = scalerMode32;
317 	else
318 		E_Exit("Failed to create a rendering output");
319 	ScalerLineBlock_t *lineBlock;
320 	if (gfx_flags & GFX_HARDWARE) {
321 		{
322 			render.scale.complexHandler = 0;
323 			lineBlock = &simpleBlock->Linear;
324 		}
325 	} else {
326 		{
327 			render.scale.complexHandler = 0;
328 			lineBlock = &simpleBlock->Random;
329 		}
330 	}
331 	switch (render.src.bpp) {
332 	case 8:
333 		render.scale.lineHandler = (*lineBlock)[0][render.scale.outMode];
334 		render.scale.linePalHandler = (*lineBlock)[4][render.scale.outMode];
335 		render.scale.inMode = scalerMode8;
336 		render.scale.cachePitch = render.src.width * 1;
337 		break;
338 	case 15:
339 		render.scale.lineHandler = (*lineBlock)[1][render.scale.outMode];
340 		render.scale.linePalHandler = 0;
341 		render.scale.inMode = scalerMode15;
342 		render.scale.cachePitch = render.src.width * 2;
343 		break;
344 	case 16:
345 		render.scale.lineHandler = (*lineBlock)[2][render.scale.outMode];
346 		render.scale.linePalHandler = 0;
347 		render.scale.inMode = scalerMode16;
348 		render.scale.cachePitch = render.src.width * 2;
349 		break;
350 	case 32:
351 		render.scale.lineHandler = (*lineBlock)[3][render.scale.outMode];
352 		render.scale.linePalHandler = 0;
353 		render.scale.inMode = scalerMode32;
354 		render.scale.cachePitch = render.src.width * 4;
355 		break;
356 	default:
357 		E_Exit("RENDER:Wrong source bpp %d", render.src.bpp );
358 	}
359 	render.scale.blocks = render.src.width / SCALER_BLOCKSIZE;
360 	render.scale.lastBlock = render.src.width % SCALER_BLOCKSIZE;
361 	render.scale.inHeight = render.src.height;
362 	/* Reset the palette change detection to it's initial value */
363 	render.pal.first= 0;
364 	render.pal.last = 255;
365 	render.pal.changed = false;
366 	memset(render.pal.modified, 0, sizeof(render.pal.modified));
367 	//Finish this frame using a copy only handler
368 	RENDER_DrawLine = RENDER_FinishLineHandler;
369 	render.scale.outWrite = 0;
370 	/* Signal the next frame to first reinit the cache */
371 	render.scale.clearCache = true;
372 	render.active=true;
373 }
374 
RENDER_CallBack(GFX_CallBackFunctions_t function)375 static void RENDER_CallBack( GFX_CallBackFunctions_t function ) {
376 	if (function == GFX_CallBackStop) {
377 		RENDER_Halt( );
378 		return;
379 	} else if (function == GFX_CallBackRedraw) {
380 		render.scale.clearCache = true;
381 		return;
382 	} else if ( function == GFX_CallBackReset) {
383 		GFX_EndUpdate( 0 );
384 		RENDER_Reset();
385 	} else {
386 		E_Exit("Unhandled GFX_CallBackReset %d", function );
387 	}
388 }
389 
RENDER_SetSize(Bitu width,Bitu height,Bitu bpp,float fps,double ratio,bool dblw,bool dblh)390 void RENDER_SetSize(Bitu width,Bitu height,Bitu bpp,float fps,double ratio,bool dblw,bool dblh) {
391 	RENDER_Halt( );
392 	if (!width || !height || width > SCALER_MAXWIDTH || height > SCALER_MAXHEIGHT) {
393 		return;
394 	}
395 	if ( ratio > 1 ) {
396 		double target = height * ratio + 0.025;
397 		ratio = target / height;
398 	} else {
399 		//This would alter the width of the screen, we don't care about rounding errors here
400 	}
401 	render.src.width=width;
402 	render.src.height=height;
403 	render.src.bpp=bpp;
404 	render.src.dblw=dblw;
405 	render.src.dblh=dblh;
406 	render.src.fps=fps;
407 	render.src.ratio=ratio;
408 	RENDER_Reset( );
409 }
410 
411 extern void GFX_SetTitle(Bit32s cycles, Bits frameskip,bool paused);
IncreaseFrameSkip(bool pressed)412 static void IncreaseFrameSkip(bool pressed) {
413 	if (!pressed)
414 		return;
415 	if (render.frameskip.max<10) render.frameskip.max++;
416 	LOG_MSG("Frame Skip at %d",render.frameskip.max);
417 	GFX_SetTitle(-1,render.frameskip.max,false);
418 }
419 
DecreaseFrameSkip(bool pressed)420 static void DecreaseFrameSkip(bool pressed) {
421 	if (!pressed)
422 		return;
423 	if (render.frameskip.max>0) render.frameskip.max--;
424 	LOG_MSG("Frame Skip at %d",render.frameskip.max);
425 	GFX_SetTitle(-1,render.frameskip.max,false);
426 }
427 
RENDER_Init(Section * sec)428 void RENDER_Init(Section * sec) {
429 	Section_prop * section=static_cast<Section_prop *>(sec);
430 
431 	//For restarting the renderer.
432 	static bool running = false;
433 	bool aspect = render.aspect;
434 	Bitu scalersize = render.scale.size;
435 	bool scalerforced = render.scale.forced;
436 	scalerOperation_t scaleOp = render.scale.op;
437 
438 	render.pal.first=256;
439 	render.pal.last=0;
440 	render.aspect=section->Get_bool("aspect");
441 	render.frameskip.max=section->Get_int("frameskip");
442 	render.frameskip.count=0;
443 	std::string cline;
444 	std::string scaler;
445 	//Check for commandline paramters and parse them through the configclass so they get checked against allowed values
446 	if (control->cmdline->FindString("-scaler",cline,false)) {
447 		section->HandleInputline(std::string("scaler=") + cline);
448 	} else if (control->cmdline->FindString("-forcescaler",cline,false)) {
449 		section->HandleInputline(std::string("scaler=") + cline + " forced");
450 	}
451 
452 	Prop_multival* prop = section->Get_multival("scaler");
453 	scaler = prop->GetSection()->Get_string("type");
454 	std::string f = prop->GetSection()->Get_string("force");
455 	render.scale.forced = false;
456 	if(f == "forced") render.scale.forced = true;
457 
458    render.scale.op = scalerOpNormal;render.scale.size = 1;
459 
460 	//If something changed that needs a ReInit
461 	// Only ReInit when there is a src.bpp (fixes crashes on startup and directly changing the scaler without a screen specified yet)
462 	if(running && render.src.bpp && ((render.aspect != aspect) || (render.scale.op != scaleOp) ||
463 				  (render.scale.size != scalersize) || (render.scale.forced != scalerforced) ||
464 				   render.scale.forced))
465 		RENDER_CallBack( GFX_CallBackReset );
466 
467 	if(!running) render.updating=true;
468 	running = true;
469 
470 	MAPPER_AddHandler(DecreaseFrameSkip,MK_f7,MMOD1,"decfskip","Dec Fskip");
471 	MAPPER_AddHandler(IncreaseFrameSkip,MK_f8,MMOD1,"incfskip","Inc Fskip");
472 	GFX_SetTitle(-1,render.frameskip.max,false);
473 }
474 
475