summaryrefslogtreecommitdiff
path: root/gs/base/gxicolor.c
diff options
context:
space:
mode:
Diffstat (limited to 'gs/base/gxicolor.c')
-rw-r--r--gs/base/gxicolor.c526
1 files changed, 526 insertions, 0 deletions
diff --git a/gs/base/gxicolor.c b/gs/base/gxicolor.c
new file mode 100644
index 000000000..4a2dcb4c7
--- /dev/null
+++ b/gs/base/gxicolor.c
@@ -0,0 +1,526 @@
+/* Copyright (C) 2001-2006 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied, modified
+ or distributed except as expressly authorized under the terms of that
+ license. Refer to licensing information at http://www.artifex.com/
+ or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
+ San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* $Id$ */
+/* Color image rendering */
+
+#include "gx.h"
+#include "memory_.h"
+#include "gpcheck.h"
+#include "gserrors.h"
+#include "gxfixed.h"
+#include "gxfrac.h"
+#include "gxarith.h"
+#include "gxmatrix.h"
+#include "gsccolor.h"
+#include "gspaint.h"
+#include "gzstate.h"
+#include "gxdevice.h"
+#include "gxcmap.h"
+#include "gxdcconv.h"
+#include "gxdcolor.h"
+#include "gxistate.h"
+#include "gxdevmem.h"
+#include "gxcpath.h"
+#include "gximage.h"
+#include "icc.h"
+#include "gsicc.h"
+
+typedef union {
+ byte v[GS_IMAGE_MAX_COLOR_COMPONENTS];
+#define BYTES_PER_BITS32 4
+#define BITS32_PER_COLOR_SAMPLES\
+ ((GS_IMAGE_MAX_COLOR_COMPONENTS + BYTES_PER_BITS32 - 1) / BYTES_PER_BITS32)
+ bits32 all[BITS32_PER_COLOR_SAMPLES]; /* for fast comparison */
+} color_samples;
+
+/* ------ Strategy procedure ------ */
+
+/* Check the prototype. */
+iclass_proc(gs_image_class_4_color);
+
+static irender_proc(image_render_color);
+irender_proc_t
+gs_image_class_4_color(gx_image_enum * penum)
+{
+ if (penum->use_mask_color) {
+ /*
+ * Scale the mask colors to match the scaling of each sample to
+ * a full byte, and set up the quick-filter parameters.
+ */
+ int i;
+ color_samples mask, test;
+ bool exact = penum->spp <= BYTES_PER_BITS32;
+
+ memset(&mask, 0, sizeof(mask));
+ memset(&test, 0, sizeof(test));
+ for (i = 0; i < penum->spp; ++i) {
+ byte v0, v1;
+ byte match = 0xff;
+
+ gx_image_scale_mask_colors(penum, i);
+ v0 = (byte)penum->mask_color.values[2 * i];
+ v1 = (byte)penum->mask_color.values[2 * i + 1];
+ while ((v0 & match) != (v1 & match))
+ match <<= 1;
+ mask.v[i] = match;
+ test.v[i] = v0 & match;
+ exact &= (v0 == match && (v1 | match) == 0xff);
+ }
+ penum->mask_color.mask = mask.all[0];
+ penum->mask_color.test = test.all[0];
+ penum->mask_color.exact = exact;
+ } else {
+ penum->mask_color.mask = 0;
+ penum->mask_color.test = ~0;
+ }
+ return &image_render_color;
+}
+
+/* ------ Rendering procedures ------ */
+
+/* Test whether a color is transparent. */
+static bool
+mask_color_matches(const byte *v, const gx_image_enum *penum,
+ int num_components)
+{
+ int i;
+
+ for (i = num_components * 2, v += num_components - 1; (i -= 2) >= 0; --v)
+ if (*v < penum->mask_color.values[i] ||
+ *v > penum->mask_color.values[i + 1]
+ )
+ return false;
+ return true;
+}
+
+/* Render a color image with 8 or fewer bits per sample. */
+static int
+image_render_color(gx_image_enum *penum_orig, const byte *buffer, int data_x,
+ uint w, int h, gx_device * dev)
+{
+ const gx_image_enum *const penum = penum_orig; /* const within proc */
+ gx_image_clue *const clues = penum_orig->clues; /* not const */
+ const gs_imager_state *pis = penum->pis;
+ gs_logical_operation_t lop = penum->log_op;
+ gx_dda_fixed_point pnext;
+ image_posture posture = penum->posture;
+ fixed xprev, yprev;
+ fixed pdyx, pdyy; /* edge of parallelogram */
+ int vci, vdi;
+ const gs_color_space *pcs = penum->pcs;
+ cs_proc_remap_color((*remap_color)) = pcs->type->remap_color;
+ cs_proc_remap_concrete_color((*remap_concrete_color)) =
+ pcs->type->remap_concrete_color;
+ gs_client_color cc;
+ bool device_color = penum->device_color;
+ const gx_color_map_procs *cmap_procs = gx_get_cmap_procs(pis, dev);
+ bits32 mask = penum->mask_color.mask;
+ bits32 test = penum->mask_color.test;
+ gx_image_clue *pic = &clues[0];
+#define pdevc (&pic->dev_color)
+ gx_image_clue *pic_next = &clues[1];
+#define pdevc_next (&pic_next->dev_color)
+ gx_image_clue empty_clue;
+ gx_image_clue clue_temp;
+ int spp = penum->spp;
+ const byte *psrc_initial = buffer + data_x * spp;
+ const byte *psrc = psrc_initial;
+ const byte *rsrc = psrc + spp; /* psrc + spp at start of run */
+ fixed xrun; /* x ditto */
+ fixed yrun; /* y ditto */
+ int irun; /* int x/rrun */
+ color_samples run; /* run value */
+ color_samples next; /* next sample value */
+ const byte *bufend = psrc + w;
+ bool use_cache = spp * penum->bps <= 12;
+ int code = 0, mcode = 0;
+ gs_cie_icc * picc_info; /*used for detecting if image source color space is CIELAB. */
+
+ if (h == 0)
+ return 0;
+ pnext = penum->dda.pixel0;
+ xrun = xprev = dda_current(pnext.x);
+ yrun = yprev = dda_current(pnext.y);
+ pdyx = dda_current(penum->dda.row.x) - penum->cur.x;
+ pdyy = dda_current(penum->dda.row.y) - penum->cur.y;
+ switch (posture) {
+ case image_portrait:
+ vci = penum->yci, vdi = penum->hci;
+ irun = fixed2int_var_rounded(xrun);
+ break;
+ case image_landscape:
+ default: /* we don't handle skew -- treat as landscape */
+ vci = penum->xci, vdi = penum->wci;
+ irun = fixed2int_var_rounded(yrun);
+ break;
+ }
+
+ if_debug5('b', "[b]y=%d data_x=%d w=%d xt=%f yt=%f\n",
+ penum->y, data_x, w, fixed2float(xprev), fixed2float(yprev));
+ memset(&run, 0, sizeof(run));
+ memset(&next, 0, sizeof(next));
+ /* Ensure that we don't get any false dev_color_eq hits. */
+ if (use_cache) {
+ set_nonclient_dev_color(&empty_clue.dev_color, gx_no_color_index);
+ pic = &empty_clue;
+ }
+ cs_full_init_color(&cc, pcs);
+ run.v[0] = ~psrc[0]; /* force remap */
+ while (psrc < bufend) {
+ dda_next(pnext.x);
+ dda_next(pnext.y);
+#define CLUE_HASH3(penum, next)\
+ &clues[(next.v[0] + (next.v[1] << 2) + (next.v[2] << 4)) & 255];
+#define CLUE_HASH4(penum, next)\
+ &clues[(next.v[0] + (next.v[1] << 2) + (next.v[2] << 4) +\
+ (next.v[3] << 6)) & 255]
+
+ if (spp == 4) { /* may be CMYK or RGBA */
+ next.v[0] = psrc[0];
+ next.v[1] = psrc[1];
+ next.v[2] = psrc[2];
+ next.v[3] = psrc[3];
+ psrc += 4;
+map4: if (posture != image_skewed && next.all[0] == run.all[0])
+ goto inc;
+ if (use_cache) {
+ pic_next = CLUE_HASH4(penum, next);
+ if (pic_next->key == next.all[0])
+ goto f;
+ /*
+ * If we are really unlucky, pic_next == pic,
+ * so mapping this color would clobber the one
+ * we're about to use for filling the run.
+ */
+ if (pic_next == pic) {
+ clue_temp = *pic;
+ pic = &clue_temp;
+ }
+ pic_next->key = next.all[0];
+ }
+ /* Check for transparent color. */
+ if ((next.all[0] & mask) == test &&
+ (penum->mask_color.exact ||
+ mask_color_matches(next.v, penum, 4))
+ ) {
+ color_set_null(pdevc_next);
+ goto mapped;
+ }
+ if (device_color) {
+ frac frac_color[4];
+
+ if (penum->alpha) {
+ /*
+ * We do not have support for DeviceN color and alpha.
+ */
+ cmap_procs->map_rgb_alpha
+ (byte2frac(next.v[0]), byte2frac(next.v[1]),
+ byte2frac(next.v[2]), byte2frac(next.v[3]),
+ pdevc_next, pis, dev,
+ gs_color_select_source);
+ goto mapped;
+ }
+ /*
+ * We can call the remap concrete_color for the colorspace
+ * directly since device_color is only true if the colorspace
+ * is concrete.
+ */
+ frac_color[0] = byte2frac(next.v[0]);
+ frac_color[1] = byte2frac(next.v[1]);
+ frac_color[2] = byte2frac(next.v[2]);
+ frac_color[3] = byte2frac(next.v[3]);
+ remap_concrete_color(frac_color, pcs, pdevc_next, pis,
+ dev, gs_color_select_source);
+ goto mapped;
+ }
+ decode_sample(next.v[3], cc, 3);
+ if_debug1('B', "[B]cc[3]=%g\n", cc.paint.values[3]);
+do3: if(spp == 3 && pcs->type->index == gs_color_space_index_CIEICC)
+ {
+ /* It is 3 channel with an ICC profile.
+ We need to check if it is an LAB image */
+
+ picc_info = pcs->params.icc.picc_info;
+
+ if( picc_info->plu->e_inSpace == icSigLabData )
+ {
+
+ /* It is a CIELAB image. For now, put in true CIELAB float values rather than normalized 0 to 1 floats */
+ /* concretization will handle the proper conversion this way */
+
+ decode_sample(next.v[0], cc, 0);
+ cc.paint.values[0]*=100.0;
+ decode_sample(next.v[1], cc, 1);
+ cc.paint.values[1] = 255.0*cc.paint.values[1] - 128.0;
+ decode_sample(next.v[2], cc, 2);
+ cc.paint.values[2] = 255.0*cc.paint.values[2] - 128.0;
+
+ } else {
+
+ /* To floats */
+
+ decode_sample(next.v[0], cc, 0);
+ decode_sample(next.v[1], cc, 1);
+ decode_sample(next.v[2], cc, 2);
+
+ }
+
+ } else {
+
+ /* To floats */
+
+ decode_sample(next.v[0], cc, 0);
+ decode_sample(next.v[1], cc, 1);
+ decode_sample(next.v[2], cc, 2);
+
+ }
+
+ if_debug3('B', "[B]cc[0..2]=%g,%g,%g\n",
+ cc.paint.values[0], cc.paint.values[1],
+ cc.paint.values[2]);
+
+ } else if (spp == 3) { /* may be RGB, but could be LAB image file with ICC profile... */
+ next.v[0] = psrc[0];
+ next.v[1] = psrc[1];
+ next.v[2] = psrc[2];
+ psrc += 3;
+ if (posture != image_skewed && next.all[0] == run.all[0])
+ goto inc;
+ if (use_cache) {
+ pic_next = CLUE_HASH3(penum, next);
+ if (pic_next->key == next.all[0])
+ goto f;
+ /* See above re the following check. */
+ if (pic_next == pic) {
+ clue_temp = *pic;
+ pic = &clue_temp;
+ }
+ pic_next->key = next.all[0];
+ }
+ /* Check for transparent color. */
+ if ((next.all[0] & mask) == test &&
+ (penum->mask_color.exact ||
+ mask_color_matches(next.v, penum, 3))
+ ) {
+ color_set_null(pdevc_next);
+ goto mapped;
+ }
+ if (device_color) {
+
+ frac frac_color[3];
+ /*
+ * We can call the remap concrete_color for the colorspace
+ * directly since device_color is only true if the colorspace
+ * is concrete.
+ */
+ frac_color[0] = byte2frac(next.v[0]);
+ frac_color[1] = byte2frac(next.v[1]);
+ frac_color[2] = byte2frac(next.v[2]);
+ remap_concrete_color(frac_color, pcs, pdevc_next, pis,
+ dev, gs_color_select_source);
+ goto mapped;
+
+ }
+ goto do3;
+ } else if (penum->alpha) {
+ if (spp == 2) { /* might be Gray + alpha */
+ next.v[2] = next.v[1] = next.v[0] = psrc[0];
+ next.v[3] = psrc[1];
+ psrc += 2;
+ goto map4;
+ } else if (spp == 5) { /* might be CMYK + alpha */
+ /* Convert CMYK to RGB. */
+ frac rgb[3];
+
+ color_cmyk_to_rgb(byte2frac(psrc[0]), byte2frac(psrc[1]),
+ byte2frac(psrc[2]), byte2frac(psrc[3]),
+ pis, rgb);
+ /*
+ * It seems silly to do all this converting between
+ * fracs and bytes, but that's what the current
+ * APIs require.
+ */
+ next.v[0] = frac2byte(rgb[0]);
+ next.v[1] = frac2byte(rgb[1]);
+ next.v[2] = frac2byte(rgb[2]);
+ next.v[3] = psrc[4];
+ psrc += 5;
+ goto map4;
+ }
+ } else { /* DeviceN */
+ int i;
+
+ use_cache = false; /* should do in initialization */
+ if (posture != image_skewed && !memcmp(psrc, run.v, spp)) {
+ psrc += spp;
+ goto inc;
+ }
+ memcpy(next.v, psrc, spp);
+ psrc += spp;
+ if ((next.all[0] & mask) == test &&
+ (penum->mask_color.exact ||
+ mask_color_matches(next.v, penum, spp))
+ ) {
+ color_set_null(pdevc_next);
+ goto mapped;
+ }
+ for (i = 0; i < spp; ++i)
+ decode_sample(next.v[i], cc, i);
+#ifdef DEBUG
+ if (gs_debug_c('B')) {
+ dprintf2("[B]cc[0..%d]=%g", spp - 1,
+ cc.paint.values[0]);
+ for (i = 1; i < spp; ++i)
+ dprintf1(",%g", cc.paint.values[i]);
+ dputs("\n");
+ }
+#endif
+ }
+ mcode = remap_color(&cc, pcs, pdevc_next, pis, dev,
+ gs_color_select_source);
+ if (mcode < 0)
+ goto fill;
+mapped: if (pic == pic_next)
+ goto fill;
+f: if (sizeof(pdevc_next->colors.binary.color[0]) <= sizeof(ulong))
+ if_debug7('B', "[B]0x%x,0x%x,0x%x,0x%x -> 0x%lx,0x%lx,0x%lx\n",
+ next.v[0], next.v[1], next.v[2], next.v[3],
+ (ulong)pdevc_next->colors.binary.color[0],
+ (ulong)pdevc_next->colors.binary.color[1],
+ (ulong) pdevc_next->type);
+ else
+ if_debug9('B', "[B]0x%x,0x%x,0x%x,0x%x -> 0x%08lx%08lx,0x%08lx%08lx,0x%lx\n",
+ next.v[0], next.v[1], next.v[2], next.v[3],
+ (ulong)(pdevc_next->colors.binary.color[0] >>
+ 8 * (sizeof(pdevc_next->colors.binary.color[0]) - sizeof(ulong))),
+ (ulong)pdevc_next->colors.binary.color[0],
+ (ulong)(pdevc_next->colors.binary.color[1] >>
+ 8 * (sizeof(pdevc_next->colors.binary.color[1]) - sizeof(ulong))),
+ (ulong)pdevc_next->colors.binary.color[1],
+ (ulong) pdevc_next->type);
+ /* NB: printf above fails to account for sizeof gx_color_index 4 or 8 bytes */
+
+ /* Even though the supplied colors don't match, */
+ /* the device colors might. */
+ if (posture != image_skewed && dev_color_eq(*pdevc, *pdevc_next))
+ goto set;
+fill: /* Fill the region between */
+ /* xrun/irun and xprev */
+ /*
+ * Note; This section is nearly a copy of a simlar section below
+ * for processing the last image pixel in the loop. This would have been
+ * made into a subroutine except for complications about the number of
+ * variables that would have been needed to be passed to the routine.
+ */
+ switch (posture) {
+ case image_portrait:
+ { /* Rectangle */
+ int xi = irun;
+ int wi = (irun = fixed2int_var_rounded(xprev)) - xi;
+
+ if (wi < 0)
+ xi += wi, wi = -wi;
+ if (wi > 0)
+ code = gx_fill_rectangle_device_rop(xi, vci, wi, vdi,
+ pdevc, dev, lop);
+ }
+ break;
+ case image_landscape:
+ { /* 90 degree rotated rectangle */
+ int yi = irun;
+ int hi = (irun = fixed2int_var_rounded(yprev)) - yi;
+
+ if (hi < 0)
+ yi += hi, hi = -hi;
+ if (hi > 0)
+ code = gx_fill_rectangle_device_rop(vci, yi, vdi, hi,
+ pdevc, dev, lop);
+ }
+ break;
+ default:
+ { /* Parallelogram */
+ code = (*dev_proc(dev, fill_parallelogram))
+ (dev, xrun, yrun, xprev - xrun, yprev - yrun, pdyx, pdyy,
+ pdevc, lop);
+ xrun = xprev;
+ yrun = yprev;
+ }
+ }
+ if (code < 0)
+ goto err;
+ rsrc = psrc;
+ if ((code = mcode) < 0) {
+ /* Invalidate any partially built cache entry. */
+ if (use_cache)
+ pic_next->key = ~next.all[0];
+ goto err;
+ }
+ if (use_cache)
+ pic = pic_next;
+ else {
+ gx_image_clue *ptemp = pic;
+
+ pic = pic_next;
+ pic_next = ptemp;
+ }
+set: run = next;
+inc: xprev = dda_current(pnext.x);
+ yprev = dda_current(pnext.y); /* harmless if no skew */
+ }
+ /* Fill the last run. */
+ /*
+ * Note; This section is nearly a copy of a simlar section above
+ * for processing an image pixel in the loop. This would have been
+ * made into a subroutine except for complications about the number
+ * variables that would have been needed to be passed to the routine.
+ */
+ switch (posture) {
+ case image_portrait:
+ { /* Rectangle */
+ int xi = irun;
+ int wi = (irun = fixed2int_var_rounded(xprev)) - xi;
+
+ if (wi < 0)
+ xi += wi, wi = -wi;
+ if (wi > 0)
+ code = gx_fill_rectangle_device_rop(xi, vci, wi, vdi,
+ pdevc, dev, lop);
+ }
+ break;
+ case image_landscape:
+ { /* 90 degree rotated rectangle */
+ int yi = irun;
+ int hi = (irun = fixed2int_var_rounded(yprev)) - yi;
+
+ if (hi < 0)
+ yi += hi, hi = -hi;
+ if (hi > 0)
+ code = gx_fill_rectangle_device_rop(vci, yi, vdi, hi,
+ pdevc, dev, lop);
+ }
+ break;
+ default:
+ { /* Parallelogram */
+ code = (*dev_proc(dev, fill_parallelogram))
+ (dev, xrun, yrun, xprev - xrun, yprev - yrun, pdyx, pdyy,
+ pdevc, lop);
+ }
+ }
+ return (code < 0 ? code : 1);
+ /* Save position if error, in case we resume. */
+err:
+ penum_orig->used.x = (rsrc - spp - psrc_initial) / spp;
+ penum_orig->used.y = 0;
+ return code;
+}