Coverage Report

Created: 2026-09-14 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/plugins/decoder_openjpeg.cc
Line
Count
Source
1
/*
2
 * OpenJPEG codec.
3
 * Copyright (c) 2023 Devon Sookhoo
4
 * Copyright (c) 2023 Dirk Farin <dirk.farin@gmail.com>
5
 *
6
 * This file is part of libheif.
7
 *
8
 * libheif is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU Lesser General Public License as
10
 * published by the Free Software Foundation, either version 3 of
11
 * the License, or (at your option) any later version.
12
 *
13
 * libheif is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public License
19
 * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
20
 */
21
22
#include "libheif/heif.h"
23
#include "libheif/heif_plugin.h"
24
#include "decoder_openjpeg.h"
25
#include "common_utils.h"
26
#include <openjpeg.h>
27
#include <cstring>
28
29
#include <vector>
30
#include <cassert>
31
#include <memory>
32
#include <string>
33
34
static const int OPENJPEG_PLUGIN_PRIORITY = 100;
35
static const int OPENJPEG_PLUGIN_PRIORITY_HTJ2K = 90;
36
37
struct openjpeg_decoder
38
{
39
  std::vector<uint8_t> encoded_data;
40
  uintptr_t user_data;
41
42
  size_t read_position = 0;
43
  std::string error_message;
44
};
45
46
47
18
#define MAX_PLUGIN_NAME_LENGTH 80
48
static char plugin_name[MAX_PLUGIN_NAME_LENGTH];
49
50
static const char* openjpeg_plugin_name()
51
9
{
52
9
  snprintf(plugin_name, MAX_PLUGIN_NAME_LENGTH, "OpenJPEG %s", opj_version());
53
9
  plugin_name[MAX_PLUGIN_NAME_LENGTH - 1] = 0;
54
55
9
  return plugin_name;
56
9
}
57
58
59
static void openjpeg_init_plugin()
60
252
{
61
252
}
62
63
64
static void openjpeg_deinit_plugin()
65
0
{
66
0
}
67
68
69
static int openjpeg_does_support_format(heif_compression_format format)
70
69.9k
{
71
69.9k
  if (format == heif_compression_JPEG2000) {
72
2.23k
    return OPENJPEG_PLUGIN_PRIORITY;
73
2.23k
  }
74
67.7k
  else if (format == heif_compression_HTJ2K) {
75
0
    return OPENJPEG_PLUGIN_PRIORITY_HTJ2K;
76
0
  }
77
67.7k
  else {
78
67.7k
    return 0;
79
67.7k
  }
80
69.9k
}
81
82
static int openjpeg_does_support_format2(const heif_decoder_plugin_compressed_format_description* format)
83
0
{
84
0
  return openjpeg_does_support_format(format->format);
85
0
}
86
87
heif_error openjpeg_new_decoder2(void** dec, const heif_decoder_plugin_options* options)
88
2.22k
{
89
2.22k
  openjpeg_decoder* decoder = new openjpeg_decoder();
90
91
2.22k
  *dec = decoder;
92
93
2.22k
  return heif_error_ok;
94
2.22k
}
95
96
heif_error openjpeg_new_decoder(void** dec)
97
0
{
98
0
  heif_decoder_plugin_options options{};
99
0
  options.format = heif_compression_JPEG2000;
100
0
  options.num_threads = 0;
101
0
  options.strict_decoding = false;
102
103
0
  return openjpeg_new_decoder2(dec, &options);
104
0
}
105
106
void openjpeg_free_decoder(void* decoder_raw)
107
2.22k
{
108
2.22k
  openjpeg_decoder* decoder = (openjpeg_decoder*) decoder_raw;
109
110
2.22k
  if (!decoder) {
111
0
    return;
112
0
  }
113
114
2.22k
  delete decoder;
115
2.22k
}
116
117
118
void openjpeg_set_strict_decoding(void* decoder_raw, int flag)
119
0
{
120
121
0
}
122
123
124
heif_error openjpeg_push_data2(void* decoder_raw, const void* frame_data, size_t frame_size,
125
                               uintptr_t user_data)
