[gs-commits] rev 11786 - trunk/gs/base

robin at ghostscript.com robin at ghostscript.com
Tue Oct 12 16:26:32 UTC 2010


Author: robin
Date: 2010-10-12 16:26:32 +0000 (Tue, 12 Oct 2010)
New Revision: 11786

Modified:
   trunk/gs/base/siscale.c
Log:
Update interpolation filter. When we are downscaling an image on either axis
we now use a simpler (less computationally complex) linear interpolation
filter rather than the existing mitchell filter.

For downscaling the difference in quality between mitchell and simple
linear interpolation is not that noticable. (But the CPU/memory usage is a
quarter what it would be for proper mitchell downscaling).

What is noticable is that the existing downscaling code is 'bent' to avoid
having to hold more than 8 temporary scaled scanlines of the image. The effect
of this is to incorrectly limit the pixels that actually go into contributing
to an output pixel, producing dropouts.

We update the code here to remove this limit. This means that downscales
may potentially take more memory and more computation to perform, but the
overall quality is better.

Regression tests show both noticable and unnoticable differences in files that
enable interpolation (270 or so). The noticable differences are all that the
new output looks smoother, at the expense of losing some contrast and solving
some 'jaggy' looking edges.

Testing with my custom dropout test file shows a huge improvement with the
new code. I'll add this to the test repo shortly.



Modified: trunk/gs/base/siscale.c
===================================================================
--- trunk/gs/base/siscale.c	2010-10-12 05:52:16 UTC (rev 11785)
+++ trunk/gs/base/siscale.c	2010-10-12 16:26:32 UTC (rev 11786)
@@ -61,18 +61,24 @@
     uint dst_offset, dst_size;
     CLIST dst_next_list;	/* for next output value */
     int dst_last_index;		/* highest index used in list */
-    CONTRIB dst_items[MAX_ISCALE_SUPPORT];	/* ditto */
+    /* Vertical filter details */
+    int filter_width;
+    int max_support;
+    double (*filter)(double);
+    double min_scale;
+    CONTRIB *dst_items; /* ditto */
 } stream_IScale_state;
 
-gs_private_st_ptrs5(st_IScale_state, stream_IScale_state,
+gs_private_st_ptrs6(st_IScale_state, stream_IScale_state,
     "ImageScaleEncode/Decode state",
     iscale_state_enum_ptrs, iscale_state_reloc_ptrs,
-    dst, src, tmp, contrib, items);
+    dst, src, tmp, contrib, items, dst_items);
 
 /* ------ Digital filter definition ------ */
 
 /* Mitchell filter definition */
-#define Mitchell_support 2.0
+#define Mitchell_support 2
+#define Mitchell_min_scale ((Mitchell_support * 2) / (MAX_ISCALE_SUPPORT - 1.01))
 #define B (1.0 / 3.0)
 #define C (1.0 / 3.0)
 static double
@@ -98,11 +104,23 @@
 	return 0;
 }
 
-#define filter_support Mitchell_support
-#define filter_proc Mitchell_filter
-#define fproc(t) filter_proc(t)
-#define fWidthIn filter_support
 
+/* Interpolated filter definition */
+#define Interp_support 1
+#define Interp_min_scale 0
+static double
+Interp_filter(double t)
+{
+    double t2 = t * t;
+
+    if (t < 0)
+        t = -t;
+
+    if (t >= 1)
+        return 0;
+    return 1 + (2*t -3)*t*t;
+}
+
 /*
  * The environment provides the following definitions:
  *      double fproc(double t)
@@ -114,18 +132,22 @@
 
 /* ------ Auxiliary procedures ------ */
 
-/* Define the minimum scale. */
-#define min_scale ((fWidthIn * 2) / (MAX_ISCALE_SUPPORT - 1.01))
-
 /* Calculate the support for a given scale. */
-/* The value is always in the range 1 .. MAX_ISCALE_SUPPORT. */
+/* The value is always in the range 1..max_support (was MAX_ISCALE_SUPPORT). */
 static int
-contrib_pixels(double scale)
+Interp_contrib_pixels(double scale)
 {
-    return (int)(fWidthIn / (scale >= 1.0 ? 1.0 : max(scale, min_scale))
+    return (int)(((float)Interp_support) / (scale >= 1.0 ? 1.0 : scale)
 		 * 2 + 1.5);
 }
 
+static int
+Mitchell_contrib_pixels(double scale)
+{
+    return (int)(((float)Mitchell_support) / (scale >= 1.0 ? 1.0 : max(scale, Mitchell_min_scale))
+                 * 2 + 1.5);
+}
+
 /* Pre-calculate filter contributions for a row or a column. */
 /* Return the highest input pixel index used. */
 static int
@@ -156,7 +178,13 @@
 	/* normally, the number of color components. */
 		     int stride,
 	/* The unit of output is 'rescale_factor' times the unit of input. */
-		     double rescale_factor
+                     double rescale_factor,
+        /* The filters width */
+                     int fWidthIn,
+        /* The filter to use */
+                     double (*fproc)(double),
+        /* minimum scale factor to use */
+                     double min_scale
 )
 {
     double WidthIn, fscale;
@@ -168,12 +196,11 @@
     if_debug1('w', "[w]calculate_contrib scale=%lg\n", scale);
     if (scale < 1.0) {
 	double clamped_scale = max(scale, min_scale);
-
-	WidthIn = fWidthIn / clamped_scale;
+        WidthIn = ((double)fWidthIn) / clamped_scale;
 	fscale = 1.0 / clamped_scale;
 	squeeze = true;
     } else {
-	WidthIn = fWidthIn;
+        WidthIn = (double)fWidthIn;
 	fscale = 1.0;
 	squeeze = false;
     }
@@ -286,7 +313,7 @@
 		          weight += *pp * cp->weight;
 		}
 		pixel = (int)(weight + 0.5);
