Coverage Report

Created: 2026-03-31 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/source/fitz/load-jpx.c
Line
Count
Source
1
// Copyright (C) 2004-2025 Artifex Software, Inc.
2
//
3
// This file is part of MuPDF.
4
//
5
// MuPDF is free software: you can redistribute it and/or modify it under the
6
// terms of the GNU Affero General Public License as published by the Free
7
// Software Foundation, either version 3 of the License, or (at your option)
8
// any later version.
9
//
10
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
// details.
14
//
15
// You should have received a copy of the GNU Affero General Public License
16
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17
//
18
// Alternative licensing terms are available from the licensor.
19
// For commercial licensing, see <https://www.artifex.com/> or contact
20
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21
// CA 94129, USA, for further information.
22
23
#include "mupdf/fitz.h"
24
25
#include "pixmap-imp.h"
26
27
#include <assert.h>
28
#include <string.h>
29
30
#if FZ_ENABLE_JPX
31
32
static void
33
jpx_ycc_to_rgb(fz_context *ctx, fz_pixmap *pix, int cbsign, int crsign)
34
0
{
35
0
  int w = pix->w;
36
0
  int h = pix->h;
37
0
  int stride = pix->stride;
38
0
  int x, y;
39
40
0
  for (y = 0; y < h; y++)
41
0
  {
42
0
    unsigned char * row = &pix->samples[stride * y];
43
0
    for (x = 0; x < w; x++)
44
0
    {
45
0
      int ycc[3];
46
0
      ycc[0] = row[x * 3 + 0];
47
0
      ycc[1] = row[x * 3 + 1];
48
0
      ycc[2] = row[x * 3 + 2];
49
50
      /* consciously skip Y */
51
0
      if (cbsign)
52
0
        ycc[1] -= 128;
53
0
      if (crsign)
54
0
        ycc[2] -= 128;
55
56
0
      row[x * 3 + 0] = fz_clampi(ycc[0] + 1.402f * ycc[2], 0, 255);
57
0
      row[x * 3 + 1] = fz_clampi(ycc[0] - 0.34413f * ycc[1] - 0.71414f * ycc[2], 0, 255);
58
0
      row[x * 3 + 2] = fz_clampi(ycc[0] + 1.772f * ycc[1], 0, 255);
59
0
    }
60
0
  }
61
0
}
62
63
#include <openjpeg.h>
64
65
typedef struct
66
{
67
  int width;
68
  int height;
69
  fz_colorspace *cs;
70
  int xres;
71
  int yres;
72
} fz_jpxd;
73
74
typedef struct
75
{
76
  const unsigned char *data;
77
  OPJ_SIZE_T size;
78
  OPJ_SIZE_T pos;
79
} stream_block;
80
81
/* OpenJPEG does not provide a safe mechanism to intercept
82
 * allocations. In the latest version all allocations go
83
 * though opj_malloc etc, but no context is passed around.
84
 *
85
 * In order to ensure that allocations throughout mupdf
86
 * are done consistently, we implement opj_malloc etc as
87
 * functions that call down to fz_malloc etc. These
88
 * require context variables, so we lock and unlock around
89
 * calls to openjpeg. Any attempt to call through
90
 * without setting these will be detected.
91
 *
92
 * It is therefore vital that any fz_lock/fz_unlock
93
 * handlers are shared between all the fz_contexts in
94
 * use at a time.
95
 */
96
97
/* Potentially we can write different versions
98
 * of get_context and set_context for different
99
 * threading systems.
100
 */
