Exemple #1
0
long GalleryUtil::GetNaturalRotation(const unsigned char *buffer, int size)
{
    long rotateAngle = 0;

#ifdef EXIF_SUPPORT
    try
    {
        ExifData *data = exif_data_new_from_data(buffer, size);
        if (data)
        {
            rotateAngle = GetNaturalRotation(data);
            exif_data_free(data);
        }
        else
        {
            LOG(VB_FILE, LOG_ERR, LOC + "Could not load exif data from buffer");
        }
    }
    catch (...)
    {
        LOG(VB_GENERAL, LOG_ERR, LOC +
            "Failed to extract EXIF headers from buffer");
    }
#else
    // Shut the compiler up about the unused argument
    (void)buffer;
    (void)size;
#endif

    return rotateAngle;
}
Exemple #2
0
void JP4::readMakerNote() {

  if (_raw_app1)
    _ed = exif_data_new_from_data(_raw_app1, _raw_app1_length);

  if (!_ed)
    return;

  ExifEntry* makerNoteEntry = exif_data_get_entry(_ed, EXIF_TAG_MAKER_NOTE);

  if (!makerNoteEntry) {
    _makerNote.gain[0]  = _makerNote.gain[1]  = _makerNote.gain[2]  = _makerNote.gain[3] = 2.0;
    _makerNote.gamma[0] = _makerNote.gamma[1] = _makerNote.gamma[2] = _makerNote.gamma[3] = 0.57;
    _makerNote.gamma_scale[0] = _makerNote.gamma_scale[1] = _makerNote.gamma_scale[2] = _makerNote.gamma_scale[3] = 0xffff;

    _makerNote.black[0] = _makerNote.black[1] = _makerNote.black[2] = _makerNote.black[3] = 10/256.0;
    _makerNote.decim_hor = 1;
    _makerNote.decim_ver = 1;
    _makerNote.bin_hor = 1;
    _makerNote.bin_ver = 1;
    return;
  }

  int makerNoteLength = makerNoteEntry->size/4;
  long makerNote[makerNoteLength];

  for (int i = 0; i < makerNoteLength; i++) {
    makerNote[i] = get_long(makerNoteEntry->data + i*4);
  }

  for (int i = 0; i < 4; i++) {
    // channel gain  8.16 (0x10000 - 1.0). Combines both analog gain and digital scaling
    _makerNote.gain[i] = makerNote[i] / 65536.0;

    // (P_PIXEL_LOW<<24) | (P_GAMMA <<16) and 16-bit (6.10) scale for gamma tables,
    _makerNote.gamma_scale[i] = (makerNote[i+4] & 0xffff);
    _makerNote.gamma[i]       = ((makerNote[i+4]>>16) & 0xff) / 100.0;
    _makerNote.black[i]       =  (makerNote[i+4]>>24) / 256.0;
  }

  if (makerNoteLength >= 12) {
    _makerNote.woi_left   = (makerNote[8] & 0xffff);
    _makerNote.woi_width  = (makerNote[8]>>16) & 0xffff;
    _makerNote.woi_top    = (makerNote[9] & 0xffff);
    _makerNote.woi_height = (makerNote[9]>>16);

    _makerNote.flip_hor = (makerNote[10] & 0x1);
    _makerNote.flip_ver = (makerNote[10]>>1) & 0x1;

    _makerNote.bayer_mode = (makerNote[10]>>2) & 0x3;
    _makerNote.color_mode = (makerNote[10]>>4) & 0x0f;

    _makerNote.decim_hor = (makerNote[10]>> 8) & 0x0f;
    _makerNote.decim_ver = (makerNote[10]>>12) & 0x0f;

    _makerNote.bin_hor = (makerNote[10]>>16) & 0x0f;
    _makerNote.bin_ver = (makerNote[10]>>20) & 0x0f;
  }
Exemple #3
0
void
metadatamux_exif_create_chunk_from_tag_list (guint8 ** buf, guint32 * size,
    const GstTagList * taglist, const MetaExifWriteOptions * opts)
{
  ExifData *ed = NULL;
  GstBuffer *exif_chunk = NULL;
  const GValue *val = NULL;

  if (!(buf && size))
    goto done;

  g_free (*buf);
  *buf = NULL;
  *size = 0;

  val = gst_tag_list_get_value_index (taglist, GST_TAG_EXIF, 0);
  if (val) {
    exif_chunk = gst_value_get_buffer (val);
    if (exif_chunk) {
      ed = exif_data_new_from_data (GST_BUFFER_DATA (exif_chunk),
          GST_BUFFER_SIZE (exif_chunk));
    }
  }

  if (!ed) {
    ed = exif_data_new ();
    GST_DEBUG ("setting byteorder %d", opts->byteorder);
    switch (opts->byteorder) {
      case GST_META_EXIF_BYTE_ORDER_MOTOROLA:
        exif_data_set_byte_order (ed, EXIF_BYTE_ORDER_MOTOROLA);
        break;
      case GST_META_EXIF_BYTE_ORDER_INTEL:
        exif_data_set_byte_order (ed, EXIF_BYTE_ORDER_INTEL);
        break;
      default:
        break;
    }
    exif_data_set_data_type (ed, EXIF_DATA_TYPE_COMPRESSED);
    exif_data_fix (ed);
  }

  gst_tag_list_foreach (taglist, metadatamux_exif_for_each_tag_in_list, ed);

  exif_data_save_data (ed, buf, size);


done:

  if (ed)
    exif_data_unref (ed);

  return;
}
Exemple #4
0
void Image::load_tags( gpointer data , gsize size )
{
	g_assert( data );
	g_assert( size );

	if ( ExifData * ed = exif_data_new_from_data( ( const unsigned char * ) data , size ) )
	{
		load_exif_tags( ed , tags );
		exif_data_unref( ed );
	}
	else
	{
		tplog2( "  FAILED TO LOAD TAGS" );
	}
}
Exemple #5
0
static int
parse_exif( VipsImage *im, void *data, int data_length )
{
#ifdef HAVE_EXIF
    {
        ExifData *ed;
        VipsExif ve;

        if( !(ed = exif_data_new_from_data( data, data_length )) )
            return( -1 );

#ifdef DEBUG_VERBOSE
        show_tags( ed );
        show_values( ed );
#endif /*DEBUG_VERBOSE*/

        /* Attach informational fields for what we find.

        	FIXME ... better to have this in the UI layer?

        	Or we could attach non-human-readable tags here (int,
        	double etc) and then move the human stuff to the UI
        	layer?

         */
        ve.image = im;
        ve.ed = ed;
        exif_data_foreach_content( ed,
        (ExifDataForeachContentFunc) attach_exif_content, &ve );

        /* Look for resolution fields and use them to set the VIPS
         * xres/yres fields.
         */
        res_from_exif( im, ed );

        attach_thumbnail( im, ed );

        exif_data_free( ed );
    }
#endif /*HAVE_EXIF*/

    return( 0 );
}
Exemple #6
0
/*  Replacement for exif_data_new_from_file() to work around
 *  filename encoding problems (see bug #335391).
 */
ExifData *
jpeg_exif_data_new_from_file (const gchar  *filename,
                              GError      **error)
{
  ExifData    *data;
  GMappedFile *file;

  g_return_val_if_fail (filename != NULL, NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  file = g_mapped_file_new (filename, FALSE, error);
  if (! file)
    return NULL;

  data = exif_data_new_from_data ((guchar *) g_mapped_file_get_contents (file),
                                  g_mapped_file_get_length (file));

  g_mapped_file_free (file);

  return data;
}
Exemple #7
0
void
metadataparse_exif_tag_list_add (GstTagList * taglist, GstTagMergeMode mode,
    GstAdapter * adapter, MetadataTagMapping mapping)
{
  const guint8 *buf;
  guint32 size;
  ExifData *exif = NULL;
  MEUserData user_data = { taglist, mode, 2, -1, 'k', 'k' };

  if (adapter == NULL || (size = gst_adapter_available (adapter)) == 0) {
    goto done;
  }

  /* add chunk tag */
  if (mapping & METADATA_TAG_MAP_WHOLECHUNK)
    metadataparse_util_tag_list_add_chunk (taglist, mode, GST_TAG_EXIF,
        adapter);

  if (!(mapping & METADATA_TAG_MAP_INDIVIDUALS))
    goto done;

  buf = gst_adapter_peek (adapter, size);

  exif = exif_data_new_from_data (buf, size);
  if (exif == NULL) {
    goto done;
  }

  exif_data_foreach_content (exif,
      metadataparse_exif_data_foreach_content_func, (void *) &user_data);

done:

  if (exif)
    exif_data_unref (exif);

  return;

}
Exemple #8
0
int
main (int argc, char **argv)
{
	ExifData *d;
	unsigned int buf_size;
	unsigned char *buf;
	int r;

	if (argc <= 1) {
		fprintf (stderr, "You need to supply a filename!\n");
		return 1;
	}

	fprintf (stdout, "Loading '%s'...\n", argv[1]);
	d = exif_data_new_from_file (argv[1]);
	if (!d) {
		fprintf (stderr, "Could not load data from '%s'!\n", argv[1]);
		return 1;
	}
	fprintf (stdout, "Loaded '%s'.\n", argv[1]);

	fprintf (stdout, "######### Test 1 #########\n");
	r = test_exif_data (d);
	if (r) return r;

	exif_data_save_data (d, &buf, &buf_size);
	exif_data_unref (d);
	d = exif_data_new_from_data (buf, buf_size);
	free (buf);

	fprintf (stdout, "######### Test 2 #########\n");
	r = test_exif_data (d);
	if (r) return r;

	fprintf (stdout, "Test successful!\n");

	return 1;
}
Exemple #9
0
void
jpeg_data_load_data (JPEGData *data, const unsigned char *d,
		     unsigned int size)
{
	unsigned int i, o, len;
	JPEGSection *s;
	JPEGMarker marker;

	if (!data) return;
	if (!d) return;

	for (o = 0; o < size;) {

		/*
		 * JPEG sections start with 0xff. The first byte that is
		 * not 0xff is a marker (hopefully).
		 */
		for (i = 0; i < MIN(7, size - o); i++)
			if (d[o + i] != 0xff)
				break;
		if (!JPEG_IS_MARKER (d[o + i])) {
			exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "jpeg-data",
					_("Data does not follow JPEG specification."));
			return;
		}
		marker = (JPEGMarker)d[o + i];

		/* Append this section */
		jpeg_data_append_section (data);
		if (!data->count) return;
		s = &data->sections[data->count - 1];
		s->marker = marker;
		o += i + 1;

		switch (s->marker) {
		case JPEG_MARKER_SOI:
		case JPEG_MARKER_EOI:
			break;
		default:

			/* Read the length of the section */
			len = ((d[o] << 8) | d[o + 1]) - 2;
			if (len > size) { o = size; break; }
			o += 2;
			if (o + len > size) { o = size; break; }

			switch (s->marker) {
			case JPEG_MARKER_APP1:
				s->content.app1 = exif_data_new_from_data (
							d + o - 4, len + 4);
				break;
			default:
				s->content._generic.data =
						(unsigned char*)malloc (sizeof (char) * len);
				if (!s->content._generic.data) return;
				s->content._generic.size = len;
				memcpy (s->content._generic.data, &d[o], len);

				/* In case of SOS, image data will follow. */
				if (s->marker == JPEG_MARKER_SOS) {
					/* -2 means 'take all but the last 2 bytes which are hoped to be JPEG_MARKER_EOI */
					data->size = size - 2 - o - len;
					if (d[o + len + data->size] != 0xFF) {
						/* A truncated file (i.e. w/o JPEG_MARKER_EOI at the end).
						   Instead of trying to use the last two bytes as marker,
						   touching memory beyond allocated memory and posssibly saving
						   back screwed file, we rather take the rest of the file. */
						data->size += 2;
					}
					data->data = (unsigned char*)malloc (
						sizeof (char) * data->size);
					if (!data->data) return;
					memcpy (data->data, d + o + len,
						data->size);
					o += data->size;
				}
				break;
			}
			o += len;
			break;
		}
	}
}
Exemple #10
0
void
jpeg_data_load_data (JPEGData *data, const unsigned char *d,
		     unsigned int size)
{
	unsigned int i, o, len;
	JPEGSection *s;
	JPEGMarker marker;

	if (!data)
		return;
	if (!d)
		return;

#ifdef DEBUG
	printf ("Parsing %i bytes...\n", size);
#endif

	for (o = 0; o < size;) {

		/*
		 * JPEG sections start with 0xff. The first byte that is
		 * not 0xff is a marker (hopefully).
		 */
		for (i = 0; i < 7; i++)
			if (d[o + i] != 0xff)
				break;
		if (!JPEG_IS_MARKER (d[o + i]))
			return;
		marker = d[o + i];

#ifdef DEBUG
		printf ("Found marker 0x%x ('%s') at %i.\n", marker,
			jpeg_marker_get_name (marker), o + i);
#endif

		/* Append this section */
		jpeg_data_append_section (data);
		s = &data->sections[data->count - 1];
		s->marker = marker;
		s->content.generic.data = NULL;
		o += i + 1;

		switch (s->marker) {
		case JPEG_MARKER_SOI:
		case JPEG_MARKER_EOI:
			break;
		default:

			/* Read the length of the section */
			len = ((d[o] << 8) | d[o + 1]) - 2;
			if (len > size) { o = size; break; }
			o += 2;
			if (o + len > size) { o = size; break; }

			switch (s->marker) {
			case JPEG_MARKER_APP1:
				s->content.app1 = exif_data_new_from_data (
							d + o - 4, len + 4);
				break;
			default:
				s->content.generic.size = len;
				s->content.generic.data =
						malloc (sizeof (char) * len);
				memcpy (s->content.generic.data, &d[o], len);

				/* In case of SOS, image data will follow. */
				if (s->marker == JPEG_MARKER_SOS) {
					data->size = size - 2 - o - len;
					data->data = malloc (
						sizeof (char) * data->size);
					memcpy (data->data, d + o + len,
						data->size);
					o += data->size;
				}
				break;
			}
			o += len;
			break;
		}
	}
}
static int
write_exif( Write *write )
{
#ifdef HAVE_EXIF
	unsigned char *data;
	size_t data_length;
	unsigned int idl;
	ExifData *ed;

	/* Either parse from the embedded EXIF, or if there's none, make
	 * some fresh EXIF we can write the resolution to.
	 */
	if( vips_image_get_typeof( write->in, VIPS_META_EXIF_NAME ) ) {
		if( vips_image_get_blob( write->in, VIPS_META_EXIF_NAME, 
			(void *) &data, &data_length ) )
			return( -1 );

		if( !(ed = exif_data_new_from_data( data, data_length )) )
			return( -1 );
	}
	else  {
		ed = exif_data_new();

		exif_data_set_option( ed, 
			EXIF_DATA_OPTION_FOLLOW_SPECIFICATION );
		exif_data_set_data_type( ed, EXIF_DATA_TYPE_COMPRESSED );
		exif_data_set_byte_order( ed, EXIF_BYTE_ORDER_INTEL );
	
		/* Create the mandatory EXIF fields with default data.
		 */
		exif_data_fix( ed );
	}

	/* Update EXIF tags from the image metadata.
	 */
	vips_exif_update( ed, write->in );

	/* Update EXIF resolution from the vips image header.
	 */
	if( vips__set_exif_resolution( ed, write->in ) ) {
		exif_data_free( ed );
		return( -1 );
	}

	/* Update EXIF image dimensions from the vips image header.
	 */
	if( set_exif_dimensions( ed, write->in ) ) {
		exif_data_free( ed );
		return( -1 );
	}

	/* Update EXIF orientation from the vips image header.
	 */
	if( set_exif_orientation( ed, write->in ) ) {
		exif_data_free( ed );
		return( -1 );
	}

	/* Reserialise and write. exif_data_save_data() returns an int for some
	 * reason.
	 */
	exif_data_save_data( ed, &data, &idl );
	if( !idl ) {
		vips_error( "VipsJpeg", "%s", _( "error saving EXIF" ) );
		exif_data_free( ed );
		return( -1 );
	}
	data_length = idl;

#ifdef DEBUG
	printf( "write_exif: attaching %zd bytes of EXIF\n", data_length  );
#endif /*DEBUG*/

	exif_data_free( ed );
	jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1, data, data_length );
	free( data );
#else /*!HAVE_EXIF*/
	/* No libexif ... just copy the embedded EXIF over.
	 */
	if( write_blob( write, VIPS_META_EXIF_NAME, JPEG_APP0 + 1 ) )
		return( -1 );
#endif /*!HAVE_EXIF*/

	return( 0 );
}
Exemple #12
0
static int
read_exif( IMAGE *im, void *data, int data_length )
{
	char *data_copy;

	/* Horrifyingly, some JPEGs have several APP1 sections. We must only
	 * use the first one that starts "Exif.."
	 */
	if( ((char *) data)[0] != 'E' ||
		((char *) data)[1] != 'x' ||
		((char *) data)[2] != 'i' ||
		((char *) data)[3] != 'f' )
		return( 0 );
	if( im_header_get_typeof( im, IM_META_EXIF_NAME ) ) 
		return( 0 );

	/* Always attach a copy of the unparsed exif data.
	 */
	if( !(data_copy = im_malloc( NULL, data_length )) )
		return( -1 );
	memcpy( data_copy, data, data_length );
	if( im_meta_set_blob( im, IM_META_EXIF_NAME, 
		(im_callback_fn) im_free, data_copy, data_length ) ) {
		im_free( data_copy );
		return( -1 );
	}

#ifdef HAVE_EXIF
{
	ExifData *ed;

	if( !(ed = exif_data_new_from_data( data, data_length )) )
		return( -1 );

	if( ed->size > 0 ) {
#ifdef DEBUG_VERBOSE
		show_tags( ed );
		show_values( ed );
#endif /*DEBUG_VERBOSE*/

		/* Attach informational fields for what we find.

			FIXME ... better to have this in the UI layer?

			Or we could attach non-human-readable tags here (int, 
			double etc) and then move the human stuff to the UI 
			layer?

		 */
		exif_data_foreach_content( ed, 
			(ExifDataForeachContentFunc) attach_exif_content, im );

		/* Look for resolution fields and use them to set the VIPS 
		 * xres/yres fields.
		 */
		set_vips_resolution( im, ed );

		attach_thumbnail( im, ed );
	}

	exif_data_free( ed );
}
#endif /*HAVE_EXIF*/

	return( 0 );
}
Exemple #13
0
static int Lloadbuffer (lua_State *L) { /** loadbuffer(buf) */
  size_t length;
  const char *buffer = luaL_checklstring(L, 1, &length);
  pushdata(L, exif_data_new_from_data((const unsigned char *)buffer, length));
  return 1;
}
int image_jpeg_read_header(MediaScanImage *i, MediaScanResult *r) {
  int ret = 1;
  int x;

  JPEGData *j = malloc(sizeof(JPEGData));
  i->_jpeg = (void *)j;
  LOG_MEM("new JPEGData @ %p\n", i->_jpeg);

  j->cinfo = malloc(sizeof(struct jpeg_decompress_struct));
  j->jpeg_error_pub = malloc(sizeof(struct jpeg_error_mgr));
  LOG_MEM("new JPEG cinfo @ %p\n", j->cinfo);
  LOG_MEM("new JPEG error_pub @ %p\n", j->jpeg_error_pub);

  j->cinfo->err = jpeg_std_error(j->jpeg_error_pub);
  j->jpeg_error_pub->error_exit = libjpeg_error_handler;
  j->jpeg_error_pub->output_message = libjpeg_output_message;

  if (setjmp(setjmp_buffer)) {
    image_jpeg_destroy(i);
    return 0;
  }

  // Save filename in case any warnings/errors occur
  strncpy(Filename, r->path, FILENAME_LEN);
  if (strlen(r->path) > FILENAME_LEN)
    Filename[FILENAME_LEN] = 0;

  jpeg_create_decompress(j->cinfo);

  // Init custom source manager to read from existing buffer
  image_jpeg_buf_src(i, r);

  // Save APP1 marker for EXIF
  jpeg_save_markers(j->cinfo, 0xE1, 1024 * 64);

  jpeg_read_header(j->cinfo, TRUE);

  i->width = j->cinfo->image_width;
  i->height = j->cinfo->image_height;
  i->channels = j->cinfo->num_components;
  r->mime_type = MIME_IMAGE_JPEG;

  // Match with DLNA profile
  for (x = 0; jpeg_profiles_mapping[x].profile; x++) {
    if (i->width <= jpeg_profiles_mapping[x].max_width && i->height <= jpeg_profiles_mapping[x].max_height) {
      r->dlna_profile = jpeg_profiles_mapping[x].profile->id;
      break;
    }
  }

  // Process Exif tag
  if (j->cinfo->marker_list != NULL) {
    jpeg_saved_marker_ptr marker = j->cinfo->marker_list;

    while (marker != NULL) {
      if (marker->marker == 0xE1
          && marker->data[0] == 'E' && marker->data[1] == 'x' && marker->data[2] == 'i' && marker->data[3] == 'f') {
        ExifData *exif;

        result_create_tag(r, "Exif");

        LOG_DEBUG("Parsing EXIF tag of size %d\n", marker->data_length);
        exif = exif_data_new_from_data(marker->data, marker->data_length);
        LOG_MEM("new EXIF data @ %p\n", exif);
        if (exif != NULL) {
          exif_data_foreach_content(exif, parse_exif_ifd, (void *)r);
          LOG_MEM("destroy EXIF data @ %p\n", exif);
          exif_data_free(exif);
        }

        break;
      }

      marker = marker->next;
    }
  }

  return ret;
}
Exemple #15
0
psd_status psd_get_image_resource(psd_context * context)
{
	psd_int length, i, size;
	psd_ushort ID;
	psd_uint tag;
	psd_uchar sizeofname;
	psd_int sizeofdata, prev_stream_pos;
	psd_uchar * buffer;
	//psd_status status;

	// Length of image resource section
	length = psd_stream_get_int(context);
	if(length <= 0)
		return psd_status_done;

	// default
	context->global_angle = 30;
	context->global_altitude = 30;

	while(length > 0)
	{
		// Signature: '8BIM'
		tag = psd_stream_get_int(context);
		if(tag == '8BIM')
		{
			length -= 4;
			// Unique identifier for the resource
			ID = psd_stream_get_short(context);
			length -= 2;
			// Name: Pascal string, padded to make the size even (a null name consists of two bytes of 0)
			sizeofname = psd_stream_get_char(context);
			if((sizeofname & 0x01) == 0)
				sizeofname ++;
			psd_stream_get_null(context, sizeofname);
			length -= sizeofname + 1;
			// Actual size of resource data that follows
			sizeofdata = psd_stream_get_int(context);
			length -= 4;
			// resource data must be even
			if(sizeofdata & 0x01)
				sizeofdata ++;
			length -= sizeofdata;

			switch(context->load_tag)
			{
				case psd_load_tag_thumbnail:
					if(ID != 1033 && ID != 1036)
					{
						psd_stream_get_null(context, sizeofdata);
						continue;
					}
					break;
				case psd_load_tag_merged:
					// alpha channels information
					if(ID != 1006 && ID != 1045 && ID != 1053)
					{
						psd_stream_get_null(context, sizeofdata);
						continue;
					}
					break;
				case psd_load_tag_exif:
					if(ID != 1058 && ID != 1059)
					{
						psd_stream_get_null(context, sizeofdata);
						continue;
					}
					break;
			}

			prev_stream_pos = context->stream.current_pos;

			if(sizeofdata > 0)
			{
				switch(ID)
				{
					// ResolutionInfo structure
					case 1005:
						// Horizontal resolution in pixels per inch.
						context->resolution_info.hres = psd_stream_get_int(context) / 65536.0f;
						// 1=display horitzontal resolution in pixels per inch; 2=display horitzontal resolution in pixels per cm.
						context->resolution_info.hres_unit = psd_stream_get_short(context);
						// Display width as 1=inches; 2=cm; 3=points; 4=picas; 5=columns.
						context->resolution_info.width_unit = psd_stream_get_short(context);
						// Vertial resolution in pixels per inch.
						context->resolution_info.vres = psd_stream_get_int(context) / 65536.0f;
						// 1=display vertical resolution in pixels per inch; 2=display vertical resolution in pixels per cm.
						context->resolution_info.vres_unit = psd_stream_get_short(context);
						// Display height as 1=inches; 2=cm; 3=points; 4=picas; 5=columns.
						context->resolution_info.height_unit = psd_stream_get_short(context);
						context->fill_resolution_info = psd_true;
						break;

					// Names of the alpha channels as a series of Pascal strings.
					case 1006:
						buffer = (psd_uchar *)psd_malloc(sizeofdata);
						if(buffer == NULL)
							return psd_status_malloc_failed;
						psd_stream_get(context, buffer, sizeofdata);
						if(context->alpha_channels == 0)
						{
							size = 0;
							// maybe odd
							while(size + 1 < sizeofdata)
							{
								size += *(buffer + size) + 1;
								context->alpha_channels ++;
							}
							context->color_channels = context->channels - context->alpha_channels;
							context->alpha_channel_info = (psd_alpha_channel_info *)psd_malloc(context->alpha_channels * sizeof(psd_alpha_channel_info));
							if(context->alpha_channel_info == NULL)
							{
								psd_free(buffer);
								return psd_status_malloc_failed;
							}
							memset(context->alpha_channel_info, 0, context->alpha_channels * sizeof(psd_alpha_channel_info));
						}

						size = 0;
						for(i = 0; i < context->alpha_channels; i ++)
						{
							memcpy(context->alpha_channel_info[i].name, buffer + size + 1, *(buffer + size));
							size += *(buffer + size) + 1;
						}
						psd_free(buffer);
						context->fill_alpha_channel_info = psd_true;
						break;

					// DisplayInfo structure
					case 1007:
						context->display_info.color = psd_stream_get_space_color(context);
						// 0..100
						context->display_info.opacity = psd_stream_get_short(context);
						psd_assert(context->display_info.opacity >= 0 && context->display_info.opacity <= 100);
						// selected = 0, protected = 1
						context->display_info.kind = psd_stream_get_char(context);
						// maybe be 2 when color mode is multichannel
						//psd_assert(context->display_info.kind == 0 || context->display_info.kind == 1);
						// padding
						psd_stream_get_char(context);
						context->fill_display_info = psd_true;
						break;

					// The caption as a Pascal string.
					case 1008:
						size = psd_stream_get_char(context);
						psd_stream_get(context, context->caption, size);
						break;

					// Layer state information
					// 2 bytes containing the index of target layer (0 = bottom layer).
					case 1024:
						context->target_layer_index = psd_stream_get_short(context);
						break;

					// Layers group information
					// 2 bytes per layer containing a group ID for the dragging groups. Layers in
					// a group have the same group ID.
					case 1026:
						context->layer_group_count = sizeofdata / 2;
						context->layer_group_id = (psd_ushort *)psd_malloc(context->layer_group_count * 2);
						if(context->layer_group_id == NULL)
							return psd_status_malloc_failed;
						for(i = 0; i < context->layer_group_count; i ++)
							context->layer_group_id[i] = psd_stream_get_short(context);
						context->fill_layer_group = psd_true;
						break;

					// (Photoshop 4.0) Thumbnail resource for Photoshop 4.0 only
					case 1033:
					// (Photoshop 5.0) Thumbnail resource (supersedes resource 1033)
					case 1036:
						if(context->load_tag == psd_load_tag_layer)
						{
							psd_stream_get_null(context, sizeofdata);
							continue;
						}

						// 1 = kJpegRGB . Also supports kRawRGB (0).
						context->thumbnail_resource.format = psd_stream_get_int(context);
						psd_assert(context->thumbnail_resource.format == 0 || context->thumbnail_resource.format == 1);
						// Width of thumbnail in pixels.
						context->thumbnail_resource.width = psd_stream_get_int(context);
						// Height of thumbnail in pixels.
						context->thumbnail_resource.height = psd_stream_get_int(context);
						// Padded row bytes = (width * bits per pixel + 31) / 32 * 4.
						context->thumbnail_resource.width_bytes = psd_stream_get_int(context);
						// Total size = widthbytes * height * planes
						context->thumbnail_resource.total_size = psd_stream_get_int(context);
						// Used for consistency check.
						context->thumbnail_resource.size_after_compression = psd_stream_get_int(context);
						context->thumbnail_resource.bits_per_pixel = psd_stream_get_short(context);
						// Bits per pixel. = 24
						psd_assert(context->thumbnail_resource.bits_per_pixel == 24);
						context->thumbnail_resource.number_of_planes = psd_stream_get_short(context);
						// Number of planes. = 1
						psd_assert(context->thumbnail_resource.number_of_planes == 1);
#ifdef PSD_INCLUDE_LIBJPEG
						if(context->thumbnail_resource.format == 0)
						{
							status = psd_thumbnail_decode_raw(&context->thumbnail_resource.thumbnail_data, 
								context->thumbnail_resource.size_after_compression, context);
						}
						else
						{
							status = psd_thumbnail_decode_jpeg(&context->thumbnail_resource.thumbnail_data, 
								context->thumbnail_resource.size_after_compression, context);
						}
						if(status != psd_status_done)
							return status;
#else
						context->thumbnail_resource.jfif_data = (psd_uchar *)psd_malloc(sizeofdata - 28);
						if(context->thumbnail_resource.jfif_data == NULL)
							return psd_status_malloc_failed;
						psd_stream_get(context, context->thumbnail_resource.jfif_data, sizeofdata - 28);
#endif
						context->fill_thumbnail_resource = psd_true;
						break;

					// (Photoshop 4.0) Copyright flag
					// Boolean indicating whether image is copyrighted. Can be set via
					// Property suite or by user in File Info...
					case 1034:
						context->copyright_flag = (psd_bool)psd_stream_get_short(context);
						psd_assert(context->copyright_flag == 0 || context->copyright_flag == 1);
						break;

					// (Photoshop 5.0) Global Angle
					// 4 bytes that contain an integer between 0 and 359, which is the global
					// lighting angle for effects layer. If not present, assumed to be 30.
					case 1037:
						context->global_angle = psd_stream_get_int(context);
						break;

					// (Photoshop 5.0) Effects visible
					// 1-byte global flag to show/hide all the effects layer. Only present when
					// they are hidden.
					case 1042:
						context->effects_visible = (psd_bool)psd_stream_get_short(context);
						psd_assert(context->effects_visible == 0 || context->effects_visible == 1);
						break;

					// (Photoshop 5.0) Unicode Alpha Names
					// Unicode string (4 bytes length followed by string).
					case 1045:
						buffer = (psd_uchar *)psd_malloc(sizeofdata);
						if(buffer == NULL)
							return psd_status_malloc_failed;
						psd_stream_get(context, buffer, sizeofdata);
						if(context->alpha_channels == 0)
						{
							size = 0;
							while(size < sizeofdata)
							{
								size += PSD_CHAR_TO_INT(buffer + size) * 2 + 4;
								context->alpha_channels ++;
							}
							context->color_channels = context->channels - context->alpha_channels;
							context->alpha_channel_info = (psd_alpha_channel_info *)psd_malloc(context->alpha_channels * sizeof(psd_alpha_channel_info));
							if(context->alpha_channel_info == NULL)
							{
								psd_free(buffer);
								return psd_status_malloc_failed;
							}
							memset(context->alpha_channel_info, 0, context->alpha_channels * sizeof(psd_alpha_channel_info));
						}

						size = 0;
						for(i = 0; i < context->alpha_channels; i ++)
						{
							context->alpha_channel_info[i].unicode_name_length = PSD_CHAR_TO_INT(buffer + size);
							context->alpha_channel_info[i].unicode_name = (psd_ushort *)psd_malloc(2 * context->alpha_channel_info[i].unicode_name_length);
							if(context->alpha_channel_info[i].unicode_name == NULL)
							{
								psd_free(buffer);
								return psd_status_malloc_failed;
							}
							memcpy(context->alpha_channel_info[i].unicode_name, buffer + size + 4, 2 * context->alpha_channel_info[i].unicode_name_length);
							size += 2 * context->alpha_channel_info[i].unicode_name_length + 4;
						}
						psd_free(buffer);
						context->fill_alpha_channel_info = psd_true;
						break;

					// (Photoshop 6.0) Indexed Color Table Count
					// 2 bytes for the number of colors in table that are actually defined
					case 1046:
						context->indexed_color_table_count = psd_stream_get_short(context);
						break;

					// (Photoshop 6.0) Transparency Index.
					// 2 bytes for the index of transparent color, if any.
					case 1047:
						context->transparency_index = psd_stream_get_short(context);
						break;

					// (Photoshop 6.0) Global Altitude
					// 4 byte entry for altitude
					case 1049:
						context->global_altitude = psd_stream_get_int(context);
						break;

					// (Photoshop 6.0) Alpha Identifiers
					// 4 bytes of length, followed by 4 bytes each for every alpha identifier.
					case 1053:
						if(context->alpha_channels == 0)
						{
							context->alpha_channels = sizeofdata / 4;
							context->color_channels = context->channels - context->alpha_channels;
							context->alpha_channel_info = (psd_alpha_channel_info *)psd_malloc(context->alpha_channels * sizeof(psd_alpha_channel_info));
							if(context->alpha_channel_info == NULL)
								return psd_status_malloc_failed;
							memset(context->alpha_channel_info, 0, context->alpha_channels * sizeof(psd_alpha_channel_info));
						}

						for(i = 0; i < context->alpha_channels; i ++)
						{
							context->alpha_channel_info[i].identifier = psd_stream_get_int(context);
						}
						context->fill_alpha_channel_info = psd_true;
						break;

					// (Photoshop 6.0) Version Info
					// 4 bytes version, 1 byte hasRealMergedData, Unicode string: writer
					// name, Unicode string: reader name, 4 bytes file version.
					case 1057:
						context->version_info.version = psd_stream_get_int(context);
						context->version_info.has_real_merged_data = psd_stream_get_bool(context);
						context->version_info.writer_name_length = psd_stream_get_int(context);
						context->version_info.writer_name = (psd_ushort *)psd_malloc(2 * context->version_info.writer_name_length);
						if(context->version_info.writer_name == NULL)
							return psd_status_malloc_failed;
						psd_stream_get(context, (psd_uchar *)context->version_info.writer_name, 2 * context->version_info.writer_name_length);
						context->version_info.reader_name_length = psd_stream_get_int(context);
						context->version_info.reader_name = (psd_ushort *)psd_malloc(2 * context->version_info.reader_name_length);
						if(context->version_info.reader_name == NULL)
							return psd_status_malloc_failed;
						psd_stream_get(context, (psd_uchar *)context->version_info.reader_name, 2 * context->version_info.reader_name_length);
						context->version_info.file_version = psd_stream_get_int(context);
						context->fill_version_info = psd_true;
						break;

// if you don't need the following image resource,
// you can undef this macro in psd_config.h to reduce the parsing time
/////////////////////////////////////////////////////////////////////////////////////////////
#ifdef PSD_GET_ALL_IMAGE_RESOURCE

					// Border information
					case 1009:
						// a fixed number (2 bytes real, 2 bytes fraction) for the border width
						context->border_info.border_width = psd_fixed_16_16_tofloat((psd_fixed_16_16)psd_stream_get_int(context));
						// 2 bytes for border units (1 = inches, 2 = cm, 3 = points, 4 = picas, 5 = columns).
						context->border_info.border_units = (psd_units)psd_stream_get_short(context);
						psd_assert(context->border_info.border_units >= psd_units_inches && 
							context->border_info.border_units <= psd_units_columns);
						context->fill_border_info = psd_true;
						break;

					// Print flags
					// A series of one-byte boolean values (see Page Setup dialog): labels, crop
					// marks, color bars, registration marks, negative, flip, interpolate, caption,
					// print flags.
					case 1011:
						context->print_flags.labels = psd_stream_get_bool(context);
						context->print_flags.crop_marks = psd_stream_get_bool(context);
						context->print_flags.color_bars = psd_stream_get_bool(context);
						context->print_flags.registration_marks = psd_stream_get_bool(context);
						context->print_flags.negative = psd_stream_get_bool(context);
						context->print_flags.flip = psd_stream_get_bool(context);
						context->print_flags.interpolate = psd_stream_get_bool(context);
						context->print_flags.caption = psd_stream_get_bool(context);
						context->print_flags.print_flags = psd_stream_get_bool(context);
						context->fill_print_flags = psd_true;
						break;

					// (Photoshop 4.0) Grid and guides information
					case 1032:
						// Version ( = 1)
						psd_assert(psd_stream_get_int(context) == 1);
						// Future implementation of document-specific grids (4 bytes horizontal, 4 bytes vertical).
						context->grid_guides.horz_grid = psd_stream_get_int(context);
						context->grid_guides.vert_grid = psd_stream_get_int(context);
						// Number of guide resource blocks (can be 0).
						context->grid_guides.guide_count = psd_stream_get_int(context);
						if(context->grid_guides.guide_count > 0)
						{
							//Location of guide in document coordinates. Since the guide is either vertical or horizontal, this only has to be one component of the coordinate.
							context->grid_guides.guide_coordinate = (psd_int *)psd_malloc(context->grid_guides.guide_count * 4);
							// Direction of guide. VHSelect is a system type of psd_uchar where 0 = vertical, 1 = horizontal.
							context->grid_guides.guide_direction = (psd_uchar *)psd_malloc(context->grid_guides.guide_count);
							if(context->grid_guides.guide_coordinate == NULL ||
								context->grid_guides.guide_direction == NULL)
							{
								psd_free(context->grid_guides.guide_coordinate);
								context->grid_guides.guide_coordinate = NULL;
								psd_free(context->grid_guides.guide_direction);
								context->grid_guides.guide_direction = NULL;
								return psd_status_malloc_failed;
							}
							for(i = 0; i < context->grid_guides.guide_count; i ++)
							{
								context->grid_guides.guide_coordinate[i] = psd_stream_get_int(context);
								context->grid_guides.guide_direction[i] = psd_stream_get_char(context);
							}
						}
						context->fill_grid_and_guides_info = psd_true;
						break;

					// (Photoshop 5.0) Color samplers resource
					case 1038:
						// Version ( = 1)
						psd_assert(psd_stream_get_int(context) == 1);
						// Number of color samplers to follow.
						context->color_samplers.number_of_color_samplers = psd_stream_get_int(context);
						if (context->color_samplers.number_of_color_samplers > 0)
						{
							context->color_samplers.resource = (psd_color_samplers_resource *)
								psd_malloc(sizeof(psd_color_samplers_resource) * context->color_samplers.number_of_color_samplers);
							if (context->color_samplers.resource == NULL)
								return psd_status_malloc_failed;
							for (i = 0; i < context->color_samplers.number_of_color_samplers; i ++)
							{
								// The vertical and horizontal position of the point (4 bytes each).
								context->color_samplers.resource[i].vertical_position = psd_stream_get_int(context);
								context->color_samplers.resource[i].horizontal_position = psd_stream_get_int(context);
								// Color Space
								context->color_samplers.resource[i].color_space = psd_stream_get_short(context);
							}
						}
						context->fill_color_samplers = psd_true;
						break;
						
					// (Photoshop 6.0) Slices
					case 1050:
						// Version ( = 6)
						psd_assert(psd_stream_get_int(context) == 6);
						// Bounding rectangle for all of the slices: top, left, bottom, right of all the slices
						context->slices_resource.bounding_top = psd_stream_get_int(context);
						context->slices_resource.bounding_left = psd_stream_get_int(context);
						context->slices_resource.bounding_bottom = psd_stream_get_int(context);
						context->slices_resource.bounding_right = psd_stream_get_int(context);
						// Name of group of slices: Unicode string
						size = psd_stream_get_int(context) * 2;
						psd_stream_get_null(context, size);
						// Number of slices to follow
						context->slices_resource.number_of_slices = psd_stream_get_int(context);
						context->slices_resource.slices_resource_block = (psd_slices_resource_block *)psd_malloc(context->slices_resource.number_of_slices * sizeof(psd_slices_resource_block));
						if(context->slices_resource.slices_resource_block == NULL)
							return psd_status_malloc_failed;
						memset(context->slices_resource.slices_resource_block, 0, context->slices_resource.number_of_slices * sizeof(psd_slices_resource_block));
						for(i = 0; i < context->slices_resource.number_of_slices; i ++)
						{
							context->slices_resource.slices_resource_block[i].id = psd_stream_get_int(context);
							context->slices_resource.slices_resource_block[i].group_id = psd_stream_get_int(context);
							context->slices_resource.slices_resource_block[i].origin = psd_stream_get_int(context);
							// NOTE: Only present if Origin = 1
							if(context->slices_resource.slices_resource_block[i].origin == 1)
								context->slices_resource.slices_resource_block[i].associated_layer_id = psd_stream_get_int(context);
							// Name: Unicode string
							size = psd_stream_get_int(context) * 2;
							psd_stream_get_null(context, size);
							context->slices_resource.slices_resource_block[i].type = psd_stream_get_int(context);
							context->slices_resource.slices_resource_block[i].left = psd_stream_get_int(context);
							context->slices_resource.slices_resource_block[i].top = psd_stream_get_int(context);
							context->slices_resource.slices_resource_block[i].right = psd_stream_get_int(context);
							context->slices_resource.slices_resource_block[i].bottom = psd_stream_get_int(context);
							// URL: Unicode string
							size = psd_stream_get_int(context) * 2;
							psd_stream_get_null(context, size);
							// Target: Unicode string
							size = psd_stream_get_int(context) * 2;
							psd_stream_get_null(context, size);
							// Message: Unicode string
							size = psd_stream_get_int(context) * 2;
							psd_stream_get_null(context, size);
							// Alt Tag: Unicode string
							size = psd_stream_get_int(context) * 2;
							psd_stream_get_null(context, size);
							context->slices_resource.slices_resource_block[i].cell_text_is_html = psd_stream_get_char(context);
							// Cell text: Unicode string
							size = psd_stream_get_int(context) * 2;
							psd_stream_get_null(context, size);
							context->slices_resource.slices_resource_block[i].horizontal_alignment = psd_stream_get_int(context);
							context->slices_resource.slices_resource_block[i].veritcal_alignment = psd_stream_get_int(context);
							context->slices_resource.slices_resource_block[i].color = psd_argb_to_color(psd_stream_get_char(context), 
								psd_stream_get_char(context), psd_stream_get_char(context), psd_stream_get_char(context));
						}
						context->fill_slices_resource = psd_true;
						break;

					// (Photoshop 6.0) URL List
					case 1054:
						// 4 byte count of URLs
						context->url_list.number_of_urls = psd_stream_get_int(context);
						if (context->url_list.number_of_urls > 0)
						{
							context->url_list.items = (psd_url_list_item *)psd_malloc(sizeof(psd_url_list_item) *
								context->url_list.number_of_urls);
							if (context->url_list.items == NULL)
								return psd_status_malloc_failed;
							memset(context->url_list.items, 0, sizeof(psd_url_list_item) * context->url_list.number_of_urls);
						}
						for (i = 0; i < context->url_list.number_of_urls; i ++)
						{
							// followed by 4 byte long
							context->url_list.items[i].tag = psd_stream_get_int(context);
							// 4 byte ID
							context->url_list.items[i].ID = psd_stream_get_int(context);
							// Unicode string for each count.
							context->url_list.items[i].name_length = psd_stream_get_int(context);
							context->url_list.items[i].name = (psd_ushort *)psd_malloc(2 * context->url_list.items[i].name_length);
							if (context->url_list.items[i].name == NULL)
								return psd_status_malloc_failed;
							psd_stream_get(context, (psd_uchar *)context->url_list.items[i].name, 
								2 * context->url_list.items[i].name_length);
						}
						context->fill_url_list = psd_true;
						break;

					// (Photoshop 7.0) EXIF data 1
					case 1058:
					// (Photoshop 7.0) EXIF data 3
					// http://www.pima.net/standards/it10/PIMA15740/exif.htm
					case 1059:
						// avoid to get the exif data for twice
						psd_assert(context->fill_exif_data == psd_false);
#	ifdef PSD_INCLUDDE_LIBEXIF
						buffer = (psd_uchar *)psd_malloc(sizeofdata + sizeof(ExifHeader));
						if (buffer == NULL)
							return psd_status_malloc_failed;
						psd_stream_get(context, buffer + sizeof(ExifHeader), sizeofdata);
						memcpy(buffer, ExifHeader, sizeof(ExifHeader));
						context->exif_data = (psd_uchar *)exif_data_new_from_data(buffer, sizeofdata + sizeof(ExifHeader));
						psd_free(buffer);
						context->fill_exif_data = psd_true;
#	else // ifdef PSD_INCLUDDE_LIBEXIF
						context->exif_data = (psd_uchar *)psd_malloc(sizeofdata);
						if (context->exif_data == NULL)
							return psd_status_malloc_failed;
						psd_stream_get(context, context->exif_data, sizeofdata);
						context->exif_data_length = sizeofdata;
						context->fill_exif_data = psd_true;
#	endif // ifdef PSD_INCLUDDE_LIBEXIF
						break;
						
					// (Photoshop 7.0) XMP metadata
					// File info as XML description
					// http://Partners.adobe.com/asn/developer/xmp/main.html
					case 1060:
#	ifdef PSD_INCLUDE_LIBXML
						buffer = (psd_uchar *)psd_malloc(sizeofdata);
						if (buffer == NULL)
							return psd_status_malloc_failed;
						psd_stream_get(context, buffer, sizeofdata);
						context->XMP_metadata = (psd_uchar *)xmlParseMemory(buffer, sizeofdata);
						psd_free(buffer);
						context->fill_XMP_metadata = psd_true;
#	else // ifdef PSD_INCLUDE_LIBXML
						context->XMP_metadata = (psd_uchar *)psd_malloc(sizeofdata);
						if (context->XMP_metadata == NULL)
							return psd_status_malloc_failed;
						psd_stream_get(context, context->XMP_metadata, sizeofdata);
						context->XMP_metadata_length = sizeofdata;
						context->fill_XMP_metadata = psd_true;
#	endif // ifdef PSD_INCLUDE_LIBXML
						break;

					// (Photoshop 7.0) Print scale
					case 1062:
						// 2 bytes style (0 = centered, 1 = size to fit, 2 = user defined).
						context->print_scale.style = psd_stream_get_short(context);
						psd_assert(context->print_scale.style >= psd_print_centered &&
							context->print_scale.style <= psd_print_user_defined);
						// 4 bytes x location (floating point).
						context->print_scale.x_location = psd_stream_get_float(context);
						// 4 bytes y location (floating point).
						context->print_scale.y_location = psd_stream_get_float(context);
						// 4 bytes scale (floating point)
						context->print_scale.scale = psd_stream_get_float(context);
						context->fill_print_scale = psd_true;
						break;

					// (Photoshop CS) Pixel Aspect Ratio
					case 1064:
						// 4 bytes (version = 1)
						psd_assert(psd_stream_get_int(context) == 1);
						// 8 bytes double, x / y of a pixel
						context->pixel_aspect_ratio = psd_stream_get_double(context);
						break;

					// Print flags information
					case 10000:
						// 2 bytes version ( = 1)
						psd_assert(psd_stream_get_short(context) == 1);
						// 1 byte center crop marks
						context->print_flags_info.center_crop = psd_stream_get_bool(context);
						// 1 byte ( = 0)
						psd_assert(psd_stream_get_char(context) == 0);
						// 4 bytes bleed width value
						context->print_flags_info.value = psd_stream_get_int(context);
						// 2 bytes bleed width scale
						context->print_flags_info.scale = psd_stream_get_short(context);
						context->fill_print_flags_info = psd_true;
						break;
						
#endif // ifdef PSD_GET_ALL_IMAGE_RESOURCE
/////////////////////////////////////////////////////////////////////////////////////////////

					default:
#ifdef PSD_GET_PATH_RESOURCE
						// Photoshop stores its paths as resources of type 8BIM, with IDs in the range 2000
						// through 2998.
						if(ID >= 2000 && ID <= 2998)
						{
							psd_get_path(context, sizeofdata);
						}
						// If the file contains a resource of type 8BIM with an ID of 2999, then this resource
						// contains a Pascal¨Cstyle string containing the name of the clipping path to use with this
						// image when saving it as an EPS file???
						else if(ID == 2999)
						{
							// we don't find any files includes the name of the clipping path.
							psd_assert(0);
						}
						else
#endif // ifdef PSD_GET_PATH_RESOURCE
						{
							psd_stream_get_null(context, sizeofdata);
						}
						break;
				}
				
				// Filler
				psd_stream_get_null(context, prev_stream_pos + sizeofdata - context->stream.current_pos);
			}
		}
		else
		{
			return psd_status_resource_signature_error;
		}
	}

	return psd_status_done;
}
void imFileFormatJPEG::iReadExifAttrib(unsigned char* data, int data_length, imAttribTable* attrib_table)
{
  ExifData* exif = exif_data_new_from_data(data, data_length);
  if (!exif)
    return;

  void* value = NULL;
  int c, value_size = 0;

  ExifByteOrder byte_order = exif_data_get_byte_order(exif);

  for (int ifd = 0; ifd < EXIF_IFD_COUNT; ifd++)
  {
    if (ifd == EXIF_IFD_1 || ifd == EXIF_IFD_INTEROPERABILITY) // Skip thumbnail and interoperability
      continue;

    ExifContent *content = exif->ifd[ifd];

    if (content && content->count) 
    {
      for (int j = 0; j < (int)content->count; j++) 
      {
        ExifEntry *entry = content->entries[j];
        int type = 0;

        const char* name = exif_tag_get_name_in_ifd(entry->tag, (ExifIfd)ifd);
        if (!name)
          continue;

        if (value_size < (int)entry->size)
        {
          value = realloc(value, entry->size);
          value_size = entry->size;
        }

        int format_size = exif_format_get_size(entry->format);

        if (entry->tag == EXIF_TAG_RESOLUTION_UNIT)
        {
          int res_unit = (int)exif_get_short (entry->data, byte_order);

          if (res_unit == 2)
            attrib_table->Set("ResolutionUnit", IM_BYTE, -1, "DPI");
          else if (res_unit == 3)
            attrib_table->Set("ResolutionUnit", IM_BYTE, -1, "DPC");

          continue;
        }

        switch (entry->format) 
        {
        case EXIF_FORMAT_UNDEFINED:
        case EXIF_FORMAT_ASCII:
        case EXIF_FORMAT_SBYTE:
        case EXIF_FORMAT_BYTE:
          {
            type = IM_BYTE;
            imbyte *bvalue = (imbyte*)value;
            for (c = 0; c < (int)entry->components; c++) 
              bvalue[c] = entry->data[c];
          }
          break;
        case EXIF_FORMAT_SSHORT:
          {
            type = IM_SHORT;
            short *svalue = (short*)value;
            for (c = 0; c < (int)entry->components; c++) 
              svalue[c] = exif_get_short(entry->data + format_size * c, byte_order);
          }
          break;
        case EXIF_FORMAT_SHORT:
          {
            type = IM_USHORT;
            imushort *usvalue = (imushort*)value;
            for (c = 0; c < (int)entry->components; c++) 
              usvalue[c] = exif_get_short(entry->data + format_size * c, byte_order);
          }
          break;
        case EXIF_FORMAT_LONG:
          {
            type = IM_INT;
            int *ivalue = (int*)value;
            for (c = 0; c < (int)entry->components; c++) 
              ivalue[c] = (int)exif_get_long(entry->data + format_size * c, byte_order);
          }
          break;
        case EXIF_FORMAT_SLONG:
          {
            type = IM_INT;
            int *ivalue = (int*)value;
            for (c = 0; c < (int)entry->components; c++) 
              ivalue[c] = (int)exif_get_slong(entry->data + format_size * c, byte_order);
          }
          break;
        case EXIF_FORMAT_RATIONAL:
          {
            ExifRational v_rat;
            type = IM_FLOAT;
            float *fvalue = (float*)value;
            for (c = 0; c < (int)entry->components; c++) 
            {
              v_rat = exif_get_rational(entry->data + format_size * c, byte_order);
              fvalue[c] = (float)v_rat.numerator / (float)v_rat.denominator;
            }
          }
          break;
        case EXIF_FORMAT_SRATIONAL:
          {
            ExifSRational v_srat;
            type = IM_FLOAT;
            float *fvalue = (float*)value;
            for (c = 0; c < (int)entry->components; c++) 
            {
              v_srat = exif_get_srational(entry->data + format_size * c, byte_order);
              fvalue[c] = (float)v_srat.numerator / (float)v_srat.denominator;
            }
          }
          break;
        case EXIF_FORMAT_FLOAT:  // defined but unsupported in libEXIF
        case EXIF_FORMAT_DOUBLE: // defined but unsupported in libEXIF
          break;
        }

        attrib_table->Set(name, type, entry->components, value);
      }
    }
  }

  if (value) free(value);

  exif_data_free(exif);
}
Exemple #17
0
static int
write_exif( Write *write )
{
	unsigned char *data;
	size_t data_length;
	unsigned int idl;
#ifdef HAVE_EXIF
	ExifData *ed;

	/* Either parse from the embedded EXIF, or if there's none, make
	 * some fresh EXIF we can write the resolution to.
	 */
	if( im_header_get_typeof( write->in, IM_META_EXIF_NAME ) ) {
		if( im_meta_get_blob( write->in, IM_META_EXIF_NAME, 
			(void *) &data, &data_length ) )
			return( -1 );

		if( !(ed = exif_data_new_from_data( data, data_length )) )
			return( -1 );
	}
	else 
		ed = exif_data_new();

	/* Update EXIF resolution from VIPS.
	 */
	if( set_exif_resolution( ed, write->in ) ) {
		exif_data_free( ed );
		return( -1 );
	}

	/* Reserialise and write. exif_data_save_data() returns an int for some
	 * reason.
	 */
	exif_data_save_data( ed, &data, &idl );
	if( !idl ) {
		im_error( "im_jpeg2vips", "%s", _( "error saving EXIF" ) );
		exif_data_free( ed );
		return( -1 );
	}
	data_length = idl;

#ifdef DEBUG
	printf( "im_vips2jpeg: attaching %zd bytes of EXIF\n", data_length  );
#endif /*DEBUG*/

	exif_data_free( ed );
	jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1, data, data_length );
	free( data );
