shithub: choc

Download patch

ref: 142a9488ad2e782d3d699b4f9b2902ffe26aeaa2
parent: 6805e192226d33c6431b1aaf60e84e1b5ea6f707
author: Simon Howard <fraggle@gmail.com>
date: Sat Jan 19 23:47:52 EST 2008

Refactor the video mode configuration system.
The previous system was built around the program choosing a screen mode
from the user's settings, this is based around choosing settings from
the specified screen mode.
As such, the old screenmultiply config variable is now gone. Also,
aspect ratio correction is now on by default.
Add new aspect ratio correction functions for horizontal squashing (as a
complement to the existing vertical stretching).

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

--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -120,12 +120,13 @@
 #define SCREENWIDTH  320
 #define SCREENHEIGHT 200
 
-// Alternate screenheight for letterbox/aspect ratio corrected mode
+// Screen width used for "squash" scale functions
 
-#define SCREENHEIGHT_4_3 240
+#define SCREENWIDTH_4_3 256
 
+// Screen height used for "stretch" scale functions.
 
-
+#define SCREENHEIGHT_4_3 240
 
 // The maximum number of players, multiplayer/networking.
 #define MAXPLAYERS		4
--- a/src/i_scale.c
+++ b/src/i_scale.c
@@ -26,11 +26,10 @@
 //
 //-----------------------------------------------------------------------------
 
-
-
 #include "doomdef.h"
 #include "doomtype.h"
 
+#include "i_video.h"
 #include "z_zone.h"
 
 // Should be screens[0]
@@ -52,6 +51,10 @@
 
 static byte *stretch_tables[2];
 
+// 50%/50% stretch table, for 800x600 squash mode
+
+static byte *half_stretch_table;
+
 // Called to set the source and destination buffers before doing the
 // scale.
 
@@ -69,7 +72,7 @@
 // 1x scale doesn't really do any scaling: it just copies the buffer
 // a line at a time for when pitch != SCREENWIDTH (!native_surface)
 
-void I_Scale1x(int x1, int y1, int x2, int y2)
+static void I_Scale1x(int x1, int y1, int x2, int y2)
 {
     byte *bufp, *screenp;
     int y;
@@ -88,7 +91,15 @@
     }
 }
 
-void I_Scale2x(int x1, int y1, int x2, int y2)
+screen_mode_t mode_scale_1x = {
+    SCREENWIDTH, SCREENHEIGHT,
+    NULL,
+    I_Scale1x,
+};
+
+// 2x scale (640x400)
+
+static void I_Scale2x(int x1, int y1, int x2, int y2)
 {
     byte *bufp, *screenp, *screenp2;
     int x, y;
@@ -118,7 +129,15 @@
     }
 }
 
-void I_Scale3x(int x1, int y1, int x2, int y2)
+screen_mode_t mode_scale_2x = {
+    SCREENWIDTH * 2, SCREENHEIGHT * 2,
+    NULL,
+    I_Scale2x,
+};
+
+// 3x scale (960x600)
+
+static void I_Scale3x(int x1, int y1, int x2, int y2)
 {
     byte *bufp, *screenp, *screenp2, *screenp3;
     int x, y;
@@ -152,7 +171,15 @@
     }
 }
 
-void I_Scale4x(int x1, int y1, int x2, int y2)
+screen_mode_t mode_scale_3x = {
+    SCREENWIDTH * 3, SCREENHEIGHT * 3,
+    NULL,
+    I_Scale3x,
+};
+
+// 4x scale (1280x800)
+
+static void I_Scale4x(int x1, int y1, int x2, int y2)
 {
     byte *bufp, *screenp, *screenp2, *screenp3, *screenp4;
     int x, y;
@@ -190,7 +217,15 @@
     }
 }
 
-void I_Scale5x(int x1, int y1, int x2, int y2)
+screen_mode_t mode_scale_4x = {
+    SCREENWIDTH * 4, SCREENHEIGHT * 4,
+    NULL,
+    I_Scale4x,
+};
+
+// 5x scale (1600x1000)
+
+static void I_Scale5x(int x1, int y1, int x2, int y2)
 {
     byte *bufp, *screenp, *screenp2, *screenp3, *screenp4, *screenp5;
     int x, y;
@@ -232,6 +267,13 @@
     }
 }
 
+screen_mode_t mode_scale_5x = {
+    SCREENWIDTH * 5, SCREENHEIGHT * 5,
+    NULL,
+    I_Scale5x,
+};
+
+
 // Search through the given palette, finding the nearest color that matches
 // the given color.
 
@@ -301,7 +343,7 @@
 // Called at startup to generate the lookup tables for aspect ratio
 // correcting scale up.
 
-void I_InitStretchTables(byte *palette)
+static void I_InitStretchTables(byte *palette)
 {
     // We only actually need two lookup tables:
     //
@@ -320,6 +362,17 @@
     puts("");
 }
 
+// Create 50%/50% table for 800x600 squash mode
+
+static void I_InitSquashTable(byte *palette)
+{
+    printf("I_InitSquashTable: Generating lookup table..");
+    fflush(stdout);
+    half_stretch_table = GenerateStretchTable(palette, 50);
+    puts("");
+}
+
+
 // 
 // Aspect ratio correcting scale up functions.
 //
@@ -327,7 +380,7 @@
 // screen mode.
 //
 
-static void WriteBlendedLine1x(byte *dest, byte *src1, byte *src2, 
+static inline void WriteBlendedLine1x(byte *dest, byte *src1, byte *src2, 
                                byte *stretch_table)
 {
     int x;
@@ -341,7 +394,9 @@
     }
 } 
 
-void I_Stretch1x(int x1, int y1, int x2, int y2)
+// 1x stretch (320x240)
+
+static void I_Stretch1x(int x1, int y1, int x2, int y2)
 {
     byte *bufp, *screenp;
     int y;
@@ -389,7 +444,14 @@
     }
 }
 