101
102
static fz_context *fz_opj_secret = NULL;
103
104
static void set_opj_context(fz_context *ctx)
105
8
{
106
8
  fz_opj_secret = ctx;
107
8
}
108
109
static fz_context *get_opj_context(void)
110
352
{
111
352
  return fz_opj_secret;
112
352
}
113
114
void fz_opj_lock(fz_context *ctx)
115
4
{
116
4
  fz_ft_lock(ctx);
117
118
4
  set_opj_context(ctx);
119
4
}
120
121
void fz_opj_unlock(fz_context *ctx)
122
4
{
123
4
  set_opj_context(NULL);
124
125
4
  fz_ft_unlock(ctx);
126
4
}
127
128
void *fz_opj_malloc(size_t size)
129
18
{
130
18
  fz_context *ctx = get_opj_context();
131
132
18
  assert(ctx != NULL);
133
134
18
  return Memento_label(fz_malloc_no_throw(ctx, size), "fz_opj_malloc");
135
18
}
136
137
void *fz_opj_calloc(size_t n, size_t size)
138
144
{
139
144
  fz_context *ctx = get_opj_context();
140
141
144
  assert(ctx != NULL);
142
143
144
  return fz_calloc_no_throw(ctx, n, size);
144
144
}
145
146
void *fz_opj_realloc(void *ptr, size_t size)
147
0
{
148
0
  fz_context *ctx = get_opj_context();
149
150
0
  assert(ctx != NULL);
151
152
0
  return fz_realloc_no_throw(ctx, ptr, size);
153
0
}
154
155
void fz_opj_free(void *ptr)
156
190
{
157
190
  fz_context *ctx = get_opj_context();
158
159
190
  assert(ctx != NULL);
160
161
190
  fz_free(ctx, ptr);
162
190
}
163
164
static void * fz_opj_aligned_malloc_n(size_t alignment, size_t size)
165
0
{
166
0
  uint8_t *ptr;
167
0
  size_t off;
168
169
0
  if (size == 0)
170
0
    return NULL;
171
172
0
  size += alignment + sizeof(uint8_t);
173
0
  ptr = fz_opj_malloc(size);
174
0
  if (ptr == NULL)
175
0
    return NULL;
176
0
  off = alignment-(((int)(intptr_t)ptr) & (alignment - 1));
177
0
  ptr[off-1] = (uint8_t)off;
178
0
  return ptr + off;
179
0
}
180
181
void * fz_opj_aligned_malloc(size_t size)
182
0
{
183
0
  return fz_opj_aligned_malloc_n(16, size);
184
0
}
185
186
void * fz_opj_aligned_32_malloc(size_t size)
187
0
{
188
0
  return fz_opj_aligned_malloc_n(32, size);
189
0
}
190
191
void fz_opj_aligned_free(void* ptr_)
192
12
{
193
12
  uint8_t *ptr = (uint8_t *)ptr_;
194
12
  uint8_t off;
195
12
  if (ptr == NULL)
196
12
    return;
197
198
0
  off = ptr[-1];
199
0
  fz_opj_free((void *)(((unsigned char *)ptr) - off));
200
0
}
201
202
#if 0
203
/* UNUSED currently, and moderately tricky, so deferred until required */
204
void * fz_opj_aligned_realloc(void *ptr, size_t size)
205
{
206
  return fz_opj_realloc(ptr, size);
207
}
208
#endif
209
210
static void fz_opj_error_callback(const char *msg, void *client_data)
211
4
{
212
4
  fz_context *ctx = (fz_context *)client_data;
213
  // strlen-1 to trim trailing newline
214
4
  fz_warn(ctx, "openjpeg error: %.*s", (int) strlen(msg)-1, msg);
215
4
}
216
217
static void fz_opj_warning_callback(const char *msg, void *client_data)
218
0
{
219
0
  fz_context *ctx = (fz_context *)client_data;
220
  // strlen-1 to trim trailing newline
221
0
  fz_warn(ctx, "openjpeg warning: %.*s", (int) strlen(msg)-1, msg);
222
0
}
223
224
static void fz_opj_info_callback(const char *msg, void *client_data)
225
8
{
226
#if 0
227
  fz_context *ctx = (fz_context *)client_data;
228
  // strlen-1 to trim trailing newline
229
  fz_warn(ctx, "openjpeg info: %.*s", (int) strlen(msg)-1, msg);
230
#endif
231
8
}
232
233
static OPJ_SIZE_T fz_opj_stream_read(void * p_buffer, OPJ_SIZE_T p_nb_bytes, void * p_user_data)
234
4
{
235
4
  stream_block *sb = (stream_block *)p_user_data;
236
4
  OPJ_SIZE_T len;
237
238
4
  len = sb->size - sb->pos;
239
4
  if (len == 0)
240
0
    return (OPJ_SIZE_T)-1; /* End of file! */
241
4
  if (len > p_nb_bytes)
242
0
    len = p_nb_bytes;
243
4
  memcpy(p_buffer, sb->data + sb->pos, len);
244
4
  sb->pos += len;
245
4
  return len;
246
4
}
247
248
static OPJ_OFF_T fz_opj_stream_skip(OPJ_OFF_T skip, void * p_user_data)
249
0
{
250
0
  stream_block *sb = (stream_block *)p_user_data;
251
252
0
  if (skip > (OPJ_OFF_T)(sb->size - sb->pos))
253
0
    skip = (OPJ_OFF_T)(sb->size - sb->pos);
254
0
  sb->pos += skip;
255
0
  return sb->pos;
256
0
}
257
258
static OPJ_BOOL fz_opj_stream_seek(OPJ_OFF_T seek_pos, void * p_user_data)
259
0
{
260
0
  stream_block *sb = (stream_block *)p_user_data;
261
262
0
  if (seek_pos > (OPJ_OFF_T)sb->size)
263
0
    return OPJ_FALSE;
264
0
  sb->pos = seek_pos;
265
0
  return OPJ_TRUE;
266
0
}
267
268
static int32_t
269
safe_mul32(fz_context *ctx, int32_t a, int32_t b)
270
0
{
271
0
  int64_t res = ((int64_t)a) * b;
272
0
  int32_t res32 = (int32_t)res;
273
274
0
  if ((res32) != res)
275
0
    fz_throw(ctx, FZ_ERROR_LIMIT, "Overflow while decoding jpx");
276
0
  return res32;
277
0
}
278
279
static int32_t
280
safe_mla32(fz_context *ctx, int32_t a, int32_t b, int32_t c)
281
0
{
282
0
  int64_t res = ((int64_t)a) * b + c;
283
0
  int32_t res32 = (int32_t)res;
284
285
0
  if ((res32) != res)
286
0
    fz_throw(ctx, FZ_ERROR_LIMIT, "Overflow while decoding jpx");
287
0
  return res32;
288
0
}
289
290
static inline void
291
template_copy_comp(unsigned char *dst0, int w, int h, int stride, const OPJ_INT32 *src, int32_t ox, int32_t oy, OPJ_UINT32 cdx, OPJ_UINT32 cdy, OPJ_UINT32 cw, OPJ_UINT32 ch, OPJ_UINT32 sgnd, OPJ_UINT32 prec, int comps)
292
0
{
293
0
  int x, y;
294
295
0
  for (y = ch; oy + cdy <= 0 && y > 0; y--)
296
0
  {
297
0
    oy += cdy;
298
0
    dst0 += cdy * stride;
299
0
    src += cw;
300
0
  }
301
0
  for (; y > 0; y--)
302
0
  {
303
0
    int32_t dymin = oy;
304
0
    int32_t dywid = cdy;
305
0
    unsigned char *dst1 = dst0 + ox * comps;
306
0
    int32_t oox = ox;
307
0
    const OPJ_INT32 *src0 = src;
308
309
0
    if (dymin < 0)
310
0
      dywid += dymin, dst1 -= dymin * stride, dymin = 0;
311
0
    if (dymin >= h)
312
0
      break;
313
0
    if (dymin + dywid > h)
314
0
      dywid = h - dymin;
315
316
0
    for (x = cw; oox + cdx <= 0 && x > 0; x--)
317
0
    {
318
0
      oox += cdx;
319
0
      dst1 += cdx * comps;
320
0
      src0++;
321
0
    }
322
0
    for (; x > 0; x--)
323
0
    {
324
0
      OPJ_INT32 v;
325
0
      int32_t xx, yy;
326
0
      int32_t dxmin = oox;
327
0
      int32_t dxwid = cdx;
328
0
      unsigned char *dst2;
329
330
0
      v = *src0++;
331
332
0
      if (sgnd)
333
0
        v = v + (1 << (prec - 1));
334
0
      if (prec > 8)
335
0
        v = v >> (prec - 8);
336
0
      else if (prec < 8)
337
0
        v = v << (8 - prec);
338
339
0
      if (dxmin < 0)
340
0
        dxwid += dxmin, dst1 -= dxmin * comps, dxmin = 0;
341
0
      if (dxmin >= w)
342
0
        break;
343
0
      if (dxmin + dxwid > w)
344
0
        dxwid = w - dxmin;
345
346
0
      dst2 = dst1;
347
0
      for (yy = dywid; yy > 0; yy--)
348
0
      {
349
0
        unsigned char *dst3 = dst2;
350
0
        for (xx = dxwid; xx > 0; xx--)
351
0
        {
352
0
          *dst3 = v;
353
0
          dst3 += comps;
354
0
        }
355
0
        dst2 += stride;
356
0
      }
357
0
      dst1 += comps * cdx;
358
0
      oox += cdx;
359
0
    }
360
0
    dst0 += stride * cdy;
361
0
    src += cw;
362
0
    oy += cdy;
363
0
  }
364
0
}
365
366
static void
367
copy_jpx_to_pixmap(fz_context *ctx, fz_pixmap *img, opj_image_t *jpx)
368
0
{
369
0
  unsigned char *dst;
370
0
  int stride, comps;
371
0
  int w = img->w;
372
0
  int h = img->h;
373
0
  int k;
374
375
0
  stride = fz_pixmap_stride(ctx, img);
376
0
  comps = fz_pixmap_components(ctx, img);
377
0
  dst = fz_pixmap_samples(ctx, img);
378
379
0
  for (k = 0; k < comps; k++)
380
0
  {
381
0
    opj_image_comp_t *comp = &(jpx->comps[k]);
382
0
    OPJ_UINT32 cdx = comp->dx;
383
0
    OPJ_UINT32 cdy = comp->dy;
384
0
    OPJ_UINT32 cw = comp->w;
385
0
    OPJ_UINT32 ch = comp->h;
386
0
    int32_t oy = safe_mul32(ctx, comp->y0, cdy) - jpx->y0;
387
0
    int32_t ox = safe_mul32(ctx, comp->x0, cdx) - jpx->x0;
388
0
    unsigned char *dst0 = dst + oy * stride;
389
0
    int prec = comp->prec;
390
0
    int sgnd = comp->sgnd;
391
392
0
    if (comp->data == NULL)
393
0
      fz_throw(ctx, FZ_ERROR_FORMAT, "No data for JP2 image component %d", k);
394
395
0
    if (fz_colorspace_is_indexed(ctx, img->colorspace))
396
0
    {
397
0
      prec = 8; /* Don't do any scaling! */
398
0
      sgnd = 0;
399
0
    }
400
401
    /* Check that none of the following will overflow. */
402
0
    (void)safe_mla32(ctx, ch, cdy, oy);
403
0
    (void)safe_mla32(ctx, cw, cdx, ox);
404
405
0
    if (cdx == 1 && cdy == 1)
406
0
      template_copy_comp(dst0, w, h, stride, comp->data, ox, oy, 1 /*cdx*/, 1 /*cdy*/, cw, ch, sgnd, prec, comps);
407
0
    else
408
0
      template_copy_comp(dst0, w, h, stride, comp->data, ox, oy, cdx, cdy, cw, ch, sgnd, prec, comps);
409
0
    dst++;
410
0
  }
411
0
}
412
413
static fz_pixmap *
414
jpx_read_image(fz_context *ctx, fz_jpxd *state, const unsigned char *data, size_t size, fz_colorspace *defcs, int onlymeta)
415
4
{
416
4
  fz_pixmap *img = NULL;
417
4
  opj_dparameters_t params;
418
4
  opj_codec_t *codec;
419
4
  opj_image_t *jpx;
420
4
  opj_stream_t *stream;
421
4
  OPJ_CODEC_FORMAT format;
422
4
  int a, n, k, numcomps;
423
4
  int w, h;
424
4
  stream_block sb;
425
426
4
  fz_var(img);
427
428
4
  if (size < 2)
429
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "not enough data to determine image format");
430
431
  /* Check for SOC marker -- if found we have a bare J2K stream */
