1 /* Copyright (C) 2001-2008 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 
14 /* $Id: sjpx.c 9801 2009-06-18 05:16:48Z giles $ */
15 /* JPXDecode filter implementation -- hooks in libjasper */
16 
17 #include "memory_.h"
18 #include "gserror.h"
19 #include "gdebug.h"
20 #include "strimpl.h"
21 #include "gsmalloc.h"
22 #include "sjpx.h"
23 
24 static void s_jpxd_set_defaults(stream_state *ss);
25 
26 /* stream implementation */
27 
28 /* As with the /JBIG2Decode filter, we let the library do its own
29    memory management through malloc() etc. and rely on our release()
30    proc being called to deallocate state.
31 */
32 
33 private_st_jpxd_state(); /* creates a gc object for our state,
34 			    defined in sjpx.h */
35 
36 /* initialize the steam.
37    this involves allocating the stream and image structures, and
38    initializing the decoder.
39  */
40 static int
s_jpxd_init(stream_state * ss)41 s_jpxd_init(stream_state * ss)
42 {
43     stream_jpxd_state *const state = (stream_jpxd_state *) ss;
44     int status = 0;
45 
46     if (state->jpx_memory == NULL) {
47       state->jpx_memory = ss->memory ?
48 		ss->memory->non_gc_memory :
49 		gs_lib_ctx_get_non_gc_memory_t();
50     }
51 
52     status = jas_init();
53 #ifdef JPX_DEBUG
54     /* raise the error reporting threshold from the default (0) */
55     jas_setdbglevel(1);
56 #endif
57 
58     if (!status) {
59 	state->buffer = gs_malloc(state->jpx_memory, 4096, 1, "JPXDecode temp buffer");
60         status = (state->buffer == NULL);
61     }
62     if (!status)
63     	state->bufsize = 4096;
64 
65     return status;
66 }
67 
68 #ifdef DEBUG
69 /* dump information from a jasper image struct for debugging */
70 static int
dump_jas_image(jas_image_t * image)71 dump_jas_image(jas_image_t *image)
72 {
73     int i, numcmpts = jas_image_numcmpts(image);
74     int clrspc = jas_image_clrspc(image);
75     const char *csname = "unrecognized vendor space";
76 
77     if (image == NULL) return 1;
78 
79     if_debug2('w', "[w]JPX image is %d x %d\n",
80 	(int)jas_image_width(image), (int)jas_image_height(image));
81 
82     /* sort the colorspace */
83     if jas_clrspc_isunknown(clrspc) csname = "unknown";
84     else switch (clrspc) {
85 	case JAS_CLRSPC_CIEXYZ: csname = "CIE XYZ"; break;
86 	case JAS_CLRSPC_CIELAB: csname = "CIE Lab"; break;
87 	case JAS_CLRSPC_SGRAY: csname = "calibrated grayscale"; break;
88 	case JAS_CLRSPC_SRGB: csname = "sRGB"; break;
89 	case JAS_CLRSPC_SYCBCR: csname = "calibrated YCbCr"; break;
90 	case JAS_CLRSPC_GENGRAY: csname = "generic gray"; break;
91 	case JAS_CLRSPC_GENRGB: csname = "generic RGB"; break;
92 	case JAS_CLRSPC_GENYCBCR: csname = "generic YCbCr"; break;
93     }
94     if_debug3('w',"[w]  colorspace is %s (family %d, member %d)\n",
95 	csname, jas_clrspc_fam(clrspc), jas_clrspc_mbr(clrspc));
96 
97     for (i = 0; i < numcmpts; i++) {
98 	int type = jas_image_cmpttype(image, i);
99 	const char *opacity = (type & JAS_IMAGE_CT_OPACITY) ? " opacity" : "";
100 	const char *name = "unrecognized";
101 	const char *issigned = "";
102 	if (jas_clrspc_fam(clrspc) == JAS_CLRSPC_FAM_GRAY)
103 	    name = "gray";
104 	else if (jas_clrspc_fam(clrspc) == JAS_CLRSPC_FAM_RGB)
105 	    switch (JAS_IMAGE_CT_COLOR(type)) {
106 		case JAS_IMAGE_CT_RGB_R: name = "red"; break;
107 		case JAS_IMAGE_CT_RGB_G: name = "green"; break;
108 		case JAS_IMAGE_CT_RGB_B: name = "blue"; break;
109 		case JAS_IMAGE_CT_UNKNOWN:
110 		default:
111 		    name = "unknown";
112 	    }
113 	else if (jas_clrspc_fam(clrspc) == JAS_CLRSPC_FAM_YCBCR)
114 	    switch (JAS_IMAGE_CT_COLOR(type)) {
115 		case JAS_IMAGE_CT_YCBCR_Y: name = "luminance Y"; break;
116 		case JAS_IMAGE_CT_YCBCR_CB: name = "chrominance Cb"; break;
117 		case JAS_IMAGE_CT_YCBCR_CR: name = "chrominance Cr"; break;
118 		case JAS_IMAGE_CT_UNKNOWN:
119 		default:
120 		    name = "unknown";
121 	    }
122 	if (jas_image_cmptsgnd(image, i))
123 	    issigned = ", signed";
124 	if_debug6('w', "[w]  component %d: type %d '%s%s' (%d bits%s)",
125 	    i, type, name, opacity, jas_image_cmptprec(image, i), issigned);
126 	if_debug4('w', " grid step (%d,%d) offset (%d,%d)\n",
127 	    (int)jas_image_cmpthstep(image, i), (int)jas_image_cmptvstep(image, i),
128 	    (int)jas_image_cmpttlx(image, i), (int)jas_image_cmpttly(image, i));
129     }
130 
131     return 0;
132 }
133 
134 /* dump the external colorspace from the interpreter for debugging */
135 static int
dump_jpxd_colorspace(const stream_jpxd_state * state)136 dump_jpxd_colorspace(const stream_jpxd_state * state)
137 {
138   const char *cspace;
139 
140   if (state->colorspace == gs_jpx_cs_unset) {
141     if_debug0('w', "[w]JPX image has no external color space set\n");
142     return 0;
143   }
144 
145   switch (state->colorspace) {
146     case gs_jpx_cs_gray: cspace = "Grayscale based"; break;
147     case gs_jpx_cs_rgb: cspace = "RGB based"; break;
148     case gs_jpx_cs_cmyk: cspace = "CMYK based"; break;
149     case gs_jpx_cs_indexed: cspace = "indexed"; break;
150     default: cspace = "unknown"; break;
151   }
152 
153   if_debug1('w', "[w]Interpreter has set an external %s color space\n",
154 	cspace);
155 
156   return 0;
157 }
158 #endif /* DEBUG */
159 
160 static int
copy_row_gray(unsigned char * dest,jas_image_t * image,int x,int y,int bytes)161 copy_row_gray(unsigned char *dest, jas_image_t *image,
162 	int x, int y, int bytes)
163 {
164     int i, p;
165     int v;
166     int shift, bits;
167 
168     v = jas_image_getcmptbytype(image, JAS_IMAGE_CT_GRAY_Y);
169     if (v < 0) return 0; /* no matching component */
170 
171     bits = jas_image_cmptprec(image, v);
172     if (bits >= 8) {
173 	/* shift down to 8 bpp */
174 	shift = max(jas_image_cmptprec(image, v) - 8, 0);
175 
176 	for (i = 1; i <= bytes; i++) {
177 	    p = jas_image_readcmptsample(image, v, x++, y);
178 	    dest[i] = p >> shift;
179 	}
180    } else if (bits == 4) {
181 	/* return two packed pixels per byte */
182 	for (i = 1; i <= bytes; i++) {
183 	    p = jas_image_readcmptsample(image, v, x++, y) << 4;
184 	    p |= jas_image_readcmptsample(image, v, x++, y);
185 	    dest[i] = p;
186 	}
187     } else {
188 	/* todo: handle other bit depths */
189 	memset(dest + 1, 0x80, bytes);
190     }
191 
192     return bytes;
193 }
194 
195 static int
copy_row_rgb(unsigned char * dest,jas_image_t * image,int x,int y,int bytes)196 copy_row_rgb(unsigned char *dest, jas_image_t *image,
197 	int x, int y, int bytes)
198 {
199     int i, p;
200     int r = jas_image_getcmptbytype(image, JAS_IMAGE_CT_RGB_R);
201     int g = jas_image_getcmptbytype(image, JAS_IMAGE_CT_RGB_G);
202     int b = jas_image_getcmptbytype(image, JAS_IMAGE_CT_RGB_B);
203     int shift = max(jas_image_cmptprec(image, 0) - 8, 0);
204     int count = (bytes/3) * 3;
205 
206     /* check if we found indexes for all three components */
207     if (r < 0 || g < 0 || b < 0) return 0;
208 
209     for (i = 1; i <= count; i+=3) {
210 	p = jas_image_readcmptsample(image, r, x, y);
211 	dest[i] = p >> shift;
212 	p = jas_image_readcmptsample(image, g, x, y);
213 	dest[i+1] = p >> shift;
214 	p = jas_image_readcmptsample(image, b, x, y);
215 	dest[i+2] = p >> shift;
216 	x++;
217     }
218 
219     return count;
220 }
221 
222 static int
copy_row_yuv(unsigned char * dest,jas_image_t * image,int x,int y,int bytes)223 copy_row_yuv(unsigned char *dest, jas_image_t *image,
224 	int x, int y, int bytes)
225 {
226     int i,j;
227     int count = (bytes/3) * 3;
228     int shift[3];
229     int clut[3];
230     int hstep[3],vstep[3];
231     int p[3],q[3];
232 
233     /* get the component mapping */
234     clut[0] = jas_image_getcmptbytype(image, JAS_IMAGE_CT_YCBCR_Y);
235     clut[1] = jas_image_getcmptbytype(image, JAS_IMAGE_CT_YCBCR_CB);
236     clut[2] = jas_image_getcmptbytype(image, JAS_IMAGE_CT_YCBCR_CR);
237 
238     /* check if we found indexes for all components */
239     if (clut[0] < 0 || clut[1] < 0 || clut[2] < 0) return 0;
240 
241     for (i = 0; i < 3; i++) {
242 	/* shift each component up to 16 bits */
243 	shift[i] = 16 - jas_image_cmptprec(image, clut[i]);
244 	/* repeat subsampled pixels */
245 	hstep[i] = jas_image_cmpthstep(image, clut[i]);
246 	vstep[i] = jas_image_cmptvstep(image, clut[i]);
247     }
248     for (i = 1; i <= count; i+=3) {
249 	/* read the sample values */
250 	for (j = 0; j < 3; j++) {
251 	    p[j] = jas_image_readcmptsample(image, clut[j], x/hstep[j], y/vstep[j]);
252 	    p[j] <<= shift[j];
253 	}
254 	/* center chroma channels if necessary */
255 	if (!jas_image_cmptsgnd(image, clut[1])) p[1] -= 0x8000;
256 	if (!jas_image_cmptsgnd(image, clut[2])) p[2] -= 0x8000;
257 	/* rotate to RGB */
258 #ifdef JPX_USE_IRT
259 	q[1] = p[0] - ((p[1] + p[2])>>2);
260 	q[0] = p[1] + q[1];
261 	q[2] = p[2] + q[1];
262 #else
263 	q[0] = (int)((double)p[0] + 1.402 * p[2]);
264 	q[1] = (int)((double)p[0] - 0.34413 * p[1] - 0.71414 * p[2]);
265 	q[2] = (int)((double)p[0] + 1.772 * p[1]);
266 #endif
267 	/* clamp */
268 	for (j = 0; j < 3; j++){
269 	  if (q[j] < 0) q[j] = 0;
270 	  else if (q[j] > 0xFFFF) q[j] = 0xFFFF;
271    	}
272 	/* write out the pixel */
273 	dest[i] = q[0] >> 8;
274 	dest[i+1] = q[1] >> 8;
275 	dest[i+2] = q[2] >> 8;
276 	x++;
277     }
278 
279     return count;
280 }
281 
282 static int
copy_row_default(unsigned char * dest,jas_image_t * image,int x,int y,int bytes)283 copy_row_default(unsigned char *dest, jas_image_t *image,
284 	int x, int y, int bytes)
285 {
286     int i, c,n;
287     int count;
288 
289     n = jas_image_numcmpts(image);
290     count = (bytes/n) * n;
291     for (i = 1; i <= count; i+=n) {
292 	for (c = 0; c < n; c++)
293 	    dest[i+c] = jas_image_readcmptsample(image, c, x, y);
294 	x++;
295     }
296 
297     return count;
298 }
299 
300 /* buffer the input stream into our state */
301 static int
s_jpxd_buffer_input(stream_jpxd_state * const state,stream_cursor_read * pr,long bytes)302 s_jpxd_buffer_input(stream_jpxd_state *const state, stream_cursor_read *pr,
303 		       long bytes)
304 {
305     /* grow internal buffer if necessary */
306     if (bytes > state->bufsize - state->buffill) {
307         int newsize = state->bufsize;
308         unsigned char *newbuf = NULL;
309         while (newsize - state->buffill < bytes)
310             newsize <<= 1;
311         newbuf = (unsigned char *)gs_malloc(state->jpx_memory, newsize, 1,
312 					    "JPXDecode temp buffer");
313         /* TODO: check for allocation failure */
314         memcpy(newbuf, state->buffer, state->buffill);
315         gs_free(state->jpx_memory, state->buffer, state->bufsize, 1,
316 		"JPXDecode temp buffer");
317         state->buffer = newbuf;
318         state->bufsize = newsize;
319     }
320 
321     /* copy requested amount of data and return */
322     memcpy(state->buffer + state->buffill, pr->ptr + 1, bytes);
323     state->buffill += bytes;
324     pr->ptr += bytes;
325     return bytes;
326 }
327 
328 /* decode the compressed image data saved in our state */
329 static int
s_jpxd_decode_image(stream_jpxd_state * state)330 s_jpxd_decode_image(stream_jpxd_state * state)
331 {
332     jas_stream_t *stream = NULL;
333     jas_image_t *image = NULL;
334     char *optstr = NULL;
335 
336     /* if the external Colorspace key is indexed, we need to ask
337        for raw index values so the external palette can be applied */
338     if (state->colorspace == gs_jpx_cs_indexed) {
339 	if_debug0('w', "[w] got indexed colorspace in s_jpxd_decode_image\n");
340 	optstr = (char *)"raw";
341     }
342     /* wrap our buffer in a jas_stream */
343     stream = jas_stream_memopen((char*)state->buffer, state->buffill);
344     if (stream == NULL) {
345 	dprintf("unable to create stream for JPX image data.\n");
346 	return ERRC;
347     }
348     /* decode an image */
349     image = jas_image_decode(stream, -1, optstr);
350     if (image == NULL) {
351 	dprintf("unable to decode JPX image data.\n");
352 	return ERRC;
353     }
354 #ifdef JPX_USE_JASPER_CM
355 	/* convert non-rgb multicomponent colorspaces to sRGB */
356 	if (jas_image_numcmpts(image) > 1 &&
357 	    jas_clrspc_fam(jas_image_clrspc(image)) != JAS_CLRSPC_FAM_RGB) {
358 	    jas_cmprof_t *outprof;
359 	    jas_image_t *rgbimage = NULL;
360 	    outprof = jas_cmprof_createfromclrspc(JAS_CLRSPC_SRGB);
361 	    if (outprof != NULL)
362 		rgbimage = jas_image_chclrspc(image, outprof, JAS_CMXFORM_INTENT_PER);
363 	    if (rgbimage != NULL) {
364 		jas_image_destroy(image);
365 		image = rgbimage;
366 	    }
367 	}
368 #endif
369     state->image = image;
370     state->offset = 0;
371     jas_stream_close(stream);
372 
373 #ifdef DEBUG
374 	dump_jas_image(image);
375 	dump_jpxd_colorspace(state);
376 #endif
377 
378     return 0;
379 }
380 
381 /* process a secton of the input and return any decoded data.
382    see strimpl.h for return codes.
383  */
384 static int
s_jpxd_process(stream_state * ss,stream_cursor_read * pr,stream_cursor_write * pw,bool last)385 s_jpxd_process(stream_state * ss, stream_cursor_read * pr,
386                  stream_cursor_write * pw, bool last)
387 {
388     stream_jpxd_state *const state = (stream_jpxd_state *) ss;
389     long in_size = pr->limit - pr->ptr;
390     long out_size = pw->limit - pw->ptr;
391     int status = 0;
392 
393     /* note that the gs stream library expects offset-by-one
394        indexing of its buffers while we use zero indexing */
395 
396     /* JasPer has its own stream library, but there's no public
397        api for handing it pieces. We need to add some plumbing
398        to convert between gs and jasper streams. In the meantime
399        just buffer the entire stream, since it can handle that
400        as input. */
401 
402     /* pass all available input to the decoder */
403     if (in_size > 0) {
404 	s_jpxd_buffer_input(state, pr, in_size);
405     }
406     if (last) {
407       if (state->image == NULL) {
408 	status = s_jpxd_decode_image(state);
409       }
410       if (state->image != NULL) {
411 	jas_image_t *image = state->image;
412 	int numcmpts = jas_image_numcmpts(image);
413 	int bits = jas_image_cmptprec(image, 0);
414 	int stride = numcmpts*jas_image_width(image);
415 	long image_size = stride*jas_image_height(image);
416 	int clrspc = jas_image_clrspc(image);
417 	int x, y;
418 	long usable, done;
419 
420 	if (bits == 4) stride = (stride + 1)/2;
421 
422 	/* copy data out of the decoded image data */
423 	/* be lazy and only write the rest of the current row */
424 	y = state->offset / stride;
425 	x = state->offset - y*stride; /* bytes, not samples */
426 	usable = min(out_size, stride - x);
427 	x = x/numcmpts;               /* now samples */
428 
429 	/* Make sure we can return a full pixel.
430 	   This can fail if we get the colorspace wrong. */
431 	if (usable < numcmpts) return ERRC;
432 
433 	if (state->colorspace != gs_jpx_cs_unset)
434 	  /* An external colorspace from the interpreter overrides */
435 	  switch (state->colorspace) {
436 	    case gs_jpx_cs_gray:
437 	    case gs_jpx_cs_indexed:
438 	    /* we've passed 'raw' but the palette is the same pixel
439 	       format as a grayscale image. The PDF interpreter will
440 	       know to handle it differently. */
441 	      done = copy_row_gray(pw->ptr, image, x, y, usable);
442 	      break;
443 	    case gs_jpx_cs_rgb:
444 	      done = copy_row_rgb(pw->ptr, image, x, y, usable);
445 	      break;
446 	    case gs_jpx_cs_cmyk:
447 	    default:
448 	      done = copy_row_default(pw->ptr, image, x, y, usable);
449 	      break;
450 	  }
451 	else /* use the stream's colorspace */
452 	  switch (jas_clrspc_fam(clrspc)) {
453 		case JAS_CLRSPC_FAM_GRAY:
454 		    done = copy_row_gray(pw->ptr, image, x, y, usable);
455 		    break;
456 		case JAS_CLRSPC_FAM_RGB:
457 		    done = copy_row_rgb(pw->ptr, image, x, y, usable);
458 		    break;
459 		case JAS_CLRSPC_FAM_YCBCR:
460 		    done = copy_row_yuv(pw->ptr, image, x, y, usable);
461 		    break;
462 		case JAS_CLRSPC_FAM_XYZ:
463 		case JAS_CLRSPC_FAM_LAB:
464 		case JAS_CLRSPC_FAM_UNKNOWN:
465 		default:
466 		    done = copy_row_default(pw->ptr, image, x, y, usable);
467 		    break;
468 	 }
469 	/* advance pointers for returned data */
470 	pw->ptr += done;
471         state->offset += done;
472         status = (state->offset < image_size) ? 1 : EOFC;
473         /* return an error if we failed to advance decoding */
474         if (done <= 0) status = ERRC;
475       } /* image != NULL */
476     } /* last */
477 
478     return status;
479 }
480 
481 /* stream release.
482    free all our decoder state.
483  */
484 static void
s_jpxd_release(stream_state * ss)485 s_jpxd_release(stream_state *ss)
486 {
487     stream_jpxd_state *const state = (stream_jpxd_state *) ss;
488 
489     if (state) {
490         if (state->image) jas_image_destroy(state->image);
491 	if (state->buffer) gs_free(state->jpx_memory, state->buffer, state->bufsize, 1,
492 				"JPXDecode temp buffer");
493     }
494 }
495 
496 /* set stream defaults.
497    This hook exists to avoid confusing the gc with bogus
498    pointers. We also set a default for client-settable
499    parameters like the requested output colorspace.
500  */
501 static void
s_jpxd_set_defaults(stream_state * ss)502 s_jpxd_set_defaults(stream_state *ss)
503 {
504     stream_jpxd_state *const state = (stream_jpxd_state *) ss;
505 
506     state->image = NULL;
507     state->offset = 0;
508     state->buffer = NULL;
509     state->bufsize = 0;
510     state->buffill = 0;
511     /* the following can be set by the client before calling init() */
512     state->colorspace = gs_jpx_cs_unset;
513 }
514 
515 
516 /* stream template */
517 const stream_template s_jpxd_template = {
518     &st_jpxd_state,
519     s_jpxd_init,
520     s_jpxd_process,
521     1, 1, /* min in and out buffer sizes we can handle
522                      should be ~32k,64k for efficiency? */
523     s_jpxd_release,
524     s_jpxd_set_defaults
525 };
526