2 * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
24 #include <jxl/decode.h>
25 #include <jxl/encode.h>
26 #include <jxl/thread_parallel_runner.h>
28 #include "mm_util_jxl.h"
29 #include "mm_util_private.h"
31 #define DEFAULT_THREAD 1
32 #define INI_CATEGORY_JPEG_XL "jpeg-xl"
33 #define INI_ITEM_NUM_OF_DECODING_TRHEADS "decoding_threads"
34 #define INI_ITEM_NUM_OF_ENCODING_TRHEADS "encoding_threads"
36 static JxlPixelFormat jxl_formats[] = {
37 [MM_UTIL_COLOR_RGB24] = {3, JXL_TYPE_UINT8, JXL_BIG_ENDIAN, 0}, // RGB -> RGB
38 [MM_UTIL_COLOR_RGBA] = {4, JXL_TYPE_UINT8, JXL_BIG_ENDIAN, 0}, // RGBA -> RGBA
41 static int __convert_dec_error(JxlDecoderStatus status)
43 int err = MM_UTIL_ERROR_NONE;
47 err = MM_UTIL_ERROR_NONE;
49 case JXL_DEC_NEED_MORE_INPUT:
50 case JXL_DEC_NEED_PREVIEW_OUT_BUFFER:
51 case JXL_DEC_NEED_DC_OUT_BUFFER:
52 case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
53 err = MM_UTIL_ERROR_INVALID_PARAMETER;
55 case JXL_DEC_JPEG_NEED_MORE_OUTPUT:
56 err = MM_UTIL_ERROR_OUT_OF_MEMORY;
60 err = MM_UTIL_ERROR_INVALID_OPERATION;
64 mm_util_warn("convert err(%d) from status(%d)", err, status);
69 static int __convert_enc_error(JxlEncoderStatus status)
71 int err = MM_UTIL_ERROR_NONE;
75 err = MM_UTIL_ERROR_NONE;
77 case JXL_ENC_NEED_MORE_OUTPUT:
78 err = MM_UTIL_ERROR_OUT_OF_MEMORY;
80 case JXL_ENC_NOT_SUPPORTED:
81 err = MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT;
85 err = MM_UTIL_ERROR_INVALID_OPERATION;
89 mm_util_warn("convert err(%d) from status(%d)", err, status);
94 static size_t __get_decoding_threads_configuration(void)
96 static int num_of_decoding_threads = -1;
98 if (num_of_decoding_threads != -1)
99 return num_of_decoding_threads;
101 return (size_t)(num_of_decoding_threads = mm_util_ini_get_int(INI_CATEGORY_JPEG_XL, INI_ITEM_NUM_OF_DECODING_TRHEADS, DEFAULT_THREAD));
104 static size_t __get_encoding_threads_configuration(void)
106 static int num_of_encoding_threads = -1;
108 if (num_of_encoding_threads != -1)
109 return num_of_encoding_threads;
111 return (size_t)(num_of_encoding_threads = mm_util_ini_get_int(INI_CATEGORY_JPEG_XL, INI_ITEM_NUM_OF_ENCODING_TRHEADS, DEFAULT_THREAD));
114 static int __set_basic_info_from_pixel_format(JxlBasicInfo *basic_info, const JxlPixelFormat *pixel_format)
116 mm_util_retvm_if(!basic_info, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid basic_info");
117 mm_util_retvm_if(!pixel_format, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid pixel_format");
119 switch (pixel_format->data_type) {
121 basic_info->bits_per_sample = 8;
122 basic_info->exponent_bits_per_sample = 0;
125 mm_util_error("not supported data_type(%d)", pixel_format->data_type);
126 return MM_UTIL_ERROR_INVALID_PARAMETER;
129 basic_info->num_color_channels = (pixel_format->num_channels < 3) ? 1 : 3;
130 if (pixel_format->num_channels == 2 || pixel_format->num_channels == 4) {
131 basic_info->alpha_exponent_bits = basic_info->exponent_bits_per_sample;
132 basic_info->alpha_bits = basic_info->bits_per_sample;
133 basic_info->num_extra_channels = 1;
135 basic_info->alpha_exponent_bits = 0;
136 basic_info->alpha_bits = 0;
139 mm_util_info("bits_per_sample = %u, exponent_bits_per_sample = %u, num_color_channels = %u, alpha_exponent_bits = %u, alpha_bits = %u, num_extra_channels = %u",
140 basic_info->bits_per_sample, basic_info->exponent_bits_per_sample, basic_info->num_color_channels,
141 basic_info->alpha_exponent_bits, basic_info->alpha_bits, basic_info->num_extra_channels);
143 return MM_UTIL_ERROR_NONE;
146 static int __set_basic_info_from_image(mm_util_image_h decoded, JxlBasicInfo *basic_info, JxlPixelFormat *format, uint8_t **pixels, size_t *pixels_size)
148 mm_image_info_s *mm_image = (mm_image_info_s *)decoded;
150 mm_util_retvm_if(!mm_image_is_valid_image(decoded), MM_UTIL_ERROR_INVALID_PARAMETER, "invalid decoded");
151 mm_util_retvm_if((mm_image->color != MM_UTIL_COLOR_RGB24) && (mm_image->color != MM_UTIL_COLOR_RGBA),
152 MM_UTIL_ERROR_INVALID_PARAMETER, "invalid format %d", mm_image->color);
153 mm_util_retvm_if(!format, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid format");
154 mm_image_debug_image(decoded, "SOURCE");
156 basic_info->xsize = (uint32_t)mm_image->width;
157 basic_info->ysize = (uint32_t)mm_image->height;
158 *format = jxl_formats[mm_image->color];
159 *pixels = mm_image->data;
160 *pixels_size = (mm_image->size == TEMP_DATA_SIZE) ? (size_t)(mm_image->width * mm_image->height * format->num_channels) : mm_image->size;
162 return MM_UTIL_ERROR_NONE;
165 static int __mm_util_decode_jpegxl(const void *buf, size_t buf_size, mm_util_color_format_e format, mm_util_image_h *decoded_image)
167 int ret = MM_UTIL_ERROR_NONE;
168 JxlDecoderStatus status = JXL_DEC_ERROR;
169 JxlDecoder *jxl_dec = NULL;
170 void *jxl_thread = NULL;
171 JxlPixelFormat *jxl_format = &jxl_formats[format];
173 uint8_t* pixels = NULL;
174 size_t pixels_size = 0;
176 mm_util_retvm_if(!buf, MM_UTIL_ERROR_INVALID_PARAMETER, "buf is null");
177 mm_util_retvm_if(buf_size == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid buf_size %zu", buf_size);
178 mm_util_retvm_if((format != MM_UTIL_COLOR_RGB24) && (format != MM_UTIL_COLOR_RGBA),
179 MM_UTIL_ERROR_INVALID_PARAMETER, "invalid format %d", format);
180 mm_util_retvm_if(!decoded_image, MM_UTIL_ERROR_INVALID_PARAMETER, "decoded_image is null");
184 jxl_dec = JxlDecoderCreate(NULL);
186 mm_util_error("failed to JxlDecoderCreate");
190 jxl_thread = JxlThreadParallelRunnerCreate(NULL, __get_decoding_threads_configuration());
192 mm_util_error("failed to JxlThreadParallelRunnerCreate");
196 status = JxlDecoderSetParallelRunner(jxl_dec, JxlThreadParallelRunner, jxl_thread);
197 if (status != JXL_DEC_SUCCESS) {
198 mm_util_error("failed to JxlDecoderSetParallelRunner(%d)", status);
202 // set the events for process to invoke
203 status = JxlDecoderSubscribeEvents(jxl_dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE);
204 if (status != JXL_DEC_SUCCESS) {
205 mm_util_error("failed to JxlDecoderSubscribeEvents(%d)", status);
209 status = JxlDecoderSetInput(jxl_dec, buf, buf_size);
210 if (status != JXL_DEC_SUCCESS) {
211 mm_util_error("failed to JxlDecoderSetInput(%d)", status);
215 // process to start container parsing
216 status = JxlDecoderProcessInput(jxl_dec);
217 if (status != JXL_DEC_BASIC_INFO) {
218 mm_util_error("failed to JxlDecoderProcessInput to start parsing (%d)", status);
222 status = JxlDecoderImageOutBufferSize(jxl_dec, jxl_format, &pixels_size);
223 if (status != JXL_DEC_SUCCESS) {
224 mm_util_error("failed to JxlDecoderImageOutBufferSize(%d)", status);
228 status = JxlDecoderGetBasicInfo(jxl_dec, &info);
229 if (status != JXL_DEC_SUCCESS) {
230 mm_util_error("failed to JxlDecoderGetBasicInfo(%d)", status);
234 // calculate the size of output buffer
235 pixels = g_malloc0(pixels_size);
237 // process to get frame
238 status = JxlDecoderProcessInput(jxl_dec);
239 if (status != JXL_DEC_FRAME) {
240 mm_util_error("failed to JxlDecoderProcessInput to get frame (%d)", status);
244 // process to set output buffer
245 status = JxlDecoderProcessInput(jxl_dec);
246 if (status != JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
247 mm_util_error("failed to JxlDecoderProcessInput to set output buffer (%d)", status);
251 status = JxlDecoderSetImageOutBuffer(jxl_dec, jxl_format, pixels, pixels_size);
252 if (status != JXL_DEC_SUCCESS) {
253 mm_util_error("failed to JxlDecoderSetImageOutBuffer(%d)", status);
257 // process to get full image
258 status = JxlDecoderProcessInput(jxl_dec);
259 if (status != JXL_DEC_FULL_IMAGE) {
260 mm_util_error("failed to JxlDecoderProcessInput to get full image (%d)", status);
264 // process to finish decoding
265 status = JxlDecoderProcessInput(jxl_dec);
266 if (status != JXL_DEC_SUCCESS) {
267 mm_util_error("failed to JxlDecoderProcessInput to finish decoding (%d)", status);
271 ret = mm_image_create_image(info.xsize, info.ysize, format, pixels, pixels_size, decoded_image);
272 if (ret != MM_UTIL_ERROR_NONE) {
273 mm_util_error("failed to mm_image_create_image");
277 mm_image_debug_image(*decoded_image, "RESULT");
283 JxlThreadParallelRunnerDestroy(jxl_thread);
284 JxlDecoderDestroy(jxl_dec);
288 return __convert_dec_error(status);
291 static int __mm_util_encode_jpegxl(mm_util_image_h decoded_image, mm_util_enc_opt_t *enc_opt, void **buf, size_t *buf_size)
293 int ret = MM_UTIL_ERROR_NONE;
294 JxlEncoderStatus status = JXL_ENC_ERROR;
295 JxlEncoder *jxl_enc = NULL;
296 void *jxl_thread = NULL;
297 JxlPixelFormat jxl_format;
298 JxlBasicInfo basic_info;
299 JxlColorEncoding color_encoding;
300 JxlEncoderFrameSettings *frame_settings = NULL;
301 uint8_t *pixels = NULL;
302 size_t pixels_size = 0;
303 size_t compressed_size = 64;
304 uint8_t *compressed = NULL;
305 uint8_t *next_out = NULL;
306 size_t avail_out = 0;
308 mm_util_retvm_if(!buf, MM_UTIL_ERROR_INVALID_PARAMETER, "buf is null");
309 mm_util_retvm_if(!buf_size, MM_UTIL_ERROR_INVALID_PARAMETER, "buf_size is null");
313 JxlEncoderInitBasicInfo(&basic_info);
315 ret = __set_basic_info_from_image(decoded_image, &basic_info,
316 &jxl_format, &pixels, &pixels_size);
317 if (ret != MM_UTIL_ERROR_NONE) {
318 mm_util_error("failed to __set_basic_info_from_image");
322 ret = __set_basic_info_from_pixel_format(&basic_info, &jxl_format);
323 if (ret != MM_UTIL_ERROR_NONE) {
324 mm_util_error("failed to __set_basic_info_from_pixel_format");
328 basic_info.uses_original_profile = (enc_opt && enc_opt->lossless) ? JXL_TRUE : JXL_FALSE;
330 jxl_enc = JxlEncoderCreate(NULL);
332 mm_util_error("failed to JxlEncoderCreate");
336 jxl_thread = JxlThreadParallelRunnerCreate(NULL, __get_encoding_threads_configuration());
338 mm_util_error("failed to JxlThreadParallelRunnerCreate");
342 status = JxlEncoderSetParallelRunner(jxl_enc, JxlThreadParallelRunner, jxl_thread);
343 if (status != JXL_ENC_SUCCESS) {
344 mm_util_error("failed to JxlDecoderSetParallelRunner(%d)", status);
348 status = JxlEncoderUseContainer(jxl_enc, JXL_TRUE);
349 if (status != JXL_ENC_SUCCESS) {
350 mm_util_error("failed to JxlEncoderUseContainer(%d)", status);
354 status = JxlEncoderSetBasicInfo(jxl_enc, &basic_info);
355 if (status != JXL_ENC_SUCCESS) {
356 mm_util_error("failed to JxlEncoderSetBasicInfo(%d)", status);
360 JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/JXL_FALSE);
361 status = JxlEncoderSetColorEncoding(jxl_enc, &color_encoding);
362 if (status != JXL_ENC_SUCCESS) {
363 mm_util_error("failed to JxlEncoderSetColorEncoding(%d)", status);
367 // set default frame_settings
368 // 'frame_settings' will be destroyed when 'JxlEncoderDestroy' is called
369 frame_settings = JxlEncoderFrameSettingsCreate(jxl_enc, NULL);
372 status = JxlEncoderSetFrameLossless(frame_settings, basic_info.uses_original_profile);
373 if (status != JXL_ENC_SUCCESS) {
374 mm_util_error("failed to JxlEncoderSetFrameLossless(%d)", status);
378 status = JxlEncoderAddImageFrame(frame_settings, &jxl_format, pixels, pixels_size);
379 if (status != JXL_ENC_SUCCESS) {
380 mm_util_error("failed to JxlEncoderAddImageFrame(%d)", status);
384 JxlEncoderCloseInput(jxl_enc);
386 compressed = g_malloc0(compressed_size);
387 next_out = compressed;
388 avail_out = compressed_size;
390 status = JXL_ENC_NEED_MORE_OUTPUT;
391 while (status == JXL_ENC_NEED_MORE_OUTPUT) {
392 status = JxlEncoderProcessOutput(jxl_enc, &next_out, &avail_out);
393 mm_util_debug("JxlEncoderProcessOutput status(%d)", status);
394 if (status == JXL_ENC_NEED_MORE_OUTPUT) {
395 size_t offset = next_out - compressed;
396 compressed_size *= 2;
397 compressed = g_realloc(compressed, compressed_size);
398 next_out = compressed + offset;
399 avail_out = compressed_size - offset;
403 mm_util_warn("JxlEncoderProcessOutput was completed");
405 if (status != JXL_ENC_SUCCESS) {
406 mm_util_error("failed to JxlEncoderProcessOutput(%d)", status);
410 compressed_size -= (next_out - compressed);
411 *buf = g_memdup2(compressed, next_out - compressed);
412 *buf_size = next_out - compressed;
414 mm_util_info("[Success] buffer: %p, size: %zu", *buf, *buf_size);
418 if (status != JXL_ENC_SUCCESS)
419 mm_util_error("JxlEncoderGetError: %d", JxlEncoderGetError(jxl_enc));
422 JxlThreadParallelRunnerDestroy(jxl_thread);
423 JxlEncoderDestroy(jxl_enc);
427 return __convert_enc_error(status);
430 int mm_util_decode_jxl_from_file(const char *path, mm_util_color_format_e format, mm_util_image_h *decoded_image)
432 int ret = MM_UTIL_ERROR_NONE;
433 void *encoded_buf = NULL;
434 size_t encoded_buf_size = 0;
436 mm_util_retvm_if(mm_util_file_read(path, &encoded_buf, &encoded_buf_size) != MM_UTIL_ERROR_NONE, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid path %s:%zu", path, encoded_buf_size);
438 ret =__mm_util_decode_jpegxl(encoded_buf, encoded_buf_size, format, decoded_image);
445 int mm_util_decode_jxl_from_buffer(const void *encoded_buf, size_t encoded_buf_size, mm_util_color_format_e format, mm_util_image_h *decoded_image)
447 return __mm_util_decode_jpegxl(encoded_buf, encoded_buf_size, format, decoded_image);
450 int mm_util_encode_jxl_to_file(mm_util_image_h decoded_image, mm_util_enc_opt_h enc_opt, const char *path)
452 int ret = MM_UTIL_ERROR_NONE;
453 void *encoded_buf = NULL;
454 size_t encoded_buf_size = 0;
456 ret = __mm_util_encode_jpegxl(decoded_image, (mm_util_enc_opt_t *)enc_opt, &encoded_buf, &encoded_buf_size);
457 if (ret != MM_UTIL_ERROR_NONE) {
458 mm_util_error("failed to __mm_util_encode_jxl(%d)", ret);
462 ret = mm_util_file_write(path, encoded_buf, encoded_buf_size);
463 if (ret != MM_UTIL_ERROR_NONE)
464 mm_util_error("failed to __write_file(%d)", ret);
471 int mm_util_encode_jxl_to_buffer(mm_util_image_h decoded_image, mm_util_enc_opt_h enc_opt, void **encoded_buf, size_t *encoded_buf_size)
473 return __mm_util_encode_jpegxl(decoded_image, (mm_util_enc_opt_t *)enc_opt, encoded_buf, encoded_buf_size);