432
4
  if (data[0] == 0xFF && data[1] == 0x4F)
433
0
    format = OPJ_CODEC_J2K;
434
4
  else
435
4
    format = OPJ_CODEC_JP2;
436
437
4
  opj_set_default_decoder_parameters(&params);
438
4
  if (fz_colorspace_is_indexed(ctx, defcs))
439
0
    params.flags |= OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG;
440
441
4
  codec = opj_create_decompress(format);
442
4
  opj_set_info_handler(codec, fz_opj_info_callback, ctx);
443
4
  opj_set_warning_handler(codec, fz_opj_warning_callback, ctx);
444
4
  opj_set_error_handler(codec, fz_opj_error_callback, ctx);
445
4
  if (!opj_setup_decoder(codec, &params))
446
0
  {
447
0
    opj_destroy_codec(codec);
448
0
    fz_throw(ctx, FZ_ERROR_LIBRARY, "j2k decode failed");
449
0
  }
450
451
4
  stream = opj_stream_default_create(OPJ_TRUE);
452
4
  sb.data = data;
453
4
  sb.pos = 0;
454
4
  sb.size = size;
455
456
4
  opj_stream_set_read_function(stream, fz_opj_stream_read);
457
4
  opj_stream_set_skip_function(stream, fz_opj_stream_skip);
