summaryrefslogtreecommitdiff
path: root/apps/plugins/doom/v_video.c
blob: 8c475614ff15cc92d60be023116d32719e05fbc9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
/* 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
 *
 *  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 "i_system.h"
#include "m_swap.h"
#include "rockmacros.h"
// Each screen is [SCREENWIDTH*SCREENHEIGHT];
byte *d_screens[6] IBSS_ATTR;
int  dirtybox[4];

/* jff 4/24/98 initialize this at runtime */
const byte *colrngs[CR_LIMIT];

// Now where did these came from?
const byte gammatable[5][256] = // CPhipps - const
   {
      {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
       17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,
       33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,
       49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,
       65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,
       81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,
       97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,
       113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,
       128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
       144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
       160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
       176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
       192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
       208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
       224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
       240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255},

      {2,4,5,7,8,10,11,12,14,15,16,18,19,20,21,23,24,25,26,27,29,30,31,
       32,33,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,54,55,
       56,57,58,59,60,61,62,63,64,65,66,67,69,70,71,72,73,74,75,76,77,
       78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,
       99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,
       115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,129,
       130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,
       146,147,148,148,149,150,151,152,153,154,155,156,157,158,159,160,
       161,162,163,163,164,165,166,167,168,169,170,171,172,173,174,175,
       175,176,177,178,179,180,181,182,183,184,185,186,186,187,188,189,
       190,191,192,193,194,195,196,196,197,198,199,200,201,202,203,204,
       205,205,206,207,208,209,210,211,212,213,214,214,215,216,217,218,
       219,220,221,222,222,223,224,225,226,227,228,229,230,230,231,232,
       233,234,235,236,237,237,238,239,240,241,242,243,244,245,245,246,
       247,248,249,250,251,252,252,253,254,255},

      {4,7,9,11,13,15,17,19,21,22,24,26,27,29,30,32,33,35,36,38,39,40,42,
       43,45,46,47,48,50,51,52,54,55,56,57,59,60,61,62,63,65,66,67,68,69,
       70,72,73,74,75,76,77,78,79,80,82,83,84,85,86,87,88,89,90,91,92,93,
       94,95,96,97,98,100,101,102,103,104,105,106,107,108,109,110,111,112,
       113,114,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,
       129,130,131,132,133,133,134,135,136,137,138,139,140,141,142,143,144,
       144,145,146,147,148,149,150,151,152,153,153,154,155,156,157,158,159,
       160,160,161,162,163,164,165,166,166,167,168,169,170,171,172,172,173,
       174,175,176,177,178,178,179,180,181,182,183,183,184,185,186,187,188,
       188,189,190,191,192,193,193,194,195,196,197,197,198,199,200,201,201,
       202,203,204,205,206,206,207,208,209,210,210,211,212,213,213,214,215,
       216,217,217,218,219,220,221,221,222,223,224,224,225,226,227,228,228,
       229,230,231,231,232,233,234,235,235,236,237,238,238,239,240,241,241,
       242,243,244,244,245,246,247,247,248,249,250,251,251,252,253,254,254,
       255},

      {8,12,16,19,22,24,27,29,31,34,36,38,40,41,43,45,47,49,50,52,53,55,
       57,58,60,61,63,64,65,67,68,70,71,72,74,75,76,77,79,80,81,82,84,85,
       86,87,88,90,91,92,93,94,95,96,98,99,100,101,102,103,104,105,106,107,
       108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,
       125,126,127,128,129,130,131,132,133,134,135,135,136,137,138,139,140,
       141,142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,
       155,156,157,158,159,160,160,161,162,163,164,165,165,166,167,168,169,
       169,170,171,172,173,173,174,175,176,176,177,178,179,180,180,181,182,
       183,183,184,185,186,186,187,188,189,189,190,191,192,192,193,194,195,
       195,196,197,197,198,199,200,200,201,202,202,203,204,205,205,206,207,
       207,208,209,210,210,211,212,212,213,214,214,215,216,216,217,218,219,
       219,220,221,221,222,223,223,224,225,225,226,227,227,228,229,229,230,
       231,231,232,233,233,234,235,235,236,237,237,238,238,239,240,240,241,
       242,242,243,244,244,245,246,246,247,247,248,249,249,250,251,251,252,
       253,253,254,254,255},

      {16,23,28,32,36,39,42,45,48,50,53,55,57,60,62,64,66,68,69,71,73,75,76,
       78,80,81,83,84,86,87,89,90,92,93,94,96,97,98,100,101,102,103,105,106,
       107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124,
       125,126,128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,
       142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,155,
       156,157,158,159,159,160,161,162,163,163,164,165,166,166,167,168,169,
       169,170,171,172,172,173,174,175,175,176,177,177,178,179,180,180,181,
       182,182,183,184,184,185,186,187,187,188,189,189,190,191,191,192,193,
       193,194,195,195,196,196,197,198,198,199,200,200,201,202,202,203,203,
       204,205,205,206,207,207,208,208,209,210,210,211,211,212,213,213,214,
       214,215,216,216,217,217,218,219,219,220,220,221,221,222,223,223,224,
       224,225,225,226,227,227,228,228,229,229,230,230,231,232,232,233,233,
       234,234,235,235,236,236,237,237,238,239,239,240,240,241,241,242,242,
       243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,
       251,252,252,253,254,254,255,255}
   };

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, 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_MarkRect
//
// Marks a rectangular portion of the screen specified by
// upper left origin and height and width dirty to minimize
// the amount of screen update necessary. No return.
//
void V_MarkRect(int x, int y, int width, int height)
{
   M_AddToBox(dirtybox, x, y);
   M_AddToBox(dirtybox, x+width-1, y+height-1);
}