-static void WriteLine2x(byte *dest, byte *src)
+screen_mode_t mode_stretch_1x = {
+    SCREENWIDTH, SCREENHEIGHT_4_3,
+    I_InitStretchTables,
+    I_Stretch1x,
+    true,
+};
+
+static inline void WriteLine2x(byte *dest, byte *src)
 {
     int x;
 
@@ -402,7 +464,7 @@
     }
 }
 
-static void WriteBlendedLine2x(byte *dest, byte *src1, byte *src2, 
+static inline void WriteBlendedLine2x(byte *dest, byte *src1, byte *src2, 
                                byte *stretch_table)
 {
     int x;
@@ -419,9 +481,9 @@
     }
 } 
 
-// Scale 2x, correcting aspect ratio in the process
+// 2x stretch (640x480)
 
-void I_Stretch2x(int x1, int y1, int x2, int y2)
+static void I_Stretch2x(int x1, int y1, int x2, int y2)
 {
     byte *bufp, *screenp;
     int y;
@@ -493,7 +555,13 @@
     }
 }
 
-static void WriteLine3x(byte *dest, byte *src)
+screen_mode_t mode_stretch_2x = {
+    SCREENWIDTH * 2, SCREENHEIGHT_4_3 * 2,
+    I_InitStretchTables,
+    I_Stretch2x,
+};
+
+static inline void WriteLine3x(byte *dest, byte *src)
 {
     int x;
 
@@ -507,7 +575,7 @@
     }
 }
 
-static void WriteBlendedLine3x(byte *dest, byte *src1, byte *src2, 
+static inline void WriteBlendedLine3x(byte *dest, byte *src1, byte *src2, 
                                byte *stretch_table)
 {
     int x;
@@ -525,7 +593,9 @@
     }
 } 
 
-void I_Stretch3x(int x1, int y1, int x2, int y2)
+// 3x stretch (960x720)
+
+static void I_Stretch3x(int x1, int y1, int x2, int y2)
 {
     byte *bufp, *screenp;
     int y;
@@ -621,7 +691,13 @@
     }
 }
 
-static void WriteLine4x(byte *dest, byte *src)
+screen_mode_t mode_stretch_3x = {
+    SCREENWIDTH * 3, SCREENHEIGHT_4_3 * 3,
+    I_InitStretchTables,
+    I_Stretch3x,
+};
+
+static inline void WriteLine4x(byte *dest, byte *src)
 {
     int x;
 
@@ -636,7 +712,7 @@
     }
 }
 
-static void WriteBlendedLine4x(byte *dest, byte *src1, byte *src2, 
+static inline void WriteBlendedLine4x(byte *dest, byte *src1, byte *src2, 
                                byte *stretch_table)
 {
     int x;
@@ -655,7 +731,9 @@
     }
 } 
 
-void I_Stretch4x(int x1, int y1, int x2, int y2)
+// 4x stretch (1280x960)
+
+static void I_Stretch4x(int x1, int y1, int x2, int y2)
 {
     byte *bufp, *screenp;
     int y;
@@ -775,7 +853,13 @@
     }
 }
 
-static void WriteLine5x(byte *dest, byte *src)
+screen_mode_t mode_stretch_4x = {
+    SCREENWIDTH * 4, SCREENHEIGHT_4_3 * 4,
+    I_InitStretchTables,
+    I_Stretch4x,
+};
+
+static inline void WriteLine5x(byte *dest, byte *src)
 {
     int x;
 
@@ -791,7 +875,9 @@
     }
 }
 
-void I_Stretch5x(int x1, int y1, int x2, int y2)
+// 5x stretch (1600x1200)
+
+static void I_Stretch5x(int x1, int y1, int x2, int y2)
 {
     byte *bufp, *screenp;
     int y;
@@ -838,4 +924,435 @@
         screenp += dest_pitch; bufp += SCREENWIDTH;
     }
 }
