Added support for JPEG screenshots. You can toggle that with the cvar "scr_screenshot...
authormolivier <molivier@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 3 Apr 2003 08:02:14 +0000 (08:02 +0000)
committermolivier <molivier@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 3 Apr 2003 08:02:14 +0000 (08:02 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@2897 d7cf8633-e32d-0410-b094-e92efae38249

cl_screen.c
cl_screen.h
gl_backend.c
gl_backend.h
gl_textures.c
jpeg.c
jpeg.h
menu.c

index 5e7ce09..eca31c2 100644 (file)
@@ -1,6 +1,7 @@
 
 #include "quakedef.h"
 #include "cl_video.h"
+#include "jpeg.h"
 
 cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100"};
 cvar_t scr_fov = {CVAR_SAVE, "fov","90"};      // 10 - 170
@@ -11,6 +12,7 @@ cvar_t scr_showturtle = {CVAR_SAVE, "showturtle","0"};
 cvar_t scr_showpause = {CVAR_SAVE, "showpause","1"};
 cvar_t scr_printspeed = {0, "scr_printspeed","8"};
 cvar_t scr_2dresolution = {CVAR_SAVE, "scr_2dresolution", "1"};
+cvar_t scr_screenshot_jpeg = {CVAR_SAVE, "scr_screenshot_jpeg","0"};
 cvar_t cl_avidemo = {0, "cl_avidemo", "0"};
 
 qboolean       scr_initialized;                // ready to draw
@@ -465,6 +467,7 @@ void CL_Screen_Init(void)
        Cvar_RegisterVariable (&scr_centertime);
        Cvar_RegisterVariable (&scr_printspeed);
        Cvar_RegisterVariable (&scr_2dresolution);
+       Cvar_RegisterVariable (&scr_screenshot_jpeg);
        Cvar_RegisterVariable (&cl_avidemo);
 
        Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
@@ -815,23 +818,29 @@ void SCR_ScreenShot_f (void)
        static int i = 0;
        char filename[16];
        char checkname[MAX_OSPATH];
-//
-// find a file name to save it to
-//
+       const char* extens;
+       qboolean jpeg = (scr_screenshot_jpeg.integer != 0);
+
+       if (jpeg)
+               extens = "jpg";
+       else
+               extens = "tga";
+
+       // find a file name to save it to
        for (; i<=9999 ; i++)
        {
-               sprintf (filename, "dp%04i.tga", i);
+               sprintf (filename, "dp%04i.%s", i, extens);
                sprintf (checkname, "%s/%s", fs_gamedir, filename);
                if (!FS_SysFileExists(checkname))
                        break;
        }
        if (i==10000)
        {
-               Con_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n");
+               Con_Printf ("SCR_ScreenShot_f: Couldn't create the image file\n");
                return;
        }
 
-       if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight))
+       if (SCR_ScreenShot (filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, jpeg))
                Con_Printf ("Wrote %s\n", filename);
        else
                Con_Printf ("unable to write %s\n", filename);
@@ -843,7 +852,7 @@ void SCR_CaptureAVIDemo(void)
 {
        char filename[32];
        sprintf(filename, "dpavi%06d.tga", cl_avidemo_frame);
-       if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight))
+       if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight, false))
                cl_avidemo_frame++;
        else
        {
@@ -915,7 +924,7 @@ static void R_Envmap_f (void)
                VectorCopy(envmapinfo[j].angles, r_refdef.viewangles);
                R_ClearScreen();
                R_RenderView ();
-               SCR_ScreenShot(filename, vid.realx, vid.realy, size, size);
+               SCR_ScreenShot(filename, vid.realx, vid.realy, size, size, false);
        }
 
        envmap = false;
index ec41eac..7f1ddb9 100644 (file)
@@ -53,6 +53,7 @@ void SHOWLMP_drawall(void);
 void SHOWLMP_clear(void);
 
 extern cvar_t scr_2dresolution;
+extern cvar_t scr_screenshot_jpeg;
 
 void CL_Screen_NewMap(void);
 void CL_Screen_Init(void);
index a454952..e21c1c8 100644 (file)
@@ -1,6 +1,7 @@
 
 #include "quakedef.h"
 #include "image.h"
