diff options
author | Robin Watts <robin.watts@artifex.com> | 2015-07-13 16:23:14 +0100 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2015-07-20 17:19:03 +0100 |
commit | 269fdfce2527cbba02e68d658af3f4b92ef40be0 (patch) | |
tree | c75e9e4abdae745c64f98eaafa86d572e747f536 /source | |
parent | d8353609755104010afaf3e1967a36959f28c5dc (diff) | |
download | mupdf-269fdfce2527cbba02e68d658af3f4b92ef40be0.tar.xz |
Improve Grid fitting of images for .gproof files.
By default in MuPDF, when we render an axis aligned image, we
'gridfit' it. This is a heuristic used to improve the rendering
of tiled images, and avoid the background showing through on the
antialiased edges.
The general algorithm we use is to expand any image outwards so that
it completely covers any pixels that it touches any part of. This is
'safe' in that we never cause any pixels to not be covered that
should otherwise be so, and is important when we have images that are
aligned with (say) line art rectangles.
For gproof files though, this gives nasty results - because we have
multiple images tiled across the page all exactly abutting, in most
cases the edges will not be on exact integer coordinates. This means
we expand both images and 1 (destination) pixel is lost. This severely
hurts the rendering (in particular on text based pages).
We therefore introduce a new type of grid fitting, where we simply
align the edges of images to the closest integer pixel. This is safe
because we know that neighbouring images will be adjusted identically
and edges will stay coincident.
We enable/disable this behaviour through a new device flag, and make
the gproof interpreter set/clear this flag when generating the page -
thus normal rendering is unaffected.
We *could* have just poked the dev->flags fields directly, but that
would require magic in the display list device to check for them
being set/unset and to poke the dev->flags fields on playback, so
instead we introduce a new fz_render_flags function (that calls a
device function) to set/unset flags.
The other attraction of this is that if we ever have devices that
'filter', we can neatly handle passing flag changes on with those.
Currently the display list implementation only copes with set/clear
of the FZ_DEVFLAG_GRIDFIT_AS_TILED option. We only readily have 6
bits available to us, so we'll just extend this as required if we
add new render flags.
Diffstat (limited to 'source')
-rw-r--r-- | source/fitz/device.c | 7 | ||||
-rw-r--r-- | source/fitz/draw-affine.c | 71 | ||||
-rw-r--r-- | source/fitz/draw-device.c | 24 | ||||
-rw-r--r-- | source/fitz/draw-imp.h | 4 | ||||
-rw-r--r-- | source/fitz/list-device.c | 45 | ||||
-rw-r--r-- | source/gprf/gprf-doc.c | 2 |
6 files changed, 132 insertions, 21 deletions
diff --git a/source/fitz/device.c b/source/fitz/device.c index 5e3bb61a..6df650f4 100644 --- a/source/fitz/device.c +++ b/source/fitz/device.c @@ -488,3 +488,10 @@ fz_end_tile(fz_context *ctx, fz_device *dev) if (dev->end_tile) dev->end_tile(ctx, dev); } + +void +fz_render_flags(fz_context *ctx, fz_device *dev, int set, int clear) +{ + if (dev->render_flags) + dev->render_flags(ctx, dev, set, clear); +} diff --git a/source/fitz/draw-affine.c b/source/fitz/draw-affine.c index ec19d936..1c6c26d1 100644 --- a/source/fitz/draw-affine.c +++ b/source/fitz/draw-affine.c @@ -807,12 +807,31 @@ fz_paint_affine_color_near(byte *dp, byte *sp, int sw, int sh, int u, int v, int */ #define MY_EPSILON 0.001 +/* We have 2 possible ways of gridfitting images. The first way, considered + * 'safe' in all cases, is to expand an image out to fill a box that entirely + * covers all the pixels touched by the current image. This is our 'standard' + * mechanism. + * The alternative, used when we know images are tiled across a page, is to + * round the edge of each image to the closest integer pixel boundary. This + * would not be safe in the general case, but gives less distortion across + * neighbouring images when tiling is used. We use this for .gproof files. + */ void -fz_gridfit_matrix(fz_matrix *m) +fz_gridfit_matrix(int as_tiled, fz_matrix *m) { if (fabsf(m->b) < FLT_EPSILON && fabsf(m->c) < FLT_EPSILON) { - if (m->a > 0) + if (as_tiled) + { + float f; + /* Nearest boundary for left */ + f = (float)(int)(m->e + 0.5); + m->a += m->e - f; /* Adjust width for change */ + m->e = f; + /* Nearest boundary for right (width really) */ + m->a = (float)(int)(m->a + 0.5); + } + else if (m->a > 0) { float f; /* Adjust left hand side onto pixel boundary */ @@ -842,7 +861,17 @@ fz_gridfit_matrix(fz_matrix *m) f -= 1.0; /* Ensure it moves left */ m->a = f; } - if (m->d > 0) + if (as_tiled) + { + float f; + /* Nearest boundary for top */ + f = (float)(int)(m->f + 0.5); + m->d += m->f - f; /* Adjust width for change */ + m->f = f; + /* Nearest boundary for bottom (height really) */ + m->d = (float)(int)(m->d + 0.5); + } + else if (m->d > 0) { float f; /* Adjust top onto pixel boundary */ @@ -875,7 +904,17 @@ fz_gridfit_matrix(fz_matrix *m) } else if (fabsf(m->a) < FLT_EPSILON && fabsf(m->d) < FLT_EPSILON) { - if (m->b > 0) + if (as_tiled) + { + float f; + /* Nearest boundary for left */ + f = (float)(int)(m->e + 0.5); + m->b += m->e - f; /* Adjust width for change */ + m->e = f; + /* Nearest boundary for right (width really) */ + m->b = (float)(int)(m->b + 0.5); + } + else if (m->b > 0) { float f; /* Adjust left hand side onto pixel boundary */ @@ -905,7 +944,17 @@ fz_gridfit_matrix(fz_matrix *m) f -= 1.0; /* Ensure it moves left */ m->b = f; } - if (m->c > 0) + if (as_tiled) + { + float f; + /* Nearest boundary for left */ + f = (float)(int)(m->f + 0.5); + m->c += m->f - f; /* Adjust width for change */ + m->f = f; + /* Nearest boundary for right (width really) */ + m->c = (float)(int)(m->c + 0.5); + } + else if (m->c > 0) { float f; /* Adjust top onto pixel boundary */ @@ -941,7 +990,7 @@ fz_gridfit_matrix(fz_matrix *m) /* Draw an image with an affine transform on destination */ static void -fz_paint_image_imp(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz_pixmap *img, const fz_matrix *ctm, byte *color, int alpha, int lerp_allowed) +fz_paint_image_imp(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz_pixmap *img, const fz_matrix *ctm, byte *color, int alpha, int lerp_allowed, int as_tiled) { byte *dp, *sp, *hp; int u, v, fa, fb, fc, fd; @@ -955,7 +1004,7 @@ fz_paint_image_imp(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz int is_rectilinear; /* grid fit the image */ - fz_gridfit_matrix(&local_ctm); + fz_gridfit_matrix(as_tiled, &local_ctm); /* turn on interpolation for upscaled and non-rectilinear transforms */ dolerp = 0; @@ -1084,15 +1133,15 @@ fz_paint_image_imp(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz } void -fz_paint_image_with_color(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz_pixmap *img, const fz_matrix *ctm, byte *color, int lerp_allowed) +fz_paint_image_with_color(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz_pixmap *img, const fz_matrix *ctm, byte *color, int lerp_allowed, int as_tiled) { assert(img->n == 1); - fz_paint_image_imp(dst, scissor, shape, img, ctm, color, 255, lerp_allowed); + fz_paint_image_imp(dst, scissor, shape, img, ctm, color, 255, lerp_allowed, as_tiled); } void -fz_paint_image(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz_pixmap *img, const fz_matrix *ctm, int alpha, int lerp_allowed) +fz_paint_image(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz_pixmap *img, const fz_matrix *ctm, int alpha, int lerp_allowed, int as_tiled) { assert(dst->n == img->n || (dst->n == 4 && img->n == 2)); - fz_paint_image_imp(dst, scissor, shape, img, ctm, NULL, alpha, lerp_allowed); + fz_paint_image_imp(dst, scissor, shape, img, ctm, NULL, alpha, lerp_allowed, as_tiled); } diff --git a/source/fitz/draw-device.c b/source/fitz/draw-device.c index a2499016..fdb631f4 100644 --- a/source/fitz/draw-device.c +++ b/source/fitz/draw-device.c @@ -610,7 +610,7 @@ fz_draw_fill_text(fz_context *ctx, fz_device *devp, fz_text *text, const fz_matr fz_matrix mat; mat.a = pixmap->w; mat.b = mat.c = 0; mat.d = pixmap->h; mat.e = x + pixmap->x; mat.f = y + pixmap->y; - fz_paint_image(state->dest, &state->scissor, state->shape, pixmap, &mat, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES)); + fz_paint_image(state->dest, &state->scissor, state->shape, pixmap, &mat, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED); } fz_drop_glyph(ctx, glyph); } @@ -1040,7 +1040,9 @@ fz_transform_pixmap(fz_context *ctx, fz_draw_device *dev, fz_pixmap *image, fz_m /* Unrotated or X-flip or Y-flip or XY-flip */ fz_matrix m = *ctm; if (gridfit) - fz_gridfit_matrix(&m); + { + fz_gridfit_matrix(dev->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, &m); + } scaled = fz_scale_pixmap_cached(ctx, image, m.e, m.f, m.a, m.d, clip, dev->cache_x, dev->cache_y); if (!scaled) return NULL; @@ -1057,7 +1059,7 @@ fz_transform_pixmap(fz_context *ctx, fz_draw_device *dev, fz_pixmap *image, fz_m fz_matrix m = *ctm; fz_irect rclip; if (gridfit) - fz_gridfit_matrix(&m); + fz_gridfit_matrix(dev->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED, &m); if (clip) { rclip.x0 = clip->y0; @@ -1174,7 +1176,7 @@ fz_draw_fill_image(fz_context *ctx, fz_device *devp, fz_image *image, const fz_m } } - fz_paint_image(state->dest, &state->scissor, state->shape, pixmap, &local_ctm, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES)); + fz_paint_image(state->dest, &state->scissor, state->shape, pixmap, &local_ctm, alpha * 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED); if (state->blendmode & FZ_BLEND_KNOCKOUT) fz_knockout_end(ctx, dev); @@ -1245,7 +1247,7 @@ fz_draw_fill_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, const colorbv[i] = colorfv[i] * 255; colorbv[i] = alpha * 255; - fz_paint_image_with_color(state->dest, &state->scissor, state->shape, pixmap, &local_ctm, colorbv, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES)); + fz_paint_image_with_color(state->dest, &state->scissor, state->shape, pixmap, &local_ctm, colorbv, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED); if (scaled) fz_drop_pixmap(ctx, scaled); @@ -1351,7 +1353,7 @@ fz_draw_clip_image_mask(fz_context *ctx, fz_device *devp, fz_image *image, const if (scaled) pixmap = scaled; } - fz_paint_image(mask, &bbox, state->shape, pixmap, &local_ctm, 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES)); + fz_paint_image(mask, &bbox, state->shape, pixmap, &local_ctm, 255, !(devp->hints & FZ_DONT_INTERPOLATE_IMAGES), devp->flags & FZ_DEVFLAG_GRIDFIT_AS_TILED); } fz_always(ctx) { @@ -2040,6 +2042,14 @@ fz_draw_drop_imp(fz_context *ctx, fz_device *devp) fz_drop_gel(ctx, gel); } +static void +fz_draw_render_flags(fz_context *ctx, fz_device *devp, int set, int clear) +{ + fz_draw_device *dev = (fz_draw_device*)devp; + + dev->flags = (dev->flags | set ) & ~clear; +} + fz_device * fz_new_draw_device(fz_context *ctx, fz_pixmap *dest) { @@ -2073,6 +2083,8 @@ fz_new_draw_device(fz_context *ctx, fz_pixmap *dest) dev->super.begin_tile = fz_draw_begin_tile; dev->super.end_tile = fz_draw_end_tile; + dev->super.render_flags = fz_draw_render_flags; + dev->flags = 0; dev->top = 0; dev->stack = &dev->init_stack[0]; diff --git a/source/fitz/draw-imp.h b/source/fitz/draw-imp.h index 3a1ddbd2..8a750860 100644 --- a/source/fitz/draw-imp.h +++ b/source/fitz/draw-imp.h @@ -35,8 +35,8 @@ void fz_paint_solid_color(unsigned char * restrict dp, int n, int w, unsigned ch void fz_paint_span(unsigned char * restrict dp, unsigned char * restrict sp, int n, int w, int alpha); void fz_paint_span_with_color(unsigned char * restrict dp, unsigned char * restrict mp, int n, int w, unsigned char *color); -void fz_paint_image(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz_pixmap *img, const fz_matrix *ctm, int alpha, int lerp_allowed); -void fz_paint_image_with_color(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz_pixmap *img, const fz_matrix *ctm, unsigned char *colorbv, int lerp_allowed); +void fz_paint_image(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz_pixmap *img, const fz_matrix *ctm, int alpha, int lerp_allowed, int gridfit_as_tiled); +void fz_paint_image_with_color(fz_pixmap *dst, const fz_irect *scissor, fz_pixmap *shape, fz_pixmap *img, const fz_matrix *ctm, unsigned char *colorbv, int lerp_allowed, int gridfit_as_tiled); void fz_paint_pixmap(fz_pixmap *dst, fz_pixmap *src, int alpha); void fz_paint_pixmap_with_mask(fz_pixmap *dst, fz_pixmap *src, fz_pixmap *msk); diff --git a/source/fitz/list-device.c b/source/fitz/list-device.c index 038517b4..0a282018 100644 --- a/source/fitz/list-device.c +++ b/source/fitz/list-device.c @@ -28,7 +28,8 @@ typedef enum fz_display_command_e FZ_CMD_BEGIN_GROUP, FZ_CMD_END_GROUP, FZ_CMD_BEGIN_TILE, - FZ_CMD_END_TILE + FZ_CMD_END_TILE, + FZ_CMD_RENDER_FLAGS } fz_display_command; /* The display list is a list of nodes. @@ -1212,6 +1213,37 @@ fz_list_end_tile(fz_context *ctx, fz_device *dev) } static void +fz_list_render_flags(fz_context *ctx, fz_device *dev, int set, int clear) +{ + int flags; + + /* Pack the options down */ + if (set == FZ_DEVFLAG_GRIDFIT_AS_TILED && clear == 0) + flags = 1; + else if (set == 0 && clear == FZ_DEVFLAG_GRIDFIT_AS_TILED) + flags = 0; + else + { + assert("Unsupported flags combination" == NULL); + return; + } + fz_append_display_node( + ctx, + dev, + FZ_CMD_RENDER_FLAGS, + flags, /* flags */ + NULL, + NULL, /* path */ + NULL, /* color */ + NULL, /* colorspace */ + NULL, /* alpha */ + NULL, /* ctm */ + NULL, /* stroke */ + NULL, /* private_data */ + 0); /* private_data_len */ +} + +static void drop_writer(fz_context *ctx, fz_device *dev) { fz_list_device *writer = (fz_list_device *)dev; @@ -1257,6 +1289,8 @@ fz_new_list_device(fz_context *ctx, fz_display_list *list) dev->super.begin_tile = fz_list_begin_tile; dev->super.end_tile = fz_list_end_tile; + dev->super.render_flags = fz_list_render_flags; + dev->super.drop_imp = drop_writer; dev->list = list; @@ -1570,7 +1604,8 @@ fz_run_display_list(fz_context *ctx, fz_display_list *list, fz_device *dev, cons if (tiled || n.cmd == FZ_CMD_BEGIN_TILE || n.cmd == FZ_CMD_END_TILE || - n.cmd == FZ_CMD_BEGIN_PAGE || n.cmd == FZ_CMD_END_PAGE) + n.cmd == FZ_CMD_BEGIN_PAGE || n.cmd == FZ_CMD_END_PAGE || + n.cmd == FZ_CMD_RENDER_FLAGS) { empty = 0; } @@ -1700,6 +1735,12 @@ visible: tiled--; fz_end_tile(ctx, dev); break; + case FZ_CMD_RENDER_FLAGS: + if (n.flags == 0) + fz_render_flags(ctx, dev, 0, FZ_DEVFLAG_GRIDFIT_AS_TILED); + else if (n.flags == 1) + fz_render_flags(ctx, dev, FZ_DEVFLAG_GRIDFIT_AS_TILED, 0); + break; } } fz_catch(ctx) diff --git a/source/gprf/gprf-doc.c b/source/gprf/gprf-doc.c index bcd631ad..e9638340 100644 --- a/source/gprf/gprf-doc.c +++ b/source/gprf/gprf-doc.c @@ -596,6 +596,7 @@ gprf_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, const fz_matrix * page_rect.y0 = 0; page_rect.x1 = 72.0 * page->width / doc->res; page_rect.y1 = 72.0 * page->height / doc->res; + fz_render_flags(ctx, dev, FZ_DEVFLAG_GRIDFIT_AS_TILED, 0); fz_begin_page(ctx, dev, &page_rect, ctm); i = 0; @@ -618,6 +619,7 @@ gprf_run_page(fz_context *ctx, fz_page *page_, fz_device *dev, const fz_matrix * } } fz_end_page(ctx, dev); + fz_render_flags(ctx, dev, 0, FZ_DEVFLAG_GRIDFIT_AS_TILED); } |