+
+screen_mode_t mode_stretch_5x = {
+    SCREENWIDTH * 5, SCREENHEIGHT_4_3 * 5,
+    I_InitStretchTables,
+    I_Stretch5x,
+};
+
+//
+// Aspect ratio correcting "squash" functions. 
+//
+// These do the opposite of the "stretch" functions above: while the
+// stretch functions increase the vertical dimensions, the squash
+// functions decrease the horizontal dimensions for the same result.
+//
+// The same blend tables from the stretch functions are reused; as 
+// a result, the dimensions are *slightly* wrong (eg. 320x200 should
+// squash to 266x200, but actually squashes to 256x200).
+//
+
+// 
+// 1x squashed scale (256x200)
+//
+
+static inline void WriteSquashedLine1x(byte *dest, byte *src)
+{
+    int x;
+
+    for (x=0; x<SCREENWIDTH; )
+    {
+        // Draw in blocks of 5
+
+        // 80% pixel 0,   20% pixel 1
+
+        *dest++ = stretch_tables[0][src[1] * 256 + src[0]];
+
+        // 60% pixel 1,   40% pixel 2
+
+        *dest++ = stretch_tables[1][src[2] * 256 + src[1]];
+
+        // 40% pixel 2,   60% pixel 3
+
+        *dest++ = stretch_tables[1][src[2] * 256 + src[3]];
+
+        // 20% pixel 3,   80% pixel 4
+
+        *dest++ = stretch_tables[0][src[3] * 256 + src[4]];
+
+        x += 5;
+        src += 5;
+    }
+}
+
+// 1x squashed (256x200)
+
+static void I_Squash1x(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;
+    }    
+
+    bufp = src_buffer;
+    screenp = (byte *) dest_buffer;
+
+    for (y=0; y<SCREENHEIGHT; ++y) 
+    {
+        WriteSquashedLine1x(screenp, bufp);
+
+        screenp += dest_pitch;
+        bufp += SCREENWIDTH;
+    }
+}
+
+screen_mode_t mode_squash_1x = {
+    SCREENWIDTH_4_3, SCREENHEIGHT,
+    I_InitStretchTables,
+    I_Squash1x,
+    true,
+};
+
+
+//
+// 2x squashed scale (512x400)
+//
+
+#define DRAW_PIXEL2 \
+      *dest++ = *dest2++ = c;
+
+static inline void WriteSquashedLine2x(byte *dest, byte *src)
+{
+    byte *dest2;
+    int x, c;
+
+    dest2 = dest + dest_pitch;
+
+    for (x=0; x<SCREENWIDTH; )
+    {
+        // Draw in blocks of 5
+
+        // 100% pixel 0
+
+        c = src[0];
+        DRAW_PIXEL2;
+
+        // 60% pixel 0, 40% pixel 1
+
+        c = stretch_tables[1][src[1] * 256 + src[0]];
+        DRAW_PIXEL2;
+
+        // 100% pixel 1
+
+        c = src[1];
+        DRAW_PIXEL2;
+
+        // 20% pixel 1, 80% pixel 2
+
+        c = stretch_tables[0][src[1] * 256 + src[2]];
+        DRAW_PIXEL2;
+
+        // 80% pixel 2, 20% pixel 3
+
+        c = stretch_tables[0][src[3] * 256 + src[2]];
+        DRAW_PIXEL2;
+
+        // 100% pixel 3
+
+        c = src[3];
+        DRAW_PIXEL2;
+
+        // 40% pixel 3, 60% pixel 4
+
+        c = stretch_tables[1][src[3] * 256 + src[4]];
+        DRAW_PIXEL2;
+
+        // 100% pixel 4
+
+        c = src[4];
+        DRAW_PIXEL2;
+
+        x += 5;
+        src += 5;
+    }
+}
+
+// 2x squash (512x400)
+
+static void I_Squash2x(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;
+    }    
+
+    bufp = src_buffer;
+    screenp = (byte *) dest_buffer;
+
+    for (y=0; y<SCREENHEIGHT; ++y) 
+    {
+        WriteSquashedLine2x(screenp, bufp);
+
+        screenp += dest_pitch * 2;
+        bufp += SCREENWIDTH;
+    }
+}
+
+screen_mode_t mode_squash_2x = {
+    SCREENWIDTH_4_3 * 2, SCREENHEIGHT * 2,
+    I_InitStretchTables,
+    I_Squash2x,
+};
+
+
+#define DRAW_PIXEL3 \
+        *dest++ = *dest2++ = *dest3++ = c
+
+static inline void WriteSquashedLine3x(byte *dest, byte *src)
+{
+    byte *dest2, *dest3;
+    int x, c;
+
+    dest2 = dest + dest_pitch;
+    dest3 = dest + dest_pitch * 2;
+
+    for (x=0; x<SCREENWIDTH; )
+    {
+        // Every 2 pixels is expanded to 5 pixels
+
+        // 100% pixel 0 x2
+
+        c = src[0];
+
+        DRAW_PIXEL3;
+        DRAW_PIXEL3;
+
+        // 50% pixel 0, 50% pixel 1
+
+        c = half_stretch_table[src[0] * 256 + src[1]];
+
+        DRAW_PIXEL3;
+
+        // 100% pixel 1
+
+        c = src[1];
+
+        DRAW_PIXEL3;
+        DRAW_PIXEL3;
+
+        x += 2;
+        src += 2;
+    }
+}
+
+
+//
+// 3x scale squashed (800x600)
+//
+// This is a special case that uses the half_stretch_table (50%) rather
+// than the normal stretch_tables(20,40%), to scale up to 800x600 
+// exactly.
+//
+
+static void I_Squash3x(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;
+    }    
+
+    bufp = src_buffer;
+    screenp = (byte *) dest_buffer;
+
+    for (y=0; y<SCREENHEIGHT; ++y) 
+    {
+        WriteSquashedLine3x(screenp, bufp);
+
+        screenp += dest_pitch * 3;
+        bufp += SCREENWIDTH;
+    }
+}
+
+screen_mode_t mode_squash_3x = {
+    800, 600,
+    I_InitSquashTable,
+    I_Squash3x,
+};
+
+#define DRAW_PIXEL4 \
+        *dest++ = *dest2++ = *dest3++ = *dest4++ = c;
+      
+static inline void WriteSquashedLine4x(byte *dest, byte *src)
+{
+    int x;
+    int c;
+    byte *dest2, *dest3, *dest4;
+
+    dest2 = dest + dest_pitch;
+    dest3 = dest + dest_pitch * 2;
+    dest4 = dest + dest_pitch * 3;
+
+    for (x=0; x<SCREENWIDTH; )
+    {
+        // Draw in blocks of 5
+
+        // 100% pixel 0  x3
+
+        c = src[0];
+        DRAW_PIXEL4;
+        DRAW_PIXEL4;
+        DRAW_PIXEL4;
+
+        // 20% pixel 0,  80% pixel 1
+
+        c = stretch_tables[0][src[0] * 256 + src[1]];
+        DRAW_PIXEL4;
+
+        // 100% pixel 1 x2
+
+        c = src[1];
+        DRAW_PIXEL4;
+        DRAW_PIXEL4;
+
+        // 40% pixel 1, 60% pixel 2
+
+        c = stretch_tables[1][src[1] * 256 + src[2]];
+        DRAW_PIXEL4;
+
+        // 100% pixel 2 x2
+
+        c = src[2];
+        DRAW_PIXEL4;
+        DRAW_PIXEL4;
+
+        // 60% pixel 2, 40% pixel 3
+
+        c = stretch_tables[1][src[3] * 256 + src[2]];
+        DRAW_PIXEL4;
+
+        // 100% pixel 3 x2
+
+        c = src[3];
+        DRAW_PIXEL4;
+        DRAW_PIXEL4;
+
+        // 80% pixel 3, 20% pixel 4
+
+        c = stretch_tables[0][src[4] * 256 + src[3]];
+        DRAW_PIXEL4;
+
+        // 100% pixel 4
+
+        c = src[4];
+        DRAW_PIXEL4;
+        DRAW_PIXEL4;
+        DRAW_PIXEL4;
+
+        x += 5;
+        src += 5;
+    }
+}
+
+//
+// 4x squashed (1024x800)
+//
+
+static void I_Squash4x(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;
+    }    
+
+    bufp = src_buffer;
+    screenp = (byte *) dest_buffer;
+
+    for (y=0; y<SCREENHEIGHT; ++y) 
+    {
+        WriteSquashedLine4x(screenp, bufp);
+
+        screenp += dest_pitch * 4;
+        bufp += SCREENWIDTH;
+    }
+}
+
+screen_mode_t mode_squash_4x = {
+    SCREENWIDTH_4_3 * 4, SCREENHEIGHT * 4,
+    I_InitStretchTables,
+    I_Squash4x,
+};
+
+#define DRAW_PIXEL5 \
+        *dest++ = *dest2++ = *dest3++ = *dest4++ = *dest5++ = c
+
+static inline void WriteSquashedLine5x(byte *dest, byte *src)
+{
+    int x;
+    int c;
+    byte *dest2, *dest3, *dest4, *dest5;
+
+    dest2 = dest + dest_pitch;
+    dest3 = dest + dest_pitch * 2;
+    dest4 = dest + dest_pitch * 3;
+    dest5 = dest + dest_pitch * 4;
+
+    for (x=0; x<SCREENWIDTH; ++x)
+    {
+        // Draw in blocks of 5
+
+        // 100% pixel 0  x4
+
+        c = *src++;
+        DRAW_PIXEL5;
+        DRAW_PIXEL5;
+        DRAW_PIXEL5;
+        DRAW_PIXEL5;
+    }
+}
+
+//
+// 5x squashed (1280x1000)
+//
+
+static void I_Squash5x(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;
+    }    
+
+    bufp = src_buffer;
+    screenp = (byte *) dest_buffer;
+
+    for (y=0; y<SCREENHEIGHT; ++y) 
+    {
+        WriteSquashedLine5x(screenp, bufp);
+
+        screenp += dest_pitch * 5;
+        bufp += SCREENWIDTH;
+    }
+}
+
+screen_mode_t mode_squash_5x = {
+    SCREENWIDTH_4_3 * 5, SCREENHEIGHT * 5,
+    I_InitStretchTables,
+    I_Squash5x,
+};
+
 