-		if_debug1('W', " %x", pixel);
+                if_debug1('W', " %g", weight);
 		*tp = (byte)CLAMP(pixel, 0, 255);
 	    }
 	} else {		/* sizeofPixelIn == 2 */
@@ -311,7 +338,7 @@
 		          weight += *pp * cp->weight;
 		}
 		pixel = (int)(weight + 0.5);
-		if_debug1('W', " %x", pixel);
+                if_debug1('W', " %g", weight);
 		*tp = (byte)CLAMP(pixel, 0, 255);
 	    }
 	}
@@ -383,28 +410,29 @@
     calculate_contrib(&ss->dst_next_list, ss->dst_items, 
 		      (double)ss->params.EntireHeightOut / ss->params.EntireHeightIn,
 		      y, ss->src_y_offset, ss->params.EntireHeightOut, ss->params.EntireHeightIn, 
-		      1, ss->params.HeightIn, MAX_ISCALE_SUPPORT, row_size,
-		      (double)ss->params.MaxValueOut / 255 );
+                      1, ss->params.HeightIn, ss->max_support, row_size,
+                      (double)ss->params.MaxValueOut / 255, ss->filter_width,
+                      ss->filter, ss->min_scale);
     int first_index_mod = ss->dst_next_list.first_pixel / row_size;
 
     if_debug2('w', "[W]calculate_dst_contrib for y = %d, y+offset=%d\n", y, y + ss->src_y_offset);
     ss->dst_last_index = last_index;
-    last_index %= MAX_ISCALE_SUPPORT;
+    last_index %= ss->max_support;
     if (last_index < first_index_mod) {		/* Shuffle the indices to account for wraparound. */
-	CONTRIB shuffle[MAX_ISCALE_SUPPORT];
+        CONTRIB *shuffle = &ss->dst_items[ss->max_support];
 	int i;
 
-	for (i = 0; i < MAX_ISCALE_SUPPORT; ++i) {
+        for (i = 0; i < ss->max_support; ++i) {
 	    shuffle[i].weight =
 		(i <= last_index ?
-		 ss->dst_items[i + MAX_ISCALE_SUPPORT - first_index_mod].weight :
+                 ss->dst_items[i + ss->max_support - first_index_mod].weight :
 		 i >= first_index_mod ?
 		 ss->dst_items[i - first_index_mod].weight :
 		 0);
 	    if_debug1('W', " %f", shuffle[i].weight);
 	}
-	memcpy(ss->dst_items, shuffle, MAX_ISCALE_SUPPORT * sizeof(CONTRIB));
-	ss->dst_next_list.n = MAX_ISCALE_SUPPORT;
+        memcpy(ss->dst_items, shuffle, ss->max_support * sizeof(CONTRIB));
+        ss->dst_next_list.n = ss->max_support;
 	ss->dst_next_list.first_pixel = 0;
     }
     if_debug0('W', "\n");
@@ -423,9 +451,18 @@
     ss->items = 0;
 }
 
+typedef struct filter_defn_s {
+    double  (*filter)(double);
+    int     filter_width;
+    int     (*contrib_pixels)(double scale);
+    double  min_scale;
+} filter_defn_s;
+
 /* Initialize the filter. */
 static int
