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>
13 // Licensed under GPLv2 or later
15 //========================================================================
18 #include "JPEG2000Stream.h"
21 #define OPENJPEG_VERSION_ENCODE(major, minor, micro) ( \
27 #ifdef OPJ_VERSION_MAJOR
28 #define OPENJPEG_VERSION OPENJPEG_VERSION_ENCODE(OPJ_VERSION_MAJOR, OPJ_VERSION_MINOR, OPJ_VERSION_BUILD)
30 // OpenJPEG started providing version macros in version 2.1.
31 // If the version macro is not found, set the version to 2.0.0 and
32 // assume there will be no API changes in 2.0.x.
33 #define OPENJPEG_VERSION OPENJPEG_VERSION_ENCODE(2, 0, 0)
37 struct JPXStreamPrivate
{
46 void init2(unsigned char *buf
, int bufLen
, OPJ_CODEC_FORMAT format
);
49 void init2(OPJ_CODEC_FORMAT format
, unsigned char *data
, int length
);
53 static inline int doLookChar(JPXStreamPrivate
* priv
) {
54 if (unlikely(priv
->counter
>= priv
->npixels
))
57 return ((unsigned char *)priv
->image
->comps
[priv
->ccounter
].data
)[priv
->counter
];
60 static inline int doGetChar(JPXStreamPrivate
* priv
) {
61 const int result
= doLookChar(priv
);
62 if (++priv
->ccounter
== priv
->ncomps
) {
69 JPXStream::JPXStream(Stream
*strA
) : FilterStream(strA
) {
70 priv
= new JPXStreamPrivate
;
71 priv
->inited
= gFalse
;
80 JPXStream::~JPXStream() {
86 void JPXStream::reset() {
91 void JPXStream::close() {
92 if (priv
->image
!= NULL
) {
93 opj_image_destroy(priv
->image
);
99 if (priv
->dinfo
!= NULL
) {
100 opj_destroy_decompress(priv
->dinfo
);
106 Goffset
JPXStream::getPos() {
107 return priv
->counter
* priv
->ncomps
+ priv
->ccounter
;
110 int JPXStream::getChars(int nChars
, Guchar
*buffer
) {
111 if (unlikely(priv
->inited
== gFalse
)) { init(); }
113 for (int i
= 0; i
< nChars
; ++i
) {
114 const int c
= doGetChar(priv
);
115 if (likely(c
!= EOF
)) buffer
[i
] = c
;
121 int JPXStream::getChar() {
122 if (unlikely(priv
->inited
== gFalse
)) { init(); }
124 return doGetChar(priv
);
127 int JPXStream::lookChar() {
128 if (unlikely(priv
->inited
== gFalse
)) { init(); }
130 return doLookChar(priv
);
133 GooString
*JPXStream::getPSFilter(int psLevel
, const char *indent
) {
137 GBool
JPXStream::isBinary(GBool last
) {
138 return str
->isBinary(gTrue
);
141 void JPXStream::getImageParams(int *bitsPerComponent
, StreamColorSpaceMode
*csMode
) {
142 if (unlikely(priv
->inited
== gFalse
)) { init(); }
144 *bitsPerComponent
= 8;
145 if (priv
->image
&& priv
->image
->numcomps
== 3)
146 *csMode
= streamCSDeviceRGB
;
147 else if (priv
->image
&& priv
->image
->numcomps
== 4)
148 *csMode
= streamCSDeviceCMYK
;
150 *csMode
= streamCSDeviceGray
;
154 static void libopenjpeg_error_callback(const char *msg
, void * /*client_data*/) {
155 error(errSyntaxError
, -1, "{0:s}", msg
);
158 static void libopenjpeg_warning_callback(const char *msg
, void * /*client_data*/) {
159 error(errSyntaxWarning
, -1, "{0:s}", msg
);
164 #define BUFFER_INITIAL_SIZE 4096
166 void JPXStream::init()
169 if (getDict()) getDict()->lookup("Length", &oLen
);
171 int bufSize
= BUFFER_INITIAL_SIZE
;
172 if (oLen
.isInt()) bufSize
= oLen
.getInt();
176 unsigned char *buf
= str
->toUnsignedChars(&length
, bufSize
);
177 priv
->init2(buf
, length
, CODEC_JP2
);
181 priv
->npixels
= priv
->image
->comps
[0].w
* priv
->image
->comps
[0].h
;
182 priv
->ncomps
= priv
->image
->numcomps
;
183 for (int component
= 0; component
< priv
->ncomps
; component
++) {
184 if (priv
->image
->comps
[component
].data
== NULL
) {
188 unsigned char *cdata
= (unsigned char *)priv
->image
->comps
[component
].data
;
190 if (priv
->image
->comps
[component
].prec
> 8)
191 adjust
= priv
->image
->comps
[component
].prec
- 8;
193 if (priv
->image
->comps
[component
].sgnd
)
194 sgndcorr
= 1 << (priv
->image
->comps
[0].prec
- 1);
195 for (int i
= 0; i
< priv
->npixels
; i
++) {
196 int r
= priv
->image
->comps
[component
].data
[i
];
199 r
= (r
>> adjust
)+((r
>> (adjust
-1))%2);
200 if (unlikely(r
> 255))
211 priv
->inited
= gTrue
;
214 void JPXStreamPrivate::init2(unsigned char *buf
, int bufLen
, OPJ_CODEC_FORMAT format
)
216 opj_cio_t
*cio
= NULL
;
218 /* Use default decompression parameters */
219 opj_dparameters_t parameters
;
220 opj_set_default_decoder_parameters(¶meters
);
221 #ifdef WITH_OPENJPEG_IGNORE_PCLR_CMAP_CDEF_FLAG
222 parameters
.flags
= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG
;
225 /* Configure the event manager to receive errors and warnings */
226 opj_event_mgr_t event_mgr
;
227 memset(&event_mgr
, 0, sizeof(opj_event_mgr_t
));
228 event_mgr
.error_handler
= libopenjpeg_error_callback
;
229 event_mgr
.warning_handler
= libopenjpeg_warning_callback
;
231 /* Get the decoder handle of the format */
232 dinfo
= opj_create_decompress(format
);
233 if (dinfo
== NULL
) goto error
;
234 /* Catch events using our callbacks */
235 opj_set_event_mgr((opj_common_ptr
)dinfo
, &event_mgr
, NULL
);
237 /* Setup the decoder decoding parameters */
238 opj_setup_decoder(dinfo
, ¶meters
);
240 /* Open a byte stream */
241 cio
= opj_cio_open((opj_common_ptr
)dinfo
, buf
, bufLen
);
242 if (cio
== NULL
) goto error
;
244 /* Decode the stream and fill the image structure */
245 image
= opj_decode(dinfo
, cio
);
247 /* Close the byte stream */
250 if (image
== NULL
) goto error
;
254 if (format
== CODEC_JP2
) {
255 error(errSyntaxWarning
, -1, "Did no succeed opening JPX Stream as JP2, trying as J2K.");
256 init2(buf
, bufLen
, CODEC_J2K
);
257 } else if (format
== CODEC_J2K
) {
258 error(errSyntaxWarning
, -1, "Did no succeed opening JPX Stream as J2K, trying as JPT.");
259 init2(buf
, bufLen
, CODEC_JPT
);
261 error(errSyntaxError
, -1, "Did no succeed opening JPX Stream.");
268 typedef struct JPXData_s
275 #define BUFFER_INITIAL_SIZE 4096
277 static OPJ_SIZE_T
jpxRead_callback(void * p_buffer
, OPJ_SIZE_T p_nb_bytes
, void * p_user_data
)
279 JPXData
*jpxData
= (JPXData
*)p_user_data
;
282 len
= jpxData
->size
- jpxData
->pos
;
286 return (OPJ_SIZE_T
)-1; /* End of file! */
287 if ((OPJ_SIZE_T
)len
> p_nb_bytes
)
289 memcpy(p_buffer
, jpxData
->data
+ jpxData
->pos
, len
);
294 static OPJ_OFF_T
jpxSkip_callback(OPJ_OFF_T skip
, void * p_user_data
)
296 JPXData
*jpxData
= (JPXData
*)p_user_data
;
298 jpxData
->pos
+= (skip
> jpxData
->size
- jpxData
->pos
) ? jpxData
->size
- jpxData
->pos
: skip
;
299 /* Always return input value to avoid "Problem with skipping JPEG2000 box, stream error" */
303 static OPJ_BOOL
jpxSeek_callback(OPJ_OFF_T seek_pos
, void * p_user_data
)
305 JPXData
*jpxData
= (JPXData
*)p_user_data
;
307 if (seek_pos
> jpxData
->size
)
309 jpxData
->pos
= seek_pos
;
313 void JPXStream::init()
316 if (getDict()) getDict()->lookup("Length", &oLen
);
318 int bufSize
= BUFFER_INITIAL_SIZE
;
319 if (oLen
.isInt()) bufSize
= oLen
.getInt();
323 unsigned char *buf
= str
->toUnsignedChars(&length
, bufSize
);
324 priv
->init2(OPJ_CODEC_JP2
, buf
, length
);
328 priv
->npixels
= priv
->image
->comps
[0].w
* priv
->image
->comps
[0].h
;
329 priv
->ncomps
= priv
->image
->numcomps
;
330 for (int component
= 0; component
< priv
->ncomps
; component
++) {
331 if (priv
->image
->comps
[component
].data
== NULL
) {
335 unsigned char *cdata
= (unsigned char *)priv
->image
->comps
[component
].data
;
337 if (priv
->image
->comps
[component
].prec
> 8)
338 adjust
= priv
->image
->comps
[component
].prec
- 8;
340 if (priv
->image
->comps
[component
].sgnd
)
341 sgndcorr
= 1 << (priv
->image
->comps
[0].prec
- 1);
342 for (int i
= 0; i
< priv
->npixels
; i
++) {
343 int r
= priv
->image
->comps
[component
].data
[i
];
346 r
= (r
>> adjust
)+((r
>> (adjust
-1))%2);
347 if (unlikely(r
> 255))
359 priv
->inited
= gTrue
;
362 void JPXStreamPrivate::init2(OPJ_CODEC_FORMAT format
, unsigned char *buf
, int length
)
368 jpxData
.size
= length
;
370 opj_stream_t
*stream
;
372 stream
= opj_stream_default_create(OPJ_TRUE
);
374 #if OPENJPEG_VERSION >= OPENJPEG_VERSION_ENCODE(2, 1, 0)
375 opj_stream_set_user_data (stream
, &jpxData
, NULL
);
377 opj_stream_set_user_data (stream
, &jpxData
);
380 opj_stream_set_read_function(stream
, jpxRead_callback
);
381 opj_stream_set_skip_function(stream
, jpxSkip_callback
);
382 opj_stream_set_seek_function(stream
, jpxSeek_callback
);
383 /* Set the length to avoid an assert */
384 opj_stream_set_user_data_length(stream
, length
);
386 opj_codec_t
*decoder
;
388 /* Use default decompression parameters */
389 opj_dparameters_t parameters
;
390 opj_set_default_decoder_parameters(¶meters
);
391 parameters
.flags
|= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG
;
393 /* Get the decoder handle of the format */
394 decoder
= opj_create_decompress(format
);
395 if (decoder
== NULL
) {
396 error(errSyntaxWarning
, -1, "Unable to create decoder");
400 /* Catch events using our callbacks */
401 opj_set_warning_handler(decoder
, libopenjpeg_warning_callback
, NULL
);
402 opj_set_error_handler(decoder
, libopenjpeg_error_callback
, NULL
);
404 /* Setup the decoder decoding parameters */
405 if (!opj_setup_decoder(decoder
, ¶meters
)) {
406 error(errSyntaxWarning
, -1, "Unable to set decoder parameters");
410 /* Decode the stream and fill the image structure */
412 if (!opj_read_header(stream
, decoder
, &image
)) {
413 error(errSyntaxWarning
, -1, "Unable to read header");
417 /* Optional if you want decode the entire image */
418 if (!opj_set_decode_area(decoder
, image
, parameters
.DA_x0
,
419 parameters
.DA_y0
, parameters
.DA_x1
, parameters
.DA_y1
)){
420 error(errSyntaxWarning
, -1, "X2");
424 /* Get the decoded image */
425 if (!(opj_decode(decoder
, stream
, image
) && opj_end_decompress(decoder
, stream
))) {
426 error(errSyntaxWarning
, -1, "Unable to decode image");
430 opj_destroy_codec(decoder
);
431 opj_stream_destroy(stream
);
437 opj_destroy_codec(decoder
);
438 if (format
== OPJ_CODEC_JP2
) {
439 error(errSyntaxWarning
, -1, "Did no succeed opening JPX Stream as JP2, trying as J2K.");
440 init2(OPJ_CODEC_J2K
, buf
, length
);
441 } else if (format
== OPJ_CODEC_J2K
) {
442 error(errSyntaxWarning
, -1, "Did no succeed opening JPX Stream as J2K, trying as JPT.");
443 init2(OPJ_CODEC_JPT
, buf
, length
);
445 error(errSyntaxError
, -1, "Did no succeed opening JPX Stream.");