//
// 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_CopyRect(int srcx, int srcy, int srcscrn, int width,
                int height, int destx, int desty, int destscrn,
                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

   V_MarkRect (destx, desty, width, height);

   src = d_screens[srcscrn]+SCREENWIDTH*srcy+srcx;
   dest = d_screens[destscrn]+SCREENWIDTH*desty+destx;

   for ( ; height>0 ; height--)
   {
      memcpy (dest, src, width);
      src += SCREENWIDTH;
      dest += SCREENWIDTH;
   }
}

//
// V_DrawBlock
//
// Draw a linear block of pixels into the view buffer.
//
// The bytes at src are copied in linear order to the screen rectangle
// at x,y in screenbuffer scrn, with size width by height.
//
// The destination rectangle is marked dirty.
//
// No return.
//
// CPhipps - modified  to take the patch translation flags. For now, only stretching is
//  implemented, to support highres in the menus
//
void V_DrawBlock(int x, int y, int scrn, int width, int height,
                 const byte *src, patch_translation_e flags)
{
   byte *dest;

#ifdef RANGECHECK
   if (x<0
         ||x+width >((flags & VPT_STRETCH) ? 320 : SCREENWIDTH)
         || y<0
         || y+height>((flags & VPT_STRETCH) ? 200 : SCREENHEIGHT))
      I_Error ("V_DrawBlock: Bad V_DrawBlock");

   if (flags & (VPT_TRANS | VPT_FLIP))
      I_Error("V_DrawBlock: Unsupported flags (%u)", flags);
#endif

   if (flags & VPT_STRETCH) {
      byte   *dest;
      int     s_width;
      fixed_t dx = (320 << FRACBITS) / SCREENWIDTH;

      x = (x * SCREENWIDTH) / 320; y = (y * SCREENHEIGHT) / 200;
      s_width = (width * SCREENWIDTH) / 320; height = (height * SCREENHEIGHT) / 200;

      if (!scrn)
         V_MarkRect (x, y, width, height);

      dest = d_screens[scrn] + y*SCREENWIDTH+x;
      // x & y no longer needed

      while (height--) {
         const byte *const src_row = src + width * ((height * 200) / SCREENHEIGHT);
         byte       *const dst_row = dest + SCREENWIDTH * height;
         fixed_t           tx;

         for (x=0, tx=0; x<s_width; x++, tx+=dx)
            dst_row[x] = src_row[tx >> FRACBITS];
      }
   } else {
      V_MarkRect (x, y, width, height);

      dest = d_screens[scrn] + y*SCREENWIDTH+x;

      while (height--) {
         memcpy (dest, src, width);
         src += width;
         dest += SCREENWIDTH;
      }
   }
}