-s_IScale_init(stream_state * st)
+do_init(stream_state        *st,
+        const filter_defn_s *horiz,
+        const filter_defn_s *vert)
 {
     stream_IScale_state *const ss = (stream_IScale_state *) st;
     gs_memory_t *mem = ss->memory;
@@ -442,24 +479,39 @@
     ss->dst_offset = 0;
 
     /* create intermediate image to hold horizontal zoom */
+    ss->max_support  = vert->contrib_pixels((double)ss->params.EntireHeightOut/
+                                            ss->params.EntireHeightIn);
+    ss->filter_width = vert->filter_width;
+    ss->filter       = vert->filter;
+    ss->min_scale    = vert->min_scale;
     ss->tmp = (byte *) gs_alloc_byte_array(mem,
-					   min(ss->params.HeightIn, MAX_ISCALE_SUPPORT),
-			      ss->params.WidthOut * ss->params.Colors * sizeof(byte),
+                                           ss->max_support,
+                                           (ss->params.WidthOut *
+                                            ss->params.Colors * sizeof(float)),
 					       "image_scale tmp");
     ss->contrib = (CLIST *) gs_alloc_byte_array(mem,
-					   max(ss->params.WidthOut, ss->params.HeightOut),
-				      sizeof(CLIST), "image_scale contrib");
-    ss->items = (CONTRIB *) gs_alloc_byte_array(mem,
-				  contrib_pixels((double)ss->params.EntireWidthOut / 
-					ss->params.EntireWidthIn) * ss->params.WidthOut,
-				 sizeof(CONTRIB), "image_scale contrib[*]");
+                                                max(ss->params.WidthOut,
+                                                    ss->params.HeightOut),
+                                                sizeof(CLIST),
+                                                "image_scale contrib");
+    ss->items = (CONTRIB *)
+                    gs_alloc_byte_array(mem,
+                                        (horiz->contrib_pixels(
+                                            (double)ss->params.EntireWidthOut /
+                                            ss->params.EntireWidthIn) *
+                                         ss->params.WidthOut),
+                                         sizeof(CONTRIB),
+                                         "image_scale contrib[*]");
+    ss->dst_items = (CONTRIB *) gs_alloc_byte_array(mem,
+                                                    ss->max_support*2,
+                                                    sizeof(CONTRIB), "image_scale contrib_dst[*]");
     /* Allocate buffers for 1 row of source and destination. */
     ss->dst = gs_alloc_byte_array(mem, ss->params.WidthOut * ss->params.Colors,
 				  ss->sizeofPixelOut, "image_scale dst");
     ss->src = gs_alloc_byte_array(mem, ss->params.WidthIn * ss->params.Colors,
 				  ss->sizeofPixelIn, "image_scale src");
     if (ss->tmp == 0 || ss->contrib == 0 || ss->items == 0 ||
-	ss->dst == 0 || ss->src == 0
+        ss->dst_items == 0 || ss->dst == 0 || ss->src == 0
 	) {
 	s_IScale_release(st);
 	return ERRC;
@@ -470,7 +522,8 @@
 		      (double)ss->params.EntireWidthOut / ss->params.EntireWidthIn,
 		      0, 0, ss->params.WidthOut, ss->params.WidthIn, 
 		      ss->params.WidthOut, ss->params.WidthIn, ss->params.WidthIn,
-		      ss->params.Colors, 255. / ss->params.MaxValueIn);
+                      ss->params.Colors, 255. / ss->params.MaxValueIn,
+                      horiz->filter_width, horiz->filter, horiz->min_scale);
 
     /* Prepare the weights for the first output row. */
     calculate_dst_contrib(ss, 0);
@@ -479,6 +532,40 @@
 
 }
 
+static const filter_defn_s Mitchell_defn =
+{
+    Mitchell_filter,
+    Mitchell_support,
+    Mitchell_contrib_pixels,
+    Mitchell_min_scale
+};
+
+static const filter_defn_s Interp_defn =
+{
+    Interp_filter,
+    Interp_support,
+    Interp_contrib_pixels,
+    Interp_min_scale
+};
+
+static int
+s_IScale_init(stream_state * st)
+{
+    stream_IScale_state *const ss = (stream_IScale_state *) st;
+    const filter_defn_s *horiz = &Mitchell_defn;
+    const filter_defn_s *vert  = &Mitchell_defn;
+
+    /* By default we use the mitchell filter, but if we are scaling down
+     * (either on the horizontal or the vertical axis) then use the simple
+     * interpolation filter for that axis. */
+    if (ss->params.EntireWidthOut < ss->params.EntireWidthIn)
+        horiz = &Interp_defn;
+    if (ss->params.EntireHeightOut < ss->params.EntireHeightIn)
+        vert = &Interp_defn;
+
+    return do_init(st, horiz, vert);
+}
+
 /* Process a buffer.  Note that this handles Encode and Decode identically. */
 static int
 s_IScale_process(stream_state * st, stream_cursor_read * pr,
@@ -555,8 +642,8 @@
 	    }
 	    /* Apply filter to zoom horizontally from src to tmp. */
 	    if_debug2('w', "[w]zoom_x y = %d to tmp row %d\n",
-		      ss->src_y, (ss->src_y % MAX_ISCALE_SUPPORT));
-	    zoom_x(ss->tmp + (ss->src_y % MAX_ISCALE_SUPPORT) *
+                      ss->src_y, (ss->src_y % ss->max_support));
+            zoom_x(ss->tmp + (ss->src_y % ss->max_support) *
 		   ss->params.WidthOut * ss->params.Colors, row,
 		   ss->sizeofPixelIn, ss->params.WidthOut, ss->params.WidthIn,
 		   ss->params.Colors, ss->contrib, ss->items);
@@ -585,6 +672,8 @@
     ss->dst = 0;
     gs_free_object(mem, ss->items, "image_scale contrib[*]");
     ss->items = 0;
+    gs_free_object(mem, ss->items, "image_scale contrib_dst[*]");
+    ss->dst_items = 0;
     gs_free_object(mem, ss->contrib, "image_scale contrib");
     ss->contrib = 0;
     gs_free_object(mem, ss->tmp, "image_scale tmp");



More information about the gs-commits mailing list