/src/libheif/libheif/plugins/encoder_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 "encoder_openjpeg.h" |
25 | | |
26 | | #include <openjpeg.h> |
27 | | #include <string.h> |
28 | | |
29 | | #include <vector> |
30 | | #include <string> |
31 | | #include <cassert> |
32 | | |
33 | | using namespace std; |
34 | | |
35 | | |
36 | | static const int OPJ_PLUGIN_PRIORITY = 80; |
37 | | |
38 | | |
39 | | struct encoder_struct_opj |
40 | | { |
41 | | int quality = 70; |
42 | | heif_chroma chroma = heif_chroma_undefined; |
43 | | opj_cparameters_t parameters; |
44 | | |
45 | | // --- output |
46 | | |
47 | | std::vector<uint8_t> codestream; //contains encoded pixel data |
48 | | bool data_read = false; |
49 | | |
50 | | // --- parameters |
51 | | |
52 | | // std::vector<parameter> parameters; |
53 | | |
54 | | // void add_param(const parameter&); |
55 | | |
56 | | // void add_param(const std::string& name, int value); |
57 | | |
58 | | // void add_param(const std::string& name, bool value); |
59 | | |
60 | | // void add_param(const std::string& name, const std::string& value); |
61 | | |
62 | | // parameter get_param(const std::string& name) const; |
63 | | |
64 | | // std::string preset; |
65 | | // std::string tune; |
66 | | |
67 | | // int logLevel = X265_LOG_NONE; |
68 | | }; |
69 | | |
70 | | static const char* kParam_chroma = "chroma"; |
71 | | static const char* const kParam_chroma_valid_values[] = { |
72 | | "420", "422", "444", nullptr |
73 | | }; |
74 | | |
75 | | |
76 | 0 | #define MAX_PLUGIN_NAME_LENGTH 80 |
77 | | static char plugin_name[MAX_PLUGIN_NAME_LENGTH]; |
78 | | |
79 | | const char* opj_plugin_name() |
80 | 0 | { |
81 | 0 | snprintf(plugin_name, MAX_PLUGIN_NAME_LENGTH, "OpenJPEG %s", opj_version()); |
82 | 0 | plugin_name[MAX_PLUGIN_NAME_LENGTH - 1] = 0; |
83 | |
|
84 | 0 | return plugin_name; |
85 | 0 | } |
86 | | |
87 | | |
88 | | #define MAX_NPARAMETERS 10 |
89 | | |
90 | | static heif_encoder_parameter opj_encoder_params[MAX_NPARAMETERS]; |
91 | | static const heif_encoder_parameter* opj_encoder_parameter_ptrs[MAX_NPARAMETERS + 1]; |
92 | | |
93 | | static void opj_init_parameters() |
94 | 252 | { |
95 | 252 | heif_encoder_parameter* p = opj_encoder_params; |
96 | 252 | const heif_encoder_parameter** d = opj_encoder_parameter_ptrs; |
97 | 252 | int i = 0; |
98 | | |
99 | 252 | assert(i < MAX_NPARAMETERS); |
100 | 252 | p->version = 2; |
101 | 252 | p->name = kParam_chroma; |
102 | 252 | p->type = heif_encoder_parameter_type_string; |
103 | | //p->string.default_value = "420"; |
104 | 252 | p->has_default = false; |
105 | 252 | p->string.valid_values = kParam_chroma_valid_values; |
106 | 252 | d[i++] = p++; |
107 | | |
108 | 252 | d[i++] = nullptr; |
109 | 252 | } |
110 | | |
111 | | void opj_init_plugin() |
112 | 252 | { |
113 | 252 | opj_init_parameters(); |
114 | 252 | } |
115 | | |
116 | | void opj_cleanup_plugin() |
117 | 0 | { |
118 | 0 | } |
119 | | |
120 | | static void opj_set_default_parameters(void* encoder); |
121 | | |
122 | | heif_error opj_new_encoder(void** encoder_out) |
123 | 0 | { |
124 | 0 | encoder_struct_opj* encoder = new encoder_struct_opj(); |
125 | 0 | *encoder_out = encoder; |
126 | |
|
127 | 0 | opj_set_default_parameters(encoder); |
128 | 0 | opj_set_default_encoder_parameters(&(encoder->parameters)); |
129 | |
|
130 | 0 | return heif_error_ok; |
131 | 0 | } |
132 | | |
133 | | void opj_free_encoder(void* encoder_raw) |
134 | 0 | { |
135 | 0 | encoder_struct_opj* encoder = (encoder_struct_opj*) encoder_raw; |
136 | 0 | delete encoder; |
137 | 0 | } |
138 | | |
139 | | heif_error opj_set_parameter_quality(void* encoder_raw, int quality) |
140 | 0 | { |
141 | 0 | auto* encoder = (encoder_struct_opj*) encoder_raw; |
142 | |
|
143 | 0 | if (quality < 0 || quality > 100) { |
144 | 0 | return heif_error_invalid_parameter_value; |
145 | 0 | } |
146 | | |
147 | 0 | encoder->quality = quality; |
148 | |
|
149 | 0 | return heif_error_ok; |
150 | 0 | } |
151 | | |
152 | | heif_error opj_get_parameter_quality(void* encoder_raw, int* quality) |
153 | 0 | { |
154 | 0 | auto* encoder = (encoder_struct_opj*) encoder_raw; |
155 | |
|
156 | 0 | *quality = encoder->quality; |
157 | |
|
158 | 0 | return heif_error_ok; |
159 | 0 | } |
160 | | |
161 | | heif_error opj_set_parameter_lossless(void* encoder_raw, int lossless) |
162 | 0 | { |
163 | 0 | auto* encoder = (encoder_struct_opj*) encoder_raw; |
164 | 0 | encoder->parameters.irreversible = lossless ? 0 : 1; |
165 | 0 | return heif_error_ok; |
166 | 0 | } |
167 | | |
168 | | heif_error opj_get_parameter_lossless(void* encoder_raw, int* lossless) |
169 | 0 | { |
170 | 0 | auto* encoder = (encoder_struct_opj*) encoder_raw; |
171 | 0 | *lossless = (encoder->parameters.irreversible == 0); |
172 | 0 | return heif_error_ok; |
173 | 0 | } |
174 | | |
175 | | heif_error opj_set_parameter_logging_level(void* encoder, int logging) |
176 | 0 | { |
177 | 0 | return heif_error_ok; |
178 | 0 | } |
179 | | |
180 | | heif_error opj_get_parameter_logging_level(void* encoder, int* logging) |
181 | 0 | { |
182 | 0 | return heif_error_ok; |
183 | 0 | } |
184 | | |
185 | | const heif_encoder_parameter** opj_list_parameters(void* encoder) |
186 | 0 | { |
187 | 0 | return opj_encoder_parameter_ptrs; |
188 | 0 | } |
189 | | |
190 | | heif_error opj_set_parameter_integer(void* encoder_raw, const char* name, int value) |
191 | 0 | { |
192 | 0 | encoder_struct_opj* encoder = (encoder_struct_opj*) encoder_raw; |
193 | |
|
194 | 0 | if (strcmp(name, heif_encoder_parameter_name_quality) == 0) { |
195 | 0 | return opj_set_parameter_quality(encoder, value); |
196 | 0 | } |
197 | | |
198 | 0 | return heif_error_unsupported_parameter; |
199 | 0 | } |
200 | | |
201 | | heif_error opj_get_parameter_integer(void* encoder_raw, const char* name, int* value) |
202 | 0 | { |
203 | 0 | encoder_struct_opj* encoder = (encoder_struct_opj*) encoder_raw; |
204 | |
|
205 | 0 | if (strcmp(name, heif_encoder_parameter_name_quality) == 0) { |
206 | 0 | return opj_get_parameter_quality(encoder, value); |
207 | 0 | } |
208 | | |
209 | 0 | return heif_error_ok; |
210 | 0 | } |
211 | | |
212 | | heif_error opj_set_parameter_boolean(void* encoder, const char* name, int value) |
213 | 0 | { |
214 | 0 | return heif_error_ok; |
215 | 0 | } |
216 | | |
217 | | heif_error opj_get_parameter_boolean(void* encoder, const char* name, int* value) |
218 | 0 | { |
219 | 0 | return heif_error_ok; |
220 | 0 | } |
221 | | |
222 | | heif_error opj_set_parameter_string(void* encoder_raw, const char* name, const char* value) |
223 | 0 | { |
224 | 0 | auto* encoder = (encoder_struct_opj*) encoder_raw; |
225 | |
|
226 | 0 | if (strcmp(name, kParam_chroma) == 0) { |
227 | 0 | if (strcmp(value, "420") == 0) { |
228 | 0 | encoder->chroma = heif_chroma_420; |
229 | 0 | return heif_error_ok; |
230 | 0 | } |
231 | 0 | else if (strcmp(value, "422") == 0) { |
232 | 0 | encoder->chroma = heif_chroma_422; |
233 | 0 | return heif_error_ok; |
234 | 0 | } |
235 | 0 | else if (strcmp(value, "444") == 0) { |
236 | 0 | encoder->chroma = heif_chroma_444; |
237 | 0 | return heif_error_ok; |
238 | 0 | } |
239 | 0 | else { |
240 | 0 | return heif_error_invalid_parameter_value; |
241 | 0 | } |
242 | 0 | } |
243 | | |
244 | 0 | return heif_error_unsupported_parameter; |
245 | 0 | } |
246 | | |
247 | | static void save_strcpy(char* dst, int dst_size, const char* src) |
248 | 0 | { |
249 | 0 | strncpy(dst, src, dst_size - 1); |
250 | 0 | dst[dst_size - 1] = 0; |
251 | 0 | } |
252 | | |
253 | | heif_error opj_get_parameter_string(void* encoder_raw, const char* name, char* value, int value_size) |
254 | 0 | { |
255 | 0 | encoder_struct_opj* encoder = (encoder_struct_opj*) encoder_raw; |
256 | |
|
257 | 0 | if (strcmp(name, kParam_chroma) == 0) { |
258 | 0 | switch (encoder->chroma) { |
259 | 0 | case heif_chroma_420: |
260 | 0 | save_strcpy(value, value_size, "420"); |
261 | 0 | break; |
262 | 0 | case heif_chroma_422: |
263 | 0 | save_strcpy(value, value_size, "422"); |
264 | 0 | break; |
265 | 0 | case heif_chroma_444: |
266 | 0 | save_strcpy(value, value_size, "444"); |
267 | 0 | break; |
268 | 0 | case heif_chroma_undefined: |
269 | 0 | save_strcpy(value, value_size, "undefined"); |
270 | 0 | break; |
271 | 0 | default: |
272 | 0 | assert(false); |
273 | 0 | return heif_error_invalid_parameter_value; |
274 | 0 | } |
275 | 0 | return heif_error_ok; |
276 | 0 | } |
277 | | |
278 | 0 | return heif_error_unsupported_parameter; |
279 | 0 | } |
280 | | |
281 | | |
282 | | static void opj_set_default_parameters(void* encoder) |
283 | 0 | { |
284 | 0 | for (const heif_encoder_parameter** p = opj_encoder_parameter_ptrs; *p; p++) { |
285 | 0 | const heif_encoder_parameter* param = *p; |
286 | |
|
287 | 0 | if (param->has_default) { |
288 | 0 | switch (param->type) { |
289 | 0 | case heif_encoder_parameter_type_integer: |
290 | 0 | opj_set_parameter_integer(encoder, param->name, param->integer.default_value); |
291 | 0 | break; |
292 | 0 | case heif_encoder_parameter_type_boolean: |
293 | 0 | opj_set_parameter_boolean(encoder, param->name, param->boolean.default_value); |
294 | 0 | break; |
295 | 0 | case heif_encoder_parameter_type_string: |
296 | 0 | opj_set_parameter_string(encoder, param->name, param->string.default_value); |
297 | 0 | break; |
298 | 0 | } |
299 | 0 | } |
300 | 0 | } |
301 | 0 | } |
302 | | |
303 | | |
304 | | void opj_query_input_colorspace(heif_colorspace* inout_colorspace, heif_chroma* inout_chroma) |
305 | 0 | { |
306 | | // Replace the input colorspace/chroma with the one that is supported by the encoder and that |
307 | | // comes as close to the input colorspace/chroma as possible. |
308 | |
|
309 | 0 | if (*inout_colorspace == heif_colorspace_monochrome) { |
310 | 0 | *inout_colorspace = heif_colorspace_monochrome; |
311 | 0 | *inout_chroma = heif_chroma_monochrome; |
312 | 0 | } |
313 | 0 | else { |
314 | 0 | *inout_colorspace = heif_colorspace_YCbCr; |
315 | 0 | *inout_chroma = heif_chroma_444; |
316 | 0 | } |
317 | 0 | } |
318 | | |
319 | | void opj_query_input_colorspace2(void* encoder_raw, heif_colorspace* inout_colorspace, heif_chroma* inout_chroma) |
320 | 0 | { |
321 | 0 | auto* encoder = (struct encoder_struct_opj*) encoder_raw; |
322 | |
|
323 | 0 | if (*inout_colorspace == heif_colorspace_monochrome) { |
324 | 0 | *inout_colorspace = heif_colorspace_monochrome; |
325 | 0 | *inout_chroma = heif_chroma_monochrome; |
326 | 0 | } |
327 | 0 | else { |
328 | 0 | *inout_colorspace = heif_colorspace_YCbCr; |
329 | |
|
330 | 0 | if (encoder->chroma != heif_chroma_undefined) { |
331 | 0 | *inout_chroma = encoder->chroma; |
332 | 0 | } |
333 | 0 | else { |
334 | 0 | *inout_chroma = heif_chroma_444; |
335 | 0 | } |
336 | 0 | } |
337 | 0 | } |
338 | | |
339 | | |
340 | | // OpenJPEG will encode a portion of the image and then call this function |
341 | | // @param src_data_raw - Newly encoded bytes provided by OpenJPEG |
342 | | // @param nb_bytes - The number of bytes or size of src_data_raw |
343 | | // @param encoder_raw - Out the new |
344 | | // @return - The number of bytes successfully transferred |
345 | | static OPJ_SIZE_T opj_write_from_buffer(void* src_data_raw, OPJ_SIZE_T nb_bytes, void* encoder_raw) |
346 | 0 | { |
347 | 0 | uint8_t* src_data = (uint8_t*) src_data_raw; |
348 | 0 | encoder_struct_opj* encoder = (encoder_struct_opj*) encoder_raw; |
349 | |
|
350 | 0 | for (size_t i = 0; i < nb_bytes; i++) { |
351 | 0 | encoder->codestream.push_back(src_data[i]); |
352 | 0 | } |
353 | |
|
354 | 0 | return nb_bytes; |
355 | 0 | } |
356 | | |
357 | | static void opj_close_from_buffer(void* p_user_data) |
358 | 0 | { |
359 | 0 | } |
360 | | |
361 | | |
362 | | // The codestream is defined in ISO/IEC 15444-1. It contains the |
363 | | // compressed image pixel data and very basic metadata. |
364 | | // @param data - Uncompressed image pixel data |
365 | | // @param encoder - The function will output codestream in encoder->codestream |
366 | | static heif_error generate_codestream(opj_image_t* image, encoder_struct_opj* encoder) |
367 | 0 | { |
368 | 0 | heif_error error; |
369 | 0 | OPJ_BOOL success; |
370 | |
|
371 | 0 | encoder->parameters.cp_disto_alloc = 1; |
372 | 0 | encoder->parameters.tcp_numlayers = 1; |
373 | 0 | encoder->parameters.tcp_rates[0] = (float)(1 + (100 - encoder->quality)/2); |
374 | |
|
375 | | #if 0 |
376 | | //Insert a human readable comment into the codestream |
377 | | if (parameters.cp_comment == NULL) { |
378 | | char buf[80]; |
379 | | #ifdef _WIN32 |
380 | | sprintf_s(buf, 80, "Created by OpenJPEG version %s", opj_version()); |
381 | | #else |
382 | | snprintf(buf, 80, "Created by OpenJPEG version %s", opj_version()); |
383 | | #endif |
384 | | parameters.cp_comment = strdup(buf); |
385 | | } |
386 | | #endif |
387 | | |
388 | | //OPJ_CODEC_J2K - Only generate the codestream |
389 | | //OPJ_CODEC_JP2 - Generate the entire jp2 file (which contains a codestream) |
390 | 0 | OPJ_CODEC_FORMAT codec_format = OPJ_CODEC_J2K; |
391 | 0 | opj_codec_t* codec = opj_create_compress(codec_format); |
392 | 0 | success = opj_setup_encoder(codec, &(encoder->parameters), image); |
393 | 0 | if (!success) { |
394 | 0 | opj_destroy_codec(codec); |
395 | 0 | error = {heif_error_Encoding_error, heif_suberror_Unspecified, "Failed to setup OpenJPEG encoder"}; |
396 | 0 | return error; |
397 | 0 | } |
398 | | |
399 | | |
400 | | //Create Stream |
401 | 0 | size_t bufferSize = 64 * 1024; // 64k |
402 | 0 | opj_stream_t* stream = opj_stream_create(bufferSize, false /* read only mode */); |
403 | 0 | if (stream == NULL) { |
404 | 0 | opj_destroy_codec(codec); |
405 | 0 | error = {heif_error_Encoding_error, heif_suberror_Unspecified, "Failed to create opj_stream_t"}; |
406 | 0 | return error; |
407 | 0 | } |
408 | | |
409 | | |
410 | | // When OpenJPEG encodes the image, it will pass the 'encoder' into the write function |
411 | 0 | opj_stream_set_user_data(stream, encoder, opj_close_from_buffer); |
412 | | |
413 | | // Tell OpenJPEG how and where to write the output data |
414 | 0 | opj_stream_set_write_function(stream, (opj_stream_write_fn) opj_write_from_buffer); |
415 | | |
416 | | // TODO: should we use this function? |
417 | | // opj_stream_set_user_data_length(stream, 0); |
418 | | |
419 | | |
420 | |
|
421 | 0 | success = opj_start_compress(codec, image, stream); |
422 | 0 | if (!success) { |
423 | 0 | opj_stream_destroy(stream); |
424 | 0 | opj_destroy_codec(codec); |
425 | 0 | error = {heif_error_Encoding_error, heif_suberror_Unspecified, "Failed opj_start_compress()"}; |
426 | 0 | return error; |
427 | 0 | } |
428 | | |
429 | 0 | success = opj_encode(codec, stream); |
430 | 0 | if (!success) { |
431 | 0 | opj_stream_destroy(stream); |
432 | 0 | opj_destroy_codec(codec); |
433 | 0 | error = {heif_error_Encoding_error, heif_suberror_Unspecified, "Failed opj_encode()"}; |
434 | 0 | return error; |
435 | 0 | } |
436 | | |
437 | 0 | success = opj_end_compress(codec, stream); |
438 | 0 | if (!success) { |
439 | 0 | opj_stream_destroy(stream); |
440 | 0 | opj_destroy_codec(codec); |
441 | 0 | error = {heif_error_Encoding_error, heif_suberror_Unspecified, "Failed opj_end_compress()"}; |
442 | 0 | return error; |
443 | 0 | } |
444 | | |
445 | 0 | opj_stream_destroy(stream); |
446 | 0 | opj_destroy_codec(codec); |
447 | |
|
448 | 0 | return heif_error_ok; |
449 | 0 | } |
450 | | |
451 | | |
452 | | heif_error opj_encode_image(void* encoder_raw, const heif_image* image, heif_image_input_class image_class) |
453 | 0 | { |
454 | 0 | encoder_struct_opj* encoder = (encoder_struct_opj*) encoder_raw; |
455 | 0 | heif_error err; |
456 | |
|
457 | 0 | heif_chroma chroma = heif_image_get_chroma_format(image); |
458 | 0 | heif_colorspace colorspace = heif_image_get_colorspace(image); |
459 | |
|
460 | 0 | int width = heif_image_get_primary_width(image); |
461 | 0 | int height = heif_image_get_primary_height(image); |
462 | |
|
463 | 0 | std::vector<heif_channel> channels; |
464 | 0 | OPJ_COLOR_SPACE opj_colorspace; |
465 | |
|
466 | 0 | switch (colorspace) { |
467 | 0 | case heif_colorspace_YCbCr: |
468 | 0 | channels = {heif_channel_Y, heif_channel_Cb, heif_channel_Cr}; |
469 | 0 | opj_colorspace = OPJ_CLRSPC_SYCC; |
470 | 0 | break; |
471 | 0 | case heif_colorspace_RGB: |
472 | 0 | channels = {heif_channel_R, heif_channel_G, heif_channel_B}; |
473 | 0 | opj_colorspace = OPJ_CLRSPC_SRGB; |
474 | 0 | break; |
475 | 0 | case heif_colorspace_monochrome: |
476 | 0 | channels = {heif_channel_Y}; |
477 | 0 | opj_colorspace = OPJ_CLRSPC_GRAY; |
478 | 0 | break; |
479 | 0 | default: |
480 | 0 | assert(false); |
481 | 0 | return {heif_error_Encoding_error, heif_suberror_Unspecified, "OpenJPEG encoder plugin received image with invalid colorspace."}; |
482 | 0 | } |
483 | | |
484 | 0 | int band_count = (int) channels.size(); |
485 | |
|
486 | 0 | opj_image_cmptparm_t component_params[4]; |
487 | 0 | memset(&component_params, 0, band_count * sizeof(opj_image_cmptparm_t)); |
488 | |
|
489 | 0 | for (int comp = 0; comp < band_count; comp++) { |
490 | |
|
491 | 0 | int bpp = heif_image_get_bits_per_pixel_range(image, channels[comp]); |
492 | |
|
493 | 0 | int sub_dx = 1, sub_dy = 1; |
494 | 0 | switch (chroma) { |
495 | 0 | case heif_chroma_420: |
496 | 0 | sub_dx = 2; |
497 | 0 | sub_dy = 2; |
498 | 0 | break; |
499 | 0 | case heif_chroma_422: |
500 | 0 | sub_dx = 2; |
501 | 0 | sub_dy = 1; |
502 | 0 | break; |
503 | 0 | default: |
504 | 0 | break; |
505 | 0 | } |
506 | | |
507 | 0 | component_params[comp].prec = bpp; |
508 | 0 | component_params[comp].sgnd = 0; |
509 | 0 | component_params[comp].dx = comp == 0 ? 1 : sub_dx; |
510 | 0 | component_params[comp].dy = comp == 0 ? 1 : sub_dy; |
511 | 0 | component_params[comp].w = comp == 0 ? width : (width + sub_dx / 2) / sub_dx; |
512 | 0 | component_params[comp].h = comp == 0 ? height : (height + sub_dy / 2) / sub_dy; |
513 | 0 | } |
514 | | |
515 | 0 | opj_image_t* opj_image = opj_image_create(band_count, &component_params[0], opj_colorspace); |
516 | 0 | if (image == nullptr) { |
517 | | // Failed to create image |
518 | 0 | err = {heif_error_Encoding_error, heif_suberror_Unspecified, "Failed create OpenJPEG image"}; |
519 | 0 | return err; |
520 | 0 | } |
521 | | |
522 | 0 | opj_image->x0 = 0; |
523 | 0 | opj_image->y0 = 0; |
524 | 0 | opj_image->x1 = width; |
525 | 0 | opj_image->y1 = height; |
526 | |
|
527 | 0 | for (int comp = 0; comp < band_count; comp++) { |
528 | 0 | int stride; |
529 | 0 | const uint8_t* p = heif_image_get_plane_readonly(image, channels[comp], &stride); |
530 | 0 | int bpp = heif_image_get_bits_per_pixel(image, channels[comp]); |
531 | |
|
532 | 0 | int cwidth = component_params[comp].w; |
533 | 0 | int cheight = component_params[comp].h; |
534 | | |
535 | | // Note: obj_image data is 32bit integer |
536 | |
|
537 | 0 | if (bpp <= 8) { |
538 | 0 | for (int y = 0; y < cheight; y++) { |
539 | 0 | for (int x = 0; x < cwidth; x++) { |
540 | 0 | opj_image->comps[comp].data[y * cwidth + x] = p[y * stride + x]; |
541 | 0 | } |
542 | 0 | } |
543 | 0 | } |
544 | 0 | else { |
545 | 0 | const uint16_t* p16 = (const uint16_t*)p; |
546 | |
|
547 | 0 | for (int y = 0; y < cheight; y++) { |
548 | 0 | for (int x = 0; x < cwidth; x++) { |
549 | 0 | opj_image->comps[comp].data[y * cwidth + x] = p16[y * stride/2 + x]; |
550 | 0 | } |
551 | 0 | } |
552 | 0 | } |
553 | 0 | } |
554 | |
|
555 | 0 | encoder->data_read = false; |
556 | 0 | encoder->codestream.clear(); //Fixes issue when encoding multiple images and old data persists. |
557 | | |
558 | | //Encodes the image into a 'codestream' which is stored in the 'encoder' variable |
559 | 0 | err = generate_codestream(opj_image, encoder); |
560 | |
|
561 | 0 | opj_image_destroy(opj_image); |
562 | |
|
563 | 0 | return err; |
564 | 0 | } |
565 | | |
566 | | heif_error opj_get_compressed_data(void* encoder_raw, uint8_t** data, int* size, enum heif_encoded_data_type* type) |
567 | 0 | { |
568 | | // Get a packet of decoded data. The data format depends on the codec. |
569 | |
|
570 | 0 | encoder_struct_opj* encoder = (encoder_struct_opj*) encoder_raw; |
571 | |
|
572 | 0 | if (encoder->data_read) { |
573 | 0 | *size = 0; |
574 | 0 | *data = nullptr; |
575 | 0 | } |
576 | 0 | else { |
577 | 0 | *size = (int) encoder->codestream.size(); |
578 | 0 | *data = encoder->codestream.data(); |
579 | 0 | encoder->data_read = true; |
580 | 0 | } |
581 | |
|
582 | 0 | return heif_error_ok; |
583 | 0 | } |
584 | | |
585 | | void opj_query_encoded_size(void* encoder, uint32_t input_width, uint32_t input_height, uint32_t* encoded_width, uint32_t* encoded_height) |
586 | 0 | { |
587 | | // --- version 3 --- |
588 | | |
589 | | // The encoded image size may be different from the input frame size, e.g. because |
590 | | // of required rounding, or a required minimum size. Use this function to return |
591 | | // the encoded size for a given input image size. |
592 | | // You may set this to NULL if no padding is required for any image size. |
593 | |
|
594 | 0 | *encoded_width = input_width; |
595 | 0 | *encoded_height = input_height; |
596 | 0 | } |
597 | | |
598 | | |
599 | | static const heif_encoder_plugin encoder_plugin_openjpeg{ |
600 | | /* plugin_api_version */ 3, |
601 | | /* compression_format */ heif_compression_JPEG2000, |
602 | | /* id_name */ "openjpeg", |
603 | | /* priority */ OPJ_PLUGIN_PRIORITY, |
604 | | /* supports_lossy_compression */ false, |
605 | | /* supports_lossless_compression */ true, |
606 | | /* get_plugin_name */ opj_plugin_name, |
607 | | /* init_plugin */ opj_init_plugin, |
608 | | /* cleanup_plugin */ opj_cleanup_plugin, |
609 | | /* new_encoder */ opj_new_encoder, |
610 | | /* free_encoder */ opj_free_encoder, |
611 | | /* set_parameter_quality */ opj_set_parameter_quality, |
612 | | /* get_parameter_quality */ opj_get_parameter_quality, |
613 | | /* set_parameter_lossless */ opj_set_parameter_lossless, |
614 | | /* get_parameter_lossless */ opj_get_parameter_lossless, |
615 | | /* set_parameter_logging_level */ opj_set_parameter_logging_level, |
616 | | /* get_parameter_logging_level */ opj_get_parameter_logging_level, |
617 | | /* list_parameters */ opj_list_parameters, |
618 | | /* set_parameter_integer */ opj_set_parameter_integer, |
619 | | /* get_parameter_integer */ opj_get_parameter_integer, |
620 | | /* set_parameter_boolean */ opj_set_parameter_boolean, |
621 | | /* get_parameter_boolean */ opj_get_parameter_boolean, |
622 | | /* set_parameter_string */ opj_set_parameter_string, |
623 | | /* get_parameter_string */ opj_get_parameter_string, |
624 | | /* query_input_colorspace */ opj_query_input_colorspace, |
625 | | /* encode_image */ opj_encode_image, |
626 | | /* get_compressed_data */ opj_get_compressed_data, |
627 | | /* query_input_colorspace (v2) */ opj_query_input_colorspace2, |
628 | | /* query_encoded_size (v3) */ opj_query_encoded_size |
629 | | }; |
630 | | |
631 | | const heif_encoder_plugin* get_encoder_plugin_openjpeg() |
632 | 252 | { |
633 | 252 | return &encoder_plugin_openjpeg; |
634 | 252 | } |
635 | | |
636 | | |
637 | | #if PLUGIN_OPENJPEG_ENCODER |
638 | | heif_plugin_info plugin_info { |
639 | | 1, |
640 | | heif_plugin_type_encoder, |
641 | | &encoder_plugin_openjpeg |
642 | | }; |
643 | | #endif |