+#include "jpeg.h"
 
 cvar_t gl_mesh_maxverts = {0, "gl_mesh_maxverts", "21760"};
 cvar_t gl_mesh_floatcolors = {0, "gl_mesh_floatcolors", "1"};
@@ -1060,7 +1061,7 @@ void R_Mesh_State(const rmeshstate_t *m)
 ==============================================================================
 */
 
-qboolean SCR_ScreenShot(char *filename, int x, int y, int width, int height)
+qboolean SCR_ScreenShot(char *filename, int x, int y, int width, int height, qboolean jpeg)
 {
        qboolean ret;
        int i, j;
@@ -1083,7 +1084,10 @@ qboolean SCR_ScreenShot(char *filename, int x, int y, int width, int height)
                }
        }
 
-       ret = Image_WriteTGARGB_preflipped(filename, width, height, buffer);
+       if (jpeg)
+               ret = JPEG_SaveImage_preflipped (filename, width, height, buffer);
+       else
+               ret = Image_WriteTGARGB_preflipped (filename, width, height, buffer);
 
        Mem_Free(buffer);
        return ret;
index 89f0a88..2443180 100644 (file)
@@ -83,8 +83,8 @@ void R_Mesh_CopyTexCoord2f(int tmu, const float *texcoord2f, int numverts);
 // copies a color4f array into varray_color4f
 void R_Mesh_CopyColor4f(const float *color4f, int numverts);
 
-// saves a section of the rendered frame to a .tga file
-qboolean SCR_ScreenShot(char *filename, int x, int y, int width, int height);
+// saves a section of the rendered frame to a .tga or .jpg file
+qboolean SCR_ScreenShot(char *filename, int x, int y, int width, int height, qboolean jpeg);
 // used by R_Envmap_f and internally in backend, clears the frame
 void R_ClearScreen(void);
 // invoke refresh of frame
index cab9f63..6bcd55a 100644 (file)
@@ -478,7 +478,9 @@ static void r_textures_start(void)
        texturedatamempool = Mem_AllocPool("Texture Storage (not yet uploaded)");
        textureprocessingmempool = Mem_AllocPool("Texture Processing Buffers");
 
-       JPEG_OpenLibrary ();
+       // Disable JPEG screenshots if the DLL isn't loaded
+       if (! JPEG_OpenLibrary ())
+               Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
 }
 
 static void r_textures_shutdown(void)
diff --git a/jpeg.c b/jpeg.c
index 3519ab1..063a86f 100644 (file)
--- a/jpeg.c
+++ b/jpeg.c
@@ -25,6 +25,7 @@
 #include "quakedef.h"
 #include "jpeg.h"
 
