replace g_memdup() to g_memdup2()
[platform/core/multimedia/libmm-utility.git] / webp / mm_util_webp.c
1 /*
2 * Copyright (c) 2020 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
19 #include <glib.h>
20 #include <webp/encode.h>
21 #include <webp/mux.h>
22
23 #include "mm_util_webp.h"
24 #include "mm_util_private.h"
25
26 typedef struct {
27         WebPAnimEncoder *enc;
28         WebPMuxAnimParams anim_params;
29         bool lossless;
30         unsigned int timestamp;
31         unsigned int frame_count;
32 } mm_util_webp_anim_enc_t;
33
34
35 static int __webp_anim_enc_init(mm_util_webp_anim_enc_t *anim_enc, mm_image_info_s *image)
36 {
37         WebPAnimEncoderOptions enc_options;
38
39         mm_util_retvm_if(!anim_enc, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc");
40         mm_util_retvm_if(!image, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid image_info");
41
42         mm_util_retvm_if(!WebPAnimEncoderOptionsInit(&enc_options), MM_UTIL_ERROR_INVALID_OPERATION, "Error! WebPAnimEncoderOptionsInit");
43
44         anim_enc->enc = WebPAnimEncoderNew(image->width, image->height, &enc_options);
45         mm_util_retvm_if(!anim_enc->enc, MM_UTIL_ERROR_INVALID_OPERATION, "Error! WebPAnimEncoderNew");
46
47         anim_enc->timestamp = 0;
48
49         return MM_UTIL_ERROR_NONE;
50 }
51
52 static void __webp_anim_enc_deinit(mm_util_webp_anim_enc_t *anim_enc)
53 {
54         if (!anim_enc)
55                 return;
56
57         WebPAnimEncoderDelete(anim_enc->enc);
58         anim_enc->frame_count = 0;
59         anim_enc->timestamp = 0;
60         anim_enc->enc = NULL;
61 }
62
63 static int __mm_image_info_to_webp_picture(mm_image_info_s *image, WebPPicture *picture)
64 {
65         size_t idx = 0;
66         unsigned char *ptr = NULL, *a = NULL, *r = NULL, *g = NULL, *b = NULL;
67         unsigned int bpp = 4;
68
69         mm_util_retvm_if(!image, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid image");
70         mm_util_retvm_if(!picture, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid picture");
71
72         picture->width = image->width;
73         picture->height = image->height;
74         // use ARGB because of libwebp recommendation(YUV incurs loss).
75         picture->use_argb = 1;
76
77         mm_util_retvm_if(!WebPPictureAlloc(picture), MM_UTIL_ERROR_INVALID_OPERATION, "Error! WebPPictureAlloc");
78
79         // convert 'unsigned char'(LSB) type to 'uint32_t'(MSB) type
80         // If we did not change the data type, the color of encoded webp was broken.
81         mm_util_debug("image colorspace: %d", image->color);
82
83         // make uint32_t(MSB) 'ARGB' from unsigned char(LSB) 'ARGB/BGRA/RGBA'.
84         ptr = (unsigned char*)(image->data);
85         switch(image->color) {
86         case MM_UTIL_COLOR_ARGB:
87                 a = ptr;
88                 r = ptr + 1;
89                 g = ptr + 2;
90                 b = ptr + 3;
91                 bpp = 4;
92                 break;
93         case MM_UTIL_COLOR_BGRA:
94                 a = ptr + 3;
95                 r = ptr + 2;
96                 g = ptr + 1;
97                 b = ptr;
98                 bpp = 4;
99                 break;
100         case MM_UTIL_COLOR_RGBA:
101                 a = ptr + 3;
102                 r = ptr;
103                 g = ptr + 1;
104                 b = ptr + 2;
105                 bpp = 4;
106                 break;
107         default:
108                 mm_util_error("not supported colorspace %d", image->color);
109                 WebPPictureFree(picture);
110                 return MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT;
111         }
112
113         while(idx < (size_t)(picture->argb_stride * picture->height)) {
114                 picture->argb[idx++] = ((*a << 24) | (*r << 16) | (*g << 8) | (*b));
115
116                 a += bpp;
117                 r += bpp;
118                 g += bpp;
119                 b += bpp;
120         }
121
122         return MM_UTIL_ERROR_NONE;
123 }
124
125 static int __mm_util_webp_anim_enc_save(mm_util_webp_anim_enc_t *anim_enc, WebPData *webp_data)
126 {
127         WebPMux *mux = NULL;
128         WebPMuxError err = WEBP_MUX_OK;
129
130         mm_util_retvm_if(!anim_enc, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc");
131         mm_util_retvm_if(!webp_data, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid webp_data");
132
133         mm_util_fenter();
134
135         // add NULL to end animation
136         if (!WebPAnimEncoderAdd(anim_enc->enc, NULL, anim_enc->timestamp, NULL)) {
137                 mm_util_error("Error! WebPAnimEncoderAdd(%s)", WebPAnimEncoderGetError(anim_enc->enc));
138                 return MM_UTIL_ERROR_INVALID_OPERATION;
139         }
140         if (!WebPAnimEncoderAssemble(anim_enc->enc, webp_data)) {
141                 mm_util_error("Error! WebPAnimEncoderAssemble(%s)", WebPAnimEncoderGetError(anim_enc->enc));
142                 return MM_UTIL_ERROR_INVALID_OPERATION;
143         }
144
145         // add animation container into webp
146         mux = WebPMuxCreate(webp_data, 1);
147         if (!mux) {
148                 mm_util_error("Error! WebPMuxCreate");
149                 return MM_UTIL_ERROR_INVALID_OPERATION;
150         }
151
152         err = WebPMuxSetAnimationParams(mux, &anim_enc->anim_params);
153         if (err != WEBP_MUX_OK) {
154                 mm_util_error("Error! WebPMuxSetAnimationParams(%d)", err);
155                 WebPMuxDelete(mux);
156                 return MM_UTIL_ERROR_INVALID_OPERATION;
157         }
158
159         err = WebPMuxAssemble(mux, webp_data);
160         if (err != WEBP_MUX_OK) {
161                 mm_util_error("Error! WebPMuxAssemble(%d)", err);
162                 WebPMuxDelete(mux);
163                 return MM_UTIL_ERROR_INVALID_OPERATION;
164         }
165
166         WebPMuxDelete(mux);
167
168         mm_util_fleave();
169
170         return MM_UTIL_ERROR_NONE;
171 }
172
173 int mm_util_webp_anim_enc_create(mm_util_webp_anim_enc_h *anim_enc_h)
174 {
175         mm_util_webp_anim_enc_t *anim_enc = NULL;
176
177         mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
178
179         anim_enc = g_new0(mm_util_webp_anim_enc_t, 1);
180         anim_enc->anim_params.bgcolor = 0xFFFFFFFF;     // white
181         anim_enc->anim_params.loop_count = 0;           // infinite
182         anim_enc->lossless = false;                     // lossy
183
184         *anim_enc_h = anim_enc;
185
186         return MM_UTIL_ERROR_NONE;
187 }
188
189 int mm_util_webp_anim_enc_set_bgcolor(mm_util_webp_anim_enc_h anim_enc_h, unsigned char alpha, unsigned char red, unsigned char green, unsigned char blue)
190 {
191         mm_util_webp_anim_enc_t *anim_enc = (mm_util_webp_anim_enc_t *)anim_enc_h;
192
193         mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
194
195         // background color of the animation, default is white
196         anim_enc->anim_params.bgcolor = (uint32_t)((alpha << 24) | (red << 16) | (green << 8) | blue);
197
198         return MM_UTIL_ERROR_NONE;
199 }
200
201 int mm_util_webp_anim_enc_set_loop_count(mm_util_webp_anim_enc_h anim_enc_h, unsigned int loop_count)
202 {
203         mm_util_webp_anim_enc_t *anim_enc = (mm_util_webp_anim_enc_t *)anim_enc_h;
204
205         mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
206
207         // number of times to repeat the animation, default is 0[infinite]
208         anim_enc->anim_params.loop_count = (int)loop_count;
209
210         return MM_UTIL_ERROR_NONE;
211 }
212
213 int mm_util_webp_anim_enc_set_lossless(mm_util_webp_anim_enc_h anim_enc_h, bool lossless)
214 {
215         mm_util_webp_anim_enc_t *anim_enc = (mm_util_webp_anim_enc_t *)anim_enc_h;
216
217         mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
218
219         // lossless, default is false due to performance
220         anim_enc->lossless = lossless;
221
222         return MM_UTIL_ERROR_NONE;
223 }
224
225 int mm_util_webp_anim_enc_add_image(mm_util_webp_anim_enc_h anim_enc_h, mm_util_image_h image)
226 {
227         int ret = MM_UTIL_ERROR_NONE;
228         mm_util_webp_anim_enc_t *anim_enc = (mm_util_webp_anim_enc_t *)anim_enc_h;
229         mm_image_info_s *frame = (mm_image_info_s *)image;
230         WebPPicture picture;
231         WebPConfig config;
232
233         mm_util_fenter();
234
235         mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
236         mm_util_retvm_if(!image, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid image");
237
238         mm_util_retvm_if(!WebPPictureInit(&picture), MM_UTIL_ERROR_INVALID_OPERATION, "Error! WebPPictureInit");
239         mm_util_retvm_if(!WebPConfigInit(&config), MM_UTIL_ERROR_INVALID_OPERATION, "Error! WebPConfigInit");
240
241         config.lossless = anim_enc->lossless;
242
243         ret = __mm_image_info_to_webp_picture(frame, &picture);
244         mm_util_retvm_if(ret != MM_UTIL_ERROR_NONE, ret, "Error! __mm_image_info_to_webp_picture(%d)", ret);
245
246         // if first frame, create animation container of webp
247         if (!anim_enc->enc) {
248                 ret = __webp_anim_enc_init(anim_enc, frame);
249                 if (ret != MM_UTIL_ERROR_NONE) {
250                         mm_util_error("Error! __webp_anim_enc_init(%d)", ret);
251                         WebPPictureFree(&picture);
252                         return ret;
253                 }
254         }
255
256         // add picture into animation container
257         if (!WebPAnimEncoderAdd(anim_enc->enc, &picture, (int)anim_enc->timestamp, &config)) {
258                 mm_util_error("Error! WebPAnimEncoderAdd(%s)", WebPAnimEncoderGetError(anim_enc->enc));
259                 __webp_anim_enc_deinit(anim_enc);
260                 ret = MM_UTIL_ERROR_INVALID_OPERATION;
261         } else {
262                 // convert the delay between frames to timestamp of each frame
263                 anim_enc->timestamp += frame->delay_time;
264                 anim_enc->frame_count++;
265         }
266
267         WebPPictureFree(&picture);
268
269         mm_util_fleave();
270
271         return ret;
272 }
273
274 int mm_util_webp_anim_enc_save_to_file(mm_util_webp_anim_enc_h anim_enc_h, const char *path)
275 {
276         int ret = MM_UTIL_ERROR_NONE;
277         mm_util_webp_anim_enc_t *anim_enc = (mm_util_webp_anim_enc_t *)anim_enc_h;
278         WebPData webp_data;
279         GError *g_error = NULL;
280
281         mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
282         mm_util_retvm_if(anim_enc->frame_count == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid frame_count");
283
284         WebPDataInit(&webp_data);
285
286         ret = __mm_util_webp_anim_enc_save(anim_enc, &webp_data);
287         if (ret != MM_UTIL_ERROR_NONE) {
288                 mm_util_error("Error! __mm_util_webp_anim_enc_save(%d)", ret);
289                 goto END;
290         }
291
292         if (!g_file_set_contents(path, (gchar *)webp_data.bytes, (gssize)webp_data.size, &g_error))
293                 mm_util_error("Error! g_file_set_contents(%s)", (g_error ? g_error->message : "none"));
294
295         if (g_error)
296                 g_error_free(g_error);
297 END:
298
299         WebPDataClear(&webp_data);
300         __webp_anim_enc_deinit(anim_enc);
301
302         return ret;
303 }
304
305 int mm_util_webp_anim_enc_save_to_buffer(mm_util_webp_anim_enc_h anim_enc_h, void **buf, size_t *buf_size)
306 {
307         int ret = MM_UTIL_ERROR_NONE;
308         mm_util_webp_anim_enc_t *anim_enc = (mm_util_webp_anim_enc_t *)anim_enc_h;
309         WebPData webp_data;
310
311         mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
312         mm_util_retvm_if(anim_enc->frame_count == 0, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid frame_count");
313         mm_util_retvm_if(!buf, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid buf");
314         mm_util_retvm_if(!buf_size, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid buf_size");
315
316         WebPDataInit(&webp_data);
317
318         ret = __mm_util_webp_anim_enc_save(anim_enc, &webp_data);
319         if (ret == MM_UTIL_ERROR_NONE) {
320                 *buf = g_memdup2(webp_data.bytes, webp_data.size);
321                 *buf_size = webp_data.size;
322         } else {
323                 mm_util_error("Error! __mm_util_webp_anim_enc_save(%d)", ret);
324         }
325
326         WebPDataClear(&webp_data);
327         __webp_anim_enc_deinit(anim_enc);
328
329         return MM_UTIL_ERROR_NONE;
330 }
331
332 void mm_util_webp_anim_enc_destroy(mm_util_webp_anim_enc_h anim_enc_h)
333 {
334         if (!anim_enc_h)
335                 return;
336
337         __webp_anim_enc_deinit((mm_util_webp_anim_enc_t *)anim_enc_h);
338         g_free(anim_enc_h);
339 }
340
341 anim_enc_module_t *anim_enc_module_register(void)
342 {
343         anim_enc_module_t *module = NULL;
344
345         mm_util_fenter();
346
347         module = g_new0(anim_enc_module_t, 1);
348
349         module->create = (ANIM_ENC_CREATE)mm_util_webp_anim_enc_create;
350         module->set_bgcolor = (ANIM_ENC_SET_BG_COLOR)mm_util_webp_anim_enc_set_bgcolor;
351         module->set_loop_count = (ANIM_ENC_SET_LOOP_COUNT)mm_util_webp_anim_enc_set_loop_count;
352         module->set_lossless = (ANIM_ENC_SET_LOSSLESS)mm_util_webp_anim_enc_set_lossless;
353         module->add_image = (ANIM_ENC_ADD_IMAGE)mm_util_webp_anim_enc_add_image;
354         module->save_to_file = (ANIM_ENC_SAVE_TO_FILE)mm_util_webp_anim_enc_save_to_file;
355         module->save_to_buffer = (ANIM_ENC_SAVE_TO_BUFFER)mm_util_webp_anim_enc_save_to_buffer;
356         module->destroy = (ANIM_ENC_DESTROY)mm_util_webp_anim_enc_destroy;
357
358         mm_util_fleave();
359
360         return module;
361 }