shithub: choc

Download patch

ref: 265c0de3419b5123052a10cef276e1221cd0630c
parent: 136009e7505acc321c2b7f710929cb7d9b88a5b2
author: Simon Howard <fraggle@gmail.com>
date: Fri Dec 15 14:35:46 EST 2006

Split off scaling code into i_scale.c. Add aspect ratio correction
stretching (fullscreen 320x240, 640x480, etc)!

Subversion-branch: /trunk/chocolate-doom
Subversion-revision: 771

--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -60,6 +60,7 @@
 hu_stuff.c           hu_stuff.h            \
 i_main.c                                   \
 info.c               info.h                \
+i_scale.c            i_scale.h             \
 i_sound.c            i_sound.h             \
 i_system.c           i_system.h            \
 i_timer.c            i_timer.h             \
--- /dev/null
+++ b/src/i_scale.c
@@ -1,0 +1,404 @@
+
+#include "doomdef.h"
+#include "doomtype.h"
+
+#include "z_zone.h"
+
+// Should be screens[0]
+
+static byte *src_buffer;
+
+// Destination buffer, ie. screen->pixels.
+
+static byte *dest_buffer;
+
+// Pitch of destination buffer, ie. screen->pitch.
+
+static int dest_pitch;
+
+// Lookup tables used for aspect ratio correction stretching code.
+// stretch_tables[0] : 20% / 80%
+// stretch_tables[1] : 40% / 60%
+// All other combinations can be reached from these two tables.
+
+static byte *stretch_tables[2];
+
+void I_InitScale(byte *_src_buffer, byte *_dest_buffer, int _dest_pitch)
+{
+    src_buffer = _src_buffer;
+    dest_buffer = _dest_buffer;
+    dest_pitch = _dest_pitch;
+}
+
+void I_Scale1x(int x1, int y1, int x2, int y2)
+{
+    byte *bufp, *screenp;
+    int y;
+    int w = x2 - x1;
+    
+    // Need to byte-copy from buffer into the screen buffer
+
+    bufp = src_buffer + y1 * SCREENWIDTH + x1;
+    screenp = (byte *) dest_buffer + y1 * dest_pitch + x1;
+
+    for (y=y1; y<y2; ++y)
+    {
+        memcpy(screenp, bufp, w);
+        screenp += dest_pitch;
+        bufp += SCREENWIDTH;
+    }
+}
+
+void I_Scale2x(int x1, int y1, int x2, int y2)
+{
+    byte *bufp, *screenp, *screenp2;
+    int x, y;
+    int multi_pitch;
+
+    multi_pitch = dest_pitch * 2;
+    bufp = src_buffer + y1 * SCREENWIDTH + x1;
+    screenp = (byte *) dest_buffer + (y1 * dest_pitch + x1) * 2;
+    screenp2 = screenp + dest_pitch;
+
+    for (y=y1; y<y2; ++y)
+    {
+        byte *sp, *sp2, *bp;
+        sp = screenp;
+        sp2 = screenp2;
+        bp = bufp;
+
+        for (x=x1; x<x2; ++x)
+        {
+            *sp++ = *bp;  *sp++ = *bp;
+            *sp2++ = *bp; *sp2++ = *bp;
+            ++bp;
+        }
+        screenp += multi_pitch;
+        screenp2 += multi_pitch;
+        bufp += SCREENWIDTH;
+    }
+}
+
+void I_Scale3x(int x1, int y1, int x2, int y2)
+{
+    byte *bufp, *screenp, *screenp2, *screenp3;
+    int x, y;
+    int multi_pitch;
+
+    multi_pitch = dest_pitch * 3;
+    bufp = src_buffer + y1 * SCREENWIDTH + x1;
+    screenp = (byte *) dest_buffer + (y1 * dest_pitch + x1) * 3;
+    screenp2 = screenp + dest_pitch;
+    screenp3 = screenp + dest_pitch * 2;
+
+    for (y=y1; y<y2; ++y)
+    {
+        byte *sp, *sp2, *sp3, *bp;
+        sp = screenp;
+        sp2 = screenp2;
+        sp3 = screenp3;
+        bp = bufp;
+
+        for (x=x1; x<x2; ++x)
+        {
+            *sp++ = *bp;  *sp++ = *bp;  *sp++ = *bp;
+            *sp2++ = *bp; *sp2++ = *bp; *sp2++ = *bp;
+            *sp3++ = *bp; *sp3++ = *bp; *sp3++ = *bp;
+            ++bp;
+        }
+        screenp += multi_pitch;
+        screenp2 += multi_pitch;
+        screenp3 += multi_pitch;
+        bufp += SCREENWIDTH;
+    }
+}
+
+void I_Scale4x(int x1, int y1, int x2, int y2)
+{
+    byte *bufp, *screenp, *screenp2, *screenp3, *screenp4;
+    int x, y;
+    int multi_pitch;
+
+    multi_pitch = dest_pitch * 4;
+    bufp = src_buffer + y1 * SCREENWIDTH + x1;
+    screenp = (byte *) dest_buffer + (y1 * dest_pitch + x1) * 4;
+    screenp2 = screenp + dest_pitch;
+    screenp3 = screenp + dest_pitch * 2;
+    screenp4 = screenp + dest_pitch * 3;
+
+    for (y=y1; y<y2; ++y)
+    {
+        byte *sp, *sp2, *sp3, *sp4, *bp;
+        sp = screenp;
+        sp2 = screenp2;
+        sp3 = screenp3;
+        sp4 = screenp4;
+        bp = bufp;
+
+        for (x=x1; x<x2; ++x)
+        {
+            *sp++ = *bp;  *sp++ = *bp;  *sp++ = *bp;  *sp++ = *bp;
+            *sp2++ = *bp; *sp2++ = *bp; *sp2++ = *bp; *sp2++ = *bp;
+            *sp3++ = *bp; *sp3++ = *bp; *sp3++ = *bp; *sp3++ = *bp;
+            *sp4++ = *bp; *sp4++ = *bp; *sp4++ = *bp; *sp4++ = *bp;
+            ++bp;
+        }
+        screenp += multi_pitch;
+        screenp2 += multi_pitch;
+        screenp3 += multi_pitch;
+        screenp4 += multi_pitch;
+        bufp += SCREENWIDTH;
+    }
+}
+
+// Search through the given palette, finding the nearest color that matches
+// the given color.
+
+static int FindNearestColor(byte *palette, int r, int g, int b)
+{
+    byte *col;
+    int best;
+    int best_diff;
+    int diff;
+    int i;
+
+    best = 0;
+    best_diff = INT_MAX;
+
+    for (i=0; i<256; ++i)
+    {
+        col = palette + i * 3;
+        diff = (r - col[0]) * (r - col[0])
+             + (g - col[1]) * (g - col[1])
+             + (b - col[2]) * (b - col[2]);
+
+        if (diff == 0)
+        {
+            return i;
+        }
+        else if (diff < best_diff)
+        {
+            best = i;
+            best_diff = diff;
+        }
+    }
+
+    return best;
+}
+
+// Create a stretch table.  This is a lookup table for blending colors.
+// pct specifies the bias between the two colors: 0 = all y, 100 = all x.
+// NB: This is identical to the lookup tables used in other ports for
+// translucency.
+
+static byte *GenerateStretchTable(byte *palette, int pct)
+{
+    byte *result;
+    int x, y;
+    int r, g, b;
+    byte *col1;
+    byte *col2;
+
+    result = Z_Malloc(256 * 256, PU_STATIC, NULL);
+
+    for (x=0; x<256; ++x)
+    {
+        for (y=0; y<256; ++y)
+        {
+            col1 = palette + x * 3;
+            col2 = palette + y * 3;
+            r = (((int) col1[0]) * pct + ((int) col2[0]) * (100 - pct)) / 100;
+            g = (((int) col1[1]) * pct + ((int) col2[1]) * (100 - pct)) / 100;
+            b = (((int) col1[2]) * pct + ((int) col2[2]) * (100 - pct)) / 100;
+            result[x * 256 + y] = FindNearestColor(palette, r, g, b);
+        }
+    }
+
+    return result;
+}
+
+void I_InitStretchTables(byte *palette)
+{
+    // We only actually need two lookup tables:
+    //
+    // mix 0%   =  just write line 1
+    // mix 20%  =  stretch_tables[0]
+    // mix 40%  =  stretch_tables[1]
+    // mix 60%  =  stretch_tables[1] used backwards
+    // mix 80%  =  stretch_tables[0] used backwards
+    // mix 100% =  just write line 2
+
+    printf("I_InitStretchTables: Generating lookup tables..");
+    fflush(stdout);
+    stretch_tables[0] = GenerateStretchTable(palette, 20);
+    printf(".."); fflush(stdout);
+    stretch_tables[1] = GenerateStretchTable(palette, 40);
+    puts("");
+}
+
+static void WriteBlendedLine1x(byte *dest, byte *src1, byte *src2, 
+                               byte *stretch_table)
+{
+    int x;
+
+    for (x=0; x<SCREENWIDTH; ++x)
+    {
+        *dest = stretch_table[*src1 * 256 + *src2];
+        ++dest;
+        ++src1;
+        ++src2;
+    }
+} 
+
+void I_Stretch1x(int x1, int y1, int x2, int y2)
+{
+    byte *bufp, *screenp;
+    int y;
+
+    // Only works with full screen update
+
+    if (x1 != 0 || y1 != 0 || x2 != SCREENWIDTH || y2 != SCREENHEIGHT)
+    {
+        return;
+    }    
+
+    // Need to byte-copy from buffer into the screen buffer
+
+    bufp = src_buffer + y1 * SCREENWIDTH + x1;
+    screenp = (byte *) dest_buffer + y1 * dest_pitch + x1;
+
+    // For every 5 lines of src_buffer, 6 lines are written to dest_buffer
+    // (200 -> 240)
+
+    for (y=0; y<SCREENHEIGHT; y += 5)
+    {
+        // 100% line 0
+        memcpy(screenp, bufp, SCREENWIDTH);
+        screenp += dest_pitch;
+
+        // 20% line 0, 80% line 1
+        WriteBlendedLine1x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[0]);
+        screenp += dest_pitch; bufp += SCREENWIDTH;
+
+        // 40% line 1, 60% line 2
+        WriteBlendedLine1x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[1]);
+        screenp += dest_pitch; bufp += SCREENWIDTH;
+
+        // 60% line 2, 40% line 3
+        WriteBlendedLine1x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[1]);
+        screenp += dest_pitch; bufp += SCREENWIDTH;
+
+        // 80% line 3, 20% line 4
+        WriteBlendedLine1x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[0]);
+        screenp += dest_pitch; bufp += SCREENWIDTH;
+
+        // 100% line 4
+        memcpy(screenp, bufp, SCREENWIDTH);
+        screenp += dest_pitch; bufp += SCREENWIDTH;
+    }
+}
+
+static void WriteLine2x(byte *dest, byte *src)
+{
+    int x;
+
+    for (x=0; x<SCREENWIDTH; ++x)
+    {
+        dest[0] = *src;
+        dest[1] = *src;
+        dest += 2;
+        ++src;
+    }
+}
+
+static void WriteBlendedLine2x(byte *dest, byte *src1, byte *src2, 
+                               byte *stretch_table)
+{
+    int x;
+    int val;
+
+    for (x=0; x<SCREENWIDTH; ++x)
+    {
+        val = stretch_table[*src1 * 256 + *src2];
+        dest[0] = val;
+        dest[1] = val;
+        dest += 2;
+        ++src1;
+        ++src2;
+    }
+} 
+
+// Scale 2x, correcting aspect ratio in the process
+
+void I_Stretch2x(int x1, int y1, int x2, int y2)
+{
+    byte *bufp, *screenp;
+    int y;
+
+    // Only works with full screen update
+
+    if (x1 != 0 || y1 != 0 || x2 != SCREENWIDTH || y2 != SCREENHEIGHT)
+    {
+        return;
+    }    
+
+    // Need to byte-copy from buffer into the screen buffer
+
+    bufp = src_buffer + y1 * SCREENWIDTH + x1;
+    screenp = (byte *) dest_buffer + y1 * dest_pitch + x1;
+
+    // For every 5 lines of src_buffer, 12 lines are written to dest_buffer.
+    // (200 -> 480)
+
+    for (y=0; y<SCREENHEIGHT; y += 5)
+    {
+        // 100% line 0
+        WriteLine2x(screenp, bufp);
+        screenp += dest_pitch;
+
+        // 100% line 0
+        WriteLine2x(screenp, bufp);
+        screenp += dest_pitch;
+
+        // 40% line 0, 60% line 1
+        WriteBlendedLine2x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[1]);
+        screenp += dest_pitch; bufp += SCREENWIDTH;
+
+        // 100% line 1
+        WriteLine2x(screenp, bufp);
+        screenp += dest_pitch;
+
+        // 80% line 1, 20% line 2
+        WriteBlendedLine2x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[0]);
+        screenp += dest_pitch; bufp += SCREENWIDTH;
+
+        // 100% line 2
+        WriteLine2x(screenp, bufp);
+        screenp += dest_pitch;
+
+        // 100% line 2
+        WriteLine2x(screenp, bufp);
+        screenp += dest_pitch;
+
+        // 20% line 2, 80% line 3
+        WriteBlendedLine2x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[0]);
+        screenp += dest_pitch; bufp += SCREENWIDTH;
+
+        // 100% line 3
+        WriteLine2x(screenp, bufp);
+        screenp += dest_pitch;
+
+        // 60% line 3, 40% line 4
+        WriteBlendedLine2x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[1]);
+        screenp += dest_pitch; bufp += SCREENWIDTH;
+
+        // 100% line 4
+        WriteLine2x(screenp, bufp);
+        screenp += dest_pitch;
+
+        // 100% line 4
+        WriteLine2x(screenp, bufp);
+        screenp += dest_pitch; bufp += SCREENWIDTH;
+    }
+}
+
--- /dev/null
+++ b/src/i_scale.h
@@ -1,0 +1,43 @@
+// Emacs style mode select   -*- C++ -*- 
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 2006 Simon Howard
+//
+// 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:
+//	System specific interface stuff.
+//
+//-----------------------------------------------------------------------------
+
+
+#ifndef __I_SCALE__
+#define __I_SCALE__
+
+#include "doomtype.h"
+
+void I_InitScale(byte *_src_buffer, byte *_dest_buffer, int _dest_pitch);
+void I_Scale1x(int x1, int y1, int x2, int y2);
+void I_Scale2x(int x1, int y1, int x2, int y2);
+void I_Scale3x(int x1, int y1, int x2, int y2);
+void I_Scale4x(int x1, int y1, int x2, int y2);
+void I_Stretch1x(int x1, int y1, int x2, int y2);
+void I_Stretch2x(int x1, int y1, int x2, int y2);
+void I_InitStretchTables(byte *palette);
+
+#endif /* #ifndef __I_SCALE__ */
+
--- a/src/i_video.c
+++ b/src/i_video.c
@@ -37,6 +37,7 @@
 #include "doomdef.h"
 #include "doomstat.h"
 #include "d_main.h"