--- a/src/i_scale.h
+++ b/src/i_scale.h
@@ -30,24 +30,31 @@
 
 #include "doomtype.h"
 
-void I_InitStretchTables(byte *palette);
 void I_InitScale(byte *_src_buffer, byte *_dest_buffer, int _dest_pitch);
 
-// Normal pixel-perfect doubling functions.
+// Scaled modes (direct multiples of 320x200)
 
-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_Scale5x(int x1, int y1, int x2, int y2);
+extern screen_mode_t mode_scale_1x;
+extern screen_mode_t mode_scale_2x;
+extern screen_mode_t mode_scale_3x;
+extern screen_mode_t mode_scale_4x;
+extern screen_mode_t mode_scale_5x;
 
-// Aspect ratio correcting scale up functions
+// Vertically stretched modes (320x200 -> multiples of 320x240)
 
-void I_Stretch1x(int x1, int y1, int x2, int y2);
-void I_Stretch2x(int x1, int y1, int x2, int y2);
-void I_Stretch3x(int x1, int y1, int x2, int y2);
-void I_Stretch4x(int x1, int y1, int x2, int y2);
-void I_Stretch5x(int x1, int y1, int x2, int y2);
+extern screen_mode_t mode_stretch_1x;
+extern screen_mode_t mode_stretch_2x;
+extern screen_mode_t mode_stretch_3x;
+extern screen_mode_t mode_stretch_4x;
+extern screen_mode_t mode_stretch_5x;
+
+// Horizontally squashed modes (320x200 -> multiples of 256x200)
+
+extern screen_mode_t mode_squash_1x;
+extern screen_mode_t mode_squash_2x;
+extern screen_mode_t mode_squash_3x;
+extern screen_mode_t mode_squash_4x;
+extern screen_mode_t mode_squash_5x;
 
 #endif /* #ifndef __I_SCALE__ */
 
--- a/src/i_video.c
+++ b/src/i_video.c
@@ -38,10 +38,11 @@
 #include "doomstat.h"
 #include "d_main.h"
 #include "i_joystick.h"
-#include "i_scale.h"
 #include "i_system.h"
 #include "i_swap.h"
 #include "i_timer.h"
+#include "i_video.h"
+#include "i_scale.h"
 #include "m_argv.h"
 #include "s_sound.h"
 #include "sounds.h"
@@ -49,17 +50,35 @@
 #include "w_wad.h"
 #include "z_zone.h"
 