458
4
  opj_stream_set_seek_function(stream, fz_opj_stream_seek);
459
4
  opj_stream_set_user_data(stream, &sb, NULL);
460
  /* Set the length to avoid an assert */
461
4
  opj_stream_set_user_data_length(stream, size);
462
463
4
  if (!opj_read_header(stream, codec, &jpx))
464
0
  {
465
0
    opj_stream_destroy(stream);
466
0
    opj_destroy_codec(codec);
467
0
    fz_throw(ctx, FZ_ERROR_LIBRARY, "Failed to read JPX header");
468
0
  }
469
470
4
  if (!onlymeta)
471
2
  {
472
2
    if (!opj_decode(codec, stream, jpx))
473
2
    {
474
2
      opj_stream_destroy(stream);
475
2
      opj_destroy_codec(codec);
476
2
      opj_image_destroy(jpx);
477
2
      fz_throw(ctx, FZ_ERROR_LIBRARY, "Failed to decode JPX image");
478
2
    }
479
2
  }
480
481
2
  opj_stream_destroy(stream);
482
2
  opj_destroy_codec(codec);
483
484
  /* jpx should never be NULL here, but check anyway */
485
2
  if (!jpx)
486
0
    fz_throw(ctx, FZ_ERROR_LIBRARY, "opj_decode failed");