/*
 * 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_DrawBackground(const char* flatname, int scrn)
{
   /* erase the entire screen to a tiled background */
   const byte *src;
   int         x,y;
   int         lump;

   // killough 4/17/98:
   src = W_CacheLumpNum(lump = firstflat + R_FlatNumForName(flatname));

   V_DrawBlock(0, 0, scrn, 64, 64, src, 0);

   for (y=0 ; y<SCREENHEIGHT ; y+=64)
      for (x=y ? 0 : 64; x<SCREENWIDTH ; x+=64)
         V_CopyRect(0, 0, scrn, ((SCREENWIDTH-x) < 64) ? (SCREENWIDTH-x) : 64,
                    ((SCREENHEIGHT-y) < 64) ? (SCREENHEIGHT-y) : 64, x, y, scrn, VPT_NONE);
   W_UnlockLumpNum(lump);
}

//
// V_GetBlock
//
// Gets a linear block of pixels from the view buffer.
//
// The pixels in the rectangle at x,y in screenbuffer scrn with size
// width by height are linearly packed into the buffer dest.
// No return
//

void V_GetBlock(int x, int y, int scrn, int width, int height, byte *dest)
{
   byte *src;

#ifdef RANGECHECK
   if (x<0
         ||x+width >SCREENWIDTH
         || y<0
         || y+height>SCREENHEIGHT)
      I_Error ("V_GetBlock: Bad arguments");
#endif

   src = d_screens[scrn] + y*SCREENWIDTH+x;

   while (height--)
   {
      memcpy (dest, src, width);
      src += SCREENWIDTH;
      dest += width;
   }
}

//
// V_Init
//
// Allocates the 4 full screen buffers in low DOS memory
// No return
//

void V_Init (void)
{
   int  i;
   // CPhipps - allocate only 2 screens all the time, the rest can be allocated as and when needed
#define PREALLOCED_SCREENS 2

   // CPhipps - no point in "stick these in low dos memory on PCs" anymore
   // Allocate the screens individually, so I_InitGraphics can release d_screens[0]
   //  if e.g. it wants a MitSHM buffer instead

   for (i=0 ; i<PREALLOCED_SCREENS ; i++)
      d_screens[i] = calloc(SCREENWIDTH*SCREENHEIGHT, 1);
   for (; i<4; i++) // Clear the rest (paranoia)
      d_screens[i] = NULL;
}