126
2.21k
{
127
2.21k
  openjpeg_decoder* decoder = (openjpeg_decoder*) decoder_raw;
128
2.21k
  const uint8_t* frame_data_src = (const uint8_t*) frame_data;
129
130
2.21k
  decoder->encoded_data.insert(decoder->encoded_data.end(), frame_data_src, frame_data_src + frame_size);
131
2.21k
  decoder->user_data = user_data;
132
133
2.21k
  return heif_error_ok;
134
2.21k
}
135
136
heif_error openjpeg_push_data(void* decoder_raw, const void* frame_data, size_t frame_size)
137
0
{
138
0
  return openjpeg_push_data2(decoder_raw, frame_data, frame_size, 0);
139
0
}
140
141
//**************************************************************************
142
143
//  This will read from our memory to the buffer.
144
145
static OPJ_SIZE_T opj_memory_stream_read(void* p_buffer, OPJ_SIZE_T p_nb_bytes, void* p_user_data)
146
2.28k
{
147
2.28k
  openjpeg_decoder* decoder = (openjpeg_decoder*) p_user_data; // Our data.
148
2.28k
  size_t data_size = decoder->encoded_data.size();
149
150
2.28k
  OPJ_SIZE_T l_nb_bytes_read = p_nb_bytes; // Amount to move to buffer.
151
152
  // Check if the current offset is outside our data buffer.
153
154
2.28k
  if (decoder->read_position >= data_size) {
155
70
    return (OPJ_SIZE_T) -1;
156
70
  }
157
158
  // Check if we are reading more than we have.
159
160
2.21k
  if (p_nb_bytes > (data_size - decoder->read_position)) {
161
    //Read all we have.
162
2.21k
    l_nb_bytes_read = data_size - decoder->read_position;
163
2.21k
  }
164
165
  // Copy the data to the internal buffer.
166
167
2.21k
  memcpy(p_buffer, &(decoder->encoded_data[decoder->read_position]), l_nb_bytes_read);
168
169
2.21k
  decoder->read_position += l_nb_bytes_read; // Update the pointer to the new location.
170
171
2.21k
  return l_nb_bytes_read;
172
2.28k
}
173
174
175
// This will write from the buffer to our memory.
176
177
static OPJ_SIZE_T opj_memory_stream_write(void* p_buffer, OPJ_SIZE_T p_nb_bytes, void* p_user_data)
178
0
{
179
0
  assert(false); // We should never need to write to the buffer.
180
0
  return 0;
181
0
}
182
183
184
// Moves the pointer forward, but never more than we have.
185
186
static OPJ_OFF_T opj_memory_stream_skip(OPJ_OFF_T p_nb_bytes, void* p_user_data)
187
0
{
188
0
  openjpeg_decoder* decoder = (openjpeg_decoder*) p_user_data; // Our data.
189
0
  size_t data_size = decoder->encoded_data.size();
190
191
0
  OPJ_SIZE_T l_nb_bytes;
192
193
194
0
  if (p_nb_bytes < 0) {
195
    //No skipping backwards.
196
0
    return -1;
197
0
  }
198
199
0
  l_nb_bytes = (OPJ_SIZE_T) p_nb_bytes; // Allowed because it is positive.
200
201
  // Do not allow jumping past the end.
202
203
0
  if (l_nb_bytes > data_size - decoder->read_position) {
204
0
    l_nb_bytes = data_size - decoder->read_position;//Jump the max.
205
0
  }
206
207
  // Make the jump.
208
209
0
  decoder->read_position += l_nb_bytes;
210
211
  // Return how far we jumped.
212
213
0
  return l_nb_bytes;
214
0
}
215
216
217
// Sets the pointer to anywhere in the memory.
218
219
static OPJ_BOOL opj_memory_stream_seek(OPJ_OFF_T p_nb_bytes, void* p_user_data)
220
0
{
221
0
  openjpeg_decoder* decoder = (openjpeg_decoder*) p_user_data; // Our data.
222
0
  size_t data_size = decoder->encoded_data.size();
223
224
  // No before the buffer.
225
0
  if (p_nb_bytes < 0)
226
0
    return OPJ_FALSE;
227
228
  // No after the buffer.
229
0
  if (p_nb_bytes > (OPJ_OFF_T) data_size)
230
0
    return OPJ_FALSE;
231
232
  // Move to new position.
233
0
  decoder->read_position = (OPJ_SIZE_T) p_nb_bytes;
234
235
0
  return OPJ_TRUE;
236
0
}
237
238
//The system needs a routine to do when finished, the name tells you what I want it to do.
239
240
static void opj_memory_stream_do_nothing(void* p_user_data)
241
2.21k
{
242
2.21k
  OPJ_ARG_NOT_USED(p_user_data);
243
2.21k
}
244
245
246
// Create a stream to use memory as the input or output.
247
248
opj_stream_t* opj_stream_create_default_memory_stream(openjpeg_decoder* p_decoder, OPJ_BOOL p_is_read_stream)
249
2.21k
{
250
2.21k
  opj_stream_t* stream;
251
252
2.21k
  if (!(stream = opj_stream_default_create(p_is_read_stream))) {
253
0
    return nullptr;
254
0
  }
255
256
  // Set how to work with the frame buffer.
257
258
2.21k
  if (p_is_read_stream) {
259
2.21k
    opj_stream_set_read_function(stream, opj_memory_stream_read);
260
2.21k
  }
261
0
  else {
262
0
    opj_stream_set_write_function(stream, opj_memory_stream_write);
263
0
  }
264
265
2.21k
  opj_stream_set_seek_function(stream, opj_memory_stream_seek);
266
267
2.21k
  opj_stream_set_skip_function(stream, opj_memory_stream_skip);
268
269
2.21k
  opj_stream_set_user_data(stream, p_decoder, opj_memory_stream_do_nothing);
270
271
2.21k
  opj_stream_set_user_data_length(stream, p_decoder->encoded_data.size());
272
273
2.21k
  return stream;
274
2.21k
}
275
276
277
//**************************************************************************
278
279
280
// Conservative upper bound on bytes OpenJPEG will allocate to decode this
281
// codestream. Saturates to UINT64_MAX on overflow. OpenJPEG stores each sample
282
// internally as OPJ_INT32 regardless of the codestream bit depth; the 3x
283
// multiplier covers the final image planes plus in-flight tile and DWT
284
// working buffers.
285
static uint64_t openjpeg_estimate_decode_memory_bytes(const opj_image_t* image)
286
2.04k
{
287
14.2k
  auto sat_mul = [](uint64_t a, uint64_t b) -> uint64_t {
288
14.2k
    if (a == 0 || b == 0) return 0;
289
13.3k
    if (a > UINT64_MAX / b) return UINT64_MAX;
290
13.3k
    return a * b;
291
13.3k
  };
292
293
6.12k
  auto sat_add = [](uint64_t a, uint64_t b) -> uint64_t {
294
6.12k
    uint64_t s = a + b;
295
6.12k
    return (s < a) ? UINT64_MAX : s;
296
6.12k
  };
297
298
2.04k
  uint64_t total = 0;
299
8.16k
  for (uint32_t c = 0; c < image->numcomps; c++) {
300
6.12k
    const opj_image_comp_t& comp = image->comps[c];
301
6.12k
    uint64_t plane = sat_mul(uint64_t(comp.w), uint64_t(comp.h));
302
6.12k
    plane = sat_mul(plane, sizeof(OPJ_INT32));
303
6.12k
    total = sat_add(total, plane);
304
6.12k
  }
305
306
2.04k
  return sat_mul(total, 3);
307
2.04k
}
308
309
310
heif_error openjpeg_decode_next_image2(void* decoder_raw, heif_image** out_img,
311
                                       uintptr_t* out_user_data,
312
                                       const heif_security_limits* limits)
