Trim trailing white spaces.
[geeqie.git] / src / jpeg_parser.c
1 /*
2  * Geeqie
3  * (C) 2004 John Ellis
4  * Copyright (C) 2008 - 2011 The Geeqie Team
5  *
6  * Author: Vladimir Nadvornik
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13 #include "main.h"
14 #include "jpeg_parser.h"
15
16 gboolean jpeg_segment_find(const guchar *data, guint size,
17                             guchar app_marker, const gchar *magic, guint magic_len,
18                             guint *seg_offset, guint *seg_length)
19 {
20         guchar marker = 0;
21         guint offset = 0;
22         guint length = 0;
23
24         while (marker != JPEG_MARKER_EOI)
25                 {
26                 offset += length;
27                 length = 2;
28
29                 if (offset + 2 >= size ||
30                     data[offset] != JPEG_MARKER) return FALSE;
31
32                 marker = data[offset + 1];
33                 if (marker != JPEG_MARKER_SOI &&
34                     marker != JPEG_MARKER_EOI)
35                         {
36                         if (offset + 4 >= size) return FALSE;
37                         length += ((guint)data[offset + 2] << 8) + data[offset + 3];
38
39                         if (marker == app_marker &&
40                             offset + length < size &&
41                             length >= 4 + magic_len &&
42                             memcmp(data + offset + 4, magic, magic_len) == 0)
43                                 {
44                                 *seg_offset = offset + 4;
45                                 *seg_length = length - 4;
46                                 return TRUE;
47                                 }
48                         }
49                 }
50         return FALSE;
51 }
52
53
54 typedef enum {
55         TIFF_BYTE_ORDER_INTEL,
56         TIFF_BYTE_ORDER_MOTOROLA
57 } TiffByteOrder;
58
59 #define TIFF_TIFD_OFFSET_TAG 0
60 #define TIFF_TIFD_OFFSET_FORMAT 2
61 #define TIFF_TIFD_OFFSET_COUNT 4
62 #define TIFF_TIFD_OFFSET_DATA 8
63 #define TIFF_TIFD_SIZE 12
64
65
66
67 guint16 tiff_byte_get_int16(const guchar *f, TiffByteOrder bo)
68 {
69         guint16 align_buf;
70
71         memcpy(&align_buf, f, sizeof(guint16));
72
73         if (bo == TIFF_BYTE_ORDER_INTEL)
74                 return GUINT16_FROM_LE(align_buf);
75         else
76                 return GUINT16_FROM_BE(align_buf);
77 }
78
79 guint32 tiff_byte_get_int32(const guchar *f, TiffByteOrder bo)
80 {
81         guint32 align_buf;
82
83         memcpy(&align_buf, f, sizeof(guint32));
84
85         if (bo == TIFF_BYTE_ORDER_INTEL)
86                 return GUINT32_FROM_LE(align_buf);
87         else
88                 return GUINT32_FROM_BE(align_buf);
89 }
90
91 void tiff_byte_put_int16(guchar *f, guint16 n, TiffByteOrder bo)
92 {
93         guint16 align_buf;
94
95         if (bo == TIFF_BYTE_ORDER_INTEL)
96                 {
97                 align_buf = GUINT16_TO_LE(n);
98                 }
99         else
100                 {
101                 align_buf = GUINT16_TO_BE(n);
102                 }
103
104         memcpy(f, &align_buf, sizeof(guint16));
105 }
106
107 void tiff_byte_put_int32(guchar *f, guint32 n, TiffByteOrder bo)
108 {
109         guint32 align_buf;
110
111         if (bo == TIFF_BYTE_ORDER_INTEL)
112                 {
113                 align_buf = GUINT32_TO_LE(n);
114                 }
115         else
116                 {
117                 align_buf = GUINT32_TO_BE(n);
118                 }
119
120         memcpy(f, &align_buf, sizeof(guint32));
121 }
122
123 gint tiff_directory_offset(const guchar *data, const guint len,
124                                 guint *offset, TiffByteOrder *bo)
125 {
126         if (len < 8) return FALSE;
127
128         if (memcmp(data, "II", 2) == 0)
129                 {
130                 *bo = TIFF_BYTE_ORDER_INTEL;
131                 }
132         else if (memcmp(data, "MM", 2) == 0)
133                 {
134                 *bo = TIFF_BYTE_ORDER_MOTOROLA;
135                 }
136         else
137                 {
138                 return FALSE;
139                 }
140
141         if (tiff_byte_get_int16(data + 2, *bo) != 0x002A)
142                 {
143                 return FALSE;
144                 }
145
146         *offset = tiff_byte_get_int32(data + 4, *bo);
147
148         return (*offset < len);
149 }
150
151 typedef gint (* FuncParseIFDEntry)(const guchar *tiff, guint offset,
152                                  guint size, TiffByteOrder bo,
153                                  gpointer data);
154
155
156 gint tiff_parse_IFD_table(const guchar *tiff, guint offset,
157                           guint size, TiffByteOrder bo,
158                           guint *next_offset,
159                           FuncParseIFDEntry parse_entry, gpointer data)
160 {
161         guint count;
162         guint i;
163         guint next;
164
165
166         /* We should be able to read number of entries in IFD0) */
167         if (size < offset + 2) return -1;
168
169         count = tiff_byte_get_int16(tiff + offset, bo);
170         offset += 2;
171         /* Entries and next IFD offset must be readable */
172         if (size < offset + count * TIFF_TIFD_SIZE + 4) return -1;
173
174         for (i = 0; i < count; i++)
175                 {
176                 parse_entry(tiff, offset + i * TIFF_TIFD_SIZE, size, bo, data);
177                 }
178         
179         next = tiff_byte_get_int32(tiff + offset + count * TIFF_TIFD_SIZE, bo);
180         if (next_offset) *next_offset = next;
181         
182         return 0;
183 }
184
185 static gint mpo_parse_Index_IFD_entry(const guchar *tiff, guint offset,
186                                  guint size, TiffByteOrder bo,
187                                  gpointer data)
188 {
189         guint tag;
190         guint format;
191         guint count;
192         guint data_val;
193         guint data_offset;
194         guint data_length;
195
196         MPOData *mpo = data;
197
198         tag = tiff_byte_get_int16(tiff + offset + TIFF_TIFD_OFFSET_TAG, bo);
199         format = tiff_byte_get_int16(tiff + offset + TIFF_TIFD_OFFSET_FORMAT, bo);
200         count = tiff_byte_get_int32(tiff + offset + TIFF_TIFD_OFFSET_COUNT, bo);
201         data_val = tiff_byte_get_int32(tiff + offset + TIFF_TIFD_OFFSET_DATA, bo);
202         DEBUG_1("   tag %x format %x count %x data_val %x", tag, format, count, data_val);
203
204         if (tag == 0xb000)
205                 {
206                 mpo->version = data_val;
207                 DEBUG_1("    mpo version %x", mpo->version);
208                 }
209         else if (tag == 0xb001)
210                 {
211                 mpo->num_images = data_val;
212                 DEBUG_1("    num images %x", mpo->num_images);
213                 }
214         else if (tag == 0xb002)
215                 {
216                 guint i;
217                 data_offset = data_val;
218                 data_length = count;
219                 if (size < data_offset || size < data_offset + data_length)
220                         {
221                         return -1;
222                         }
223                 if (count != mpo->num_images * 16)
224                         {
225                         return -1;
226                         }
227                 
228                 mpo->images = g_new0(MPOEntry, mpo->num_images);
229                         
230                 for (i = 0; i < mpo->num_images; i++) {
231                         guint image_attr = tiff_byte_get_int32(tiff + data_offset + i * 16, bo);
232                         mpo->images[i].type_code = image_attr & 0xffffff;
233                         mpo->images[i].representative = !!(image_attr & 0x20000000);
234                         mpo->images[i].dependent_child = !!(image_attr & 0x40000000);
235                         mpo->images[i].dependent_parent = !!(image_attr & 0x80000000);
236                         mpo->images[i].length = tiff_byte_get_int32(tiff + data_offset + i * 16 + 4, bo);
237                         mpo->images[i].offset = tiff_byte_get_int32(tiff + data_offset + i * 16 + 8, bo);
238                         mpo->images[i].dep1 = tiff_byte_get_int16(tiff + data_offset + i * 16 + 12, bo);
239                         mpo->images[i].dep2 = tiff_byte_get_int16(tiff + data_offset + i * 16 + 14, bo);
240                         
241                         if (i == 0)
242                                 {
243                                 mpo->images[i].offset = 0;
244                                 }
245                         else
246                                 {
247                                 mpo->images[i].offset += mpo->mpo_offset;
248                                 }
249                                 
250                         DEBUG_1("   image %x %x %x", image_attr, mpo->images[i].length, mpo->images[i].offset);
251                         }
252                 }
253
254         return 0;
255 }
256
257 static gint mpo_parse_Attributes_IFD_entry(const guchar *tiff, guint offset,
258                                  guint size, TiffByteOrder bo,
259                                  gpointer data)
260 {
261         guint tag;
262         guint format;
263         guint count;
264         guint data_val;
265
266         MPOEntry *mpe = data;
267
268         tag = tiff_byte_get_int16(tiff + offset + TIFF_TIFD_OFFSET_TAG, bo);
269         format = tiff_byte_get_int16(tiff + offset + TIFF_TIFD_OFFSET_FORMAT, bo);
270         count = tiff_byte_get_int32(tiff + offset + TIFF_TIFD_OFFSET_COUNT, bo);
271         data_val = tiff_byte_get_int32(tiff + offset + TIFF_TIFD_OFFSET_DATA, bo);
272         DEBUG_1("   tag %x format %x count %x data_val %x", tag, format, count, data_val);
273
274         switch (tag)
275                 {
276                 case 0xb000:
277                         mpe->MPFVersion = data_val;
278                         DEBUG_1("    mpo version %x", data_val);
279                         break;
280                 case 0xb101:
281                         mpe->MPIndividualNum = data_val;
282                         DEBUG_1("    Individual Image Number %x", mpe->MPIndividualNum);
283                         break;
284                 case 0xb201:
285                         mpe->PanOrientation = data_val;
286                         break;
287 /*
288
289 FIXME:
290 Panorama Scanning Orientation PanOrientation 45569 B201 LONG 1
291 Panorama Horizontal Overlap PanOverlap_H 45570 B202 RATIONAL 1
292 Panorama Vertical Overlap PanOverlap_V 45571 B203 RATIONAL 1
293 Base Viewpoint Number BaseViewpointNum 45572 B204 LONG 1
294 Convergence Angle ConvergenceAngle 45573 B205 SRATIONAL 1
295 Baseline Length BaselineLength 45574 B206 RATIONAL 1
296 Divergence Angle VerticalDivergence 45575 B207 SRATIONAL 1
297 Horizontal Axis Distance AxisDistance_X 45576 B208 SRATIONAL 1
298 Vertical Axis Distance AxisDistance_Y 45577 B209 SRATIONAL 1
299 Collimation Axis Distance AxisDistance_Z 45578 B20A SRATIONAL 1
300 Yaw Angle YawAngle 45579 B20B SRATIONAL 1
301 Pitch Angle PitchAngle 45580 B20C SRATIONAL 1
302 Roll Angle RollAngle 45581 B20D
303   */
304                 default:
305                         break;
306                 }
307
308         return 0;
309 }
310
311 MPOData *jpeg_get_mpo_data(const guchar *data, guint size)
312 {
313         guint seg_offset;
314         guint seg_size;
315         if (jpeg_segment_find(data, size, JPEG_MARKER_APP2, "MPF\x00", 4, &seg_offset, &seg_size) && seg_size >16)
316                 {
317                 guint offset;
318                 guint next_offset;
319                 TiffByteOrder bo;
320                 MPOData *mpo;
321                 guint i;
322
323                 DEBUG_1("mpo signature found at %x", seg_offset);
324                 seg_offset += 4;
325                 seg_size -= 4;
326                 
327                 if (!tiff_directory_offset(data + seg_offset, seg_size, &offset, &bo)) return NULL;
328
329                 mpo = g_new0(MPOData, 1);
330                 mpo->mpo_offset = seg_offset;
331                 
332                 tiff_parse_IFD_table(data + seg_offset,  offset , seg_size, bo, &next_offset, mpo_parse_Index_IFD_entry, (gpointer)mpo);
333                 if (!mpo->images) mpo->num_images = 0;
334                 
335         
336                 for (i = 0; i < mpo->num_images; i++)
337                         {
338                         if (mpo->images[i].offset + mpo->images[i].length > size)
339                                 {
340                                 mpo->num_images = i;
341                                 DEBUG_1("MPO file truncated to %d valid images, %d %d", i, mpo->images[i].offset + mpo->images[i].length, size);
342                                 break;
343                                 }
344                         }
345                 
346                 for (i = 0; i < mpo->num_images; i++)
347                         {
348                         if (i == 0)
349                                 {
350                                 offset = next_offset;
351                                 }
352                         else
353                                 {
354                                 if (!jpeg_segment_find(data + mpo->images[i].offset, mpo->images[i].length, JPEG_MARKER_APP2, "MPF\x00", 4, &seg_offset, &seg_size) || seg_size <=16)
355                                         {
356                                         DEBUG_1("MPO image %d: MPO signature not found", i);
357                                         continue;
358                                         }
359                                 
360                                 seg_offset += 4;
361                                 seg_size -= 4;
362                                 if (!tiff_directory_offset(data + mpo->images[i].offset + seg_offset, seg_size, &offset, &bo))
363                                         {
364                                         DEBUG_1("MPO image %d: invalid directory offset", i);
365                                         continue;
366                                         }
367
368                                 }
369                         tiff_parse_IFD_table(data + mpo->images[i].offset + seg_offset,  offset , seg_size, bo, NULL, mpo_parse_Attributes_IFD_entry, (gpointer)&mpo->images[i]);
370                         }
371                 
372                 return mpo;
373                 }
374         return NULL;
375 }
376
377 void jpeg_mpo_data_free(MPOData *mpo)
378 {
379         if (mpo)
380                 {
381                 if (mpo->images) g_free(mpo->images);
382                 g_free(mpo);
383                 }
384 }
385
386
387 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */