xref: /plan9/sys/src/cmd/gs/src/sjpx.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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