2003-03-18 Lutz Mueller <lutz@users.sourceforge.net>
[platform/upstream/libexif.git] / libexif / exif-loader.c
1 #include <config.h>
2 #include "exif-loader.h"
3
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include <libjpeg/jpeg-marker.h>
8
9 typedef enum {
10         EL_READ = 0,
11         EL_READ_SIZE_HIGH_BYTE,
12         EL_READ_SIZE_LOW_BYTE,
13         EL_SKIP_BYTES,
14         EL_EXIF_FOUND,
15         EL_FAILED
16 } ExifLoaderState;
17
18 struct _ExifLoader {
19         ExifLoaderState state;
20
21         int      size;
22         int      last_marker;
23         unsigned char *buf;
24         int      bytes_read;
25
26         unsigned int ref_count;
27 };
28
29 #undef  MIN
30 #define MIN(a, b)  (((a) < (b)) ? (a) : (b))
31
32 /* This function imitates code from libexif, written by Lutz
33  * Müller. See libexif/exif-data.c:exif_data_new_from_file. Here, it
34  * can cope with a sequence of data chunks.
35  */
36 unsigned char
37 exif_loader_write (ExifLoader *eld, unsigned char *buf, unsigned int len)
38 {
39         int i;
40         int len_remain;
41
42         if (!eld) return 0;
43         if (eld->state == EL_FAILED) return 0;
44         if (eld->size && eld->bytes_read == eld->size) return 0;
45
46         for (i = 0; (i < len) && (eld->state != EL_EXIF_FOUND) &&
47                                  (eld->state != EL_FAILED); i++) {
48
49                 switch (eld->state) {
50                 case EL_SKIP_BYTES:
51                         eld->size--;
52                         if (eld->size == 0) {
53                                 eld->state = EL_READ;
54                         }
55                         break;
56                         
57                 case EL_READ_SIZE_HIGH_BYTE:
58                         eld->size = buf [i] << 8;
59                         eld->state = EL_READ_SIZE_LOW_BYTE;
60                         break;
61                         
62                 case EL_READ_SIZE_LOW_BYTE:
63                         eld->size |= buf [i];
64
65                         switch (eld->last_marker) {
66                         case JPEG_MARKER_APP0:
67                                 eld->state = EL_SKIP_BYTES;
68                                 break;
69
70                         case JPEG_MARKER_APP1:
71                                 eld->state = EL_EXIF_FOUND;
72                                 break;
73
74                         case 0:
75                                 /*
76                                  * Assume that we are reading EXIF data. 
77                                  * This should probably be verified by reading
78                                  * some bytes ahead.
79                                  */
80                                 eld->state = EL_EXIF_FOUND;
81                                 break;
82                         default:
83                                 return 0;
84                         }
85
86                         eld->last_marker = 0;
87                         break;
88
89                 default:
90                         if (buf[i] != 0xff) {
91                                 if (buf [i] == JPEG_MARKER_APP0 ||
92                                     buf [i] == JPEG_MARKER_APP1) {
93                                         eld->state = EL_READ_SIZE_HIGH_BYTE;
94                                         eld->last_marker = buf [i];
95
96                                 } else if (buf [i] == JPEG_MARKER_SOI) {
97                                         /* Nothing */
98                                 } else {
99                                         /* Assume that we are reading EXIF
100                                          * data. This should probably be
101                                          * verified by reading some bytes
102                                          * ahead.
103                                          */
104                                         eld->last_marker = JPEG_MARKER_APP1;
105                                         eld->state = EL_READ_SIZE_HIGH_BYTE;
106                                         i--;
107                                 }
108                         }
109                 }
110         }
111
112         len_remain = len - i;
113         if (!len_remain) return 1;
114
115         if (eld->state == EL_EXIF_FOUND && len_remain > 0) {
116                 if (eld->buf == NULL) {
117                         eld->buf = malloc (sizeof (unsigned char) * eld->size);
118                         eld->bytes_read = 0;
119                 }
120
121                 if (eld->bytes_read < eld->size) {
122                         int cp_len;
123
124                         /* the number of bytes we need to copy */
125                         cp_len = MIN (eld->size - eld->bytes_read, len_remain);
126                         
127                         if ((cp_len + eld->bytes_read) > eld->size) return 1;
128
129                         /* Copy memory */
130                         memcpy (eld->buf + eld->bytes_read, &buf[i], cp_len);
131                         eld->bytes_read += cp_len;
132                 }
133         }
134
135         return 1;
136 }
137
138 ExifLoader *
139 exif_loader_new (void)
140 {
141         ExifLoader *loader = malloc (sizeof (ExifLoader));
142
143         memset (loader, 0, sizeof (ExifLoader));
144         
145         return loader;
146 }
147
148 void
149 exif_loader_ref (ExifLoader *loader)
150 {
151         if (loader) loader->ref_count++;
152 }
153
154 void
155 exif_loader_unref (ExifLoader *loader)
156 {
157         if (!loader) return;
158         if (!--loader->ref_count) {
159                 exif_loader_reset (loader);
160                 free (loader);
161         }
162 }
163
164 void
165 exif_loader_reset (ExifLoader *loader)
166 {
167         if (!loader) return;
168         free (loader->buf); loader->buf = NULL;
169         loader->size = 0;
170         loader->bytes_read = 0;
171         loader->last_marker = 0;
172         loader->state = 0;
173 }
174
175 ExifData *
176 exif_loader_get_data (ExifLoader *loader)
177 {
178         return exif_data_new_from_data (loader->buf, loader->bytes_read);
179 }