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