//
// V_DrawMemPatch
//
// CPhipps - unifying patch drawing routine, handles all cases and combinations
//  of stretching, flipping and translating
//
// This function is big, hopefully not too big that gcc can't optimise it well.
// In fact it packs pretty well, there is no big performance lose for all this merging;
// the inner loops themselves are just the same as they always were
// (indeed, laziness of the people who wrote the 'clones' of the original V_DrawPatch
//  means that their inner loops weren't so well optimised, so merging code may even speed them).
//
void V_DrawMemPatch(int x, int y, int scrn, const patch_t *patch,
                    int cm, patch_translation_e flags)
{
   const byte *trans;

   if (cm<CR_LIMIT)
      trans=colrngs[cm];
   else
      trans=translationtables + 256*((cm-CR_LIMIT)-1);
   y -= SHORT(patch->topoffset);
   x -= SHORT(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 (x<0
         ||x+SHORT(patch->width) > ((flags & VPT_STRETCH) ? 320 : SCREENWIDTH)
         || y<0
         || y+SHORT(patch->height) > ((flags & VPT_STRETCH) ? 200 :  SCREENHEIGHT))
   {
      rb->splash(HZ*2, "This wad does not follow standard doom graphics!");
      // killough 1/19/98: improved error message:
      I_Error("V_DrawMemPatch: Patch (%d,%d)-(%d,%d) exceeds LFB Bad V_DrawMemPatch (flags=%u)",
         x, y, x+SHORT(patch->width), y+SHORT(patch->height), flags);
   }

   if (!(flags & VPT_STRETCH)) {
      unsigned int             col;
      const column_t *column;
      byte           *desttop = d_screens[scrn]+y*SCREENWIDTH+x;
      unsigned int    w = SHORT(patch->width);

      if (!scrn)
         V_MarkRect (x, y, w, SHORT(patch->height));

      w--; // CPhipps - note: w = width-1 now, speeds up flipping

      for (col=0 ; (unsigned int)col<=w ; desttop++, col++) {
         column = (column_t *)((byte *)patch +
                               LONG(patch->columnofs[(flags & VPT_FLIP) ? w-col : col]));

         // step through the posts in a column
         while (column->topdelta != 0xff ) {
            // killough 2/21/98: Unrolled and performance-tuned

            register const byte *source = (byte *)column + 3;
            register byte *dest = desttop + column->topdelta*SCREENWIDTH;
            register int count = column->length;

            if (!(flags & VPT_TRANS)) {
               if ((count-=4)>=0)
                  do {
                     register byte s0,s1;
                     s0 = source[0];
                     s1 = source[1];
                     dest[0] = s0;
                     dest[SCREENWIDTH] = s1;
                     dest += SCREENWIDTH*2;
                     s0 = source[2];
                     s1 = source[3];
                     source += 4;
                     dest[0] = s0;
                     dest[SCREENWIDTH] = s1;
                     dest += SCREENWIDTH*2;
                  } while ((count-=4)>=0);
               if (count+=4)
                  do {
                     *dest = *source++;
                     dest += SCREENWIDTH;
                  } while (--count);
               column = (column_t *)(source+1); //killough 2/21/98 even faster
            } 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[SCREENWIDTH] = s1;
                     dest += SCREENWIDTH*2;
                     s0 = source[2];
                     s1 = source[3];
                     s0 = trans[s0];
                     s1 = trans[s1];
                     source += 4;
                     dest[0] = s0;
                     dest[SCREENWIDTH] = s1;
                     dest += SCREENWIDTH*2;
                  } while ((count-=4)>=0);
               if (count+=4)
                  do {
                     *dest = trans[*source++];
                     dest += SCREENWIDTH;
                  } while (--count);
               column = (column_t *)(source+1);
            }
         }
      }
   }
   else {
      // CPhipps - move stretched patch drawing code here
      //         - reformat initialisers, move variables into inner blocks

      byte *desttop;
      int   col;
      int   w = (SHORT( patch->width ) << 16) -1; // CPhipps - -1 for faster flipping
      int   stretchx, stretchy;
      int   DX  = (SCREENWIDTH<<16)  / 320;
      int   DXI = (320<<16)          / SCREENWIDTH;
      int   DY  = (SCREENHEIGHT<<16) / 200;
      register int DYI = (200<<16)   / SCREENHEIGHT;

      stretchx = ( x * DX ) >> 16;
      stretchy = ( y * DY ) >> 16;

      if (!scrn)
         V_MarkRect ( stretchx, stretchy, (SHORT( patch->width ) * DX ) >> 16,
                      (SHORT( patch->height) * DY ) >> 16 );

      desttop = d_screens[scrn] + stretchy * SCREENWIDTH +  stretchx;

      // Clamp down the screenwidth
      if(w>>16>=319)
         w=(SCREENWIDTH-1)*DXI;

      for ( col = 0; col <= w; x++, col+=DXI, desttop++ ) {
         const column_t *column;
         {
            unsigned int d = patch->columnofs[(flags & VPT_FLIP) ? ((w - col)>>16): (col>>16)];
            column = (column_t*)((byte*)patch + LONG(d));
         }

         while ( column->topdelta != 0xff ) {
            register const byte *source = ( byte* ) column + 3;
            register byte       *dest = desttop + (( column->topdelta * DY ) >> 16 ) * SCREENWIDTH;
            register int         count  = ( column->length * DY ) >> 16;
            register int         srccol = 0x8000;

            count = (count>SCREENHEIGHT)?SCREENHEIGHT:count;  // Bounds checking allows those messed up
                                                              // GP32 mods to work (they're using patch->
                                                              // height values of 240, this code cuts off
                                                              // thier bottom few pixels

            if (flags & VPT_TRANS)
               while (count--) {
                  *dest  =  trans[source[srccol>>16]];
                  dest  +=  SCREENWIDTH;
                  srccol+=  DYI;
               }
            else
               while (count--) {
                  *dest  =  source[srccol>>16];
                  dest  +=  SCREENWIDTH;
                  srccol+=  DYI;
               }
            column = ( column_t* ) (( byte* ) column + ( column->length ) + 4 );
         }
      }
   }
}

