1
/* Copyright (C) 2001-2006 Artifex Software, Inc.
4
This software is provided AS-IS with no warranty, either express or
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.
14
/* $Id: sjpx_luratech.c 8816 2008-07-03 18:25:52Z giles $ */
15
/* JPXDecode filter implementation -- hooks in the Luratech JPEG2K CSDK */
23
#include "sjpx_luratech.h"
27
/* JPXDecode stream implementation using the Luratech library */
29
/* if linking against a SDK build that requires a separate license key,
30
you can change the following undefs to defines and set them here. */
32
#ifndef JP2_LICENSE_NUM_1
33
# undef JP2_LICENSE_NUM_1
35
#ifndef JP2_LICENSE_NUM_2
36
# undef JP2_LICENSE_NUM_2
40
/* As with the /JBIG2Decode filter, we let the library do its
41
memory management through malloc() etc. and rely on our release()
42
proc being called to deallocate state.
45
private_st_jpxd_state(); /* creates a gc object for our state,
48
#define JPX_BUFFER_SIZE 1024
50
/** callback for the codec library */
52
/* memory allocation */
53
static void * JP2_Callback_Conv
54
s_jpx_alloc(long size, JP2_Callback_Param param)
56
void *result = malloc(size);
62
static JP2_Error JP2_Callback_Conv
63
s_jpx_free(void *ptr, JP2_Callback_Param param)
70
/* pass any available input to the library */
71
static unsigned long JP2_Callback_Conv
72
s_jpxd_read_data(unsigned char *pucData,
73
unsigned long ulPos, unsigned long ulSize,
74
JP2_Callback_Param param)
76
stream_jpxd_state *const state = (stream_jpxd_state *) param;
77
unsigned long copy_bytes = min(ulSize, state->inbuf_fill - ulPos);
79
memcpy(pucData, state->inbuf + ulPos, copy_bytes);
84
/* write decompressed data into our image buffer */
85
static JP2_Error JP2_Callback_Conv
86
s_jpxd_write_data(unsigned char * pucData,
89
unsigned long ulStart,
91
JP2_Callback_Param param)
93
stream_jpxd_state *const state = (stream_jpxd_state *) param;
98
if (ulRow >= state->height) return cJP2_Error_Invalid_Height;
99
if (ulStart + ulNum >= state->width) ulNum = state->width - ulStart;
101
/* Here we just copy a byte at a time into an image buffer,
102
interleaving with whatever data already exists. For multi-
103
component images, it would be more efficient to save rows
104
from each call in planar buffers and interleave a tile at
105
a time into a stipe buffer for output */
107
/* todo: handle non-8-bit samples, subsampled components,
108
and Y'CrCb colorspace rotation */
110
p = state->image + state->stride*ulRow + state->ncomp*ulStart;
111
if (state->ncomp == 1)
112
memcpy(p, pucData, ulNum);
113
else for (i = 0; i < ulNum; i++) {
114
p[sComponent] = pucData[i];
117
return cJP2_Error_OK;
121
s_jpxd_inbuf(stream_jpxd_state *state, stream_cursor_read * pr)
123
long in_size = pr->limit - pr->ptr;
125
/* allocate the input buffer if needed */
126
if (state->inbuf == NULL) {
127
state->inbuf = malloc(JPX_BUFFER_SIZE);
128
if (state->inbuf == NULL) return gs_error_VMerror;
129
state->inbuf_size = JPX_BUFFER_SIZE;
130
state->inbuf_fill = 0;
133
/* grow the input buffer if needed */
134
if (state->inbuf_size < state->inbuf_fill + in_size) {
136
unsigned long new_size = state->inbuf_size;
138
while (new_size < state->inbuf_fill + in_size)
139
new_size = new_size << 1;
141
if_debug1('s', "[s]jpxd growing input buffer to %lu bytes\n",
143
new = realloc(state->inbuf, new_size);
144
if (new == NULL) return gs_error_VMerror;
147
state->inbuf_size = new_size;
150
/* copy the available input into our buffer */
151
/* note that the gs stream library uses offset-by-one
152
indexing of its buffers while we use zero indexing */
153
memcpy(state->inbuf + state->inbuf_fill, pr->ptr + 1, in_size);
154
state->inbuf_fill += in_size;
160
/* initialize the steam.
161
this involves allocating the stream and image structures, and
162
initializing the decoder.
165
s_jpxd_init(stream_state * ss)
167
stream_jpxd_state *const state = (stream_jpxd_state *) ss;
169
if (state->jpx_memory == NULL) {
170
state->jpx_memory = ss->memory ?
171
ss->memory->non_gc_memory :
172
gs_lib_ctx_get_non_gc_memory_t();
175
state->handle = (JP2_Decomp_Handle)NULL;
178
state->inbuf_size = 0;
179
state->inbuf_fill = 0;
192
/* process a secton of the input and return any decoded data.
193
see strimpl.h for return codes.
196
s_jpxd_process(stream_state * ss, stream_cursor_read * pr,
197
stream_cursor_write * pw, bool last)
199
stream_jpxd_state *const state = (stream_jpxd_state *) ss;
201
JP2_Property_Value result;
202
long in_size = pr->limit - pr->ptr;
203
long out_size = pw->limit - pw->ptr;
206
/* buffer available data */
207
s_jpxd_inbuf(state, pr);
211
/* we have all the data, decode and return */
213
if (state->handle == (JP2_Decomp_Handle)NULL) {
214
/* initialize decompressor */
215
err = JP2_Decompress_Start(&state->handle,
216
/* memory allocator callbacks */
217
s_jpx_alloc, (JP2_Callback_Param)state,
218
s_jpx_free, (JP2_Callback_Param)state,
219
/* our read callback */
220
s_jpxd_read_data, (JP2_Callback_Param)state
222
if (err != cJP2_Error_OK) {
223
dlprintf1("Luratech JP2 error %d starting decompression\n", (int)err);
226
#if defined(JP2_LICENSE_NUM_1) && defined(JP2_LICENSE_NUM_2)
227
/* set the license keys if appropriate */
228
error = JP2_Decompress_SetLicense(state->handle,
229
JP2_LICENSE_NUM_1, JP2_LICENSE_NUM_2);
230
if (error != cJP2_Error_OK) {
231
dlprintf1("Luratech JP2 error %d setting license\n", (int)err);
235
/* parse image parameters */
236
err = JP2_Decompress_GetProp(state->handle,
237
cJP2_Prop_Components, &result, -1, -1);
238
if (err != cJP2_Error_OK) {
239
dlprintf1("Luratech JP2 error %d decoding number of image components\n", (int)err);
242
state->ncomp = result;
244
if_debug1('w', "[w]jpxd image has %d components\n", state->ncomp);
247
const char *cspace = "unknown";
248
err = JP2_Decompress_GetProp(state->handle,
249
cJP2_Prop_Extern_Colorspace, &result, -1, -1);
250
if (err != cJP2_Error_OK) {
251
dlprintf1("Luratech JP2 error %d decoding colorspace\n", (int)err);
255
case cJP2_Colorspace_Gray: cspace = "gray"; break;
256
case cJP2_Colorspace_RGBa: cspace = "sRGB"; break;
257
case cJP2_Colorspace_RGB_YCCa:
258
cspace = "sRGB YCrCb"; break;
259
case cJP2_Colorspace_CIE_LABa:
260
cspace = "CIE Lab"; break;
261
case cJP2_Colorspace_ICCa:
262
cspace = "ICC profile"; break;
263
case cJP2_Colorspace_Palette_Gray:
264
cspace = "indexed gray"; break;
265
case cJP2_Colorspace_Palette_RGBa:
266
cspace = "indexed sRGB"; break;
267
case cJP2_Colorspace_Palette_RGB_YCCa:
268
cspace = "indexed sRGB YCrCb"; break;
269
case cJP2_Colorspace_Palette_CIE_LABa:
270
cspace = "indexed CIE Lab"; break;
271
case cJP2_Colorspace_Palette_ICCa:
272
cspace = "indexed with ICC profile"; break;
274
if_debug1('w', "[w]jpxd image colorspace is %s\n", cspace);
277
/* the library doesn't return the overall image dimensions
278
or depth, so we take the maximum of the component values */
286
for (comp = 0; comp < state->ncomp; comp++) {
287
err= JP2_Decompress_GetProp(state->handle,
288
cJP2_Prop_Width, &result, -1, (short)comp);
289
if (err != cJP2_Error_OK) {
290
dlprintf2("Luratech JP2 error %d decoding "
291
"width for component %d\n", (int)err, comp);
295
err= JP2_Decompress_GetProp(state->handle,
296
cJP2_Prop_Height, &result, -1, (short)comp);
297
if (err != cJP2_Error_OK) {
298
dlprintf2("Luratech JP2 error %d decoding "
299
"height for component %d\n", (int)err, comp);
303
err= JP2_Decompress_GetProp(state->handle,
304
cJP2_Prop_Bits_Per_Sample, &result, -1, (short)comp);
305
if (err != cJP2_Error_OK) {
306
dlprintf2("Luratech JP2 error %d decoding "
307
"bits per sample for component %d\n", (int)err, comp);
311
err= JP2_Decompress_GetProp(state->handle,
312
cJP2_Prop_Signed_Samples, &result, -1, (short)comp);
313
if (err != cJP2_Error_OK) {
314
dlprintf2("Luratech JP2 error %d decoding "
315
"signedness of component %d\n", (int)err, comp);
320
"[w]jpxd image component %d has %dx%d %s %d bit samples\n",
322
is_signed ? "signed" : "unsigned", bits);
324
/* update image maximums */
325
if (state->width < width) state->width = width;
326
if (state->height < height) state->height = height;
327
if (state->bpc < bits) state->bpc = bits;
330
if_debug3('w', "[w]jpxd decoding image at %ldx%ld"
331
" with %d bits per component\n",
332
state->width, state->height, state->bpc);
336
if (state->handle != (JP2_Decomp_Handle)NULL &&
337
state->image == NULL) {
339
/* allocate our output buffer */
340
state->stride = state->width*state->ncomp;
341
state->image = malloc(state->stride*state->height);
342
if (state->image == NULL) return ERRC;
344
/* attach our output callback */
345
err = JP2_Decompress_SetProp(state->handle,
346
cJP2_Prop_Output_Parameter, (JP2_Property_Value)state);
347
if (err != cJP2_Error_OK) {
348
dlprintf1("Luratech JP2 error %d setting output parameter\n", (int)err);
351
err = JP2_Decompress_SetProp(state->handle,
352
cJP2_Prop_Output_Function,
353
(JP2_Property_Value)s_jpxd_write_data);
354
if (err != cJP2_Error_OK) {
355
dlprintf1("Luratech JP2 error %d setting output function\n", (int)err);
359
/* decompress the image */
360
err = JP2_Decompress_Image(state->handle);
361
if (err != cJP2_Error_OK) {
362
dlprintf1("Luratech JP2 error %d decoding image data\n", (int)err);
363
return ERRC; /* parsing error */
367
/* copy out available data */
368
if (state->image != NULL && out_size > 0) {
369
/* copy some output data */
370
long available = min(out_size,
371
state->stride*state->height - state->offset);
372
memcpy(pw->ptr + 1, state->image + state->offset, available);
373
state->offset += available;
374
pw->ptr += available;
375
/* more output to deliver? */
376
if (state->offset == state->stride*state->height) return 1;
380
/* ask for more data */
385
free all our decoder state.
388
s_jpxd_release(stream_state *ss)
390
stream_jpxd_state *const state = (stream_jpxd_state *) ss;
394
err = JP2_Decompress_End(state->handle);
395
if (state->inbuf) free(state->inbuf);
396
if (state->image) free(state->image);
400
/* stream template */
401
const stream_template s_jpxd_template = {
405
1024, 1024, /* min in and out buffer sizes we can handle
406
should be ~32k,64k for efficiency? */
412
/*** encode support **/
414
/* we provide a C-only encode filter for generating JPX image data
415
for embedding in PDF. */
417
/* create a gc object for our state, defined in sjpx_luratech.h */
418
private_st_jpxe_state();
420
/* callback for uncompressed data input */
421
static JP2_Error JP2_Callback_Conv
422
s_jpxe_read(unsigned char *buffer, short component,
423
unsigned long row, unsigned long start,
424
unsigned long num, JP2_Callback_Param param)
426
stream_jpxe_state *state = (stream_jpxe_state *)param;
427
unsigned long available, sentinel;
431
if (component < 0 || component >= state->components) {
432
dlprintf2("Luratech JP2 requested image data for unknown component %d of %u\n",
433
(int)component, state->components);
434
return cJP2_Error_Invalid_Component_Index;
437
/* todo: handle subsampled components and bpc != 8 */
439
/* clip to array bounds */
440
sentinel = row*state->stride + (start + num)*state->components;
441
available = min(sentinel, state->infill);
442
num = min(num, available / state->components);
444
p = state->inbuf + state->stride*row + state->components*start;
445
if (state->components == 1)
446
memcpy(buffer, p, num);
447
else for (i = 0; i < num; i++) {
448
buffer[i] = p[component];
449
p += state->components;
452
if (available < sentinel) return cJP2_Error_Failure_Read;
453
else return cJP2_Error_OK;
456
/* callback for compressed data output */
457
static JP2_Error JP2_Callback_Conv
458
s_jpxe_write(unsigned char *buffer,
459
unsigned long pos, unsigned long size,
460
JP2_Callback_Param param)
462
stream_jpxe_state *state = (stream_jpxe_state *)param;
465
if (state == NULL) return cJP2_Error_Invalid_Pointer;
467
/* allocate the output buffer if necessary */
468
if (state->outbuf == NULL) {
469
state->outbuf = malloc(JPX_BUFFER_SIZE);
470
if (state->outbuf == NULL) {
471
dprintf("jpx encode: failed to allocate output buffer.\n");
472
return cJP2_Error_Failure_Malloc;
474
state->outsize = JPX_BUFFER_SIZE;
477
/* grow the output buffer if necessary */
478
while (pos+size > state->outsize) {
479
unsigned char *new = realloc(state->outbuf, state->outsize*2);
481
dprintf1("jpx encode: failed to resize output buffer"
482
" beyond %lu bytes.\n", state->outsize);
483
return cJP2_Error_Failure_Malloc;
487
if_debug1('s', "[s] jpxe output buffer resized to %lu bytes\n",
491
/* copy data into our buffer; we've assured there is enough room. */
492
memcpy(state->outbuf + pos, buffer, size);
493
/* update high water mark */
494
if (state->outfill < pos + size) state->outfill = pos + size;
496
return cJP2_Error_OK;
499
/* set defaults for user-configurable parameters */
501
s_jpxe_set_defaults(stream_state *ss)
503
stream_jpxe_state *state = (stream_jpxe_state *)ss;
505
/* most common default colorspace */
506
state->colorspace = gs_jpx_cs_rgb;
508
/* default to lossy 60% quality */
513
/* initialize the stream */
515
s_jpxe_init(stream_state *ss)
517
stream_jpxe_state *state = (stream_jpxe_state *)ss;
521
/* width, height, bpc and colorspace are set by the client,
522
calculate the rest */
523
switch (state->colorspace) {
524
case gs_jpx_cs_gray: state->components = 1; break;
525
case gs_jpx_cs_rgb: state->components = 3; break;
526
case gs_jpx_cs_cmyk: state->components = 4; break;
527
default: state->components = 0;
529
state->stride = state->width * state->components;
531
if_debug3('w', "[w] jpxe init %lux%lu image with %d components\n",
532
state->width, state->height, state->components);
533
if_debug1('w', "[w] jpxe init image is %d bits per component\n", state->bpc);
535
if (state->lossless) {
536
if_debug0('w', "[w] jpxe image using lossless encoding\n");
537
state->quality = 100; /* implies lossless */
539
if_debug1('w', "[w] jpxe image quality level %d\n", state->quality);
542
/* null the input buffer */
547
/* null the output buffer */
548
state->outbuf = NULL;
553
/* initialize the encoder */
554
err = JP2_Compress_Start(&state->handle,
555
/* memory allocator callbacks */
556
s_jpx_alloc, (JP2_Callback_Param)state,
557
s_jpx_free, (JP2_Callback_Param)state,
559
if (err != cJP2_Error_OK) {
560
dlprintf1("Luratech JP2 error %d starting compressor\n", (int)err);
564
#if defined(JP2_LICENSE_NUM_1) && defined(JP2_LICENSE_NUM_2)
565
/* set license keys if appropriate */
566
error = JP2_Decompress_SetLicense(state->handle,
567
JP2_LICENSE_NUM_1, JP2_LICENSE_NUM_2);
568
if (error != cJP2_Error_OK) {
569
dlprintf1("Luratech JP2 error %d setting license\n", (int)err);
574
/* install our callbacks */
575
err = JP2_Compress_SetProp(state->handle,
576
cJP2_Prop_Input_Parameter, (JP2_Property_Value)state, -1, -1);
577
if (err != cJP2_Error_OK) {
578
dlprintf1("Luratech JP2 error %d setting input callback parameter.\n", (int)err);
581
err = JP2_Compress_SetProp(state->handle,
582
cJP2_Prop_Input_Function, (JP2_Property_Value)s_jpxe_read, -1, -1);
583
if (err != cJP2_Error_OK) {
584
dlprintf1("Luratech JP2 error %d setting input callback function.\n", (int)err);
587
err = JP2_Compress_SetProp(state->handle,
588
cJP2_Prop_Write_Parameter, (JP2_Property_Value)state, -1, -1);
589
if (err != cJP2_Error_OK) {
590
dlprintf1("Luratech JP2 error %d setting compressed output callback parameter.\n", (int)err);
593
err = JP2_Compress_SetProp(state->handle,
594
cJP2_Prop_Write_Function, (JP2_Property_Value)s_jpxe_write, -1, -1);
595
if (err != cJP2_Error_OK) {
596
dlprintf1("Luratech JP2 error %d setting compressed output callback function.\n", (int)err);
600
/* set image parameters - the same for all components */
601
err = JP2_Compress_SetProp(state->handle,
602
cJP2_Prop_Width, state->width, -1, -1);
603
if (err != cJP2_Error_OK) {
604
dlprintf1("Luratech JP2 error %d setting width\n", (int)err);
607
err = JP2_Compress_SetProp(state->handle,
608
cJP2_Prop_Height, state->height, -1, -1);
609
if (err != cJP2_Error_OK) {
610
dlprintf1("Luratech JP2 error %d setting height\n", (int)err);
613
err = JP2_Compress_SetProp(state->handle,
614
cJP2_Prop_Bits_Per_Sample, state->bpc, -1, -1);
615
if (err != cJP2_Error_OK) {
616
dlprintf1("Luratech JP2 error %d setting bits per sample\n", (int)err);
620
switch (state->colorspace) {
621
case gs_jpx_cs_gray: value = cJP2_Colorspace_Gray; break;
622
case gs_jpx_cs_rgb: value = cJP2_Colorspace_RGBa; break;
623
case gs_jpx_cs_cmyk: value = cJP2_Colorspace_CMYKa; break;
625
dlprintf1("Unknown colorspace %d initializing JP2 encoder\n",
626
(int)state->colorspace);
629
err = JP2_Compress_SetProp(state->handle,
630
cJP2_Prop_Extern_Colorspace, value, -1, -1);
631
if (err != cJP2_Error_OK) {
632
dlprintf1("Luratech JP2 error %d setting colorspace\n", (int)err);
636
if (state->lossless) {
637
/* the default encoding mode is lossless */
641
/* otherwise, set 9,7 wavelets and quality-target mode */
642
err = JP2_Compress_SetProp(state->handle,
643
cJP2_Prop_Wavelet_Filter, cJP2_Wavelet_9_7, -1, -1);
644
if (err != cJP2_Error_OK) {
645
dlprintf1("Luratech JP2 error %d setting wavelet filter\n", (int)err);
648
err = JP2_Compress_SetProp(state->handle,
649
cJP2_Prop_Rate_Quality, state->quality, -1, -1);
650
if (err != cJP2_Error_OK) {
651
dlprintf1("Luratech JP2 error %d setting compression quality\n", (int)err);
655
/* we use the encoder's defaults for all other parameters */
660
/* process input and return any encoded data.
661
see strimpl.h for return codes. */
663
s_jpxe_process(stream_state *ss, stream_cursor_read *pr,
664
stream_cursor_write *pw, bool last)
666
stream_jpxe_state *state = (stream_jpxe_state *)ss;
667
long in_size = pr->limit - pr->ptr;
668
long out_size = pw->limit - pw->ptr;
672
/* HACK -- reinstall our callbacks in case the GC has moved our state structure */
673
/* this should be done instead from a pointer relocation callback, or initialization
674
moved entirely inside the process routine. */
675
err = JP2_Compress_SetProp(state->handle,
676
cJP2_Prop_Input_Parameter, (JP2_Property_Value)state, -1, -1);
677
if (err != cJP2_Error_OK) {
678
dlprintf1("Luratech JP2 error %d setting input callback parameter.\n", (int)err);
681
err = JP2_Compress_SetProp(state->handle,
682
cJP2_Prop_Write_Parameter, (JP2_Property_Value)state, -1, -1);
683
if (err != cJP2_Error_OK) {
684
dlprintf1("Luratech JP2 error %d setting compressed output callback parameter.\n", (int)err);
690
/* allocate our input buffer if necessary */
691
if (state->inbuf == NULL) {
692
state->inbuf = malloc(JPX_BUFFER_SIZE);
693
if (state->inbuf == NULL) {
694
dprintf("jpx encode: failed to allocate input buffer.\n");
697
state->insize = JPX_BUFFER_SIZE;
700
/* grow our input buffer if necessary */
701
while (state->infill + in_size > state->insize) {
702
unsigned char *new = realloc(state->inbuf, state->insize*2);
704
dprintf("jpx encode: failed to resize input buffer.\n");
711
/* copy available input */
712
memcpy(state->inbuf + state->infill, pr->ptr + 1, in_size);
713
state->infill += in_size;
717
if (last && state->outbuf == NULL) {
718
/* We have all the data; call the compressor.
719
our callback will automatically allocate the output buffer */
720
if_debug0('w', "[w] jpxe process compressing image data\n");
721
err = JP2_Compress_Image(state->handle);
722
if (err != cJP2_Error_OK) {
723
dlprintf1("Luratech JP2 error %d compressing image data.\n", (int)err);
728
if (state->outbuf != NULL) {
729
/* copy out any available output data */
730
available = min(out_size, state->outfill - state->offset);
731
memcpy(pw->ptr + 1, state->outbuf + state->offset, available);
732
pw->ptr += available;
733
state->offset += available;
735
/* do we have any more data? */
736
if (state->outfill - state->offset > 0) return 1;
737
else return EOFC; /* all done */
740
/* something went wrong above */
744
/* stream release. free all our state. */
746
s_jpxe_release(stream_state *ss)
748
stream_jpxe_state *state = (stream_jpxe_state *)ss;
751
/* close the library compression context */
752
err = JP2_Compress_End(state->handle);
753
if (err != cJP2_Error_OK) {
754
/* we can't return an error, so only print on debug builds */
755
if_debug1('w', "[w]jpxe Luratech JP2 error %d"
756
" closing compression context", (int)err);
759
/* free our own storage */
764
/* encoder stream template */
765
const stream_template s_jpxe_template = {
769
1024, 1024, /* min in and out buffer sizes */