2 * Copyright (C) 2004 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
5 * Author: Vladimir Nadvornik
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.
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.
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.
23 #include "jpeg-parser.h"
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)
33 while (marker != JPEG_MARKER_EOI)
38 if (offset + 2 >= size ||
39 data[offset] != JPEG_MARKER) return FALSE;
41 marker = data[offset + 1];
42 if (marker != JPEG_MARKER_SOI &&
43 marker != JPEG_MARKER_EOI)
45 if (offset + 4 >= size) return FALSE;
46 length += (static_cast<guint>(data[offset + 2]) << 8) + data[offset + 3];
48 if (marker == app_marker &&
49 offset + length < size &&
50 length >= 4 + magic_len &&
51 memcmp(data + offset + 4, magic, magic_len) == 0)
53 *seg_offset = offset + 4;
54 *seg_length = length - 4;
64 TIFF_BYTE_ORDER_INTEL,
65 TIFF_BYTE_ORDER_MOTOROLA
69 TIFF_TIFD_OFFSET_TAG = 0,
70 TIFF_TIFD_OFFSET_FORMAT = 2,
71 TIFF_TIFD_OFFSET_COUNT = 4,
72 TIFF_TIFD_OFFSET_DATA = 8,
78 guint16 tiff_byte_get_int16(const guchar *f, TiffByteOrder bo)
82 memcpy(&align_buf, f, sizeof(guint16));
84 if (bo == TIFF_BYTE_ORDER_INTEL)
85 return GUINT16_FROM_LE(align_buf);
87 return GUINT16_FROM_BE(align_buf);
90 guint32 tiff_byte_get_int32(const guchar *f, TiffByteOrder bo)
94 memcpy(&align_buf, f, sizeof(guint32));
96 if (bo == TIFF_BYTE_ORDER_INTEL)
97 return GUINT32_FROM_LE(align_buf);
99 return GUINT32_FROM_BE(align_buf);
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)
108 if (bo == TIFF_BYTE_ORDER_INTEL)
110 align_buf = GUINT16_TO_LE(n);
114 align_buf = GUINT16_TO_BE(n);
117 memcpy(f, &align_buf, sizeof(guint16));
120 void tiff_byte_put_int32_unused(guchar *f, guint32 n, TiffByteOrder bo)
124 if (bo == TIFF_BYTE_ORDER_INTEL)
126 align_buf = GUINT32_TO_LE(n);
130 align_buf = GUINT32_TO_BE(n);
133 memcpy(f, &align_buf, sizeof(guint32));
135 #pragma GCC diagnostic pop
137 gint tiff_directory_offset(const guchar *data, const guint len,
138 guint *offset, TiffByteOrder *bo)
140 if (len < 8) return FALSE;
142 if (memcmp(data, "II", 2) == 0)
144 *bo = TIFF_BYTE_ORDER_INTEL;
146 else if (memcmp(data, "MM", 2) == 0)
148 *bo = TIFF_BYTE_ORDER_MOTOROLA;
155 if (tiff_byte_get_int16(data + 2, *bo) != 0x002A)
160 *offset = tiff_byte_get_int32(data + 4, *bo);
162 return (*offset < len);
165 using FuncParseIFDEntry = gint (*)(const guchar *, guint, guint, TiffByteOrder, gpointer);
168 gint tiff_parse_IFD_table(const guchar *tiff, guint offset,
169 guint size, TiffByteOrder bo,
171 FuncParseIFDEntry parse_entry, gpointer data)
178 /* We should be able to read number of entries in IFD0) */
179 if (size < offset + 2) return -1;
181 count = tiff_byte_get_int16(tiff + offset, bo);
183 /* Entries and next IFD offset must be readable */
184 if (size < offset + count * TIFF_TIFD_SIZE + 4) return -1;
186 for (i = 0; i < count; i++)
188 parse_entry(tiff, offset + i * TIFF_TIFD_SIZE, size, bo, data);
191 next = tiff_byte_get_int32(tiff + offset + count * TIFF_TIFD_SIZE, bo);
192 if (next_offset) *next_offset = next;
197 static gint mpo_parse_Index_IFD_entry(const guchar *tiff, guint offset,
198 guint size, TiffByteOrder bo,
208 auto mpo = static_cast<MPOData *>(data);
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);
218 mpo->version = data_val;
219 DEBUG_1(" mpo version %x", mpo->version);
221 else if (tag == 0xb001)
223 mpo->num_images = data_val;
224 DEBUG_1(" num images %x", mpo->num_images);
226 else if (tag == 0xb002)
229 data_offset = data_val;
231 if (size < data_offset || size < data_offset + data_length)
235 if (count != mpo->num_images * 16)
240 mpo->images = g_new0(MPOEntry, mpo->num_images);
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);
255 mpo->images[i].offset = 0;
259 mpo->images[i].offset += mpo->mpo_offset;
262 DEBUG_1(" image %x %x %x", image_attr, mpo->images[i].length, mpo->images[i].offset);
269 static gint mpo_parse_Attributes_IFD_entry(const guchar *tiff, guint offset,
270 guint, TiffByteOrder bo,
278 auto mpe = static_cast<MPOEntry *>(data);
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);
289 mpe->MPFVersion = data_val;
290 DEBUG_1(" mpo version %x", data_val);
293 mpe->MPIndividualNum = data_val;
294 DEBUG_1(" Individual Image Number %x", mpe->MPIndividualNum);
297 mpe->PanOrientation = data_val;
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
323 MPOData *jpeg_get_mpo_data(const guchar *data, guint size)
327 if (jpeg_segment_find(data, size, JPEG_MARKER_APP2, "MPF\x00", 4, &seg_offset, &seg_size) && seg_size >16)
330 guint next_offset = 0;
335 DEBUG_1("mpo signature found at %x", seg_offset);
339 if (!tiff_directory_offset(data + seg_offset, seg_size, &offset, &bo)) return nullptr;
341 mpo = g_new0(MPOData, 1);
342 mpo->mpo_offset = seg_offset;
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;
348 for (i = 0; i < mpo->num_images; i++)
350 if (mpo->images[i].offset + mpo->images[i].length > size)
353 DEBUG_1("MPO file truncated to %d valid images, %d %d", i, mpo->images[i].offset + mpo->images[i].length, size);
358 for (i = 0; i < mpo->num_images; i++)
362 offset = next_offset;
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)
368 DEBUG_1("MPO image %d: MPO signature not found", i);
374 if (!tiff_directory_offset(data + mpo->images[i].offset + seg_offset, seg_size, &offset, &bo))
376 DEBUG_1("MPO image %d: invalid directory offset", i);
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]);
389 void jpeg_mpo_data_free(MPOData *mpo)
393 if (mpo->images) g_free(mpo->images);
399 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */