summaryrefslogtreecommitdiff
path: root/draw
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2011-11-08 17:46:48 +0000
committerRobin Watts <robin.watts@artifex.com>2011-11-14 16:32:45 +0000
commit5c4ff53fb89b5068bc901fd5909be9e5d2d0cb0b (patch)
tree8dc1faaa37e23192dc586a00d7e7e95a1439366f /draw
parenta6e91235376900869f74673caf7bf17fc257bdd7 (diff)
downloadmupdf-5c4ff53fb89b5068bc901fd5909be9e5d2d0cb0b.tar.xz
Grid fitting tweaks.
Extract the grid fitting code from the scaling code and the affine image drawing code into it's own separate function. This reduces code duplication. It also allows us to make better allowance for rounding errors. Add a voodoo offset in the draw_affine.c code for painting interpolated images. This gives us the best possible match between all the different combinations of scaled/unscaled and interpolated/uninterpolated images.
Diffstat (limited to 'draw')
-rw-r--r--draw/draw_affine.c158
-rw-r--r--draw/draw_device.c10
-rw-r--r--draw/draw_scale.c59
-rw-r--r--draw/draw_simple_scale.c59
4 files changed, 157 insertions, 129 deletions
diff --git a/draw/draw_affine.c b/draw/draw_affine.c
index 95d28bea..c1ee88f8 100644
--- a/draw/draw_affine.c
+++ b/draw/draw_affine.c
@@ -454,6 +454,146 @@ fz_paint_affine_color_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int
}
}
+/* RJW: The following code was originally written to be sensitive to
+ * FLT_EPSILON. Given the way the 'minimum representable difference'
+ * between 2 floats changes size as we scale, we now pick a larger
+ * value to ensure idempotency even with rounding problems. The
+ * value we pick is still far smaller than would ever show up with
+ * antialiasing.
+ */
+#define MY_EPSILON 0.001
+
+void
+fz_gridfit_matrix(fz_matrix *m)
+{
+ if (fabsf(m->b) < FLT_EPSILON && fabsf(m->c) < FLT_EPSILON)
+ {
+ if (m->a > 0)
+ {
+ float f;
+ /* Adjust left hand side onto pixel boundary */
+ f = (float)(int)(m->e);
+ if (f - m->e > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves left */
+ m->a += m->e - f; /* width gets wider as f <= m->e */
+ m->e = f;
+ /* Adjust right hand side onto pixel boundary */
+ f = (float)(int)(m->a);
+ if (m->a - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves right */
+ m->a = f;
+ }
+ else if (m->a < 0)
+ {
+ float f;
+ /* Adjust right hand side onto pixel boundary */
+ f = (float)(int)(m->e);
+ if (m->e - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves right */
+ m->a += m->e - f; /* width gets wider (more -ve) */
+ m->e = f;
+ /* Adjust left hand side onto pixel boundary */
+ f = (float)(int)(m->a);
+ if (f - m->a > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves left */
+ m->a = f;
+ }
+ if (m->d > 0)
+ {
+ float f;
+ /* Adjust top onto pixel boundary */
+ f = (float)(int)(m->f);
+ if (f - m->f > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves upwards */
+ m->d += m->f - f; /* width gets wider as f <= m->f */
+ m->f = f;
+ /* Adjust bottom onto pixel boundary */
+ f = (float)(int)(m->d);
+ if (m->d - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves down */
+ m->d = f;
+ }
+ else if (m->d < 0)
+ {
+ float f;
+ /* Adjust bottom onto pixel boundary */
+ f = (float)(int)(m->f);
+ if (m->f - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves down */
+ m->d += m->f - f; /* width gets wider (more -ve) */
+ m->f = f;
+ /* Adjust top onto pixel boundary */
+ f = (float)(int)(m->d);
+ if (f - m->d > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves up */
+ m->d = f;
+ }
+ }
+ else if (fabsf(m->a) < FLT_EPSILON && fabsf(m->d) < FLT_EPSILON)
+ {
+ if (m->b > 0)
+ {
+ float f;
+ /* Adjust left hand side onto pixel boundary */
+ f = (float)(int)(m->f);
+ if (f - m->f > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves left */
+ m->b += m->f - f; /* width gets wider as f <= m->f */
+ m->f = f;
+ /* Adjust right hand side onto pixel boundary */
+ f = (float)(int)(m->b);
+ if (m->b - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves right */
+ m->b = f;
+ }
+ else if (m->b < 0)
+ {
+ float f;
+ /* Adjust right hand side onto pixel boundary */
+ f = (float)(int)(m->f);
+ if (m->f - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves right */
+ m->b += m->f - f; /* width gets wider (more -ve) */
+ m->f = f;
+ /* Adjust left hand side onto pixel boundary */
+ f = (float)(int)(m->b);
+ if (f - m->b > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves left */
+ m->b = f;
+ }
+ if (m->c > 0)
+ {
+ float f;
+ /* Adjust top onto pixel boundary */
+ f = (float)(int)(m->e);
+ if (f - m->e > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves upwards */
+ m->c += m->e - f; /* width gets wider as f <= m->e */
+ m->e = f;
+ /* Adjust bottom onto pixel boundary */
+ f = (float)(int)(m->c);
+ if (m->c - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves down */
+ m->c = f;
+ }
+ else if (m->c < 0)
+ {
+ float f;
+ /* Adjust bottom onto pixel boundary */
+ f = (float)(int)(m->e);
+ if (m->e - f > MY_EPSILON)
+ f += 1.0; /* Ensure it moves down */
+ m->c += m->e - f; /* width gets wider (more -ve) */
+ m->e = f;
+ /* Adjust top onto pixel boundary */
+ f = (float)(int)(m->c);
+ if (f - m->c > MY_EPSILON)
+ f -= 1.0; /* Ensure it moves up */
+ m->c = f;
+ }
+ }
+}
+
/* Draw an image with an affine transform on destination */
static void
@@ -469,15 +609,7 @@ fz_paint_image_imp(fz_pixmap *dst, fz_bbox scissor, fz_pixmap *shape, fz_pixmap
void (*paintfn)(byte *dp, byte *sp, int sw, int sh, int u, int v, int fa, int fb, int w, int n, int alpha, byte *color, byte *hp);
/* grid fit the image */
- if (fz_is_rectilinear(ctm))
- {
- ctm.a = roundup(ctm.a);
- ctm.b = roundup(ctm.b);
- ctm.c = roundup(ctm.c);
- ctm.d = roundup(ctm.d);
- ctm.e = floorf(ctm.e);
- ctm.f = floorf(ctm.f);
- }
+ fz_gridfit_matrix(&ctm);
/* turn on interpolation for upscaled and non-rectilinear transforms */
dolerp = 0;
@@ -519,6 +651,14 @@ fz_paint_image_imp(fz_pixmap *dst, fz_bbox scissor, fz_pixmap *shape, fz_pixmap
u = (fa * x) + (fc * y) + inv.e * 65536 + ((fa + fc) >> 1);
v = (fb * x) + (fd * y) + inv.f * 65536 + ((fb + fd) >> 1);
+ /* RJW: The following is voodoo. No idea why it works, but it gives
+ * the best match between scaled/unscaled/interpolated/non-interpolated
+ * that we have found. */
+ if (dolerp) {
+ u -= 32768;
+ v -= 32768;
+ }
+
dp = dst->samples + ((y - dst->y) * dst->w + (x - dst->x)) * dst->n;
n = dst->n;
sp = img->samples;
diff --git a/draw/draw_device.c b/draw/draw_device.c
index 76b55276..0efe701a 100644
--- a/draw/draw_device.c
+++ b/draw/draw_device.c
@@ -862,7 +862,10 @@ fz_transform_pixmap(fz_pixmap *image, fz_matrix *ctm, int x, int y, int dx, int
if (ctm->a != 0 && ctm->b == 0 && ctm->c == 0 && ctm->d != 0)
{
/* Unrotated or X-flip or Y-flip or XY-flip */
- scaled = fz_scale_pixmap_gridfit(image, ctm->e, ctm->f, ctm->a, ctm->d, gridfit);
+ fz_matrix m = *ctm;
+ if (gridfit)
+ fz_gridfit_matrix(&m);
+ scaled = fz_scale_pixmap(image, m.e, m.f, m.a, m.d);
if (scaled == NULL)
return NULL;
ctm->a = scaled->w;
@@ -875,7 +878,10 @@ fz_transform_pixmap(fz_pixmap *image, fz_matrix *ctm, int x, int y, int dx, int
if (ctm->a == 0 && ctm->b != 0 && ctm->c != 0 && ctm->d == 0)
{
/* Other orthogonal flip/rotation cases */
- scaled = fz_scale_pixmap_gridfit(image, ctm->f, ctm->e, ctm->b, ctm->c, gridfit);
+ fz_matrix m = *ctm;
+ if (gridfit)
+ fz_gridfit_matrix(&m);
+ scaled = fz_scale_pixmap(image, m.f, m.e, m.b, m.c);
if (scaled == NULL)
return NULL;
ctm->b = scaled->w;
diff --git a/draw/draw_scale.c b/draw/draw_scale.c
index bb766398..b7aa72f3 100644
--- a/draw/draw_scale.c
+++ b/draw/draw_scale.c
@@ -1207,65 +1207,6 @@ scale_single_col(unsigned char *dst, unsigned char *src, fz_weights *weights, in
#endif /* SINGLE_PIXEL_SPECIALS */
fz_pixmap *
-fz_scale_pixmap_gridfit(fz_pixmap *src, float x, float y, float w, float h, int gridfit)
-{
- if (gridfit) {
- float n;
- if (w > 0) {
- /* Adjust the left hand edge, leftwards to a pixel boundary */
- n = (float)(int)x; /* n is now on a pixel boundary */
- if (n > x) /* Ensure it's the pixel boundary BELOW x */
- n -= 1.0f;
- w += x-n; /* width gets wider as x >= n */
- x = n;
- /* Adjust the right hand edge rightwards to a pixel boundary */
- n = (float)(int)w; /* n is now the integer width <= w */
- if (n != w) /* If w isn't an integer already, bump it */
- w = 1.0f + n;/* up to the next integer. */
- } else {
- /* Adjust the right hand edge, rightwards to a pixel boundary */
- n = (float)(int)x; /* n is now on a pixel boundary */
- if (n > x) /* Ensure it's the pixel boundary <= x */
- n -= 1.0f;
- if (n != x) /* If x isn't on a pixel boundary already, */
- n += 1.0f; /* make n be the pixel boundary above x. */
- w -= n-x; /* Expand width (more negative!) as n >= x */
- x = n;
- /* Adjust the left hand edge leftwards to a pixel boundary */
- n = (float)(int)w;
- if (n != w)
- w = n - 1.0f;
- }
- if (h > 0) {
- /* Adjust the bottom edge, downwards to a pixel boundary */
- n = (float)(int)y; /* n is now on a pixel boundary */
- if (n > y) /* Ensure it's the pixel boundary BELOW y */
- n -= 1.0f;
- h += y-n; /* height gets larger as y >= n */
- y = n;
- /* Adjust the top edge upwards to a pixel boundary */
- n = (float)(int)h; /* n is now the integer height <= h */
- if (n != h) /* If h isn't an integer already, bump it */
- h = 1.0f + n;/* up to the next integer. */
- } else {
- /* Adjust the top edge, upwards to a pixel boundary */
- n = (float)(int)y; /* n is now on a pixel boundary */
- if (n > y) /* Ensure it's the pixel boundary <= y */
- n -= 1.0f;
- if (n != y) /* If y isn't on a pixel boundary already, */
- n += 1.0f; /* make n be the pixel boundary above y. */
- h -= n-y; /* Expand height (more negative!) as n >= y */
- y = n;
- /* Adjust the bottom edge downwards to a pixel boundary */
- n = (float)(int)h;
- if (n != h)
- h = n - 1.0f;
- }
- }
- return fz_scale_pixmap(src, x, y, w, h);
-}
-
-fz_pixmap *
fz_scale_pixmap(fz_pixmap *src, float x, float y, float w, float h)
{
fz_scale_filter *filter = &fz_scale_filter_simple;
diff --git a/draw/draw_simple_scale.c b/draw/draw_simple_scale.c
index fc706597..c2d429b1 100644
--- a/draw/draw_simple_scale.c
+++ b/draw/draw_simple_scale.c
@@ -1167,65 +1167,6 @@ scale_single_col(unsigned char *dst, unsigned char *src, fz_weights *weights, in
#endif /* SINGLE_PIXEL_SPECIALS */
fz_pixmap *
-fz_scale_pixmap_gridfit(fz_pixmap *src, float x, float y, float w, float h, int gridfit)
-{
- if (gridfit) {
- float n;
- if (w > 0) {
- /* Adjust the left hand edge, leftwards to a pixel boundary */
- n = (float)(int)x; /* n is now on a pixel boundary */
- if (n > x) /* Ensure it's the pixel boundary BELOW x */
- n -= 1.0f;
- w += x-n; /* width gets wider as x >= n */
- x = n;
- /* Adjust the right hand edge rightwards to a pixel boundary */
- n = (float)(int)w; /* n is now the integer width <= w */
- if (n != w) /* If w isn't an integer already, bump it */
- w = 1.0f + n;/* up to the next integer. */
- } else {
- /* Adjust the right hand edge, rightwards to a pixel boundary */
- n = (float)(int)x; /* n is now on a pixel boundary */
- if (n > x) /* Ensure it's the pixel boundary <= x */
- n -= 1.0f;
- if (n != x) /* If x isn't on a pixel boundary already, */
- n += 1.0f; /* make n be the pixel boundary above x. */
- w -= n-x; /* Expand width (more negative!) as n >= x */
- x = n;
- /* Adjust the left hand edge leftwards to a pixel boundary */
- n = (float)(int)w;
- if (n != w)
- w = n - 1.0f;
- }
- if (h > 0) {
- /* Adjust the bottom edge, downwards to a pixel boundary */
- n = (float)(int)y; /* n is now on a pixel boundary */
- if (n > y) /* Ensure it's the pixel boundary BELOW y */
- n -= 1.0f;
- h += y-n; /* height gets larger as y >= n */
- y = n;
- /* Adjust the top edge upwards to a pixel boundary */
- n = (float)(int)h; /* n is now the integer height <= h */
- if (n != h) /* If h isn't an integer already, bump it */
- h = 1.0f + n;/* up to the next integer. */
- } else {
- /* Adjust the top edge, upwards to a pixel boundary */
- n = (float)(int)y; /* n is now on a pixel boundary */
- if (n > y) /* Ensure it's the pixel boundary <= y */
- n -= 1.0f;
- if (n != y) /* If y isn't on a pixel boundary already, */
- n += 1.0f; /* make n be the pixel boundary above y. */
- h -= n-y; /* Expand height (more negative!) as n >= y */
- y = n;
- /* Adjust the bottom edge downwards to a pixel boundary */
- n = (float)(int)h;
- if (n != h)
- h = n - 1.0f;
- }
- }
- return fz_scale_pixmap(src, x, y, w, h);
-}
-
-fz_pixmap *
fz_scale_pixmap(fz_pixmap *src, float x, float y, float w, float h)
{
fz_scale_filter *filter = &fz_scale_filter_simple;