[gs-cvs] rev 8529 - trunk/gs/src

leonardo at ghostscript.com leonardo at ghostscript.com
Sun Feb 17 15:25:48 PST 2008


Author: leonardo
Date: 2008-02-17 15:25:47 -0800 (Sun, 17 Feb 2008)
New Revision: 8529

Modified:
   trunk/gs/src/gxfixed.h
   trunk/gs/src/gximage.h
   trunk/gs/src/gxipixel.c
   trunk/gs/src/gxiscale.c
   trunk/gs/src/lib.mak
   trunk/gs/src/siscale.c
   trunk/gs/src/sisparam.h
Log:
Fix (images) : Improve coordinate precision when scaling an image (continued 3).

DETAILS :

Ghostscript Bug 687345 "Image interpolation problem at a band boundary"

1. The macro fixed2int_pixround returns a mathematically incorrect
result with negative half-integer arguments.
For example fixed2int_pixround(-2.5) = -3 when the math gives -2.
We guess it was designed for positive page coordinates only,
but later it was applied to objects with negative coordinates.
Particularly, an image can start outside the page with a negative coordinate,
and fixed2int_pixround causes distorsions with banding.

We do not update fixed2int_pixround for backward compatibility 
of other parts of the algorithm. Instead that the patch defines 
a mew macro fixed2int_pixround_perfect, and applies it to
images with Interpolate=true. (gxfixed.h, gxipixel.c, gxiscale.c).

2. Added gx_image_enum_s::yi0 to provide entire image origion in the page.
This value does not depend on band size, so it gives more stability -
see below. (gximage.h).

3. Added entire image sizes to stream_image_scale_params_s
for source image and scaled image. These values do not depend on band size
and provide more stability - see how they are used in siscale.c. 
(sisparam.h, gxiscale.c).

4. (siscale.c) calculate_contrib now computes the filter kernel center
with global (page) coordinates of the image, using the right macro 
fixed2int_pixround_perfect and with the rational arithmetics
instead floats. It gives the kernel center position relatively to source image
with no dependence on the band size.  Also improved the debug printing
about that.

5. In siscale.c we keep track of some experiments done while
developing the patch. We need to save them to history because they're not trivial.
See comments in code. We'll remove them from trunk with a separate patch.

EXPECTED DIFFERENCES :

The patch eliminates banding/nobanding difference with :
148-11.ps
A-12-3480-0109-5.pdf
B-12-3077-1831-7-001.pdf
C-12-2706-0239-9-001.pdf
Note it changes both banded and unbanded rasters.

Unexpectedly it causaes a 1 pixel difference with 
D-12-2025-9478-9.pdf,
which looks minor but needs a further investigations.

Minor differences (a small shift of an image due to better scale precision) 
happen due to better image precision :

"Bug687660a.ps" 
"D-12-2025-9478-9.pdf" 
"E-12-1866-0406-7.pdf" 
"FIG3.eps" 


Modified: trunk/gs/src/gxfixed.h
===================================================================
--- trunk/gs/src/gxfixed.h	2008-02-17 22:32:15 UTC (rev 8528)
+++ trunk/gs/src/gxfixed.h	2008-02-17 23:25:47 UTC (rev 8529)
@@ -76,6 +76,8 @@
 #define fixed2int_ceiling(x) ((int)_fixed_rshift((x)+_fixed_fraction_v))
 #define fixed_pre_pixround(x) ((x)+_fixed_pixround_v)
 #define fixed2int_pixround(x) fixed2int(fixed_pre_pixround(x))
+#define fixed2int_pixround_perfect(x) ((x) < 0 && ((x) & (fixed_1 - fixed_epsilon)) == fixed_half \
+	? (int)_fixed_rshift(x) + 1 : fixed2int_pixround(x))
 #define fixed_is_int(x) !((x)&_fixed_fraction_v)
 #if ARCH_INTS_ARE_SHORT & !ARCH_IS_BIG_ENDIAN
 /* Do some of the shifting and extraction ourselves. */

