[gs-cvs] rev 8819 - trunk/gs/src
giles at ghostscript.com
giles at ghostscript.com
Fri Jul 4 11:20:45 PDT 2008
Author: giles
Date: 2008-07-04 11:20:44 -0700 (Fri, 04 Jul 2008)
New Revision: 8819
Modified:
trunk/gs/src/sjpx.c
trunk/gs/src/zfjpx.c
Log:
Parse common color space keys in JPXDecode filtered images and pass them
to the filter, allowing JPX-encoded CMYK images to display properly.
Fixes bug 688807.
DETAILS:
The fundamental issue is that the jasper library does not support the
ISO 15444-2 (JPX) extensions to the JP2 format for handling full ICC
and CMYK color spaces. So while the the PDF image dictionary and the
JPX streams both specify a CMYK ICC profile describing the image, jasper
ignores the extended colr boxes and marks the image as sRGB + an unknown
component, just based on it having three or more components. Our stream
implementation used this information and tried to return RGB pixels when
the graphics library expected CMYK, causing errors or hangs.
Jasper should be amended to handle these extensions. However, since in
PDF a color space in the image's stream dictionary overrides what is in
the stream, I felt implementing the override was the more useful
resolution.
This patch doesn't handle all color spaces, just Device* and ICCBased,
which are the most common. In the case of ICCBased spaces, we try to
look up the alternate, and use that if it is a Device* space; otherwise
we guess based on the number of components.
EXPECTED DIFFERENCES:
jpx-file2.pdf
jpx-file3.pdf
These files are encoded in a YCbCr (YUV) color space, but the image
dictionaries specify a DeviceRGB color space. Previously we converted
the YCbCr image to RGB before returning it. With this change we now
return the YCbCr pixels unconverted which are then assumed to be RGB,
giving false color. This matches Adobe Reader 8 and is a more reasonable
reading of the spec, so these are considered progressions.
The second file, jpx-file3.pdf does not match Adobe Reader because it is
chroma subsampled, and we don't upsample the plane when returning it in
raw mode.
Modified: trunk/gs/src/sjpx.c
===================================================================
--- trunk/gs/src/sjpx.c 2008-07-03 18:25:55 UTC (rev 8818)
+++ trunk/gs/src/sjpx.c 2008-07-04 18:20:44 UTC (rev 8819)
@@ -140,6 +140,31 @@
return 0;
}
+
+/* dump the external colorspace from the interpreter for debugging */
+static int
+dump_jpxd_colorspace(const stream_jpxd_state * state)
+{
+ char *cspace;
+
+ if (state->colorspace == gs_jpx_cs_unset) {
+ if_debug0('w', "[w]JPX image has no external color space set\n");
+ return 0;
+ }
+
+ switch (state->colorspace) {
+ case gs_jpx_cs_gray: cspace = "Grayscale based"; break;
+ case gs_jpx_cs_rgb: cspace = "RGB based"; break;
+ case gs_jpx_cs_cmyk: cspace = "CMYK based"; break;
+ case gs_jpx_cs_indexed: cspace = "indexed"; break;
+ default: cspace = "unknown"; break;
+ }
+
+ if_debug1('w', "[w]Interpreter has set an external %s color space\n",
+ cspace);
+
+ return 0;
+}
#endif /* DEBUG */
static int
@@ -332,6 +357,7 @@
#ifdef DEBUG
dump_jas_image(image);
+ dump_jpxd_colorspace(state);
#endif
return 0;
@@ -375,21 +401,36 @@
int x, y;
long usable, done;
+ /* copy data out of the decoded image data */
+ /* be lazy and only write the rest of the current row */
y = state->offset / stride;
x = state->offset - y*stride; /* bytes, not samples */
usable = min(out_size, stride - x);
+ x = x/numcmpts; /* now samples */
+
/* Make sure we can return a full pixel.
This can fail if we get the colorspace wrong. */
if (usable < numcmpts) return ERRC;
- x = x/numcmpts; /* now samples */
- /* copy data out of the decoded image data */
- /* be lazy and only write the rest of the current row */
- if (state->colorspace == gs_jpx_cs_indexed) {
- /* we've passed 'raw' but the palette is the same pixel
- format as a grayscale image. The PDF interpreter will
- know to handle it differently. */
- done = copy_row_gray(pw->ptr, image, x, y, usable);
- } else /* use the stream's colorspace */
+
+ if (state->colorspace != gs_jpx_cs_unset)
+ /* An external colorspace from the interpreter overrides */
+ switch (state->colorspace) {
+ case gs_jpx_cs_gray:
+ case gs_jpx_cs_indexed:
+ /* we've passed 'raw' but the palette is the same pixel
+ format as a grayscale image. The PDF interpreter will
+ know to handle it differently. */
+ done = copy_row_gray(pw->ptr, image, x, y, usable);
+ break;
+ case gs_jpx_cs_rgb:
+ done = copy_row_rgb(pw->ptr, image, x, y, usable);
+ break;
+ case gs_jpx_cs_cmyk:
+ default:
+ done = copy_row_default(pw->ptr, image, x, y, usable);
+ break;
+ }
+ else /* use the stream's colorspace */
switch (jas_clrspc_fam(clrspc)) {
case JAS_CLRSPC_FAM_GRAY:
done = copy_row_gray(pw->ptr, image, x, y, usable);
Modified: trunk/gs/src/zfjpx.c
===================================================================
--- trunk/gs/src/zfjpx.c 2008-07-03 18:25:55 UTC (rev 8818)
+++ trunk/gs/src/zfjpx.c 2008-07-04 18:20:44 UTC (rev 8819)
@@ -1,6 +1,6 @@
/* Copyright (C) 2001-2008 Artifex Software, Inc.
All Rights Reserved.
-
+
This software is provided AS-IS with no warranty, either express or
implied.
@@ -37,6 +37,10 @@
#include "sjpx.h"
#endif
+/* macro to test a name ref against a C string */
+# define ISTRCMP(ref, string) (memcmp((ref)->value.const_bytes, string, \
+ min(strlen(string), r_size(ref))))
+
/* <source> /JPXDecode <file> */
/* <source> <dict> /JPXDecode <file> */
static int
@@ -69,14 +73,56 @@
/* get a reference to the name's string value */
name_string_ref(imemory, csname, &sref);
/* request raw index values if the colorspace is /Indexed */
- if (!memcmp(sref.value.const_bytes, "Indexed", min(7,r_size(&sref))))
+ if (!ISTRCMP(&sref, "Indexed"))
state.colorspace = gs_jpx_cs_indexed;
+ /* tell the filter what output we want for other spaces */
+ else if (!ISTRCMP(&sref, "DeviceGray"))
+ state.colorspace = gs_jpx_cs_gray;
+ else if (!ISTRCMP(&sref, "DeviceRGB"))
+ state.colorspace = gs_jpx_cs_rgb;
+ else if (!ISTRCMP(&sref, "DeviceCMYK"))
+ state.colorspace = gs_jpx_cs_cmyk;
+ else if (!ISTRCMP(&sref, "ICCBased")) {
+ /* The second array element should be the profile's
+ stream dict */
+ ref *csdict = sop->value.refs + 1;
+ ref *nref;
+ ref altname;
+ if (r_is_array(sop) && (r_size(sop) > 1) &&
+ r_has_type(csdict, t_dictionary)) {
+ check_dict_read(*csdict);
+ /* try to look up the alternate space */
+ if (dict_find_string(csdict, "Alternate", &nref) > 0) {
+ name_string_ref(imemory, csname, &altname);
+ if (!ISTRCMP(&altname, "DeviceGray"))
+ state.colorspace = gs_jpx_cs_gray;
+ else if (!ISTRCMP(&altname, "DeviceRGB"))
+ state.colorspace = gs_jpx_cs_rgb;
+ else if (!ISTRCMP(&altname, "DeviceCMYK"))
+ state.colorspace = gs_jpx_cs_cmyk;
+ }
+ /* else guess based on the number of components */
+ if (state.colorspace == gs_jpx_cs_unset &&
+ dict_find_string(csdict, "N", &nref) > 0) {
+ if_debug1('w', "[w] JPX image has an external %d"
+ " channel colorspace\n", nref->value.intval);
+ switch (nref->value.intval) {
+ case 1: state.colorspace = gs_jpx_cs_gray;
+ break;
+ case 3: state.colorspace = gs_jpx_cs_rgb;
+ break;
+ case 4: state.colorspace = gs_jpx_cs_cmyk;
+ break;
+ }
+ }
+ }
+ }
} else {
if_debug0('w', "[w] Couldn't read JPX ColorSpace key!\n");
}
}
}
-
+
/* we pass npop=0, since we've no arguments left to consume */
/* we pass 0 instead of the usual rspace(sop) which will allocate storage
for filter state from the same memory pool as the stream it's coding.
More information about the gs-cvs
mailing list