+#include "i_scale.h"
 #include "i_system.h"
 #include "i_timer.h"
 #include "m_argv.h"
@@ -49,15 +50,21 @@
 
 // Alternate screenheight for letterbox mode
 
-#define LETTERBOX_SCREENHEIGHT 240
+#define SCREENHEIGHT_4_3 240
 
 enum
 {
     FULLSCREEN_OFF,
     FULLSCREEN_ON,
-    FULLSCREEN_LETTERBOX,
 };
 
+enum
+{
+    RATIO_CORRECT_NONE,
+    RATIO_CORRECT_LETTERBOX,
+    RATIO_CORRECT_STRETCH,
+};
+
 extern void M_QuitDOOM();
 
 static SDL_Surface *screen;
@@ -90,6 +97,10 @@
 
 int fullscreen = FULLSCREEN_ON;
 
+// Aspect ratio correction mode
+
+int aspect_ratio_correct = RATIO_CORRECT_NONE;
+
 // Time to wait for the screen to settle on startup before starting the
 // game (ms)
 
@@ -547,165 +558,77 @@
 
 // Update a small portion of the screen
 //
-// Does 2x stretching and buffer blitting if neccessary
+// Does stretching and buffer blitting if neccessary
 
 static void BlitArea(int x1, int y1, int x2, int y2)
 {
-    int w = x2 - x1;
+    void (*scale_function)(int x1, int y1, int x2, int y2);
     int x_offset, y_offset;
 
-    x_offset = ((screen->w / screenmultiply) - SCREENWIDTH) / 2;
-    y_offset = ((screen->h / screenmultiply) - SCREENHEIGHT) / 2;
-    
-    // Need to byte-copy from buffer into the screen buffer
-
-    if (screenmultiply == 1 && !native_surface)
+    if (native_surface)
     {
-        byte *bufp, *screenp;
-        int y;
-        int pitch;
-
-        if (SDL_LockSurface(screen) >= 0)
-        {
-            pitch = screen->pitch;
-            bufp = screens[0] + y1 * SCREENWIDTH + x1;
-            screenp = (byte *) screen->pixels + (y1 + y_offset) * pitch 
-                                              + x1 + x_offset;
-    
-            for (y=y1; y<y2; ++y)
-            {
-                memcpy(screenp, bufp, w);
-                screenp += pitch;
-                bufp += SCREENWIDTH;
-            }
-    
-            SDL_UnlockSurface(screen);
-        }
+	return;
     }
 
-    // scales the screen size before blitting it
-
-    if (screenmultiply == 2)
+    if (aspect_ratio_correct == RATIO_CORRECT_LETTERBOX)
     {
-        byte *bufp, *screenp, *screenp2;
-        int x, y;
-        int pitch;
-
-        if (SDL_LockSurface(screen) >= 0)
-        {
-            pitch = screen->pitch * 2;
-            bufp = screens[0] + y1 * SCREENWIDTH + x1;
-            screenp = (byte *) screen->pixels 
-                    + (y1 + y_offset) * pitch 
-                    + (x1 + x_offset) * 2;
-            screenp2 = screenp + screen->pitch;
-    
-            for (y=y1; y<y2; ++y)
-            {
-                byte *sp, *sp2, *bp;
-                sp = screenp;
-                sp2 = screenp2;
-                bp = bufp;
-    
-                for (x=x1; x<x2; ++x)
-                {
-                    *sp++ = *bp;  *sp++ = *bp;
-                    *sp2++ = *bp; *sp2++ = *bp;
-                    ++bp;
-                }
-                screenp += pitch;
-                screenp2 += pitch;
-                bufp += SCREENWIDTH;
-            }
-    
-            SDL_UnlockSurface(screen);
-        }
+        x_offset = (screen->w - SCREENWIDTH * screenmultiply) / 2;
+        y_offset = (screen->h - SCREENHEIGHT * screenmultiply) / 2;
     }
-
-    if (screenmultiply == 3)
+    else
     {
-        byte *bufp, *screenp, *screenp2, *screenp3;
-        int x, y;
-        int pitch;
+        x_offset = 0;
+        y_offset = 0;
+    }
 
-        if (SDL_LockSurface(screen) >= 0)
+    if (aspect_ratio_correct == RATIO_CORRECT_STRETCH)
+    {
+        if (screenmultiply == 1)
         {
-            pitch = screen->pitch * 3;
-            bufp = screens[0] + y1 * SCREENWIDTH + x1;
-            screenp = (byte *) screen->pixels 
-                    + (y1 + y_offset) * pitch 
-                    + (x1 + x_offset) * 3;
-            screenp2 = screenp + screen->pitch;
-            screenp3 = screenp2 + screen->pitch;
-    
-            for (y=y1; y<y2; ++y)
-            {
-                byte *sp, *sp2, *sp3, *bp;
-                sp = screenp;
-                sp2 = screenp2;
-                sp3 = screenp3;
-                bp = bufp;
-    
-                for (x=x1; x<x2; ++x)
-                {
-                    *sp++ = *bp;  *sp++ = *bp;  *sp++ = *bp;
-                    *sp2++ = *bp; *sp2++ = *bp; *sp2++ = *bp;
-                    *sp3++ = *bp; *sp3++ = *bp; *sp3++ = *bp;
-                    ++bp;
-                }
-                screenp += pitch;
-                screenp2 += pitch;
-                screenp3 += pitch;
-                bufp += SCREENWIDTH;
-            }
-    
-            SDL_UnlockSurface(screen);
+            scale_function = I_Stretch1x;
         }
+        else if (screenmultiply == 2)
+        { 
+            scale_function = I_Stretch2x;
+        }
+        else
+        {
+            I_Error("No aspect ratio stretching function for screenmultiply=%i",
+                    screenmultiply);
+            return;
+        }
+    } else {
+        if (screenmultiply == 1)
+        {
+            scale_function = I_Scale1x;
+        }
+        else if (screenmultiply == 2) 
+        {
+            scale_function = I_Scale2x;
+        }
+        else if (screenmultiply == 3)
+        {
+            scale_function = I_Scale3x;
+        }
+        else if (screenmultiply == 4)
+        {
+            scale_function = I_Scale4x;
+        }
+        else
+        {
+            I_Error("No scale function found!");
+            return;
+        }
     }
 
-    if (screenmultiply == 4)
+    if (SDL_LockSurface(screen) >= 0)
     {
-        byte *bufp, *screenp, *screenp2, *screenp3, *screenp4;
-        int x, y;
-        int pitch;
-
-        if (SDL_LockSurface(screen) >= 0)
-        {
-            pitch = screen->pitch * 4;
-            bufp = screens[0] + y1 * SCREENWIDTH + x1;
-            screenp = (byte *) screen->pixels 
-                    + (y1 + y_offset) * pitch 
-                    + (x1 + x_offset) * 4;
-            screenp2 = screenp + screen->pitch;
-            screenp3 = screenp2 + screen->pitch;
-            screenp4 = screenp3 + screen->pitch;
-    
-            for (y=y1; y<y2; ++y)
-            {
-                byte *sp, *sp2, *sp3, *sp4, *bp;
-                sp = screenp;
-                sp2 = screenp2;
-                sp3 = screenp3;
-                sp4 = screenp4;
-                bp = bufp;
-    
-                for (x=x1; x<x2; ++x)
-                {
-                    *sp++ = *bp;  *sp++ = *bp;  *sp++ = *bp;  *sp++ = *bp;
-                    *sp2++ = *bp; *sp2++ = *bp; *sp2++ = *bp; *sp2++ = *bp;
-                    *sp3++ = *bp; *sp3++ = *bp; *sp3++ = *bp; *sp3++ = *bp;
-                    *sp4++ = *bp; *sp4++ = *bp; *sp4++ = *bp; *sp4++ = *bp;
-                    ++bp;
-                }
-                screenp += pitch;
-                screenp2 += pitch;
-                screenp3 += pitch;
-                screenp4 += pitch;
-                bufp += SCREENWIDTH;
-            }
-    
-            SDL_UnlockSurface(screen);
-        }
+        I_InitScale(screens[0], 
+                    (byte *) screen->pixels + (y_offset * screen->pitch)
+                                            + x_offset, 
+                    screen->pitch);
+        scale_function(x1, y1, x2, y2);
+      	SDL_UnlockSurface(screen);
     }
 }
 
