static struct image *make_image P_ ((Lisp_Object spec, unsigned hash));
static void free_image P_ ((struct frame *f, struct image *img));
+static int check_image_size P_ ((struct frame *f, int width, int height));
+#define MAX_IMAGE_SIZE 6.0
+Lisp_Object Vmax_image_size;
/* Allocate and return a new image structure for image specification
SPEC. SPEC has a hash value of HASH. */
}
}
+/* Return 1 if the given widths and heights are valid for display;
+ otherwise, return 0. */
+
+int
+check_image_size (f, width, height)
+ struct frame *f;
+ int width;
+ int height;
+{
+ int w, h;
+
+ if (width <= 0 || height <= 0)
+ return 0;
+
+ if (INTEGERP (Vmax_image_size))
+ w = h = XINT (Vmax_image_size);
+ else if (FLOATP (Vmax_image_size))
+ {
+ if (f != NULL)
+ {
+ w = FRAME_PIXEL_WIDTH (f);
+ h = FRAME_PIXEL_HEIGHT (f);
+ }
+ else
+ w = h = 1024; /* Arbitrary size for unknown frame. */
+ w = (int) (XFLOAT_DATA (Vmax_image_size) * w);
+ h = (int) (XFLOAT_DATA (Vmax_image_size) * h);
+ }
+ else
+ return 1;
+
+ return (width <= w && height <= h);
+}
/* Prepare image IMG for display on frame F. Must be called before
drawing an image. */
if (img->hash == hash && !NILP (Fequal (img->spec, spec)))
break;
+ if (img && img->load_failed_p)
+ {
+ free_image (f, img);
+ img = NULL;
+ }
+
/* If not found, create a new image and cache it. */
if (img == NULL)
{
if (stat (file, &st) == 0
&& (fp = fopen (file, "rb")) != NULL
- && (buf = (char *) xmalloc (st.st_size),
+ && (buf = (unsigned char *) xmalloc (st.st_size),
fread (buf, 1, st.st_size, fp) == st.st_size))
{
*size = st.st_size;
static int xbm_load_image P_ ((struct frame *f, struct image *img,
unsigned char *, unsigned char *));
static int xbm_image_p P_ ((Lisp_Object object));
-static int xbm_read_bitmap_data P_ ((unsigned char *, unsigned char *,
+static int xbm_read_bitmap_data P_ ((struct frame *f,
+ unsigned char *, unsigned char *,
int *, int *, unsigned char **));
static int xbm_file_p P_ ((Lisp_Object));
CONTENTS looks like an in-memory XBM file. */
static int
-xbm_read_bitmap_data (contents, end, width, height, data)
+xbm_read_bitmap_data (f, contents, end, width, height, data)
+ struct frame *f;
unsigned char *contents, *end;
int *width, *height;
unsigned char **data;
expect (XBM_TK_NUMBER);
}
- if (*width < 0 || *height < 0)
+ if (!check_image_size (f, *width, *height))
goto failure;
else if (data == NULL)
goto success;
bytes_per_line = (*width + 7) / 8 + padding_p;
nbytes = bytes_per_line * *height;
- p = *data = (char *) xmalloc (nbytes);
+ p = *data = (unsigned char *) xmalloc (nbytes);
if (v10)
{
unsigned char *data;
int success_p = 0;
- rc = xbm_read_bitmap_data (contents, end, &img->width, &img->height, &data);
+ rc = xbm_read_bitmap_data (f, contents, end, &img->width, &img->height, &data);
if (rc)
{
unsigned long foreground = FRAME_FOREGROUND_PIXEL (f);
{
int w, h;
return (STRINGP (data)
- && xbm_read_bitmap_data (SDATA (data),
+ && xbm_read_bitmap_data (NULL, SDATA (data),
(SDATA (data)
+ SBYTES (data)),
&w, &h, NULL));
max_color_idx = 255;
}
- if (width < 0
- || height < 0
+ if (!check_image_size (f, width, height)
|| (type != PBM_MONO && max_color_idx < 0))
goto error;
fn_png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
&interlace_type, NULL, NULL);
+ if (!check_image_size (f, width, height))
+ goto error;
+
/* If image contains simply transparency data, we prefer to
construct a clipping mask. */
if (fn_png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
DEF_IMGLIB_FN (jpeg_destroy_decompress);
DEF_IMGLIB_FN (jpeg_read_header);
DEF_IMGLIB_FN (jpeg_read_scanlines);
-DEF_IMGLIB_FN (jpeg_stdio_src);
DEF_IMGLIB_FN (jpeg_std_error);
DEF_IMGLIB_FN (jpeg_resync_to_restart);
LOAD_IMGLIB_FN (library, jpeg_read_scanlines);
LOAD_IMGLIB_FN (library, jpeg_start_decompress);
LOAD_IMGLIB_FN (library, jpeg_read_header);
- LOAD_IMGLIB_FN (library, jpeg_stdio_src);
LOAD_IMGLIB_FN (library, jpeg_CreateDecompress);
LOAD_IMGLIB_FN (library, jpeg_destroy_decompress);
LOAD_IMGLIB_FN (library, jpeg_std_error);
#define fn_jpeg_destroy_decompress jpeg_destroy_decompress
#define fn_jpeg_read_header jpeg_read_header
#define fn_jpeg_read_scanlines jpeg_read_scanlines
-#define fn_jpeg_stdio_src jpeg_stdio_src
#define fn_jpeg_std_error jpeg_std_error
#define jpeg_resync_to_restart_wrapper jpeg_resync_to_restart
libjpeg.doc from the JPEG lib distribution. */
static void
-our_init_source (cinfo)
+our_common_init_source (cinfo)
+ j_decompress_ptr cinfo;
+{
+}
+
+
+/* Method to terminate data source. Called by
+ jpeg_finish_decompress() after all data has been processed. */
+
+static void
+our_common_term_source (cinfo)
j_decompress_ptr cinfo;
{
}
so this only adds a fake end of input marker at the end. */
static boolean
-our_fill_input_buffer (cinfo)
+our_memory_fill_input_buffer (cinfo)
j_decompress_ptr cinfo;
{
/* Insert a fake EOI marker. */
is the JPEG data source manager. */
static void
-our_skip_input_data (cinfo, num_bytes)
+our_memory_skip_input_data (cinfo, num_bytes)
j_decompress_ptr cinfo;
long num_bytes;
{
}
-/* Method to terminate data source. Called by
- jpeg_finish_decompress() after all data has been processed. */
-
-static void
-our_term_source (cinfo)
- j_decompress_ptr cinfo;
-{
-}
-
-
/* Set up the JPEG lib for reading an image from DATA which contains
LEN bytes. CINFO is the decompression info structure created for
reading the image. */
}
src = (struct jpeg_source_mgr *) cinfo->src;
- src->init_source = our_init_source;
- src->fill_input_buffer = our_fill_input_buffer;
- src->skip_input_data = our_skip_input_data;
+ src->init_source = our_common_init_source;
+ src->fill_input_buffer = our_memory_fill_input_buffer;
+ src->skip_input_data = our_memory_skip_input_data;
src->resync_to_restart = jpeg_resync_to_restart_wrapper; /* Use default method. */
- src->term_source = our_term_source;
+ src->term_source = our_common_term_source;
src->bytes_in_buffer = len;
src->next_input_byte = data;
}
+struct jpeg_stdio_mgr
+{
+ struct jpeg_source_mgr mgr;
+ boolean finished;
+ FILE *file;
+ JOCTET *buffer;
+};
+
+
+/* Size of buffer to read JPEG from file.
+ Not too big, as we want to use alloc_small. */
+#define JPEG_STDIO_BUFFER_SIZE 8192
+
+
+/* Fill input buffer method for JPEG data source manager. Called
+ whenever more data is needed. The data is read from a FILE *. */
+
+static boolean
+our_stdio_fill_input_buffer (cinfo)
+ j_decompress_ptr cinfo;
+{
+ struct jpeg_stdio_mgr *src;
+
+ src = (struct jpeg_stdio_mgr *) cinfo->src;
+ if (!src->finished)
+ {
+ size_t bytes;
+
+ bytes = fread (src->buffer, 1, JPEG_STDIO_BUFFER_SIZE, src->file);
+ if (bytes > 0)
+ src->mgr.bytes_in_buffer = bytes;
+ else
+ {
+ WARNMS (cinfo, JWRN_JPEG_EOF);
+ src->finished = 1;
+ src->buffer[0] = (JOCTET) 0xFF;
+ src->buffer[1] = (JOCTET) JPEG_EOI;
+ src->mgr.bytes_in_buffer = 2;
+ }
+ src->mgr.next_input_byte = src->buffer;
+ }
+
+ return 1;
+}
+
+
+/* Method to skip over NUM_BYTES bytes in the image data. CINFO->src
+ is the JPEG data source manager. */
+
+static void
+our_stdio_skip_input_data (cinfo, num_bytes)
+ j_decompress_ptr cinfo;
+ long num_bytes;
+{
+ struct jpeg_stdio_mgr *src;
+ src = (struct jpeg_stdio_mgr *) cinfo->src;
+
+ while (num_bytes > 0 && !src->finished)
+ {
+ if (num_bytes <= src->mgr.bytes_in_buffer)
+ {
+ src->mgr.bytes_in_buffer -= num_bytes;
+ src->mgr.next_input_byte += num_bytes;
+ break;
+ }
+ else
+ {
+ num_bytes -= src->mgr.bytes_in_buffer;
+ src->mgr.bytes_in_buffer = 0;
+ src->mgr.next_input_byte = NULL;
+
+ our_stdio_fill_input_buffer (cinfo);
+ }
+ }
+}
+
+
+/* Set up the JPEG lib for reading an image from a FILE *.
+ CINFO is the decompression info structure created for
+ reading the image. */
+
+static void
+jpeg_file_src (cinfo, fp)
+ j_decompress_ptr cinfo;
+ FILE *fp;
+{
+ struct jpeg_stdio_mgr *src;
+
+ if (cinfo->src != NULL)
+ src = (struct jpeg_stdio_mgr *) cinfo->src;
+ else
+ {
+ /* First time for this JPEG object? */
+ cinfo->src = (struct jpeg_source_mgr *)
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+ sizeof (struct jpeg_stdio_mgr));
+ src = (struct jpeg_stdio_mgr *) cinfo->src;
+ src->buffer = (JOCTET *)
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+ JPEG_STDIO_BUFFER_SIZE);
+ }
+
+ src->file = fp;
+ src->finished = 0;
+ src->mgr.init_source = our_common_init_source;
+ src->mgr.fill_input_buffer = our_stdio_fill_input_buffer;
+ src->mgr.skip_input_data = our_stdio_skip_input_data;
+ src->mgr.resync_to_restart = jpeg_resync_to_restart_wrapper; /* Use default method. */
+ src->mgr.term_source = our_common_term_source;
+ src->mgr.bytes_in_buffer = 0;
+ src->mgr.next_input_byte = NULL;
+}
+
+
/* Load image IMG for use on frame F. Patterned after example.c
from the JPEG lib. */
fn_jpeg_CreateDecompress (&cinfo, JPEG_LIB_VERSION, sizeof (cinfo));
if (NILP (specified_data))
- fn_jpeg_stdio_src (&cinfo, (FILE *) fp);
+ jpeg_file_src (&cinfo, (FILE *) fp);
else
jpeg_memory_src (&cinfo, SDATA (specified_data),
SBYTES (specified_data));
width = img->width = cinfo.output_width;
height = img->height = cinfo.output_height;
+ if (!check_image_size (f, width, height))
+ {
+ image_error ("Invalid image size", Qnil, Qnil);
+ longjmp (mgr.setjmp_buffer, 2);
+ }
+
/* Create X image and pixmap. */
if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap))
longjmp (mgr.setjmp_buffer, 2);
of width x height 32-bit values. */
fn_TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width);
fn_TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height);
+
+ if (!check_image_size (f, width, height))
+ {
+ image_error ("Invalid image size", Qnil, Qnil);
+ UNGCPRO;
+ return 0;
+ }
+
buf = (uint32 *) xmalloc (width * height * sizeof *buf);
rc = fn_TIFFReadRGBAImage (tiff, width, height, buf, 0);
}
}
+ /* Before reading entire contents, check the declared image size. */
+ if (!check_image_size (f, gif->SWidth, gif->SHeight))
+ {
+ image_error ("Invalid image size", Qnil, Qnil);
+ fn_DGifCloseFile (gif);
+ UNGCPRO;
+ return 0;
+ }
+
/* Read entire contents. */
rc = fn_DGifSlurp (gif);
if (rc == GIF_ERROR)
max (gif->Image.Top + gif->Image.Height,
image_top + image_height));
+ if (!check_image_size (f, width, height))
+ {
+ image_error ("Invalid image size", Qnil, Qnil);
+ fn_DGifCloseFile (gif);
+ UNGCPRO;
+ return 0;
+ }
+
/* Create the X image and pixmap. */
if (!x_create_x_image_and_pixmap (f, width, height, 0, &ximg, &img->pixmap))
{
in_height = XFASTINT (pt_height) / 72.0;
img->height = in_height * FRAME_X_DISPLAY_INFO (f)->resy;
+ if (!check_image_size (f, img->width, img->height))
+ {
+ image_error ("Invalid image size", Qnil, Qnil);
+ return 0;
+ }
+
/* Create the pixmap. */
xassert (img->pixmap == NO_PIXMAP);
Vimage_library_alist = Qnil;
Fput (intern ("image-library-alist"), Qrisky_local_variable, Qt);
+ DEFVAR_LISP ("max-image-size", &Vmax_image_size,
+ doc: /* Maximum size of images.
+Emacs will not load an image into memory if its pixel width or
+pixel height exceeds this limit.
+
+If the value is an integer, it directly specifies the maximum
+image height and width, measured in pixels. If it is a floating
+point number, it specifies the maximum image height and width
+as a ratio to the frame height and width. If the value is
+non-numeric, there is no explicit limit on the size of images. */);
+ Vmax_image_size = make_float (MAX_IMAGE_SIZE);
+
Vimage_type_cache = Qnil;
staticpro (&Vimage_type_cache);