+
 /*
 =================================================================
 
@@ -46,13 +47,23 @@ typedef int jboolean;
 #define JPEG_LIB_VERSION  62  // Version 6b
 
 typedef void *j_common_ptr;
+typedef struct jpeg_compress_struct *j_compress_ptr;
 typedef struct jpeg_decompress_struct *j_decompress_ptr;
-typedef enum {JPEG_DUMMY1} J_COLOR_SPACE;
-typedef enum {JPEG_DUMMY2} J_DCT_METHOD;
-typedef enum {JPEG_DUMMY3} J_DITHER_MODE;
+typedef enum
+{
+       JCS_UNKNOWN,
+       JCS_GRAYSCALE,
+       JCS_RGB,
+       JCS_YCbCr,
+       JCS_CMYK,
+       JCS_YCCK
+} J_COLOR_SPACE;
+typedef enum {JPEG_DUMMY1} J_DCT_METHOD;
+typedef enum {JPEG_DUMMY2} J_DITHER_MODE;
 typedef unsigned int JDIMENSION;
 
-#define JPOOL_PERMANENT 0
+#define JPOOL_PERMANENT        0       // lasts until master record is destroyed
+#define JPOOL_IMAGE            1       // lasts until done with image/datastream
 
 #define JPEG_EOI       0xD9  // EOI marker code
 
@@ -63,6 +74,7 @@ typedef unsigned int JDIMENSION;
 #define NUM_HUFF_TBLS 4
 #define NUM_ARITH_TBLS 16
 #define MAX_COMPS_IN_SCAN 4
+#define C_MAX_BLOCKS_IN_MCU 10
 #define D_MAX_BLOCKS_IN_MCU 10
 
 struct jpeg_memory_mgr
@@ -214,6 +226,90 @@ struct jpeg_decompress_struct
 };
 
 
+struct jpeg_compress_struct
+{
+       struct jpeg_error_mgr *err;
+       struct jpeg_memory_mgr *mem;
+       void *progress;
+       void *client_data;
+       jboolean is_decompressor;
+       int global_state;
+
+       void *dest;
+       JDIMENSION image_width;
+       JDIMENSION image_height;
+       int input_components;
+       J_COLOR_SPACE in_color_space;
+       double input_gamma;
+       int data_precision;
+
+       int num_components;
+       J_COLOR_SPACE jpeg_color_space;
+       void *comp_info;
+       void *quant_tbl_ptrs[NUM_QUANT_TBLS];
+       void *dc_huff_tbl_ptrs[NUM_HUFF_TBLS];
+       void *ac_huff_tbl_ptrs[NUM_HUFF_TBLS];
+       qbyte arith_dc_L[NUM_ARITH_TBLS];
+       qbyte arith_dc_U[NUM_ARITH_TBLS];
+       qbyte arith_ac_K[NUM_ARITH_TBLS];
+
+       int num_scans;
+       const void *scan_info;
+       jboolean raw_data_in;
+       jboolean arith_code;
+       jboolean optimize_coding;
+       jboolean CCIR601_sampling;
+       int smoothing_factor;
+       J_DCT_METHOD dct_method;
+
+       unsigned int restart_interval;
+       int restart_in_rows;
+
+       jboolean write_JFIF_header;
+       qbyte JFIF_major_version;
+       qbyte JFIF_minor_version;
+       qbyte density_unit;
+       unsigned short X_density;
+       unsigned short Y_density;
+       jboolean write_Adobe_marker;
+       JDIMENSION next_scanline;
+
+       jboolean progressive_mode;
+       int max_h_samp_factor;
+       int max_v_samp_factor;
+       JDIMENSION total_iMCU_rows;
+       int comps_in_scan;
+       void *cur_comp_info[MAX_COMPS_IN_SCAN];
+       JDIMENSION MCUs_per_row;
+       JDIMENSION MCU_rows_in_scan;
+       int blocks_in_MCU;
+       int MCU_membership[C_MAX_BLOCKS_IN_MCU];
+       int Ss, Se, Ah, Al;
+
+       void *master;
+       void *main;
+       void *prep;
+       void *coef;
+       void *marker;
+       void *cconvert;
+       void *downsample;
+       void *fdct;
+       void *entropy;
+       void *script_space;
+       int script_space_size;
+};
+
+struct jpeg_destination_mgr
+{
+       qbyte* next_output_byte;
+       size_t free_in_buffer;
+
+       void (*init_destination) (j_compress_ptr cinfo);
+       jboolean (*empty_output_buffer) (j_compress_ptr cinfo);
+       void (*term_destination) (j_compress_ptr cinfo);
+};
+
+
 /*
 =================================================================
 
@@ -223,30 +319,62 @@ struct jpeg_decompress_struct
 */
 
 // Functions exported from libjpeg
+#define qjpeg_create_compress(cinfo) \
+       qjpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, (size_t) sizeof(struct jpeg_compress_struct))
+#define qjpeg_create_decompress(cinfo) \
+       qjpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, (size_t) sizeof(struct jpeg_decompress_struct))
+
+static void (*qjpeg_CreateCompress) (j_compress_ptr cinfo, int version, size_t structsize);
 static void (*qjpeg_CreateDecompress) (j_decompress_ptr cinfo, int version, size_t structsize);
+static void (*qjpeg_destroy_compress) (j_compress_ptr cinfo);
 static void (*qjpeg_destroy_decompress) (j_decompress_ptr cinfo);
+static void (*qjpeg_finish_compress) (j_compress_ptr cinfo);
 static jboolean (*qjpeg_finish_decompress) (j_decompress_ptr cinfo);
 static jboolean (*qjpeg_resync_to_restart) (j_decompress_ptr cinfo, int desired);
 static int (*qjpeg_read_header) (j_decompress_ptr cinfo, jboolean require_image);
 static JDIMENSION (*qjpeg_read_scanlines) (j_decompress_ptr cinfo, qbyte** scanlines, JDIMENSION max_lines);
