2009-05-02 23:03:37 +02:00
|
|
|
/*
|
2009-05-02 23:35:44 +02:00
|
|
|
* Copyright (C) 2002-2003 The DOSBox Team
|
2009-05-02 23:03:37 +02:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Library General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
2009-05-02 23:35:44 +02:00
|
|
|
#include <sys/types.h>
|
2009-05-02 23:20:05 +02:00
|
|
|
#include <dirent.h>
|
|
|
|
|
2009-05-02 23:03:37 +02:00
|
|
|
#include "dosbox.h"
|
|
|
|
#include "video.h"
|
|
|
|
#include "render.h"
|
2009-05-02 23:20:05 +02:00
|
|
|
#include "setup.h"
|
|
|
|
#include "keyboard.h"
|
|
|
|
#include "cross.h"
|
2009-05-02 23:35:44 +02:00
|
|
|
#include "support.h"
|
2009-05-02 23:03:37 +02:00
|
|
|
|
2009-05-02 23:27:47 +02:00
|
|
|
#define MAX_RES 2048
|
2009-05-02 23:03:37 +02:00
|
|
|
|
|
|
|
struct PalData {
|
|
|
|
struct {
|
|
|
|
Bit8u red;
|
|
|
|
Bit8u green;
|
|
|
|
Bit8u blue;
|
|
|
|
Bit8u unused;
|
|
|
|
} rgb[256];
|
|
|
|
Bitu first;
|
|
|
|
Bitu last;
|
2009-05-02 23:27:47 +02:00
|
|
|
union {
|
|
|
|
Bit32u bpp32[256];
|
|
|
|
Bit16u bpp16[256];
|
|
|
|
} lookup;
|
2009-05-02 23:03:37 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
struct {
|
|
|
|
Bitu width;
|
|
|
|
Bitu height;
|
|
|
|
Bitu bpp;
|
|
|
|
Bitu pitch;
|
2009-05-02 23:27:47 +02:00
|
|
|
Bitu flags;
|
2009-05-02 23:03:37 +02:00
|
|
|
float ratio;
|
2009-05-02 23:35:44 +02:00
|
|
|
RENDER_Draw_Handler draw_handler;
|
2009-05-02 23:03:37 +02:00
|
|
|
} src;
|
2009-05-02 23:27:47 +02:00
|
|
|
struct {
|
|
|
|
Bitu width;
|
|
|
|
Bitu height;
|
|
|
|
Bitu pitch;
|
|
|
|
Bitu bpp; /* The type of BPP the operation requires for input */
|
|
|
|
RENDER_Operation type;
|
2009-05-02 23:35:44 +02:00
|
|
|
RENDER_Operation want_type;
|
|
|
|
RENDER_Part_Handler part_handler;
|
2009-05-02 23:27:47 +02:00
|
|
|
void * dest;
|
|
|
|
void * buffer;
|
|
|
|
void * pixels;
|
|
|
|
} op;
|
|
|
|
struct {
|
|
|
|
Bitu count;
|
|
|
|
Bitu max;
|
|
|
|
} frameskip;
|
2009-05-02 23:03:37 +02:00
|
|
|
Bitu flags;
|
|
|
|
PalData pal;
|
2009-05-02 23:35:44 +02:00
|
|
|
#if (C_SSHOT)
|
|
|
|
struct {
|
|
|
|
RENDER_Operation type;
|
|
|
|
Bitu pitch;
|
|
|
|
const char * dir;
|
|
|
|
} shot;
|
|
|
|
#endif
|
2009-05-02 23:27:47 +02:00
|
|
|
bool keep_small;
|
2009-05-02 23:20:05 +02:00
|
|
|
bool screenshot;
|
2009-05-02 23:27:47 +02:00
|
|
|
bool active;
|
2009-05-02 23:03:37 +02:00
|
|
|
} render;
|
|
|
|
|
2009-05-02 23:27:47 +02:00
|
|
|
/* Forward declerations */
|
|
|
|
static void RENDER_ResetPal(void);
|
|
|
|
|
|
|
|
/* Include the different rendering routines */
|
2009-05-02 23:35:44 +02:00
|
|
|
#include "render_normal.h"
|
|
|
|
#include "render_scale2x.h"
|
2009-05-02 23:27:47 +02:00
|
|
|
|
2009-05-02 23:35:44 +02:00
|
|
|
|
|
|
|
#if (C_SSHOT)
|
|
|
|
#include <png.h>
|
2009-05-02 23:20:05 +02:00
|
|
|
|
|
|
|
/* Take a screenshot of the data that should be rendered */
|
|
|
|
static void TakeScreenShot(Bit8u * bitmap) {
|
|
|
|
Bitu last=0;char file_name[CROSS_LEN];
|
|
|
|
DIR * dir;struct dirent * dir_ent;
|
|
|
|
png_structp png_ptr;
|
|
|
|
png_infop info_ptr;
|
|
|
|
png_bytep * row_pointers;
|
|
|
|
png_color palette[256];
|
|
|
|
Bitu i;
|
|
|
|
|
|
|
|
/* Find a filename to open */
|
2009-05-02 23:35:44 +02:00
|
|
|
dir=opendir(render.shot.dir);
|
2009-05-02 23:20:05 +02:00
|
|
|
if (!dir) {
|
2009-05-02 23:35:44 +02:00
|
|
|
LOG_MSG("Can't open snapshot directory %s",render.shot.dir);
|
2009-05-02 23:20:05 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
while (dir_ent=readdir(dir)) {
|
|
|
|
char tempname[CROSS_LEN];
|
|
|
|
strcpy(tempname,dir_ent->d_name);
|
|
|
|
char * test=strstr(tempname,".png");
|
|
|
|
if (!test) continue;
|
|
|
|
*test=0;
|
|
|
|
if (strlen(tempname)<5) continue;
|
|
|
|
if (strncmp(tempname,"snap",4)!=0) continue;
|
|
|
|
Bitu num=atoi(&tempname[4]);
|
|
|
|
if (num>=last) last=num+1;
|
|
|
|
}
|
|
|
|
closedir(dir);
|
2009-05-02 23:35:44 +02:00
|
|
|
sprintf(file_name,"%s%csnap%04d.png",render.shot.dir,CROSS_FILESPLIT,last);
|
|
|
|
/* Open the actual file */
|
2009-05-02 23:20:05 +02:00
|
|
|
FILE * fp=fopen(file_name,"wb");
|
|
|
|
if (!fp) {
|
2009-05-02 23:35:44 +02:00
|
|
|
LOG_MSG("Can't open file %s for snapshot",file_name);
|
2009-05-02 23:20:05 +02:00
|
|
|
return;
|
|
|
|
}
|
2009-05-02 23:35:44 +02:00
|
|
|
|
|
|
|
/* First try to alloacte the png structures */
|
|
|
|
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,NULL, NULL);
|
|
|
|
if (!png_ptr) return;
|
|
|
|
info_ptr = png_create_info_struct(png_ptr);
|
|
|
|
if (!info_ptr) {
|
|
|
|
png_destroy_write_struct(&png_ptr,(png_infopp)NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finalize the initing of png library */
|
2009-05-02 23:20:05 +02:00
|
|
|
png_init_io(png_ptr, fp);
|
|
|
|
png_set_compression_level(png_ptr,Z_BEST_COMPRESSION);
|
|
|
|
|
|
|
|
/* set other zlib parameters */
|
|
|
|
png_set_compression_mem_level(png_ptr, 8);
|
|
|
|
png_set_compression_strategy(png_ptr,Z_DEFAULT_STRATEGY);
|
|
|
|
png_set_compression_window_bits(png_ptr, 15);
|
|
|
|
png_set_compression_method(png_ptr, 8);
|
|
|
|
png_set_compression_buffer_size(png_ptr, 8192);
|
|
|
|
|
|
|
|
if (render.src.bpp==8) {
|
|
|
|
png_set_IHDR(png_ptr, info_ptr, render.src.width, render.src.height,
|
|
|
|
8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
|
|
|
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
|
|
|
for (i=0;i<256;i++) {
|
|
|
|
palette[i].red=render.pal.rgb[i].red;
|
|
|
|
palette[i].green=render.pal.rgb[i].green;
|
|
|
|
palette[i].blue=render.pal.rgb[i].blue;
|
|
|
|
}
|
|
|
|
png_set_PLTE(png_ptr, info_ptr, palette,256);
|
|
|
|
} else {
|
|
|
|
png_set_IHDR(png_ptr, info_ptr, render.src.width, render.src.height,
|
|
|
|
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
|
|
|
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
|
|
|
}
|
|
|
|
/*Allocate an array of scanline pointers*/
|
|
|
|
row_pointers=(png_bytep*)malloc(render.src.height*sizeof(png_bytep));
|
|
|
|
for (i=0;i<render.src.height;i++) {
|
2009-05-02 23:35:44 +02:00
|
|
|
row_pointers[i]=(bitmap+i*render.src.width);
|
2009-05-02 23:20:05 +02:00
|
|
|
}
|
|
|
|
/*tell the png library what to encode.*/
|
|
|
|
png_set_rows(png_ptr, info_ptr, row_pointers);
|
|
|
|
|
|
|
|
/*Write image to file*/
|
|
|
|
png_write_png(png_ptr, info_ptr, 0, NULL);
|
|
|
|
|
|
|
|
/*close file*/
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
/*Destroy PNG structs*/
|
|
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
|
|
|
|
|
|
/*clean up dynamically allocated RAM.*/
|
|
|
|
free(row_pointers);
|
2009-05-02 23:35:44 +02:00
|
|
|
LOG_MSG("Took snapshot in file %s",file_name);
|
2009-05-02 23:20:05 +02:00
|
|
|
}
|
|
|
|
|
2009-05-02 23:35:44 +02:00
|
|
|
static void EnableScreenShot(void) {
|
|
|
|
render.shot.type=render.op.type;
|
|
|
|
render.op.type=OP_Shot;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
2009-05-02 23:20:05 +02:00
|
|
|
|
|
|
|
|
2009-05-02 23:03:37 +02:00
|
|
|
/* This could go kinda bad with multiple threads */
|
|
|
|
static void Check_Palette(void) {
|
|
|
|
if (render.pal.first>render.pal.last) return;
|
2009-05-02 23:27:47 +02:00
|
|
|
switch (render.op.bpp) {
|
|
|
|
case 8:
|
|
|
|
GFX_SetPalette(render.pal.first,render.pal.last-render.pal.first+1,(GFX_PalEntry *)&render.pal.rgb[render.pal.first]);
|
|
|
|
break;
|
|
|
|
case 16:
|
|
|
|
for (;render.pal.first<=render.pal.last;render.pal.first++) {
|
|
|
|
render.pal.lookup.bpp16[render.pal.first]=GFX_GetRGB(
|
|
|
|
render.pal.rgb[render.pal.first].red,
|
|
|
|
render.pal.rgb[render.pal.first].green,
|
|
|
|
render.pal.rgb[render.pal.first].blue);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 32:
|
|
|
|
for (;render.pal.first<=render.pal.last;render.pal.first++) {
|
|
|
|
render.pal.lookup.bpp32[render.pal.first]=
|
|
|
|
GFX_GetRGB(
|
|
|
|
render.pal.rgb[render.pal.first].red,
|
|
|
|
render.pal.rgb[render.pal.first].green,
|
|
|
|
render.pal.rgb[render.pal.first].blue);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
/* Setup pal index to startup values */
|
2009-05-02 23:03:37 +02:00
|
|
|
render.pal.first=256;
|
|
|
|
render.pal.last=0;
|
|
|
|
}
|
|
|
|
|
2009-05-02 23:27:47 +02:00
|
|
|
static void RENDER_ResetPal(void) {
|
|
|
|
render.pal.first=0;
|
|
|
|
render.pal.last=255;
|
2009-05-02 23:03:37 +02:00
|
|
|
}
|
|
|
|
|
2009-05-02 23:27:47 +02:00
|
|
|
void RENDER_SetPal(Bit8u entry,Bit8u red,Bit8u green,Bit8u blue) {
|
|
|
|
render.pal.rgb[entry].red=red;
|
|
|
|
render.pal.rgb[entry].green=green;
|
|
|
|
render.pal.rgb[entry].blue=blue;
|
|
|
|
if (render.pal.first>entry) render.pal.first=entry;
|
|
|
|
if (render.pal.last<entry) render.pal.last=entry;
|
2009-05-02 23:03:37 +02:00
|
|
|
}
|
|
|
|
|
2009-05-02 23:35:44 +02:00
|
|
|
void RENDER_DoUpdate(void) {
|
2009-05-02 23:27:47 +02:00
|
|
|
if (render.frameskip.count<render.frameskip.max) {
|
|
|
|
render.frameskip.count++;
|
2009-05-02 23:35:44 +02:00
|
|
|
return;
|
2009-05-02 23:03:37 +02:00
|
|
|
}
|
2009-05-02 23:27:47 +02:00
|
|
|
render.frameskip.count=0;
|
|
|
|
if (render.src.bpp==8) Check_Palette();
|
2009-05-02 23:35:44 +02:00
|
|
|
GFX_DoUpdate();
|
2009-05-02 23:03:37 +02:00
|
|
|
}
|
|
|
|
|
2009-05-02 23:35:44 +02:00
|
|
|
static void RENDER_DrawScreen(void * data) {
|
2009-05-02 23:27:47 +02:00
|
|
|
switch (render.op.type) {
|
2009-05-02 23:35:44 +02:00
|
|
|
doagain:
|
2009-05-02 23:27:47 +02:00
|
|
|
case OP_None:
|
2009-05-02 23:35:44 +02:00
|
|
|
render.op.dest=render.op.pixels=data;
|
|
|
|
render.src.draw_handler(render.op.part_handler);
|
|
|
|
break;
|
|
|
|
case OP_Scale2x:
|
|
|
|
render.op.dest=render.op.pixels=data;
|
|
|
|
render.src.draw_handler(render.op.part_handler);
|
2009-05-02 23:27:47 +02:00
|
|
|
break;
|
2009-05-02 23:35:44 +02:00
|
|
|
#if (C_SSHOT)
|
|
|
|
case OP_Shot:
|
|
|
|
render.shot.pitch=render.op.pitch;
|
|
|
|
render.op.pitch=render.src.width;
|
|
|
|
render.op.pixels=malloc(render.src.width*render.src.height);
|
|
|
|
render.src.draw_handler(Normal_DN_8);
|
|
|
|
TakeScreenShot((Bit8u *)render.op.pixels);
|
|
|
|
free(render.op.pixels);
|
|
|
|
render.op.pitch=render.shot.pitch;
|
|
|
|
render.op.type=render.shot.type;
|
|
|
|
goto doagain;
|
|
|
|
#endif
|
2009-05-02 23:27:47 +02:00
|
|
|
}
|
2009-05-02 23:03:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void RENDER_Resize(Bitu * width,Bitu * height) {
|
|
|
|
/* Calculate the new size the window should be */
|
|
|
|
if (!*width && !*height) {
|
|
|
|
/* Special command to reset any resizing for fullscreen */
|
|
|
|
*width=render.src.width;
|
|
|
|
*height=render.src.height;
|
|
|
|
} else {
|
|
|
|
if ((*width/render.src.ratio)<*height) *height=(Bitu)(*width/render.src.ratio);
|
|
|
|
else *width=(Bitu)(*height*render.src.ratio);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-02 23:35:44 +02:00
|
|
|
void RENDER_SetSize(Bitu width,Bitu height,Bitu bpp,Bitu pitch,float ratio,Bitu flags,RENDER_Draw_Handler draw_handler) {
|
2009-05-02 23:27:47 +02:00
|
|
|
if ((!width) || (!height) || (!pitch)) {
|
|
|
|
render.active=false;return;
|
|
|
|
}
|
2009-05-02 23:35:44 +02:00
|
|
|
GFX_Stop();
|
2009-05-02 23:03:37 +02:00
|
|
|
render.src.width=width;
|
|
|
|
render.src.height=height;
|
|
|
|
render.src.bpp=bpp;
|
|
|
|
render.src.pitch=pitch;
|
2009-05-02 23:27:47 +02:00
|
|
|
render.src.ratio=ratio;
|
|
|
|
render.src.flags=flags;
|
2009-05-02 23:35:44 +02:00
|
|
|
render.src.draw_handler=draw_handler;
|
2009-05-02 23:03:37 +02:00
|
|
|
|
2009-05-02 23:35:44 +02:00
|
|
|
GFX_ModeCallBack mode_callback=0;
|
2009-05-02 23:27:47 +02:00
|
|
|
switch (render.op.want_type) {
|
2009-05-02 23:03:37 +02:00
|
|
|
|
2009-05-02 23:27:47 +02:00
|
|
|
case OP_None:
|
|
|
|
normalop:
|
|
|
|
switch (render.src.flags) {
|
2009-05-02 23:35:44 +02:00
|
|
|
case DoubleNone:
|
|
|
|
flags=0;
|
|
|
|
break;
|
|
|
|
case DoubleWidth:
|
|
|
|
width*=2;
|
|
|
|
flags=GFX_SHADOW;
|
|
|
|
break;
|
|
|
|
case DoubleHeight:
|
|
|
|
height*=2;
|
|
|
|
flags=0;
|
|
|
|
break;
|
2009-05-02 23:27:47 +02:00
|
|
|
case DoubleBoth:
|
|
|
|
if (render.keep_small) {
|
|
|
|
render.src.flags=0;
|
2009-05-02 23:35:44 +02:00
|
|
|
flags=0;
|
2009-05-02 23:27:47 +02:00
|
|
|
} else {
|
|
|
|
width*=2;height*=2;
|
2009-05-02 23:35:44 +02:00
|
|
|
flags=GFX_SHADOW;
|
2009-05-02 23:27:47 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2009-05-02 23:35:44 +02:00
|
|
|
mode_callback=Render_Normal_CallBack;
|
|
|
|
break;
|
|
|
|
case OP_Scale2x:
|
|
|
|
switch (render.src.flags) {
|
|
|
|
case DoubleBoth:
|
|
|
|
if (render.keep_small) goto normalop;
|
|
|
|
mode_callback=Render_Scale2x_CallBack;
|
|
|
|
width*=2;height*=2;
|
|
|
|
#if defined (SCALE2X_NORMAL)
|
|
|
|
flags=GFX_SHADOW;
|
|
|
|
#elif defined (SCALE2X_MMX)
|
|
|
|
flags=GFX_FIXED_BPP;
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto normalop;
|
|
|
|
}
|
|
|
|
|
2009-05-02 23:27:47 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto normalop;
|
|
|
|
|
|
|
|
}
|
2009-05-02 23:35:44 +02:00
|
|
|
GFX_SetSize(width,height,bpp,flags,mode_callback,RENDER_DrawScreen);
|
|
|
|
GFX_Start();
|
2009-05-02 23:20:05 +02:00
|
|
|
}
|
2009-05-02 23:27:47 +02:00
|
|
|
|
|
|
|
static void IncreaseFrameSkip(void) {
|
|
|
|
if (render.frameskip.max<10) render.frameskip.max++;
|
|
|
|
LOG_MSG("Frame Skip at %d",render.frameskip.max);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void DecreaseFrameSkip(void) {
|
|
|
|
if (render.frameskip.max>0) render.frameskip.max--;
|
|
|
|
LOG_MSG("Frame Skip at %d",render.frameskip.max);
|
|
|
|
}
|
|
|
|
|
2009-05-02 23:20:05 +02:00
|
|
|
void RENDER_Init(Section * sec) {
|
2009-05-02 23:35:44 +02:00
|
|
|
MSG_Add("RENDER_CONFIGFILE_HELP","Available scalers: scale2x, none\n");
|
2009-05-02 23:20:05 +02:00
|
|
|
Section_prop * section=static_cast<Section_prop *>(sec);
|
2009-05-02 23:27:47 +02:00
|
|
|
|
2009-05-02 23:03:37 +02:00
|
|
|
render.pal.first=256;
|
|
|
|
render.pal.last=0;
|
2009-05-02 23:27:47 +02:00
|
|
|
render.keep_small=section->Get_bool("keepsmall");
|
|
|
|
render.frameskip.max=section->Get_int("frameskip");
|
|
|
|
render.frameskip.count=0;
|
2009-05-02 23:35:44 +02:00
|
|
|
#if (C_SSHOT)
|
|
|
|
render.shot.dir=section->Get_string("snapshots");
|
|
|
|
KEYBOARD_AddEvent(KBD_f5,KBD_MOD_CTRL,EnableScreenShot);
|
|
|
|
#endif
|
|
|
|
const char * scaler;std::string cline;
|
|
|
|
if (control->cmdline->FindString("-scaler",cline,false)) {
|
|
|
|
scaler=cline.c_str();
|
|
|
|
} else {
|
|
|
|
scaler=section->Get_string("scaler");
|
|
|
|
}
|
|
|
|
if (!strcasecmp(scaler,"none")) render.op.want_type=OP_None;
|
|
|
|
else if (!strcasecmp(scaler,"scale2x")) render.op.want_type=OP_Scale2x;
|
|
|
|
else {
|
|
|
|
render.op.want_type=OP_None;
|
|
|
|
LOG_MSG("Illegal scaler type %s,falling back to none.",scaler);
|
|
|
|
}
|
|
|
|
KEYBOARD_AddEvent(KBD_f7,KBD_MOD_CTRL,DecreaseFrameSkip);
|
|
|
|
KEYBOARD_AddEvent(KBD_f8,KBD_MOD_CTRL,IncreaseFrameSkip);
|
2009-05-02 23:03:37 +02:00
|
|
|
}
|
|
|
|
|