1 //========================================================================
5 // A JPX stream decoder using OpenJPEG
7 // Copyright 2008-2010, 2012 Albert Astals Cid <aacid@kde.org>
8 // Copyright 2011 Daniel Glöckner <daniel-gl@gmx.net>
9 // Copyright 2014 Thomas Freitag <Thomas.Freitag@alfa.de>
10 // Copyright 2013, 2014 Adrian Johnson <ajohnson@redneon.com>
11 // Copyright 2015 Adam Reichold <adam.reichold@t-online.de>
12 // Copyright 2015 Jakub Wilk <jwilk@jwilk.net>
14 // Licensed under GPLv2 or later
16 //========================================================================
19 #include "JPEG2000Stream.h"
22 #define OPENJPEG_VERSION_ENCODE(major, minor, micro) ( \
28 #ifdef OPJ_VERSION_MAJOR
29 #define OPENJPEG_VERSION OPENJPEG_VERSION_ENCODE(OPJ_VERSION_MAJOR, OPJ_VERSION_MINOR, OPJ_VERSION_BUILD)
31 // OpenJPEG started providing version macros in version 2.1.
32 // If the version macro is not found, set the version to 2.0.0 and
33 // assume there will be no API changes in 2.0.x.
34 #define OPENJPEG_VERSION OPENJPEG_VERSION_ENCODE(2, 0, 0)
38 struct JPXStreamPrivate
{
47 void init2(unsigned char *buf
, int bufLen
, OPJ_CODEC_FORMAT format
);
50 void init2(OPJ_CODEC_FORMAT format
, unsigned char *data
, int length
);
54 static inline int doLookChar(JPXStreamPrivate
* priv
) {
55 if (unlikely(priv
->counter
>= priv
->npixels
))
58 return ((unsigned char *)priv
->image
->comps
[priv
->ccounter
].data
)[priv
->counter
];
61 static inline int doGetChar(JPXStreamPrivate
* priv
) {
62 const int result
= doLookChar(priv
);
63 if (++priv
->ccounter
== priv
->ncomps
) {
70 JPXStream::JPXStream(Stream
*strA
) : FilterStream(strA
) {
71 priv
= new JPXStreamPrivate
;
72 priv
->inited
= gFalse
;
81 JPXStream::~JPXStream() {
87 void JPXStream::reset() {
92 void JPXStream::close() {
93 if (priv
->image
!= NULL
) {
94 opj_image_destroy(priv
->image
);
100 if (priv
->dinfo
!= NULL
) {
101 opj_destroy_decompress(priv
->dinfo
);
107 Goffset
JPXStream::getPos() {
108 return priv
->counter
* priv
->ncomps
+ priv
->ccounter
;
111 int JPXStream::getChars(int nChars
, Guchar
*buffer
) {
112 if (unlikely(priv
->inited
== gFalse
)) { init(); }
114 for (int i
= 0; i
< nChars
; ++i
) {
115 const int c
= doGetChar(priv
);
116 if (likely(c
!= EOF
)) buffer
[i
] = c
;
122 int JPXStream::getChar() {
123 if (unlikely(priv
->inited
== gFalse
)) { init(); }
125 return doGetChar(priv
);
128 int JPXStream::lookChar() {
129 if (unlikely(priv
->inited
== gFalse
)) { init(); }
131 return doLookChar(priv
);
134 GooString
*JPXStream::getPSFilter(int psLevel
, const char *indent
) {
138 GBool
JPXStream::isBinary(GBool last
) {
139 return str
->isBinary(gTrue
);
142 void JPXStream::getImageParams(int *bitsPerComponent
, StreamColorSpaceMode
*csMode
) {
143 if (unlikely(priv
->inited
== gFalse
)) { init(); }
145 *bitsPerComponent
= 8;
146 if (priv
->image
&& priv
->image
->numcomps
== 3)
147 *csMode
= streamCSDeviceRGB
;
148 else if (priv
->image
&& priv
->image
->numcomps
== 4)
149 *csMode
= streamCSDeviceCMYK
;
151 *csMode
= streamCSDeviceGray
;
155 static void libopenjpeg_error_callback(const char *msg
, void * /*client_data*/) {
156 error(errSyntaxError
, -1, "{0:s}", msg
);
159 static void libopenjpeg_warning_callback(const char *msg
, void * /*client_data*/) {
160 error(errSyntaxWarning
, -1, "{0:s}", msg
);
165 #define BUFFER_INITIAL_SIZE 4096
167 void JPXStream::init()
170 if (getDict()) getDict()->lookup("Length", &oLen
);
172 int bufSize
= BUFFER_INITIAL_SIZE
;
173 if (oLen
.isInt()) bufSize
= oLen
.getInt();
177 unsigned char *buf
= str
->toUnsignedChars(&length
, bufSize
);
178 priv
->init2(buf
, length
, CODEC_JP2
);
182 priv
->npixels
= priv
->image
->comps
[0].w
* priv
->image
->comps
[0].h
;
183 priv
->ncomps
= priv
->image
->numcomps
;
184 for (int component
= 0; component
< priv
->ncomps
; component
++) {
185 if (priv
->image
->comps
[component
].data
== NULL
) {
189 unsigned char *cdata
= (unsigned char *)priv
->image
->comps
[component
].data
;
191 if (priv
->image
->comps
[component
].prec
> 8)
192 adjust
= priv
->image
->comps
[component
].prec
- 8;
194 if (priv
->image
->comps
[component
].sgnd
)
195 sgndcorr
= 1 << (priv
->image
->comps
[0].prec
- 1);
196 for (int i
= 0; i
< priv
->npixels
; i
++) {
197 int r
= priv
->image
->comps
[component
].data
[i
];
200 r
= (r
>> adjust
)+((r
>> (adjust
-1))%2);
201 if (unlikely(r
> 255))
212 priv
->inited
= gTrue
;
215 void JPXStreamPrivate::init2(unsigned char *buf
, int bufLen
, OPJ_CODEC_FORMAT format
)
217 opj_cio_t
*cio
= NULL
;
219 /* Use default decompression parameters */
220 opj_dparameters_t parameters
;
221 opj_set_default_decoder_parameters(¶meters
);
222 #ifdef WITH_OPENJPEG_IGNORE_PCLR_CMAP_CDEF_FLAG
223 parameters
.flags
= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG
;
226 /* Configure the event manager to receive errors and warnings */
227 opj_event_mgr_t event_mgr
;
228 memset(&event_mgr
, 0, sizeof(opj_event_mgr_t
));
229 event_mgr
.error_handler
= libopenjpeg_error_callback
;
230 event_mgr
.warning_handler
= libopenjpeg_warning_callback
;
232 /* Get the decoder handle of the format */
233 dinfo
= opj_create_decompress(format
);
234 if (dinfo
== NULL
) goto error
;
235 /* Catch events using our callbacks */
236 opj_set_event_mgr((opj_common_ptr
)dinfo
, &event_mgr
, NULL
);
238 /* Setup the decoder decoding parameters */
239 opj_setup_decoder(dinfo
, ¶meters
);
241 /* Open a byte stream */
242 cio
= opj_cio_open((opj_common_ptr
)dinfo
, buf
, bufLen
);
243 if (cio
== NULL
) goto error
;
245 /* Decode the stream and fill the image structure */
246 image
= opj_decode(dinfo
, cio
);
248 /* Close the byte stream */
251 if (image
== NULL
) goto error
;
255 if (format
== CODEC_JP2
) {
256 error(errSyntaxWarning
, -1, "Did not succeed opening JPX Stream as JP2, trying as J2K.");
257 init2(buf
, bufLen
, CODEC_J2K
);
258 } else if (format
== CODEC_J2K
) {
259 error(errSyntaxWarning
, -1, "Did not succeed opening JPX Stream as J2K, trying as JPT.");
260 init2(buf
, bufLen
, CODEC_JPT
);
262 error(errSyntaxError
, -1, "Did not succeed opening JPX Stream.");
269 typedef struct JPXData_s
276 #define BUFFER_INITIAL_SIZE 4096
278 static OPJ_SIZE_T
jpxRead_callback(void * p_buffer
, OPJ_SIZE_T p_nb_bytes
, void * p_user_data
)
280 JPXData
*jpxData
= (JPXData
*)p_user_data
;
283 len
= jpxData
->size
- jpxData
->pos
;
287 return (OPJ_SIZE_T
)-1; /* End of file! */
288 if ((OPJ_SIZE_T
)len
> p_nb_bytes
)
290 memcpy(p_buffer
, jpxData
->data
+ jpxData
->pos
, len
);
295 static OPJ_OFF_T
jpxSkip_callback(OPJ_OFF_T skip
, void * p_user_data
)
297 JPXData
*jpxData
= (JPXData
*)p_user_data
;
299 jpxData
->pos
+= (skip
> jpxData
->size
- jpxData
->pos
) ? jpxData
->size
- jpxData
->pos
: skip
;
300 /* Always return input value to avoid "Problem with skipping JPEG2000 box, stream error" */
304 static OPJ_BOOL
jpxSeek_callback(OPJ_OFF_T seek_pos
, void * p_user_data
)
306 JPXData
*jpxData
= (JPXData
*)p_user_data
;
308 if (seek_pos
> jpxData
->size
)
310 jpxData
->pos
= seek_pos
;
314 void JPXStream::init()
317 if (getDict()) getDict()->lookup("Length", &oLen
);
319 int bufSize
= BUFFER_INITIAL_SIZE
;
320 if (oLen
.isInt()) bufSize
= oLen
.getInt();
324 unsigned char *buf
= str
->toUnsignedChars(&length
, bufSize
);
325 priv
->init2(OPJ_CODEC_JP2
, buf
, length
);
329 priv
->npixels
= priv
->image
->comps
[0].w
* priv
->image
->comps
[0].h
;
330 priv
->ncomps
= priv
->image
->numcomps
;
331 for (int component
= 0; component
< priv
->ncomps
; component
++) {
332 if (priv
->image
->comps
[component
].data
== NULL
) {
336 unsigned char *cdata
= (unsigned char *)priv
->image
->comps
[component
].data
;
338 if (priv
->image
->comps
[component
].prec
> 8)
339 adjust
= priv
->image
->comps
[component
].prec
- 8;
341 if (priv
->image
->comps
[component
].sgnd
)
342 sgndcorr
= 1 << (priv
->image
->comps
[0].prec
- 1);
343 for (int i
= 0; i
< priv
->npixels
; i
++) {
344 int r
= priv
->image
->comps
[component
].data
[i
];
347 r
= (r
>> adjust
)+((r
>> (adjust
-1))%2);
348 if (unlikely(r
> 255))
360 priv
->inited
= gTrue
;
363 void JPXStreamPrivate::init2(OPJ_CODEC_FORMAT format
, unsigned char *buf
, int length
)
369 jpxData
.size
= length
;
371 opj_stream_t
*stream
;
373 stream
= opj_stream_default_create(OPJ_TRUE
);
375 #if OPENJPEG_VERSION >= OPENJPEG_VERSION_ENCODE(2, 1, 0)
376 opj_stream_set_user_data (stream
, &jpxData
, NULL
);
378 opj_stream_set_user_data (stream
, &jpxData
);
381 opj_stream_set_read_function(stream
, jpxRead_callback
);
382 opj_stream_set_skip_function(stream
, jpxSkip_callback
);
383 opj_stream_set_seek_function(stream
, jpxSeek_callback
);
384 /* Set the length to avoid an assert */
385 opj_stream_set_user_data_length(stream
, length
);
387 opj_codec_t
*decoder
;
389 /* Use default decompression parameters */
390 opj_dparameters_t parameters
;
391 opj_set_default_decoder_parameters(¶meters
);
392 parameters
.flags
|= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG
;
394 /* Get the decoder handle of the format */
395 decoder
= opj_create_decompress(format
);
396 if (decoder
== NULL
) {
397 error(errSyntaxWarning
, -1, "Unable to create decoder");
401 /* Catch events using our callbacks */
402 opj_set_warning_handler(decoder
, libopenjpeg_warning_callback
, NULL
);
403 opj_set_error_handler(decoder
, libopenjpeg_error_callback
, NULL
);
405 /* Setup the decoder decoding parameters */
406 if (!opj_setup_decoder(decoder
, ¶meters
)) {
407 error(errSyntaxWarning
, -1, "Unable to set decoder parameters");
411 /* Decode the stream and fill the image structure */
413 if (!opj_read_header(stream
, decoder
, &image
)) {
414 error(errSyntaxWarning
, -1, "Unable to read header");
418 /* Optional if you want decode the entire image */
419 if (!opj_set_decode_area(decoder
, image
, parameters
.DA_x0
,
420 parameters
.DA_y0
, parameters
.DA_x1
, parameters
.DA_y1
)){
421 error(errSyntaxWarning
, -1, "X2");
425 /* Get the decoded image */
426 if (!(opj_decode(decoder
, stream
, image
) && opj_end_decompress(decoder
, stream
))) {
427 error(errSyntaxWarning
, -1, "Unable to decode image");
431 opj_destroy_codec(decoder
);
432 opj_stream_destroy(stream
);
438 opj_destroy_codec(decoder
);
439 if (format
== OPJ_CODEC_JP2
) {
440 error(errSyntaxWarning
, -1, "Did no succeed opening JPX Stream as JP2, trying as J2K.");
441 init2(OPJ_CODEC_J2K
, buf
, length
);
442 } else if (format
== OPJ_CODEC_J2K
) {
443 error(errSyntaxWarning
, -1, "Did no succeed opening JPX Stream as J2K, trying as JPT.");
444 init2(OPJ_CODEC_JPT
, buf
, length
);
446 error(errSyntaxError
, -1, "Did no succeed opening JPX Stream.");