Modified: trunk/gs/src/gximage.h
===================================================================
--- trunk/gs/src/gximage.h	2008-02-17 22:32:15 UTC (rev 8528)
+++ trunk/gs/src/gximage.h	2008-02-17 23:25:47 UTC (rev 8529)
@@ -254,6 +254,7 @@
 				/* (landscape only) */
     gs_int_point xyi;		/* integer origin of row */
 				/* (Interpolate only) */
+    int yi0;			/* integer y of entire image origin. */
     int yci, hci;		/* integer y & h of row (portrait) */
     int xci, wci;		/* integer x & w of row (landscape) */
     /* The maps are set at initialization.  We put them here */

Modified: trunk/gs/src/gxipixel.c
===================================================================
--- trunk/gs/src/gxipixel.c	2008-02-17 22:32:15 UTC (rev 8528)
+++ trunk/gs/src/gxipixel.c	2008-02-17 23:25:47 UTC (rev 8529)
@@ -551,6 +551,7 @@
 	dda_init(penum->dda.row.y, mty, col_extent.y, height);
 	penum->dst_width = row_extent.x;
 	penum->dst_height = col_extent.y;
+	penum->yi0 = fixed2int_pixround_perfect(dda_current(penum->dda.row.y)); /* For gs_image_class_0_interpolate. */
 	if (penum->rect.y) {
 	    dda_advance(penum->dda.row.x, penum->rect.y);
 	    dda_advance(penum->dda.row.y, penum->rect.y);

Modified: trunk/gs/src/gxiscale.c
===================================================================
--- trunk/gs/src/gxiscale.c	2008-02-17 22:32:15 UTC (rev 8528)
+++ trunk/gs/src/gxiscale.c	2008-02-17 23:25:47 UTC (rev 8529)
@@ -16,6 +16,7 @@
 #include "gx.h"
 #include "math_.h"
 #include "memory_.h"
+#include "stdint_.h"
 #include "gpcheck.h"
 #include "gserrors.h"
 #include "gxfixed.h"
@@ -101,12 +102,20 @@
     iss.BitsPerComponentOut = sizeof(frac) * 8;
     iss.MaxValueOut = frac_1;
     iss.WidthOut = (int)ceil(fabs(dst_xy.x));
+#if 0
     iss.HeightOut = (int)ceil(fabs(dst_xy.y));
+#else
+    iss.HeightOut = fixed2int_pixround_perfect((fixed)((int64_t)(penum->rect.y + penum->rect.h) * 
+						penum->dst_height / penum->Height))
+	- fixed2int_pixround_perfect((fixed)((int64_t)penum->rect.y * penum->dst_height / penum->Height));
+#endif
     iss.WidthIn = penum->rect.w;
     iss.HeightIn = penum->rect.h;
-    iss.xscale = any_abs((float)penum->dst_width / penum->Width / fixed_1);
-    iss.yscale = any_abs((float)penum->dst_height / penum->Height / fixed_1);
-    iss.dst_y_offset = penum->rect.y * iss.yscale;
+    iss.src_y_offset = penum->rect.y;
+    iss.EntireWidthIn = penum->Width;
+    iss.EntireHeightIn = penum->Height;
+    iss.EntireWidthOut = fixed2int_pixround(any_abs(penum->dst_width));
+    iss.EntireHeightOut = fixed2int_pixround(any_abs(penum->dst_height));
     pccs = cs_concrete_space(pcs, pis);
     iss.Colors = cs_num_components(pccs);
     if (penum->bps <= 8 && penum->device_color) {
@@ -182,7 +191,13 @@
 	    dda_advance(x0, penum->rect.w);
 	penum->xyi.x = fixed2int_pixround(dda_current(x0));
     }
+#if 0
     penum->xyi.y = fixed2int_pixround(dda_current(penum->dda.pixel0.y));
+#else
+    penum->xyi.y = penum->yi0 + fixed2int_pixround_perfect((fixed)((int64_t)penum->rect.y 
+				    * penum->dst_height / penum->Height))
+		* (penum->matrix.yy > 0 ? 1 : -1);
+#endif
     if_debug0('b', "[b]render=interpolate\n");
     return &image_render_interpolate;
 }
@@ -372,6 +387,7 @@
 		    }
 		}
 		LINE_ACCUM_COPY(dev, out, bpp, xo, x, raster, ry);