+static void (*qjpeg_set_defaults) (j_compress_ptr cinfo);
+static void (*qjpeg_set_quality) (j_compress_ptr cinfo, int quality, jboolean force_baseline);
+static jboolean (*qjpeg_start_compress) (j_compress_ptr cinfo, jboolean write_all_tables);
 static jboolean (*qjpeg_start_decompress) (j_decompress_ptr cinfo);
 static struct jpeg_error_mgr* (*qjpeg_std_error) (struct jpeg_error_mgr *err);
+static JDIMENSION (*qjpeg_write_scanlines) (j_compress_ptr cinfo, qbyte** scanlines, JDIMENSION num_lines);
 
 static dllfunction_t jpegfuncs[] =
 {
+       {"jpeg_CreateCompress",         (void **) &qjpeg_CreateCompress},
        {"jpeg_CreateDecompress",       (void **) &qjpeg_CreateDecompress},
+       {"jpeg_destroy_compress",       (void **) &qjpeg_destroy_compress},
        {"jpeg_destroy_decompress",     (void **) &qjpeg_destroy_decompress},
+       {"jpeg_finish_compress",        (void **) &qjpeg_finish_compress},
        {"jpeg_finish_decompress",      (void **) &qjpeg_finish_decompress},
        {"jpeg_resync_to_restart",      (void **) &qjpeg_resync_to_restart},
        {"jpeg_read_header",            (void **) &qjpeg_read_header},
        {"jpeg_read_scanlines",         (void **) &qjpeg_read_scanlines},
+       {"jpeg_set_defaults",           (void **) &qjpeg_set_defaults},
+       {"jpeg_set_quality",            (void **) &qjpeg_set_quality},
+       {"jpeg_start_compress",         (void **) &qjpeg_start_compress},
        {"jpeg_start_decompress",       (void **) &qjpeg_start_decompress},
        {"jpeg_std_error",                      (void **) &qjpeg_std_error},
+       {"jpeg_write_scanlines",        (void **) &qjpeg_write_scanlines},
        {NULL, NULL}
 };
 
 // Handle for JPEG DLL
-static dllhandle_t jpeg_dll = NULL;
+dllhandle_t jpeg_dll = NULL;
+
+static qbyte jpeg_eoi_marker [2] = {0xFF, JPEG_EOI};
+static qboolean error_in_jpeg;
+
+// Our own output manager for JPEG compression
+typedef struct
+{
+       struct jpeg_destination_mgr pub;
+
+       qfile_t* outfile;
+       qbyte* buffer;
+} my_destination_mgr;
+typedef my_destination_mgr* my_dest_ptr;
 
 
 /*
@@ -286,7 +414,7 @@ qboolean JPEG_OpenLibrary (void)
        // Load the DLL
        if (! (jpeg_dll = Sys_LoadLibrary (dllname)))
        {
-               Con_Printf("Can't find %s. JPEG support disabled\n", dllname);
+               Con_Printf ("Can't find %s. JPEG support disabled\n", dllname);
                return false;
        }
 
@@ -294,12 +422,12 @@ qboolean JPEG_OpenLibrary (void)
        for (func = jpegfuncs; func && func->name != NULL; func++)
                if (!(*func->funcvariable = (void *) Sys_GetProcAddress (jpeg_dll, func->name)))
                {
-                       Con_Printf("missing function \"%s\" - broken JPEG library!\n", func->name);
+                       Con_Printf ("missing function \"%s\" - broken JPEG library!\n", func->name);
                        JPEG_CloseLibrary ();
                        return false;
                }
 
-       Con_Printf("%s loaded. JPEG support enabled\n", dllname);
+       Con_Printf ("%s loaded. JPEG support enabled\n", dllname);
        return true;
 }
 
@@ -324,13 +452,11 @@ void JPEG_CloseLibrary (void)
 /*
 =================================================================
 
-  Functions for handling JPEG images
+       JPEG decompression
 
 =================================================================
 */
 
