forked from old/legacy-imlib2
JPEG loader: Parse EXIF data and handle orientation
This commit is contained in:
parent
c5e0e786f9
commit
b200f57e92
|
@ -88,7 +88,7 @@ id3_la_LDFLAGS = -module -avoid-version
|
|||
id3_la_LIBADD = $(ID3_LIBS) $(top_builddir)/src/lib/libImlib2.la
|
||||
id3_la_LIBTOOLFLAGS = --tag=disable-static
|
||||
|
||||
jpeg_la_SOURCES = loader_jpeg.c
|
||||
jpeg_la_SOURCES = loader_jpeg.c exif.c exif.h
|
||||
jpeg_la_CPPFLAGS = $(JPEG_CFLAGS) $(AM_CPPFLAGS)
|
||||
jpeg_la_LDFLAGS = -module -avoid-version
|
||||
jpeg_la_LIBADD = $(JPEG_LIBS) $(top_builddir)/src/lib/libImlib2.la
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "exif.h"
|
||||
|
||||
#define EXIF_DEBUG 0
|
||||
#if EXIF_DEBUG
|
||||
#define D(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define D(...)
|
||||
#endif
|
||||
|
||||
// IFD types
|
||||
#define IFD_TYPE_BYTE 1 // 8-bit unsigned integer
|
||||
#define IFD_TYPE_ASCII 2 // 8-bit byte that contains a 7-bit ASCII code; the last byte must be NUL (binary zero)
|
||||
#define IFD_TYPE_SHORT 3 // 16-bit (2-byte) unsigned integer
|
||||
#define IFD_TYPE_LONG 4 // 32-bit (4-byte) unsigned integer
|
||||
#define IFD_TYPE_RATIONAL 5 // Two LONGs: the first represents the numerator of a fraction; the second, the denominator
|
||||
|
||||
#define IFD_TYPE_SBYTE 6 // 8-bit signed (twos-complement) integer
|
||||
#define IFD_TYPE_UNDEFINED 7 // 8-bit byte that may contain anything, depending on the definition of the field
|
||||
#define IFD_TYPE_SSHORT 8 // 16-bit (2-byte) signed (twos-complement) integer
|
||||
#define IFD_TYPE_SLONG 9 // 32-bit (4-byte) signed (twos-complement) integer
|
||||
#define IFD_TYPE_SRATIONAL 10 // Two SLONG’s: the first represents the numerator of a fraction, the second the denominator
|
||||
#define IFD_TYPE_FLOAT 11 // Single precision (4-byte) IEEE format
|
||||
#define IFD_TYPE_DOUBLE 12 // Double precision (8-byte) IEEE format
|
||||
|
||||
// IFD tags
|
||||
#define TIFF_Orientation 0x0112
|
||||
#define TIFF_ExifIFD 0x8769
|
||||
#define TIFF_GpsIFD 0x8825
|
||||
|
||||
static unsigned int
|
||||
get_u16(const unsigned char *p, int swap)
|
||||
{
|
||||
if (swap)
|
||||
return p[0] << 8 | p[1];
|
||||
else
|
||||
return p[1] << 8 | p[0];
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
get_u32(const unsigned char *p, int swap)
|
||||
{
|
||||
if (swap)
|
||||
return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
|
||||
else
|
||||
return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse TIFF IFD (Image File Directory)
|
||||
*/
|
||||
static void
|
||||
tiff_parse_ifd(int lvl, const unsigned char *p, unsigned int len,
|
||||
const unsigned char *ifd, int swap, ExifInfo * ei)
|
||||
{
|
||||
const unsigned char *pp = ifd;
|
||||
unsigned int tag, type, cnt;
|
||||
unsigned int iifd, nifd;
|
||||
|
||||
D("%s: len=%x(%d)\n", __func__, len, len);
|
||||
|
||||
if (pp + 2 - p > len)
|
||||
{
|
||||
D("Bad IFD offset\n");
|
||||
return;
|
||||
}
|
||||
|
||||
nifd = get_u16(pp, swap);
|
||||
pp += 2;
|
||||
|
||||
for (iifd = 0; iifd < nifd; iifd++, pp += 12)
|
||||
{
|
||||
if (pp + 12 - p > len)
|
||||
{
|
||||
D("Bad offset, break\n");
|
||||
return;
|
||||
}
|
||||
tag = get_u16(pp, swap);
|
||||
type = get_u16(pp + 2, swap);
|
||||
cnt = get_u32(pp + 4, swap);
|
||||
|
||||
#if EXIF_DEBUG
|
||||
unsigned int val = get_u32(pp + 8, swap);
|
||||
|
||||
D("%*s %3d/%3d: tag=%04x type=%2d cnt=%5d val=%9d(0x%08x)\n",
|
||||
4 * lvl, " ", iifd, nifd, tag, type, cnt, val, val);
|
||||
#endif
|
||||
|
||||
switch (tag)
|
||||
{
|
||||
case TIFF_Orientation:
|
||||
if (type == IFD_TYPE_SHORT && cnt == 1)
|
||||
ei->orientation = get_u16(pp + 8, swap);
|
||||
#if EXIF_DEBUG
|
||||
break;
|
||||
#else
|
||||
return; // Done
|
||||
#endif
|
||||
#if EXIF_DEBUG
|
||||
case TIFF_ExifIFD:
|
||||
case TIFF_GpsIFD:
|
||||
if (lvl > 0)
|
||||
break;
|
||||
tiff_parse_ifd(lvl + 1, p, len, p + val, swap, ei);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse EXIF data (Exif.. are not part of the TIFF file)
|
||||
*/
|
||||
int
|
||||
exif_parse(const void *data, unsigned int len, ExifInfo * ei)
|
||||
{
|
||||
const unsigned char *ptr = data;
|
||||
int swap;
|
||||
unsigned int word;
|
||||
|
||||
D("%s: len=%x(%d)\n", __func__, len, len);
|
||||
|
||||
if (memcmp(ptr, "Exif", 4) != 0)
|
||||
{
|
||||
D("Not EXIF data\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ptr += 6; // ptr-> TIFF header
|
||||
len -= 6;
|
||||
|
||||
word = ptr[0] << 8 | ptr[1];
|
||||
if (word == 0x4949) // II
|
||||
swap = 0;
|
||||
else if (word == 0x4d4d) // MM
|
||||
swap = 1;
|
||||
else
|
||||
return 1;
|
||||
|
||||
word = get_u16(ptr + 2, swap);
|
||||
if (word != 42)
|
||||
{
|
||||
D("Bad TIFF version: %d\n", word);
|
||||
return 1;
|
||||
}
|
||||
|
||||
word = get_u32(ptr + 4, swap);
|
||||
if (word > len)
|
||||
{
|
||||
D("Bad IFD offset: %d\n", word);
|
||||
return 1;
|
||||
}
|
||||
|
||||
tiff_parse_ifd(0, ptr, len, ptr + word, swap, ei);
|
||||
|
||||
switch (ei->orientation)
|
||||
{
|
||||
default:
|
||||
case ORIENT_TOPLEFT:
|
||||
case ORIENT_TOPRIGHT:
|
||||
case ORIENT_BOTRIGHT:
|
||||
case ORIENT_BOTLEFT:
|
||||
ei->swap_wh = 0;
|
||||
break;
|
||||
case ORIENT_LEFTTOP:
|
||||
case ORIENT_RIGHTTOP:
|
||||
case ORIENT_RIGHTBOT:
|
||||
case ORIENT_LEFTBOT:
|
||||
ei->swap_wh = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
D("Orientation: %d (swap w/h=%d)\n", ei->orientation, ei->swap_wh);
|
||||
|
||||
return len;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef EXIF_PARSE_H
|
||||
#define EXIF_PARSE_H
|
||||
|
||||
#define ORIENT_TOPLEFT 1
|
||||
#define ORIENT_TOPRIGHT 2
|
||||
#define ORIENT_BOTRIGHT 3
|
||||
#define ORIENT_BOTLEFT 4
|
||||
#define ORIENT_LEFTTOP 5
|
||||
#define ORIENT_RIGHTTOP 6
|
||||
#define ORIENT_RIGHTBOT 7
|
||||
#define ORIENT_LEFTBOT 8
|
||||
|
||||
typedef struct {
|
||||
unsigned char orientation;
|
||||
char swap_wh;
|
||||
} ExifInfo;
|
||||
|
||||
int exif_parse(const void *data, unsigned int len,
|
||||
ExifInfo * ei);
|
||||
|
||||
#endif /* EXIF_PARSE_H */
|
|
@ -1,6 +1,14 @@
|
|||
#include "loader_common.h"
|
||||
#include <jpeglib.h>
|
||||
#include <setjmp.h>
|
||||
#include "exif.h"
|
||||
|
||||
#define DEBUG 0
|
||||
#if DEBUG
|
||||
#define D(fmt...) fprintf(stdout, "JPEG loader: " fmt)
|
||||
#else
|
||||
#define D(fmt...)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_error_mgr jem;
|
||||
|
@ -65,7 +73,8 @@ load2(ImlibImage * im, int load_data)
|
|||
ImLib_JPEG_data jdata;
|
||||
DATA8 *ptr, *line[16];
|
||||
DATA32 *ptr2;
|
||||
int x, y, l, scans;
|
||||
int x, y, l, scans, inc;
|
||||
ExifInfo ei = { 0 };
|
||||
|
||||
/* set up error handling */
|
||||
jds.err = _jdata_init(&jdata);
|
||||
|
@ -79,13 +88,38 @@ load2(ImlibImage * im, int load_data)
|
|||
|
||||
jpeg_create_decompress(&jds);
|
||||
jpeg_stdio_src(&jds, im->fp);
|
||||
jpeg_save_markers(&jds, JPEG_APP0 + 1, 256);
|
||||
jpeg_read_header(&jds, TRUE);
|
||||
|
||||
im->w = w = jds.image_width;
|
||||
im->h = h = jds.image_height;
|
||||
/* Get orientation */
|
||||
ei.orientation = ORIENT_TOPLEFT;
|
||||
|
||||
if (jds.marker_list)
|
||||
{
|
||||
jpeg_saved_marker_ptr m = jds.marker_list;
|
||||
|
||||
D("Markers: %p: m=%02x len=%d/%d\n", m,
|
||||
m->marker, m->original_length, m->data_length);
|
||||
|
||||
exif_parse(m->data, m->data_length, &ei);
|
||||
}
|
||||
|
||||
w = jds.image_width;
|
||||
h = jds.image_height;
|
||||
if (!IMAGE_DIMENSIONS_OK(w, h))
|
||||
goto quit;
|
||||
|
||||
if (ei.swap_wh)
|
||||
{
|
||||
im->w = h;
|
||||
im->h = w;
|
||||
}
|
||||
else
|
||||
{
|
||||
im->w = w;
|
||||
im->h = h;
|
||||
}
|
||||
|
||||
UNSET_FLAG(im->flags, F_HAS_ALPHA);
|
||||
|
||||
if (!load_data)
|
||||
|
@ -127,6 +161,45 @@ load2(ImlibImage * im, int load_data)
|
|||
{
|
||||
ptr = line[y];
|
||||
|
||||
switch (ei.orientation)
|
||||
{
|
||||
default:
|
||||
case ORIENT_TOPLEFT:
|
||||
ptr2 = im->data + (l + y) * w;
|
||||
inc = 1;
|
||||
break;
|
||||
case ORIENT_TOPRIGHT:
|
||||
ptr2 = im->data + (l + y) * w + w - 1;
|
||||
inc = -1;
|
||||
break;
|
||||
case ORIENT_BOTRIGHT:
|
||||
ptr2 = im->data + (h - 1 - (l + y)) * w + w - 1;
|
||||
inc = -1;
|
||||
break;
|
||||
case ORIENT_BOTLEFT:
|
||||
ptr2 = im->data + (h - 1 - (l + y)) * w;
|
||||
inc = 1;
|
||||
break;
|
||||
case ORIENT_LEFTTOP:
|
||||
ptr2 = im->data + (l + y);
|
||||
inc = h;
|
||||
break;
|
||||
case ORIENT_RIGHTTOP:
|
||||
ptr2 = im->data + (h - 1 - (l + y));
|
||||
inc = h;
|
||||
break;
|
||||
case ORIENT_RIGHTBOT:
|
||||
ptr2 = im->data + (h - 1 - (l + y)) + (w - 1) * h;
|
||||
inc = -h;
|
||||
break;
|
||||
case ORIENT_LEFTBOT:
|
||||
ptr2 = im->data + (l + y) + (w - 1) * h;
|
||||
inc = -h;
|
||||
break;
|
||||
}
|
||||
D("l,s,y=%d,%d, %d - x,y=%4ld,%4ld\n", l, y, l + y,
|
||||
(ptr2 - im->data) % im->w, (ptr2 - im->data) / im->w);
|
||||
|
||||
switch (jds.out_color_space)
|
||||
{
|
||||
default:
|
||||
|
@ -136,7 +209,7 @@ load2(ImlibImage * im, int load_data)
|
|||
{
|
||||
*ptr2 = PIXEL_ARGB(0xff, ptr[0], ptr[0], ptr[0]);
|
||||
ptr++;
|
||||
ptr2++;
|
||||
ptr2 += inc;
|
||||
}
|
||||
break;
|
||||
case JCS_RGB:
|
||||
|
@ -144,7 +217,7 @@ load2(ImlibImage * im, int load_data)
|
|||
{
|
||||
*ptr2 = PIXEL_ARGB(0xff, ptr[0], ptr[1], ptr[2]);
|
||||
ptr += jds.output_components;
|
||||
ptr2++;
|
||||
ptr2 += inc;
|
||||
}
|
||||
break;
|
||||
case JCS_CMYK:
|
||||
|
@ -154,18 +227,26 @@ load2(ImlibImage * im, int load_data)
|
|||
ptr[1] * ptr[3] / 255,
|
||||
ptr[2] * ptr[3] / 255);
|
||||
ptr += jds.output_components;
|
||||
ptr2++;
|
||||
ptr2 += inc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ei.orientation != ORIENT_TOPLEFT &&
|
||||
ei.orientation != ORIENT_TOPRIGHT)
|
||||
continue;
|
||||
if (im->lc && __imlib_LoadProgressRows(im, l, scans))
|
||||
{
|
||||
rc = LOAD_BREAK;
|
||||
goto quit;
|
||||
}
|
||||
}
|
||||
if (ei.orientation != ORIENT_TOPLEFT && ei.orientation != ORIENT_TOPRIGHT)
|
||||
{
|
||||
if (im->lc)
|
||||
__imlib_LoadProgressRows(im, 0, im->h);
|
||||
}
|
||||
|
||||
jpeg_finish_decompress(&jds);
|
||||
|
||||
|
|
Loading…
Reference in New Issue