view src/jpeg_parser.c @ 1977:af653599660d lm

More use of const, fix up a warning.
author Laurent Monin <zas@norz.org>
date Mon, 14 May 2012 23:01:01 +0200
parents 4417578c55f9
children 8a09320fe8a7
line wrap: on
line source

/*
 * Geeqie
 * (C) 2004 John Ellis
 * Copyright (C) 2008 - 2011 The Geeqie Team
 *
 * Author: Vladimir Nadvornik
 *
 * This software is released under the GNU General Public License (GNU GPL).
 * Please read the included file COPYING for more information.
 * This software comes with no warranty of any kind, use at your own risk!
 */

#include "main.h" 
#include "jpeg_parser.h"

gboolean jpeg_segment_find(const guchar *data, guint size,
			    guchar app_marker, const gchar *magic, guint magic_len,
			    guint *seg_offset, guint *seg_length)
{
	guchar marker = 0;
	guint offset = 0;
	guint length = 0;

	while (marker != JPEG_MARKER_EOI)
		{
		offset += length;
		length = 2;

		if (offset + 2 >= size ||
		    data[offset] != JPEG_MARKER) return FALSE;

		marker = data[offset + 1];
		if (marker != JPEG_MARKER_SOI &&
		    marker != JPEG_MARKER_EOI)
			{
			if (offset + 4 >= size) return FALSE;
			length += ((guint)data[offset + 2] << 8) + data[offset + 3];

			if (marker == app_marker &&
			    offset + length < size &&
			    length >= 4 + magic_len &&
			    memcmp(data + offset + 4, magic, magic_len) == 0)
				{
				*seg_offset = offset + 4;
				*seg_length = length - 4;
				return TRUE;
				}
			}
		}
	return FALSE;
}


typedef enum {
	TIFF_BYTE_ORDER_INTEL,
	TIFF_BYTE_ORDER_MOTOROLA
} TiffByteOrder;

#define TIFF_TIFD_OFFSET_TAG 0
#define TIFF_TIFD_OFFSET_FORMAT 2
#define TIFF_TIFD_OFFSET_COUNT 4
#define TIFF_TIFD_OFFSET_DATA 8
#define TIFF_TIFD_SIZE 12



guint16 tiff_byte_get_int16(const guchar *f, TiffByteOrder bo)
{
	guint16 align_buf;

	memcpy(&align_buf, f, sizeof(guint16));

	if (bo == TIFF_BYTE_ORDER_INTEL)
		return GUINT16_FROM_LE(align_buf);
	else
		return GUINT16_FROM_BE(align_buf);
}

guint32 tiff_byte_get_int32(const guchar *f, TiffByteOrder bo)
{
	guint32 align_buf;

	memcpy(&align_buf, f, sizeof(guint32));

	if (bo == TIFF_BYTE_ORDER_INTEL)
		return GUINT32_FROM_LE(align_buf);
	else
		return GUINT32_FROM_BE(align_buf);
}

void tiff_byte_put_int16(guchar *f, guint16 n, TiffByteOrder bo)
{
	guint16 align_buf;

	if (bo == TIFF_BYTE_ORDER_INTEL)
		{
		align_buf = GUINT16_TO_LE(n);
		}
	else
		{
		align_buf = GUINT16_TO_BE(n);
		}

	memcpy(f, &align_buf, sizeof(guint16));
}

void tiff_byte_put_int32(guchar *f, guint32 n, TiffByteOrder bo)
{
	guint32 align_buf;

	if (bo == TIFF_BYTE_ORDER_INTEL)
		{
		align_buf = GUINT32_TO_LE(n);
		}
	else
		{
		align_buf = GUINT32_TO_BE(n);
		}

	memcpy(f, &align_buf, sizeof(guint32));
}

gint tiff_directory_offset(const guchar *data, const guint len,
				guint *offset, TiffByteOrder *bo)
{
	if (len < 8) return FALSE;

	if (memcmp(data, "II", 2) == 0)
		{
		*bo = TIFF_BYTE_ORDER_INTEL;
		}
	else if (memcmp(data, "MM", 2) == 0)
		{
		*bo = TIFF_BYTE_ORDER_MOTOROLA;
		}
	else
		{
		return FALSE;
		}

	if (tiff_byte_get_int16(data + 2, *bo) != 0x002A)
		{
		return FALSE;
		}

	*offset = tiff_byte_get_int32(data + 4, *bo);

	return (*offset < len);
}

