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