Now part of GNU Emacs. Update FSF's address.
[bpt/emacs.git] / src / image.c
index bdc78c2..1996d84 100644 (file)
@@ -1099,7 +1099,10 @@ or omitted means use the selected frame.  */)
 
 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.  */
@@ -1151,6 +1154,39 @@ free_image (f, img)
     }
 }
 
+/* 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.  */
@@ -1708,6 +1744,12 @@ lookup_image (f, spec)
     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)
     {
@@ -2178,7 +2220,7 @@ slurp_file (file, size)
 
   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;
@@ -2551,7 +2593,8 @@ static int xbm_load P_ ((struct frame *f, struct image *img));
 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));
 
@@ -2939,7 +2982,8 @@ Create_Pixmap_From_Bitmap_Data(f, img, data, fg, bg, non_default_colors)
    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;
@@ -2992,7 +3036,7 @@ xbm_read_bitmap_data (contents, end, width, height, 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;
@@ -3029,7 +3073,7 @@ xbm_read_bitmap_data (contents, end, width, height, data)
 
   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)
     {
@@ -3096,7 +3140,7 @@ xbm_load_image (f, img, contents, end)
   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);
@@ -3150,7 +3194,7 @@ xbm_file_p (data)
 {
   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));
@@ -5465,8 +5509,7 @@ pbm_load (f, img)
        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;
 
@@ -5966,6 +6009,9 @@ png_load (f, img)
   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))
@@ -6358,7 +6404,6 @@ DEF_IMGLIB_FN (jpeg_finish_decompress);
 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);
 
@@ -6374,7 +6419,6 @@ init_jpeg_functions (Lisp_Object libraries)
   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);
@@ -6400,7 +6444,6 @@ jpeg_resync_to_restart_wrapper(cinfo, desired)
 #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
 
@@ -6427,7 +6470,17 @@ my_error_exit (cinfo)
    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;
 {
 }
@@ -6438,7 +6491,7 @@ our_init_source (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.  */
@@ -6458,7 +6511,7 @@ our_fill_input_buffer (cinfo)
    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;
 {
@@ -6475,16 +6528,6 @@ our_skip_input_data (cinfo, 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.  */
@@ -6508,16 +6551,130 @@ jpeg_memory_src (cinfo, data, len)
     }
 
   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.  */
 
@@ -6601,7 +6758,7 @@ jpeg_load (f, img)
   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));
@@ -6615,6 +6772,12 @@ jpeg_load (f, img)
   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);
@@ -7044,6 +7207,14 @@ tiff_load (f, img)
      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);
@@ -7348,6 +7519,15 @@ gif_load (f, img)
        }
     }
 
+  /* 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)
@@ -7381,6 +7561,14 @@ gif_load (f, img)
                              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))
     {
@@ -7833,6 +8021,12 @@ gs_load (f, img)
   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);
 
@@ -8106,6 +8300,18 @@ listed; they're always supported.  */);
   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);