typedef gint (* FuncParseIFDEntry)(const guchar *tiff, guint offset,
				 guint size, TiffByteOrder bo,
				 gpointer data);


gint tiff_parse_IFD_table(const guchar *tiff, guint offset,
			  guint size, TiffByteOrder bo,
			  guint *next_offset,
			  FuncParseIFDEntry parse_entry, gpointer data)
{
	guint count;
	guint i;
	guint next;


	/* We should be able to read number of entries in IFD0) */
	if (size < offset + 2) return -1;

	count = tiff_byte_get_int16(tiff + offset, bo);
	offset += 2;
	/* Entries and next IFD offset must be readable */
	if (size < offset + count * TIFF_TIFD_SIZE + 4) return -1;

	for (i = 0; i < count; i++)
		{
		parse_entry(tiff, offset + i * TIFF_TIFD_SIZE, size, bo, data);
		}
	
	next = tiff_byte_get_int32(tiff + offset + count * TIFF_TIFD_SIZE, bo);
	if (next_offset) *next_offset = next;
	
	return 0;
}

static gint mpo_parse_Index_IFD_entry(const guchar *tiff, guint offset,
				 guint size, TiffByteOrder bo,
				 gpointer data)
{
	guint tag;
	guint format;
	guint count;
	guint data_val;
	guint data_offset;
	guint data_length;

	MPOData *mpo = data;

	tag = tiff_byte_get_int16(tiff + offset + TIFF_TIFD_OFFSET_TAG, bo);
	format = tiff_byte_get_int16(tiff + offset + TIFF_TIFD_OFFSET_FORMAT, bo);
	count = tiff_byte_get_int32(tiff + offset + TIFF_TIFD_OFFSET_COUNT, bo);
	data_val = tiff_byte_get_int32(tiff + offset + TIFF_TIFD_OFFSET_DATA, bo);
	DEBUG_1("   tag %x format %x count %x data_val %x", tag, format, count, data_val);

        if (tag == 0xb000)
        	{
        	mpo->version = data_val;
		DEBUG_1("    mpo version %x", mpo->version);
        	}
        else if (tag == 0xb001)
        	{
        	mpo->num_images = data_val;
		DEBUG_1("    num images %x", mpo->num_images);
        	}
	else if (tag == 0xb002)
		{
		guint i;
		data_offset = data_val;
		data_length = count;
		if (size < data_offset || size < data_offset + data_length)
			{
			return -1;
			}
		if (count != mpo->num_images * 16)
			{
			return -1;
			}
		
		mpo->images = g_new0(MPOEntry, mpo->num_images);
			
		for (i = 0; i < mpo->num_images; i++) {
			guint image_attr = tiff_byte_get_int32(tiff + data_offset + i * 16, bo);
			mpo->images[i].type_code = image_attr & 0xffffff;
			mpo->images[i].representative = !!(image_attr & 0x20000000);
			mpo->images[i].dependent_child = !!(image_attr & 0x40000000);
			mpo->images[i].dependent_parent = !!(image_attr & 0x80000000);
			mpo->images[i].length = tiff_byte_get_int32(tiff + data_offset + i * 16 + 4, bo);
			mpo->images[i].offset = tiff_byte_get_int32(tiff + data_offset + i * 16 + 8, bo);
			mpo->images[i].dep1 = tiff_byte_get_int16(tiff + data_offset + i * 16 + 12, bo);
			mpo->images[i].dep2 = tiff_byte_get_int16(tiff + data_offset + i * 16 + 14, bo);
			
			if (i == 0) 
				{
				mpo->images[i].offset = 0;
				}
			else
				{
			 	mpo->images[i].offset += mpo->mpo_offset;
			 	}
			 	
			DEBUG_1("   image %x %x %x", image_attr, mpo->images[i].length, mpo->images[i].offset);
			}
		}

	return 0;
}