+		/*if_debug1('w', "[w]Y=%d:\n", ry);*/ /* See siscale.c about 'w'. */
 		penum->line_xy++;
 		if_debug0('B', "\n");
 	    }

Modified: trunk/gs/src/lib.mak
===================================================================
--- trunk/gs/src/lib.mak	2008-02-17 22:32:15 UTC (rev 8528)
+++ trunk/gs/src/lib.mak	2008-02-17 23:25:47 UTC (rev 8529)
@@ -1787,7 +1787,7 @@
 	$(GLCC) $(GLO_)siinterp.$(OBJ) $(C_) $(GLSRC)siinterp.c
 
 $(GLOBJ)siscale.$(OBJ) : $(GLSRC)siscale.c $(AK)\
- $(math__h) $(memory__h) $(stdio__h) $(gdebug_h)\
+ $(math__h) $(memory__h) $(stdio__h) $(stdint__h) $(gdebug_h)\
  $(siscale_h) $(strimpl_h)
 	$(GLCC) $(GLO_)siscale.$(OBJ) $(C_) $(GLSRC)siscale.c
 
@@ -2284,7 +2284,7 @@
 	$(ADDMOD) $(GLD)psl2lib -include $(GLD)colimlib $(GLD)psl2cs
 
 $(GLOBJ)gxiscale.$(OBJ) : $(GLSRC)gxiscale.c $(GXERR)\
- $(math__h) $(memory__h) $(gpcheck_h)\
+ $(math__h) $(memory__h) $(stdint__h) $(gpcheck_h)\
  $(gsccolor_h) $(gspaint_h)\
  $(gxarith_h) $(gxcmap_h) $(gxcpath_h) $(gxdcolor_h) $(gxdevice_h)\
  $(gxdevmem_h) $(gxfixed_h) $(gxfrac_h) $(gximage_h) $(gxistate_h)\

Modified: trunk/gs/src/siscale.c
===================================================================
--- trunk/gs/src/siscale.c	2008-02-17 22:32:15 UTC (rev 8528)
+++ trunk/gs/src/siscale.c	2008-02-17 23:25:47 UTC (rev 8529)
@@ -16,6 +16,7 @@
 #include "math_.h"
 #include "memory_.h"
 #include "stdio_.h"
+#include "stdint_.h"
 #include "gdebug.h"
 #include "strimpl.h"
 #include "siscale.h"
@@ -68,7 +69,6 @@
     /* The init procedure sets the following. */
     int sizeofPixelIn;		/* bytes per input value, 1 or 2 */
     int sizeofPixelOut;		/* bytes per output value, 1 or 2 */
-    double xscale, yscale;
     void /*PixelIn */ *src;
     void /*PixelOut */ *dst;
     PixelTmp *tmp;
@@ -78,7 +78,7 @@
     int src_y;
     uint src_offset, src_size;
     int dst_y;
-    float dst_y_offset;
+    int src_y_offset;
     uint dst_offset, dst_size;
     CLIST dst_next_list;	/* for next output value */
     int dst_last_index;		/* highest index used in list */
@@ -161,8 +161,12 @@
 		     double scale,
 	/* Start generating weights for input pixel 'starting_output_index'. */
 		     int starting_output_index,
-	/* Offset of output pixel from the output image start. */
-		     float dst_offset,
+	/* Offset of input subimage from the input image start. */
+		     int src_y_offset,
+	/* Entire output image size. */
+		     int dst_size,
+	/* Entire input image size. */
+		     int src_size,
 	/* Generate 'size' weight lists. */
 		     int size,
 	/* Limit pixel indices to 'limit', for clamping at the edges */
@@ -225,19 +229,54 @@
 	int first_pixel = min(lmin, rmin);
 	int last_pixel = max(lmax, rmax);
 #else
+#if 0
+#if 1 /* CET 148-14 */
+	float dst_offset_fraction = floor(dst_offset) - dst_offset; /* Compensate 
+				rounding for penum->xyi.y in gs_image_class_0_interpolate. */
+#else  /* CET 148-13 */
 	float dst_offset_fraction = ceil(dst_offset) - dst_offset; /* Compensate 
 				rounding for penum->xyi.y in gs_image_class_0_interpolate. */
