From 7662af082b44f8164d1adadc5688f30d888e6a63 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 22 Aug 2014 15:35:13 +0200 Subject: 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. --- source/fitz/test-device.c | 168 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 148 insertions(+), 20 deletions(-) (limited to 'source/fitz/test-device.c') 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 -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; } -- cgit v1.2.3