Add output type texturepp for pixel-perfect scaling
The new output type `texturepp' was added, which implements pixel-perfect scaling using SDL's hardware-accelerated texture output. In pixel-pefect mode, each original pixel is displayed as a rectangle m by n pixels, so that m:n yields a reasonably good approximation of the pixel aspect ratio (PAR) of the emulated graphical mode while using as much screen space as possible. The balance between the precision of aspect ratio and the utilisation of screen space is specified as the `parweight' parameter to pp_getscale() and is currently hard-coded in sdlmain.cpp. This implementation emulatates pixel-perfect mode as a special case of nearest-neighbor interpolation when the horisontal and vertical scaling factors are integers.
This commit is contained in:
parent
83f625178a
commit
d1be65b105
10 changed files with 398 additions and 39 deletions
|
@ -641,6 +641,7 @@ src/libs/zmbv/Makefile
|
|||
src/libs/gui_tk/Makefile
|
||||
src/libs/decoders/Makefile
|
||||
src/libs/nuked/Makefile
|
||||
src/libs/ppscale/Makefile
|
||||
src/misc/Makefile
|
||||
src/shell/Makefile
|
||||
src/platform/Makefile
|
||||
|
|
|
@ -30,28 +30,34 @@ typedef enum {
|
|||
|
||||
typedef void (*GFX_CallBack_t)( GFX_CallBackFunctions_t function );
|
||||
|
||||
#define GFX_CAN_8 0x0001
|
||||
#define GFX_CAN_15 0x0002
|
||||
#define GFX_CAN_16 0x0004
|
||||
#define GFX_CAN_32 0x0008
|
||||
#define GFX_CAN_8 0x0001
|
||||
#define GFX_CAN_15 0x0002
|
||||
#define GFX_CAN_16 0x0004
|
||||
#define GFX_CAN_32 0x0008
|
||||
|
||||
#define GFX_LOVE_8 0x0010
|
||||
#define GFX_LOVE_15 0x0020
|
||||
#define GFX_LOVE_16 0x0040
|
||||
#define GFX_LOVE_32 0x0080
|
||||
#define GFX_LOVE_8 0x0010
|
||||
#define GFX_LOVE_15 0x0020
|
||||
#define GFX_LOVE_16 0x0040
|
||||
#define GFX_LOVE_32 0x0080
|
||||
|
||||
#define GFX_RGBONLY 0x0100
|
||||
#define GFX_RGBONLY 0x0100
|
||||
#define GFX_DBL_H 0x0200 /* double-width flag */
|
||||
#define GFX_DBL_W 0x0400 /* double-height flag */
|
||||
|
||||
#define GFX_SCALING 0x1000
|
||||
#define GFX_HARDWARE 0x2000
|
||||
|
||||
#define GFX_CAN_RANDOM 0x4000 //If the interface can also do random access surface
|
||||
#define GFX_CAN_RANDOM 0x4000 //If the interface can also do random access surface
|
||||
#define GFX_UNITY_SCALE 0x8000 /* turn of all scaling in render.cpp */
|
||||
|
||||
void GFX_Events(void);
|
||||
Bitu GFX_GetBestMode(Bitu flags);
|
||||
Bitu GFX_GetRGB(Bit8u red,Bit8u green,Bit8u blue);
|
||||
Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,GFX_CallBack_t cb);
|
||||
void GFX_SetShader(const char* src);
|
||||
Bitu GFX_SetSize(Bitu width, Bitu height, Bitu flags,
|
||||
double scalex, double scaley,
|
||||
GFX_CallBack_t callback,
|
||||
double pixel_aspect);
|
||||
|
||||
void GFX_ResetScreen(void);
|
||||
void GFX_Start(void);
|
||||
|
|
|
@ -17,6 +17,7 @@ dosbox_LDADD = cpu/libcpu.a debug/libdebug.a dos/libdos.a fpu/libfpu.a hardware
|
|||
hardware/serialport/libserial.a \
|
||||
libs/decoders/libdecoders.a \
|
||||
libs/gui_tk/libgui_tk.a \
|
||||
libs/nuked/libnuked.a
|
||||
libs/nuked/libnuked.a \
|
||||
libs/ppscale/libppscale.a
|
||||
|
||||
EXTRA_DIST = winres.rc dosbox.ico
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
|
@ -413,10 +413,19 @@ forcenormal:
|
|||
break;
|
||||
}
|
||||
gfx_flags=GFX_GetBestMode(gfx_flags);
|
||||
if (gfx_flags & GFX_UNITY_SCALE &&
|
||||
simpleBlock != NULL &&
|
||||
strstr( simpleBlock->name, "Normal" ) == simpleBlock->name ) {
|
||||
gfx_scalew = 1.0;
|
||||
gfx_scaleh = 1.0;
|
||||
xscale = 1 ;
|
||||
yscale = 1 ;
|
||||
simpleBlock = &ScaleNormal1x;
|
||||
}
|
||||
if (!gfx_flags) {
|
||||
if (!complexBlock && simpleBlock == &ScaleNormal1x)
|
||||
if (!complexBlock && simpleBlock == &ScaleNormal1x)
|
||||
E_Exit("Failed to create a rendering output");
|
||||
else
|
||||
else
|
||||
goto forcenormal;
|
||||
}
|
||||
width *= xscale;
|
||||
|
@ -433,10 +442,18 @@ forcenormal:
|
|||
}
|
||||
}
|
||||
/* Setup the scaler variables */
|
||||
#if C_OPENGL
|
||||
GFX_SetShader(render.shader_src);
|
||||
#endif
|
||||
gfx_flags=GFX_SetSize(width,height,gfx_flags,gfx_scalew,gfx_scaleh,&RENDER_CallBack);
|
||||
double par; /* the pixel aspect ratio of the source pixel array */
|
||||
par = (double)width / height / 4 * 3; /* MS-DOS screen is always 4:3 */
|
||||
|
||||
if (dblh)
|
||||
gfx_flags |= GFX_DBL_H;
|
||||
if (dblw)
|
||||
gfx_flags |= GFX_DBL_W;
|
||||
|
||||
#if C_OPENGL
|
||||
GFX_SetShader(render.shader_src);
|
||||
#endif
|
||||
gfx_flags=GFX_SetSize(width,height,gfx_flags,gfx_scalew,gfx_scaleh,&RENDER_CallBack,par);
|
||||
if (gfx_flags & GFX_CAN_8)
|
||||
render.scale.outMode = scalerMode8;
|
||||
else if (gfx_flags & GFX_CAN_15)
|
||||
|
@ -445,7 +462,7 @@ forcenormal:
|
|||
render.scale.outMode = scalerMode16;
|
||||
else if (gfx_flags & GFX_CAN_32)
|
||||
render.scale.outMode = scalerMode32;
|
||||
else
|
||||
else
|
||||
E_Exit("Failed to create a rendering output");
|
||||
ScalerLineBlock_t *lineBlock;
|
||||
if (gfx_flags & GFX_HARDWARE) {
|
||||
|
@ -532,7 +549,7 @@ static void RENDER_CallBack( GFX_CallBackFunctions_t function ) {
|
|||
|
||||
void RENDER_SetSize(Bitu width,Bitu height,Bitu bpp,float fps,double ratio,bool dblw,bool dblh) {
|
||||
RENDER_Halt( );
|
||||
if (!width || !height || width > SCALER_MAXWIDTH || height > SCALER_MAXHEIGHT) {
|
||||
if (!width || !height || width > SCALER_MAXWIDTH || height > SCALER_MAXHEIGHT) {
|
||||
return;
|
||||
}
|
||||
if ( ratio > 1 ) {
|
||||
|
@ -641,7 +658,7 @@ static bool RENDER_GetShader(std::string& shader_path) {
|
|||
if (render.shader_src==NULL || s != render.shader_src) {
|
||||
src = strdup(s.c_str());
|
||||
if (src==NULL) LOG_MSG("WARNING: Couldn't copy shader source");
|
||||
} else {
|
||||
} else {
|
||||
src = render.shader_src;
|
||||
render.shader_src = NULL;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <math.h>
|
||||
#ifdef WIN32
|
||||
#include <signal.h>
|
||||
#include <process.h>
|
||||
|
@ -55,6 +56,10 @@
|
|||
#include "control.h"
|
||||
#include "render.h"
|
||||
|
||||
extern "C" {
|
||||
#include "../libs/ppscale/ppscale.h"
|
||||
}
|
||||
|
||||
#define MAPPERFILE "mapper-sdl2-" VERSION ".map"
|
||||
//#define DISABLE_JOYSTICK
|
||||
|
||||
|
@ -203,6 +208,9 @@ enum SCREEN_TYPES {
|
|||
SCREEN_OPENGL
|
||||
};
|
||||
|
||||
/* Modes of simple pixel scaling, currently encoded in the output type: */
|
||||
enum ScalingMode {SmNone, SmNearest, SmPerfect};
|
||||
|
||||
enum PRIORITY_LEVELS {
|
||||
PRIORITY_LEVEL_PAUSE,
|
||||
PRIORITY_LEVEL_LOWEST,
|
||||
|
@ -212,18 +220,19 @@ enum PRIORITY_LEVELS {
|
|||
PRIORITY_LEVEL_HIGHEST
|
||||
};
|
||||
|
||||
|
||||
struct SDL_Block {
|
||||
bool inited;
|
||||
bool active; //If this isn't set don't draw
|
||||
bool updating;
|
||||
bool update_display_contents;
|
||||
bool resizing_window;
|
||||
ScalingMode scaling_mode;
|
||||
struct {
|
||||
Bit32u width;
|
||||
Bit32u height;
|
||||
Bitu flags;
|
||||
double scalex,scaley;
|
||||
Bit32u width;
|
||||
Bit32u height;
|
||||
double pixel_aspect;
|
||||
Bitu flags;
|
||||
double scalex, scaley;
|
||||
GFX_CallBack_t callback;
|
||||
} draw;
|
||||
bool wait_on_error;
|
||||
|
@ -294,6 +303,8 @@ struct SDL_Block {
|
|||
MouseControlType control_choice;
|
||||
bool middle_will_release;
|
||||
} mouse;
|
||||
int ppscale_x, ppscale_y; /* x and y scales for pixel-perfect */
|
||||
bool double_h, double_w; /* double-height and double-width flags */
|
||||
SDL_Rect updateRects[1024];
|
||||
Bitu num_joysticks;
|
||||
#if defined (WIN32)
|
||||
|
@ -441,7 +452,10 @@ static void PauseDOSBox(bool pressed) {
|
|||
}
|
||||
|
||||
/* Reset the screen with current values in the sdl structure */
|
||||
Bitu GFX_GetBestMode(Bitu flags) {
|
||||
Bitu GFX_GetBestMode(Bitu flags)
|
||||
{
|
||||
if (sdl.scaling_mode == SmPerfect)
|
||||
flags |= GFX_UNITY_SCALE;
|
||||
switch (sdl.desktop.want_type) {
|
||||
case SCREEN_SURFACE:
|
||||
check_surface:
|
||||
|
@ -773,17 +787,111 @@ static bool LoadGLShaders(const char *src, GLuint *vertex, GLuint *fragment) {
|
|||
}
|
||||
#endif
|
||||
|
||||
Bitu GFX_SetSize(Bitu width,Bitu height,Bitu flags,double scalex,double scaley,GFX_CallBack_t callback) {
|
||||
/* Get the maximum area available for drawing: */
|
||||
/* TODO: The current implementation will not let the pixel-perfect mode to */
|
||||
/* to apply even the simplest 1:2 and 2:1 corrections at the original */
|
||||
/* window resolution, therefore: consider a special case for PP mode. */
|
||||
static void GetAvailableArea(int &width, int &height)
|
||||
{
|
||||
bool fixed;
|
||||
double par;
|
||||
|
||||
fixed = false;
|
||||
if (sdl.desktop.fullscreen) {
|
||||
if (sdl.desktop.full.fixed) {
|
||||
width = sdl.desktop.full.width;
|
||||
height = sdl.desktop.full.height;
|
||||
fixed = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (sdl.desktop.window.width > 0) {
|
||||
width = sdl.desktop.window.width;
|
||||
height = sdl.desktop.window.height;
|
||||
fixed = true;
|
||||
}
|
||||
}
|
||||
if (!fixed) {
|
||||
par = sdl.draw.pixel_aspect;
|
||||
if (par > 1.0)
|
||||
height = static_cast<int>(round(height * par));
|
||||
if (par < 1.0)
|
||||
width = static_cast<int>(round(width / par));
|
||||
}
|
||||
}
|
||||
|
||||
// ATT: aspect is the final aspect ratio of the image including its pixel dimensions and PAR
|
||||
/*static void GetActualArea( Bit16u av_w, Bit16u av_h, Bit16u *w, Bit16u *h, double aspect )
|
||||
{ double as_x, as_y;
|
||||
|
||||
if( aspect > 1.0 )
|
||||
{ as_y = aspect; as_x = 1.0; } else
|
||||
{ as_x = 1.0/aspect; as_y = 1.0; }
|
||||
|
||||
if( av_h / as_y < av_w / as_x )
|
||||
{ *h = av_h; *w = round( (double)av_h / aspect ); } else
|
||||
{ *w = av_w; *h = round( (double)av_w * aspect ); }
|
||||
}*/
|
||||
|
||||
/* Initialise pixel-perfect mode: */
|
||||
static bool InitPp(Bit16u avw, Bit16u avh)
|
||||
{
|
||||
bool ok;
|
||||
double newpar;
|
||||
/* TODO: consider reading apsect importance from the .ini-file */
|
||||
ok = pp_getscale(
|
||||
sdl.draw.width, sdl.draw.height,
|
||||
sdl.draw.pixel_aspect,
|
||||
avw, avh,
|
||||
1.14, /* relative importance of aspect ratio */
|
||||
&sdl.ppscale_x,
|
||||
&sdl.ppscale_y
|
||||
) == 0;
|
||||
if (ok) {
|
||||
newpar = ( double )sdl.ppscale_y / sdl.ppscale_x;
|
||||
LOG_MSG( "Pixel-perfect scaling:\n"
|
||||
"%ix%i (%#4.3g) --[%ix%i]--> %ix%i (%#4.3g)",
|
||||
sdl.draw.width, sdl.draw.height, sdl.draw.pixel_aspect,
|
||||
sdl.ppscale_x, sdl.ppscale_y,
|
||||
sdl.ppscale_x * sdl.draw.width, sdl.ppscale_y * sdl.draw.height,
|
||||
newpar);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
Bitu GFX_SetSize(Bitu width, Bitu height, Bitu flags,
|
||||
double scalex, double scaley,
|
||||
GFX_CallBack_t callback,
|
||||
double pixel_aspect)
|
||||
{
|
||||
int avw, avh; /* available width and height */
|
||||
Bitu retFlags = 0;
|
||||
if (sdl.updating)
|
||||
GFX_EndUpdate( 0 );
|
||||
|
||||
sdl.draw.pixel_aspect = pixel_aspect;
|
||||
sdl.draw.width=width;
|
||||
sdl.draw.height=height;
|
||||
sdl.draw.callback=callback;
|
||||
sdl.draw.scalex=scalex;
|
||||
sdl.draw.scaley=scaley;
|
||||
|
||||
Bitu retFlags = 0;
|
||||
sdl.double_h = (flags & GFX_DBL_H) > 0;
|
||||
sdl.double_w = (flags & GFX_DBL_W) > 0;
|
||||
|
||||
avw = width;
|
||||
avh = height;
|
||||
GetAvailableArea(avw, avh);
|
||||
LOG_MSG("Input image: %ix%i DW: %i DH: %i PAR: %5.3g",
|
||||
(int)width, (int)height, sdl.double_h, sdl.double_w, pixel_aspect );
|
||||
LOG_MSG("Available area: %ix%i", avw, avh);
|
||||
if (sdl.scaling_mode == SmPerfect) {
|
||||
if (!InitPp(avw, avh)) {
|
||||
LOG_MSG("Failed to initialise pixel-perfect mode, reverting to surface.");
|
||||
goto dosurface;
|
||||
}
|
||||
}
|
||||
|
||||
switch (sdl.desktop.want_type) {
|
||||
case SCREEN_SURFACE:
|
||||
dosurface:
|
||||
|
@ -860,11 +968,35 @@ dosurface:
|
|||
SDL_FillRect(sdl.surface, NULL, SDL_MapRGB(sdl.surface->format, 0, 0, 0));
|
||||
SDL_UpdateWindowSurface(sdl.window);
|
||||
break;
|
||||
case SCREEN_TEXTURE:
|
||||
{
|
||||
if (!GFX_SetupWindowScaled(sdl.desktop.want_type)) {
|
||||
LOG_MSG("SDL:Can't set video mode, falling back to surface");
|
||||
goto dosurface;
|
||||
case SCREEN_TEXTURE: {
|
||||
int imgw, imgh, wndw, wndh; /* image and window width and height */
|
||||
/* TODO: set up all ScalingMode-related settings here. Currently, the */
|
||||
/* interpolation hint is set at the reading of settings. */
|
||||
if (sdl.scaling_mode != SmPerfect) {
|
||||
if (!GFX_SetupWindowScaled(sdl.desktop.want_type)) {
|
||||
LOG_MSG("SDL:Can't set video mode, falling back to surface");
|
||||
goto dosurface;
|
||||
}
|
||||
} else {
|
||||
imgw = sdl.ppscale_x * sdl.draw.width;
|
||||
imgh = sdl.ppscale_y * sdl.draw.height;
|
||||
|
||||
if (sdl.desktop.fullscreen) {
|
||||
wndh = avh; wndw = avw;
|
||||
} else {
|
||||
wndh = imgh; wndw = imgw;
|
||||
}
|
||||
|
||||
sdl.clip.w = imgw;
|
||||
sdl.clip.h = imgh;
|
||||
sdl.clip.x = (wndw - imgw) / 2;
|
||||
sdl.clip.y = (wndh - imgh) / 2;
|
||||
|
||||
|
||||
sdl.window = GFX_SetSDLWindowMode(
|
||||
wndw, wndh, sdl.desktop.fullscreen,
|
||||
SCREEN_TEXTURE);
|
||||
|
||||
}
|
||||
if (strcmp(sdl.rendererDriver, "auto"))
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, sdl.rendererDriver);
|
||||
|
@ -1077,7 +1209,11 @@ dosurface:
|
|||
// No borders
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||||
if (!sdl.opengl.bilinear || ( (sdl.clip.h % height) == 0 && (sdl.clip.w % width) == 0) ) {
|
||||
if(
|
||||
sdl.scaling_mode != SmNone || (
|
||||
sdl.clip.h % height == 0 &&
|
||||
sdl.clip.w % width == 0 )
|
||||
) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
} else {
|
||||
|
@ -1648,18 +1784,26 @@ static void GUI_StartUp(Section * sec) {
|
|||
sdl.desktop.want_type=SCREEN_SURFACE;
|
||||
} else if (output == "texture") {
|
||||
sdl.desktop.want_type=SCREEN_TEXTURE;
|
||||
sdl.scaling_mode = SmNone;
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
|
||||
} else if (output == "texturenb") {
|
||||
sdl.desktop.want_type=SCREEN_TEXTURE;
|
||||
sdl.scaling_mode = SmNearest;
|
||||
// Currently the default, but... oh well
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
|
||||
} else if (output == "texturepp") {
|
||||
sdl.desktop.want_type=SCREEN_TEXTURE;
|
||||
sdl.scaling_mode = SmPerfect;
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
|
||||
#if C_OPENGL
|
||||
} else if (output == "opengl") {
|
||||
sdl.desktop.want_type=SCREEN_OPENGL;
|
||||
sdl.opengl.bilinear=true;
|
||||
sdl.scaling_mode = SmNone;
|
||||
sdl.opengl.bilinear = true;
|
||||
} else if (output == "openglnb") {
|
||||
sdl.desktop.want_type=SCREEN_OPENGL;
|
||||
sdl.opengl.bilinear=false;
|
||||
sdl.scaling_mode = SmNearest;
|
||||
sdl.opengl.bilinear = false;
|
||||
#endif
|
||||
} else {
|
||||
LOG_MSG("SDL: Unsupported output device %s, switching back to surface",output.c_str());
|
||||
|
@ -2212,6 +2356,7 @@ void Config_Add_SDL() {
|
|||
"surface",
|
||||
"texture",
|
||||
"texturenb",
|
||||
"texturepp",
|
||||
#if C_OPENGL
|
||||
"opengl",
|
||||
"openglnb",
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
|
||||
SUBDIRS = zmbv gui_tk decoders nuked
|
||||
SUBDIRS = zmbv gui_tk decoders nuked ppscale
|
||||
|
|
7
src/libs/ppscale/Makefile.am
Normal file
7
src/libs/ppscale/Makefile.am
Normal file
|
@ -0,0 +1,7 @@
|
|||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
|
||||
EXTRA_DIST = ppscale.h
|
||||
|
||||
noinst_LIBRARIES = libppscale.a
|
||||
|
||||
libppscale_a_SOURCES = ppscale.c
|
147
src/libs/ppscale/ppscale.c
Normal file
147
src/libs/ppscale/ppscale.c
Normal file
|
@ -0,0 +1,147 @@
|
|||
/* Copyright 2018-2020 Anton Shepelev (anton.txt@gmail.com) */
|
||||
/* Usage of the works is permitted provided that this instrument is retained */
|
||||
/* with the works, so that any entity that uses the works is notified of this */
|
||||
/* instrument. DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. */
|
||||
|
||||
/* ----------------------- Pixel-perfect scaling unit ----------------------- */
|
||||
/* This unit uses the Horstmann indentation style. */
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
static double min( double a, double b )
|
||||
{ double res;
|
||||
if( a < b ) res = a;
|
||||
else res = b;
|
||||
return res;
|
||||
}
|
||||
|
||||
int pp_getscale /* calculate integer scales for pixel-perfect magnification */
|
||||
( int win, int hin, /* input dimensions */
|
||||
double par, /* input pixel aspect ratio */
|
||||
int wout, int hout, /* output dimensions */
|
||||
double parweight, /* weight of PAR in scale estimation */
|
||||
int *sx, int *sy /* horisontal and vertical scales */
|
||||
) /* returns -1 on error and 0 on success */
|
||||
{ int sxc, syc, sxm, sym; /* current and maximum x and y scales */
|
||||
int exactpar; /* whether to enforce exact aspect ratio */
|
||||
double parrat; /* ratio of current PAR and target PAR */
|
||||
double errpar, errsize, err; /* PAR error, size error, and total error */
|
||||
double errmin; /* minimal error so far */
|
||||
double parnorm; /* target PAR "normalised" to exceed 1.0 */
|
||||
double srat; /* ratio of maximum size to current */
|
||||
|
||||
if /* sanity checks: */
|
||||
( win <= 0 || hin <= 0 ||
|
||||
win > wout || hin > hout ||
|
||||
par <= 0.0
|
||||
)
|
||||
return -1;
|
||||
|
||||
/* enforce aspect ratio priority for 1:n and 1:n pixel proportions: */
|
||||
if( par > 1.0 ) parnorm = par;
|
||||
else parnorm = 1.0 / par;
|
||||
/* whether our PAR is an integer ratio: */
|
||||
exactpar = parnorm - floor( parnorm ) < 0.01;
|
||||
|
||||
sxm = sxc = (int)floor( ( double )wout / win );
|
||||
sym = syc = (int)floor( ( double )hout / hin );
|
||||
|
||||
errmin = -1; /* this value marks the first iteration */
|
||||
while( 1 )
|
||||
{ parrat = (double)syc / sxc / par;
|
||||
|
||||
/* calculate aspect-ratio error: */
|
||||
if( parrat > 1.0 ) errpar = parrat;
|
||||
else errpar = 1.0 / parrat;
|
||||
|
||||
srat = min( (double)sym/syc, (double)sxm/sxc );
|
||||
|
||||
/* calculate size error: */
|
||||
/* if PAR is exact, exclude size error from the fitness function: */
|
||||
if( exactpar ) errsize = 1.0;
|
||||
else errsize = pow( srat, parweight );
|
||||
|
||||
err = errpar * errsize; /* total error */
|
||||
|
||||
/* check for a new optimum: */
|
||||
if( err < errmin || errmin == -1 )
|
||||
{ *sx = sxc;
|
||||
*sy = syc;
|
||||
errmin = err;
|
||||
}
|
||||
|
||||
/* try a smaller magnification: */
|
||||
if( parrat < 1.0 ) sxc--;
|
||||
else syc--;
|
||||
|
||||
/* do not explore magnifications smaller than half the screen: */
|
||||
if( srat >= 2.0 ) break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* RAT: the many scalar arguments are not unifined into one of more stucts */
|
||||
/* because doing so somehow thwarts GCC's O3 optimisations, and the */
|
||||
/* alrorithm works up to 30% slower. */
|
||||
int pp_scale /* magnify an image in a pixel-perfect manner */
|
||||
( char* simg, int spitch, /* source image and pitch */
|
||||
int *rx, int *ry, /* location and */
|
||||
int *rw, int *rh, /* size of the rectangle to process */
|
||||
char* dimg, int dpitch, /* destination image and pitch */
|
||||
int bypp, /* bytes per pixel */
|
||||
int sx, int sy /* horisontal and vertical scales */
|
||||
) /* return -1 on error and 0 on success */
|
||||
{ int x, y, /* coordinate of a source pixel */
|
||||
ix, iy, /* horisontal and vertical subpixel indices */
|
||||
b, /* index of byte in a source pixel */
|
||||
drowsz; /* destination row size in bytes */
|
||||
|
||||
char *srow, *spix, *spos, /* source row, current pixel, and position */
|
||||
*drow, *drow0, *dpos; /* dest. row, base row, and position */
|
||||
|
||||
if /* minimal sanity checks: */
|
||||
( bypp < 1 || sx < 1 || sy < 1 || *rw < 1 || *rh < 1 ||
|
||||
simg == NULL || dimg == NULL ||
|
||||
spitch < *rw * bypp || dpitch < *rw * sx * bypp
|
||||
)
|
||||
return -1;
|
||||
|
||||
drowsz = bypp * *rw * sx;
|
||||
srow = simg + *ry * spitch + *rx * bypp;
|
||||
drow = dimg + sy * *ry * dpitch + sx * *rx * bypp;
|
||||
for( y = 0; y < *rh; y += 1 )
|
||||
{ drow0 = drow;
|
||||
dpos = drow;
|
||||
spos = srow;
|
||||
spix = srow;
|
||||
|
||||
for( x = 0; x < *rw; x += 1 )
|
||||
{ for( ix = 0; ix < sx; ix += 1 )
|
||||
{ for( b = 0; b < bypp; b += 1 )
|
||||
{ *dpos = *spos;
|
||||
dpos += 1;
|
||||
spos += 1;
|
||||
}
|
||||
spos = spix;
|
||||
}
|
||||
spix += bypp;
|
||||
}
|
||||
|
||||
iy = 1;
|
||||
while( 1 )
|
||||
{ drow = drow + dpitch; /* next destination row */
|
||||
if( iy == sy ) break; /* terminate if source row scaled */
|
||||
memcpy( drow, drow0, drowsz ); /* duplicate base row below */
|
||||
iy += 1;
|
||||
}
|
||||
|
||||
srow = srow + spitch; /* next source row */
|
||||
}
|
||||
|
||||
/* return the output rectagle: */
|
||||
*rx *= sx; * ry *= sy; *rw *= sx; *rh *= sy;
|
||||
|
||||
return 0;
|
||||
}
|
33
src/libs/ppscale/ppscale.h
Normal file
33
src/libs/ppscale/ppscale.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* Copyright 2018-2020 Anton Shepelev (anton.txt@gmail.com) */
|
||||
/* Usage of the works is permitted provided that this instrument is retained */
|
||||
/* with the works, so that any entity that uses the works is notified of this */
|
||||
/* instrument. DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. */
|
||||
|
||||
/* ----------------------- Pixel-perfect scaling unit ----------------------- */
|
||||
/* This unit uses the Horstmann indentation style. */
|
||||
|
||||
#ifndef PPSCALE_H
|
||||
#define PPSCALE_H
|
||||
|
||||
int pp_getscale /* calculate integer scales for pixel-perfect magnification */
|
||||
( int win, int hin, /* input dimensions */
|
||||
double par, /* input pixel aspect ratio */
|
||||
int wout, int hout, /* output dimensions */
|
||||
double parweight, /* weight of PAR in scale estimation */
|
||||
int *sx, int *sy /* horisontal and vertical scales */
|
||||
); /* returns -1 on error and 0 on success */
|
||||
|
||||
|
||||
/* RAT: the many scalar arguments are not unifined into one of more stucts */
|
||||
/* because doing so somehow thwarts GCC's O3 optimisations, and the */
|
||||
/* alrorithm works up to 30% slower. */
|
||||
int pp_scale /* magnify an image in a pixel-perfect manner */
|
||||
( char* simg, int spitch, /* source image and pitch */
|
||||
int *rx, int *ry, /* location and */
|
||||
int *rw, int *rh, /* size of the rectangle to process */
|
||||
char* dimg, int dpitch, /* destination image and pitch */
|
||||
int bypp, /* bytes per pixel */
|
||||
int sx, int sy /* horisontal and vertical scales */
|
||||
); /* return -1 on error and 0 on success */
|
||||
|
||||
#endif /* PPSCALE_H */
|
|
@ -252,6 +252,7 @@
|
|||
<ClCompile Include="..\src\libs\decoders\vorbis.c" />
|
||||
<ClCompile Include="..\src\libs\decoders\wav.c" />
|
||||
<ClCompile Include="..\src\libs\nuked\nukedopl.cpp" />
|
||||
<ClCompile Include="..\src\libs\ppscale\ppscale.c" />
|
||||
<ClCompile Include="..\src\misc\cross.cpp" />
|
||||
<ClCompile Include="..\src\misc\messages.cpp" />
|
||||
<ClCompile Include="..\src\misc\programs.cpp" />
|
||||
|
@ -375,6 +376,7 @@
|
|||
<ClInclude Include="..\src\libs\decoders\xxh3.h" />
|
||||
<ClInclude Include="..\src\libs\decoders\xxhash.h" />
|
||||
<ClInclude Include="..\src\libs\nuked\nukedopl.h" />
|
||||
<ClInclude Include="..\src\libs\ppscale\ppscale.h" />
|
||||
<ClInclude Include="..\src\platform\visualc\config.h" />
|
||||
<ClInclude Include="..\src\platform\visualc\unistd.h" />
|
||||
</ItemGroup>
|
||||
|
|
Loading…
Add table
Reference in a new issue