487
488
2
  numcomps = (int)jpx->numcomps;
489
490
  /* Count number of alpha and color channels */
491
2
  switch (jpx->color_space)
492
2
  {
493
0
  default:
494
0
  case OPJ_CLRSPC_UNKNOWN:
495
0
  case OPJ_CLRSPC_UNSPECIFIED:
496
0
    if (defcs && defcs->n <= numcomps)
497
0
    {
498
0
      n = defcs->n;
499
0
    }
500
0
    else
501
0
    {
502
0
      if (numcomps >= 3)
503
0
      {
504
0
        fz_warn(ctx, "unknown JPX colorspace; assuming RGB");
505
0
        n = 3;
506
0
      }
507
0
      else
508
0
      {
509
0
        fz_warn(ctx, "unknown JPX colorspace; assuming Gray");
510
0
        n = 1;
511
0
      }
512
0
    }
513
0
    break;
514
2
  case OPJ_CLRSPC_SRGB:
515
2
  case OPJ_CLRSPC_SYCC:
516
2
  case OPJ_CLRSPC_EYCC:
517
2
    n = 3;
518
2
    break;
519
0
  case OPJ_CLRSPC_GRAY:
520
0
    n = 1;
521
0
    break;
522
0
  case OPJ_CLRSPC_CMYK:
523
0
    n = 4;
524
0
    break;
525
526
2
  }
