X-Git-Url: http://git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=jpeg.c;h=beab84335221c9ad4e3f06cb4e765fdc8bbc45b8;hp=70171f8904eb1f1374023b2666529321f642ef05;hb=e3d73d3b315e86fc321b6ef02bb7dc14742d156a;hpb=cd4b8dfb40679d00e1803be76e1b6ea0ce32df96 diff --git a/jpeg.c b/jpeg.c index 70171f89..beab8433 100644 --- a/jpeg.c +++ b/jpeg.c @@ -22,11 +22,42 @@ */ -#include "quakedef.h" +#include "darkplaces.h" +#include "cl_screen.h" #include "image.h" #include "jpeg.h" +#include "image_png.h" +extern cvar_t sv_writepicture_quality; +cvar_t r_texture_jpeg_fastpicmip = {CF_CLIENT | CF_ARCHIVE, "r_texture_jpeg_fastpicmip", "1", "perform gl_picmip during decompression for JPEG files (faster)"}; +// jboolean is unsigned char instead of int on Win32 +#ifdef WIN32 +typedef unsigned char jboolean; +#else +typedef int jboolean; +#endif + +#ifdef LINK_TO_LIBJPEG +#include +#define qjpeg_create_compress jpeg_create_compress +#define qjpeg_create_decompress jpeg_create_decompress +#define qjpeg_destroy_compress jpeg_destroy_compress +#define qjpeg_destroy_decompress jpeg_destroy_decompress +#define qjpeg_finish_compress jpeg_finish_compress +#define qjpeg_finish_decompress jpeg_finish_decompress +#define qjpeg_resync_to_restart jpeg_resync_to_restart +#define qjpeg_read_header jpeg_read_header +#define qjpeg_read_scanlines jpeg_read_scanlines +#define qjpeg_set_defaults jpeg_set_defaults +#define qjpeg_set_quality jpeg_set_quality +#define qjpeg_start_compress jpeg_start_compress +#define qjpeg_start_decompress jpeg_start_decompress +#define qjpeg_std_error jpeg_std_error +#define qjpeg_write_scanlines jpeg_write_scanlines +#define qjpeg_simple_progression jpeg_simple_progression +#define jpeg_dll true +#else /* ================================================================= @@ -38,18 +69,12 @@ ================================================================= */ -// jboolean is unsigned char instead of int on Win32 -#ifdef WIN32 -typedef unsigned char jboolean; -#else -typedef int jboolean; -#endif - -#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; + +#define JPEG_LIB_VERSION 62 // Version 6b + typedef enum { JCS_UNKNOWN, @@ -81,16 +106,16 @@ typedef unsigned int JDIMENSION; struct jpeg_memory_mgr { void* (*alloc_small) (j_common_ptr cinfo, int pool_id, size_t sizeofobject); - void (*alloc_large) (); - void (*alloc_sarray) (); - void (*alloc_barray) (); - void (*request_virt_sarray) (); - void (*request_virt_barray) (); - void (*realize_virt_arrays) (); - void (*access_virt_sarray) (); - void (*access_virt_barray) (); - void (*free_pool) (); - void (*self_destruct) (); + void (*_reserve_space_for_alloc_large) (void *dummy, ...); + void (*_reserve_space_for_alloc_sarray) (void *dummy, ...); + void (*_reserve_space_for_alloc_barray) (void *dummy, ...); + void (*_reserve_space_for_request_virt_sarray) (void *dummy, ...); + void (*_reserve_space_for_request_virt_barray) (void *dummy, ...); + void (*_reserve_space_for_realize_virt_arrays) (void *dummy, ...); + void (*_reserve_space_for_access_virt_sarray) (void *dummy, ...); + void (*_reserve_space_for_access_virt_barray) (void *dummy, ...); + void (*_reserve_space_for_free_pool) (void *dummy, ...); + void (*_reserve_space_for_self_destruct) (void *dummy, ...); long max_memory_to_use; long max_alloc_chunk; @@ -144,9 +169,9 @@ typedef struct { /* The decompressor output side may not use these variables. */ int dc_tbl_no; /* DC entropy table selector (0..3) */ int ac_tbl_no; /* AC entropy table selector (0..3) */ - + /* Remaining fields should be treated as private by applications. */ - + /* These values are computed during compression or decompression startup: */ /* Component's size in DCT blocks. * Any dummy blocks added to complete an MCU are not counted; therefore @@ -405,6 +430,7 @@ static jboolean (*qjpeg_start_compress) (j_compress_ptr cinfo, jboolean write_al 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, unsigned char** scanlines, JDIMENSION num_lines); +static void (*qjpeg_simple_progression) (j_compress_ptr cinfo); static dllfunction_t jpegfuncs[] = { @@ -423,14 +449,18 @@ static dllfunction_t jpegfuncs[] = {"jpeg_start_decompress", (void **) &qjpeg_start_decompress}, {"jpeg_std_error", (void **) &qjpeg_std_error}, {"jpeg_write_scanlines", (void **) &qjpeg_write_scanlines}, + {"jpeg_simple_progression", (void **) &qjpeg_simple_progression}, {NULL, NULL} }; // Handle for JPEG DLL dllhandle_t jpeg_dll = NULL; +qbool jpeg_tried_loading = 0; +#endif static unsigned char jpeg_eoi_marker [2] = {0xFF, JPEG_EOI}; -static qboolean error_in_jpeg; +static jmp_buf error_in_jpeg; +static qbool jpeg_toolarge; // Our own output manager for JPEG compression typedef struct @@ -459,13 +489,14 @@ JPEG_OpenLibrary Try to load the JPEG DLL ==================== */ -qboolean JPEG_OpenLibrary (void) +qbool JPEG_OpenLibrary (void) { +#ifdef LINK_TO_LIBJPEG + return true; +#else const char* dllnames [] = { -#if defined(WIN64) - "libjpeg64.dll", -#elif defined(WIN32) +#if defined(WIN32) "libjpeg.dll", #elif defined(MACOSX) "libjpeg.62.dylib", @@ -480,8 +511,20 @@ qboolean JPEG_OpenLibrary (void) if (jpeg_dll) return true; + if (jpeg_tried_loading) // only try once + return false; + + jpeg_tried_loading = true; + +#ifdef __ANDROID__ + // loading the native Android libjpeg.so causes crashes + Con_Printf("Not opening libjpeg.so dynamically on Android - use LINK_TO_LIBJPEG instead if it is needed.\n"); + return false; +#endif + // Load the DLL - return Sys_LoadLibrary (dllnames, &jpeg_dll, jpegfuncs); + return Sys_LoadDependency (dllnames, &jpeg_dll, jpegfuncs); +#endif } @@ -494,7 +537,10 @@ Unload the JPEG DLL */ void JPEG_CloseLibrary (void) { - Sys_UnloadLibrary (&jpeg_dll); +#ifndef LINK_TO_LIBJPEG + Sys_FreeLibrary (&jpeg_dll); + jpeg_tried_loading = false; // allow retry +#endif } @@ -514,7 +560,7 @@ static jboolean JPEG_FillInputBuffer (j_decompress_ptr cinfo) cinfo->src->next_input_byte = jpeg_eoi_marker; cinfo->src->bytes_in_buffer = 2; - return TRUE; + return true; } static void JPEG_SkipInputData (j_decompress_ptr cinfo, long num_bytes) @@ -546,7 +592,7 @@ static void JPEG_MemSrc (j_decompress_ptr cinfo, const unsigned char *buffer, si static void JPEG_ErrorExit (j_common_ptr cinfo) { ((struct jpeg_decompress_struct*)cinfo)->err->output_message (cinfo); - error_in_jpeg = true; + longjmp(error_in_jpeg, 1); } @@ -557,27 +603,37 @@ JPEG_LoadImage Load a JPEG image into a BGRA buffer ==================== */ -unsigned char* JPEG_LoadImage_BGRA (const unsigned char *f, int filesize) +unsigned char* JPEG_LoadImage_BGRA (const unsigned char *f, int filesize, int *miplevel) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; - unsigned char *image_buffer, *scanline; + unsigned char *image_buffer = NULL, *scanline = NULL; unsigned int line; + int submip = 0; // No DLL = no JPEGs if (!jpeg_dll) return NULL; + if(miplevel && r_texture_jpeg_fastpicmip.integer) + submip = bound(0, *miplevel, 3); + cinfo.err = qjpeg_std_error (&jerr); qjpeg_create_decompress (&cinfo); + if(setjmp(error_in_jpeg)) + goto error_caught; + cinfo.err = qjpeg_std_error (&jerr); + cinfo.err->error_exit = JPEG_ErrorExit; JPEG_MemSrc (&cinfo, f, filesize); - qjpeg_read_header (&cinfo, TRUE); + qjpeg_read_header (&cinfo, true); + cinfo.scale_num = 1; + cinfo.scale_denom = (1 << submip); qjpeg_start_decompress (&cinfo); - image_width = cinfo.image_width; - image_height = cinfo.image_height; + image_width = cinfo.output_width; + image_height = cinfo.output_height; - if (image_width > 4096 || image_height > 4096 || image_width <= 0 || image_height <= 0) + if (image_width > 32768 || image_height > 32768 || image_width <= 0 || image_height <= 0) { Con_Printf("JPEG_LoadImage: invalid image size %ix%i\n", image_width, image_height); return NULL; @@ -637,12 +693,23 @@ unsigned char* JPEG_LoadImage_BGRA (const unsigned char *f, int filesize) line++; } - Mem_Free (scanline); + Mem_Free (scanline); scanline = NULL; qjpeg_finish_decompress (&cinfo); qjpeg_destroy_decompress (&cinfo); + if(miplevel) + *miplevel -= submip; + return image_buffer; + +error_caught: + if(scanline) + Mem_Free (scanline); + if(image_buffer) + Mem_Free (image_buffer); + qjpeg_destroy_decompress (&cinfo); + return NULL; } @@ -668,10 +735,7 @@ 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; - } + longjmp(error_in_jpeg, 1); dest->pub.next_output_byte = dest->buffer; dest->pub.free_in_buffer = JPEG_OUTPUT_BUF_SIZE; @@ -686,7 +750,7 @@ static void JPEG_TermDestination (j_compress_ptr cinfo) // Write any data remaining in the buffer if (datacount > 0) if (FS_Write (dest->outfile, dest->buffer, datacount) != (fs_offset_t)datacount) - error_in_jpeg = true; + longjmp(error_in_jpeg, 1); } static void JPEG_FileDest (j_compress_ptr cinfo, qfile_t* outfile) @@ -713,8 +777,11 @@ static void JPEG_Mem_InitDestination (j_compress_ptr cinfo) static jboolean JPEG_Mem_EmptyOutputBuffer (j_compress_ptr cinfo) { - error_in_jpeg = true; - return false; + my_dest_ptr dest = (my_dest_ptr)cinfo->dest; + jpeg_toolarge = true; + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = dest->bufsize; + return true; } static void JPEG_Mem_TermDestination (j_compress_ptr cinfo) @@ -736,7 +803,7 @@ static void JPEG_MemDest (j_compress_ptr cinfo, void* buf, size_t bufsize) dest->pub.term_destination = JPEG_Mem_TermDestination; dest->outfile = NULL; - dest->buffer = buf; + dest->buffer = (unsigned char *) buf; dest->bufsize = bufsize; } @@ -748,7 +815,7 @@ JPEG_SaveImage_preflipped Save a preflipped JPEG image to a file ==================== */ -qboolean JPEG_SaveImage_preflipped (const char *filename, int width, int height, unsigned char *data) +qbool JPEG_SaveImage_preflipped (const char *filename, int width, int height, unsigned char *data) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; @@ -764,13 +831,14 @@ qboolean JPEG_SaveImage_preflipped (const char *filename, int width, int height, } // Open the file - file = FS_Open (filename, "wb", true, false); + file = FS_OpenRealFile(filename, "wb", true); if (!file) return false; + if(setjmp(error_in_jpeg)) + goto error_caught; cinfo.err = qjpeg_std_error (&jerr); cinfo.err->error_exit = JPEG_ErrorExit; - error_in_jpeg = false; qjpeg_create_compress (&cinfo); JPEG_FileDest (&cinfo, file); @@ -781,7 +849,8 @@ qboolean JPEG_SaveImage_preflipped (const char *filename, int width, int height, cinfo.in_color_space = JCS_RGB; cinfo.input_components = 3; qjpeg_set_defaults (&cinfo); - qjpeg_set_quality (&cinfo, (int)(scr_screenshot_jpeg_quality.value * 100), TRUE); + qjpeg_set_quality (&cinfo, (int)(scr_screenshot_jpeg_quality.value * 100), true); + qjpeg_simple_progression (&cinfo); // turn off subsampling (to make text look better) cinfo.optimize_coding = 1; @@ -802,8 +871,6 @@ qboolean JPEG_SaveImage_preflipped (const char *filename, int width, int height, scanline = &data[offset - cinfo.next_scanline * linesize]; qjpeg_write_scanlines (&cinfo, &scanline, 1); - if (error_in_jpeg) - break; } qjpeg_finish_compress (&cinfo); @@ -811,16 +878,19 @@ qboolean JPEG_SaveImage_preflipped (const char *filename, int width, int height, FS_Close (file); return true; + +error_caught: + qjpeg_destroy_compress (&cinfo); + FS_Close (file); + return false; } static size_t JPEG_try_SaveImage_to_Buffer (struct jpeg_compress_struct *cinfo, char *jpegbuf, size_t jpegsize, int quality, int width, int height, unsigned char *data) { unsigned char *scanline; unsigned int linesize; - int offset; - - error_in_jpeg = false; + jpeg_toolarge = false; JPEG_MemDest (cinfo, jpegbuf, jpegsize); // Set the parameters for compression @@ -829,7 +899,7 @@ static size_t JPEG_try_SaveImage_to_Buffer (struct jpeg_compress_struct *cinfo, cinfo->in_color_space = JCS_RGB; cinfo->input_components = 3; qjpeg_set_defaults (cinfo); - qjpeg_set_quality (cinfo, quality, FALSE); + qjpeg_set_quality (cinfo, quality, false); cinfo->comp_info[0].h_samp_factor = 2; cinfo->comp_info[0].v_samp_factor = 2; @@ -843,19 +913,19 @@ static size_t JPEG_try_SaveImage_to_Buffer (struct jpeg_compress_struct *cinfo, // Compress each scanline linesize = width * 3; - offset = linesize * (cinfo->image_height - 1); while (cinfo->next_scanline < cinfo->image_height) { - scanline = &data[offset - cinfo->next_scanline * linesize]; + scanline = &data[cinfo->next_scanline * linesize]; qjpeg_write_scanlines (cinfo, &scanline, 1); - if (error_in_jpeg) - break; } qjpeg_finish_compress (cinfo); - return error_in_jpeg ? 0 : ((my_dest_ptr) cinfo->dest)->bufsize; + if(jpeg_toolarge) + return 0; + + return ((my_dest_ptr) cinfo->dest)->bufsize; } size_t JPEG_SaveImage_to_Buffer (char *jpegbuf, size_t jpegsize, int width, int height, unsigned char *data) @@ -874,6 +944,8 @@ size_t JPEG_SaveImage_to_Buffer (char *jpegbuf, size_t jpegsize, int width, int return false; } + if(setjmp(error_in_jpeg)) + goto error_caught; cinfo.err = qjpeg_std_error (&jerr); cinfo.err->error_exit = JPEG_ErrorExit; @@ -899,11 +971,11 @@ size_t JPEG_SaveImage_to_Buffer (char *jpegbuf, size_t jpegsize, int width, int } #endif - //quality_guess = (100 * jpegsize - 41000) / (width*height) + 2; // fits random data - quality_guess = (256 * jpegsize - 81920) / (width*height) - 8; // fits Nexuiz's map pictures + //quality_guess = (int)((100 * jpegsize - 41000) / (width*height) + 2); // fits random data + quality_guess = (int)((256 * jpegsize - 81920) / (width*height) - 8); // fits Nexuiz's/Xonotic's map pictures - quality_guess = bound(0, quality_guess, 90); - quality = quality_guess + 10; // assume it can do 10 failed attempts + quality_guess = bound(0, quality_guess, 100); + quality = bound(0, quality_guess + sv_writepicture_quality.integer, 100); // assume it can do 10 failed attempts while(!(result = JPEG_try_SaveImage_to_Buffer(&cinfo, jpegbuf, jpegsize, quality, width, height, data))) { @@ -914,9 +986,14 @@ size_t JPEG_SaveImage_to_Buffer (char *jpegbuf, size_t jpegsize, int width, int return 0; } } + qjpeg_destroy_compress (&cinfo); Con_DPrintf("JPEG_SaveImage_to_Buffer: guessed quality/size %d/%d, actually got %d/%d\n", quality_guess, (int)jpegsize, quality, (int)result); return result; + +error_caught: + qjpeg_destroy_compress (&cinfo); + return 0; } typedef struct CompressedImageCacheItem @@ -933,14 +1010,15 @@ static CompressedImageCacheItem *CompressedImageCache[COMPRESSEDIMAGECACHE_SIZE] static void CompressedImageCache_Add(const char *imagename, size_t maxsize, void *compressed, size_t compressed_size) { - const char *hashkey = va("%s:%d", imagename, (int) maxsize); + char vabuf[1024]; + const char *hashkey = va(vabuf, sizeof(vabuf), "%s:%d", imagename, (int) maxsize); int hashindex = CRC_Block((unsigned char *) hashkey, strlen(hashkey)) % COMPRESSEDIMAGECACHE_SIZE; CompressedImageCacheItem *i; if(strlen(imagename) >= MAX_QPATH) return; // can't add this - - i = Z_Malloc(sizeof(CompressedImageCacheItem)); + + i = (CompressedImageCacheItem*) Z_Malloc(sizeof(CompressedImageCacheItem)); strlcpy(i->imagename, imagename, sizeof(i->imagename)); i->maxsize = maxsize; i->compressed = compressed; @@ -951,7 +1029,8 @@ static void CompressedImageCache_Add(const char *imagename, size_t maxsize, void static CompressedImageCacheItem *CompressedImageCache_Find(const char *imagename, size_t maxsize) { - const char *hashkey = va("%s:%d", imagename, (int) maxsize); + char vabuf[1024]; + const char *hashkey = va(vabuf, sizeof(vabuf), "%s:%d", imagename, (int) maxsize); int hashindex = CRC_Block((unsigned char *) hashkey, strlen(hashkey)) % COMPRESSEDIMAGECACHE_SIZE; CompressedImageCacheItem *i = CompressedImageCache[hashindex]; @@ -960,15 +1039,20 @@ static CompressedImageCacheItem *CompressedImageCache_Find(const char *imagename if(i->maxsize == maxsize) if(!strcmp(i->imagename, imagename)) return i; + i = i->next; } return NULL; } -qboolean Image_Compress(const char *imagename, size_t maxsize, void **buf, size_t *size) +qbool Image_Compress(const char *imagename, size_t maxsize, void **buf, size_t *size) { unsigned char *imagedata, *newimagedata; int maxPixelCount; int components[3] = {2, 1, 0}; + CompressedImageCacheItem *i; + + JPEG_OpenLibrary (); // for now; LH had the idea of replacing this by a better format + PNG_OpenLibrary (); // for loading // No DLL = no JPEGs if (!jpeg_dll) @@ -977,15 +1061,16 @@ qboolean Image_Compress(const char *imagename, size_t maxsize, void **buf, size_ return false; } - CompressedImageCacheItem *i = CompressedImageCache_Find(imagename, maxsize); + i = CompressedImageCache_Find(imagename, maxsize); if(i) { *size = i->compressed_size; *buf = i->compressed; + return (*buf != NULL); } // load the image - imagedata = loadimagepixelsbgra(imagename, true, false); + imagedata = loadimagepixelsbgra(imagename, true, false, false, NULL); if(!imagedata) return false; @@ -1005,7 +1090,7 @@ qboolean Image_Compress(const char *imagename, size_t maxsize, void **buf, size_ Image_MipReduce32(imagedata, imagedata, &image_width, &image_height, &one, image_width/2, image_height/2, 1); } - newimagedata = Mem_Alloc(tempmempool, image_width * image_height * 3); + newimagedata = (unsigned char *) Mem_Alloc(tempmempool, image_width * image_height * 3); // convert the image from BGRA to RGB Image_CopyMux(newimagedata, imagedata, image_width, image_height, false, false, false, 3, 4, components); @@ -1013,7 +1098,9 @@ qboolean Image_Compress(const char *imagename, size_t maxsize, void **buf, size_ // try to compress it to JPEG *buf = Z_Malloc(maxsize); - *size = JPEG_SaveImage_to_Buffer(*buf, maxsize, image_width, image_height, newimagedata); + *size = JPEG_SaveImage_to_Buffer((char *) *buf, maxsize, image_width, image_height, newimagedata); + Mem_Free(newimagedata); + if(!*size) { Z_Free(*buf);