-enum
-{
-    FULLSCREEN_OFF,
-    FULLSCREEN_ON,
+// Non aspect ratio-corrected modes (direct multiples of 320x200)
+
+static screen_mode_t *screen_modes[] = {
+    &mode_scale_1x,
+    &mode_scale_2x,
+    &mode_scale_3x,
+    &mode_scale_4x,
+    &mode_scale_5x,
 };
 
-enum
-{
-    RATIO_CORRECT_NONE,
-    RATIO_CORRECT_LETTERBOX,
-    RATIO_CORRECT_STRETCH,
+// Aspect ratio corrected modes (4:3 ratio)
+
+static screen_mode_t *screen_modes_corrected[] = {
+
+    // Vertically stretched modes (320x200 -> 320x240 and multiples)
+
+    &mode_stretch_1x,
+    &mode_stretch_2x,
+    &mode_stretch_3x,
+    &mode_stretch_4x,
+    &mode_stretch_5x,
+
+    // Horizontally squashed modes (320x200 -> 256x200 and multiples)
+
+    &mode_squash_1x,
+    &mode_squash_2x,
+    &mode_squash_3x,
+    &mode_squash_4x,
+    &mode_squash_5x,
 };
 
 extern void M_QuitDOOM();
@@ -71,6 +90,7 @@
 static SDL_Surface *screen;
 
 // palette
+
 static SDL_Color palette[256];
 static boolean palette_to_set;
 
@@ -89,6 +109,11 @@
 
 static boolean native_surface;
 
+// Screen width and height, from configuration file.
+
+int screen_width = SCREENWIDTH;
+int screen_height = SCREENHEIGHT;
+
 // Automatically adjust video settings if the selected mode is 
 // not a valid video mode.
 
@@ -96,11 +121,11 @@
 
 // Run in full screen mode?  (int type for config code)
 
-int fullscreen = FULLSCREEN_ON;
+int fullscreen = true;
 
 // Aspect ratio correction mode
 
-int aspect_ratio_correct = RATIO_CORRECT_NONE;
+int aspect_ratio_correct = true;
 
 // Time to wait for the screen to settle on startup before starting the
 // game (ms)
@@ -116,13 +141,6 @@
 
 boolean screenvisible;
 
-// Blocky mode,
-// replace each 320x200 pixel with screenmultiply*screenmultiply pixels.
-// According to Dave Taylor, it still is a bonehead thing
-// to use ....
-
-int screenmultiply = 1;
-
 // disk image data and background overwritten by the disk to be
 // restored by EndRead
 
@@ -135,6 +153,10 @@
 
 static SDL_Cursor *cursors[2];
 
+// The screen mode and scale functions being used
+
+static screen_mode_t *screen_mode;
+
 // If true, keyboard mapping is ignored, like in Vanilla Doom.
 // The sensible thing to do is to disable this if you have a non-US
 // keyboard.
@@ -168,7 +190,7 @@
     // always grab the mouse when full screen (dont want to 
     // see the mouse pointer)
 
-    if (fullscreen != FULLSCREEN_OFF)
+    if (fullscreen)
         return true;
 
     // Don't grab the mouse if mouse input is disabled
@@ -624,73 +646,10 @@
 	return;
     }
 
-    if (aspect_ratio_correct == RATIO_CORRECT_LETTERBOX)
-    {
-        x_offset = (screen->w - SCREENWIDTH * screenmultiply) / 2;
-        y_offset = (screen->h - SCREENHEIGHT * screenmultiply) / 2;
-    }
-    else
-    {
-        x_offset = 0;
-        y_offset = 0;
-    }
+    x_offset = (screen->w - screen_mode->width) / 2;
+    y_offset = (screen->h - screen_mode->height) / 2;
+    scale_function = screen_mode->DrawScreen;
 
-    if (aspect_ratio_correct == RATIO_CORRECT_STRETCH)
-    {
-        if (screenmultiply == 1)
-        {
-            scale_function = I_Stretch1x;
-        }
-        else if (screenmultiply == 2)
-        { 
-            scale_function = I_Stretch2x;
-        }
-        else if (screenmultiply == 3)
-        { 
-            scale_function = I_Stretch3x;
-        }
-        else if (screenmultiply == 4)
-        { 
-            scale_function = I_Stretch4x;
-        }
-        else if (screenmultiply == 5)
-        {
-            scale_function = I_Stretch5x;
-        }
-        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 if (screenmultiply == 5)
-        {
-            scale_function = I_Scale5x;
-        }
-        else
-        {
-            I_Error("No scale function found!");
-            return;
-        }
-    }
-
     if (SDL_LockSurface(screen) >= 0)
     {
         I_InitScale(screens[0], 
@@ -710,11 +669,15 @@
 
     // Update the area
 
+/*
+    TODO: fix loading disk
+
     SDL_UpdateRect(screen, 
                    x1 * screenmultiply, 
                    y1 * screenmultiply, 
                    (x2-x1) * screenmultiply, 
                    (y2-y1) * screenmultiply);
+*/
 }
 
 void I_BeginRead(void)
@@ -889,43 +852,6 @@
     SDL_FreeSurface(surface);
 }
 
-// Get window dimensions for the current settings
-
-static void GetWindowDimensions(int *windowwidth, int *windowheight)
-{
-    *windowwidth = SCREENWIDTH * screenmultiply;
-
-    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
-// Not all machines support running in 320x200/640x400 (only support 4:3)
-// Some don't even support modes below 640x480.
-
-static boolean CheckValidFSMode(void)
-{
-    SDL_Rect **modes;
-    int i;
-    int w, h;
-
-    GetWindowDimensions(&w, &h);
-
-    modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
-
-    for (i=0; modes[i]; ++i)
-    {
-        if (w == modes[i]->w && h == modes[i]->h)
-            return true;
-    }
-
-    // not found
-
-    return false;
-}
-
 static void CheckCommandLine(void)
 {
     //!
@@ -961,7 +887,7 @@
 
     if (M_CheckParm("-window") || M_CheckParm("-nofullscreen"))
     {
-        fullscreen = FULLSCREEN_OFF;
+        fullscreen = false;
     }
 
     //!
@@ -972,7 +898,7 @@
 
     if (M_CheckParm("-fullscreen"))
     {
-        fullscreen = FULLSCREEN_ON;
+        fullscreen = true;
     }
 
     //!
@@ -983,138 +909,207 @@
 
     nomouse = M_CheckParm("-nomouse") > 0;
 
-    // 2x, 3x, 4x, 5x scale mode
- 
-    //!
-    // @category video 
-    //
-    // Don't scale up the screen.
-    //
+}
 
-    if (M_CheckParm("-1"))
+// Pick the modes list to use:
+
+static void GetScreenModes(screen_mode_t ***modes_list, int *num_modes)
+{
+    if (aspect_ratio_correct)
     {
-        screenmultiply = 1;
+        *modes_list = screen_modes_corrected;
+        *num_modes = arrlen(screen_modes_corrected);
     }
-
-    //!
-    // @category video 
-    //
-    // Double up the screen to 2x its size.
-    //
-
-    if (M_CheckParm("-2"))
+    else
     {
-        screenmultiply = 2;
+        *modes_list = screen_modes;
+        *num_modes = arrlen(screen_modes);
     }
+}
 
-    //!
-    // @category video 
-    //
-    // Double up the screen to 3x its size.
-    //
+// Find which screen_mode_t to use for the given width and height.
 
-    if (M_CheckParm("-3"))
-    {
-        screenmultiply = 3;
-    }
+static screen_mode_t *I_FindScreenMode(int w, int h)
+{
+    screen_mode_t **modes_list;
+    screen_mode_t *best_mode;
+    int modes_list_length;
+    int num_pixels;
+    int best_num_pixels;
+    int i;
 
-    //!
-    // @category video 
-    //
-    // Double up the screen to 4x its size.
-    //
+    // Special case: 320x200 and 640x400 are available even if aspect 
+    // ratio correction is turned on.  These modes have non-square
+    // pixels.
 
-    if (M_CheckParm("-4"))
+    if (w == SCREENWIDTH && h == SCREENHEIGHT)
     {
-        screenmultiply = 4;
+        return &mode_scale_1x;
     }
+    else if (w == SCREENWIDTH*2 && h == SCREENHEIGHT*2)
+    {
+        return &mode_scale_2x;
+    }
 
-    //!
-    // @category video
-    //
-    // Double up the screen to 5x its size.
-    //
+    GetScreenModes(&modes_list, &modes_list_length);
 
-    if (M_CheckParm("-5"))
+    // Find the biggest screen_mode_t in the list that fits within these 
+    // dimensions
+
+    best_mode = NULL;
+    best_num_pixels = 0;
+
+    for (i=0; i<modes_list_length; ++i) 
     {
-        screenmultiply = 5;
+        // Will this fit within the dimensions? If not, ignore.
+
+        if (modes_list[i]->width > w || modes_list[i]->height > h)
+        {
+            continue;
+        }
+
+        num_pixels = modes_list[i]->width * modes_list[i]->height;
+
+        if (num_pixels > best_num_pixels)
+        {
+            // This is a better mode than the current one
+
+            best_mode = modes_list[i];
+            best_num_pixels = num_pixels;
+        }
     }
 
-    if (screenmultiply < 1)
-        screenmultiply = 1;
-    if (screenmultiply > 5)
-        screenmultiply = 5;
+    return best_mode;
 }
 
-static void AutoAdjustSettings(void)
+// If the video mode set in the configuration file is not available,
+// try to choose a different mode.
+
+static void I_AutoAdjustSettings(void)
 {
-    int oldw, oldh;
-    int old_ratio, old_screenmultiply;
+    if (fullscreen)
+    {
+        SDL_Rect **modes;
+        SDL_Rect *best_mode;
+        screen_mode_t *screen_mode;
+        int num_pixels, best_num_pixels;
+        int i;
 
-    GetWindowDimensions(&oldw, &oldh);
-    old_screenmultiply = screenmultiply;
-    old_ratio = aspect_ratio_correct;
+        modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
 
-    if (!CheckValidFSMode() && screenmultiply == 1 
-     && fullscreen == FULLSCREEN_ON
-     && aspect_ratio_correct == RATIO_CORRECT_NONE)
-    {
-        // 320x200 is not valid.
+        // Find the best mode that matches the mode specified in the
+        // configuration file
 
-        // Try turning on letterbox mode - avoid doubling up
-        // the screen if possible
+        best_mode = NULL;
+        best_num_pixels = INT_MAX;
 
-        aspect_ratio_correct = RATIO_CORRECT_LETTERBOX;
+        for (i=0; modes[i] != NULL; ++i) 
+        {
+            //printf("%ix%i?\n", modes[i]->w, modes[i]->h);
 
-        if (!CheckValidFSMode())
+            // What screen_mode_t would be used for this video mode?
+
+            screen_mode = I_FindScreenMode(modes[i]->w, modes[i]->h);
+
+            // Never choose a screen mode that we cannot run in, or
+            // is poor quality for fullscreen
+
+            if (screen_mode == NULL || screen_mode->poor_quality)
+            {
+            //    printf("\tUnsupported / poor quality\n");
+                continue;
+            }
+
+            // Do we have the exact mode?
+            // If so, no autoadjust needed
+
+            if (screen_width == modes[i]->w && screen_height == modes[i]->h)
+            {
+            //    printf("\tExact mode!\n");
+                return;
+            }
+
+            // Only use modes bigger than the specified mode
+
+            if (modes[i]->w < screen_width || modes[i]->h < screen_height)
+            {
+            //    printf("\t< %ix%i\n", screen_width, screen_height);
+                continue;
+            }
+
+            // Is this mode better than the current mode?
+
+            num_pixels = modes[i]->w * modes[i]->h;
+
+            if (num_pixels < best_num_pixels)
+            {
+            //    printf("\tA valid mode\n");
+                best_num_pixels = num_pixels;
+                best_mode = modes[i];
+            }
+        }
+
+        if (best_mode == NULL)
         {
-            // That doesn't work. Change it back.
+            // Unable to find a valid mode!
 
-            aspect_ratio_correct = RATIO_CORRECT_NONE;
+            if (screen_width <= SCREENWIDTH && screen_height <= SCREENHEIGHT)
+            {
+                I_Error("Unable to find any valid video mode at all!");
+            }
+
+            // Reset back to the original defaults and try to find a
+            // mode
+
+            screen_width = SCREENWIDTH;
+            screen_height = SCREENHEIGHT;
+
+            I_AutoAdjustSettings();
+          
+            return;
         }
-    }
 
-    if (!CheckValidFSMode() && screenmultiply == 1)
-    {
-        // Try doubling up the screen to 640x400
+        printf("I_InitGraphics: %ix%i mode not supported on this machine.\n",
+               screen_width, screen_height);
 
-        screenmultiply = 2;
-    }
+        screen_width = best_mode->w;
+        screen_height = best_mode->h;
 
-    if (!CheckValidFSMode() 
-     && fullscreen == FULLSCREEN_ON
-     && aspect_ratio_correct == RATIO_CORRECT_NONE)
+    }
+    else
     {
-        // This is not a valid mode.  Try turning on letterbox mode
+        screen_mode_t *best_mode;
 
-        aspect_ratio_correct = RATIO_CORRECT_LETTERBOX;
-    }
+        //
+        // Windowed mode.
+        //
+        // Find a screen_mode_t to fit within the current settings
+        //
 
-    if (old_ratio != aspect_ratio_correct
-     || old_screenmultiply != screenmultiply)
-    {
-        printf("I_InitGraphics: %ix%i resolution is not supported "
-               "on this machine. \n", oldw, oldh);
-        printf("I_InitGraphics: Video settings adjusted to "
-               "compensate:\n");
-        
-        if (old_ratio != aspect_ratio_correct)
-            printf("\tletterbox mode on (aspect_ratio_correct=%i)\n",
-                   aspect_ratio_correct);
-        if (screenmultiply != old_screenmultiply)
-            printf("\tscreenmultiply=%i\n", screenmultiply);
-        
-        printf("NOTE: Your video settings have been adjusted.  "
-               "To disable this behavior,\n"
-               "set autoadjust_video_settings to 0 in your "
-               "configuration file.\n");
+        best_mode = I_FindScreenMode(screen_width, screen_height);
+
+        // Do we have the exact mode already?
+
+        if (best_mode->width == screen_width 
+         && best_mode->height == screen_height)
+        {
+            return;
+        }
+
+        printf("I_InitGraphics: Cannot run at specified mode: %ix%i\n",
+               screen_width, screen_height);
+
+        screen_width = best_mode->width;
+        screen_height = best_mode->height;
     }
-    
-    if (!CheckValidFSMode())
-    {
-        printf("I_InitGraphics: WARNING: Unable to find a valid "
-               "fullscreen video mode to run in.\n");
-    }
+
+    printf("I_InitGraphics: Auto-adjusted to %ix%i.\n",
+           screen_width, screen_height);
+
+    printf("NOTE: Your video settings have been adjusted.  "
+           "To disable this behavior,\n"
+           "set autoadjust_video_settings to 0 in your "
+           "configuration file.\n");
 }
 
 // Check if we have been invoked as a screensaver by xscreensaver.
@@ -1131,24 +1126,6 @@
     }
 }
 
