free mpo data
[geeqie.git] / src / jpeg_parser.c
1
2 #include "main.h" 
3 #include "jpeg_parser.h"
4
5 gboolean jpeg_segment_find(guchar *data, guint size,
6                             guchar app_marker, const gchar *magic, guint magic_len,
7                             guint *seg_offset, guint *seg_length)
8 {
9         guchar marker = 0;
10         guint offset = 0;
11         guint length = 0;
12
13         while (marker != JPEG_MARKER_EOI)
14                 {
15                 offset += length;
16                 length = 2;
17
18                 if (offset + 2 >= size ||
19                     data[offset] != JPEG_MARKER) return FALSE;
20
21                 marker = data[offset + 1];
22                 if (marker != JPEG_MARKER_SOI &&
23                     marker != JPEG_MARKER_EOI)
24                         {
25                         if (offset + 4 >= size) return FALSE;
26                         length += ((guint)data[offset + 2] << 8) + data[offset + 3];
27
28                         if (marker == app_marker &&
29                             offset + length < size &&
30                             length >= 4 + magic_len &&
31                             memcmp(data + offset + 4, magic, magic_len) == 0)
32                                 {
33                                 *seg_offset = offset + 4;
34                                 *seg_length = length - 4;
35                                 return TRUE;
36                                 }
37                         }
38                 }
39         return FALSE;
40 }
41
42
43 typedef enum {
44         TIFF_BYTE_ORDER_INTEL,
45         TIFF_BYTE_ORDER_MOTOROLA
46 } TiffByteOrder;
47
48 #define TIFF_TIFD_OFFSET_TAG 0
49 #define TIFF_TIFD_OFFSET_FORMAT 2
50 #define TIFF_TIFD_OFFSET_COUNT 4
51 #define TIFF_TIFD_OFFSET_DATA 8
52 #define TIFF_TIFD_SIZE 12
53
54
55
56 guint16 tiff_byte_get_int16(guchar *f, TiffByteOrder bo)
57 {
58         guint16 align_buf;
59
60         memcpy(&align_buf, f, sizeof(guint16));
61
62         if (bo == TIFF_BYTE_ORDER_INTEL)
63                 return GUINT16_FROM_LE(align_buf);
64         else
65                 return GUINT16_FROM_BE(align_buf);
66 }
67
68 guint32 tiff_byte_get_int32(guchar *f, TiffByteOrder bo)
69 {
70         guint32 align_buf;
71
72         memcpy(&align_buf, f, sizeof(guint32));
73
74         if (bo == TIFF_BYTE_ORDER_INTEL)
75                 return GUINT32_FROM_LE(align_buf);
76         else
77                 return GUINT32_FROM_BE(align_buf);
78 }
79
80 void tiff_byte_put_int16(guchar *f, guint16 n, TiffByteOrder bo)
81 {
82         guint16 align_buf;
83
84         if (bo == TIFF_BYTE_ORDER_INTEL)
85                 {
86                 align_buf = GUINT16_TO_LE(n);
87                 }
88         else
89                 {
90                 align_buf = GUINT16_TO_BE(n);
91                 }
92
93         memcpy(f, &align_buf, sizeof(guint16));
94 }
95
96 void tiff_byte_put_int32(guchar *f, guint32 n, TiffByteOrder bo)
97 {
98         guint32 align_buf;
99
100         if (bo == TIFF_BYTE_ORDER_INTEL)
101                 {
102                 align_buf = GUINT32_TO_LE(n);
103                 }
104         else
105                 {
106                 align_buf = GUINT32_TO_BE(n);
107                 }
108
109         memcpy(f, &align_buf, sizeof(guint32));
110 }
111
112 gint tiff_directory_offset(guchar *data, const guint len,
113                                 guint *offset, TiffByteOrder *bo)
114 {
115         if (len < 8) return FALSE;
116
117         if (memcmp(data, "II", 2) == 0)
118                 {
119                 *bo = TIFF_BYTE_ORDER_INTEL;
120                 }
121         else if (memcmp(data, "MM", 2) == 0)
122                 {
123                 *bo = TIFF_BYTE_ORDER_MOTOROLA;
124                 }
125         else
126                 {
127                 return FALSE;
128                 }
129
130         if (tiff_byte_get_int16(data + 2, *bo) != 0x002A)
131                 {
132                 return FALSE;
133                 }
134
135         *offset = tiff_byte_get_int32(data + 4, *bo);
136
137         return (*offset < len);
138 }
139
140 typedef gint (* FuncParseIFDEntry)(guchar *tiff, guint offset,
141                                  guint size, TiffByteOrder bo,
142                                  gpointer data);
143
144
145 gint tiff_parse_IFD_table(guchar *tiff, guint offset,
146                           guint size, TiffByteOrder bo,
147                           guint *next_offset,
148                           FuncParseIFDEntry parse_entry, gpointer data)
149 {
150         guint count;
151         guint i;
152         guint next;
153
154
155         /* We should be able to read number of entries in IFD0) */
156         if (size < offset + 2) return -1;
157
158         count = tiff_byte_get_int16(tiff + offset, bo);
159         offset += 2;
160 printf("count %d\n", count);
161         /* Entries and next IFD offset must be readable */
162         if (size < offset + count * TIFF_TIFD_SIZE + 4) return -1;
163
164         for (i = 0; i < count; i++)
165                 {
166                 parse_entry(tiff, offset + i * TIFF_TIFD_SIZE, size, bo, data);
167                 }
168         
169         next = tiff_byte_get_int32(tiff + offset + count * TIFF_TIFD_SIZE, bo);
170 printf("next %d\n", next);
171         if (next_offset) *next_offset = next;
172         
173         return 0;
174 }
175
176 static gint mpo_parse_Index_IFD_entry(guchar *tiff, guint offset,
177                                  guint size, TiffByteOrder bo,
178                                  gpointer data)
179 {
180         guint tag;
181         guint format;
182         guint count;
183         guint data_val;
184         guint data_offset;
185         guint data_length;
186
187         MPOData *mpo = data;
188
189         tag = tiff_byte_get_int16(tiff + offset + TIFF_TIFD_OFFSET_TAG, bo);
190         format = tiff_byte_get_int16(tiff + offset + TIFF_TIFD_OFFSET_FORMAT, bo);
191         count = tiff_byte_get_int32(tiff + offset + TIFF_TIFD_OFFSET_COUNT, bo);
192         data_val = tiff_byte_get_int32(tiff + offset + TIFF_TIFD_OFFSET_DATA, bo);
193 printf("tag %x format %x count %x data_val %x\n", tag, format, count, data_val);
194
195         if (tag == 0xb000)
196                 {
197                 mpo->version = data_val;
198                 }
199         else if (tag == 0xb001)
200                 {
201                 mpo->num_images = data_val;
202                 }
203         else if (tag == 0xb002)
204                 {
205                 guint i;
206                 data_offset = data_val;
207                 data_length = count;
208                 if (size < data_offset || size < data_offset + data_length)
209                         {
210                         return -1;
211                         }
212                 if (count != mpo->num_images * 16)
213                         {
214                         return -1;
215                         }
216                 
217                 mpo->images = g_new0(MPOEntry, mpo->num_images);
218                         
219                 for (i = 0; i < mpo->num_images; i++) {
220                         guint image_attr = tiff_byte_get_int32(tiff + data_offset + i * 16, bo);
221                         mpo->images[i].type_code = image_attr & 0xffffff;
222                         mpo->images[i].representative = !!(image_attr & 0x20000000);
223                         mpo->images[i].dependent_child = !!(image_attr & 0x40000000);
224                         mpo->images[i].dependent_parent = !!(image_attr & 0x80000000);
225                         mpo->images[i].length = tiff_byte_get_int32(tiff + data_offset + i * 16 + 4, bo);
226                         mpo->images[i].offset = tiff_byte_get_int32(tiff + data_offset + i * 16 + 8, bo);
227                         mpo->images[i].dep1 = tiff_byte_get_int16(tiff + data_offset + i * 16 + 12, bo);
228                         mpo->images[i].dep2 = tiff_byte_get_int16(tiff + data_offset + i * 16 + 14, bo);
229                         
230                         if (i == 0) 
231                                 {
232                                 mpo->images[i].offset = 0;
233                                 }
234                         else
235                                 {
236                                 mpo->images[i].offset += mpo->mpo_offset;
237                                 }
238                                 
239                         printf("img %x %x %x\n", image_attr, mpo->images[i].length, mpo->images[i].offset);
240                         }
241                 }
242
243         return 0;
244 }
245
246 MPOData *jpeg_get_mpo_data(guchar *data, guint size)
247 {
248         guint seg_offset;
249         guint seg_size;
250         if (jpeg_segment_find(data, size, JPEG_MARKER_APP2, "MPF\x00", 4, &seg_offset, &seg_size) && seg_size >16)
251                 {
252                 guint offset;
253                 TiffByteOrder bo;
254                 MPOData *mpo;
255                 guint i;
256
257                 printf("mpo signature found at %x\n", seg_offset); 
258                 data += seg_offset + 4;
259                 seg_size -= 4;
260                 
261                 if (!tiff_directory_offset(data, seg_size, &offset, &bo)) return NULL;
262
263                 mpo = g_new0(MPOData, 1);
264                 mpo->mpo_offset = seg_offset + 4;
265                 
266                 tiff_parse_IFD_table(data,  offset , seg_size, bo, NULL, mpo_parse_Index_IFD_entry, (gpointer)mpo);
267                 if (!mpo->images) mpo->num_images = 0;
268                 
269                 for (i = 0; i < mpo->num_images; i++)
270                         {
271                         if (mpo->images[i].offset + mpo->images[i].length > size)
272                                 {
273                                 mpo->num_images = i;
274                                 DEBUG_1("MPO file truncated to %d valid images", i);
275                                 break;
276                                 }
277                         }
278                 return mpo;
279                 }
280         return NULL;
281 }
282
283 void jpeg_mpo_data_free(MPOData *mpo)
284 {
285         if (mpo)
286                 {
287                 if (mpo->images) g_free(mpo->images);
288                 g_free(mpo);
289                 }
290 }