@@ -902,10 +825,10 @@
 {
     *windowwidth = SCREENWIDTH * screenmultiply;
 
-    if (fullscreen == FULLSCREEN_LETTERBOX)
-        *windowheight = LETTERBOX_SCREENHEIGHT * screenmultiply;
-    else
+    if (aspect_ratio_correct == RATIO_CORRECT_NONE)
         *windowheight = SCREENHEIGHT * screenmultiply;
+    else
+        *windowheight = SCREENHEIGHT_4_3 * screenmultiply;
 }
 
 // Check if the screen mode for the current settings is in the list available
@@ -988,14 +911,15 @@
 static void AutoAdjustSettings(void)
 {
     int oldw, oldh;
-    int old_fullscreen, old_screenmultiply;
+    int old_ratio, old_screenmultiply;
 
     GetWindowDimensions(&oldw, &oldh);
     old_screenmultiply = screenmultiply;
-    old_fullscreen = fullscreen;
+    old_ratio = aspect_ratio_correct;
 
     if (!CheckValidFSMode() && screenmultiply == 1 
-     && fullscreen == FULLSCREEN_ON)
+     && fullscreen == FULLSCREEN_ON
+     && aspect_ratio_correct == RATIO_CORRECT_NONE)
     {
         // 320x200 is not valid.
 
@@ -1002,13 +926,13 @@
         // Try turning on letterbox mode - avoid doubling up
         // the screen if possible
 
-        fullscreen = FULLSCREEN_LETTERBOX;
+        aspect_ratio_correct = RATIO_CORRECT_LETTERBOX;
 
         if (!CheckValidFSMode())
         {
             // That doesn't work. Change it back.
 
-            fullscreen = FULLSCREEN_ON;
+            aspect_ratio_correct = RATIO_CORRECT_NONE;
         }
     }
 
