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