-// In screensaver mode, pick a screenmultiply value that fits
-// inside the screen.  It is okay to do this because settings
-// are not saved in screensaver mode.
-
-static void FindScreensaverMultiply(void)
-{
-    int i;
-
-    for (i=1; i<=4; ++i)
-    {
-        if (SCREENWIDTH * i <= screen->w
-         && SCREENHEIGHT * i <= screen->h)
-        {
-            screenmultiply = i;
-        }
-    }
-}
-
 static void CreateCursors(void)
 {
     static Uint8 empty_cursor_data = 0;
@@ -1245,47 +1222,56 @@
 
     CheckCommandLine();
 
-    // Don't allow letterbox mode when windowed
- 
-    if (!fullscreen && aspect_ratio_correct == RATIO_CORRECT_LETTERBOX)
+    doompal = W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE);
+
+    if (screensaver_mode)
     {
-        aspect_ratio_correct = RATIO_CORRECT_NONE;
+        windowwidth = 0;
+        windowheight = 0;
     }
-
-    if (fullscreen && autoadjust_video_settings)
+    else
     {
-        // Check that the fullscreen mode we are trying to use is valid;
-        // if not, try to automatically select a more appropriate one.
+        if (autoadjust_video_settings)
+        {
+            I_AutoAdjustSettings();
+        }
 
-        AutoAdjustSettings();
-    }
+        windowwidth = screen_width;
+        windowheight = screen_height;
 