527
528
2
  if (n > numcomps)
529
0
  {
530
0
    fz_warn(ctx, "JPX numcomps (%d) doesn't match color_space (%d)", numcomps, n);
531
0
    n = numcomps;
532
0
  }
533
2
  a = numcomps - n;
534
535
2
  if (!onlymeta)
536
0
  {
537
0
    for (k = 0; k < numcomps; ++k)
538
0
    {
539
0
      if (!jpx->comps[k].data)
540
0
      {
541
0
        opj_image_destroy(jpx);
542
0
        fz_throw(ctx, FZ_ERROR_FORMAT, "image components are missing data");
543
0
      }
544
0
    }
545
0
  }
546
547
2
  w = state->width = jpx->x1 - jpx->x0;
548
2
  h = state->height = jpx->y1 - jpx->y0;
549
2
  state->xres = 72; /* openjpeg does not read the JPEG 2000 resc box */
550
2
  state->yres = 72; /* openjpeg does not read the JPEG 2000 resc box */
551
552
2
  if (w < 0 || h < 0)
553
0
  {
554
0
    opj_image_destroy(jpx);
555
0
    fz_throw(ctx, FZ_ERROR_LIMIT, "Unbelievable size for jpx");
556
0
  }
557
558
2
  state->cs = NULL;
559
560
2
  if (defcs)
561
0
  {
562
0
    if (defcs->n == n)
563
0
      state->cs = fz_keep_colorspace(ctx, defcs);
564
0
    else
565
0
      fz_warn(ctx, "jpx file and dict colorspace do not match");
566
0
  }
567
568
2
#if FZ_ENABLE_ICC
569
2
  if (!state->cs && jpx->icc_profile_buf && jpx->icc_profile_len > 0)
570
0
  {
571
0
    fz_buffer *cbuf = NULL;
572
0
    fz_var(cbuf);
573
574
0
    fz_try(ctx)
575
0
    {
576
0
      cbuf = fz_new_buffer_from_copied_data(ctx, jpx->icc_profile_buf, jpx->icc_profile_len);
577
0
      state->cs = fz_new_icc_colorspace(ctx, FZ_COLORSPACE_NONE, 0, NULL, cbuf);
578
0
    }
579
0
    fz_always(ctx)
580
0
      fz_drop_buffer(ctx, cbuf);
581
0
    fz_catch(ctx)
582
0
    {
583
0
      fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
584
0
      fz_report_error(ctx);
585
0
      fz_warn(ctx, "ignoring embedded ICC profile in JPX");
586
0
    }
587
588
0
    if (state->cs && state->cs->n != n)
589
0
    {
590
0
      fz_warn(ctx, "invalid number of components in ICC profile, ignoring ICC profile in JPX");
591
0
      fz_drop_colorspace(ctx, state->cs);
592
0
      state->cs = NULL;
593
0
    }
594
0
  }
