dosbox-wii/src/gui/render.cpp
2009-05-02 21:53:27 +00:00

513 lines
13 KiB
C++

/*
* Copyright (C) 2002-2004 The DOSBox Team
*
* 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.
*/
/* $Id: render.cpp,v 1.25 2004/01/31 22:38:27 harekiet Exp $ */
#include <sys/types.h>
#include <dirent.h>
#include <assert.h>
#include <math.h>
#include "dosbox.h"
#include "video.h"
#include "render.h"
#include "setup.h"
#include "keyboard.h"
#include "cross.h"
#include "support.h"
#define RENDER_MAXWIDTH 1280
#define RENDER_MAXHEIGHT 1024
struct PalData {
struct {
Bit8u red;
Bit8u green;
Bit8u blue;
Bit8u unused;
} rgb[256];
volatile Bitu first;
volatile Bitu last;
union {
Bit32u bpp32[256];
Bit16u bpp16[256];
Bit32u yuv[256];
} lookup;
};
static struct {
struct {
Bitu width;
Bitu height;
Bitu bpp;
Bitu pitch;
Bitu scalew;
Bitu scaleh;
double ratio;
} src;
struct {
Bitu width;
Bitu height;
Bitu pitch;
Bitu line;
Bitu bpp;
RENDER_Operation type;
RENDER_Operation want_type;
RENDER_Line_Handler line_handler;
Bit8u * draw;
Bit8u * buffer;
Bit8u * pixels;
} op;
struct {
Bitu count;
Bitu max;
} frameskip;
PalData pal;
struct {
Bit8u hlines[RENDER_MAXHEIGHT];
} normal;
#if (C_SSHOT)
struct {
RENDER_Operation type;
Bitu bpp,width,height,line;
const char * dir;
Bit8u * buffer,* draw;
bool usesrc;
} shot;
#endif
bool active;
bool aspect;
bool updating;
} render;
Bit8u render_line_cache[4][RENDER_MAXWIDTH*4]; //Bit32u pixels
RENDER_Line_Handler RENDER_DrawLine;
Bit8u * RENDER_TempLine;
/* Forward declerations */
static void RENDER_ResetPal(void);
/* Include the different rendering routines */
#include "render_templates.h"
#include "render_normal.h"
#include "render_scale2x.h"
#if (C_SSHOT)
#include <png.h>
static void RENDER_ShotDraw(Bit8u * src) {
if (render.shot.usesrc) {
memcpy(render.shot.draw,src,render.shot.line);
render.shot.draw+=render.shot.line;
} else render.op.line_handler(src);
}
/* 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 */
dir=opendir(render.shot.dir);
if (!dir) {
LOG_MSG("Can't open snapshot directory %s",render.shot.dir);
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);
sprintf(file_name,"%s%csnap%04d.png",render.shot.dir,CROSS_FILESPLIT,last);
/* Open the actual file */
FILE * fp=fopen(file_name,"wb");
if (!fp) {
LOG_MSG("Can't open file %s for snapshot",file_name);
return;
}
/* 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 */
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.shot.bpp==8) {
png_set_IHDR(png_ptr, info_ptr, render.shot.width, render.shot.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.shot.width, render.shot.height,
render.shot.bpp, 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.shot.height*sizeof(png_bytep));
for (i=0;i<render.shot.height;i++) {
row_pointers[i]=(bitmap+i*render.shot.line);
}
/*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);
LOG_MSG("Took snapshot in file %s",file_name);
}
static void EnableScreenShot(void) {
render.shot.type=render.op.type;
render.op.type=OP_Shot;
}
#endif
static void Check_Palette(void) {
if (render.pal.first>render.pal.last) return;
Bitu i;
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 (i=render.pal.first;i<=render.pal.last;i++) {
Bit8u r=render.pal.rgb[i].red;
Bit8u g=render.pal.rgb[i].green;
Bit8u b=render.pal.rgb[i].blue;
render.pal.lookup.bpp16[i]=GFX_GetRGB(r,g,b);
}
break;
case 24:
case 32:
for (i=render.pal.first;i<=render.pal.last;i++) {
Bit8u r=render.pal.rgb[i].red;
Bit8u g=render.pal.rgb[i].green;
Bit8u b=render.pal.rgb[i].blue;
render.pal.lookup.bpp32[i]=GFX_GetRGB(r,g,b);
}
break;
}
/* Setup pal index to startup values */
render.pal.first=256;
render.pal.last=0;
}
static void RENDER_ResetPal(void) {
render.pal.first=0;
render.pal.last=255;
}
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;
}
static void RENDER_EmptyLineHandler(Bit8u * src) {
}
bool RENDER_StartUpdate(void) {
if (render.updating) return false;
if (render.frameskip.count<render.frameskip.max) {
render.frameskip.count++;
return false;
}
render.frameskip.count=0;
if (render.src.bpp==8) Check_Palette();
render.op.line=0;
RENDER_TempLine=render_line_cache[0];
switch (render.op.type) {
case OP_None:
case OP_Normal2x:
case OP_AdvMame2x:
am2x.cmd_index=am2x.cmd_data;
if (!GFX_StartUpdate(render.op.pixels,render.op.pitch)) return false;
RENDER_DrawLine=render.op.line_handler;;
break;
#if (C_SSHOT)
case OP_Shot:
if (render.shot.buffer) free(render.shot.buffer);
if (render.shot.usesrc) {
render.shot.width=render.src.width;
render.shot.height=render.src.height;
render.shot.bpp=render.src.bpp;
} else {
render.shot.width=render.op.width;
render.shot.height=render.op.height;
render.shot.bpp=render.op.bpp;
}
switch (render.shot.bpp) {
case 8:render.shot.line=render.shot.width;break;
case 15:
case 16:render.shot.line=render.shot.width*2;break;
case 24:render.shot.line=render.shot.width*3;break;
case 32:render.shot.line=render.shot.width*4;break;
}
render.shot.buffer=(Bit8u*)malloc(render.shot.line*render.shot.height);
render.op.pixels=render.shot.buffer;
render.op.pitch=render.shot.line;
render.shot.draw=render.shot.buffer;
RENDER_DrawLine=RENDER_ShotDraw;
break;
#endif
}
render.updating=true;
return true;
}
void RENDER_EndUpdate(void) {
if (!render.updating) return;
#if (C_SSHOT)
switch (render.op.type) {
case OP_None:
case OP_Normal2x:
case OP_AdvMame2x:
break;
case OP_Shot:
TakeScreenShot(render.shot.buffer);
free(render.shot.buffer);
render.shot.buffer=0;
render.op.type=render.shot.type;
break;
}
#endif /* If Things are added to please check the define */
GFX_EndUpdate();
RENDER_DrawLine=RENDER_EmptyLineHandler;
render.updating=false;
}
void RENDER_ReInit(void) {
if (render.updating) RENDER_EndUpdate();
Bitu width=render.src.width;
Bitu height=render.src.height;
Bitu scalew=render.src.scalew;
Bitu scaleh=render.src.scaleh;
double gfx_scalew=1.0;
double gfx_scaleh=1.0;
if (render.src.ratio>1.0) gfx_scaleh*=render.src.ratio;
else gfx_scalew*=(1/render.src.ratio);
Bitu gfx_flags;
Bitu bpp=GFX_GetBestMode(render.src.bpp,gfx_flags);
Bitu index;
switch (bpp) {
case 8: index=0;break;
case 16:index=1;break;
case 24:index=2;break;
case 32:index=3;break;
}
/* Initial scaler testing */
switch (render.op.want_type) {
case OP_Normal2x:
case OP_None:
render.op.type=render.op.want_type;
normalop:
if (gfx_flags & GFX_HASSCALING) {
gfx_scalew*=scalew;
gfx_scaleh*=scaleh;
render.op.line_handler=Normal_8[index];
for (Bitu i=0;i<render.src.height;i++) {
render.normal.hlines[i]=0;
}
} else {
gfx_scaleh*=scaleh;
if (scalew==2) {
if (scaleh>1 && (render.op.type==OP_None)) {
render.op.line_handler=Normal_8[index];
scalew>>=1;gfx_scaleh/=2;
} else {
render.op.line_handler=Normal_2x_8[index];
}
} else render.op.line_handler=Normal_8[index];
width*=scalew;
double lines=0.0;
height=0;
for (Bitu i=0;i<render.src.height;i++) {
lines+=gfx_scaleh;
Bitu temp_lines=(Bitu)(lines);
lines-=temp_lines;
render.normal.hlines[i]=(temp_lines>0) ? temp_lines-1 : 0;
height+=temp_lines;
}
}
break;
case OP_AdvMame2x:
if (scalew!=2){
render.op.type=OP_Normal2x;
goto normalop;
}
if (gfx_flags & GFX_HASSCALING) {
height=scaleh*height;
} else {
height=(Bitu)(gfx_scaleh*scaleh*height);
}
width<<=1;
{
Bits i;
double src_add=(double)height/(double)render.src.height;
double src_index=0;
double src_lines=0;
Bitu src_done=0;
Bitu height=0;
am2x.cmd_index=am2x.cmd_data;
am2x.buf_pos=0;am2x.buf_used=0;
for (i=0;i<=(Bits)render.src.height;i++) {
src_lines+=src_add;
Bitu lines=(Bitu)src_lines;
src_lines-=lines;
switch (lines) {
case 0:
break;
case 1:
AdvMame2x_AddLine(i,i,i);
break;
case 2:
AdvMame2x_AddLine(i-1,i,i+1);
AdvMame2x_AddLine(i+1,i,i-1);
break;
default:
AdvMame2x_AddLine(i-1,i,i+1);
for (lines-=2;lines>0;lines--) AdvMame2x_AddLine(i,i,i);
AdvMame2x_AddLine(i+1,i,i-1);
break;
}
AdvMame2x_CheckLines(i);
}
render.op.line_handler=AdvMame2x_8_Table[index];
}
break;
}
render.op.bpp=bpp;
render.op.width=width;
render.op.height=height;
GFX_SetSize(width,height,bpp,gfx_scalew,gfx_scaleh,&RENDER_ReInit);
RENDER_ResetPal();
render.active=true;
}
void RENDER_SetSize(Bitu width,Bitu height,Bitu bpp,Bitu pitch,double ratio,Bitu scalew,Bitu scaleh) {
if ((!width) || (!height) || (!pitch)) {
render.active=false;
return;
}
render.src.width=width;
render.src.height=height;
render.src.bpp=bpp;
render.src.pitch=pitch;
render.src.ratio=render.aspect ? ratio : 1.0;
render.src.scalew=scalew;
render.src.scaleh=scaleh;
RENDER_ReInit();
}
extern void GFX_SetTitle(Bits cycles, Bits frameskip);
static void IncreaseFrameSkip(void) {
if (render.frameskip.max<10) render.frameskip.max++;
LOG_MSG("Frame Skip at %d",render.frameskip.max);
GFX_SetTitle(-1,render.frameskip.max);
}
static void DecreaseFrameSkip(void) {
if (render.frameskip.max>0) render.frameskip.max--;
LOG_MSG("Frame Skip at %d",render.frameskip.max);
GFX_SetTitle(-1,render.frameskip.max);
}
void RENDER_Init(Section * sec) {
Section_prop * section=static_cast<Section_prop *>(sec);
render.pal.first=256;
render.pal.last=0;
render.aspect=section->Get_bool("aspect");
render.frameskip.max=section->Get_int("frameskip");
render.frameskip.count=0;
render.updating=true;
#if (C_SSHOT)
render.shot.dir=section->Get_string("snapdir");
render.shot.usesrc=true;
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,"normal2x")) render.op.want_type=OP_Normal2x;
else if (!strcasecmp(scaler,"advmame2x")) render.op.want_type=OP_AdvMame2x;
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);
GFX_SetTitle(-1,render.frameskip.max);
}