--- gdk-pixbuf-2.31.1/gdk-pixbuf/gdk-pixbuf-scale.c	2016-11-28 16:56:22.000000000 +0100
+++ gdk-pixbuf-2.31.1b/gdk-pixbuf/gdk-pixbuf-scale.c	2016-11-29 14:41:00.250786319 +0100
@@ -125,6 +125,26 @@
  * same pixbuf, it will be overwritten during the scaling which
  * results in rendering artifacts.
  **/
+
+/* Integer square root to avoid bringing in the math library.
+ * From http://freaknet.org/martin/tape/gos/misc/personal/msc/sqrt */
+static unsigned long
+int_sqrt(unsigned long number) {
+#define NEXT(n, i)  (((n) + (i)/(n)) >> 1)
+  unsigned long n  = 1;
+  unsigned long n1 = NEXT(n, number);
+
+  while(abs(n1 - n) > 1) {
+    n  = n1;
+    n1 = NEXT(n, number);
+  }
+  while((n1*n1) > number) {
+    n1 -= 1;
+  }
+  return n1;
+#undef NEXT
+}
+
 void
 gdk_pixbuf_scale (const GdkPixbuf *src,
 		  GdkPixbuf       *dest,
@@ -140,6 +160,7 @@
 {
   const guint8 *src_pixels;
   guint8 *dest_pixels;
+  GdkPixbuf *tmp = NULL;	/* Temporary image for two-step scaling */
 
   g_return_if_fail (GDK_IS_PIXBUF (src));
   g_return_if_fail (GDK_IS_PIXBUF (dest));
@@ -153,11 +174,81 @@
   dest_pixels = gdk_pixbuf_get_pixels (dest);
   src_pixels = gdk_pixbuf_read_pixels (src);
 
+  /* make_filter_table() bloats out in VM usage and consumes 100% CPU for
+   * tens of seconds when downscaling by a large factor. We get round this
+   * by doing extreme reductions in two steps.
+   * The "pathological" ratios are derived from timed test runs and are placed
+   * at a point where the execution time has increased above 150% of the usual
+   * but has not doubled yet. This turns out to be when the total area is
+   * reduced by more than 1600 times or one of the dimensions by a factor of
+   * more than 256.
+   * GDK_INTER_BILINEAR, GDK_INTERP_TILES and GDK_INTER_HYPER all exhibit
+   * similar symptoms, leaving only GDK_INTERP_NEAREST not needing this trick.
+   */
+#define PATHO_SIZE_RATIO (1/256.0)
+#define PATHO_AREA_RATIO (1/1600.0)
+  if (interp_type != GDK_INTERP_NEAREST)
+    {
+      int tmp_width, tmp_height;
+      guint8 *tmp_pixels;
+
+      if (scale_x * scale_y < PATHO_AREA_RATIO)
+	{
+	  /* reduce image in two equal-ratio steps, the geometric mean of
+	   * the source width and destination width. We avoid using sqrt()
+	   * to avoid hauling in the math library, and don't have the dimensions
+	   * of the source area on hand, so calculate them as dest_* / scale_*
+	   */
+	  tmp_width = int_sqrt((int)(dest_width * (dest_width / scale_x) + .5));
+	  tmp_height = int_sqrt((int)(dest_height * (dest_height / scale_y) + .5));
+	}
+      else if (scale_x < PATHO_SIZE_RATIO)
+	{
+	  /* Do width reduction in two steps */
+	  tmp_width = int_sqrt((int)dest_width * (dest_width / scale_x) + .5);
+	  tmp_height = dest_height;
+ 	}
+      else if (scale_y < PATHO_SIZE_RATIO)
+	{
+	  /* Do height reduction in two steps */
+	  tmp_width = dest_width;
+	  tmp_height = int_sqrt((int)dest_height * (dest_height / scale_y) + .5);
+	}
+      else
+	goto no_opt;
+
+      /* Scale the source image into a top-left-aligned temporary pixbuf.
+       * The offset_[xy] into the source image is done here and the dest_[xy]
+       * offset into the destination image will be done in the final scaling.
+       */
+      tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, src->has_alpha, 8, tmp_width, tmp_height);
+      if (!tmp)
+	goto no_opt;
+      tmp_pixels = gdk_pixbuf_read_pixels (tmp);
+
+      /* Now do the prescaling to an intermediate size */
+      _pixops_scale (tmp_pixels, tmp->width, tmp->height, tmp->rowstride,
+		     tmp->n_channels, tmp->has_alpha, src_pixels, src->width,
+		     src->height, src->rowstride, src->n_channels, src->has_alpha,
+		     0, 0, tmp_width, tmp_height, offset_x, offset_y,
+		     scale_x / ((double)dest_width / tmp_width),
+		     scale_y / ((double)dest_height / tmp_height),
+		     (PixopsInterpType)interp_type);
+      src = tmp; src_pixels = gdk_pixbuf_read_pixels (src);
+      offset_x = offset_y = 0;
+      scale_x = (double)dest_width / tmp_width;
+      scale_y = (double)dest_height / tmp_height;
+    }
+
+no_opt:
   _pixops_scale (dest_pixels, dest->width, dest->height, dest->rowstride,
                  dest->n_channels, dest->has_alpha, src_pixels, src->width,
                  src->height, src->rowstride, src->n_channels, src->has_alpha,
                  dest_x, dest_y, dest_width, dest_height, offset_x, offset_y,
                  scale_x, scale_y, (PixopsInterpType)interp_type);
+
+  if (tmp) 
+    gdk_pixbuf_unref (tmp);
 }
 
 /**
