/* Emacs style mode select -*- C++ -*- *----------------------------------------------------------------------------- * * * PrBoom: a Doom port merged with LxDoom and LSDLDoom * based on BOOM, a modified and improved DOOM engine * Copyright (C) 1999 by * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman * Copyright (C) 1999-2000 by * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze * Copyright 2005, 2006 by * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko * * 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 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. * * DESCRIPTION: * Gamma correction LUT stuff. * Color range translation support * Functions to draw patches (by post) directly to screen. * Functions to blit a block to the screen. * *----------------------------------------------------------------------------- */ #include "doomdef.h" #include "r_main.h" #include "r_draw.h" #include "m_bbox.h" #include "w_wad.h" /* needed for color translation lump lookup */ #include "v_video.h" #include "i_video.h" #include "r_filter.h" #include "lprintf.h" // Each screen is [SCREENWIDTH*SCREENHEIGHT]; screeninfo_t screens[NUM_SCREENS]; /* jff 4/24/98 initialize this at runtime */ const byte *colrngs[CR_LIMIT]; int usegamma; /* * V_InitColorTranslation * * Loads the color translation tables from predefined lumps at game start * No return * * Used for translating text colors from the red palette range * to other colors. The first nine entries can be used to dynamically * switch the output of text color thru the HUlib_drawText routine * by embedding ESCn in the text to obtain color n. Symbols for n are * provided in v_video.h. * * cphipps - constness of crdef_t stuff fixed */ typedef struct { const char *name; const byte **map; } crdef_t; // killough 5/2/98: table-driven approach static const crdef_t crdefs[] = { {"CRBRICK", &colrngs[CR_BRICK ]}, {"CRTAN", &colrngs[CR_TAN ]}, {"CRGRAY", &colrngs[CR_GRAY ]}, {"CRGREEN", &colrngs[CR_GREEN ]}, {"CRBROWN", &colrngs[CR_BROWN ]}, {"CRGOLD", &colrngs[CR_GOLD ]}, {"CRRED", &colrngs[CR_RED ]}, {"CRBLUE", &colrngs[CR_BLUE ]}, {"CRORANGE", &colrngs[CR_ORANGE]}, {"CRYELLOW", &colrngs[CR_YELLOW]}, {"CRBLUE2", &colrngs[CR_BLUE2]}, {NULL} }; // killough 5/2/98: tiny engine driven by table above void V_InitColorTranslation(void) { register const crdef_t *p; for (p=crdefs; p->name; p++) *p->map = W_CacheLumpName(p->name); } // // V_CopyRect // // Copies a source rectangle in a screen buffer to a destination // rectangle in another screen buffer. Source origin in srcx,srcy, // destination origin in destx,desty, common size in width and height. // Source buffer specfified by srcscrn, destination buffer by destscrn. // // Marks the destination rectangle on the screen dirty. // // No return. // void V_CopyRect8(int srcx, int srcy, int srcscrn, int width, int height, int destx, int desty, int destscrn, enum patch_translation_e flags) { byte *src; byte *dest; if (flags & VPT_STRETCH) { srcx=srcx*SCREENWIDTH/320; srcy=srcy*SCREENHEIGHT/200; width=width*SCREENWIDTH/320; height=height*SCREENHEIGHT/200; destx=destx*SCREENWIDTH/320; desty=desty*SCREENHEIGHT/200; } #ifdef RANGECHECK if (srcx<0 ||srcx+width >SCREENWIDTH || srcy<0 || srcy+height>SCREENHEIGHT ||destx<0||destx+width >SCREENWIDTH || desty<0 || desty+height>SCREENHEIGHT) I_Error ("V_CopyRect: Bad arguments"); #endif src = screens[srcscrn].data+screens[srcscrn].pitch*srcy+srcx; dest = screens[destscrn].data+screens[destscrn].pitch*desty+destx; for ( ; height>0 ; height--) { memcpy (dest, src, width); src += screens[srcscrn].pitch; dest += screens[destscrn].pitch; } } /* * V_DrawBackground tiles a 64x64 patch over the entire screen, providing the * background for the Help and Setup screens, and plot text betwen levels. * cphipps - used to have M_DrawBackground, but that was used the framebuffer * directly, so this is my code from the equivalent function in f_finale.c */ void V_DrawBackground8(const char* flatname, int scrn) { /* erase the entire screen to a tiled background */ const byte *src; byte *dest; int x,y; int width,height; int lump; // killough 4/17/98: src = W_CacheLumpNum(lump = firstflat + R_FlatNumForName(flatname)); /* V_DrawBlock(0, 0, scrn, 64, 64, src, 0); */ width = height = 64; dest = screens[scrn].data; while (height--) { memcpy (dest, src, width); src += width; dest += screens[scrn].pitch; } /* end V_DrawBlock */ for (y=0 ; ytopoffset; x -= patch->leftoffset; // CPhipps - auto-no-stretch if not high-res if (flags & VPT_STRETCH) if ((SCREENWIDTH==320) && (SCREENHEIGHT==200)) flags &= ~VPT_STRETCH; // CPhipps - null translation pointer => no translation if (!trans) flags &= ~VPT_TRANS; if (!(flags & VPT_STRETCH)) { int col; byte *desttop = screens[scrn].data+y*screens[scrn].pitch+x; unsigned int w = patch->width; if (y<0 || y+patch->height > ((flags & VPT_STRETCH) ? 200 : SCREENHEIGHT)) { // killough 1/19/98: improved error message: lprintf(LO_WARN, "V_DrawMemPatch8: Patch (%d,%d)-(%d,%d) exceeds LFB in vertical direction (horizontal is clipped)\n" "Bad V_DrawMemPatch8 (flags=%u)", x, y, x+patch->width, y+patch->height, flags); return; } w--; // CPhipps - note: w = width-1 now, speeds up flipping for (col=0 ; (unsigned int)col<=w ; desttop++, col++, x++) { int i; const rcolumn_t *column = &patch->columns[(flags & VPT_FLIP) ? w-col : col]; if (x < 0) continue; if (x >= SCREENWIDTH) break; // step through the posts in a column for (i=0; inumPosts; i++) { const rpost_t *post = &column->posts[i]; // killough 2/21/98: Unrolled and performance-tuned register const byte *source = column->pixels + post->topdelta; register byte *dest = desttop + post->topdelta*screens[scrn].pitch; register int count = post->length; if (!(flags & VPT_TRANS)) { if ((count-=4)>=0) do { register byte s0,s1; s0 = source[0]; s1 = source[1]; dest[0] = s0; dest[screens[scrn].pitch] = s1; dest += screens[scrn].pitch*2; s0 = source[2]; s1 = source[3]; source += 4; dest[0] = s0; dest[screens[scrn].pitch] = s1; dest += screens[scrn].pitch*2; } while ((count-=4)>=0); if (count+=4) do { *dest = *source++; dest += screens[scrn].pitch; } while (--count); } else { // CPhipps - merged translation code here if ((count-=4)>=0) do { register byte s0,s1; s0 = source[0]; s1 = source[1]; s0 = trans[s0]; s1 = trans[s1]; dest[0] = s0; dest[screens[scrn].pitch] = s1; dest += screens[scrn].pitch*2; s0 = source[2]; s1 = source[3]; s0 = trans[s0]; s1 = trans[s1]; source += 4; dest[0] = s0; dest[screens[scrn].pitch] = s1; dest += screens[scrn].pitch*2; } while ((count-=4)>=0); if (count+=4) do { *dest = trans[*source++]; dest += screens[scrn].pitch; } while (--count); } } } } else { // CPhipps - move stretched patch drawing code here // - reformat initialisers, move variables into inner blocks int col; int w = (patch->width << 16) - 1; // CPhipps - -1 for faster flipping int left, right, top, bottom; int DX = (SCREENWIDTH<<16) / 320; int DXI = (320<<16) / SCREENWIDTH; int DY = (SCREENHEIGHT<<16) / 200; int DYI = (200<<16) / SCREENHEIGHT; R_DrawColumn_f colfunc; draw_column_vars_t dcvars; draw_vars_t olddrawvars = drawvars; R_SetDefaultDrawColumnVars(&dcvars); drawvars.topleft = screens[scrn].data; drawvars.pitch = screens[scrn].pitch; if (flags & VPT_TRANS) { colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_TRANSLATED, drawvars.filterpatch, RDRAW_FILTER_NONE); dcvars.translation = trans; } else { colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_STANDARD, drawvars.filterpatch, RDRAW_FILTER_NONE); } left = ( x * DX ) >> FRACBITS; top = ( y * DY ) >> FRACBITS; right = ( (x + patch->width) * DX ) >> FRACBITS; bottom = ( (y + patch->height) * DY ) >> FRACBITS; dcvars.texheight = patch->height; dcvars.iscale = DYI; dcvars.drawingmasked = max(patch->width, patch->height) > 8; dcvars.edgetype = drawvars.patch_edges; if (drawvars.filterpatch == RDRAW_FILTER_LINEAR) { // bias the texture u coordinate if (patch->isNotTileable) col = -(FRACUNIT>>1); else col = (patch->width<>1); } else { col = 0; } for (dcvars.x=left; dcvars.x>16): (col>>16); const rcolumn_t *column = R_GetPatchColumn(patch, colindex); const rcolumn_t *prevcolumn = R_GetPatchColumn(patch, colindex-1); const rcolumn_t *nextcolumn = R_GetPatchColumn(patch, colindex+1); // ignore this column if it's to the left of our clampRect if (dcvars.x < 0) continue; if (dcvars.x >= SCREENWIDTH) break; dcvars.texu = ((flags & VPT_FLIP) ? ((patch->width<width<numPosts; i++) { const rpost_t *post = &column->posts[i]; int yoffset = 0; dcvars.yl = (((y + post->topdelta) * DY)>>FRACBITS); dcvars.yh = (((y + post->topdelta + post->length) * DY - (FRACUNIT>>1))>>FRACBITS); dcvars.edgeslope = post->slope; if ((dcvars.yh < 0) || (dcvars.yh < top)) continue; if ((dcvars.yl >= SCREENHEIGHT) || (dcvars.yl >= bottom)) continue; if (dcvars.yh >= bottom) { dcvars.yh = bottom-1; dcvars.edgeslope &= ~RDRAW_EDGESLOPE_BOT_MASK; } if (dcvars.yh >= SCREENHEIGHT) { dcvars.yh = SCREENHEIGHT-1; dcvars.edgeslope &= ~RDRAW_EDGESLOPE_BOT_MASK; } if (dcvars.yl < 0) { yoffset = 0-dcvars.yl; dcvars.yl = 0; dcvars.edgeslope &= ~RDRAW_EDGESLOPE_TOP_MASK; } if (dcvars.yl < top) { yoffset = top-dcvars.yl; dcvars.yl = top; dcvars.edgeslope &= ~RDRAW_EDGESLOPE_TOP_MASK; } dcvars.source = column->pixels + post->topdelta + yoffset; dcvars.prevsource = prevcolumn ? prevcolumn->pixels + post->topdelta + yoffset: dcvars.source; dcvars.nextsource = nextcolumn ? nextcolumn->pixels + post->topdelta + yoffset: dcvars.source; dcvars.texturemid = -((dcvars.yl-centery)*dcvars.iscale); colfunc(&dcvars); } } R_ResetColumnBuffer(); drawvars = olddrawvars; } } // CPhipps - some simple, useful wrappers for that function, for drawing patches from wads // CPhipps - GNU C only suppresses generating a copy of a function if it is // static inline; other compilers have different behaviour. // This inline is _only_ for the function below void V_DrawNumPatch8(int x, int y, int scrn, int lump, int cm, enum patch_translation_e flags) { V_DrawMemPatch8(x, y, scrn, R_CachePatchNum(lump), cm, flags); R_UnlockPatchNum(lump); } // // V_SetPalette // // CPhipps - New function to set the palette to palette number pal. // Handles loading of PLAYPAL and calls I_SetPalette void V_SetPalette(int pal) { if (V_GetMode() == VID_MODEGL) { #ifdef GL_DOOM gld_SetPalette(pal); #endif } else { I_SetPalette(pal); } } // // V_FillRect // // CPhipps - New function to fill a rectangle with a given colour void V_FillRect8(int scrn, int x, int y, int width, int height, byte colour) { byte* dest = screens[scrn].data + x + y*screens[scrn].pitch; while (height--) { memset(dest, colour, width); dest += screens[scrn].pitch; } } static void WRAP_V_DrawLine(fline_t* fl, int color); static void V_PlotPixel8(int scrn, int x, int y, byte color); #ifdef GL_DOOM void WRAP_gld_FillRect(int scrn, int x, int y, int width, int height, byte colour) { gld_FillBlock(x,y,width,height,colour); } void WRAP_gld_CopyRect(int srcx, int srcy, int srcscrn, int width, int height, int destx, int desty, int destscrn, enum patch_translation_e flags) { } void WRAP_gld_DrawBackground(const char *flatname, int n) { gld_DrawBackground(flatname); } void WRAP_gld_DrawNumPatch(int x, int y, int scrn, int lump, int cm, enum patch_translation_e flags) { gld_DrawNumPatch(x,y,lump,cm,flags); } void WRAP_gld_DrawBlock(int x, int y, int scrn, int width, int height, const byte *src, enum patch_translation_e flags) { } void V_PlotPixelGL(int scrn, int x, int y, byte color) { gld_DrawLine(x-1, y, x+1, y, color); gld_DrawLine(x, y-1, x, y+1, color); } void WRAP_gld_DrawLine(fline_t* fl, int color) { gld_DrawLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, color); } #endif static void NULL_FillRect(int scrn, int x, int y, int width, int height, byte colour) {} static void NULL_CopyRect(int srcx, int srcy, int srcscrn, int width, int height, int destx, int desty, int destscrn, enum patch_translation_e flags) {} static void NULL_DrawBackground(const char *flatname, int n) {} static void NULL_DrawNumPatch(int x, int y, int scrn, int lump, int cm, enum patch_translation_e flags) {} static void NULL_DrawBlock(int x, int y, int scrn, int width, int height, const byte *src, enum patch_translation_e flags) {} static void NULL_PlotPixel(int scrn, int x, int y, byte color) {} static void NULL_DrawLine(fline_t* fl, int color) {} video_mode_t default_videomode; static video_mode_t current_videomode = VID_MODE8; V_CopyRect_f V_CopyRect = NULL_CopyRect; V_FillRect_f V_FillRect = NULL_FillRect; V_DrawNumPatch_f V_DrawNumPatch = NULL_DrawNumPatch; V_DrawBackground_f V_DrawBackground = NULL_DrawBackground; V_PlotPixel_f V_PlotPixel = NULL_PlotPixel; V_DrawLine_f V_DrawLine = NULL_DrawLine; // // V_InitMode // void V_InitMode(video_mode_t mode) { #ifndef GL_DOOM if (mode == VID_MODEGL) mode = VID_MODE8; #endif switch (mode) { case VID_MODE8: //case VID_MODE16: //case VID_MODE32: lprintf(LO_INFO, "V_InitMode: using 8 bit video mode\n"); V_CopyRect = V_CopyRect8; V_FillRect = V_FillRect8; V_DrawNumPatch = V_DrawNumPatch8; V_DrawBackground = V_DrawBackground8; V_PlotPixel = V_PlotPixel8; V_DrawLine = WRAP_V_DrawLine; current_videomode = VID_MODE8; break; #ifdef GL_DOOM case VID_MODEGL: lprintf(LO_INFO, "V_InitMode: using OpenGL video mode\n"); V_CopyRect = WRAP_gld_CopyRect; V_FillRect = WRAP_gld_FillRect; V_DrawNumPatch = WRAP_gld_DrawNumPatch; V_DrawBackground = WRAP_gld_DrawBackground; V_PlotPixel = V_PlotPixelGL; V_DrawLine = WRAP_gld_DrawLine; current_videomode = VID_MODEGL; break; #endif } R_FilterInit(); } // // V_GetMode // video_mode_t V_GetMode(void) { return current_videomode; } // // V_GetModePixelDepth // static int V_GetModePixelDepth(video_mode_t mode) { switch (mode) { case VID_MODE8: return 1; //case VID_MODE16: return 2; //case VID_MODE32: return 4; default: return 0; } } // // V_GetNumPixelBits // int V_GetNumPixelBits(void) { return V_GetModePixelDepth(current_videomode) * 8; } // // V_GetPixelDepth // int V_GetPixelDepth(void) { return V_GetModePixelDepth(current_videomode); } // // V_AllocScreen // void V_AllocScreen(screeninfo_t *scrn) { if (!scrn->not_on_heap) if ((scrn->pitch * scrn->height) > 0) scrn->data = malloc(scrn->pitch*scrn->height); } // // V_AllocScreens // void V_AllocScreens(void) { int i; for (i=0; inot_on_heap) { free(scrn->data); scrn->data = NULL; } } // // V_FreeScreens // void V_FreeScreens(void) { int i; for (i=0; ia.x < 0 || fl->a.x >= SCREENWIDTH || fl->a.y < 0 || fl->a.y >= SCREENHEIGHT || fl->b.x < 0 || fl->b.x >= SCREENWIDTH || fl->b.y < 0 || fl->b.y >= SCREENHEIGHT ) { //jff 8/3/98 use logical output routine lprintf(LO_DEBUG, "fuck %d \r", fuck++); return; } #endif #define PUTDOT(xx,yy,cc) V_PlotPixel(0,xx,yy,(byte)cc) dx = fl->b.x - fl->a.x; ax = 2 * (dx<0 ? -dx : dx); sx = dx<0 ? -1 : 1; dy = fl->b.y - fl->a.y; ay = 2 * (dy<0 ? -dy : dy); sy = dy<0 ? -1 : 1; x = fl->a.x; y = fl->a.y; if (ax > ay) { d = ay - ax/2; while (1) { PUTDOT(x,y,color); if (x == fl->b.x) return; if (d>=0) { y += sy; d -= ax; } x += sx; d += ay; } } else { d = ax - ay/2; while (1) { PUTDOT(x, y, color); if (y == fl->b.y) return; if (d >= 0) { x += sx; d -= ay; } y += sy; d += ax; } } }