v_video.c 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035
  1. /* Emacs style mode select -*- C++ -*-
  2. *-----------------------------------------------------------------------------
  3. *
  4. *
  5. * PrBoom: a Doom port merged with LxDoom and LSDLDoom
  6. * based on BOOM, a modified and improved DOOM engine
  7. * Copyright (C) 1999 by
  8. * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
  9. * Copyright (C) 1999-2000 by
  10. * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
  11. * Copyright 2005, 2006 by
  12. * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
  13. *
  14. * This program is free software; you can redistribute it and/or
  15. * modify it under the terms of the GNU General Public License
  16. * as published by the Free Software Foundation; either version 2
  17. * of the License, or (at your option) any later version.
  18. *
  19. * This program is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. * GNU General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU General Public License
  25. * along with this program; if not, write to the Free Software
  26. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  27. * 02111-1307, USA.
  28. *
  29. * DESCRIPTION:
  30. * Gamma correction LUT stuff.
  31. * Color range translation support
  32. * Functions to draw patches (by post) directly to screen.
  33. * Functions to blit a block to the screen.
  34. *
  35. *-----------------------------------------------------------------------------
  36. */
  37. #include "doomdef.h"
  38. #include "r_main.h"
  39. #include "r_draw.h"
  40. #include "m_bbox.h"
  41. #include "w_wad.h" /* needed for color translation lump lookup */
  42. #include "v_video.h"
  43. #include "i_video.h"
  44. #include "r_filter.h"
  45. #include "lprintf.h"
  46. // Each screen is [SCREENWIDTH*SCREENHEIGHT];
  47. screeninfo_t screens[NUM_SCREENS];
  48. /* jff 4/24/98 initialize this at runtime */
  49. const byte *colrngs[CR_LIMIT];
  50. int usegamma;
  51. /*
  52. * V_InitColorTranslation
  53. *
  54. * Loads the color translation tables from predefined lumps at game start
  55. * No return
  56. *
  57. * Used for translating text colors from the red palette range
  58. * to other colors. The first nine entries can be used to dynamically
  59. * switch the output of text color thru the HUlib_drawText routine
  60. * by embedding ESCn in the text to obtain color n. Symbols for n are
  61. * provided in v_video.h.
  62. *
  63. * cphipps - constness of crdef_t stuff fixed
  64. */
  65. typedef struct {
  66. const char *name;
  67. const byte **map;
  68. } crdef_t;
  69. // killough 5/2/98: table-driven approach
  70. static const crdef_t crdefs[] = {
  71. {"CRBRICK", &colrngs[CR_BRICK ]},
  72. {"CRTAN", &colrngs[CR_TAN ]},
  73. {"CRGRAY", &colrngs[CR_GRAY ]},
  74. {"CRGREEN", &colrngs[CR_GREEN ]},
  75. {"CRBROWN", &colrngs[CR_BROWN ]},
  76. {"CRGOLD", &colrngs[CR_GOLD ]},
  77. {"CRRED", &colrngs[CR_RED ]},
  78. {"CRBLUE", &colrngs[CR_BLUE ]},
  79. {"CRORANGE", &colrngs[CR_ORANGE]},
  80. {"CRYELLOW", &colrngs[CR_YELLOW]},
  81. {"CRBLUE2", &colrngs[CR_BLUE2]},
  82. {NULL, NULL}
  83. };
  84. // killough 5/2/98: tiny engine driven by table above
  85. void V_InitColorTranslation(void)
  86. {
  87. register const crdef_t *p;
  88. for (p=crdefs; p->name; p++)
  89. *p->map = W_CacheLumpName(p->name);
  90. }
  91. //
  92. // V_CopyRect
  93. //
  94. // Copies a source rectangle in a screen buffer to a destination
  95. // rectangle in another screen buffer. Source origin in srcx,srcy,
  96. // destination origin in destx,desty, common size in width and height.
  97. // Source buffer specfified by srcscrn, destination buffer by destscrn.
  98. //
  99. // Marks the destination rectangle on the screen dirty.
  100. //
  101. // No return.
  102. //
  103. static void FUNC_V_CopyRect(int srcx, int srcy, int srcscrn, int width,
  104. int height, int destx, int desty, int destscrn,
  105. enum patch_translation_e flags)
  106. {
  107. byte *src;
  108. byte *dest;
  109. if (flags & VPT_STRETCH)
  110. {
  111. srcx=srcx*SCREENWIDTH/320;
  112. srcy=srcy*SCREENHEIGHT/200;
  113. width=width*SCREENWIDTH/320;
  114. height=height*SCREENHEIGHT/200;
  115. destx=destx*SCREENWIDTH/320;
  116. desty=desty*SCREENHEIGHT/200;
  117. }
  118. #ifdef RANGECHECK
  119. if (srcx<0
  120. ||srcx+width >SCREENWIDTH
  121. || srcy<0
  122. || srcy+height>SCREENHEIGHT
  123. ||destx<0||destx+width >SCREENWIDTH
  124. || desty<0
  125. || desty+height>SCREENHEIGHT)
  126. I_Error ("V_CopyRect: Bad arguments");
  127. #endif
  128. src = screens[srcscrn].data+screens[srcscrn].byte_pitch*srcy+srcx*V_GetPixelDepth();
  129. dest = screens[destscrn].data+screens[destscrn].byte_pitch*desty+destx*V_GetPixelDepth();
  130. for ( ; height>0 ; height--)
  131. {
  132. memcpy (dest, src, width*V_GetPixelDepth());
  133. src += screens[srcscrn].byte_pitch;
  134. dest += screens[destscrn].byte_pitch;
  135. }
  136. }
  137. /*
  138. * V_DrawBackground tiles a 64x64 patch over the entire screen, providing the
  139. * background for the Help and Setup screens, and plot text betwen levels.
  140. * cphipps - used to have M_DrawBackground, but that was used the framebuffer
  141. * directly, so this is my code from the equivalent function in f_finale.c
  142. */
  143. static void FUNC_V_DrawBackground(const char* flatname, int scrn)
  144. {
  145. /* erase the entire screen to a tiled background */
  146. const byte *src;
  147. int x,y;
  148. int width,height;
  149. int lump;
  150. // killough 4/17/98:
  151. src = W_CacheLumpNum(lump = firstflat + R_FlatNumForName(flatname));
  152. /* V_DrawBlock(0, 0, scrn, 64, 64, src, 0); */
  153. width = height = 64;
  154. if (V_GetMode() == VID_MODE8) {
  155. byte *dest = screens[scrn].data;
  156. while (height--) {
  157. memcpy (dest, src, width);
  158. src += width;
  159. dest += screens[scrn].byte_pitch;
  160. }
  161. } else if (V_GetMode() == VID_MODE15) {
  162. unsigned short *dest = (unsigned short *)screens[scrn].data;
  163. while (height--) {
  164. int i;
  165. for (i=0; i<width; i++) {
  166. dest[i] = VID_PAL15(src[i], VID_COLORWEIGHTMASK);
  167. }
  168. src += width;
  169. dest += screens[scrn].short_pitch;
  170. }
  171. } else if (V_GetMode() == VID_MODE16) {
  172. unsigned short *dest = (unsigned short *)screens[scrn].data;
  173. while (height--) {
  174. int i;
  175. for (i=0; i<width; i++) {
  176. dest[i] = VID_PAL16(src[i], VID_COLORWEIGHTMASK);
  177. }
  178. src += width;
  179. dest += screens[scrn].short_pitch;
  180. }
  181. } else if (V_GetMode() == VID_MODE32) {
  182. unsigned int *dest = (unsigned int *)screens[scrn].data;
  183. while (height--) {
  184. int i;
  185. for (i=0; i<width; i++) {
  186. dest[i] = VID_PAL32(src[i], VID_COLORWEIGHTMASK);
  187. }
  188. src += width;
  189. dest += screens[scrn].int_pitch;
  190. }
  191. }
  192. /* end V_DrawBlock */
  193. for (y=0 ; y<SCREENHEIGHT ; y+=64)
  194. for (x=y ? 0 : 64; x<SCREENWIDTH ; x+=64)
  195. V_CopyRect(0, 0, scrn, ((SCREENWIDTH-x) < 64) ? (SCREENWIDTH-x) : 64,
  196. ((SCREENHEIGHT-y) < 64) ? (SCREENHEIGHT-y) : 64, x, y, scrn, VPT_NONE);
  197. W_UnlockLumpNum(lump);
  198. }
  199. //
  200. // V_Init
  201. //
  202. // Allocates the 4 full screen buffers in low DOS memory
  203. // No return
  204. //
  205. void V_Init (void)
  206. {
  207. int i;
  208. // reset the all
  209. for (i = 0; i<NUM_SCREENS; i++) {
  210. screens[i].data = NULL;
  211. screens[i].not_on_heap = false;
  212. screens[i].width = 0;
  213. screens[i].height = 0;
  214. screens[i].byte_pitch = 0;
  215. screens[i].short_pitch = 0;
  216. screens[i].int_pitch = 0;
  217. }
  218. }
  219. //
  220. // V_DrawMemPatch
  221. //
  222. // CPhipps - unifying patch drawing routine, handles all cases and combinations
  223. // of stretching, flipping and translating
  224. //
  225. // This function is big, hopefully not too big that gcc can't optimise it well.
  226. // In fact it packs pretty well, there is no big performance lose for all this merging;
  227. // the inner loops themselves are just the same as they always were
  228. // (indeed, laziness of the people who wrote the 'clones' of the original V_DrawPatch
  229. // means that their inner loops weren't so well optimised, so merging code may even speed them).
  230. //
  231. static void V_DrawMemPatch(int x, int y, int scrn, const rpatch_t *patch,
  232. int cm, enum patch_translation_e flags)
  233. {
  234. const byte *trans;
  235. if (cm<CR_LIMIT)
  236. trans=colrngs[cm];
  237. else
  238. trans=translationtables + 256*((cm-CR_LIMIT)-1);
  239. y -= patch->topoffset;
  240. x -= patch->leftoffset;
  241. // CPhipps - auto-no-stretch if not high-res
  242. if (flags & VPT_STRETCH)
  243. if ((SCREENWIDTH==320) && (SCREENHEIGHT==200))
  244. flags &= ~VPT_STRETCH;
  245. // CPhipps - null translation pointer => no translation
  246. if (!trans)
  247. flags &= ~VPT_TRANS;
  248. if (V_GetMode() == VID_MODE8 && !(flags & VPT_STRETCH)) {
  249. int col;
  250. byte *desttop = screens[scrn].data+y*screens[scrn].byte_pitch+x*V_GetPixelDepth();
  251. unsigned int w = patch->width;
  252. if (y<0 || y+patch->height > ((flags & VPT_STRETCH) ? 200 : SCREENHEIGHT)) {
  253. // killough 1/19/98: improved error message:
  254. lprintf(LO_WARN, "V_DrawMemPatch8: Patch (%d,%d)-(%d,%d) exceeds LFB in vertical direction (horizontal is clipped)\n"
  255. "Bad V_DrawMemPatch8 (flags=%u)", x, y, x+patch->width, y+patch->height, flags);
  256. return;
  257. }
  258. w--; // CPhipps - note: w = width-1 now, speeds up flipping
  259. for (col=0 ; (unsigned int)col<=w ; desttop++, col++, x++) {
  260. int i;
  261. const int colindex = (flags & VPT_FLIP) ? (w - col) : (col);
  262. const rcolumn_t *column = R_GetPatchColumn(patch, colindex);
  263. if (x < 0)
  264. continue;
  265. if (x >= SCREENWIDTH)
  266. break;
  267. // step through the posts in a column
  268. for (i=0; i<column->numPosts; i++) {
  269. const rpost_t *post = &column->posts[i];
  270. // killough 2/21/98: Unrolled and performance-tuned
  271. const byte *source = column->pixels + post->topdelta;
  272. byte *dest = desttop + post->topdelta*screens[scrn].byte_pitch;
  273. int count = post->length;
  274. if (!(flags & VPT_TRANS)) {
  275. if ((count-=4)>=0)
  276. do {
  277. register byte s0,s1;
  278. s0 = source[0];
  279. s1 = source[1];
  280. dest[0] = s0;
  281. dest[screens[scrn].byte_pitch] = s1;
  282. dest += screens[scrn].byte_pitch*2;
  283. s0 = source[2];
  284. s1 = source[3];
  285. source += 4;
  286. dest[0] = s0;
  287. dest[screens[scrn].byte_pitch] = s1;
  288. dest += screens[scrn].byte_pitch*2;
  289. } while ((count-=4)>=0);
  290. if (count+=4)
  291. do {
  292. *dest = *source++;
  293. dest += screens[scrn].byte_pitch;
  294. } while (--count);
  295. } else {
  296. // CPhipps - merged translation code here
  297. if ((count-=4)>=0)
  298. do {
  299. register byte s0,s1;
  300. s0 = source[0];
  301. s1 = source[1];
  302. s0 = trans[s0];
  303. s1 = trans[s1];
  304. dest[0] = s0;
  305. dest[screens[scrn].byte_pitch] = s1;
  306. dest += screens[scrn].byte_pitch*2;
  307. s0 = source[2];
  308. s1 = source[3];
  309. s0 = trans[s0];
  310. s1 = trans[s1];
  311. source += 4;
  312. dest[0] = s0;
  313. dest[screens[scrn].byte_pitch] = s1;
  314. dest += screens[scrn].byte_pitch*2;
  315. } while ((count-=4)>=0);
  316. if (count+=4)
  317. do {
  318. *dest = trans[*source++];
  319. dest += screens[scrn].byte_pitch;
  320. } while (--count);
  321. }
  322. }
  323. }
  324. }
  325. else {
  326. // CPhipps - move stretched patch drawing code here
  327. // - reformat initialisers, move variables into inner blocks
  328. int col;
  329. int w = (patch->width << 16) - 1; // CPhipps - -1 for faster flipping
  330. int left, right, top, bottom;
  331. int DX = (SCREENWIDTH<<16) / 320;
  332. int DXI = (320<<16) / SCREENWIDTH;
  333. int DY = (SCREENHEIGHT<<16) / 200;
  334. int DYI = (200<<16) / SCREENHEIGHT;
  335. R_DrawColumn_f colfunc;
  336. draw_column_vars_t dcvars;
  337. draw_vars_t olddrawvars = drawvars;
  338. R_SetDefaultDrawColumnVars(&dcvars);
  339. drawvars.byte_topleft = screens[scrn].data;
  340. drawvars.short_topleft = (unsigned short *)screens[scrn].data;
  341. drawvars.int_topleft = (unsigned int *)screens[scrn].data;
  342. drawvars.byte_pitch = screens[scrn].byte_pitch;
  343. drawvars.short_pitch = screens[scrn].short_pitch;
  344. drawvars.int_pitch = screens[scrn].int_pitch;
  345. if (!(flags & VPT_STRETCH)) {
  346. DX = 1 << 16;
  347. DXI = 1 << 16;
  348. DY = 1 << 16;
  349. DYI = 1 << 16;
  350. }
  351. if (flags & VPT_TRANS) {
  352. colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_TRANSLATED, drawvars.filterpatch, RDRAW_FILTER_NONE);
  353. dcvars.translation = trans;
  354. } else {
  355. colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_STANDARD, drawvars.filterpatch, RDRAW_FILTER_NONE);
  356. }
  357. left = ( x * DX ) >> FRACBITS;
  358. top = ( y * DY ) >> FRACBITS;
  359. right = ( (x + patch->width) * DX ) >> FRACBITS;
  360. bottom = ( (y + patch->height) * DY ) >> FRACBITS;
  361. dcvars.texheight = patch->height;
  362. dcvars.iscale = DYI;
  363. dcvars.drawingmasked = MAX(patch->width, patch->height) > 8;
  364. dcvars.edgetype = drawvars.patch_edges;
  365. if (drawvars.filterpatch == RDRAW_FILTER_LINEAR) {
  366. // bias the texture u coordinate
  367. if (patch->isNotTileable)
  368. col = -(FRACUNIT>>1);
  369. else
  370. col = (patch->width<<FRACBITS)-(FRACUNIT>>1);
  371. }
  372. else {
  373. col = 0;
  374. }
  375. for (dcvars.x=left; dcvars.x<right; dcvars.x++, col+=DXI) {
  376. int i;
  377. const int colindex = (flags & VPT_FLIP) ? ((w - col)>>16): (col>>16);
  378. const rcolumn_t *column = R_GetPatchColumn(patch, colindex);
  379. const rcolumn_t *prevcolumn = R_GetPatchColumn(patch, colindex-1);
  380. const rcolumn_t *nextcolumn = R_GetPatchColumn(patch, colindex+1);
  381. // ignore this column if it's to the left of our clampRect
  382. if (dcvars.x < 0)
  383. continue;
  384. if (dcvars.x >= SCREENWIDTH)
  385. break;
  386. dcvars.texu = ((flags & VPT_FLIP) ? ((patch->width<<FRACBITS)-col) : col) % (patch->width<<FRACBITS);
  387. // step through the posts in a column
  388. for (i=0; i<column->numPosts; i++) {
  389. const rpost_t *post = &column->posts[i];
  390. int yoffset = 0;
  391. dcvars.yl = (((y + post->topdelta) * DY)>>FRACBITS);
  392. dcvars.yh = (((y + post->topdelta + post->length) * DY - (FRACUNIT>>1))>>FRACBITS);
  393. dcvars.edgeslope = post->slope;
  394. if ((dcvars.yh < 0) || (dcvars.yh < top))
  395. continue;
  396. if ((dcvars.yl >= SCREENHEIGHT) || (dcvars.yl >= bottom))
  397. continue;
  398. if (dcvars.yh >= bottom) {
  399. dcvars.yh = bottom-1;
  400. dcvars.edgeslope &= ~RDRAW_EDGESLOPE_BOT_MASK;
  401. }
  402. if (dcvars.yh >= SCREENHEIGHT) {
  403. dcvars.yh = SCREENHEIGHT-1;
  404. dcvars.edgeslope &= ~RDRAW_EDGESLOPE_BOT_MASK;
  405. }
  406. if (dcvars.yl < 0) {
  407. yoffset = 0-dcvars.yl;
  408. dcvars.yl = 0;
  409. dcvars.edgeslope &= ~RDRAW_EDGESLOPE_TOP_MASK;
  410. }
  411. if (dcvars.yl < top) {
  412. yoffset = top-dcvars.yl;
  413. dcvars.yl = top;
  414. dcvars.edgeslope &= ~RDRAW_EDGESLOPE_TOP_MASK;
  415. }
  416. dcvars.source = column->pixels + post->topdelta + yoffset;
  417. dcvars.prevsource = prevcolumn ? prevcolumn->pixels + post->topdelta + yoffset: dcvars.source;
  418. dcvars.nextsource = nextcolumn ? nextcolumn->pixels + post->topdelta + yoffset: dcvars.source;
  419. dcvars.texturemid = -((dcvars.yl-centery)*dcvars.iscale);
  420. colfunc(&dcvars);
  421. }
  422. }
  423. R_ResetColumnBuffer();
  424. drawvars = olddrawvars;
  425. }
  426. }
  427. // CPhipps - some simple, useful wrappers for that function, for drawing patches from wads
  428. // CPhipps - GNU C only suppresses generating a copy of a function if it is
  429. // static inline; other compilers have different behaviour.
  430. // This inline is _only_ for the function below
  431. static void FUNC_V_DrawNumPatch(int x, int y, int scrn, int lump,
  432. int cm, enum patch_translation_e flags)
  433. {
  434. V_DrawMemPatch(x, y, scrn, R_CachePatchNum(lump), cm, flags);
  435. R_UnlockPatchNum(lump);
  436. }
  437. unsigned short *V_Palette15 = NULL;
  438. unsigned short *V_Palette16 = NULL;
  439. unsigned int *V_Palette32 = NULL;
  440. static unsigned short *Palettes15 = NULL;
  441. static unsigned short *Palettes16 = NULL;
  442. static unsigned int *Palettes32 = NULL;
  443. static int currentPaletteIndex = 0;
  444. //
  445. // V_UpdateTrueColorPalette
  446. //
  447. void V_UpdateTrueColorPalette(video_mode_t mode) {
  448. int i, w, p;
  449. byte r,g,b;
  450. int nr,ng,nb;
  451. float t;
  452. int paletteNum = (V_GetMode() == VID_MODEGL ? 0 : currentPaletteIndex);
  453. static int usegammaOnLastPaletteGeneration = -1;
  454. int pplump = W_GetNumForName("PLAYPAL");
  455. int gtlump = (W_CheckNumForName)("GAMMATBL",ns_prboom);
  456. const byte *pal = W_CacheLumpNum(pplump);
  457. // opengl doesn't use the gamma
  458. const byte *const gtable =
  459. (const byte *)W_CacheLumpNum(gtlump) +
  460. (V_GetMode() == VID_MODEGL ? 0 : 256*(usegamma))
  461. ;
  462. int numPals = W_LumpLength(pplump) / (3*256);
  463. const float dontRoundAbove = 220;
  464. float roundUpR, roundUpG, roundUpB;
  465. if (usegammaOnLastPaletteGeneration != usegamma) {
  466. if (Palettes15) free(Palettes15);
  467. if (Palettes16) free(Palettes16);
  468. if (Palettes32) free(Palettes32);
  469. Palettes15 = NULL;
  470. Palettes16 = NULL;
  471. Palettes32 = NULL;
  472. usegammaOnLastPaletteGeneration = usegamma;
  473. }
  474. if (mode == VID_MODE32) {
  475. if (!Palettes32) {
  476. // set int palette
  477. Palettes32 = (unsigned int*)malloc(numPals*256*sizeof(unsigned int)*VID_NUMCOLORWEIGHTS);
  478. for (p=0; p<numPals; p++) {
  479. for (i=0; i<256; i++) {
  480. r = gtable[pal[(256*p+i)*3+0]];
  481. g = gtable[pal[(256*p+i)*3+1]];
  482. b = gtable[pal[(256*p+i)*3+2]];
  483. // ideally, we should always round up, but very bright colors
  484. // overflow the blending adds, so they don't get rounded.
  485. roundUpR = (r > dontRoundAbove) ? 0 : 0.5f;
  486. roundUpG = (g > dontRoundAbove) ? 0 : 0.5f;
  487. roundUpB = (b > dontRoundAbove) ? 0 : 0.5f;
  488. for (w=0; w<VID_NUMCOLORWEIGHTS; w++) {
  489. t = (float)(w)/(float)(VID_NUMCOLORWEIGHTS-1);
  490. nr = (int)(r*t+roundUpR);
  491. ng = (int)(g*t+roundUpG);
  492. nb = (int)(b*t+roundUpB);
  493. Palettes32[((p*256+i)*VID_NUMCOLORWEIGHTS)+w] = (
  494. (nr<<16) | (ng<<8) | nb
  495. );
  496. }
  497. }
  498. }
  499. }
  500. V_Palette32 = Palettes32 + paletteNum*256*VID_NUMCOLORWEIGHTS;
  501. }
  502. else if (mode == VID_MODE16) {
  503. if (!Palettes16) {
  504. // set short palette
  505. Palettes16 = (unsigned short*)malloc(numPals*256*sizeof(unsigned short)*VID_NUMCOLORWEIGHTS);
  506. for (p=0; p<numPals; p++) {
  507. for (i=0; i<256; i++) {
  508. r = gtable[pal[(256*p+i)*3+0]];
  509. g = gtable[pal[(256*p+i)*3+1]];
  510. b = gtable[pal[(256*p+i)*3+2]];
  511. // ideally, we should always round up, but very bright colors
  512. // overflow the blending adds, so they don't get rounded.
  513. roundUpR = (r > dontRoundAbove) ? 0 : 0.5f;
  514. roundUpG = (g > dontRoundAbove) ? 0 : 0.5f;
  515. roundUpB = (b > dontRoundAbove) ? 0 : 0.5f;
  516. for (w=0; w<VID_NUMCOLORWEIGHTS; w++) {
  517. t = (float)(w)/(float)(VID_NUMCOLORWEIGHTS-1);
  518. nr = (int)((r>>3)*t+roundUpR);
  519. ng = (int)((g>>2)*t+roundUpG);
  520. nb = (int)((b>>3)*t+roundUpB);
  521. Palettes16[((p*256+i)*VID_NUMCOLORWEIGHTS)+w] = (
  522. (nr<<11) | (ng<<5) | nb
  523. );
  524. }
  525. }
  526. }
  527. }
  528. V_Palette16 = Palettes16 + paletteNum*256*VID_NUMCOLORWEIGHTS;
  529. }
  530. else if (mode == VID_MODE15) {
  531. if (!Palettes15) {
  532. // set short palette
  533. Palettes15 = (unsigned short*)malloc(numPals*256*sizeof(unsigned short)*VID_NUMCOLORWEIGHTS);
  534. for (p=0; p<numPals; p++) {
  535. for (i=0; i<256; i++) {
  536. r = gtable[pal[(256*p+i)*3+0]];
  537. g = gtable[pal[(256*p+i)*3+1]];
  538. b = gtable[pal[(256*p+i)*3+2]];
  539. // ideally, we should always round up, but very bright colors
  540. // overflow the blending adds, so they don't get rounded.
  541. roundUpR = (r > dontRoundAbove) ? 0 : 0.5f;
  542. roundUpG = (g > dontRoundAbove) ? 0 : 0.5f;
  543. roundUpB = (b > dontRoundAbove) ? 0 : 0.5f;
  544. for (w=0; w<VID_NUMCOLORWEIGHTS; w++) {
  545. t = (float)(w)/(float)(VID_NUMCOLORWEIGHTS-1);
  546. nr = (int)((r>>3)*t+roundUpR);
  547. ng = (int)((g>>3)*t+roundUpG);
  548. nb = (int)((b>>3)*t+roundUpB);
  549. Palettes15[((p*256+i)*VID_NUMCOLORWEIGHTS)+w] = (
  550. (nr<<10) | (ng<<5) | nb
  551. );
  552. }
  553. }
  554. }
  555. }
  556. V_Palette15 = Palettes15 + paletteNum*256*VID_NUMCOLORWEIGHTS;
  557. }
  558. W_UnlockLumpNum(pplump);
  559. W_UnlockLumpNum(gtlump);
  560. }
  561. //---------------------------------------------------------------------------
  562. // V_DestroyTrueColorPalette
  563. //---------------------------------------------------------------------------
  564. static void V_DestroyTrueColorPalette(video_mode_t mode) {
  565. if (mode == VID_MODE15) {
  566. if (Palettes15) free(Palettes15);
  567. Palettes15 = NULL;
  568. V_Palette15 = NULL;
  569. }
  570. if (mode == VID_MODE16) {
  571. if (Palettes16) free(Palettes16);
  572. Palettes16 = NULL;
  573. V_Palette16 = NULL;
  574. }
  575. if (mode == VID_MODE32) {
  576. if (Palettes32) free(Palettes32);
  577. Palettes32 = NULL;
  578. V_Palette32 = NULL;
  579. }
  580. }
  581. void V_DestroyUnusedTrueColorPalettes(void) {
  582. if (V_GetMode() != VID_MODE15) V_DestroyTrueColorPalette(VID_MODE15);
  583. if (V_GetMode() != VID_MODE16) V_DestroyTrueColorPalette(VID_MODE16);
  584. if (V_GetMode() != VID_MODE32) V_DestroyTrueColorPalette(VID_MODE32);
  585. }
  586. //
  587. // V_SetPalette
  588. //
  589. // CPhipps - New function to set the palette to palette number pal.
  590. // Handles loading of PLAYPAL and calls I_SetPalette
  591. void V_SetPalette(int pal)
  592. {
  593. currentPaletteIndex = pal;
  594. if (V_GetMode() == VID_MODEGL) {
  595. #ifdef GL_DOOM
  596. gld_SetPalette(pal);
  597. #endif
  598. } else {
  599. I_SetPalette(pal);
  600. if (V_GetMode() == VID_MODE15 || V_GetMode() == VID_MODE16 || V_GetMode() == VID_MODE32) {
  601. // V_SetPalette can be called as part of the gamma setting before
  602. // we've loaded any wads, which prevents us from reading the palette - POPE
  603. if (W_CheckNumForName("PLAYPAL") >= 0) {
  604. V_UpdateTrueColorPalette(V_GetMode());
  605. }
  606. }
  607. }
  608. }
  609. //
  610. // V_FillRect
  611. //
  612. // CPhipps - New function to fill a rectangle with a given colour
  613. static void V_FillRect8(int scrn, int x, int y, int width, int height, byte colour)
  614. {
  615. byte* dest = screens[scrn].data + x + y*screens[scrn].byte_pitch;
  616. while (height--) {
  617. memset(dest, colour, width);
  618. dest += screens[scrn].byte_pitch;
  619. }
  620. }
  621. static void V_FillRect15(int scrn, int x, int y, int width, int height, byte colour)
  622. {
  623. unsigned short* dest = (unsigned short *)screens[scrn].data + x + y*screens[scrn].short_pitch;
  624. int w;
  625. short c = VID_PAL15(colour, VID_COLORWEIGHTMASK);
  626. while (height--) {
  627. for (w=0; w<width; w++) {
  628. dest[w] = c;
  629. }
  630. dest += screens[scrn].short_pitch;
  631. }
  632. }
  633. static void V_FillRect16(int scrn, int x, int y, int width, int height, byte colour)
  634. {
  635. unsigned short* dest = (unsigned short *)screens[scrn].data + x + y*screens[scrn].short_pitch;
  636. int w;
  637. short c = VID_PAL16(colour, VID_COLORWEIGHTMASK);
  638. while (height--) {
  639. for (w=0; w<width; w++) {
  640. dest[w] = c;
  641. }
  642. dest += screens[scrn].short_pitch;
  643. }
  644. }
  645. static void V_FillRect32(int scrn, int x, int y, int width, int height, byte colour)
  646. {
  647. unsigned int* dest = (unsigned int *)screens[scrn].data + x + y*screens[scrn].int_pitch;
  648. int w;
  649. int c = VID_PAL32(colour, VID_COLORWEIGHTMASK);
  650. while (height--) {
  651. for (w=0; w<width; w++) {
  652. dest[w] = c;
  653. }
  654. dest += screens[scrn].int_pitch;
  655. }
  656. }
  657. static void WRAP_V_DrawLine(fline_t* fl, int color);
  658. static void V_PlotPixel8(int scrn, int x, int y, byte color);
  659. static void V_PlotPixel15(int scrn, int x, int y, byte color);
  660. static void V_PlotPixel16(int scrn, int x, int y, byte color);
  661. static void V_PlotPixel32(int scrn, int x, int y, byte color);
  662. #ifdef GL_DOOM
  663. static void WRAP_gld_FillRect(int scrn, int x, int y, int width, int height, byte colour)
  664. {
  665. gld_FillBlock(x,y,width,height,colour);
  666. }
  667. static 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)
  668. {
  669. }
  670. static void WRAP_gld_DrawBackground(const char *flatname, int n)
  671. {
  672. gld_DrawBackground(flatname);
  673. }
  674. static void WRAP_gld_DrawNumPatch(int x, int y, int scrn, int lump, int cm, enum patch_translation_e flags)
  675. {
  676. gld_DrawNumPatch(x,y,lump,cm,flags);
  677. }
  678. static void V_PlotPixelGL(int scrn, int x, int y, byte color) {
  679. gld_DrawLine(x-1, y, x+1, y, color);
  680. gld_DrawLine(x, y-1, x, y+1, color);
  681. }
  682. static void WRAP_gld_DrawLine(fline_t* fl, int color)
  683. {
  684. gld_DrawLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, color);
  685. }
  686. #endif
  687. static void NULL_FillRect(int scrn, int x, int y, int width, int height, byte colour) {}
  688. 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) {}
  689. static void NULL_DrawBackground(const char *flatname, int n) {}
  690. static void NULL_DrawNumPatch(int x, int y, int scrn, int lump, int cm, enum patch_translation_e flags) {}
  691. static void NULL_PlotPixel(int scrn, int x, int y, byte color) {}
  692. static void NULL_DrawLine(fline_t* fl, int color) {}
  693. const char *default_videomode;
  694. static video_mode_t current_videomode = VID_MODE8;
  695. V_CopyRect_f V_CopyRect = NULL_CopyRect;
  696. V_FillRect_f V_FillRect = NULL_FillRect;
  697. V_DrawNumPatch_f V_DrawNumPatch = NULL_DrawNumPatch;
  698. V_DrawBackground_f V_DrawBackground = NULL_DrawBackground;
  699. V_PlotPixel_f V_PlotPixel = NULL_PlotPixel;
  700. V_DrawLine_f V_DrawLine = NULL_DrawLine;
  701. //
  702. // V_InitMode
  703. //
  704. void V_InitMode(video_mode_t mode) {
  705. #ifndef GL_DOOM
  706. if (mode == VID_MODEGL)
  707. mode = VID_MODE8;
  708. #endif
  709. switch (mode) {
  710. case VID_MODE8:
  711. lprintf(LO_INFO, "V_InitMode: using 8 bit video mode\n");
  712. V_CopyRect = FUNC_V_CopyRect;
  713. V_FillRect = V_FillRect8;
  714. V_DrawNumPatch = FUNC_V_DrawNumPatch;
  715. V_DrawBackground = FUNC_V_DrawBackground;
  716. V_PlotPixel = V_PlotPixel8;
  717. V_DrawLine = WRAP_V_DrawLine;
  718. current_videomode = VID_MODE8;
  719. break;
  720. case VID_MODE15:
  721. lprintf(LO_INFO, "V_InitMode: using 15 bit video mode\n");
  722. V_CopyRect = FUNC_V_CopyRect;
  723. V_FillRect = V_FillRect15;
  724. V_DrawNumPatch = FUNC_V_DrawNumPatch;
  725. V_DrawBackground = FUNC_V_DrawBackground;
  726. V_PlotPixel = V_PlotPixel15;
  727. V_DrawLine = WRAP_V_DrawLine;
  728. current_videomode = VID_MODE15;
  729. break;
  730. case VID_MODE16:
  731. lprintf(LO_INFO, "V_InitMode: using 16 bit video mode\n");
  732. V_CopyRect = FUNC_V_CopyRect;
  733. V_FillRect = V_FillRect16;
  734. V_DrawNumPatch = FUNC_V_DrawNumPatch;
  735. V_DrawBackground = FUNC_V_DrawBackground;
  736. V_PlotPixel = V_PlotPixel16;
  737. V_DrawLine = WRAP_V_DrawLine;
  738. current_videomode = VID_MODE16;
  739. break;
  740. case VID_MODE32:
  741. lprintf(LO_INFO, "V_InitMode: using 32 bit video mode\n");
  742. V_CopyRect = FUNC_V_CopyRect;
  743. V_FillRect = V_FillRect32;
  744. V_DrawNumPatch = FUNC_V_DrawNumPatch;
  745. V_DrawBackground = FUNC_V_DrawBackground;
  746. V_PlotPixel = V_PlotPixel32;
  747. V_DrawLine = WRAP_V_DrawLine;
  748. current_videomode = VID_MODE32;
  749. break;
  750. #ifdef GL_DOOM
  751. case VID_MODEGL:
  752. lprintf(LO_INFO, "V_InitMode: using OpenGL video mode\n");
  753. V_CopyRect = WRAP_gld_CopyRect;
  754. V_FillRect = WRAP_gld_FillRect;
  755. V_DrawNumPatch = WRAP_gld_DrawNumPatch;
  756. V_DrawBackground = WRAP_gld_DrawBackground;
  757. V_PlotPixel = V_PlotPixelGL;
  758. V_DrawLine = WRAP_gld_DrawLine;
  759. current_videomode = VID_MODEGL;
  760. break;
  761. #endif
  762. default: break;
  763. }
  764. R_FilterInit();
  765. }
  766. //
  767. // V_GetMode
  768. //
  769. video_mode_t V_GetMode(void) {
  770. return current_videomode;
  771. }
  772. //
  773. // V_GetModePixelDepth
  774. //
  775. int V_GetModePixelDepth(video_mode_t mode) {
  776. switch (mode) {
  777. case VID_MODE8: return 1;
  778. case VID_MODE15: return 2;
  779. case VID_MODE16: return 2;
  780. case VID_MODE32: return 4;
  781. default: return 0;
  782. }
  783. }
  784. //
  785. // V_GetNumPixelBits
  786. //
  787. int V_GetNumPixelBits(void) {
  788. switch (current_videomode) {
  789. case VID_MODE8: return 8;
  790. case VID_MODE15: return 15;
  791. case VID_MODE16: return 16;
  792. case VID_MODE32: return 32;
  793. default: return 0;
  794. }
  795. }
  796. //
  797. // V_GetPixelDepth
  798. //
  799. int V_GetPixelDepth(void) {
  800. return V_GetModePixelDepth(current_videomode);
  801. }
  802. //
  803. // V_AllocScreen
  804. //
  805. void V_AllocScreen(screeninfo_t *scrn) {
  806. if (!scrn->not_on_heap)
  807. if ((scrn->byte_pitch * scrn->height) > 0)
  808. scrn->data = malloc(scrn->byte_pitch*scrn->height);
  809. }
  810. //
  811. // V_AllocScreens
  812. //
  813. void V_AllocScreens(void) {
  814. int i;
  815. for (i=0; i<NUM_SCREENS; i++)
  816. V_AllocScreen(&screens[i]);
  817. }
  818. //
  819. // V_FreeScreen
  820. //
  821. void V_FreeScreen(screeninfo_t *scrn) {
  822. if (!scrn->not_on_heap) {
  823. free(scrn->data);
  824. scrn->data = NULL;
  825. }
  826. }
  827. //
  828. // V_FreeScreens
  829. //
  830. void V_FreeScreens(void) {
  831. int i;
  832. for (i=0; i<NUM_SCREENS; i++)
  833. V_FreeScreen(&screens[i]);
  834. }
  835. static void V_PlotPixel8(int scrn, int x, int y, byte color) {
  836. screens[scrn].data[x+screens[scrn].byte_pitch*y] = color;
  837. }
  838. static void V_PlotPixel15(int scrn, int x, int y, byte color) {
  839. ((unsigned short *)screens[scrn].data)[x+screens[scrn].short_pitch*y] = VID_PAL15(color, VID_COLORWEIGHTMASK);
  840. }
  841. static void V_PlotPixel16(int scrn, int x, int y, byte color) {
  842. ((unsigned short *)screens[scrn].data)[x+screens[scrn].short_pitch*y] = VID_PAL16(color, VID_COLORWEIGHTMASK);
  843. }
  844. static void V_PlotPixel32(int scrn, int x, int y, byte color) {
  845. ((unsigned int *)screens[scrn].data)[x+screens[scrn].int_pitch*y] = VID_PAL32(color, VID_COLORWEIGHTMASK);
  846. }
  847. //
  848. // WRAP_V_DrawLine()
  849. //
  850. // Draw a line in the frame buffer.
  851. // Classic Bresenham w/ whatever optimizations needed for speed
  852. //
  853. // Passed the frame coordinates of line, and the color to be drawn
  854. // Returns nothing
  855. //
  856. static void WRAP_V_DrawLine(fline_t* fl, int color)
  857. {
  858. register int x;
  859. register int y;
  860. register int dx;
  861. register int dy;
  862. register int sx;
  863. register int sy;
  864. register int ax;
  865. register int ay;
  866. register int d;
  867. #ifdef RANGECHECK // killough 2/22/98
  868. static int fuck = 0;
  869. // For debugging only
  870. if
  871. (
  872. fl->a.x < 0 || fl->a.x >= SCREENWIDTH
  873. || fl->a.y < 0 || fl->a.y >= SCREENHEIGHT
  874. || fl->b.x < 0 || fl->b.x >= SCREENWIDTH
  875. || fl->b.y < 0 || fl->b.y >= SCREENHEIGHT
  876. )
  877. {
  878. //jff 8/3/98 use logical output routine
  879. lprintf(LO_DEBUG, "fuck %d \r", fuck++);
  880. return;
  881. }
  882. #endif
  883. #define PUTDOT(xx,yy,cc) V_PlotPixel(0,xx,yy,(byte)cc)
  884. dx = fl->b.x - fl->a.x;
  885. ax = 2 * (dx<0 ? -dx : dx);
  886. sx = dx<0 ? -1 : 1;
  887. dy = fl->b.y - fl->a.y;
  888. ay = 2 * (dy<0 ? -dy : dy);
  889. sy = dy<0 ? -1 : 1;
  890. x = fl->a.x;
  891. y = fl->a.y;
  892. if (ax > ay)
  893. {
  894. d = ay - ax/2;
  895. while (1)
  896. {
  897. PUTDOT(x,y,color);
  898. if (x == fl->b.x) return;
  899. if (d>=0)
  900. {
  901. y += sy;
  902. d -= ax;
  903. }
  904. x += sx;
  905. d += ay;
  906. }
  907. }
  908. else
  909. {
  910. d = ax - ay/2;
  911. while (1)
  912. {
  913. PUTDOT(x, y, color);
  914. if (y == fl->b.y) return;
  915. if (d >= 0)
  916. {
  917. x += sx;
  918. d -= ay;
  919. }
  920. y += sy;
  921. d += ax;
  922. }
  923. }
  924. }