-    // Generate lookup tables before setting the video mode.
+        screen_mode = I_FindScreenMode(windowwidth, windowheight);
 
-    doompal = W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE);
+        if (screen_mode == NULL)
+        {
+            I_Error("I_InitGraphics: Unable to find a screen mode small "
+                    "enough for %ix%i", windowwidth, windowheight);
+        }
 
-    if (aspect_ratio_correct == RATIO_CORRECT_STRETCH)
-    {
-        I_InitStretchTables(doompal);
+        if (windowwidth != screen_mode->width
+         || windowheight != screen_mode->height)
+        {
+            printf("I_InitGraphics: Letterboxed (%ix%i within %ix%i)\n",
+                   screen_mode->width, screen_mode->height,
+                   windowwidth, windowheight);
+        }
+
+        // Generate lookup tables before setting the video mode.
+
+        if (screen_mode->InitMode != NULL)
+        {
+            screen_mode->InitMode(doompal);
+        }
     }
 
     // Set the video mode.
 
-    GetWindowDimensions(&windowwidth, &windowheight);
-
     flags |= SDL_SWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF;
 
-    if (fullscreen != FULLSCREEN_OFF)
+    if (fullscreen)
     {
         flags |= SDL_FULLSCREEN;
     }
 
-    if (screensaver_mode)
-    {
-        windowwidth = 0;
-        windowheight = 0;
-    }
-
     screen = SDL_SetVideoMode(windowwidth, windowheight, 8, flags);
 
     if (screen == NULL)
