Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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
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
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
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
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
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
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
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
};