2 * Copyright (c) 2020 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.
20 #include <webp/encode.h>
23 #include "mm_util_webp.h"
24 #include "mm_util_private.h"
28 WebPMuxAnimParams anim_params;
30 unsigned int timestamp;
31 unsigned int frame_count;
32 } mm_util_webp_anim_enc_t;
35 static int __webp_anim_enc_init(mm_util_webp_anim_enc_t *anim_enc, mm_image_info_s *image)
37 WebPAnimEncoderOptions enc_options;
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");
42 mm_util_retvm_if(!WebPAnimEncoderOptionsInit(&enc_options), MM_UTIL_ERROR_INVALID_OPERATION, "Error! WebPAnimEncoderOptionsInit");
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");
47 anim_enc->timestamp = 0;
49 return MM_UTIL_ERROR_NONE;
52 static void __webp_anim_enc_deinit(mm_util_webp_anim_enc_t *anim_enc)
57 WebPAnimEncoderDelete(anim_enc->enc);
58 anim_enc->frame_count = 0;
59 anim_enc->timestamp = 0;
63 static int __mm_image_info_to_webp_picture(mm_image_info_s *image, WebPPicture *picture)
66 unsigned char *ptr = NULL, *a = NULL, *r = NULL, *g = NULL, *b = NULL;
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");
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;
77 mm_util_retvm_if(!WebPPictureAlloc(picture), MM_UTIL_ERROR_INVALID_OPERATION, "Error! WebPPictureAlloc");
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);
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:
93 case MM_UTIL_COLOR_BGRA:
100 case MM_UTIL_COLOR_RGBA:
108 mm_util_error("not supported colorspace %d", image->color);
109 WebPPictureFree(picture);
110 return MM_UTIL_ERROR_NOT_SUPPORTED_FORMAT;
113 while(idx < (size_t)(picture->argb_stride * picture->height)) {
114 picture->argb[idx++] = ((*a << 24) | (*r << 16) | (*g << 8) | (*b));
122 return MM_UTIL_ERROR_NONE;
125 static int __mm_util_webp_anim_enc_save(mm_util_webp_anim_enc_t *anim_enc, WebPData *webp_data)
128 WebPMuxError err = WEBP_MUX_OK;
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");
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;
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;
145 // add animation container into webp
146 mux = WebPMuxCreate(webp_data, 1);
148 mm_util_error("Error! WebPMuxCreate");
149 return MM_UTIL_ERROR_INVALID_OPERATION;
152 err = WebPMuxSetAnimationParams(mux, &anim_enc->anim_params);
153 if (err != WEBP_MUX_OK) {
154 mm_util_error("Error! WebPMuxSetAnimationParams(%d)", err);
156 return MM_UTIL_ERROR_INVALID_OPERATION;
159 err = WebPMuxAssemble(mux, webp_data);
160 if (err != WEBP_MUX_OK) {
161 mm_util_error("Error! WebPMuxAssemble(%d)", err);
163 return MM_UTIL_ERROR_INVALID_OPERATION;
170 return MM_UTIL_ERROR_NONE;
173 int mm_util_webp_anim_enc_create(mm_util_webp_anim_enc_h *anim_enc_h)
175 mm_util_webp_anim_enc_t *anim_enc = NULL;
177 mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
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
184 *anim_enc_h = anim_enc;
186 return MM_UTIL_ERROR_NONE;
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)
191 mm_util_webp_anim_enc_t *anim_enc = (mm_util_webp_anim_enc_t *)anim_enc_h;
193 mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
195 // background color of the animation, default is white
196 anim_enc->anim_params.bgcolor = (uint32_t)((alpha << 24) | (red << 16) | (green << 8) | blue);
198 return MM_UTIL_ERROR_NONE;
201 int mm_util_webp_anim_enc_set_loop_count(mm_util_webp_anim_enc_h anim_enc_h, unsigned int loop_count)
203 mm_util_webp_anim_enc_t *anim_enc = (mm_util_webp_anim_enc_t *)anim_enc_h;
205 mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
207 // number of times to repeat the animation, default is 0[infinite]
208 anim_enc->anim_params.loop_count = (int)loop_count;
210 return MM_UTIL_ERROR_NONE;
213 int mm_util_webp_anim_enc_set_lossless(mm_util_webp_anim_enc_h anim_enc_h, bool lossless)
215 mm_util_webp_anim_enc_t *anim_enc = (mm_util_webp_anim_enc_t *)anim_enc_h;
217 mm_util_retvm_if(!anim_enc_h, MM_UTIL_ERROR_INVALID_PARAMETER, "invalid anim_enc_h");
219 // lossless, default is false due to performance
220 anim_enc->lossless = lossless;
222 return MM_UTIL_ERROR_NONE;
225 int mm_util_webp_anim_enc_add_image(mm_util_webp_anim_enc_h anim_enc_h, mm_util_image_h image)
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;
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");
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");
241 config.lossless = anim_enc->lossless;
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);
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);
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;
262 // convert the delay between frames to timestamp of each frame
263 anim_enc->timestamp += frame->delay_time;
264 anim_enc->frame_count++;
267 WebPPictureFree(&picture);
274 int mm_util_webp_anim_enc_save_to_file(mm_util_webp_anim_enc_h anim_enc_h, const char *path)
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;
279 GError *g_error = NULL;
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");
284 WebPDataInit(&webp_data);
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);
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"));
296 g_error_free(g_error);
299 WebPDataClear(&webp_data);
300 __webp_anim_enc_deinit(anim_enc);
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)
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;
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");
316 WebPDataInit(&webp_data);
318 ret = __mm_util_webp_anim_enc_save(anim_enc, &webp_data);
319 if (ret == MM_UTIL_ERROR_NONE) {
320 *buf = g_memdup(webp_data.bytes, webp_data.size);
321 *buf_size = webp_data.size;
323 mm_util_error("Error! __mm_util_webp_anim_enc_save(%d)", ret);
326 WebPDataClear(&webp_data);
327 __webp_anim_enc_deinit(anim_enc);
329 return MM_UTIL_ERROR_NONE;
332 void mm_util_webp_anim_enc_destroy(mm_util_webp_anim_enc_h anim_enc_h)
337 __webp_anim_enc_deinit((mm_util_webp_anim_enc_t *)anim_enc_h);
341 anim_enc_module_t *anim_enc_module_register(void)
343 anim_enc_module_t *module = NULL;
347 module = g_new0(anim_enc_module_t, 1);
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;