summaryrefslogtreecommitdiff
path: root/source/fitz/separation.c
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2017-07-07 14:27:33 +0100
committerRobin Watts <robin.watts@artifex.com>2017-09-08 17:46:50 +0100
commita7f36241cba4d1807ab4664201aa0975755d6772 (patch)
tree6a15ab1b47af54d9f733915a20c0f609b07bc72f /source/fitz/separation.c
parentd4afa7b72e0b7d774250f13c3c36d04a5e1415d0 (diff)
downloadmupdf-a7f36241cba4d1807ab4664201aa0975755d6772.tar.xz
Update draw device to cope with spots.
If draw device is passed a pixmap with disabled separations, it may have to push an extra group at the top to allow for the actual rendering to properly happen in cmyk+spots, and then get folded down at the end. This pushing cannot happen at create time, due to it being dependent on the defualt_cs settings. Accordingly, we push it (just once) on the first drawing operation. This means we need to be able to convert from "colorspace + set of spots" to "different colorspace + different set of spots". This in turn means we need to be able to drive lcms slightly differently (to tell it whether to copy the spots unchanged or not), so we have to amend the CMS interface code a bit for that. Currently we lack plotters to properly cope with plotting images and shades with spots, so this will give a warning and do nothing. To be filled in in future commits. Ensure fz_get_icc_link accepts NULL to mean default color params. Incorporates fixes from Michel Vrhel: With transparency groups we can have RGB + spot pixmaps. When drawing into those we need a mixture of colorant polarity. Ensure that fz_convert_separation_colors takes account of this. Fix C1 of Altona_Technical_1v1_x3.pdf (allow for output intent in fz_clone_pixmap_area_with_different_seps).
Diffstat (limited to 'source/fitz/separation.c')
-rw-r--r--source/fitz/separation.c334
1 files changed, 334 insertions, 0 deletions
diff --git a/source/fitz/separation.c b/source/fitz/separation.c
index 45b60b92..0baf30b4 100644
--- a/source/fitz/separation.c
+++ b/source/fitz/separation.c
@@ -167,3 +167,337 @@ int fz_count_active_separations(fz_context *ctx, const fz_separations *sep)
c++;
return c;
}
+
+fz_separations *fz_clone_separations_for_overprint(fz_context *ctx, fz_separations *sep)
+{
+ int i, j, n, c;
+ fz_separations *clone;
+
+ if (!sep)
+ return NULL;
+
+ n = sep->num_separations;
+ c = 0;
+ for (i = 0; i < n; i++)
+ {
+ fz_separation_behavior state = sep_state(sep, i);
+ if (state == FZ_SEPARATION_COMPOSITE)
+ c++;
+ }
+
+ /* If no composites, then we are fine to render direct. */
+ if (c == 0)
+ return NULL;
+
+ /* We need to clone us a separation structure, with all
+ * the composite separations marked as enabled. */
+ clone = fz_malloc_struct(ctx, fz_separations);
+
+ fz_try(ctx)
+ {
+ clone->refs = 1;
+ clone->controllable = 0;
+ for (i = 0; i < n; i++)
+ {
+ fz_separation_behavior beh = sep_state(sep, i);
+ if (beh == FZ_SEPARATION_DISABLED)
+ continue;
+ j = clone->num_separations++;
+ if (beh == FZ_SEPARATION_COMPOSITE)
+ beh = FZ_SEPARATION_SPOT;
+ fz_set_separation_behavior(ctx, clone, j, beh);
+ clone->name[j] = sep->name[i] ? fz_strdup(ctx, sep->name[i]) : NULL;
+ clone->equiv_rgb[j] = sep->equiv_rgb[i];
+ clone->equiv_cmyk[j] = sep->equiv_cmyk[i];
+ }
+ }
+ fz_catch(ctx)
+ {
+ fz_drop_separations(ctx, clone);
+ fz_rethrow(ctx);
+ }
+
+ return clone;
+}
+
+fz_pixmap *
+fz_clone_pixmap_area_with_different_seps(fz_context *ctx, fz_pixmap *src, const fz_irect *bbox, fz_colorspace *dcs, fz_separations *dseps, fz_colorspace *prf, fz_default_colorspaces *default_cs)
+{
+ fz_irect local_bbox;
+ fz_pixmap *dst;
+ fz_colorspace *oi = fz_default_output_intent(ctx, default_cs);
+
+ if (fz_colorspace_n(ctx, dcs) == fz_colorspace_n(ctx, oi))
+ dcs = oi;
+
+ if (bbox == NULL)
+ {
+ local_bbox.x0 = src->x;
+ local_bbox.y0 = src->y;
+ local_bbox.x1 = src->x + src->w;
+ local_bbox.y1 = src->y + src->h;
+ bbox = &local_bbox;
+ }
+
+ dst = fz_new_pixmap_with_bbox(ctx, dcs, bbox, dseps, src->alpha);
+ if (src->flags & FZ_PIXMAP_FLAG_INTERPOLATE)
+ dst->flags |= FZ_PIXMAP_FLAG_INTERPOLATE;
+ else
+ dst->flags &= ~FZ_PIXMAP_FLAG_INTERPOLATE;
+
+ return fz_copy_pixmap_area_converting_seps(ctx, dst, src, prf, default_cs);
+}
+
+fz_pixmap *
+fz_copy_pixmap_area_converting_seps(fz_context *ctx, fz_pixmap *dst, fz_pixmap *src, fz_colorspace *prf, fz_default_colorspaces *default_cs)
+{
+ int dw = dst->w;
+ int dh = dst->h;
+ fz_separations *sseps = src->seps;
+ fz_separations *dseps = dst->seps;
+ int sseps_n = sseps ? sseps->num_separations : 0;
+ int dseps_n = dseps ? dseps->num_separations : 0;
+ int sstride = src->stride;
+ int dstride = dst->stride;
+ int sn = src->n;
+ int dn = dst->n;
+ int sa = src->alpha;
+ int da = dst->alpha;
+ int ss = src->s;
+ int ds = dst->s;
+ int sc = sn - ss - sa;
+ int dc = dn - ds - da;
+ const unsigned char *sdata = src->samples + sstride * (dst->y - src->y) + (dst->x - src->x) * sn;
+ unsigned char *ddata = dst->samples;
+ signed char map[FZ_MAX_COLORS];
+ int x, y, i, j, k;
+ unsigned char mapped[FZ_MAX_COLORS];
+ int unmapped = sseps_n;
+
+ assert(da == sa);
+ assert(ss == fz_count_active_separations(ctx, sseps));
+ assert(ds == fz_count_active_separations(ctx, dseps));
+
+ dstride -= dn * dw;
+ sstride -= sn * dw;
+
+ /* Process colorants first */
+ if (dst->colorspace == src->colorspace)
+ {
+ /* Simple copy */
+ unsigned char *dd = ddata;
+ const unsigned char *sd = sdata;
+ for (y = dh; y > 0; y--)
+ {
+ for (x = dw; x > 0; x--)
+ {
+ for (i = 0; i < dn; i++)
+ dd[i] = sd[i];
+ dd += dn;
+ sd += sn;
+ }
+ dd += dstride;
+ sd += sstride;
+ }
+ }
+ else
+ {
+ fz_pixmap_converter *pc = fz_lookup_pixmap_converter(ctx, dst->colorspace, src->colorspace);
+
+ pc(ctx, dst, src, prf, default_cs, NULL, 0);
+ }
+
+ /* Make a map of what spots go where */
+ for (i = 0, k = 0; i < dseps_n; i++)
+ {
+ const char *name;
+
+ if (sep_state(dseps, i) >= FZ_SEPARATION_DISABLED)
+ continue;
+ name = dseps->name[i];
+ map[k] = -1;
+ mapped[k] = 0;
+ for (j = 0; j < sseps_n; j++)
+ {
+ if (sep_state(sseps, j) >= FZ_SEPARATION_DISABLED)
+ continue;
+ if (!strcmp(name, sseps->name[j]))
+ {
+ map[k] = j;
+ unmapped--;
+ mapped[k] = 1;
+ break;
+ }
+ }
+ k++;
+ }
+ if (sa)
+ map[k] = sseps_n;
+
+ /* Now we need to make d[i] = map[i] < 0 : 255 ? s[map[i]] */
+
+ {
+ unsigned char *dd = ddata + dc;
+ const unsigned char *sd = sdata + sc;
+ for (y = dh; y > 0; y--)
+ {
+ for (x = dw; x > 0; x--)
+ {
+ for (i = 0; i < ds; i++)
+ dd[i] = map[i] < 0 ? 255 : sd[map[i]];
+ dd += dn;
+ sd += sn;
+ }
+ dd += dstride;
+ sd += sstride;
+ }
+ }
+
+ /* If we've handled all the spots, we're done. */
+ if (unmapped == 0)
+ return dst;
+
+ /* Still need to handle mapping 'lost' spots down to process colors */
+ for (i = 0; i < sseps_n; i++)
+ {
+ uint8_t convert[4];
+ uint32_t c;
+
+ if (mapped[i])
+ continue;
+ /* Src spot i is not mapped. We need to convert that down. */
+ switch (dc)
+ {
+ case 1: /* Grey */
+ /* FIXME: Should we hold a grey equivalent in each spot? */
+ c = sseps->equiv_rgb[i];
+ convert[0] = ((c & 0xff) * 77 + ((c>>8) & 0xff) * 150 + ((c>>16) & 0xff) * 28 + 255)>>8;
+ break;
+ case 3: /* RGB */
+ c = sseps->equiv_rgb[i];
+ convert[0] = c;
+ convert[1] = c>>8;
+ convert[2] = c>>16;
+ break;
+ case 4: /* CMYK */
+ c = sseps->equiv_cmyk[i];
+ convert[0] = c;
+ convert[1] = c>>8;
+ convert[2] = c>>16;
+ convert[3] = c>>24;
+ break;
+ }
+
+ {
+ unsigned char *dd = ddata;
+ const unsigned char *sd = sdata + sc;
+ for (y = dh; y > 0; y--)
+ {
+ for (x = dw; x > 0; x--)
+ {
+ unsigned char v = sd[i];
+ if (v == 0)
+ continue;
+ for (i = 0; i < dc; i++)
+ dd[i] = fz_clampi(dd[i] + fz_mul255(v, convert[i]), 0, 255);
+ dd += dn;
+ sd += sn;
+ }
+ dd += dstride;
+ sd += sstride;
+ }
+ }
+ }
+
+ return dst;
+}
+
+void fz_convert_separation_colors(fz_context *ctx, const fz_color_params *color_params, const fz_colorspace *dst_cs, const fz_separations *dst_seps, float *dst_color, const fz_colorspace *src_cs, const float *src_color)
+{
+ int i, j, n, dc, ds, dn, pred;
+ float remainders[FZ_MAX_COLORS];
+ int remaining = 0;
+
+ assert(dst_cs && dst_seps && src_cs && dst_color && src_color);
+ assert(fz_colorspace_is_device_n(ctx, src_cs));
+
+ dc = fz_colorspace_n(ctx, dst_cs);
+ ds = dst_seps->num_separations;
+ dn = dc + ds;
+
+ i = 0;
+ if (!fz_colorspace_is_subtractive(ctx, dst_cs))
+ for (; i < dc; i++)
+ dst_color[i] = 1;
+ for (; i < dn; i++)
+ dst_color[i] = 0;
+
+ n = fz_colorspace_n(ctx, src_cs);
+ pred = 0;
+ for (i = 0; i < n; i++)
+ {
+ const char *name = fz_colorspace_colorant(ctx, src_cs, i);
+
+ if (i == 0 && !strcmp(name, "All"))
+ {
+ /* This is only supposed to happen in separation spaces, not DeviceN */
+ if (n != 1)
+ fz_warn(ctx, "All found in DeviceN space");
+ for (i = 0; i < dn; i++)
+ dst_color[i] = src_color[0];
+ break;
+ }
+ if (!strcmp(name, "None"))
+ continue;
+
+ /* The most common case is that the colorant we match is the
+ * one after the one we matched before, so optimise for that. */
+ for (j = pred; j < ds; j++)
+ {
+ const char *dname = dst_seps->name[j];
+ if (!strcmp(name, dname))
+ goto found_sep;
+ }
+ for (j = 0; j < pred; j++)
+ {
+ const char *dname = dst_seps->name[j];
+ if (!strcmp(name, dname))
+ goto found_sep;
+ }
+ for (j = 0; j < dc; j++)
+ {
+ const char *dname = fz_colorspace_colorant(ctx, dst_cs, j);
+ if (!strcmp(name, dname))
+ goto found_process;
+ }
+ if (0) {
+found_sep:
+ dst_color[j+dc] = src_color[i];
+ pred = j+1;
+ }
+ else if (0)
+ {
+found_process:
+ dst_color[j] += src_color[i];
+ }
+ else
+ {
+ if (remaining == 0)
+ {
+ memset(remainders, 0, sizeof(float) * n);
+ remaining = 1;
+ }
+ remainders[i] = src_color[i];
+ }
+ }
+
+ if (remaining)
+ {
+ /* There were some spots that didn't copy over */
+ float converted[FZ_MAX_COLORS];
+ fz_convert_color(ctx, color_params, NULL, dst_cs, converted, src_cs, remainders);
+
+ for (i = 0; i < dc; i++)
+ dst_color[i] += converted[i];
+ }
+}