initial upload
[apps/native/smart-surveillance-camera.git] / src / exif.c
1  /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
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 <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <libexif/exif-loader.h>
22 #include <libexif/exif-utils.h>
23 #include <libexif/exif-data.h>
24 #include "log.h"
25
26 #define ASCII_COMMENT_HEADER "ASCII\0\0\0"
27 // #define CHECK_EXIF_BEFOR_CREATE
28
29 static int check_exif_from_data(const unsigned char *img, unsigned int size)
30 {
31 #ifdef CHECK_EXIF_BEFOR_CREATE
32         ExifLoader *loader = NULL;
33         unsigned char ret = 0;
34         const unsigned char *buf = NULL;
35         unsigned int b_size = 0;
36
37         retv_if(!img, -1);
38         retv_if(size == 0, -1);
39
40         loader = exif_loader_new();
41         retv_if(!loader, -1);
42
43         ret = exif_loader_write(loader, img, size);
44         if (ret) {
45                 _E("failed to loader write");
46                 exif_loader_unref(loader);
47                 return -1;
48         }
49
50         exif_loader_get_buf(loader, &buf, &b_size);
51         if (buf) {
52                 _E("image has already exif data [%u]", b_size);
53                 exif_loader_unref(loader);
54                 return -1;
55         }
56
57         exif_loader_unref(loader);
58 #endif /* CHECK_EXIF_BEFOR_CREATE */
59
60         return 0;
61 }
62
63 static ExifEntry *
64 add_tag_by_init(ExifData *exif, ExifIfd ifd, ExifTag tag)
65 {
66         ExifEntry *entry = NULL;
67
68         retv_if(!exif, NULL);
69
70         entry = exif_content_get_entry(exif->ifd[ifd], tag);
71         if (!entry) {
72                 entry = exif_entry_new();
73                 retv_if(!entry, NULL);
74
75                 exif_entry_initialize(entry, tag);
76
77                 entry->tag = tag;
78                 exif_content_add_entry(exif->ifd[ifd], entry);
79
80                 exif_entry_unref(entry);
81         }
82         return entry;
83 }
84
85 static ExifEntry *
86 add_tag_by_malloc(ExifData *exif, ExifIfd ifd, ExifTag tag, unsigned int size)
87 {
88         ExifEntry *entry = NULL;
89         void *buffer = NULL;
90         ExifMem *mem = NULL;
91
92         retv_if(!exif, NULL);
93         retv_if(size == 0, NULL);
94
95         mem = exif_mem_new_default();
96         retv_if(!mem, NULL);
97
98         entry = exif_entry_new_mem(mem);
99         if (!entry) {
100                 _E("failed to exif_entry_new_mem()");
101                 exif_mem_unref(mem);
102                 return NULL;
103         }
104
105         buffer = exif_mem_alloc(mem, size);
106         if (!buffer) {
107                 _E("failed to exif_mem_alloc()");
108                 exif_mem_unref(mem);
109                 exif_entry_unref(entry);
110                 return NULL;
111         }
112
113         entry->data = buffer;
114         entry->size = size;
115         entry->tag = tag;
116         entry->components = size;
117         entry->format = EXIF_FORMAT_UNDEFINED;
118         exif_content_add_entry (exif->ifd[ifd], entry);
119
120         exif_mem_unref(mem);
121         exif_entry_unref(entry);
122
123         return entry;
124 }
125
126 static ExifEntry *
127 add_tag_user_comment(ExifData *exif, const char *comment, unsigned int length)
128 {
129         ExifEntry *entry = NULL;
130         size_t header_size = 0;
131
132         retv_if(!exif, NULL);
133         retv_if(!comment, NULL);
134         retv_if(length == 0, NULL);
135
136         header_size = sizeof(ASCII_COMMENT_HEADER) -1;
137
138         entry = add_tag_by_malloc(exif, EXIF_IFD_EXIF, EXIF_TAG_USER_COMMENT,
139                                 header_size + length);
140
141         retv_if(!entry, NULL);
142
143         memcpy(entry->data, ASCII_COMMENT_HEADER, header_size);
144         memcpy(entry->data + header_size, comment, length);
145
146         return entry;
147 }
148
149 static int
150 create_exif_data(const unsigned char *jpg_data, unsigned int jpg_size,
151                 unsigned int jpg_width, unsigned int jpg_height,
152                 const char *user_comment, unsigned int comment_len,
153                 unsigned char **exif_data, unsigned int *exif_size)
154 {
155
156         unsigned char *data;
157         unsigned int data_len;
158         ExifEntry *ee = NULL;
159         ExifData *exif = NULL;
160
161         retv_if(!jpg_data, -1);
162         retv_if(!exif_data, -1);
163         retv_if(!exif_size, -1);
164
165         if (check_exif_from_data(jpg_data, jpg_size))
166                 return -1;
167
168         exif = exif_data_new();
169         retv_if(!exif, -1);
170
171         exif_data_set_option(exif, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
172         exif_data_set_data_type(exif, EXIF_DATA_TYPE_COMPRESSED);
173         exif_data_set_byte_order(exif, EXIF_BYTE_ORDER_INTEL);
174
175         exif_data_fix(exif);
176
177         ee = add_tag_by_init(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION);
178         exif_set_long(ee->data, EXIF_BYTE_ORDER_INTEL, jpg_width);
179
180         ee = add_tag_by_init(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION);
181         exif_set_long(ee->data, EXIF_BYTE_ORDER_INTEL, jpg_height);
182
183         ee = add_tag_user_comment(exif, user_comment, comment_len);
184
185         exif_data_save_data(exif, &data, &data_len);
186         if (!data) {
187                 _E("failed to get exif data");
188                 exif_data_unref(exif);
189                 return -1;
190         }
191         exif_data_unref(exif);
192
193         *exif_data = data;
194         *exif_size = data_len;
195
196         return 0;
197 }
198
199 static int
200 save_jpeg_file(const char *output_file,
201                 const unsigned char *jpg_data, unsigned int jpg_size)
202 {
203         FILE *fp = NULL;
204         retv_if(!output_file, -1);
205         retv_if(!jpg_data, -1);
206         retv_if(jpg_size <= 2, -1);
207
208         fp = fopen(output_file, "wb");
209         retv_if(!fp, -1);
210
211         if (1 != fwrite(jpg_data, jpg_size, 1, fp)) {
212                 _E("failed to write jpg data");
213                 fclose(fp);
214                 return -1;
215         }
216
217         fflush(fp);
218         fclose(fp);
219
220         _D("file saved [%s]", output_file);
221
222         return 0;
223 }
224
225 static int
226 save_jpeg_file_with_exif(const char *output_file,
227                 const unsigned char *jpg_data, unsigned int jpg_size,
228                 unsigned char *exif_data, unsigned int exif_size)
229 {
230         FILE *fp = NULL;
231         const unsigned char exif_header[] = { 0xff, 0xd8, 0xff, 0xe1 }; // FFD8 -SOI Maker, FFE1 - APP1 Maker
232         const unsigned short exif_header_length = 4;
233         const unsigned short jpg_offset = 2; // SOI maker offset
234         unsigned short exif_length = 0;
235
236         ((unsigned char *)&exif_length)[0] = (exif_size + 2) >> 8;
237         ((unsigned char *)&exif_length)[1] = (exif_size + 2) & 0x00ff;
238
239         retv_if(!output_file, -1);
240         retv_if(!jpg_data, -1);
241         retv_if(!exif_data, -1);
242         retv_if(jpg_size <= 2, -1);
243         retv_if(exif_size == 0, -1);
244
245         fp = fopen(output_file, "wb");
246         retv_if(!fp, -1);
247
248         if (1 != fwrite(exif_header, exif_header_length, 1, fp)) {
249                 _E("failed to write exif header");
250                 goto ERROR;
251         }
252
253         if (1 != fwrite(&exif_length, sizeof(exif_length), 1, fp)) {
254                 _E("failed to write exif legth");
255                 goto ERROR;
256         }
257
258         if (1 != fwrite(exif_data, exif_size, 1, fp)) {
259                 _E("failed to write exif");
260                 goto ERROR;
261         }
262
263         if (1 != fwrite(jpg_data + jpg_offset, jpg_size - jpg_offset, 1, fp)) {
264                 _E("failed to write jpg data");
265                 goto ERROR;
266         }
267
268         fflush(fp);
269         fclose(fp);
270
271         return 0;
272 ERROR:
273         fclose(fp);
274         unlink(output_file);
275         _E(" failed to save file - [%s]", output_file);
276         return -1;
277 }
278
279 int exif_write_jpg_file_with_comment(const char *output_file,
280                 const unsigned char *jpg_data, unsigned int jpg_size,
281                 unsigned int jpg_width, unsigned int jpg_height,
282                 const char *comment, unsigned int comment_len)
283 {
284         int ret = 0;
285         unsigned char *exif_data  = NULL;
286         unsigned int exif_size = 0;
287
288         if (!comment || (comment_len ==  0)) {
289                 _W("There is no comment");
290                 return save_jpeg_file(output_file, jpg_data, jpg_size);
291         }
292
293         ret = create_exif_data(jpg_data, jpg_size, jpg_width, jpg_height,
294                         comment, comment_len, &exif_data, &exif_size);
295         if (ret) {
296                 _E("failed to create_exif_data(), save jpg data only");
297                 return save_jpeg_file(output_file, jpg_data, jpg_size);
298         }
299
300         ret = save_jpeg_file_with_exif(
301                         output_file, jpg_data, jpg_size, exif_data, exif_size);
302
303         free(exif_data);
304         return ret;
305 }