313
2.21k
{
314
2.21k
  auto* decoder = (struct openjpeg_decoder*) decoder_raw;
315
316
2.21k
  if (decoder->encoded_data.empty()) {
317
0
    *out_img = nullptr;
318
0
    return heif_error_ok;
319
0
  }
320
321
322
2.21k
  OPJ_BOOL success;
323
2.21k
  opj_dparameters_t decompression_parameters;
324
2.21k
  std::unique_ptr<opj_codec_t, void (OPJ_CALLCONV *)(opj_codec_t*)> l_codec(opj_create_decompress(OPJ_CODEC_J2K),
325
2.21k
                                                               opj_destroy_codec);
326
327
  // Initialize Decoder
328
2.21k
  opj_set_default_decoder_parameters(&decompression_parameters);
329
2.21k
  success = opj_setup_decoder(l_codec.get(), &decompression_parameters);
330
2.21k
  if (!success) {
331
0
    return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "opj_setup_decoder()"};
332
0
  }
333
334
335
  // Create Input Stream
336
337
2.21k
  OPJ_BOOL is_read_stream = true;
338
2.21k
  std::unique_ptr<opj_stream_t, void (OPJ_CALLCONV *)(opj_stream_t*)> stream(opj_stream_create_default_memory_stream(decoder, is_read_stream),
339
2.21k
                                                                opj_stream_destroy);
