summaryrefslogtreecommitdiff
path: root/source/fitz
diff options
context:
space:
mode:
authorTor Andersson <tor.andersson@artifex.com>2014-08-22 15:35:13 +0200
committerTor Andersson <tor.andersson@artifex.com>2014-08-27 13:15:56 +0200
commit7662af082b44f8164d1adadc5688f30d888e6a63 (patch)
tree61867c232cebef6f510eef1e404f81c4411c4c2e /source/fitz
parent357162fe1eaec81638611d57ed178d8dfc3cdc47 (diff)
downloadmupdf-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/fitz')
-rw-r--r--source/fitz/test-device.c168
1 files changed, 148 insertions, 20 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;
}