@@ -1019,14 +943,16 @@
         screenmultiply = 2;
     }
 
-    if (!CheckValidFSMode() && fullscreen == FULLSCREEN_ON)
+    if (!CheckValidFSMode() 
+     && fullscreen == FULLSCREEN_ON
+     && aspect_ratio_correct == RATIO_CORRECT_NONE)
     {
         // This is not a valid mode.  Try turning on letterbox mode
 
-        fullscreen = FULLSCREEN_LETTERBOX;
+        aspect_ratio_correct = RATIO_CORRECT_LETTERBOX;
     }
 
-    if (old_fullscreen != fullscreen 
+    if (old_ratio != aspect_ratio_correct
      || old_screenmultiply != screenmultiply)
     {
         printf("I_InitGraphics: %ix%i resolution is not supported "
@@ -1034,8 +960,8 @@
         printf("I_InitGraphics: Video settings adjusted to "
                "compensate:\n");
         
-        if (fullscreen != old_fullscreen)
-            printf("\tletterbox mode on (fullscreen=2)\n");
+        if (old_ratio != aspect_ratio_correct)
+            printf("\tletterbox mode on (aspect_ratio_correct=2)\n");
         if (screenmultiply != old_screenmultiply)
             printf("\tscreenmultiply=%i\n", screenmultiply);
         
@@ -1102,6 +1028,7 @@
 void I_InitGraphics(void)
 {
     SDL_Event dummy;
+    byte *doompal;
     int flags = 0;
     char *env;
 
@@ -1155,6 +1082,13 @@
 
     CheckCommandLine();
 
+    // Don't allow letterbox mode when windowed
+ 
+    if (!fullscreen && aspect_ratio_correct == RATIO_CORRECT_LETTERBOX)
+    {
+        aspect_ratio_correct = RATIO_CORRECT_NONE;
+    }
+
     if (fullscreen && autoadjust_video_settings)
     {
         // Check that the fullscreen mode we are trying to use is valid;
@@ -1163,6 +1097,17 @@
         AutoAdjustSettings();
     }
 
+    // Generate lookup tables before setting the video mode.
+
+    doompal = W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE);
+
+    if (aspect_ratio_correct == RATIO_CORRECT_STRETCH)
+    {
+        I_InitStretchTables(doompal);
+    }
+
+    // Set the video mode.
+
     GetWindowDimensions(&windowwidth, &windowheight);
 
     flags |= SDL_SWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF;
@@ -1212,7 +1157,7 @@
     
     // Set the palette
 
-    I_SetPalette (W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE));
+    I_SetPalette(doompal);
     SDL_SetColors(screen, palette, 0, 256);
 
     // Setup title and icon
@@ -1240,7 +1185,9 @@
 
     native_surface = !SDL_MUSTLOCK(screen) 
                   && screenmultiply == 1 
-                  && screen->pitch == SCREENWIDTH;
+                  && screen->pitch == SCREENWIDTH
+                  && (aspect_ratio_correct == RATIO_CORRECT_NONE
+                   || aspect_ratio_correct == RATIO_CORRECT_LETTERBOX);
 
     // If not, allocate a buffer and copy from that buffer to the 
     // screen when we do an update
@@ -1249,9 +1196,9 @@
     {
 	screens[0] = (unsigned char *) (screen->pixels);
 
-        if (fullscreen == FULLSCREEN_LETTERBOX)
+        if (aspect_ratio_correct == RATIO_CORRECT_LETTERBOX)
         {
-            screens[0] += ((LETTERBOX_SCREENHEIGHT - SCREENHEIGHT) * screen->pitch) / 2;
+            screens[0] += ((SCREENHEIGHT_4_3 - SCREENHEIGHT) * screen->pitch) / 2;
         }
     }
     else
--- a/src/i_video.h
+++ b/src/i_video.h
@@ -64,6 +64,7 @@
 extern boolean screenvisible;
 extern int screenmultiply;
 extern int fullscreen;
+extern int aspect_ratio_correct;
 extern boolean grabmouse;
 extern float mouse_acceleration;
 extern int mouse_threshold;
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -348,6 +348,7 @@
 {
     {"autoadjust_video_settings",   &autoadjust_video_settings, DEFAULT_INT, 0, 0},
     {"fullscreen",                  &fullscreen, DEFAULT_INT, 0, 0},
+    {"aspect_ratio_correct",        &aspect_ratio_correct, DEFAULT_INT, 0, 0},
     {"startup_delay",               &startup_delay, DEFAULT_INT, 0, 0},
     {"screenmultiply",              &screenmultiply, DEFAULT_INT, 0, 0},
     {"grabmouse",                   &grabmouse, DEFAULT_INT, 0, 0},