]> git.eshelyaron.com Git - emacs.git/commitdiff
Improve overflow checking in svg_load_image
authorPaul Eggert <eggert@cs.ucla.edu>
Fri, 3 Dec 2021 17:47:22 +0000 (09:47 -0800)
committerPaul Eggert <eggert@cs.ucla.edu>
Fri, 3 Dec 2021 18:25:09 +0000 (10:25 -0800)
* src/image.c: Include math.h, for lrint.
(scale_image_size, compute_image_size): Use ‘double’, not ‘int’
for image size args, since librsvg uses ‘double’ for pixel counts.
(scale_image_size): Use ceil instead of rounding, to avoid
discarding fractional SVG pixels.  Divisor and multiplier are now
double instead of int, for better portability to librsvg
functions with fractional pixel sizes.
(image_get_dimension, compute_image_size, svg_load_image):
Be more careful about ignoring, rejecting or clipping scale
factors or sizes that are out of integer range.
(compute_image_size): Don’t bother to calculate :max-width if
:width is specified, and likewise for :max-height and :height.

src/image.c

index 1db2b78ad5b635584ea13fb5fe650067d3079dc5..14e9944db2b54ec5d1f1056b806b07e7517b58fb 100644 (file)
@@ -31,6 +31,7 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 
 #include <setjmp.h>
 
+#include <math.h>
 #include <stdint.h>
 #include <c-ctype.h>
 #include <flexmember.h>
@@ -2016,14 +2017,16 @@ postprocess_image (struct frame *f, struct image *img)
    safely rounded and clipped to int range.  */
 
 static int
-scale_image_size (int size, size_t divisor, size_t multiplier)
+scale_image_size (int size, double divisor, double multiplier)
 {
   if (divisor != 0)
     {
-      double s = size;
-      double scaled = s * multiplier / divisor + 0.5;
+      double scaled = size * multiplier / divisor;
       if (scaled < INT_MAX)
-       return scaled;
+       {
+         /* Use ceil, as rounding can discard fractional SVG pixels.  */
+         return ceil (scaled);
+       }
     }
   return INT_MAX;
 }
@@ -2044,84 +2047,77 @@ image_get_dimension (struct image *img, Lisp_Object symbol)
   if (FIXNATP (value))
     return min (XFIXNAT (value), INT_MAX);
   if (CONSP (value) && NUMBERP (CAR (value)) && EQ (Qem, CDR (value)))
-    return min (img->face_font_size * XFLOATINT (CAR (value)), INT_MAX);
+    return scale_image_size (img->face_font_size, 1, XFLOATINT (CAR (value)));
 
   return -1;
 }
 
 /* Compute the desired size of an image with native size WIDTH x HEIGHT.
-   Use SPEC to deduce the size.  Store the desired size into
+   Use IMG to deduce the size.  Store the desired size into
    *D_WIDTH x *D_HEIGHT.  Store -1 x -1 if the native size is OK.  */
 static void
-compute_image_size (size_t width, size_t height,
+compute_image_size (double width, double height,
                    struct image *img,
                    int *d_width, int *d_height)
 {
-  Lisp_Object value;
-  int int_value;
-  int desired_width = -1, desired_height = -1, max_width = -1, max_height = -1;
   double scale = 1;
-
-  value = image_spec_value (img->spec, QCscale, NULL);
+  Lisp_Object value = image_spec_value (img->spec, QCscale, NULL);
   if (NUMBERP (value))
-    scale = XFLOATINT (value);
-
-  int_value = image_get_dimension (img, QCmax_width);
-  if (int_value >= 0)
-    max_width = int_value;
-
-  int_value = image_get_dimension (img, QCmax_height);
-  if (int_value >= 0)
-    max_height = int_value;
+    {
+      double dval = XFLOATINT (value);
+      if (0 <= dval)
+       scale = dval;
+    }
 
   /* If width and/or height is set in the display spec assume we want
      to scale to those values.  If either h or w is unspecified, the
      unspecified should be calculated from the specified to preserve
      aspect ratio.  */
-  int_value = image_get_dimension (img, QCwidth);
-  if (int_value >= 0)
+  int desired_width = image_get_dimension (img, QCwidth), max_width;
+  if (desired_width < 0)
+    max_width = image_get_dimension (img, QCmax_width);
+  else
     {
-      desired_width = int_value * scale;
+      desired_width = scale_image_size (desired_width, 1, scale);
       /* :width overrides :max-width. */
       max_width = -1;
     }
 
-  int_value = image_get_dimension (img, QCheight);
-  if (int_value >= 0)
+  int desired_height = image_get_dimension (img, QCheight), max_height;
+  if (desired_height < 0)
+    max_height = image_get_dimension (img, QCmax_height);
+  else
     {
-      desired_height = int_value * scale;
+      desired_height = scale_image_size (desired_height, 1, scale);
       /* :height overrides :max-height. */
       max_height = -1;
     }
 
   /* If we have both width/height set explicitly, we skip past all the
      aspect ratio-preserving computations below. */
-  if (desired_width != -1 && desired_height != -1)
+  if (0 <= desired_width && 0 <= desired_height)
     goto out;
 
-  width = width * scale;
-  height = height * scale;
-
-  if (desired_width != -1)
+  if (0 <= desired_width)
     /* Width known, calculate height. */
     desired_height = scale_image_size (desired_width, width, height);
-  else if (desired_height != -1)
+  else if (0 <= desired_height)
     /* Height known, calculate width. */
     desired_width = scale_image_size (desired_height, height, width);
   else
     {
-      desired_width = width;
-      desired_height = height;
+      desired_width = scale_image_size (width, 1, scale);
+      desired_height = scale_image_size (height, 1, scale);
     }
 
-  if (max_width != -1 && desired_width > max_width)
+  if (0 <= max_width && max_width < desired_width)
     {
       /* The image is wider than :max-width. */
       desired_width = max_width;
       desired_height = scale_image_size (desired_width, width, height);
     }
 
-  if (max_height != -1 && desired_height > max_height)
+  if (0 <= max_height && max_height < desired_height)
     {
       /* The image is higher than :max-height. */
       desired_height = max_height;
@@ -10484,7 +10480,7 @@ svg_load_image (struct frame *f, struct image *img, char *contents,
   else
     viewbox_width = viewbox_height = 0;
 
-  if (viewbox_width == 0 || viewbox_height == 0)
+  if (! (0 < viewbox_width && 0 < viewbox_height))
     {
       /* We haven't found a usable set of sizes, so try working out
          the visible area.  */
@@ -10505,8 +10501,8 @@ svg_load_image (struct frame *f, struct image *img, char *contents,
   compute_image_size (viewbox_width, viewbox_height, img,
                       &width, &height);
 
-  width *= FRAME_SCALE_FACTOR (f);
-  height *= FRAME_SCALE_FACTOR (f);
+  width = scale_image_size (width, 1, FRAME_SCALE_FACTOR (f));
+  height = scale_image_size (height, 1, FRAME_SCALE_FACTOR (f));
 
   if (! check_image_size (f, width, height))
     {