-static qbyte jpeg_eoi_marker [2] = {0xFF, JPEG_EOI};
-
 static void JPEG_Noop (j_decompress_ptr cinfo) {}
 
 static jboolean JPEG_FillInputBuffer (j_decompress_ptr cinfo)
@@ -356,7 +482,7 @@ static void JPEG_SkipInputData (j_decompress_ptr cinfo, long num_bytes)
 
 static void JPEG_MemSrc (j_decompress_ptr cinfo, qbyte *buffer)
 {
-       cinfo->src = (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof (struct jpeg_source_mgr));
+       cinfo->src = cinfo->mem->alloc_small ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof (struct jpeg_source_mgr));
 
        cinfo->src->next_input_byte = buffer;
        cinfo->src->bytes_in_buffer = fs_filesize;
@@ -368,6 +494,12 @@ static void JPEG_MemSrc (j_decompress_ptr cinfo, qbyte *buffer)
        cinfo->src->term_source = JPEG_Noop;
 }
 
+static void JPEG_ErrorExit (j_common_ptr cinfo)
+{
+       ((struct jpeg_decompress_struct*)cinfo)->err->output_message (cinfo);
+       error_in_jpeg = true;
+}
+
 
 /*
 ====================
@@ -388,7 +520,7 @@ qbyte* JPEG_LoadImage (qbyte *f, int matchwidth, int matchheight)
                return NULL;
 
        cinfo.err = qjpeg_std_error (&jerr);
-       qjpeg_CreateDecompress (&cinfo, JPEG_LIB_VERSION, (size_t) sizeof(struct jpeg_decompress_struct));
+       qjpeg_create_decompress (&cinfo);
        JPEG_MemSrc (&cinfo, f);
        qjpeg_read_header (&cinfo, TRUE);
        qjpeg_start_decompress (&cinfo);
@@ -462,3 +594,125 @@ qbyte* JPEG_LoadImage (qbyte *f, int matchwidth, int matchheight)
 
        return image_rgba;
 }
+
+
+/*
+=================================================================
+
+  JPEG compression
+
+=================================================================
+*/
+
+#define JPEG_OUTPUT_BUF_SIZE 4096
+static void JPEG_InitDestination (j_compress_ptr cinfo)
+{
+       my_dest_ptr dest = (my_dest_ptr)cinfo->dest;
+       dest->buffer = (qbyte*)cinfo->mem->alloc_small ((j_common_ptr) cinfo, JPOOL_IMAGE, JPEG_OUTPUT_BUF_SIZE * sizeof(qbyte));
+       dest->pub.next_output_byte = dest->buffer;
+       dest->pub.free_in_buffer = JPEG_OUTPUT_BUF_SIZE;
+}
+
+static jboolean JPEG_EmptyOutputBuffer (j_compress_ptr cinfo)
+{
+       my_dest_ptr dest = (my_dest_ptr)cinfo->dest;
+
+       if (FS_Write (dest->outfile, dest->buffer, JPEG_OUTPUT_BUF_SIZE) != (size_t) JPEG_OUTPUT_BUF_SIZE)
+       {
+               error_in_jpeg = true;
+               return false;
+       }
+
+       dest->pub.next_output_byte = dest->buffer;
+       dest->pub.free_in_buffer = JPEG_OUTPUT_BUF_SIZE;
+       return true;
+}
+
+static void JPEG_TermDestination (j_compress_ptr cinfo)
+{
+       my_dest_ptr dest = (my_dest_ptr)cinfo->dest;
+       size_t datacount = JPEG_OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
+
+       // Write any data remaining in the buffer
+       if (datacount > 0)
+               if (FS_Write (dest->outfile, dest->buffer, datacount) != datacount)
+                       error_in_jpeg = true;
+}
+
+static void JPEG_MemDest (j_compress_ptr cinfo, qfile_t* outfile)
+{
+       my_dest_ptr dest;
+
+       // First time for this JPEG object?
+       if (cinfo->dest == NULL)
+               cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(my_destination_mgr));
+
+       dest = (my_dest_ptr)cinfo->dest;
+       dest->pub.init_destination = JPEG_InitDestination;
+       dest->pub.empty_output_buffer = JPEG_EmptyOutputBuffer;
+       dest->pub.term_destination = JPEG_TermDestination;
+       dest->outfile = outfile;
+}
+
+
+/*
+====================
+JPEG_SaveImage_preflipped
+
+Save a preflipped JPEG image to a file
+====================
+*/
+qboolean JPEG_SaveImage_preflipped (const char *filename, int width, int height, qbyte *data)
+{
+       struct jpeg_compress_struct cinfo;
+       struct jpeg_error_mgr jerr;
+       qbyte *scanline;
+       unsigned int offset, linesize;
+       qfile_t* file;
+
+       // No DLL = no JPEGs
+       if (!jpeg_dll)
+       {
+               Con_Printf ("You need the libjpeg library to save JPEG images\n");
+               return false;
+       }
+
+       // Open the file
+       file = FS_Open (filename, "wb", true);
+       if (!file)
+               return false;
+
+       cinfo.err = qjpeg_std_error (&jerr);
+       cinfo.err->error_exit = JPEG_ErrorExit;
+       error_in_jpeg = false;
+
+       qjpeg_create_compress (&cinfo);
+       JPEG_MemDest (&cinfo, file);
+
+       // Set the parameters for compression
+       cinfo.image_width = width;
+       cinfo.image_height = height;
+       cinfo.in_color_space = JCS_RGB;
+       cinfo.input_components = 3;
+       qjpeg_set_defaults (&cinfo);
+       qjpeg_set_quality (&cinfo, 90, TRUE);  // 90% quality; FIXME: use a cvar
+       qjpeg_start_compress (&cinfo, true);
+
+       // Compress each scanline
+       linesize = cinfo.image_width * 3;
+       offset = linesize * (cinfo.image_height - 1);
+       while (cinfo.next_scanline < cinfo.image_height)
+       {
+               scanline = &data[offset - cinfo.next_scanline * linesize];
+
+               qjpeg_write_scanlines (&cinfo, &scanline, 1);
+               if (error_in_jpeg)
+                       break;
+       }
+
+       qjpeg_finish_compress (&cinfo);
+       qjpeg_destroy_compress (&cinfo);
+
+       FS_Close (file);
+       return true;
+}
diff --git a/jpeg.h b/jpeg.h
index d9de27c..787f9c8 100644 (file)
--- a/jpeg.h
+++ b/jpeg.h
@@ -28,6 +28,7 @@
 qboolean JPEG_OpenLibrary (void);
 void JPEG_CloseLibrary (void);
 qbyte* JPEG_LoadImage (qbyte *f, int matchwidth, int matchheight);