595
2
#endif
596
597
2
  if (!state->cs)
598
2
  {
599
2
    switch (n)
600
2
    {
601
0
    case 1: state->cs = fz_keep_colorspace(ctx, fz_device_gray(ctx)); break;
602
2
    case 3: state->cs = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); break;
603
0
    case 4: state->cs = fz_keep_colorspace(ctx, fz_device_cmyk(ctx)); break;
604
0
    default:
605
0
      {
606
0
        opj_image_destroy(jpx);
607
0
        fz_throw(ctx, FZ_ERROR_FORMAT, "unsupported number of components: %d", n);
608
0
      }
609
2
    }
610
2
  }
611
612
2
  if (onlymeta)
613
2
  {
614
2
    opj_image_destroy(jpx);
615
2
    return NULL;
616
2
  }
617
618
0
  fz_try(ctx)
619
0
  {
620
0
    a = !!a; /* ignore any superfluous alpha channels */
621
0
    img = fz_new_pixmap(ctx, state->cs, w, h, NULL, a);
622
0
    fz_clear_pixmap_with_value(ctx, img, 0);
623
0
    copy_jpx_to_pixmap(ctx, img, jpx);
624
625
0
    if (jpx->color_space == OPJ_CLRSPC_SYCC && n == 3 && a == 0)
626
0
      jpx_ycc_to_rgb(ctx, img, 1, 1);
627
0
    if (a)
628
0
      fz_premultiply_pixmap(ctx, img);
629
0
  }
630
0
  fz_always(ctx)
631
0
  {
632
0
    fz_drop_colorspace(ctx, state->cs);
633
0
    opj_image_destroy(jpx);
634
0
  }
635
0
  fz_catch(ctx)
636
0
  {
637
0
    fz_drop_pixmap(ctx, img);
638
0
    fz_rethrow(ctx);
639
0
  }
640
641
0
  return img;
642
0
}
643
644
fz_pixmap *
645
fz_load_jpx(fz_context *ctx, const unsigned char *data, size_t size, fz_colorspace *defcs)
646
2
{
647
2
  fz_jpxd state = { 0 };
648
2
  fz_pixmap *pix = NULL;
649
650
4
  fz_try(ctx)
651
4
  {
652
2
    fz_opj_lock(ctx);
653
2
    pix = jpx_read_image(ctx, &state, data, size, defcs, 0);
654
2
  }
655
4
  fz_always(ctx)
656
2
    fz_opj_unlock(ctx);
657
2
  fz_catch(ctx)
658
2
    fz_rethrow(ctx);
659
660
0
  return pix;
661
2
}
662
663
void
664
fz_load_jpx_info(fz_context *ctx, const unsigned char *data, size_t size, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep, fz_colorspace *defcs)
665
2
{
666
2
  fz_jpxd state = { 0 };
667
668
4
  fz_try(ctx)
669
4
  {
670
2
    fz_opj_lock(ctx);
671
2
    jpx_read_image(ctx, &state, data, size, defcs, 1);
672
2
  }
673
4
  fz_always(ctx)
674
2
    fz_opj_unlock(ctx);
675
2
  fz_catch(ctx)
676
0
    fz_rethrow(ctx);
677
678
2
  *cspacep = state.cs;
679
2
  *wp = state.width;
680
2
  *hp = state.height;
681
2
  *xresp = state.xres;
682
2
  *yresp = state.yres;
683
2
}
684
685
#else /* FZ_ENABLE_JPX */
686
687
fz_pixmap *
688
fz_load_jpx(fz_context *ctx, const unsigned char *data, size_t size, fz_colorspace *defcs)
689
{
690
  fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "JPX support disabled");
691
}
692
693
void
694
fz_load_jpx_info(fz_context *ctx, const unsigned char *data, size_t size, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep)
695
{
696
  fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "JPX support disabled");
697
}
698
699
#endif