1 /* Copyright (C) 2003-2004 artofcode LLC. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: sjpx.c,v 1.12 2005/03/16 14:57:42 igor Exp $ */
18 /* JPXDecode filter implementation -- hooks in libjasper */
19
20 #include "memory_.h"
21 #ifdef JPX_DEBUG
22 #include "stdio_.h"
23 #endif
24
25 #include "gserrors.h"
26 #include "gserror.h"
27 #include "gdebug.h"
28 #include "strimpl.h"
29 #include "gsmalloc.h"
30 #include "sjpx.h"
31
32 /* stream implementation */
33
34 /* As with the /JBIG2Decode filter, we let the library do its own
35 memory management through malloc() etc. and rely on our release()
36 proc being called to deallocate state.
37 */
38
39 private_st_jpxd_state(); /* creates a gc object for our state,
40 defined in sjpx.h */
41
42 /* initialize the steam.
43 this involves allocating the stream and image structures, and
44 initializing the decoder.
45 */
46 private int
s_jpxd_init(stream_state * ss)47 s_jpxd_init(stream_state * ss)
48 {
49 stream_jpxd_state *const state = (stream_jpxd_state *) ss;
50 int status = 0;
51
52 state->buffer = NULL;
53 state->bufsize = 0;
54 state->buffill = 0;
55 state->stream = NULL;
56 state->image = NULL;
57 state->offset = 0;
58 state->jpx_memory = ss->memory ? ss->memory->non_gc_memory : gs_lib_ctx_get_non_gc_memory_t();
59
60 status = jas_init();
61
62 if (!status) {
63 state->buffer = gs_malloc(state->jpx_memory, 4096, 1, "JPXDecode temp buffer");
64 status = (state->buffer == NULL);
65 }
66 if (!status)
67 state->bufsize = 4096;
68
69 return status;
70 }
71
72 #ifdef JPX_DEBUG
73 /* dump information from a jasper image struct for debugging */
74 private int
dump_jas_image(jas_image_t * image)75 dump_jas_image(jas_image_t *image)
76 {
77 int i, numcmpts = jas_image_numcmpts(image);
78 int clrspc = jas_image_clrspc(image);
79 const char *csname = "unrecognized vendor space";
80
81 if (image == NULL) return 1;
82
83 dprintf2("JPX image is %d x %d\n",
84 jas_image_width(image), jas_image_height(image));
85
86 /* sort the colorspace */
87 if jas_clrspc_isunknown(clrspc) csname = "unknown";
88 else switch (clrspc) {
89 case JAS_CLRSPC_CIEXYZ: csname = "CIE XYZ"; break;
90 case JAS_CLRSPC_CIELAB: csname = "CIE Lab"; break;
91 case JAS_CLRSPC_SGRAY: csname = "calibrated grayscale"; break;
92 case JAS_CLRSPC_SRGB: csname = "sRGB"; break;
93 case JAS_CLRSPC_SYCBCR: csname = "calibrated YCbCr"; break;
94 case JAS_CLRSPC_GENGRAY: csname = "generic gray"; break;
95 case JAS_CLRSPC_GENRGB: csname = "generic RGB"; break;
96 case JAS_CLRSPC_GENYCBCR: csname = "generic YCbCr"; break;
97 }
98 dprintf3(" colorspace is %s (family %d, member %d)\n",
99 csname, jas_clrspc_fam(clrspc), jas_clrspc_mbr(clrspc));
100
101 for (i = 0; i < numcmpts; i++) {
102 int type = jas_image_cmpttype(image, i);
103 const char *opacity = (type & JAS_IMAGE_CT_OPACITY) ? " opacity" : "";
104 const char *name = "unrecognized";
105 const char *issigned = "";
106 if (jas_clrspc_fam(clrspc) == JAS_CLRSPC_FAM_GRAY)
107 name = "gray";
108 else if (jas_clrspc_fam(clrspc) == JAS_CLRSPC_FAM_RGB)
109 switch (JAS_IMAGE_CT_COLOR(type)) {
110 case JAS_IMAGE_CT_RGB_R: name = "red"; break;
111 case JAS_IMAGE_CT_RGB_G: name = "green"; break;
112 case JAS_IMAGE_CT_RGB_B: name = "blue"; break;
113 case JAS_IMAGE_CT_UNKNOWN:
114 default:
115 name = "unknown";
116 }
117 else if (jas_clrspc_fam(clrspc) == JAS_CLRSPC_FAM_YCBCR)
118 switch (JAS_IMAGE_CT_COLOR(type)) {
119 case JAS_IMAGE_CT_YCBCR_Y: name = "luminance Y"; break;
120 case JAS_IMAGE_CT_YCBCR_CB: name = "chrominance Cb"; break;
121 case JAS_IMAGE_CT_YCBCR_CR: name = "chrominance Cr"; break;
122 case JAS_IMAGE_CT_UNKNOWN:
123 default:
124 name = "unknown";
125 }
126 if (jas_image_cmptsgnd(image, i))
127 issigned = ", signed";
128 dprintf6(" component %d: type %d '%s%s' (%d bits%s)",
129 i, type, name, opacity, jas_image_cmptprec(image, i), issigned);
130 dprintf4(" grid step (%d,%d) offset (%d,%d)\n",
131 jas_image_cmpthstep(image, i), jas_image_cmptvstep(image, i),
132 jas_image_cmpttlx(image, i), jas_image_cmpttly(image, i));
133 }
134
135 return 0;
136 }
137 #endif /* JPX_DEBUG */
138
139 private int
copy_row_gray(unsigned char * dest,jas_image_t * image,int x,int y,int bytes)140 copy_row_gray(unsigned char *dest, jas_image_t *image,
141 int x, int y, int bytes)
142 {
143 int i, p;
144 int v = jas_image_getcmptbytype(image, JAS_IMAGE_CT_GRAY_Y);
145 int shift = max(jas_image_cmptprec(image, v) - 8, 0);
146
147 for (i = 1; i <= bytes; i++) {
148 p = jas_image_readcmptsample(image, v, x++, y);
149 dest[i] = p >> shift;
150 }
151
152 return bytes;
153 }
154
155 private int
copy_row_rgb(unsigned char * dest,jas_image_t * image,int x,int y,int bytes)156 copy_row_rgb(unsigned char *dest, jas_image_t *image,
157 int x, int y, int bytes)
158 {
159 int i, p;
160 int r = jas_image_getcmptbytype(image, JAS_IMAGE_CT_RGB_R);
161 int g = jas_image_getcmptbytype(image, JAS_IMAGE_CT_RGB_G);
162 int b = jas_image_getcmptbytype(image, JAS_IMAGE_CT_RGB_B);
163 int shift = max(jas_image_cmptprec(image, 0) - 8, 0);
164 int count = (bytes/3) * 3;
165
166 for (i = 1; i <= count; i+=3) {
167 p = jas_image_readcmptsample(image, r, x, y);
168 dest[i] = p >> shift;
169 p = jas_image_readcmptsample(image, g, x, y);
170 dest[i+1] = p >> shift;
171 p = jas_image_readcmptsample(image, b, x, y);
172 dest[i+2] = p >> shift;
173 x++;
174 }
175
176 return count;
177 }
178
179 private int
copy_row_yuv(unsigned char * dest,jas_image_t * image,int x,int y,int bytes)180 copy_row_yuv(unsigned char *dest, jas_image_t *image,
181 int x, int y, int bytes)
182 {
183 int i,j;
184 int count = (bytes/3) * 3;
185 int shift[3];
186 int clut[3];
187 int hstep[3],vstep[3];
188 int p[3],q[3];
189
190 /* get the component mapping */
191 clut[0] = jas_image_getcmptbytype(image, JAS_IMAGE_CT_YCBCR_Y);
192 clut[1] = jas_image_getcmptbytype(image, JAS_IMAGE_CT_YCBCR_CB);
193 clut[2] = jas_image_getcmptbytype(image, JAS_IMAGE_CT_YCBCR_CR);
194
195 for (i = 0; i < 3; i++) {
196 /* shift each component up to 16 bits */
197 shift[i] = 16 - jas_image_cmptprec(image, clut[i]);
198 /* repeat subsampled pixels */
199 hstep[i] = jas_image_cmpthstep(image, clut[i]);
200 vstep[i] = jas_image_cmptvstep(image, clut[i]);
201 }
202 for (i = 1; i <= count; i+=3) {
203 /* read the sample values */
204 for (j = 0; j < 3; j++) {
205 p[j] = jas_image_readcmptsample(image, clut[j], x/hstep[j], y/vstep[j]);
206 p[j] <<= shift[j];
207 }
208 /* center chroma channels if necessary */
209 if (!jas_image_cmptsgnd(image, clut[1])) p[1] -= 0x8000;
210 if (!jas_image_cmptsgnd(image, clut[2])) p[2] -= 0x8000;
211 /* rotate to RGB */
212 #ifdef JPX_USE_IRT
213 q[1] = p[0] - ((p[1] + p[2])>>2);
214 q[0] = p[1] + q[1];
215 q[2] = p[2] + q[1];
216 #else
217 q[0] = (int)((double)p[0] + 1.402 * p[2]);
218 q[1] = (int)((double)p[0] - 0.34413 * p[1] - 0.71414 * p[2]);
219 q[2] = (int)((double)p[0] + 1.772 * p[1]);
220 #endif
221 /* clamp */
222 for (j = 0; j < 3; j++){
223 if (q[j] < 0) q[j] = 0;
224 else if (q[j] > 0xFFFF) q[j] = 0xFFFF;
225 }
226 /* write out the pixel */
227 dest[i] = q[0] >> 8;
228 dest[i+1] = q[1] >> 8;
229 dest[i+2] = q[2] >> 8;
230 x++;
231 }
232
233 return count;
234 }
235
236 private int
copy_row_default(unsigned char * dest,jas_image_t * image,int x,int y,int bytes)237 copy_row_default(unsigned char *dest, jas_image_t *image,
238 int x, int y, int bytes)
239 {
240 int i, c,n;
241 int count;
242
243 n = jas_image_numcmpts(image);
244 count = (bytes/n) * n;
245 for (i = 1; i <= count; i+=n) {
246 for (c = 0; c < n; c++)
247 dest[i+c] = jas_image_readcmptsample(image, c, x, y);
248 x++;
249 }
250
251 return count;
252 }
253
254 /* buffer the input stream into our state */
255 private int
s_jpxd_buffer_input(stream_jpxd_state * const state,stream_cursor_read * pr,long bytes)256 s_jpxd_buffer_input(stream_jpxd_state *const state, stream_cursor_read *pr,
257 long bytes)
258 {
259 /* grow internal buffer if necessary */
260 if (bytes > state->bufsize - state->buffill) {
261 int newsize = state->bufsize;
262 unsigned char *newbuf = NULL;
263 while (newsize - state->buffill < bytes)
264 newsize <<= 1;
265 newbuf = (unsigned char *)gs_malloc(state->jpx_memory, newsize, 1,
266 "JPXDecode temp buffer");
267 /* TODO: check for allocation failure */
268 memcpy(newbuf, state->buffer, state->buffill);
269 gs_free(state->jpx_memory, state->buffer, state->bufsize, 1,
270 "JPXDecode temp buffer");
271 state->buffer = newbuf;
272 state->bufsize = newsize;
273 }
274
275 /* copy requested amount of data and return */
276 memcpy(state->buffer + state->buffill, pr->ptr + 1, bytes);
277 state->buffill += bytes;
278 pr->ptr += bytes;
279 return bytes;
280 }
281
282 /* decode the compressed image data saved in our state */
283 private int
s_jpxd_decode_image(stream_jpxd_state * state)284 s_jpxd_decode_image(stream_jpxd_state * state)
285 {
286 jas_stream_t *stream = state->stream;
287 jas_image_t *image = NULL;
288
289 /* see if an image is available */
290 if (stream != NULL) {
291 image = jas_image_decode(stream, -1, 0);
292 if (image == NULL) {
293 dprintf("unable to decode JPX image data.\n");
294 return ERRC;
295 }
296 #ifdef JPX_USE_JASPER_CM
297 /* convert non-rgb multicomponent colorspaces to sRGB */
298 if (jas_image_numcmpts(image) > 1 &&
299 jas_clrspc_fam(jas_image_clrspc(image)) != JAS_CLRSPC_FAM_RGB) {
300 jas_cmprof_t *outprof;
301 jas_image_t *rgbimage = NULL;
302 outprof = jas_cmprof_createfromclrspc(JAS_CLRSPC_SRGB);
303 if (outprof != NULL)
304 rgbimage = jas_image_chclrspc(image, outprof, JAS_CMXFORM_INTENT_PER);
305 if (rgbimage != NULL) {
306 jas_image_destroy(image);
307 image = rgbimage;
308 }
309 }
310 #endif
311 state->image = image;
312 state->offset = 0;
313 jas_stream_close(stream);
314 state->stream = NULL;
315
316 #ifdef JPX_DEBUG
317 dump_jas_image(image);
318 #endif
319
320 }
321
322 return 0;
323 }
324
325 /* process a secton of the input and return any decoded data.
326 see strimpl.h for return codes.
327 */
328 private int
s_jpxd_process(stream_state * ss,stream_cursor_read * pr,stream_cursor_write * pw,bool last)329 s_jpxd_process(stream_state * ss, stream_cursor_read * pr,
330 stream_cursor_write * pw, bool last)
331 {
332 stream_jpxd_state *const state = (stream_jpxd_state *) ss;
333 jas_stream_t *stream = state->stream;
334 long in_size = pr->limit - pr->ptr;
335 long out_size = pw->limit - pw->ptr;
336 int status = 0;
337
338 /* note that the gs stream library expects offset-by-one
339 indexing of its buffers while we use zero indexing */
340
341 /* JasPer has its own stream library, but there's no public
342 api for handing it pieces. We need to add some plumbing
343 to convert between gs and jasper streams. In the meantime
344 just buffer the entire stream, since it can handle that
345 as input. */
346
347 /* pass all available input to the decoder */
348 if (in_size > 0) {
349 s_jpxd_buffer_input(state, pr, in_size);
350 }
351 if ((last == 1) && (stream == NULL) && (state->image == NULL)) {
352 /* turn our buffer into a stream */
353 stream = jas_stream_memopen((char*)state->buffer, state->bufsize);
354 state->stream = stream;
355 }
356 if (out_size > 0) {
357 if (state->image == NULL) {
358 status = s_jpxd_decode_image(state);
359 }
360 if (state->image != NULL) {
361 jas_image_t *image = state->image;
362 int numcmpts = jas_image_numcmpts(image);
363 int stride = numcmpts*jas_image_width(image);
364 long image_size = stride*jas_image_height(image);
365 int clrspc = jas_image_clrspc(image);
366 int x, y;
367 long usable, done;
368 y = state->offset / stride;
369 x = state->offset - y*stride; /* bytes, not samples */
370 usable = min(out_size, stride - x);
371 x = x/numcmpts; /* now samples */
372 /* copy data out of the decoded image data */
373 /* be lazy and only write the rest of the current row */
374 switch (jas_clrspc_fam(clrspc)) {
375 case JAS_CLRSPC_FAM_RGB:
376 done = copy_row_rgb(pw->ptr, image, x, y, usable);
377 break;
378 case JAS_CLRSPC_FAM_YCBCR:
379 done = copy_row_yuv(pw->ptr, image, x, y, usable);
380 break;
381 case JAS_CLRSPC_FAM_GRAY:
382 done = copy_row_gray(pw->ptr, image, x, y, usable);
383 break;
384 case JAS_CLRSPC_FAM_XYZ:
385 case JAS_CLRSPC_FAM_LAB:
386 case JAS_CLRSPC_FAM_UNKNOWN:
387 default:
388 done = copy_row_default(pw->ptr, image, x, y, usable);
389 break;
390 }
391 pw->ptr += done;
392 state->offset += done;
393 status = (state->offset < image_size) ? 1 : 0;
394 }
395 }
396
397 return status;
398 }
399
400 /* stream release.
401 free all our decoder state.
402 */
403 private void
s_jpxd_release(stream_state * ss)404 s_jpxd_release(stream_state *ss)
405 {
406 stream_jpxd_state *const state = (stream_jpxd_state *) ss;
407
408 if (state) {
409 if (state->image) jas_image_destroy(state->image);
410 if (state->stream) jas_stream_close(state->stream);
411 if (state->buffer) gs_free(state->jpx_memory, state->buffer, state->bufsize, 1,
412 "JPXDecode temp buffer");
413 }
414 }
415
416 /* set stream defaults.
417 this hook exists to avoid confusing the gc with bogus
418 pointers. we use it similarly just to NULL all the pointers.
419 (could just be done in _init?)
420 */
421 private void
s_jpxd_set_defaults(stream_state * ss)422 s_jpxd_set_defaults(stream_state *ss)
423 {
424 stream_jpxd_state *const state = (stream_jpxd_state *) ss;
425
426 state->stream = NULL;
427 state->image = NULL;
428 state->offset = 0;
429 state->buffer = NULL;
430 state->bufsize = 0;
431 state->buffill = 0;
432 }
433
434
435 /* stream template */
436 const stream_template s_jpxd_template = {
437 &st_jpxd_state,
438 s_jpxd_init,
439 s_jpxd_process,
440 1, 1, /* min in and out buffer sizes we can handle
441 should be ~32k,64k for efficiency? */
442 s_jpxd_release,
443 s_jpxd_set_defaults
444 };
445