340
341
342
  // Read Codestream Header
343
2.21k
  opj_image_t* image_ptr = nullptr;
344
2.21k
  success = opj_read_header(stream.get(), l_codec.get(), &image_ptr);
345
2.21k
  if (!success) {
346
156
    return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "opj_read_header()"};
347
156
  }
348
349
2.05k
  std::unique_ptr<opj_image_t, void (OPJ_CALLCONV *)(opj_image_t*)> image(image_ptr, opj_image_destroy);
350
351
  // Reject obvious memory bombs before letting OpenJPEG allocate decode buffers.
352
  // OpenJPEG has no built-in resource limit API, so we enforce libheif's limits here.
353
2.05k
  if (image->x1 < image->x0 || image->y1 < image->y0) {
354
0
    return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified,
355
0
            "Invalid JPEG 2000 image bounding box"};
356
0
  }
357
358
2.05k
  uint64_t img_w = image->x1 - image->x0;
359
2.05k
  uint64_t img_h = image->y1 - image->y0;
360
361
  // image->x0,x1,y0,y1 are uint32, thus no overflow here
362
2.05k
  uint64_t pixels = img_w * img_h;
363
2.05k
  if (limits->max_image_size_pixels > 0 && pixels > limits->max_image_size_pixels) {
364
14
    return {heif_error_Memory_allocation_error, heif_suberror_Security_limit_exceeded,
365
14
            "JPEG 2000 image exceeds maximum allowed image size"};
366
14
  }
367
368
2.04k
  uint64_t estimated_memory = openjpeg_estimate_decode_memory_bytes(image.get());
369
2.04k
  if (limits->max_memory_block_size > 0 && estimated_memory > limits->max_memory_block_size) {
370
0
    return {heif_error_Memory_allocation_error, heif_suberror_Security_limit_exceeded,
371
0
            "JPEG 2000 image would require too much memory to decode"};
372
0
  }
373
374
  // TODO: also enforce limits->max_components against image->numcomps, and
375
  // limits->max_number_of_tiles against opj_get_cstr_info()->tw * th.
