/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 |