// 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

#ifdef __GNUC__
inline
#endif
void V_DrawNumPatch(int x, int y, int scrn, int lump,
                    int cm, patch_translation_e flags)
{
   V_DrawMemPatch(x, y, scrn, (const patch_t*)W_CacheLumpNum(lump),
                  cm, flags);
   W_UnlockLumpNum(lump);
}

/* cph -
 * V_NamePatchWidth - returns width of a patch.
 * V_NamePatchHeight- returns height of a patch.
 *
 * Doesn't really belong here, but is often used in conjunction with
 *  this code.
 * This is needed to reduce the number of patches being held locked
 *  in memory, since a lot of code was locking and holding pointers
 *  to graphics in order to get this info easily. Also, we do endian
 *  correction here, which reduces the chance of other code forgetting
 *  this.
 */
int V_NamePatchWidth(const char* name)
{
   int lump = W_GetNumForName(name);
   int w;

   w = SHORT(((const patch_t*)W_CacheLumpNum(lump))->width);
   W_UnlockLumpNum(lump);
   return w;
}

int V_NamePatchHeight(const char* name)
{
   int lump = W_GetNumForName(name);
   int w;

   w = SHORT(((const patch_t*)W_CacheLumpNum(lump))->height);
   W_UnlockLumpNum(lump);
   return w;
}

// CPhipps -
// V_PatchToBlock
//
// Returns a simple bitmap which contains the patch. See-through parts of the
// patch will be undefined (in fact black for now)

byte *V_PatchToBlock(const char* name, int cm,
                     patch_translation_e flags,
                     unsigned short* width, unsigned short* height)
{
   (void)cm;
   (void)flags;
   byte          *oldscr = d_screens[1];
   byte          *block;
   const patch_t *patch;

   d_screens[1] = calloc(SCREENWIDTH*SCREENHEIGHT, 1);

   patch = W_CacheLumpName(name);
// One of those odd things that don't seem to have a purpose other then rangechecking
//  On screens smaller than 320X200 this line causes problems.
//   V_DrawMemPatch(SHORT(patch->leftoffset), SHORT(patch->topoffset),
//                  1, patch, cm, flags);

#ifdef RANGECHECK
   if (flags & VPT_STRETCH)
      I_Error("V_PatchToBlock: Stretching not supported");
#endif

   *width = SHORT(patch->width); *height = SHORT(patch->height);

   W_UnlockLumpName(name);

   V_GetBlock(0, 0, 1, *width, *height,
              block = malloc((long)(*width) * (*height)));

   free(d_screens[1]);
   d_screens[1] = oldscr;
   return block;
}

//
// 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)
{
   I_SetPalette(pal);
}

//
// V_FillRect
//
// CPhipps - New function to fill a rectangle with a given colour
void V_FillRect(int scrn, int x, int y, int width, int height, byte colour)
{
   byte* dest = d_screens[scrn] + x + y*SCREENWIDTH;
   while (height--) {
      memset(dest, colour, width);
      dest += SCREENWIDTH;
   }
}