+#endif
 	double center = (starting_output_index  + i + dst_offset_fraction) / scale - 0.5;
 	int left = (int)ceil(center - WidthIn);
 	int right = (int)floor(center + WidthIn);
+#else
+	/* Here we need :
+	   double scale = (double)dst_size / src_size;
+	   float dst_offset_fraction = floor(dst_offset) - dst_offset;
+	   double center = (starting_output_index  + i + dst_offset_fraction) / scale - 0.5;
+	   int left = (int)ceil(center - WidthIn);
+	   int right = (int)floor(center + WidthIn);
+	   We can't compute 'right' in floats because float arithmetics is not associative.
+	   In older versions tt caused a 1 pixel bias of image bands due to 
+	   rounding direction appears to depend on src_y_offset. So compute in rartionals.
+	 */
+#if 0
+#if 0 /* CET 148-14 */
+	int dst_y_offset_fraction_num = -(int)((int64_t)src_y_offset * dst_size % src_size);
+#else /* CET 148-13 */
+	int dst_y_offset_fraction_num = (src_size - (int)((int64_t)src_y_offset * dst_size % src_size)) % src_size;
+#endif
+#else
+	int dst_y_offset_fraction_num = (int)((int64_t)src_y_offset * dst_size % src_size) * 2 <= src_size
+			? -(int)((int64_t)src_y_offset * dst_size % src_size)
+			: src_size - (int)((int64_t)src_y_offset * dst_size % src_size);
+#endif
+	int center_denom = dst_size; 
+	int64_t center_num = /* center * center_denom = */ 
+	    (starting_output_index  + i) * src_size + dst_y_offset_fraction_num - (center_denom / 2);
+	int left = (int)((center_num - WidthIn * center_denom + (center_denom - 1)) / center_denom);
+	int right = (int)((center_num + WidthIn * center_denom) / center_denom);
+	double center = (double)center_num / center_denom;
+#endif
 #define clamp_pixel(j) (j < 0 ? 0 : j >= limit ? limit - 1 : j)
 	int first_pixel = clamp_pixel(left);
 	int last_pixel = clamp_pixel(right);
 #endif
 	CONTRIB *p;
 
-	if_debug4('W', "[W]i=%d, i+offset=%g scale=%lg center=%lg : ", starting_output_index + i, 
-		starting_output_index + i + dst_offset, scale, center);
+	if_debug4('w', "[w]i=%d, i+offset=%lg scale=%lg center=%lg : ", starting_output_index + i, 
+		starting_output_index + i + (double)src_y_offset / src_size * dst_size, scale, center);
 	if (last_pixel > last_index)
 	    last_index = last_pixel;
 	contrib[i].first_pixel = (first_pixel % modulus) * stride;
@@ -257,7 +296,7 @@
 
                 p[k].weight +=
 		    (PixelWeight) (weight * scaled_factor);
-		if_debug2('W', " %d %f", k, (float)p[k].weight);
+		if_debug2('w', " %d %f", k, (float)p[k].weight);
 	    }
 
 	} else {
@@ -271,10 +310,10 @@
 
 		p[k].weight +=
 		    (PixelWeight) (weight * scaled_factor);
-		if_debug2('W', " %d %f", k, (float)p[k].weight);
+		if_debug2('w', " %d %f", k, (float)p[k].weight);
 	    }
 	}
-	if_debug0('W', "\n");
+	if_debug0('w', "\n");
     }
     return last_index;
 }
@@ -402,12 +441,14 @@
 {
     uint row_size = ss->params.WidthOut * ss->params.Colors;
     int last_index =
-    calculate_contrib(&ss->dst_next_list, ss->dst_items, ss->yscale,
-		      y, ss->dst_y_offset, 1, ss->params.HeightIn, MAX_ISCALE_SUPPORT, row_size,
+    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 / (fixedScaleFactor * unitPixelTmp) );
     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->dst_y_offset);
+    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;
     if (last_index < first_index_mod) {		/* Shuffle the indices to account for wraparound. */
@@ -427,7 +468,7 @@
 	ss->dst_next_list.n = MAX_ISCALE_SUPPORT;
 	ss->dst_next_list.first_pixel = 0;
     }
