diff options
-rw-r--r-- | include/mupdf/fitz/device.h | 30 | ||||
-rw-r--r-- | source/fitz/test-device.c | 496 | ||||
-rw-r--r-- | source/tools/mudraw.c | 2 |
3 files changed, 385 insertions, 143 deletions
diff --git a/include/mupdf/fitz/device.h b/include/mupdf/fitz/device.h index caa01cb2..d0a8f0ed 100644 --- a/include/mupdf/fitz/device.h +++ b/include/mupdf/fitz/device.h @@ -296,11 +296,39 @@ fz_device *fz_new_bbox_device(fz_context *ctx, fz_rect *rectp); Currently only tests for the presence of non-grayscale colors. + is_color: Possible values returned: + 0: Definitely greyscale + 1: Probably color (all colors were grey, but there + were images or shadings in a non grey colorspace). + 2: Definitely color + threshold: The difference from grayscale that will be tolerated. Typical values to use are either 0 (be exact) and 0.02 (allow an imperceptible amount of slop). + + options: A set of bitfield options, from the FZ_TEST_OPT set. + + passthrough: A device to pass all calls through to, or NULL. + If set, then the test device can both test and pass through to + an underlying device (like, say, the display list device). This + means that a display list can be created and at the end we'll + know if it's color or not. + + In the absence of a passthrough device, the device will throw + an exception to stop page interpretation when color is found. */ -fz_device *fz_new_test_device(fz_context *ctx, int *is_color, float threshold); +fz_device *fz_new_test_device(fz_context *ctx, int *is_color, float threshold, int options, fz_device *passthrough); + +enum +{ + /* If set, test every pixel of images exhaustively. + * If clear, just look at colorspaces for images. */ + FZ_TEST_OPT_IMAGES = 1, + + /* If set, test every pixel of shadings. */ + /* If clear, just look at colorspaces for shadings. */ + FZ_TEST_OPT_SHADINGS = 2 +}; /* fz_new_draw_device: Create a device to draw on a pixmap. diff --git a/source/fitz/test-device.c b/source/fitz/test-device.c index 15e6a3ff..1c1aa628 100644 --- a/source/fitz/test-device.c +++ b/source/fitz/test-device.c @@ -5,6 +5,9 @@ typedef struct fz_test_device_s fz_device super; int *is_color; float threshold; + int options; + fz_device *passthrough; + int resolved; } fz_test_device; static int @@ -26,19 +29,18 @@ is_rgb_color_u8(int threshold_u8, int r, int g, int b) } static void -fz_test_color(fz_context *ctx, fz_device *dev, fz_colorspace *colorspace, const float *color) +fz_test_color(fz_context *ctx, fz_test_device *t, fz_colorspace *colorspace, const float *color) { - fz_test_device *t = (fz_test_device*)dev; - if (!*t->is_color && colorspace && colorspace != fz_device_gray(ctx)) { if (colorspace == fz_device_rgb(ctx)) { if (is_rgb_color(t->threshold, color[0], color[1], color[2])) { - *t->is_color = 1; - dev->hints |= FZ_IGNORE_IMAGE; - fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); + *t->is_color = 2; + t->resolved = 1; + if (t->passthrough == NULL) + fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); } } else @@ -47,49 +49,81 @@ fz_test_color(fz_context *ctx, fz_device *dev, fz_colorspace *colorspace, const fz_convert_color(ctx, fz_device_rgb(ctx), rgb, colorspace, color); if (is_rgb_color(t->threshold, rgb[0], rgb[1], rgb[2])) { - *t->is_color = 1; - dev->hints |= FZ_IGNORE_IMAGE; - fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); + *t->is_color = 2; + t->resolved = 1; + if (t->passthrough == NULL) + { + t->super.hints |= FZ_IGNORE_IMAGE; + fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); + } } } } } static void -fz_test_fill_path(fz_context *ctx, fz_device *dev, const fz_path *path, int even_odd, const fz_matrix *ctm, +fz_test_fill_path(fz_context *ctx, fz_device *dev_, const fz_path *path, int even_odd, const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha) { - if (alpha != 0.0f) - fz_test_color(ctx, dev, colorspace, color); + fz_test_device *dev = (fz_test_device*)dev_; + + if (dev->resolved == 0) + { + if (alpha != 0.0f) + fz_test_color(ctx, dev, colorspace, color); + } + if (dev->passthrough) + fz_fill_path(ctx, dev->passthrough, path, even_odd, ctm, colorspace, color, alpha); } static void -fz_test_stroke_path(fz_context *ctx, fz_device *dev, const fz_path *path, const fz_stroke_state *stroke, +fz_test_stroke_path(fz_context *ctx, fz_device *dev_, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha) { - if (alpha != 0.0f) - fz_test_color(ctx, dev, colorspace, color); + fz_test_device *dev = (fz_test_device*)dev_; + + if (dev->resolved == 0) + { + if (alpha != 0.0f) + fz_test_color(ctx, dev, colorspace, color); + } + if (dev->passthrough) + fz_stroke_path(ctx, dev->passthrough, path, stroke, ctm, colorspace, color, alpha); } static void -fz_test_fill_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_matrix *ctm, +fz_test_fill_text(fz_context *ctx, fz_device *dev_, const fz_text *text, const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha) { - if (alpha != 0.0f) - fz_test_color(ctx, dev, colorspace, color); + fz_test_device *dev = (fz_test_device*)dev_; + + if (dev->resolved == 0) + { + if (alpha != 0.0f) + fz_test_color(ctx, dev, colorspace, color); + } + if (dev->passthrough) + fz_fill_text(ctx, dev->passthrough, text, ctm, colorspace, color, alpha); } static void -fz_test_stroke_text(fz_context *ctx, fz_device *dev, const fz_text *text, const fz_stroke_state *stroke, +fz_test_stroke_text(fz_context *ctx, fz_device *dev_, const fz_text *text, const fz_stroke_state *stroke, const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha) { - if (alpha != 0.0f) - fz_test_color(ctx, dev, colorspace, color); + fz_test_device *dev = (fz_test_device*)dev_; + + if (dev->resolved == 0) + { + if (alpha != 0.0f) + fz_test_color(ctx, dev, colorspace, color); + } + if (dev->passthrough) + fz_stroke_text(ctx, dev->passthrough, text, stroke, ctm, colorspace, color, alpha); } struct shadearg { - fz_device *dev; + fz_test_device *dev; fz_shade *shade; }; @@ -97,176 +131,336 @@ static void prepare_vertex(fz_context *ctx, void *arg_, fz_vertex *v, const float *color) { struct shadearg *arg = arg_; - fz_device *dev = arg->dev; + fz_test_device *dev = arg->dev; fz_shade *shade = arg->shade; if (!shade->use_function) fz_test_color(ctx, dev, shade->colorspace, color); } static void -fz_test_fill_shade(fz_context *ctx, fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) +fz_test_fill_shade(fz_context *ctx, fz_device *dev_, fz_shade *shade, const fz_matrix *ctm, float alpha) { - if (shade->use_function) - { - int i; - for (i = 0; i < 256; i++) - fz_test_color(ctx, dev, shade->colorspace, shade->function[i]); - } - else + fz_test_device *dev = (fz_test_device*)dev_; + + if (dev->resolved == 0) { - struct shadearg arg; - arg.dev = dev; - arg.shade = shade; - fz_process_mesh(ctx, shade, ctm, prepare_vertex, NULL, &arg); + if ((dev->options & FZ_TEST_OPT_SHADINGS) == 0) + { + if (shade->colorspace != fz_device_gray(ctx)) + { + /* Don't test every pixel. Upgrade us from "black and white" to "probably color" */ + if (*dev->is_color == 0) + *dev->is_color = 1; + dev->resolved = 1; + if (dev->passthrough == NULL) + fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); + } + } + else + { + if (shade->use_function) + { + int i; + for (i = 0; i < 256; i++) + fz_test_color(ctx, dev, shade->colorspace, shade->function[i]); + } + else + { + struct shadearg arg; + arg.dev = dev; + arg.shade = shade; + fz_process_mesh(ctx, shade, ctm, prepare_vertex, NULL, &arg); + } + } } + if (dev->passthrough) + fz_fill_shade(ctx, dev->passthrough, shade, ctm, alpha); } static void -fz_test_fill_image(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha) +fz_test_fill_image(fz_context *ctx, fz_device *dev_, fz_image *image, const fz_matrix *ctm, float alpha) { - fz_test_device *t = (fz_test_device*)dev; + fz_test_device *dev = (fz_test_device*)dev_; - fz_pixmap *pix; - unsigned int count, i, k, h, sa, ss; - unsigned char *s; - fz_compressed_buffer *buffer; + while (dev->resolved == 0) /* So we can break out */ + { + fz_pixmap *pix; + unsigned int count, i, k, h, sa, ss; + unsigned char *s; + fz_compressed_buffer *buffer; - if (*t->is_color || !image->colorspace || image->colorspace == fz_device_gray(ctx)) - return; + if (*dev->is_color || !image->colorspace || image->colorspace == fz_device_gray(ctx)) + break; - buffer = fz_compressed_image_buffer(ctx, image); - if (buffer && image->bpc == 8) - { - fz_stream *stream = fz_open_compressed_buffer(ctx, buffer); - count = (unsigned int)image->w * (unsigned int)image->h; - if (image->colorspace == fz_device_rgb(ctx)) + if ((dev->options & FZ_TEST_OPT_IMAGES) == 0) { - int threshold_u8 = t->threshold * 255; - for (i = 0; i < count; i++) + /* Don't test every pixel. Upgrade us from "black and white" to "probably color" */ + if (*dev->is_color == 0) + *dev->is_color = 1; + dev->resolved = 1; + if (dev->passthrough == NULL) + fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); + break; + } + + buffer = fz_compressed_image_buffer(ctx, image); + if (buffer && image->bpc == 8) + { + fz_stream *stream = fz_open_compressed_buffer(ctx, buffer); + count = (unsigned int)image->w * (unsigned int)image->h; + if (image->colorspace == fz_device_rgb(ctx)) { - int r = fz_read_byte(ctx, stream); - int g = fz_read_byte(ctx, stream); - int b = fz_read_byte(ctx, stream); - if (is_rgb_color_u8(threshold_u8, r, g, b)) + int threshold_u8 = dev->threshold * 255; + for (i = 0; i < count; i++) { - *t->is_color = 1; - dev->hints |= FZ_IGNORE_IMAGE; - fz_drop_stream(ctx, stream); - fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); - break; + int r = fz_read_byte(ctx, stream); + int g = fz_read_byte(ctx, stream); + int b = fz_read_byte(ctx, stream); + if (is_rgb_color_u8(threshold_u8, r, g, b)) + { + *dev->is_color = 1; + dev->resolved = 1; + fz_drop_stream(ctx, stream); + if (dev->passthrough == NULL) + fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); + break; + } } } - } - else - { - fz_color_converter cc; - unsigned int n = (unsigned int)image->n; - - fz_init_cached_color_converter(ctx, &cc, fz_device_rgb(ctx), image->colorspace); - for (i = 0; i < count; i++) + else { - float cs[FZ_MAX_COLORS]; - float ds[FZ_MAX_COLORS]; + fz_color_converter cc; + unsigned int n = (unsigned int)image->n; - for (k = 0; k < n; k++) - cs[k] = fz_read_byte(ctx, stream) / 255.0f; - - cc.convert(ctx, &cc, ds, cs); - - if (is_rgb_color(t->threshold, ds[0], ds[1], ds[2])) + fz_init_cached_color_converter(ctx, &cc, fz_device_rgb(ctx), image->colorspace); + for (i = 0; i < count; i++) { - *t->is_color = 1; - dev->hints |= FZ_IGNORE_IMAGE; - fz_drop_stream(ctx, stream); - fz_fin_cached_color_converter(ctx, &cc); - fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); - break; + float cs[FZ_MAX_COLORS]; + float ds[FZ_MAX_COLORS]; + + for (k = 0; k < n; k++) + cs[k] = fz_read_byte(ctx, stream) / 255.0f; + + cc.convert(ctx, &cc, ds, cs); + + if (is_rgb_color(dev->threshold, ds[0], ds[1], ds[2])) + { + *dev->is_color = 1; + dev->resolved = 1; + if (dev->passthrough == NULL) + { + fz_drop_stream(ctx, stream); + fz_fin_cached_color_converter(ctx, &cc); + fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); + } + break; + } } + fz_fin_cached_color_converter(ctx, &cc); } - fz_fin_cached_color_converter(ctx, &cc); + fz_drop_stream(ctx, stream); + break; } - fz_drop_stream(ctx, stream); - return; - } - pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, 0, 0); - if (pix == NULL) /* Should never happen really, but... */ - return; + pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, 0, 0); + if (pix == NULL) /* Should never happen really, but... */ + break; - count = pix->w; - h = pix->h; - s = pix->samples; - sa = pix->alpha; - ss = pix->stride - pix->w * pix->n; + count = pix->w; + h = pix->h; + s = pix->samples; + sa = pix->alpha; + ss = pix->stride - pix->w * pix->n; - if (pix->colorspace == fz_device_rgb(ctx)) - { - int threshold_u8 = t->threshold * 255; - while (h--) + if (pix->colorspace == fz_device_rgb(ctx)) { - for (i = 0; i < count; i++) + int threshold_u8 = dev->threshold * 255; + while (h--) { - if ((!sa || s[3] != 0) && is_rgb_color_u8(threshold_u8, s[0], s[1], s[2])) + for (i = 0; i < count; i++) { - *t->is_color = 1; - dev->hints |= FZ_IGNORE_IMAGE; - fz_drop_pixmap(ctx, pix); - fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); - break; + if ((!sa || s[3] != 0) && is_rgb_color_u8(threshold_u8, s[0], s[1], s[2])) + { + *dev->is_color = 1; + dev->resolved = 1; + if (dev->passthrough == NULL) + { + fz_drop_pixmap(ctx, pix); + fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); + } + break; + } + s += 3 + sa; } - s += 3 + sa; + s += ss; } - s += ss; } - } - else - { - fz_color_converter cc; - unsigned int n = (unsigned int)pix->n-1; - - fz_init_cached_color_converter(ctx, &cc, fz_device_rgb(ctx), pix->colorspace); - while (h--) + else { - for (i = 0; i < count; i++) - { - float cs[FZ_MAX_COLORS]; - float ds[FZ_MAX_COLORS]; - - for (k = 0; k < n; k++) - cs[k] = (*s++) / 255.0f; - if (sa && *s++ == 0) - continue; - - cc.convert(ctx, &cc, ds, cs); + fz_color_converter cc; + unsigned int n = (unsigned int)pix->n-1; - if (is_rgb_color(t->threshold, ds[0], ds[1], ds[2])) + fz_init_cached_color_converter(ctx, &cc, fz_device_rgb(ctx), pix->colorspace); + while (h--) + { + for (i = 0; i < count; i++) { - *t->is_color = 1; - dev->hints |= FZ_IGNORE_IMAGE; - fz_fin_cached_color_converter(ctx, &cc); - fz_drop_pixmap(ctx, pix); - fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); - break; + float cs[FZ_MAX_COLORS]; + float ds[FZ_MAX_COLORS]; + + for (k = 0; k < n; k++) + cs[k] = (*s++) / 255.0f; + if (sa && *s++ == 0) + continue; + + cc.convert(ctx, &cc, ds, cs); + + if (is_rgb_color(dev->threshold, ds[0], ds[1], ds[2])) + { + *dev->is_color = 1; + dev->resolved = 1; + if (dev->passthrough == NULL) + { + fz_fin_cached_color_converter(ctx, &cc); + fz_drop_pixmap(ctx, pix); + fz_throw(ctx, FZ_ERROR_ABORT, "Page found as color; stopping interpretation"); + } + break; + } } + s += ss; } - s += ss; + fz_fin_cached_color_converter(ctx, &cc); } - fz_fin_cached_color_converter(ctx, &cc); - } - fz_drop_pixmap(ctx, pix); + fz_drop_pixmap(ctx, pix); + break; + } + if (dev->passthrough) + fz_fill_image(ctx, dev->passthrough, image, ctm, alpha); } static void -fz_test_fill_image_mask(fz_context *ctx, fz_device *dev, fz_image *image, const fz_matrix *ctm, +fz_test_fill_image_mask(fz_context *ctx, fz_device *dev_, fz_image *image, const fz_matrix *ctm, fz_colorspace *colorspace, const float *color, float alpha) { - /* We assume that at least some of the image pixels are non-zero */ - fz_test_color(ctx, dev, colorspace, color); + fz_test_device *dev = (fz_test_device*)dev_; + + if (dev->resolved == 0) + { + /* We assume that at least some of the image pixels are non-zero */ + fz_test_color(ctx, dev, colorspace, color); + } + if (dev->passthrough) + fz_fill_image_mask(ctx, dev->passthrough, image, ctm, colorspace, color, alpha); +} + +static void +fz_test_clip_path(fz_context *ctx, fz_device *dev_, const fz_path *path, int even_odd, const fz_matrix *ctm, const fz_rect *scissor) +{ + fz_test_device *dev = (fz_test_device*)dev_; + + fz_clip_path(ctx, dev->passthrough, path, even_odd, ctm, scissor); +} + +static void +fz_test_clip_stroke_path(fz_context *ctx, fz_device *dev_, const fz_path *path, const fz_stroke_state *stroke, const fz_matrix *ctm, const fz_rect *scissor) +{ + fz_test_device *dev = (fz_test_device*)dev_; + + fz_clip_stroke_path(ctx, dev->passthrough, path, stroke, ctm, scissor); +} + +static void +fz_test_clip_text(fz_context *ctx, fz_device *dev_, const fz_text *text, const fz_matrix *ctm, const fz_rect *scissor) +{ + fz_test_device *dev = (fz_test_device*)dev_; + + fz_clip_text(ctx, dev->passthrough, text, ctm, scissor); +} + +static void +fz_test_clip_stroke_text(fz_context *ctx, fz_device *dev_, const fz_text *text, const fz_stroke_state *stroke, const fz_matrix *ctm, const fz_rect *scissor) +{ + fz_test_device *dev = (fz_test_device*)dev_; + + fz_clip_stroke_text(ctx, dev->passthrough, text, stroke, ctm, scissor); +} + +static void +fz_test_ignore_text(fz_context *ctx, fz_device *dev_, const fz_text *text, const fz_matrix *ctm) +{ + fz_test_device *dev = (fz_test_device*)dev_; + + fz_ignore_text(ctx, dev->passthrough, text, ctm); +} + +static void +fz_test_clip_image_mask(fz_context *ctx, fz_device *dev_, fz_image *img, const fz_matrix *ctm, const fz_rect *scissor) +{ + fz_test_device *dev = (fz_test_device*)dev_; + + fz_clip_image_mask(ctx, dev->passthrough, img, ctm, scissor); +} + +static void +fz_test_pop_clip(fz_context *ctx, fz_device *dev_) +{ + fz_test_device *dev = (fz_test_device*)dev_; + + fz_pop_clip(ctx, dev->passthrough); +} + +static void +fz_test_begin_mask(fz_context *ctx, fz_device *dev_, const fz_rect *rect, int luminosity, fz_colorspace *cs, const float *bc) +{ + fz_test_device *dev = (fz_test_device*)dev_; + + fz_begin_mask(ctx, dev->passthrough, rect, luminosity, cs, bc); +} + +static void +fz_test_end_mask(fz_context *ctx, fz_device *dev_) +{ + fz_test_device *dev = (fz_test_device*)dev_; + + fz_end_mask(ctx, dev->passthrough); +} + +static void +fz_test_begin_group(fz_context *ctx, fz_device *dev_, const fz_rect *rect, int isolated, int knockout, int blendmode, float alpha) +{ + fz_test_device *dev = (fz_test_device*)dev_; + + fz_begin_group(ctx, dev->passthrough, rect, isolated, knockout, blendmode, alpha); +} + +static void +fz_test_end_group(fz_context *ctx, fz_device *dev_) +{ + fz_test_device *dev = (fz_test_device*)dev_; + + fz_end_group(ctx, dev->passthrough); +} + +static int +fz_test_begin_tile(fz_context *ctx, fz_device *dev_, const fz_rect *area, const fz_rect *view, float xstep, float ystep, const fz_matrix *ctm, int id) +{ + fz_test_device *dev = (fz_test_device*)dev_; + + return fz_begin_tile_id(ctx, dev->passthrough, area, view, xstep, ystep, ctm, id); +} + +static void +fz_test_end_tile(fz_context *ctx, fz_device *dev_) +{ + fz_test_device *dev = (fz_test_device*)dev_; + + fz_end_tile(ctx, dev->passthrough); } fz_device * -fz_new_test_device(fz_context *ctx, int *is_color, float threshold) +fz_new_test_device(fz_context *ctx, int *is_color, float threshold, int options, fz_device *passthrough) { fz_test_device *dev = fz_new_device(ctx, sizeof *dev); @@ -278,8 +472,28 @@ fz_new_test_device(fz_context *ctx, int *is_color, float threshold) dev->super.fill_image = fz_test_fill_image; dev->super.fill_image_mask = fz_test_fill_image_mask; + if (passthrough) + { + dev->super.clip_path = fz_test_clip_path; + dev->super.clip_stroke_path = fz_test_clip_stroke_path; + dev->super.clip_text = fz_test_clip_text; + dev->super.clip_stroke_text = fz_test_clip_stroke_text; + dev->super.ignore_text = fz_test_ignore_text; + dev->super.clip_image_mask = fz_test_clip_image_mask; + dev->super.pop_clip = fz_test_pop_clip; + dev->super.begin_mask = fz_test_begin_mask; + dev->super.end_mask = fz_test_end_mask; + dev->super.begin_group = fz_test_begin_group; + dev->super.end_group = fz_test_end_group; + dev->super.begin_tile = fz_test_begin_tile; + dev->super.end_tile = fz_test_end_tile; + } + dev->is_color = is_color; + dev->options = options; dev->threshold = threshold; + dev->passthrough = passthrough; + dev->resolved = 0; *dev->is_color = 0; diff --git a/source/tools/mudraw.c b/source/tools/mudraw.c index 85553ea8..12d5247a 100644 --- a/source/tools/mudraw.c +++ b/source/tools/mudraw.c @@ -1147,7 +1147,7 @@ static void drawpage(fz_context *ctx, fz_document *doc, int pagenum) if (showfeatures) { int iscolor; - dev = fz_new_test_device(ctx, &iscolor, 0.02f); + dev = fz_new_test_device(ctx, &iscolor, 0.02f, 0, NULL); if (lowmemory) fz_enable_device_hints(ctx, dev, FZ_NO_CACHE); fz_try(ctx) |