diff options
author | Tor Andersson <tor.andersson@artifex.com> | 2014-08-22 15:35:13 +0200 |
---|---|---|
committer | Tor Andersson <tor.andersson@artifex.com> | 2014-08-27 13:15:56 +0200 |
commit | 7662af082b44f8164d1adadc5688f30d888e6a63 (patch) | |
tree | 61867c232cebef6f510eef1e404f81c4411c4c2e /source | |
parent | 357162fe1eaec81638611d57ed178d8dfc3cdc47 (diff) | |
download | mupdf-7662af082b44f8164d1adadc5688f30d888e6a63.tar.xz |
Revise test-device; thresholding and exhaustive checking.
The original version of the test-device could characterise pages
as being grayscale/color purely based on the colorspaces used.
This could easily be upset by grayscale images or shadings that
happened to be specified in non-grayscale colorspaces however.
We now look at the actual shading and image color values, and use
a threshold value to allow for some measure of rounding errors in
color values that are in practice grayscale.
Diffstat (limited to 'source')
-rw-r--r-- | source/fitz/test-device.c | 168 | ||||
-rw-r--r-- | source/tools/mudraw.c | 2 |
2 files changed, 149 insertions, 21 deletions
diff --git a/source/fitz/test-device.c b/source/fitz/test-device.c index ffa3917b..d4193c43 100644 --- a/source/fitz/test-device.c +++ b/source/fitz/test-device.c @@ -1,21 +1,49 @@ #include <mupdf/fitz.h> -static void -fz_test_colorspace(fz_context *ctx, fz_colorspace *colorspace, int *iscolor) +struct test +{ + int *is_color; + float threshold; +}; + +static int +is_rgb_color(float threshold, float r, float g, float b) +{ + float rg_diff = fz_abs(r - g); + float rb_diff = fz_abs(r - b); + float gb_diff = fz_abs(g - b); + return rg_diff > threshold || rb_diff > threshold || gb_diff > threshold; +} + +static int +is_rgb_color_u8(int threshold_u8, int r, int g, int b) { - if (colorspace && colorspace != fz_device_gray(ctx)) - *iscolor = 1; + int rg_diff = fz_absi(r - g); + int rb_diff = fz_absi(r - b); + int gb_diff = fz_absi(g - b); + return rg_diff > threshold_u8 || rb_diff > threshold_u8 || gb_diff > threshold_u8; } static void -fz_test_color(fz_context *ctx, fz_colorspace *colorspace, float *color, int *iscolor) +fz_test_color(fz_device *dev, fz_colorspace *colorspace, const float *color) { - if (!*iscolor && colorspace && colorspace != fz_device_gray(ctx)) + fz_context *ctx = dev->ctx; + struct test *t = dev->user; + + if (!*t->is_color && colorspace && colorspace != fz_device_gray(ctx)) { - float rgb[3]; - fz_convert_color(ctx, fz_device_rgb(ctx), rgb, colorspace, color); - if (rgb[0] != rgb[1] || rgb[0] != rgb[2]) - *iscolor = 1; + if (colorspace == fz_device_rgb(ctx)) + { + if (is_rgb_color(t->threshold, color[0], color[1], color[2])) + *t->is_color = 1; + } + else + { + float rgb[3]; + 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; + } } } @@ -23,53 +51,153 @@ static void fz_test_fill_path(fz_device *dev, fz_path *path, int even_odd, const fz_matrix *ctm, fz_colorspace *colorspace, float *color, float alpha) { - fz_test_color(dev->ctx, colorspace, color, dev->user); + fz_test_color(dev, colorspace, color); } static void fz_test_stroke_path(fz_device *dev, fz_path *path, fz_stroke_state *stroke, const fz_matrix *ctm, fz_colorspace *colorspace, float *color, float alpha) { - fz_test_color(dev->ctx, colorspace, color, dev->user); + fz_test_color(dev, colorspace, color); } static void fz_test_fill_text(fz_device *dev, fz_text *text, const fz_matrix *ctm, fz_colorspace *colorspace, float *color, float alpha) { - fz_test_color(dev->ctx, colorspace, color, dev->user); + fz_test_color(dev, colorspace, color); } static void fz_test_stroke_text(fz_device *dev, fz_text *text, fz_stroke_state *stroke, const fz_matrix *ctm, fz_colorspace *colorspace, float *color, float alpha) { - fz_test_color(dev->ctx, colorspace, color, dev->user); + fz_test_color(dev, colorspace, color); +} + +struct shadearg +{ + fz_device *dev; + fz_shade *shade; +}; + +static void +prepare_vertex(void *arg0, fz_vertex *v, const float *color) +{ + struct shadearg *arg = arg0; + fz_device *dev = arg->dev; + fz_shade *shade = arg->shade; + if (!shade->use_function) + fz_test_color(dev, shade->colorspace, color); } static void fz_test_fill_shade(fz_device *dev, fz_shade *shade, const fz_matrix *ctm, float alpha) { - fz_test_colorspace(dev->ctx, shade->colorspace, dev->user); + fz_context *ctx = dev->ctx; + + if (shade->use_function) + { + int i; + for (i = 0; i < 256; i++) + fz_test_color(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); + } } static void fz_test_fill_image(fz_device *dev, fz_image *image, const fz_matrix *ctm, float alpha) { - fz_test_colorspace(dev->ctx, image->colorspace, dev->user); + fz_context *ctx = dev->ctx; + struct test *t = dev->user; + + fz_pixmap *pix; + unsigned int count, i, k; + unsigned char *s; + + if (*t->is_color || !image->colorspace || image->colorspace == fz_device_gray(ctx)) + return; + + pix = fz_new_pixmap_from_image(ctx, image, 0, 0); + if (pix == NULL) /* Should never happen really, but... */ + return; + + count = (unsigned int)pix->w * (unsigned int)pix->h; + s = pix->samples; + + if (pix->colorspace == fz_device_rgb(ctx)) + { + int threshold_u8 = t->threshold * 255; + for (i = 0; i < count; i++) + { + if (is_rgb_color_u8(threshold_u8, s[0], s[1], s[2])) + { + *t->is_color = 1; + break; + } + s += 4; + } + } + else + { + fz_color_converter cc; + int n = pix->n-1; + + fz_init_cached_color_converter(ctx, &cc, fz_device_rgb(ctx), pix->colorspace); + 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; + s++; + + cc.convert(&cc, ds, cs); + + if (is_rgb_color(t->threshold, ds[0], ds[1], ds[2])) + { + *t->is_color = 1; + break; + } + } + fz_fin_cached_color_converter(&cc); + } } static void fz_test_fill_image_mask(fz_device *dev, fz_image *image, const fz_matrix *ctm, fz_colorspace *colorspace, float *color, float alpha) { - fz_test_color(dev->ctx, colorspace, color, dev->user); + /* We assume that at least some of the image pixels are non-zero */ + fz_test_color(dev, colorspace, color); } fz_device * -fz_new_test_device(fz_context *ctx, int *iscolor) +fz_new_test_device(fz_context *ctx, int *is_color, float threshold) { - fz_device *dev = fz_new_device(ctx, iscolor); + struct test *t; + fz_device *dev; + + t = fz_malloc_struct(ctx, struct test); + t->is_color = is_color; + t->threshold = threshold; + + fz_try(ctx) + { + dev = fz_new_device(ctx, t); + } + fz_catch(ctx) + { + fz_free(ctx, t); + fz_rethrow(ctx); + } dev->fill_path = fz_test_fill_path; dev->stroke_path = fz_test_stroke_path; @@ -79,7 +207,7 @@ fz_new_test_device(fz_context *ctx, int *iscolor) dev->fill_image = fz_test_fill_image; dev->fill_image_mask = fz_test_fill_image_mask; - *iscolor = 0; + *t->is_color = 0; return dev; } diff --git a/source/tools/mudraw.c b/source/tools/mudraw.c index 79092d9c..198e47ce 100644 --- a/source/tools/mudraw.c +++ b/source/tools/mudraw.c @@ -316,7 +316,7 @@ static void drawpage(fz_context *ctx, fz_document *doc, int pagenum) if (showfeatures) { int iscolor; - dev = fz_new_test_device(ctx, &iscolor); + dev = fz_new_test_device(ctx, &iscolor, 0.02f); if (list) fz_run_display_list(list, dev, &fz_identity, &fz_infinite_rect, NULL); else |