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