@@ -1293,14 +1279,6 @@
         I_Error("Error setting video mode: %s\n", SDL_GetError());
     }
 
-    // In screensaver mode, screenmultiply as large as possible
-    // and set a blank cursor.
-
-    if (screensaver_mode)
-    {
-        FindScreensaverMultiply();
-    }
-
     // Start with a clear black screen
     // (screen will be flipped after we set the palette)
 
@@ -1316,7 +1294,7 @@
 
         SDL_UnlockSurface(screen);
     }
-    
+
     // Set the palette
 
     I_SetPalette(doompal);
@@ -1332,6 +1310,26 @@
     UpdateFocus();
     UpdateGrab();
 
+    // In screensaver mode, now find a screen_mode to use.
+
+    if (screensaver_mode)
+    {
+        screen_mode = I_FindScreenMode(screen->w, screen->h);
+
+        if (screen_mode == NULL)
+        {
+            I_Error("I_InitGraphics: Unable to find a screen mode small "
+                    "enough for %ix%i", screen->w, screen->h);
+        }
+
+        // Generate lookup tables before setting the video mode.
+
+        if (screen_mode->InitMode != NULL)
+        {
+            screen_mode->InitMode(doompal);
+        }
+    }
+    
     // On some systems, it takes a second or so for the screen to settle
     // after changing modes.  We include the option to add a delay when
     // setting the screen mode, so that the game doesn't start immediately
@@ -1348,10 +1346,9 @@
     // If we have to multiply, drawing is done to a separate 320x200 buf
 
     native_surface = !SDL_MUSTLOCK(screen) 
-                  && screenmultiply == 1 
+                  && screen_mode == &mode_scale_1x
                   && screen->pitch == SCREENWIDTH
-                  && (aspect_ratio_correct == RATIO_CORRECT_NONE
-                   || aspect_ratio_correct == RATIO_CORRECT_LETTERBOX);
+                  && aspect_ratio_correct;
 
     // If not, allocate a buffer and copy from that buffer to the 
     // screen when we do an update
@@ -1358,25 +1355,23 @@
 
     if (native_surface)
     {
-	screens[0] = (unsigned char *) (screen->pixels);
+	screens[0] = (unsigned char *) screen->pixels;
 
-        if (aspect_ratio_correct == RATIO_CORRECT_LETTERBOX)
-        {
-            screens[0] += ((SCREENHEIGHT_4_3 - SCREENHEIGHT) * screen->pitch) / 2;
-        }
+        screens[0] += (screen->h - SCREENHEIGHT) / 2;
     }
     else
     {
-	screens[0] = (unsigned char *) Z_Malloc (SCREENWIDTH * SCREENHEIGHT, PU_STATIC, NULL);
+	screens[0] = (unsigned char *) Z_Malloc (SCREENWIDTH * SCREENHEIGHT, 
+                                                 PU_STATIC, NULL);
+
+        // Clear the screen to black.
+
+        memset(screens[0], 0, SCREENWIDTH * SCREENHEIGHT);
     }
 
     // "Loading from disk" icon
 
     LoadDiskImage();
-
-    // Clear the screen to black.
-
-    memset(screens[0], 0, SCREENWIDTH * SCREENHEIGHT);
 
     // We need SDL to give us translated versions of keys as well
 
--- a/src/i_video.h
+++ b/src/i_video.h
@@ -31,8 +31,31 @@
 
 #include "doomtype.h"
 
+typedef struct 
+{
+        // Screen width and height
 
+        int width;
+        int height;
 
+        // Initialisation function to call when using this mode.
+        // Called with a pointer to the Doom palette.
+        //
+        // If NULL, no init function is called.
+
+        void (*InitMode)(byte *palette);
+        
+        // Function to call to draw the screen from the source buffer.
+
+        void (*DrawScreen)(int x1, int y1, int x2, int y2);
+
+        // If true, this is a "poor quality" mode.  The autoadjust 
+        // code should always attempt to use a different mode to this 
+        // mode in fullscreen.
+
+        boolean poor_quality;
+} screen_mode_t;
+
 // Called by D_DoomMain,
 // determines the hardware configuration
 // and sets up the video mode
@@ -63,10 +86,10 @@
 extern char *video_driver;
 extern int autoadjust_video_settings;
 extern boolean screenvisible;
-extern int screenmultiply;
+extern int screen_width, screen_height;
 extern int fullscreen;
 extern int aspect_ratio_correct;
-extern boolean grabmouse;
+extern int grabmouse;
 extern float mouse_acceleration;
 extern int mouse_threshold;
 extern int startup_delay;
@@ -73,3 +96,4 @@
 extern int vanilla_keyboard_mapping;
 
 #endif
+
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -415,7 +415,8 @@
     {"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},
+    {"screen_width",                &screen_width, DEFAULT_INT, 0, 0},
+    {"screen_height",               &screen_height, DEFAULT_INT, 0, 0},
     {"grabmouse",                   &grabmouse, DEFAULT_INT, 0, 0},
     {"novert",                      &novert, DEFAULT_INT, 0, 0},
     {"mouse_acceleration",          &mouse_acceleration, DEFAULT_FLOAT, 0, 0},