DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/output/output_tools_xbrz.cpp
00001 #include <stdlib.h>
00002 #include <assert.h>
00003 #include <math.h>
00004 
00005 #include "dosbox.h"
00006 #include "sdlmain.h"
00007 
00008 using namespace std;
00009 
00010 #if C_XBRZ
00011 
00012 struct SDL_xBRZ sdl_xbrz;
00013 
00014 void xBRZ_Initialize()
00015 {
00016     Section_prop* section = static_cast<Section_prop *>(control->GetSection("render"));
00017 
00018     LOG(LOG_MISC, LOG_DEBUG)("Early init (renderer): xBRZ options");
00019 
00020     // set some defaults
00021     sdl_xbrz.task_granularity = 16;
00022     sdl_xbrz.max_scale_factor = xbrz::SCALE_FACTOR_MAX;
00023 
00024     // read options related to xBRZ here
00025     Prop_multival* prop = section->Get_multival("scaler");
00026     std::string scaler = prop->GetSection()->Get_string("type");
00027     sdl_xbrz.enable = ((scaler == "xbrz") || (scaler == "xbrz_bilinear"));
00028     sdl_xbrz.postscale_bilinear = (scaler == "xbrz_bilinear");
00029     xBRZ_Change_Options(section);
00030 }
00031 
00032 void xBRZ_Change_Options(Section_prop* section)
00033 {
00034     sdl_xbrz.task_granularity = section->Get_int("xbrz slice");
00035     sdl_xbrz.fixed_scale_factor = section->Get_int("xbrz fixed scale factor");
00036     sdl_xbrz.max_scale_factor = section->Get_int("xbrz max scale factor");
00037     if ((sdl_xbrz.max_scale_factor < 2) || (sdl_xbrz.max_scale_factor > xbrz::SCALE_FACTOR_MAX))
00038         sdl_xbrz.max_scale_factor = xbrz::SCALE_FACTOR_MAX;
00039     if ((sdl_xbrz.fixed_scale_factor < 2) || (sdl_xbrz.fixed_scale_factor > xbrz::SCALE_FACTOR_MAX))
00040         sdl_xbrz.fixed_scale_factor = 0;
00041 }
00042 
00043 // returns true if scaling possible/enabled, false otherwise
00044 bool xBRZ_SetScaleParameters(int srcWidth, int srcHeight, int dstWidth, int dstHeight)
00045 {
00046     sdl_xbrz.scale_factor = (sdl_xbrz.fixed_scale_factor == 0) ?
00047         static_cast<int>(std::sqrt((double)dstWidth * dstHeight / (srcWidth * srcHeight)) + 0.5) :
00048         sdl_xbrz.fixed_scale_factor;
00049 
00050     // enable minimal scaling if upscale is still possible but requires post-downscale
00051     // having aspect ratio correction on always implies enabled scaler because it gives better quality than DOSBox own method
00052     if (sdl_xbrz.scale_factor == 1 && (render.aspect || dstWidth > srcWidth || dstHeight > srcHeight))
00053         sdl_xbrz.scale_factor = 2;
00054 
00055     if (sdl_xbrz.scale_factor >= 2)
00056     {
00057         // ok to scale, now clamp scale factor if corresponding max option is set
00058         sdl_xbrz.scale_factor = min(sdl_xbrz.scale_factor, sdl_xbrz.max_scale_factor);
00059         sdl_xbrz.scale_on = true;
00060     }
00061     else
00062     {
00063         // scaling impossible
00064         sdl_xbrz.scale_on = false;
00065     }
00066 
00067     return sdl_xbrz.scale_on;
00068 }
00069 
00070 void xBRZ_Render(const uint32_t* renderBuf, uint32_t* xbrzBuf, const Bit16u *changedLines, const int srcWidth, const int srcHeight, int scalingFactor)
00071 {
00072 #ifdef XBRZ_PPL
00073     if (changedLines) // perf: in worst case similar to full input scaling
00074     {
00075         concurrency::task_group tg; // perf: task_group is slightly faster than pure parallel_for
00076 
00077         int yLast = 0;
00078         Bitu y = 0, index = 0;
00079         while (y < sdl.draw.height)
00080         {
00081             if (!(index & 1))
00082                 y += changedLines[index];
00083             else
00084             {
00085                 const int sliceFirst = (int)y;
00086                 const int sliceLast = (int)y + changedLines[index];
00087                 y += changedLines[index];
00088 
00089                 int yFirst = max(yLast, sliceFirst - 2); // we need to update two adjacent lines as well since they are analyzed by xBRZ!
00090                 yLast = min(srcHeight, sliceLast + 2);   // (and make sure to not overlap with last slice!)
00091                 for (int i = yFirst; i < yLast; i += sdl_xbrz.task_granularity)
00092                 {
00093                     tg.run([=] { 
00094                         const int iLast = min(i + sdl_xbrz.task_granularity, yLast);
00095                         xbrz::scale(scalingFactor, renderBuf, xbrzBuf, srcWidth, srcHeight, xbrz::ColorFormat::RGB, xbrz::ScalerCfg(), i, iLast);
00096                     });
00097                 }
00098             }
00099             index++;
00100         }
00101         tg.wait();
00102     }
00103     else // process complete input image
00104     {
00105         concurrency::task_group tg;
00106         for (int i = 0; i < srcHeight; i += sdl_xbrz.task_granularity)
00107         {
00108             tg.run([=] { 
00109                 const int iLast = min(i + sdl_xbrz.task_granularity, srcHeight);
00110                 xbrz::scale(scalingFactor, renderBuf, xbrzBuf, srcWidth, srcHeight, xbrz::ColorFormat::RGB, xbrz::ScalerCfg(), i, iLast);
00111             });
00112         }
00113         tg.wait();
00114     }
00115 #else
00116     /* non-PPL for non-Windows.
00117     * combine the code above, cleanly, if possible. */
00118     if (changedLines)
00119     {
00120         int yLast = 0;
00121         Bitu y = 0, index = 0;
00122         while (y < sdl.draw.height)
00123         {
00124             if (!(index & 1))
00125                 y += changedLines[index];
00126             else
00127             {
00128                 const int sliceFirst = int(y);
00129                 const int sliceLast = int(y + changedLines[index]);
00130                 y += changedLines[index];
00131 
00132                 int yFirst = max(yLast, sliceFirst - 2); // we need to update two adjacent lines as well since they are analyzed by xBRZ!
00133                 yLast = min(srcHeight, sliceLast + 2);  // (and make sure to not overlap with last slice!)
00134                 xbrz::scale((size_t)scalingFactor, renderBuf, xbrzBuf, srcWidth, srcHeight, xbrz::ColorFormat::RGB, xbrz::ScalerCfg(), yFirst, yLast);
00135             }
00136             index++;
00137         }
00138     }
00139     else // process complete input image
00140     {
00141         xbrz::scale((size_t)scalingFactor, renderBuf, xbrzBuf, srcWidth, srcHeight, xbrz::ColorFormat::RGB, xbrz::ScalerCfg(), 0, srcHeight);
00142     }
00143 #endif /*XBRZ_PPL*/
00144 }
00145 
00146 #endif /*C_XBRZ*/
00147 
00148 #if C_XBRZ || C_SURFACE_POSTRENDER_ASPECT
00149 
00150 void xBRZ_PostScale(const uint32_t* src, const int srcWidth, const int srcHeight, const int srcPitch, 
00151                     uint32_t* tgt, const int tgtWidth, const int tgtHeight, const int tgtPitch, 
00152                     const bool bilinear, const int task_granularity)
00153 {
00154     (void)task_granularity;
00155 
00156 # if defined(XBRZ_PPL)
00157     if (bilinear) {
00158         concurrency::task_group tg;
00159         for (int i = 0; i < tgtHeight; i += task_granularity)
00160             tg.run([=] {
00161                 const int iLast = min(i + task_granularity, tgtHeight);
00162                 xbrz::bilinearScale(&src[0], srcWidth, srcHeight, srcPitch, &tgt[0], tgtWidth, tgtHeight, tgtPitch, i, iLast, [](uint32_t pix) { return pix; });  
00163             });
00164         tg.wait();
00165     }
00166     else
00167     {
00168         concurrency::task_group tg;
00169         for (int i = 0; i < tgtHeight; i += task_granularity)
00170             tg.run([=] {
00171                 const int iLast = min(i + task_granularity, tgtHeight);
00172                 // perf: going over target is by factor 4 faster than going over source for similar image sizes
00173                 xbrz::nearestNeighborScale(&src[0], srcWidth, srcHeight, srcPitch, &tgt[0], tgtWidth, tgtHeight, tgtPitch, i, iLast, [](uint32_t pix) { return pix; });
00174             });
00175         tg.wait();
00176     }
00177 #else
00178     if (bilinear)
00179         xbrz::bilinearScale(&src[0], srcWidth, srcHeight, srcPitch, &tgt[0], tgtWidth, tgtHeight, tgtPitch, 0, tgtHeight, [](uint32_t pix) { return pix; });
00180     else
00181         xbrz::nearestNeighborScale(&src[0], srcWidth, srcHeight, srcPitch, &tgt[0], tgtWidth, tgtHeight, tgtPitch, 0, tgtHeight, [](uint32_t pix) { return pix; });
00182 #endif
00183 }
00184 
00185 #endif /*C_XBRZ || C_SURFACE_POSTRENDER_ASPECT*/