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