Change 'JxlEncoderOptions' to 'JxlEncoderFrameSettings'
[platform/core/multimedia/libmm-utility.git] / jxl / mm_util_jxl.c
1 /*
2 * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17 #include <stdio.h>
18 #include <stdint.h>
19 #include <fcntl.h>
20 #include <glib.h>
21 #include <inttypes.h>
22 #include <unistd.h>
23
24 #include <jxl/decode.h>
25 #include <jxl/encode.h>
26 #include <jxl/thread_parallel_runner.h>
27
28 #include "mm_util_jxl.h"
29 #include "mm_util_private.h"
30
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"
35
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
39 };
40
41 static int __convert_dec_error(JxlDecoderStatus status)
42 {
43         int err = MM_UTIL_ERROR_NONE;
44
45         switch (status) {
46         case JXL_DEC_SUCCESS:
47                 err = MM_UTIL_ERROR_NONE;
48                 break;
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;
54                 break;
55         case JXL_DEC_JPEG_NEED_MORE_OUTPUT:
56                 err = MM_UTIL_ERROR_OUT_OF_MEMORY;
57                 break;
58         case JXL_DEC_ERROR:
59         default:
60                 err = MM_UTIL_ERROR_INVALID_OPERATION;
61                 break;
62         }
63
64         mm_util_warn("convert err(%d) from status(%d)", err, status);
65
66         return err;
67 }
68
69 static int __convert_enc_error(JxlEncoderStatus status)
70 {
71         int err = MM_UTIL_ERROR_NONE;
72
73         switch (status) {
74         case JXL_ENC_SUCCESS:
75                 err = MM_UTIL_ERROR_NONE;
76                 break;
77         case JXL_ENC_NEED_MORE_OUTPUT:
78                 err = MM_UTIL_ERROR_OUT_OF_MEMORY;
79                 break;
80         case JXL_ENC_NOT_SUPPORTED:
81                 err = MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT;
82                 break;
83         case JXL_ENC_ERROR:
84         default:
85                 err = MM_UTIL_ERROR_INVALID_OPERATION;
86                 break;
87         }
88
89         mm_util_warn("convert err(%d) from status(%d)", err, status);
90
91         return err;
92 }
93
94 static size_t __get_decoding_threads_configuration(void)
95 {
96         static int num_of_decoding_threads = -1;
97
98         if (num_of_decoding_threads != -1)
99                 return num_of_decoding_threads;
100
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));
102 }
103
104 static size_t __get_encoding_threads_configuration(void)
105 {
106         static int num_of_encoding_threads = -1;
107
108         if (num_of_encoding_threads != -1)
109                 return num_of_encoding_threads;
110
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));
112 }
113
114 static int __set_basic_info_from_pixel_format(JxlBasicInfo *basic_info, const JxlPixelFormat *pixel_format)
115 {
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");
118
119         switch (pixel_format->data_type) {
120         case JXL_TYPE_UINT8:
121                 basic_info->bits_per_sample = 8;
122                 basic_info->exponent_bits_per_sample = 0;
123                 break;
124         default:
125                 mm_util_error("not supported data_type(%d)", pixel_format->data_type);
126                 return MM_UTIL_ERROR_INVALID_PARAMETER;
127         }
128
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;
134         } else {
135                 basic_info->alpha_exponent_bits = 0;
136                 basic_info->alpha_bits = 0;
137         }
138
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);
142
143         return MM_UTIL_ERROR_NONE;
144 }
145
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)
147 {
148         mm_image_info_s *mm_image = (mm_image_info_s *)decoded;
149
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");
155
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;
161
162         return MM_UTIL_ERROR_NONE;
163 }
164
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)
166 {
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];
172         JxlBasicInfo info;
173         uint8_t* pixels = NULL;
174         size_t pixels_size = 0;
175
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");
181
182         mm_util_fenter();
183
184         jxl_dec = JxlDecoderCreate(NULL);
185         if (!jxl_dec) {
186                 mm_util_error("failed to JxlDecoderCreate");
187                 goto Exit;
188         }
189
190         jxl_thread = JxlThreadParallelRunnerCreate(NULL, __get_decoding_threads_configuration());
191         if (!jxl_thread) {
192                 mm_util_error("failed to JxlThreadParallelRunnerCreate");
193                 goto Exit;
194         }
195
196         status = JxlDecoderSetParallelRunner(jxl_dec, JxlThreadParallelRunner, jxl_thread);
197         if (status != JXL_DEC_SUCCESS) {
198                 mm_util_error("failed to JxlDecoderSetParallelRunner(%d)", status);
199                 goto Exit;
200         }
201
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);
206                 goto Exit;
207         }
208
209         status = JxlDecoderSetInput(jxl_dec, buf, buf_size);
210         if (status != JXL_DEC_SUCCESS) {
211                 mm_util_error("failed to JxlDecoderSetInput(%d)", status);
212                 goto Exit;
213         }
214
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);
219                 goto Exit;
220         }
221
222         status = JxlDecoderImageOutBufferSize(jxl_dec, jxl_format, &pixels_size);
223         if (status != JXL_DEC_SUCCESS) {
224           mm_util_error("failed to JxlDecoderImageOutBufferSize(%d)", status);
225           goto Exit;
226         }
227
228         status = JxlDecoderGetBasicInfo(jxl_dec, &info);
229         if (status != JXL_DEC_SUCCESS) {
230           mm_util_error("failed to JxlDecoderGetBasicInfo(%d)", status);
231           goto Exit;
232         }
233
234         // calculate the size of output buffer
235         pixels = g_malloc0(pixels_size);
236
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);
241                 goto Exit;
242         }
243
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);
248                 goto Exit;
249         }
250
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);
254                 goto Exit;
255         }
256
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);
261                 goto Exit;
262         }
263
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);
268                 goto Exit;
269         }
270
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");
274                 goto Exit;
275         }
276
277         mm_image_debug_image(*decoded_image, "RESULT");
278
279 Exit:
280
281         g_free(pixels);
282
283         JxlThreadParallelRunnerDestroy(jxl_thread);
284         JxlDecoderDestroy(jxl_dec);
285
286         mm_util_fleave();
287
288         return __convert_dec_error(status);
289 }
290
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)
292 {
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;
307
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");
310
311         mm_util_fenter();
312
313         JxlEncoderInitBasicInfo(&basic_info);
314
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");
319                 return ret;
320         }
321
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");
325                 return ret;
326         }
327
328         basic_info.uses_original_profile = (enc_opt && enc_opt->lossless) ? JXL_TRUE : JXL_FALSE;
329
330         jxl_enc = JxlEncoderCreate(NULL);
331         if (!jxl_enc) {
332                 mm_util_error("failed to JxlEncoderCreate");
333                 goto Exit;
334         }
335
336         jxl_thread = JxlThreadParallelRunnerCreate(NULL, __get_encoding_threads_configuration());
337         if (!jxl_thread) {
338                 mm_util_error("failed to JxlThreadParallelRunnerCreate");
339                 goto Exit;
340         }
341
342         status = JxlEncoderSetParallelRunner(jxl_enc, JxlThreadParallelRunner, jxl_thread);
343         if (status != JXL_ENC_SUCCESS) {
344                 mm_util_error("failed to JxlDecoderSetParallelRunner(%d)", status);
345                 goto Exit;
346         }
347
348         status = JxlEncoderUseContainer(jxl_enc, JXL_TRUE);
349         if (status != JXL_ENC_SUCCESS) {
350                 mm_util_error("failed to JxlEncoderUseContainer(%d)", status);
351                 goto Exit;
352         }
353
354         status = JxlEncoderSetBasicInfo(jxl_enc, &basic_info);
355         if (status != JXL_ENC_SUCCESS) {
356                 mm_util_error("failed to JxlEncoderSetBasicInfo(%d)", status);
357                 goto Exit;
358         }
359
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);
364                 goto Exit;
365         }
366
367         // set default frame_settings
368         // 'frame_settings' will be destroyed when 'JxlEncoderDestroy' is called
369         frame_settings = JxlEncoderFrameSettingsCreate(jxl_enc, NULL);
370
371         // set lossless
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);
375                 goto Exit;
376         }
377
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);
381                 goto Exit;
382         }
383
384         JxlEncoderCloseInput(jxl_enc);
385
386         compressed = g_malloc0(compressed_size);
387         next_out = compressed;
388         avail_out = compressed_size;
389
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;
400                 }
401         }
402
403         mm_util_warn("JxlEncoderProcessOutput was completed");
404
405         if (status != JXL_ENC_SUCCESS) {
406                 mm_util_error("failed to JxlEncoderProcessOutput(%d)", status);
407                 goto Exit;
408         }
409
410         compressed_size -= (next_out - compressed);
411         *buf = g_memdup2(compressed, next_out - compressed);
412         *buf_size = next_out - compressed;
413
414         mm_util_info("[Success] buffer: %p, size: %zu", *buf, *buf_size);
415
416 Exit:
417
418         if (status != JXL_ENC_SUCCESS)
419                 mm_util_error("JxlEncoderGetError: %d", JxlEncoderGetError(jxl_enc));
420
421         g_free(compressed);
422         JxlThreadParallelRunnerDestroy(jxl_thread);
423         JxlEncoderDestroy(jxl_enc);
424
425         mm_util_fleave();
426
427         return __convert_enc_error(status);
428 }
429
430 int mm_util_decode_jxl_from_file(const char *path, mm_util_color_format_e format, mm_util_image_h *decoded_image)
431 {
432         int ret = MM_UTIL_ERROR_NONE;
433         void *encoded_buf = NULL;
434         size_t encoded_buf_size = 0;
435
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);
437
438         ret =__mm_util_decode_jpegxl(encoded_buf, encoded_buf_size, format, decoded_image);
439
440         g_free(encoded_buf);
441
442         return ret;
443 }
444
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)
446 {
447         return __mm_util_decode_jpegxl(encoded_buf, encoded_buf_size, format, decoded_image);
448 }
449
450 int mm_util_encode_jxl_to_file(mm_util_image_h decoded_image, mm_util_enc_opt_h enc_opt, const char *path)
451 {
452         int ret = MM_UTIL_ERROR_NONE;
453         void *encoded_buf = NULL;
454         size_t encoded_buf_size = 0;
455
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);
459                 return ret;
460         }
461
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);
465
466         g_free(encoded_buf);
467
468         return ret;
469 }
470
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)
472 {
473         return __mm_util_encode_jpegxl(decoded_image, (mm_util_enc_opt_t *)enc_opt, encoded_buf, encoded_buf_size);
474 }