static gint mpo_parse_Attributes_IFD_entry(const guchar *tiff, guint offset,
				 guint size, TiffByteOrder bo,
				 gpointer data)
{
	guint tag;
	guint format;
	guint count;
	guint data_val;
	guint data_offset;
	guint data_length;

	MPOEntry *mpe = data;

	tag = tiff_byte_get_int16(tiff + offset + TIFF_TIFD_OFFSET_TAG, bo);
	format = tiff_byte_get_int16(tiff + offset + TIFF_TIFD_OFFSET_FORMAT, bo);
	count = tiff_byte_get_int32(tiff + offset + TIFF_TIFD_OFFSET_COUNT, bo);
	data_val = tiff_byte_get_int32(tiff + offset + TIFF_TIFD_OFFSET_DATA, bo);
	DEBUG_1("   tag %x format %x count %x data_val %x", tag, format, count, data_val);

        switch (tag) 
        	{
        	case 0xb000: 
        		mpe->MPFVersion = data_val;
			DEBUG_1("    mpo version %x", data_val);
        		break;
        	case 0xb101: 
        		mpe->MPIndividualNum = data_val;
			DEBUG_1("    Individual Image Number %x", mpe->MPIndividualNum);
        		break;
        	case 0xb201: 
        		mpe->PanOrientation = data_val;
        		break;
/*

FIXME:
Panorama Scanning Orientation PanOrientation 45569 B201 LONG 1
Panorama Horizontal Overlap PanOverlap_H 45570 B202 RATIONAL 1
Panorama Vertical Overlap PanOverlap_V 45571 B203 RATIONAL 1
Base Viewpoint Number BaseViewpointNum 45572 B204 LONG 1
Convergence Angle ConvergenceAngle 45573 B205 SRATIONAL 1
Baseline Length BaselineLength 45574 B206 RATIONAL 1
Divergence Angle VerticalDivergence 45575 B207 SRATIONAL 1
Horizontal Axis Distance AxisDistance_X 45576 B208 SRATIONAL 1
Vertical Axis Distance AxisDistance_Y 45577 B209 SRATIONAL 1
Collimation Axis Distance AxisDistance_Z 45578 B20A SRATIONAL 1
Yaw Angle YawAngle 45579 B20B SRATIONAL 1
Pitch Angle PitchAngle 45580 B20C SRATIONAL 1
Roll Angle RollAngle 45581 B20D 
  */      	
		default: 
			break;
		}

	return 0;
}

MPOData *jpeg_get_mpo_data(const guchar *data, guint size)
{
	guint seg_offset;
	guint seg_size;
	if (jpeg_segment_find(data, size, JPEG_MARKER_APP2, "MPF\x00", 4, &seg_offset, &seg_size) && seg_size >16)
		{
		guint offset;
		guint next_offset;
		TiffByteOrder bo;
		MPOData *mpo;
		guint i;

		DEBUG_1("mpo signature found at %x", seg_offset); 
		seg_offset += 4;
		seg_size -= 4;
		
		if (!tiff_directory_offset(data + seg_offset, seg_size, &offset, &bo)) return NULL;

		mpo = g_new0(MPOData, 1);
		mpo->mpo_offset = seg_offset;
		
		tiff_parse_IFD_table(data + seg_offset,  offset , seg_size, bo, &next_offset, mpo_parse_Index_IFD_entry, (gpointer)mpo);
		if (!mpo->images) mpo->num_images = 0;
		
	
		for (i = 0; i < mpo->num_images; i++)
			{
			if (mpo->images[i].offset + mpo->images[i].length > size)
				{
				mpo->num_images = i;
				DEBUG_1("MPO file truncated to %d valid images, %d %d", i, mpo->images[i].offset + mpo->images[i].length, size);
				break;
				}
			}
		
		for (i = 0; i < mpo->num_images; i++)
			{
			if (i == 0) 
				{
				offset = next_offset;
				}
			else
				{
				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)
					{
					DEBUG_1("MPO image %d: MPO signature not found", i);
					continue;
					}
				
				seg_offset += 4;
				seg_size -= 4;
				if (!tiff_directory_offset(data + mpo->images[i].offset + seg_offset, seg_size, &offset, &bo)) 
					{
					DEBUG_1("MPO image %d: invalid directory offset", i);
					continue;
					}

				}
			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]);
			}
		
		return mpo;
		}
	return NULL;
}

void jpeg_mpo_data_free(MPOData *mpo)
{
	if (mpo)
		{
		if (mpo->images) g_free(mpo->images);
		g_free(mpo);
		}
}