DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/gui/render.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 
00020 #include <sys/types.h>
00021 #include <assert.h>
00022 #include <math.h>
00023 
00024 #include "dosbox.h"
00025 #include "video.h"
00026 #include "render.h"
00027 #include "setup.h"
00028 #include "control.h"
00029 #include "mapper.h"
00030 #include "cross.h"
00031 #include "hardware.h"
00032 #include "support.h"
00033 #include "sdlmain.h"
00034 
00035 #include "render_scalers.h"
00036 #if defined(__SSE__)
00037 #include <xmmintrin.h>
00038 #include <emmintrin.h>
00039 #endif
00040 
00041 Render_t render;
00042 Bitu last_gfx_flags = 0;
00043 ScalerLineHandler_t RENDER_DrawLine;
00044 
00045 void RENDER_CallBack( GFX_CallBackFunctions_t function );
00046 
00047 static void Check_Palette(void) {
00048     /* Clean up any previous changed palette data */
00049     if (render.pal.changed) {
00050         memset(render.pal.modified, 0, sizeof(render.pal.modified));
00051         render.pal.changed = false;
00052     }
00053     if (render.pal.first>render.pal.last) 
00054         return;
00055     Bitu i;
00056     switch (render.scale.outMode) {
00057     case scalerMode8:
00058         GFX_SetPalette(render.pal.first,render.pal.last-render.pal.first+1,(GFX_PalEntry *)&render.pal.rgb[render.pal.first]);
00059         break;
00060     case scalerMode15:
00061     case scalerMode16:
00062         for (i=render.pal.first;i<=render.pal.last;i++) {
00063             Bit8u r=render.pal.rgb[i].red;
00064             Bit8u g=render.pal.rgb[i].green;
00065             Bit8u b=render.pal.rgb[i].blue;
00066             Bit16u newPal = GFX_GetRGB(r,g,b);
00067             if (newPal != render.pal.lut.b16[i]) {
00068                 render.pal.changed = true;
00069                 render.pal.modified[i] = 1;
00070                 render.pal.lut.b16[i] = newPal;
00071             }
00072         }
00073         break;
00074     case scalerMode32:
00075     default:
00076         for (i=render.pal.first;i<=render.pal.last;i++) {
00077             Bit8u r=render.pal.rgb[i].red;
00078             Bit8u g=render.pal.rgb[i].green;
00079             Bit8u b=render.pal.rgb[i].blue;
00080             Bit32u newPal = GFX_GetRGB(r,g,b);
00081             if (newPal != render.pal.lut.b32[i]) {
00082                 render.pal.changed = true;
00083                 render.pal.modified[i] = 1;
00084                 render.pal.lut.b32[i] = newPal;
00085             }
00086         }
00087         break;
00088     }
00089     /* Setup pal index to startup values */
00090     render.pal.first=256;
00091     render.pal.last=0;
00092 }
00093 
00094 uint32_t GFX_palette32bpp[256] = {0};
00095 
00096 unsigned int GFX_GetBShift();
00097 
00098 void RENDER_SetPal(Bit8u entry,Bit8u red,Bit8u green,Bit8u blue) {
00099     if (GFX_GetBShift() == 0) {
00100         GFX_palette32bpp[entry] =
00101             ((uint32_t)red << (uint32_t)16) +
00102             ((uint32_t)green << (uint32_t)8) +
00103             ((uint32_t)blue << (uint32_t)0);
00104     }
00105     else {
00106         GFX_palette32bpp[entry] =
00107             ((uint32_t)blue << (uint32_t)16) +
00108             ((uint32_t)green << (uint32_t)8) +
00109             ((uint32_t)red << (uint32_t)0);
00110     }
00111 
00112     render.pal.rgb[entry].red=red;
00113     render.pal.rgb[entry].green=green;
00114     render.pal.rgb[entry].blue=blue;
00115     if (render.pal.first>entry) render.pal.first=entry;
00116     if (render.pal.last<entry) render.pal.last=entry;
00117 }
00118 
00119 static void RENDER_EmptyLineHandler(const void * src) {
00120     (void)src;//UNUSED
00121 }
00122 
00123 /*HACK*/
00124 #if defined(__SSE__) && defined(_M_AMD64)
00125 # define sse2_available (1) /* SSE2 is always available on x86_64 */
00126 #else
00127 # ifdef __SSE__
00128 extern bool                             sse1_available;
00129 extern bool                             sse2_available;
00130 # endif
00131 #endif
00132 /*END HACK*/
00133 
00134 static void RENDER_StartLineHandler(const void * s) {
00135     if (s) {
00136         const Bitu *src = (Bitu*)s;
00137         Bitu *cache = (Bitu*)(render.scale.cacheRead);
00138         Bits count = (Bits)render.src.start;
00139 #if defined(__SSE__)
00140         if (sse2_available) {
00141 #define MY_SIZEOF_INT_P sizeof(*src)
00142             static const Bitu simd_inc = 16/MY_SIZEOF_INT_P;
00143             while (count >= (Bits)simd_inc) {
00144                 __m128i v = _mm_loadu_si128((const __m128i*)src);
00145                 __m128i c = _mm_loadu_si128((const __m128i*)cache);
00146                 __m128i cmp = _mm_cmpeq_epi32(v, c);
00147                 if (GCC_UNLIKELY(_mm_movemask_epi8(cmp) != 0xFFFF))
00148                     goto cacheMiss;
00149                 count-=(Bits)simd_inc; src+=simd_inc; cache+=simd_inc;
00150             }
00151 #undef MY_SIZEOF_INT_P
00152         }
00153         else
00154 #endif
00155         {
00156             while (count) {
00157                 if (GCC_UNLIKELY(src[0] != cache[0]))
00158                     goto cacheMiss;
00159                 count--; src++; cache++;
00160             }
00161         }
00162     }
00163 /* cacheHit */
00164     render.scale.cacheRead += render.scale.cachePitch;
00165     Scaler_ChangedLines[0] += Scaler_Aspect[ render.scale.inLine ];
00166     render.scale.inLine++;
00167     render.scale.outLine++;
00168     return;
00169 cacheMiss:
00170     if (!GFX_StartUpdate( render.scale.outWrite, render.scale.outPitch )) {
00171         RENDER_DrawLine = RENDER_EmptyLineHandler;
00172         return;
00173     }
00174     render.scale.outWrite += render.scale.outPitch * Scaler_ChangedLines[0];
00175     RENDER_DrawLine = render.scale.lineHandler;
00176     RENDER_DrawLine( s );
00177 }
00178 
00179 static void RENDER_FinishLineHandler(const void * s) {
00180     if (s) {
00181         const Bitu *src = (Bitu*)s;
00182         Bitu *cache = (Bitu*)(render.scale.cacheRead);
00183         for (Bitu x=render.src.start;x>0;) {
00184             cache[0] = src[0];
00185             x--; src++; cache++;
00186         }
00187     }
00188     render.scale.cacheRead += render.scale.cachePitch;
00189 }
00190 
00191 
00192 static void RENDER_ClearCacheHandler(const void * src) {
00193     Bitu x, width;
00194     Bit32u *srcLine, *cacheLine;
00195     srcLine = (Bit32u *)src;
00196     cacheLine = (Bit32u *)render.scale.cacheRead;
00197     width = render.scale.cachePitch / 4;
00198     for (x=0;x<width;x++)
00199         cacheLine[x] = ~srcLine[x];
00200     render.scale.lineHandler( src );
00201 }
00202 
00203 extern void GFX_SetTitle(Bit32s cycles,Bits frameskip,Bits timing,bool paused);
00204 
00205 bool RENDER_StartUpdate(void) {
00206     if (GCC_UNLIKELY(render.updating))
00207         return false;
00208     if (GCC_UNLIKELY(!render.active))
00209         return false;
00210     if (GCC_UNLIKELY(render.frameskip.count<render.frameskip.max)) {
00211         render.frameskip.count++;
00212         return false;
00213     }
00214     render.frameskip.count=0;
00215     if (render.scale.inMode == scalerMode8) {
00216         Check_Palette();
00217     }
00218     render.scale.inLine = 0;
00219     render.scale.outLine = 0;
00220     render.scale.cacheRead = (Bit8u*)&scalerSourceCache;
00221     render.scale.outWrite = 0;
00222     render.scale.outPitch = 0;
00223     Scaler_ChangedLines[0] = 0;
00224     Scaler_ChangedLineIndex = 0;
00225     /* Clearing the cache will first process the line to make sure it's never the same */
00226     if (GCC_UNLIKELY( render.scale.clearCache) ) {
00227 //      LOG_MSG("Clearing cache");
00228         //Will always have to update the screen with this one anyway, so let's update already
00229         if (GCC_UNLIKELY(!GFX_StartUpdate( render.scale.outWrite, render.scale.outPitch )))
00230             return false;
00231         render.fullFrame = true;
00232         RENDER_DrawLine = RENDER_ClearCacheHandler;
00233     } else {
00234         if (render.pal.changed) {
00235             /* Assume pal changes always do a full screen update anyway */
00236             if (GCC_UNLIKELY(!GFX_StartUpdate( render.scale.outWrite, render.scale.outPitch )))
00237                 return false;
00238             RENDER_DrawLine = render.scale.linePalHandler;
00239             render.fullFrame = true;
00240         } else {
00241             RENDER_DrawLine = RENDER_StartLineHandler;
00242             if (GCC_UNLIKELY(CaptureState & (CAPTURE_IMAGE|CAPTURE_VIDEO))) 
00243                 render.fullFrame = true;
00244             else
00245                 render.fullFrame = false;
00246         }
00247     }
00248     render.updating = true;
00249     return true;
00250 }
00251 
00252 static void RENDER_Halt( void ) {
00253     RENDER_DrawLine = RENDER_EmptyLineHandler;
00254     GFX_EndUpdate( 0 );
00255     render.updating=false;
00256     render.active=false;
00257 }
00258 
00259 extern Bitu PIC_Ticks;
00260 extern bool pause_on_vsync;
00261 void PauseDOSBox(bool pressed);
00262 
00263 void RENDER_EndUpdate( bool abort ) {
00264     if (GCC_UNLIKELY(!render.updating))
00265         return;
00266 
00267     if (!abort && render.active && RENDER_DrawLine == RENDER_ClearCacheHandler)
00268         render.scale.clearCache = false;
00269 
00270     RENDER_DrawLine = RENDER_EmptyLineHandler;
00271     if (GCC_UNLIKELY(CaptureState & (CAPTURE_IMAGE|CAPTURE_VIDEO))) {
00272         Bitu pitch, flags;
00273         flags = 0;
00274         if (render.src.dblw != render.src.dblh) {
00275             if (render.src.dblw) flags|=CAPTURE_FLAG_DBLW;
00276             if (render.src.dblh) flags|=CAPTURE_FLAG_DBLH;
00277         }
00278         float fps = render.src.fps;
00279         pitch = render.scale.cachePitch;
00280         if (render.frameskip.max)
00281             fps /= 1+render.frameskip.max;
00282         CAPTURE_AddImage( render.src.width, render.src.height, render.src.bpp, pitch,
00283             flags, fps, (Bit8u *)&scalerSourceCache, (Bit8u*)&render.pal.rgb );
00284     }
00285     if ( render.scale.outWrite ) {
00286         GFX_EndUpdate( abort? NULL : Scaler_ChangedLines );
00287         render.frameskip.hadSkip[render.frameskip.index] = 0;
00288     } else {
00289 #if 0
00290         Bitu total = 0, i;
00291         render.frameskip.hadSkip[render.frameskip.index] = 1;
00292         for (i = 0;i<RENDER_SKIP_CACHE;i++) 
00293             total += render.frameskip.hadSkip[i];
00294         LOG_MSG( "Skipped frame %d %d", PIC_Ticks, (total * 100) / RENDER_SKIP_CACHE );
00295 #endif
00296         // Force output to update the screen even if nothing changed...
00297         // works only with Direct3D output (GFX_StartUpdate() was probably not even called)
00298         if (render.forceUpdate) GFX_EndUpdate( 0 );
00299     }
00300     render.frameskip.index = (render.frameskip.index + 1) & (RENDER_SKIP_CACHE - 1);
00301     render.updating=false;
00302 
00303     if (pause_on_vsync) {
00304         pause_on_vsync = false;
00305         PauseDOSBox(true);
00306     }
00307 }
00308 
00309 static Bitu MakeAspectTable(Bitu skip,Bitu height,double scaley,Bitu miny) {
00310     Bitu i;
00311     double lines=0;
00312     Bitu linesadded=0;
00313     for (i=0;i<skip;i++)
00314         Scaler_Aspect[i] = 0;
00315 
00316     height += skip;
00317     for (i=skip;i<height;i++) {
00318         lines += scaley;
00319         if (lines >= miny) {
00320             Bitu templines = (Bitu)lines;
00321             lines -= templines;
00322             linesadded += templines;
00323             Scaler_Aspect[i] = templines;
00324         } else {
00325             Scaler_Aspect[i] = 0;
00326         }
00327     }
00328     return linesadded;
00329 }
00330 
00331 void RENDER_Reset( void ) {
00332     Bitu width=render.src.width;
00333     Bitu height=render.src.height;
00334     bool dblw=render.src.dblw;
00335     bool dblh=render.src.dblh;
00336 
00337     double gfx_scalew;
00338     double gfx_scaleh;
00339 
00340     if (width == 0 || height == 0)
00341         return;
00342     
00343     Bitu gfx_flags, xscale, yscale;
00344     ScalerSimpleBlock_t     *simpleBlock = &ScaleNormal1x;
00345     ScalerComplexBlock_t    *complexBlock = 0;
00346     gfx_scalew = 1;
00347     gfx_scaleh = 1;
00348 
00349 #if !C_XBRZ
00350     if (render.aspect == ASPECT_TRUE && !render.aspectOffload)
00351 #else
00352     if (render.aspect == ASPECT_TRUE && !render.aspectOffload && !(sdl_xbrz.enable && sdl_xbrz.scale_on))
00353 #endif
00354     {
00355         if (render.src.ratio>1.0) {
00356             gfx_scalew = 1;
00357             gfx_scaleh = render.src.ratio;
00358         } else {
00359             gfx_scalew = (1.0/render.src.ratio);
00360             gfx_scaleh = 1;
00361         }
00362     }
00363 
00364     if ((dblh && dblw) || (render.scale.forced && !dblh && !dblw)) {
00365         /* Initialize always working defaults */
00366         if (render.scale.size == 2)
00367             simpleBlock = &ScaleNormal2x;
00368         else if (render.scale.size == 3)
00369             simpleBlock = &ScaleNormal3x;
00370         else if (render.scale.size == 1 && !(dblh || dblw) && render.scale.hardware)
00371             simpleBlock = &ScaleNormal1x;
00372         else if (render.scale.size == 4 && !(dblh || dblw) && render.scale.hardware)
00373             simpleBlock = &ScaleNormal2x;
00374         else if (render.scale.size == 6 && !(dblh || dblw) && render.scale.hardware)
00375             simpleBlock = &ScaleNormal3x;
00376         else if (render.scale.size == 4 && !render.scale.hardware)
00377             simpleBlock = &ScaleNormal4x;
00378         else if (render.scale.size == 5 && !render.scale.hardware)
00379             simpleBlock = &ScaleNormal5x;
00380         else if (render.scale.size == 8 && !(dblh || dblw) && render.scale.hardware)
00381             simpleBlock = &ScaleNormal4x;
00382         else if (render.scale.size == 10 && !(dblh || dblw) && render.scale.hardware)
00383             simpleBlock = &ScaleNormal5x;
00384         else
00385             simpleBlock = &ScaleNormal1x;
00386         /* Maybe override them */
00387 #if RENDER_USE_ADVANCED_SCALERS>0
00388         switch (render.scale.op) {
00389 #if RENDER_USE_ADVANCED_SCALERS>2
00390         case scalerOpAdvInterp:
00391             if (render.scale.size == 2)
00392                 complexBlock = &ScaleAdvInterp2x;
00393             else if (render.scale.size == 3)
00394                 complexBlock = &ScaleAdvInterp3x;
00395             break;
00396         case scalerOpAdvMame:
00397             if (render.scale.size == 2)
00398                 complexBlock = &ScaleAdvMame2x;
00399             else if (render.scale.size == 3)
00400                 complexBlock = &ScaleAdvMame3x;
00401             break;
00402         case scalerOpHQ:
00403             if (render.scale.size == 2)
00404                 complexBlock = &ScaleHQ2x;
00405             else if (render.scale.size == 3)
00406                 complexBlock = &ScaleHQ3x;
00407             break;
00408         case scalerOpSuperSaI:
00409             if (render.scale.size == 2)
00410                 complexBlock = &ScaleSuper2xSaI;
00411             break;
00412         case scalerOpSuperEagle:
00413             if (render.scale.size == 2)
00414                 complexBlock = &ScaleSuperEagle;
00415             break;
00416         case scalerOpSaI:
00417             if (render.scale.size == 2)
00418                 complexBlock = &Scale2xSaI;
00419             break;
00420 #endif
00421         case scalerOpTV:
00422             if (render.scale.size == 2)
00423                 simpleBlock = &ScaleTV2x;
00424             else if (render.scale.size == 3)
00425                 simpleBlock = &ScaleTV3x;
00426             break;
00427         case scalerOpRGB:
00428             if (render.scale.size == 2)
00429                 simpleBlock = &ScaleRGB2x;
00430             else if (render.scale.size == 3)
00431                 simpleBlock = &ScaleRGB3x;
00432             break;
00433         case scalerOpScan:
00434             if (render.scale.size == 2)
00435                 simpleBlock = &ScaleScan2x;
00436             else if (render.scale.size == 3)
00437                 simpleBlock = &ScaleScan3x;
00438             break;
00439         default:
00440             break;
00441         }
00442 #endif
00443     } else if (dblw && !render.scale.hardware) {
00444         simpleBlock = &ScaleNormalDw;
00445     } else if (dblh && !render.scale.hardware) {
00446         simpleBlock = &ScaleNormalDh;
00447     } else  {
00448 forcenormal:
00449         complexBlock = 0;
00450         simpleBlock = &ScaleNormal1x;
00451     }
00452     if (complexBlock) {
00453 #if RENDER_USE_ADVANCED_SCALERS>1
00454         if ((width >= SCALER_COMPLEXWIDTH - 16) || height >= SCALER_COMPLEXHEIGHT - 16) {
00455             LOG_MSG("Scaler can't handle this resolution, going back to normal");
00456             goto forcenormal;
00457         }
00458 #else
00459         goto forcenormal;
00460 #endif
00461         gfx_flags = complexBlock->gfxFlags;
00462         xscale = complexBlock->xscale;  
00463         yscale = complexBlock->yscale;
00464 //      LOG_MSG("Scaler:%s",complexBlock->name);
00465     } else {
00466         gfx_flags = simpleBlock->gfxFlags;
00467         xscale = simpleBlock->xscale;   
00468         yscale = simpleBlock->yscale;
00469 //      LOG_MSG("Scaler:%s",simpleBlock->name);
00470     }
00471     switch (render.src.bpp) {
00472     case 8:
00473         render.src.start = ( render.src.width * 1) / sizeof(Bitu);
00474         if (gfx_flags & GFX_CAN_8)
00475             gfx_flags |= GFX_LOVE_8;
00476         else
00477             gfx_flags |= GFX_LOVE_32;
00478         break;
00479     case 15:
00480         render.src.start = ( render.src.width * 2) / sizeof(Bitu);
00481         gfx_flags |= GFX_LOVE_15;
00482         gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY;
00483         break;
00484     case 16:
00485         render.src.start = ( render.src.width * 2) / sizeof(Bitu);
00486         gfx_flags |= GFX_LOVE_16;
00487         gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY;
00488         break;
00489     case 32:
00490         render.src.start = ( render.src.width * 4) / sizeof(Bitu);
00491         gfx_flags |= GFX_LOVE_32;
00492         gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY;
00493         break;
00494     default:
00495         render.src.start = ( render.src.width * 1) / sizeof(Bitu);
00496         if (gfx_flags & GFX_CAN_8)
00497             gfx_flags |= GFX_LOVE_8;
00498         else
00499             gfx_flags |= GFX_LOVE_32;
00500         break;
00501     }
00502 #if !defined(C_SDL2)
00503     gfx_flags=GFX_GetBestMode(gfx_flags);
00504 #endif
00505     if (!gfx_flags) {
00506         if (!complexBlock && simpleBlock == &ScaleNormal1x) 
00507             E_Exit("Failed to create a rendering output");
00508         else 
00509             goto forcenormal;
00510     }
00511     width *= xscale;
00512     Bitu skip = complexBlock ? 1 : 0;
00513     if (gfx_flags & GFX_SCALING) {
00514         if(render.scale.size == 1 && render.scale.hardware) { //hardware_none
00515             if(dblh)
00516             gfx_scaleh *= 1;
00517             if(dblw)
00518             gfx_scalew *= 1;
00519         } else if(render.scale.size == 4 && render.scale.hardware) {
00520             if(dblh)
00521             gfx_scaleh *= 2;
00522             if(dblw)
00523             gfx_scalew *= 2;
00524         } else if(render.scale.size == 6 && render.scale.hardware) {
00525             if(dblh && dblw) {
00526             gfx_scaleh *= 3; gfx_scalew *= 3;
00527             } else if(dblh) {
00528             gfx_scaleh *= 2;
00529             } else if(dblw)
00530             gfx_scalew *= 2;
00531         } else if(render.scale.size == 8 && render.scale.hardware) { //hardware4x
00532             if(dblh)
00533             gfx_scaleh *= 4;
00534             if(dblw)
00535             gfx_scalew *= 4;
00536         } else if(render.scale.size == 10 && render.scale.hardware) { //hardware5x
00537             if(dblh && dblw) {
00538             gfx_scaleh *= 5; gfx_scalew *= 5;
00539             } else if(dblh) {
00540             gfx_scaleh *= 4;
00541             } else if(dblw)
00542             gfx_scalew *= 4;
00543         }
00544         height = MakeAspectTable(skip, render.src.height, yscale, yscale );
00545     } else {
00546         // Print a warning when hardware scalers are selected, hopefully the first
00547         // video mode will not have dblh or dblw or AR will be wrong
00548         if (render.scale.hardware) {
00549             LOG_MSG("Output does not support hardware scaling, switching to normal scalers");
00550             render.scale.hardware=false;
00551         }
00552         if ((gfx_flags & GFX_CAN_RANDOM) && gfx_scaleh > 1) {
00553             gfx_scaleh *= yscale;
00554             height = MakeAspectTable( skip, render.src.height, gfx_scaleh, yscale );
00555         } else {
00556             gfx_flags &= ~GFX_CAN_RANDOM;       //Hardware surface when possible
00557             height = MakeAspectTable( skip, render.src.height, yscale, yscale);
00558         }
00559     }
00560 /* update the aspect ratio */
00561     sdl.srcAspect.x = render.src.width * (render.src.dblw ? 2 : 1);
00562     sdl.srcAspect.y = (int)floor((render.src.height * (render.src.dblh ? 2 : 1) * render.src.ratio) + 0.5);
00563     sdl.srcAspect.xToY = (double)sdl.srcAspect.x / sdl.srcAspect.y;
00564     sdl.srcAspect.yToX = (double)sdl.srcAspect.y / sdl.srcAspect.x;
00565     LOG_MSG("Aspect ratio: %u x %u  xToY=%.3f yToX=%.3f",sdl.srcAspect.x,sdl.srcAspect.y,sdl.srcAspect.xToY,sdl.srcAspect.yToX);
00566 /* Setup the scaler variables */
00567     gfx_flags=GFX_SetSize(width,height,gfx_flags,gfx_scalew,gfx_scaleh,&RENDER_CallBack);
00568     if (gfx_flags & GFX_CAN_8)
00569         render.scale.outMode = scalerMode8;
00570     else if (gfx_flags & GFX_CAN_15)
00571         render.scale.outMode = scalerMode15;
00572     else if (gfx_flags & GFX_CAN_16)
00573         render.scale.outMode = scalerMode16;
00574     else if (gfx_flags & GFX_CAN_32)
00575         render.scale.outMode = scalerMode32;
00576     else 
00577         E_Exit("Failed to create a rendering output");
00578     ScalerLineBlock_t *lineBlock;
00579     if (gfx_flags & GFX_HARDWARE) {
00580 #if RENDER_USE_ADVANCED_SCALERS>1
00581         if (complexBlock) {
00582             lineBlock = &ScalerCache;
00583             render.scale.complexHandler = complexBlock->Linear[ render.scale.outMode ];
00584         } else
00585 #endif
00586         {
00587             render.scale.complexHandler = 0;
00588             lineBlock = &simpleBlock->Linear;
00589         }
00590     } else {
00591 #if RENDER_USE_ADVANCED_SCALERS>1
00592         if (complexBlock) {
00593             lineBlock = &ScalerCache;
00594             render.scale.complexHandler = complexBlock->Random[ render.scale.outMode ];
00595         } else
00596 #endif
00597         {
00598             render.scale.complexHandler = 0;
00599             lineBlock = &simpleBlock->Random;
00600         }
00601     }
00602     switch (render.src.bpp) {
00603     case 8:
00604         render.scale.lineHandler = (*lineBlock)[0][render.scale.outMode];
00605         render.scale.linePalHandler = (*lineBlock)[4][render.scale.outMode];
00606         render.scale.inMode = scalerMode8;
00607         render.scale.cachePitch = render.src.width * 1;
00608         break;
00609     case 15:
00610         render.scale.lineHandler = (*lineBlock)[1][render.scale.outMode];
00611         render.scale.linePalHandler = 0;
00612         render.scale.inMode = scalerMode15;
00613         render.scale.cachePitch = render.src.width * 2;
00614         break;
00615     case 16:
00616         render.scale.lineHandler = (*lineBlock)[2][render.scale.outMode];
00617         render.scale.linePalHandler = 0;
00618         render.scale.inMode = scalerMode16;
00619         render.scale.cachePitch = render.src.width * 2;
00620         break;
00621     case 32:
00622         render.scale.lineHandler = (*lineBlock)[3][render.scale.outMode];
00623         render.scale.linePalHandler = 0;
00624         render.scale.inMode = scalerMode32;
00625         render.scale.cachePitch = render.src.width * 4;
00626         break;
00627     default:
00628         //render.src.bpp=8;
00629         render.scale.lineHandler = (*lineBlock)[0][render.scale.outMode];
00630         render.scale.linePalHandler = (*lineBlock)[4][render.scale.outMode];
00631         render.scale.inMode = scalerMode8;
00632         render.scale.cachePitch = render.src.width * 1;
00633         break;
00634         //E_Exit("RENDER:Wrong source bpp %d", render.src.bpp );
00635     }
00636     render.scale.blocks = render.src.width / SCALER_BLOCKSIZE;
00637     render.scale.lastBlock = render.src.width % SCALER_BLOCKSIZE;
00638     render.scale.inHeight = render.src.height;
00639     /* Reset the palette change detection to it's initial value */
00640     render.pal.first= 0;
00641     render.pal.last = 255;
00642     render.pal.changed = false;
00643     memset(render.pal.modified, 0, sizeof(render.pal.modified));
00644     //Finish this frame using a copy only handler
00645     RENDER_DrawLine = RENDER_FinishLineHandler;
00646     render.scale.outWrite = 0;
00647     /* Signal the next frame to first reinit the cache */
00648     render.scale.clearCache = true;
00649     render.active=true;
00650 
00651     last_gfx_flags = gfx_flags;
00652 }
00653 
00654 void RENDER_CallBack( GFX_CallBackFunctions_t function ) {
00655     if (function == GFX_CallBackStop) {
00656         RENDER_Halt( ); 
00657         return;
00658     } else if (function == GFX_CallBackRedraw) {
00659         render.scale.clearCache = true;
00660         return;
00661     } else if ( function == GFX_CallBackReset) {
00662         GFX_EndUpdate( 0 ); 
00663         RENDER_Reset();
00664     } else {
00665         E_Exit("Unhandled GFX_CallBackReset %d", function );
00666     }
00667 }
00668 
00669 void RENDER_SetSize(Bitu width,Bitu height,Bitu bpp,float fps,double scrn_ratio) {
00670     RENDER_Halt( );
00671     if (!width || !height || width > SCALER_MAXWIDTH || height > SCALER_MAXHEIGHT) { 
00672         return; 
00673     }
00674 
00675     // figure out doublewidth/height values
00676     bool dblw = false;
00677     bool dblh = false;
00678     double ratio = (((double)width)/((double)height))/scrn_ratio;
00679     if(ratio > 1.6) {
00680         dblh=true;
00681         ratio /= 2.0;
00682     } else if(ratio < 0.75) {
00683         dblw=true;
00684         ratio *= 2.0;
00685     } else if(!dblw && !dblh && (width < 370) && (height < 280)) {
00686         dblw=true; dblh=true;
00687     }
00688     LOG_MSG("pixratio %1.3f, dw %s, dh %s",ratio,dblw?"true":"false",dblh?"true":"false");
00689 
00690     if ( ratio > 1.0 ) {
00691         double target = height * ratio + 0.1;
00692         ratio = target / height;
00693     } else {
00694         //This would alter the width of the screen, we don't care about rounding errors here
00695     }
00696     render.src.width=width;
00697     render.src.height=height;
00698     render.src.bpp=bpp;
00699     render.src.dblw=dblw;
00700     render.src.dblh=dblh;
00701     render.src.fps=fps;
00702     render.src.ratio=ratio;
00703     render.src.scrn_ratio=scrn_ratio;
00704     RENDER_Reset( );
00705 }
00706 
00707 //extern void GFX_SetTitle(Bit32s cycles, Bits frameskip, Bits timing, bool paused);
00708 static void IncreaseFrameSkip(bool pressed) {
00709     if (!pressed)
00710         return;
00711     if (render.frameskip.max<10) render.frameskip.max++;
00712     LOG_MSG("Frame Skip at %d",(int)render.frameskip.max);
00713     GFX_SetTitle(-1,(Bits)render.frameskip.max,-1,false);
00714 }
00715 
00716 static void DecreaseFrameSkip(bool pressed) {
00717     if (!pressed)
00718         return;
00719     if (render.frameskip.max>0) render.frameskip.max--;
00720     LOG_MSG("Frame Skip at %d",(int)render.frameskip.max);
00721     GFX_SetTitle(-1,(Bits)render.frameskip.max,-1,false);
00722 }
00723 /* Disabled as I don't want to waste a keybind for that. Might be used in the future (Qbix)
00724 static void ChangeScaler(bool pressed) {
00725     if (!pressed)
00726         return;
00727     render.scale.op = (scalerOperation)((int)render.scale.op+1);
00728     if((render.scale.op) >= scalerLast || render.scale.size == 1) {
00729         render.scale.op = (scalerOperation)0;
00730         if(++render.scale.size > 3)
00731             render.scale.size = 1;
00732     }
00733     RENDER_CallBack( GFX_CallBackReset );
00734 } */
00735 
00736 #include "vga.h"
00737 
00738 void RENDER_SetForceUpdate(bool f) {
00739     render.forceUpdate = f;
00740 }
00741 
00742 void RENDER_UpdateFrameskipMenu(void) {
00743     char tmp[64];
00744 
00745     for (unsigned int f=0;f <= 10;f++) {
00746         sprintf(tmp,"frameskip_%u",f);
00747         DOSBoxMenu::item &item = mainMenu.get_item(tmp);
00748         item.check(render.frameskip.max == f);
00749     }
00750 }
00751 
00752 void VGA_SetupDrawing(Bitu /*val*/);
00753 
00754 void RENDER_OnSectionPropChange(Section *x) {
00755     (void)x;//UNUSED
00756     Section_prop * section = static_cast<Section_prop *>(control->GetSection("render"));
00757 
00758     bool p_doublescan = vga.draw.doublescan_set;
00759     bool p_char9 = vga.draw.char9_set;
00760     int p_aspect = render.aspect;
00761 
00762     std::string s_aspect = section->Get_string("aspect");
00763     render.aspect = ASPECT_FALSE;
00764     if (s_aspect == "true" || s_aspect == "1" || s_aspect == "yes") render.aspect = ASPECT_TRUE;
00765 #if C_SURFACE_POSTRENDER_ASPECT
00766     if (s_aspect == "nearest") render.aspect = ASPECT_NEAREST;
00767     if (s_aspect == "bilinear") render.aspect = ASPECT_BILINEAR;
00768 #endif
00769 
00770     render.frameskip.max = (Bitu)section->Get_int("frameskip");
00771 
00772     vga.draw.doublescan_set=section->Get_bool("doublescan");
00773     vga.draw.char9_set=section->Get_bool("char9");
00774 
00775     if (render.aspect != p_aspect || vga.draw.doublescan_set != p_doublescan || vga.draw.char9_set != p_char9)
00776         RENDER_CallBack(GFX_CallBackReset);
00777     if (vga.draw.doublescan_set != p_doublescan || vga.draw.char9_set != p_char9)
00778         VGA_StartResize();
00779 
00780     mainMenu.get_item("vga_9widetext").check(vga.draw.char9_set).refresh_item(mainMenu);
00781     mainMenu.get_item("doublescan").check(vga.draw.doublescan_set).refresh_item(mainMenu);
00782 
00783 #if C_XBRZ
00784     xBRZ_Change_Options(section);
00785 #endif
00786 
00787     RENDER_UpdateFrameskipMenu();
00788 }
00789 
00790 std::string RENDER_GetScaler(void) {
00791     Section_prop * section=static_cast<Section_prop *>(control->GetSection("render"));
00792     Prop_multival* prop = section->Get_multival("scaler");
00793     return prop->GetSection()->Get_string("type");
00794 }
00795 
00796 extern const char *scaler_menu_opts[][2];
00797 
00798 void RENDER_UpdateScalerMenu(void) {
00799     const std::string scaler = RENDER_GetScaler();
00800 
00801     for (size_t i=0;scaler_menu_opts[i][0] != NULL;i++) {
00802         const std::string name = std::string("scaler_set_") + scaler_menu_opts[i][0];
00803         mainMenu.get_item(name).check(scaler == scaler_menu_opts[i][0]).refresh_item(mainMenu);
00804     }
00805 }
00806 
00807 void RENDER_UpdateFromScalerSetting(void) {
00808     Section_prop * section=static_cast<Section_prop *>(control->GetSection("render"));
00809     Prop_multival* prop = section->Get_multival("scaler");
00810     std::string f = prop->GetSection()->Get_string("force");
00811     std::string scaler = prop->GetSection()->Get_string("type");
00812 
00813 #if C_XBRZ
00814     bool old_xBRZ_enable = sdl_xbrz.enable;
00815     sdl_xbrz.enable = false;
00816 #endif
00817 
00818     render.scale.forced = false;
00819     if(f == "forced") render.scale.forced = true;
00820    
00821     if (scaler == "none") { render.scale.op = scalerOpNormal; render.scale.size = 1; render.scale.hardware=false; }
00822     else if (scaler == "normal2x") { render.scale.op = scalerOpNormal; render.scale.size = 2; render.scale.hardware=false; }
00823     else if (scaler == "normal3x") { render.scale.op = scalerOpNormal; render.scale.size = 3; render.scale.hardware=false; }
00824     else if (scaler == "normal4x") { render.scale.op = scalerOpNormal; render.scale.size = 4; render.scale.hardware=false; }
00825     else if (scaler == "normal5x") { render.scale.op = scalerOpNormal; render.scale.size = 5; render.scale.hardware=false; }
00826 #if RENDER_USE_ADVANCED_SCALERS>2
00827     else if (scaler == "advmame2x") { render.scale.op = scalerOpAdvMame; render.scale.size = 2; render.scale.hardware=false; }
00828     else if (scaler == "advmame3x") { render.scale.op = scalerOpAdvMame; render.scale.size = 3; render.scale.hardware=false; }
00829     else if (scaler == "advinterp2x") { render.scale.op = scalerOpAdvInterp; render.scale.size = 2; render.scale.hardware=false; }
00830     else if (scaler == "advinterp3x") { render.scale.op = scalerOpAdvInterp; render.scale.size = 3; render.scale.hardware=false; }
00831     else if (scaler == "hq2x") { render.scale.op = scalerOpHQ; render.scale.size = 2; render.scale.hardware=false; }
00832     else if (scaler == "hq3x") { render.scale.op = scalerOpHQ; render.scale.size = 3; render.scale.hardware=false; }
00833     else if (scaler == "2xsai") { render.scale.op = scalerOpSaI; render.scale.size = 2; render.scale.hardware=false; }
00834     else if (scaler == "super2xsai") { render.scale.op = scalerOpSuperSaI; render.scale.size = 2; render.scale.hardware=false; }
00835     else if (scaler == "supereagle") { render.scale.op = scalerOpSuperEagle; render.scale.size = 2; render.scale.hardware=false; }
00836 #endif
00837 #if RENDER_USE_ADVANCED_SCALERS>0
00838     else if (scaler == "tv2x") { render.scale.op = scalerOpTV; render.scale.size = 2; render.scale.hardware=false; }
00839     else if (scaler == "tv3x") { render.scale.op = scalerOpTV; render.scale.size = 3; render.scale.hardware=false; }
00840     else if (scaler == "rgb2x"){ render.scale.op = scalerOpRGB; render.scale.size = 2; render.scale.hardware=false; }
00841     else if (scaler == "rgb3x"){ render.scale.op = scalerOpRGB; render.scale.size = 3; render.scale.hardware=false; }
00842     else if (scaler == "scan2x"){ render.scale.op = scalerOpScan; render.scale.size = 2; render.scale.hardware=false; }
00843     else if (scaler == "scan3x"){ render.scale.op = scalerOpScan; render.scale.size = 3; render.scale.hardware=false; }
00844 #endif
00845     else if (scaler == "hardware_none") { render.scale.op = scalerOpNormal; render.scale.size = 1; render.scale.hardware=true; }
00846     else if (scaler == "hardware2x") { render.scale.op = scalerOpNormal; render.scale.size = 4; render.scale.hardware=true; }
00847     else if (scaler == "hardware3x") { render.scale.op = scalerOpNormal; render.scale.size = 6; render.scale.hardware=true; }
00848     else if (scaler == "hardware4x") { render.scale.op = scalerOpNormal; render.scale.size = 8; render.scale.hardware=true; }
00849     else if (scaler == "hardware5x") { render.scale.op = scalerOpNormal; render.scale.size = 10; render.scale.hardware=true; }
00850 #if C_XBRZ
00851     else if (scaler == "xbrz" || scaler == "xbrz_bilinear") { 
00852         render.scale.op = scalerOpNormal; 
00853         render.scale.size = 1; 
00854         render.scale.hardware = false; 
00855         vga.draw.doublescan_set = false; 
00856         sdl_xbrz.enable = true; 
00857         sdl_xbrz.postscale_bilinear = (scaler == "xbrz_bilinear");
00858     }
00859 #endif
00860 
00861 #if C_XBRZ
00862     if (old_xBRZ_enable != sdl_xbrz.enable) RENDER_CallBack(GFX_CallBackReset);
00863 #endif
00864 }
00865 
00866 void RENDER_Init() {
00867     Section_prop * section=static_cast<Section_prop *>(control->GetSection("render"));
00868 
00869     LOG(LOG_MISC,LOG_DEBUG)("Initializing renderer");
00870 
00871     control->GetSection("render")->onpropchange.push_back(&RENDER_OnSectionPropChange);
00872 
00873     vga.draw.doublescan_set=section->Get_bool("doublescan");
00874     vga.draw.char9_set=section->Get_bool("char9");
00875 
00876     mainMenu.get_item("vga_9widetext").check(vga.draw.char9_set).refresh_item(mainMenu);
00877     mainMenu.get_item("doublescan").check(vga.draw.doublescan_set).refresh_item(mainMenu);
00878 
00879     //For restarting the renderer.
00880     static bool running = false;
00881     int aspect = render.aspect;
00882     Bitu scalersize = render.scale.size;
00883     bool scalerforced = render.scale.forced;
00884     scalerOperation_t scaleOp = render.scale.op;
00885 
00886     render.scale.cacheRead = NULL;
00887     render.scale.outWrite = NULL;
00888 
00889     render.pal.first=0;
00890     render.pal.last=255;
00891 
00892     std::string s_aspect = section->Get_string("aspect");
00893     render.aspect = ASPECT_FALSE;
00894     if (s_aspect == "true" || s_aspect == "1") render.aspect = ASPECT_TRUE;
00895 #if C_SURFACE_POSTRENDER_ASPECT
00896     if (s_aspect == "nearest") render.aspect = ASPECT_NEAREST;
00897     if (s_aspect == "bilinear") render.aspect = ASPECT_BILINEAR;
00898 #endif
00899 
00900     render.frameskip.max=(Bitu)section->Get_int("frameskip");
00901 
00902     RENDER_UpdateFrameskipMenu();
00903 
00904     /* BUG FIX: Some people's dosbox.conf files have frameskip=-1 WTF?? */
00905     /* without this fix, nothing displays, EVER */
00906     if ((int)render.frameskip.max < 0) render.frameskip.max = 0;
00907                                 
00908     render.frameskip.count=0;
00909     render.forceUpdate=false;
00910     std::string cline;
00911     std::string scaler;
00912     //Check for commandline paramters and parse them through the configclass so they get checked against allowed values
00913     if (control->cmdline->FindString("-scaler",cline,false)) {
00914         section->HandleInputline(std::string("scaler=") + cline);
00915     } else if (control->cmdline->FindString("-forcescaler",cline,false)) {
00916         section->HandleInputline(std::string("scaler=") + cline + " forced");
00917     }
00918 
00919     RENDER_UpdateFromScalerSetting();
00920 
00921     render.autofit=section->Get_bool("autofit");
00922 
00923     //If something changed that needs a ReInit
00924     // Only ReInit when there is a src.bpp (fixes crashes on startup and directly changing the scaler without a screen specified yet)
00925     if(running && render.src.bpp && ((render.aspect != aspect) || (render.scale.op != scaleOp) || 
00926                   (render.scale.size != scalersize) || (render.scale.forced != scalerforced) ||
00927                    render.scale.forced))
00928         RENDER_CallBack( GFX_CallBackReset );
00929 
00930     if(!running) render.updating=true;
00931     running = true;
00932 
00933     MAPPER_AddHandler(DecreaseFrameSkip,MK_nothing,0,"decfskip","Dec Fskip");
00934     MAPPER_AddHandler(IncreaseFrameSkip,MK_nothing,0,"incfskip","Inc Fskip");
00935 
00936     GFX_SetTitle(-1,(Bits)render.frameskip.max,-1,false);
00937 
00938     RENDER_UpdateScalerMenu();
00939 }
00940