376
377
2.04k
  if (image->numcomps != 3 && image->numcomps != 1) {
378
    //TODO - Handle other numbers of components
379
0
    return {heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Number of components must be 3 or 1"};
380
0
  }
381
2.04k
  else if ((image->color_space != OPJ_CLRSPC_UNSPECIFIED) && (image->color_space != OPJ_CLRSPC_SRGB)) {
382
    //TODO - Handle other colorspaces
383
0
    return {heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, "Colorspace must be SRGB"};
384
0
  }
385
386
2.04k
  const int width = (image->x1 - image->x0);
387
2.04k
  const int height = (image->y1 - image->y0);
388
389
390
  /* Get the decoded image */
391
2.04k
  success = opj_decode(l_codec.get(), stream.get(), image.get());
392
2.04k
  if (!success) {
393
562
    return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "opj_decode()"};
394
562
  }
395
396
397
1.48k
  success = opj_end_decompress(l_codec.get(), stream.get());
398
1.48k
  if (!success) {
399
0
    return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "opj_end_decompress()"};
400
0
  }
401
402
403
1.48k
  heif_colorspace colorspace = heif_colorspace_YCbCr;
404
1.48k
  heif_chroma chroma = heif_chroma_444; //heif_chroma_interleaved_RGB;
405
406
1.48k
  std::vector<heif_channel> channels;
407
408
1.48k
  if (image->numcomps == 1) {
409
0
    colorspace = heif_colorspace_monochrome;
410
0
    chroma = heif_chroma_monochrome;
411
0
    channels = {heif_channel_Y};
412
0
  }
413
1.48k
  else if (image->numcomps == 3 &&
414
1.48k
           image->comps[1].dx == 1 &&
415
1.40k
           image->comps[1].dy == 1) {
416
1.38k
    colorspace = heif_colorspace_YCbCr;
417
1.38k
    chroma = heif_chroma_444;
418
1.38k
    channels = {heif_channel_Y, heif_channel_Cb, heif_channel_Cr};
419
1.38k
  }
420
96
  else if (image->numcomps == 3 &&
421
96
           image->comps[1].dx == 2 &&
422
1
           image->comps[1].dy == 1) {
423
0
    colorspace = heif_colorspace_YCbCr;
424
0
    chroma = heif_chroma_422;
425
0
    channels = {heif_channel_Y, heif_channel_Cb, heif_channel_Cr};
426
0
  }
427
96
  else if (image->numcomps == 3 &&
428
96
           image->comps[1].dx == 2 &&
429
1
           image->comps[1].dy == 2) {
430
0
    colorspace = heif_colorspace_YCbCr;
431
0
    chroma = heif_chroma_420;
432
0
    channels = {heif_channel_Y, heif_channel_Cb, heif_channel_Cr};
433
0
  }
434
96
  else {
435
96
    return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "unsupported image format"};
436
96
  }
437
438
439
  // Validate per-component sizes against the chroma format derived above. A malformed
440
  // JPEG 2000 stream may set comp[1].dx/dy consistently with a chroma format yet declare
441
  // comp[c].w/h that do not match the subsampled dimensions; using such planes downstream
442
  // causes out-of-bounds reads in color conversion (issue #1796).
443
5.40k
  for (size_t c = 0; c < image->numcomps; c++) {
444
4.07k
    uint32_t expected_w, expected_h;
445
4.07k
    get_subsampled_size(static_cast<uint32_t>(width), static_cast<uint32_t>(height),
446
4.07k
                        channels[c], chroma, &expected_w, &expected_h);
447
4.07k
    if (image->comps[c].w != expected_w || image->comps[c].h != expected_h) {
448
60
      return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified,
449
60
              "JPEG 2000 component size does not match the image's chroma subsampling"};
450
60
    }
451
4.07k
  }
452
453
1.32k
  heif_error error = heif_image_create(width, height, colorspace, chroma, out_img);
454
1.32k
  if (error.code) {
455
0
    return error;
456
0
  }