-    if_debug0('w', "\n");
+    if_debug0('W', "\n");
 }
 
 /* Set default parameter values (actually, just clear pointers). */
@@ -452,14 +493,12 @@
 
     ss->sizeofPixelIn = ss->params.BitsPerComponentIn / 8;
     ss->sizeofPixelOut = ss->params.BitsPerComponentOut / 8;
-    ss->xscale = ss->params.xscale;
-    ss->yscale = ss->params.yscale;
 
     ss->src_y = 0;
     ss->src_size = ss->params.WidthIn * ss->sizeofPixelIn * ss->params.Colors;
     ss->src_offset = 0;
     ss->dst_y = 0;
-    ss->dst_y_offset = ss->params.dst_y_offset;
+    ss->src_y_offset = ss->params.src_y_offset;
     ss->dst_size = ss->params.WidthOut * ss->sizeofPixelOut * ss->params.Colors;
     ss->dst_offset = 0;
 
@@ -472,7 +511,8 @@
 					   max(ss->params.WidthOut, ss->params.HeightOut),
 				      sizeof(CLIST), "image_scale contrib");
     ss->items = (CONTRIB *) gs_alloc_byte_array(mem,
-				  contrib_pixels(ss->xscale) * ss->params.WidthOut,
+				  contrib_pixels((double)ss->params.EntireWidthOut / 
+					ss->params.EntireWidthIn) * ss->params.WidthOut,
 				 sizeof(CONTRIB), "image_scale contrib[*]");
     /* Allocate buffers for 1 row of source and destination. */
     ss->dst = gs_alloc_byte_array(mem, ss->params.WidthOut * ss->params.Colors,
@@ -487,8 +527,10 @@
 /****** WRONG ******/
     }
     /* Pre-calculate filter contributions for a row. */
-    calculate_contrib(ss->contrib, ss->items, ss->xscale,
-		      0, 0, ss->params.WidthOut, ss->params.WidthIn, ss->params.WidthIn,
+    calculate_contrib(ss->contrib, ss->items,
+		      (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, (double)unitPixelTmp * fixedScaleFactor / ss->params.MaxValueIn);
 
     /* Prepare the weights for the first output row. */
@@ -532,7 +574,8 @@
 	    /* inter-assigned freely, but not compared! */
 	    if ((void *)row != ss->dst)		/* no buffering */
 		goto adv;
-	} {			/* We're delivering a buffered output row. */
+	} 
+	{			/* We're delivering a buffered output row. */
 	    uint wcount = ss->dst_size - ss->dst_offset;
 	    uint ncopy = min(wleft, wcount);
 
@@ -544,7 +587,7 @@
 	    ss->dst_offset = 0;
 	}
 	/* Advance to the next output row. */
-      adv:++(ss->dst_y);
+      adv:++ss->dst_y;
 	if (ss->dst_y != ss->params.HeightOut)
 	    calculate_dst_contrib(ss, ss->dst_y);
     }

Modified: trunk/gs/src/sisparam.h
===================================================================
--- trunk/gs/src/sisparam.h	2008-02-17 22:32:15 UTC (rev 8528)
+++ trunk/gs/src/sisparam.h	2008-02-17 23:25:47 UTC (rev 8529)
@@ -56,9 +56,11 @@
 				/* 0 < MaxValueOut < 1 << BitsPerComponentOut*/
     int WidthOut, HeightOut;	/* > 0 */
     bool ColorPolarityAdditive;	/* needed by SpecialDownScale filter */
-    double xscale;		/* X scaling factor when the image covers fractional pixels. */
-    double yscale;		/* Y scaling factor when the image covers fractional pixels. */
-    float dst_y_offset;		/* Offset of the subimage due to grid fitting. */
+    int src_y_offset;		/* Offset of the subimage in the source image. */
+    int EntireWidthIn;		/* Height of entire input image. */
+    int EntireHeightIn;		/* Height of entire input image. */
+    int EntireWidthOut;		/* Height of entire output image. */
+    int EntireHeightOut;	/* Height of entire output image. */
 } stream_image_scale_params_t;
 
 /* Define a generic image scaling stream state. */



More information about the gs-cvs mailing list