+qboolean JPEG_SaveImage_preflipped (const char *filename, int width, int height, qbyte *data);
 
 
 #endif
diff --git a/menu.c b/menu.c
index f4bda35..0d905a5 100644 (file)
--- a/menu.c
+++ b/menu.c
@@ -1274,7 +1274,7 @@ void M_DrawCheckbox (int x, int y, int on)
 }
 
 
-#define OPTIONS_ITEMS 27
+#define OPTIONS_ITEMS 28
 
 int options_cursor;
 
@@ -1287,6 +1287,7 @@ void M_Menu_Options_f (void)
 
 extern cvar_t gl_delayfinish;
 extern cvar_t slowmo;
+extern dllhandle_t jpeg_dll;
 
 void M_Menu_Options_AdjustSliders (int dir)
 {
@@ -1301,46 +1302,50 @@ void M_Menu_Options_AdjustSliders (int dir)
                Cvar_SetValueQuick (&scr_viewsize, bound(30, scr_viewsize.value + dir * 10, 120));
                break;
        case 8:
-               Cvar_SetValueQuick (&r_sky, !r_sky.integer);
+               if (jpeg_dll != NULL)
+                       Cvar_SetValueQuick (&scr_screenshot_jpeg, !scr_screenshot_jpeg.integer);
                break;
        case 9:
-               Cvar_SetValueQuick (&v_overbrightbits, bound(0, v_overbrightbits.integer + dir, 4));
+               Cvar_SetValueQuick (&r_sky, !r_sky.integer);
                break;
        case 10:
-               Cvar_SetValueQuick (&gl_combine, !gl_combine.integer);
+               Cvar_SetValueQuick (&v_overbrightbits, bound(0, v_overbrightbits.integer + dir, 4));
                break;
        case 11:
-               Cvar_SetValueQuick (&gl_dither, !gl_dither.integer);
+               Cvar_SetValueQuick (&gl_combine, !gl_combine.integer);
                break;
        case 12:
-               Cvar_SetValueQuick (&gl_delayfinish, !gl_delayfinish.integer);
+               Cvar_SetValueQuick (&gl_dither, !gl_dither.integer);
                break;
        case 13:
+               Cvar_SetValueQuick (&gl_delayfinish, !gl_delayfinish.integer);
+               break;
+       case 14:
                Cvar_SetValueQuick (&slowmo, bound(0, slowmo.value + dir * 0.25, 5));
                break;
-       case 14: // music volume
+       case 15: // music volume
                #ifdef _WIN32
                Cvar_SetValueQuick (&bgmvolume, bound(0, bgmvolume.value + dir * 1.0, 1));
                #else
                Cvar_SetValueQuick (&bgmvolume, bound(0, bgmvolume.value + dir * 0.1, 1));
                #endif
                break;
-       case 15: // sfx volume
+       case 16: // sfx volume
                Cvar_SetValueQuick (&volume, bound(0, volume.value + dir * 0.1, 1));
                break;
-       case 16:
+       case 17:
                Cvar_SetValueQuick (&crosshair, bound(0, crosshair.integer + dir, 5));
                break;
-       case 17:
+       case 18:
                Cvar_SetValueQuick (&crosshair_size, bound(1, crosshair_size.value + dir, 5));
                break;
-       case 18: // static crosshair
+       case 19: // static crosshair
                Cvar_SetValueQuick (&crosshair_static, !crosshair_static.integer);
                break;
-       case 19: // show framerate
+       case 20: // show framerate
                Cvar_SetValueQuick (&showfps, !showfps.integer);
                break;
-       case 20: // always run
+       case 21: // always run
                if (cl_forwardspeed.value > 200)
                {
                        Cvar_SetValueQuick (&cl_forwardspeed, 200);
@@ -1352,22 +1357,22 @@ void M_Menu_Options_AdjustSliders (int dir)
                        Cvar_SetValueQuick (&cl_backspeed, 400);
                }
                break;
-       case 21: // lookspring
+       case 22: // lookspring
                Cvar_SetValueQuick (&lookspring, !lookspring.integer);
                break;
-       case 22: // lookstrafe
+       case 23: // lookstrafe
                Cvar_SetValueQuick (&lookstrafe, !lookstrafe.integer);
                break;
-       case 23: // mouse speed
+       case 24: // mouse speed
                Cvar_SetValueQuick (&sensitivity, bound(1, sensitivity.value + dir * 0.5, 50));
                break;
-       case 24: // mouse look
+       case 25: // mouse look
                Cvar_SetValueQuick (&freelook, !freelook.integer);
                break;
-       case 25: // invert mouse
+       case 26: // invert mouse
                Cvar_SetValueQuick (&m_pitch, -m_pitch.value);
                break;
-       case 26: // windowed mouse
+       case 27: // windowed mouse
                Cvar_SetValueQuick (&vid_mouse, !vid_mouse.integer);
                break;
        }
@@ -1391,6 +1396,7 @@ void M_Options_Draw (void)
        M_Print(16, y, " Color Control Options");y += 8;
        M_Print(16, y, "         2D Resolution");M_DrawSlider(220, y, scr_2dresolution.value, 0, 1);y += 8;
        M_Print(16, y, "           Screen size");M_DrawSlider(220, y, scr_viewsize.value, 30, 120);y += 8;
+       M_ItemPrint(16, y, "      JPEG screenshots", jpeg_dll != NULL);M_DrawCheckbox(220, y, scr_screenshot_jpeg.integer);y += 8;
        M_Print(16, y, "                   Sky");M_DrawCheckbox(220, y, r_sky.integer);y += 8;
        M_Print(16, y, "       Overbright Bits");M_DrawSlider(220, y, v_overbrightbits.value, 0, 4);y += 8;
        M_Print(16, y, "       Texture Combine");M_DrawCheckbox(220, y, gl_combine.integer);y += 8;