#else /*!HAVE_EXIF*/
	/* No libexif ... just copy the embedded EXIF over.
	 */
	if( im_header_get_typeof( write->in, IM_META_EXIF_NAME ) ) {
		if( im_meta_get_blob( write->in, IM_META_EXIF_NAME, 
			(void *) &data, &data_length ) )
			return( -1 );

#ifdef DEBUG
		printf( "im_vips2jpeg: attaching %d bytes of EXIF\n", 
			data_length  );
#endif /*DEBUG*/

		jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1, 
			data, data_length );
	}
#endif /*!HAVE_EXIF*/

	return( 0 );
}
Exemple #18
0
/* Examine the metadata tags on the image and update the EXIF block.
 */
int
vips__exif_update( VipsImage *image )
{
	unsigned char *data;
	size_t length;
	unsigned int idl;
	ExifData *ed;

	/* Either parse from the embedded EXIF, or if there's none, make
	 * some fresh EXIF we can write the resolution to.
	 */
	if( vips_image_get_typeof( image, VIPS_META_EXIF_NAME ) ) {
		if( vips_image_get_blob( image, VIPS_META_EXIF_NAME, 
			(void *) &data, &length ) )
			return( -1 );

		if( !(ed = exif_data_new_from_data( data, length )) )
			return( -1 );
	}
	else  {
		ed = exif_data_new();

		exif_data_set_option( ed, 
			EXIF_DATA_OPTION_FOLLOW_SPECIFICATION );
		exif_data_set_data_type( ed, EXIF_DATA_TYPE_COMPRESSED );
		exif_data_set_byte_order( ed, EXIF_BYTE_ORDER_INTEL );
	
		/* Create the mandatory EXIF fields with default data.
		 */
		exif_data_fix( ed );
	}

	/* Update EXIF tags from the image metadata.
	 */
	vips_exif_update( ed, image );

	/* Update EXIF resolution from the vips image header.
	 */
	if( vips_exif_resolution_from_image( ed, image ) ) {
		exif_data_free( ed );
		return( -1 );
	}

	/* Update EXIF image dimensions from the vips image header.
	 */
	if( vips_exif_set_dimensions( ed, image ) ) {
		exif_data_free( ed );
		return( -1 );
	}

	/* Update EXIF orientation from the vips image header.
	 */
	if( vips_exif_set_orientation( ed, image ) ) {
		exif_data_free( ed );
		return( -1 );
	}

	/* Update the thumbnail.
	 */
	if( vips_exif_set_thumbnail( ed, image ) ) {
		exif_data_free( ed );
		return( -1 );
	}

	/* Reserialise and write. exif_data_save_data() returns an int for some
	 * reason.
	 */
	exif_data_save_data( ed, &data, &idl );
	if( !idl ) {
		vips_error( "exif", "%s", _( "error saving EXIF" ) );
		exif_data_free( ed );
		return( -1 );
	}
	length = idl;

#ifdef DEBUG
	printf( "vips__exif_update: generated %zd bytes of EXIF\n", length  );
#endif /*DEBUG*/

	vips_image_set_blob( image, VIPS_META_EXIF_NAME, 
		(VipsCallbackFn) vips_free, data, length );

	exif_data_free( ed );

	return( 0 );
}
Exemple #19
0
static int analyzer_jpeg_pload_analyze(struct analyzer *analyzer, struct analyzer_pload_buffer *pload, void *buffer, size_t buff_len) {


	struct analyzer_jpeg_pload_priv *priv = analyzer_pload_buffer_get_priv(pload);

	if (!priv) {
		priv = malloc(sizeof(struct analyzer_jpeg_pload_priv));
		if (!priv) {
			pom_oom(sizeof(struct analyzer_jpeg_pload_priv));
			return POM_ERR;
		}
		memset(priv, 0, sizeof(struct analyzer_jpeg_pload_priv));

		priv->pload_buff = buffer;
		priv->pload_buff_len = buff_len;
		
		// Setup error handler
		struct jpeg_error_mgr *jerr = malloc(sizeof(struct jpeg_error_mgr));
		if (!jerr) {
			free(priv);
			pom_oom(sizeof(struct jpeg_error_mgr));
			return POM_ERR;
		}
		memset(jerr, 0, sizeof(struct jpeg_error_mgr));
		priv->cinfo.err = jpeg_std_error(jerr);
		priv->cinfo.err->error_exit = analyzer_jpeg_lib_error_exit;

		// Allocate the decompressor
		jpeg_create_decompress(&priv->cinfo);

		priv->cinfo.client_data = priv;

#ifdef HAVE_LIBEXIF
		// Save APP1
		jpeg_save_markers(&priv->cinfo, JPEG_APP0 + 1, 0xFFFF);
#endif

		// Allocate the source
		
		struct jpeg_source_mgr *src = malloc(sizeof(struct jpeg_source_mgr));
		if (!src) {
			free(priv->cinfo.err);
			pom_oom(sizeof(struct jpeg_source_mgr));
			jpeg_destroy_decompress(&priv->cinfo);
			free(priv);
			return POM_ERR;
		}
		memset(src, 0, sizeof(struct jpeg_source_mgr));

		src->init_source = analyzer_jpeg_lib_init_source;
		src->fill_input_buffer = analyzer_jpeg_lib_fill_input_buffer;
		src->skip_input_data = analyzer_jpeg_lib_skip_input_data;
		src->resync_to_restart = jpeg_resync_to_restart;
		src->term_source = analyzer_jpeg_lib_term_source;
		priv->cinfo.src = src;


		analyzer_pload_buffer_set_priv(pload, priv);

	} else {
		priv->pload_buff = buffer;
		priv->pload_buff_len = buff_len;
	}


	if (priv->jpeg_lib_pos >= buff_len)
		// Nothing more to process
		return POM_OK;

	int res = POM_OK;
	if (!setjmp(priv->jmp_buff)) {

		if (priv->jpeg_lib_pos) {
			// It's not garanteed that buffer points to the
			// same memory area after each call, so we reset it here
			priv->cinfo.src->next_input_byte = buffer + priv->jpeg_lib_pos;
		}

		if (jpeg_read_header(&priv->cinfo, TRUE) == JPEG_SUSPENDED)
			return POM_OK; // Headers are incomplete

		struct data *data = analyzer_pload_buffer_get_data(pload);

		PTYPE_UINT16_SETVAL(data[analyzer_jpeg_pload_width].value, priv->cinfo.image_width);
		data_set(data[analyzer_jpeg_pload_width]);
		PTYPE_UINT16_SETVAL(data[analyzer_jpeg_pload_height].value, priv->cinfo.image_height);
		data_set(data[analyzer_jpeg_pload_height]);
		debug_jpeg("JPEG read header returned %u, image is %ux%u", res, priv->cinfo.image_width, priv->cinfo.image_height);

#ifdef HAVE_LIBEXIF		
		// Parse the exif data
		jpeg_saved_marker_ptr marker;
		for (marker = priv->cinfo.marker_list; marker && marker->marker != JPEG_APP0 + 1; marker = marker->next);

		if (marker) {
			ExifData *exif_data = exif_data_new_from_data(marker->data, marker->data_length);
			if (!exif_data) {
				pomlog(POMLOG_DEBUG "Unable to parse EXIF data");
			}

			exif_data_foreach_content(exif_data, analyzer_jpeg_exif_content_process, pload);

			exif_data_free(exif_data);
		}
#endif

		analyzer_pload_buffer_set_state(pload, analyzer_pload_buffer_state_analyzed);

	} else {
		pomlog(POMLOG_DEBUG "Error while parsing JPEG headers");
		res = POM_ERR;
	}

	free(priv->cinfo.err);
	free(priv->cinfo.src);
	jpeg_destroy_decompress(&priv->cinfo);
	free(priv);
	analyzer_pload_buffer_set_priv(pload, NULL);

	return res;
}