457
458
5.29k
  for (size_t c = 0; c < image->numcomps; c++) {
459
3.97k
    const opj_image_comp_t& opj_comp = image->comps[c];
460
461
3.97k
    int bit_depth = opj_comp.prec;
462
3.97k
    int cwidth = opj_comp.w;
463
3.97k
    int cheight = opj_comp.h;
464
465
3.97k
    error = heif_image_add_plane_safe(*out_img, channels[c], cwidth, cheight, bit_depth, limits);
466
3.97k
    if (error.code) {
467
      // copy error message to decoder object because heif_image will be released
468
0
      decoder->error_message = error.message;
469
0
      error.message = decoder->error_message.c_str();
470
471
0
      heif_image_release(*out_img);
472
0
      *out_img = nullptr;
473
0
      return error;
474
0
    }
475
476
3.97k
    size_t stride = 0;
477
3.97k
    uint8_t* p = heif_image_get_plane2(*out_img, channels[c], &stride);
478
479
480
    // TODO: a SIMD implementation to convert int32 to uint8 would speed this up
481
    // https://stackoverflow.com/questions/63774643/how-to-convert-uint32-to-uint8-using-simd-but-not-avx512
482
483
3.97k
    if (bit_depth <= 8) {
484
1.98M
      for (int y = 0; y < cheight; y++) {
485
999M
        for (int x = 0; x < cwidth; x++) {
486
997M
          p[y * stride + x] = (uint8_t) opj_comp.data[y * cwidth + x];
487
997M
        }
488
1.97M
      }
489
3.94k
    }
490
29
    else {
491
29
      uint16_t* p16 = (uint16_t*)p;
492
12.1k
      for (int y = 0; y < cheight; y++) {
493
6.19M
        for (int x = 0; x < cwidth; x++) {
494
6.18M
          p16[y * stride/2 + x] = (uint16_t) opj_comp.data[y * cwidth + x];
495
6.18M
        }
496
12.1k
      }
497
29
    }
498
3.97k
  }
499
500
1.32k
  if (out_user_data) {
501
0
    *out_user_data = decoder->user_data;
502
0
  }
503
504
1.32k
  decoder->encoded_data.clear();
505
1.32k
  decoder->read_position = 0;
506
507
1.32k
  return heif_error_ok;
508
1.32k
}
509
510
heif_error openjpeg_decode_next_image(void* decoder_raw, heif_image** out_img,
511
                                      const heif_security_limits* limits)
512
0
{
513
0
  return openjpeg_decode_next_image2(decoder_raw, out_img, nullptr, limits);
514
0
}
515
516
heif_error openjpeg_decode_image(void* decoder_raw, heif_image** out_img)
517
0
{
518
0
  auto* limits = heif_get_global_security_limits();
519
0
  return openjpeg_decode_next_image(decoder_raw, out_img, limits);
520
0
}
521
522
heif_error openjpeg_flush_data(void* decoder)
523
2.21k
{
524
2.21k
  return heif_error_ok;
525
2.21k
}
526
527
528
static const heif_decoder_plugin decoder_openjpeg{
529
    5,
530
    openjpeg_plugin_name,
531
    openjpeg_init_plugin,
532
    openjpeg_deinit_plugin,
533
    openjpeg_does_support_format,
534
    openjpeg_new_decoder,
535
    openjpeg_free_decoder,
536
    openjpeg_push_data,
537
    openjpeg_decode_image,
538
    openjpeg_set_strict_decoding,
539
    "openjpeg",
540
    openjpeg_decode_next_image,
541
    /* minimum_required_libheif_version */ LIBHEIF_MAKE_VERSION(1,21,0),
542
    openjpeg_does_support_format2,
543
    openjpeg_new_decoder2,
544
    openjpeg_push_data2,
545
    openjpeg_flush_data,
546
    openjpeg_decode_next_image2
547
};
548
549
const heif_decoder_plugin* get_decoder_plugin_openjpeg()
550
252
{
551
252
  return &decoder_openjpeg;
552
252
}
553
554
555
#if PLUGIN_OPENJPEG_DECODER
556
heif_plugin_info plugin_info {
557
  1,
558
  heif_plugin_type_decoder,
559
  &decoder_openjpeg
560
};
561
#endif