An example of using libexif to set JPEG Orientation Tag : http://www.jwz.org/blog/2008/12/cocoa-exif-rotation/
#include <stdio.h> #include <stdlib.h> #include <libexif/exif-data.h> int main(int argc, char *argv[]) { if(argc != 2) { printf("Usage: foo <image>\n"); exit(1); } int orientation = 0; ExifData *exifData = exif_data_new_from_file(argv[1]); if (exifData) { ExifByteOrder byteOrder = exif_data_get_byte_order(exifData); ExifEntry *exifEntry = exif_data_get_entry(exifData, EXIF_TAG_ORIENTATION); if (exifEntry) orientation = exif_get_short(exifEntry->data, byteOrder); exif_data_free(exifData); } /* 0th Row 0th Column 1 top left side 2 top right side 3 bottom right side 4 bottom left side 5 left side top 6 right side top 7 right side bottom 8 left side bottom */ if(orientation != 0) { printf("orientation == %d\n", orientation); } exit(0); }
在Github上找到了一个不错的代码,但本人能力有限,还未曾看懂,希望高手看懂后能给解释下。
/*
* jpegtran.c
*
* Copyright (C) 1995-1997, Thomas G. Lane.
* This file is part of the Independent JPEG Group's software.
* For conditions of distribution and use, see the accompanying README file.
*
* plenty of changes by Gerd Hoffmann <kraxel@bytesex.org>, with focus on
* digital image processing and sane exif handling:
*
* - does transformations only (flip/rotate/transpose/transverse).
* - also transforms the exif thumbnail if present.
* - can automatically figure transformation from the
* exif orientation tag.
* - updates the exif orientation tag.
* - updates the exif pixel dimension tags.
*
* This file contains a command-line user interface for JPEG transcoding.
* It is very similar to cjpeg.c, but provides lossless transcoding between
* different JPEG file formats. It also provides some lossless and sort-of-
* lossless transformations of JPEG data.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <utime.h>
#include <setjmp.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <jpeglib.h>
#include "transupp.h" /* Support routines for jpegtran */
#include "jpegtools.h"
#include "misc.h"
#include <libexif/exif-data.h>
#include <libexif/exif-utils.h>
#include <libexif/exif-ifd.h>
#include <libexif/exif-tag.h>
static int do_transform(struct jpeg_decompress_struct *src,
struct jpeg_compress_struct *dst,
JXFORM_CODE transform,
unsigned char *comment,
char *thumbnail, int tsize,
unsigned int flags);
static JXFORM_CODE transmagic[] = {
[ 1 ] = JXFORM_NONE,
[ 2 ] = JXFORM_FLIP_H,
[ 3 ] = JXFORM_ROT_180,
[ 4 ] = JXFORM_FLIP_V,
[ 5 ] = JXFORM_TRANSPOSE,
[ 6 ] = JXFORM_ROT_90,
[ 7 ] = JXFORM_TRANSVERSE,
[ 8 ] = JXFORM_ROT_270,
};
#if 0
static char *transname[] = {
[ JXFORM_NONE ] = "none",
[ JXFORM_FLIP_H ] = "flip h",
[ JXFORM_FLIP_V ] = "flip v",
[ JXFORM_TRANSPOSE ] = "transpose",
[ JXFORM_TRANSVERSE ] = "transverse",
[ JXFORM_ROT_90 ] = "rot 90",
[ JXFORM_ROT_180 ] = "rot 190",
[ JXFORM_ROT_270 ] = "rot 270",
};
#endif
/* ---------------------------------------------------------------------- */
/* libjpeg error handler -- exit via longjump */
struct longjmp_error_mgr {
struct jpeg_error_mgr jpeg;
jmp_buf setjmp_buffer;
};
static void longjmp_error_exit(j_common_ptr cinfo)
{
struct longjmp_error_mgr *h = (struct longjmp_error_mgr*)cinfo->err;
(*cinfo->err->output_message)(cinfo);
longjmp(h->setjmp_buffer, 1);
}
/* ---------------------------------------------------------------------- */
static long get_int(ExifData *ed, ExifEntry *ee)
{
ExifByteOrder o = exif_data_get_byte_order(ed);
long value;
switch (ee->format) {
case EXIF_FORMAT_SHORT:
value = exif_get_short (ee->data, o);
break;
case EXIF_FORMAT_LONG:
value = exif_get_long (ee->data, o);
break;
case EXIF_FORMAT_SLONG:
value = exif_get_slong (ee->data, o);
break;
default:
fprintf(stderr,"get_int oops\n");
exit(1);
}
return value;
}
static void set_int(ExifData *ed, ExifEntry *ee, long value)
{
ExifByteOrder o = exif_data_get_byte_order(ed);
switch (ee->format) {
case EXIF_FORMAT_SHORT:
exif_set_short (ee->data, o, value);
break;
case EXIF_FORMAT_LONG:
exif_set_long (ee->data, o, value);
break;
case EXIF_FORMAT_SLONG:
exif_set_slong (ee->data, o, value);
break;
default:
fprintf(stderr,"set_int oops\n");
exit(1);
}
}
static void update_orientation(ExifData *ed, int ifd, int orientation)
{
ExifEntry *ee;
ee = exif_content_get_entry(ed->ifd[ifd], 0x0112);
if (NULL == ee)
return;
set_int(ed,ee,orientation);
}
static void update_dimension(ExifData *ed, JXFORM_CODE transform,
int src_x, int src_y)
{
static struct {
int idf;
int tag;
int x;
} fields[] = {
{
.idf = EXIF_IFD_EXIF,
.tag = EXIF_TAG_PIXEL_X_DIMENSION,
.x = 1,
},{
.idf = EXIF_IFD_EXIF,
.tag = EXIF_TAG_PIXEL_Y_DIMENSION,
.x = 0,
},{
.idf = EXIF_IFD_INTEROPERABILITY,
.tag = EXIF_TAG_RELATED_IMAGE_WIDTH,
.x = 1,
},{
.idf = EXIF_IFD_INTEROPERABILITY,
.tag = EXIF_TAG_RELATED_IMAGE_LENGTH,
.x = 0,
}
};
ExifEntry *ee;
int i;
for (i = 0; i < sizeof(fields)/sizeof(fields[0]); i++) {
ee = exif_content_get_entry(ed->ifd[fields[i].idf], fields[i].tag);
if (!ee)
continue;
switch (transform) {
case JXFORM_ROT_90:
case JXFORM_ROT_270:
case JXFORM_TRANSPOSE:
case JXFORM_TRANSVERSE:
/* x/y reversed */
set_int(ed, ee, fields[i].x ? src_y : src_x);
break;
default:
/* normal */
set_int(ed, ee, fields[i].x ? src_x : src_y);
break;
}
}
}
static int get_orientation(ExifData *ed)
{
ExifEntry *ee;
ee = exif_content_get_entry(ed->ifd[EXIF_IFD_0], 0x0112);
if (NULL == ee)
return 1; /* top - left */
return get_int(ed,ee);
}
/* ---------------------------------------------------------------------- */
struct th {
struct jpeg_decompress_struct src;
struct jpeg_compress_struct dst;
struct jpeg_error_mgr jsrcerr, jdsterr;
unsigned char *in;
unsigned char *out;
int isize, osize;
};
static void thumbnail_src_init(struct jpeg_decompress_struct *cinfo)
{
struct th *h = container_of(cinfo, struct th, src);
cinfo->src->next_input_byte = h->in;
cinfo->src->bytes_in_buffer = h->isize;
}
static int thumbnail_src_fill(struct jpeg_decompress_struct *cinfo)
{
fprintf(stderr,"jpeg: panic: no more thumbnail input data\n");
exit(1);
}
static void thumbnail_src_skip(struct jpeg_decompress_struct *cinfo,
long num_bytes)
{
cinfo->src->next_input_byte += num_bytes;
}
static void thumbnail_src_term(struct jpeg_decompress_struct *cinfo)
{
/* nothing */
}
static void thumbnail_dest_init(struct jpeg_compress_struct *cinfo)
{
struct th *h = container_of(cinfo, struct th, dst);
h->osize = h->isize * 2;
h->out = malloc(h->osize);
cinfo->dest->next_output_byte = h->out;
cinfo->dest->free_in_buffer = h->osize;
}
static boolean thumbnail_dest_flush(struct jpeg_compress_struct *cinfo)
{
fprintf(stderr,"jpeg: panic: output buffer full\n");
exit(1);
}
static void thumbnail_dest_term(struct jpeg_compress_struct *cinfo)
{
struct th *h = container_of(cinfo, struct th, dst);
h->osize -= cinfo->dest->free_in_buffer;
}
static struct jpeg_source_mgr thumbnail_src = {
.init_source = thumbnail_src_init,
.fill_input_buffer = thumbnail_src_fill,
.skip_input_data = thumbnail_src_skip,
.resync_to_restart = jpeg_resync_to_restart,
.term_source = thumbnail_src_term,
};
static struct jpeg_destination_mgr thumbnail_dst = {
.init_destination = thumbnail_dest_init,
.empty_output_buffer = thumbnail_dest_flush,
.term_destination = thumbnail_dest_term,
};
static void do_thumbnail(ExifData *ed, JXFORM_CODE transform)
{
struct th th;
if (JXFORM_NONE == transform)
return;
memset(&th,0,sizeof(th));
th.in = ed->data;
th.isize = ed->size;
/* setup src */
th.src.err = jpeg_std_error(&th.jsrcerr);
jpeg_create_decompress(&th.src);
th.src.src = &thumbnail_src;
/* setup dst */
th.dst.err = jpeg_std_error(&th.jdsterr);
jpeg_create_compress(&th.dst);
th.dst.dest = &thumbnail_dst;
/* transform image */
do_transform(&th.src,&th.dst,transform,NULL,NULL,0,JFLAG_TRANSFORM_IMAGE);
/* cleanup */
jpeg_destroy_decompress(&th.src);
jpeg_destroy_compress(&th.dst);
/* replace thumbnail */
free(ed->data);
ed->data = th.out;
ed->size = th.osize;
}
static void do_exif(struct jpeg_decompress_struct *src,
JXFORM_CODE *transform,
char *thumbnail, int tsize,
unsigned int flags)
{
jpeg_saved_marker_ptr mark;
ExifData *ed = NULL;
unsigned char *data;
unsigned int size;
for (mark = src->marker_list; NULL != mark; mark = mark->next) {
if (mark->marker != JPEG_APP0 +1)
continue;
ed = exif_data_new_from_data(mark->data,mark->data_length);
break;
}
if (flags & JFLAG_UPDATE_THUMBNAIL) {
if (NULL == ed)
ed = exif_data_new();
if (NULL == mark) {
mark = src->mem->alloc_large((j_common_ptr)src,JPOOL_IMAGE,sizeof(*mark));
memset(mark,0,sizeof(*mark));
mark->marker = JPEG_APP0 +1;
mark->next = src->marker_list;
src->marker_list = mark;
}
if (ed->data)
free(ed->data);
ed->data = thumbnail;
ed->size = tsize;
}
if (NULL == ed)
return;
if (-1 == *transform) {
/* automagic image transformation */
int orientation = get_orientation(ed);
*transform = JXFORM_NONE;
if (orientation >= 1 && orientation <= 8)
*transform = transmagic[orientation];
#if 0
if (debug)
fprintf(stderr,"autotrans: %s\n",transname[*transform]);
#endif
}
/* update exif data */
if (flags & JFLAG_UPDATE_ORIENTATION) {
update_orientation(ed,EXIF_IFD_0,1);
update_orientation(ed,EXIF_IFD_1,1);
}
if (ed->data && ed->data[0] == 0xff && ed->data[1] == 0xd8 &&
(flags & JFLAG_TRANSFORM_THUMBNAIL))
do_thumbnail(ed,*transform);
update_dimension(ed, (flags & JFLAG_TRANSFORM_IMAGE) ? *transform : JXFORM_NONE,
src->image_width, src->image_height);
/* build new exif data block */
exif_data_save_data(ed,&data,&size);
exif_data_unref(ed);
/* update jpeg APP1 (EXIF) marker */
mark->data = src->mem->alloc_large((j_common_ptr)src,JPOOL_IMAGE,size);
mark->original_length = size;
mark->data_length = size;
memcpy(mark->data,data,size);
free(data);
}
/* ---------------------------------------------------------------------- */
static void do_comment(struct jpeg_decompress_struct *src,
unsigned char *comment)
{
jpeg_saved_marker_ptr mark;
int size;
/* find or create comment marker */
for (mark = src->marker_list;; mark = mark->next) {
if (mark->marker == JPEG_COM)
break;
if (NULL == mark->next) {
mark->next = src->mem->alloc_large((j_common_ptr)src,JPOOL_IMAGE,
sizeof(*mark));
mark = mark->next;
memset(mark,0,sizeof(*mark));
mark->marker = JPEG_COM;
break;
}
}
/* update comment marker */
size = strlen(comment) +1;
mark->data = src->mem->alloc_large((j_common_ptr)src,JPOOL_IMAGE,size);
mark->original_length = size;
mark->data_length = size;
memcpy(mark->data,comment,size);
}
static int do_transform(struct jpeg_decompress_struct *src,
struct jpeg_compress_struct *dst,
JXFORM_CODE transform,
unsigned char *comment,
char *thumbnail, int tsize,
unsigned int flags)
{
jvirt_barray_ptr * src_coef_arrays;
jvirt_barray_ptr * dst_coef_arrays;
jpeg_transform_info transformoption;
jcopy_markers_setup(src, JCOPYOPT_ALL);
if (JPEG_HEADER_OK != jpeg_read_header(src, TRUE))
return -1;
do_exif(src,&transform,thumbnail,tsize,flags);
if (-1 == transform)
transform = JXFORM_NONE;
if (!(flags & JFLAG_TRANSFORM_IMAGE))
transform = JXFORM_NONE;
if ((flags & JFLAG_UPDATE_COMMENT) && NULL != comment)
do_comment(src,comment);
memset(&transformoption,0,sizeof(transformoption));
transformoption.transform = transform;
transformoption.trim = FALSE;
transformoption.force_grayscale = FALSE;
/* Any space needed by a transform option must be requested before
* jpeg_read_coefficients so that memory allocation will be done right.
*/
jtransform_request_workspace(src, &transformoption);
src_coef_arrays = jpeg_read_coefficients(src);
jpeg_copy_critical_parameters(src, dst);
dst_coef_arrays = jtransform_adjust_parameters
(src, dst, src_coef_arrays, &transformoption);
/* Start compressor (note no image data is actually written here) */
jpeg_write_coefficients(dst, dst_coef_arrays);
/* Copy to the output file any extra markers that we want to preserve */
jcopy_markers_execute(src, dst, JCOPYOPT_ALL);
/* Execute image transformation, if any */
jtransform_execute_transformation(src, dst,
src_coef_arrays,
&transformoption);
/* Finish compression and release memory */
jpeg_finish_compress(dst);
jpeg_finish_decompress(src);
return 0;
}
/* ---------------------------------------------------------------------- */
int jpeg_transform_fp(FILE *in, FILE *out,
JXFORM_CODE transform,
unsigned char *comment,
char *thumbnail, int tsize,
unsigned int flags)
{
struct jpeg_decompress_struct src;
struct jpeg_compress_struct dst;
struct jpeg_error_mgr jdsterr;
struct longjmp_error_mgr jsrcerr;
/* setup src */
src.err = jpeg_std_error(&jsrcerr.jpeg);
jsrcerr.jpeg.error_exit = longjmp_error_exit;
if (setjmp(jsrcerr.setjmp_buffer))
/* something went wrong within the jpeg library ... */
goto oops;
jpeg_create_decompress(&src);
jpeg_stdio_src(&src, in);
/* setup dst */
dst.err = jpeg_std_error(&jdsterr);
jpeg_create_compress(&dst);
jpeg_stdio_dest(&dst, out);
/* transform image */
do_transform(&src,&dst,transform,comment,thumbnail,tsize,flags);
/* cleanup */
jpeg_destroy_decompress(&src);
jpeg_destroy_compress(&dst);
return 0;
oops:
jpeg_destroy_decompress(&src);
jpeg_destroy_compress(&dst);
return -1;
}
int jpeg_transform_files(char *infile, char *outfile,
JXFORM_CODE transform,
unsigned char *comment,
char *thumbnail, int tsize,
unsigned int flags)
{
int rc;
FILE *in;
FILE *out;
/* open infile */
in = fopen(infile,"r");
if (NULL == in) {
fprintf(stderr,"open %s: %s\n",infile,strerror(errno));
return -1;
}
/* open outfile */
out = fopen(outfile,"w");
if (NULL == out) {
fprintf(stderr,"open %s: %s\n",outfile,strerror(errno));
fclose(in);
return -1;
}
/* go! */
rc = jpeg_transform_fp(in,out,transform,comment,thumbnail,tsize,flags);
fclose(in);
fclose(out);
return rc;
}
int jpeg_transform_inplace(char *file,
JXFORM_CODE transform,
unsigned char *comment,
char *thumbnail, int tsize,
unsigned int flags)
{
char *tmpfile;
char *bakfile;
struct stat st;
int fd;
FILE *in = NULL;
FILE *out = NULL;
/* are we allowed to write to the file? */
if (0 != access(file,W_OK)) {
fprintf(stderr,"access %s: %s\n",file,strerror(errno));
return -1;
}
/* open infile */
in = fopen(file,"r");
if (NULL == in) {
fprintf(stderr,"open %s: %s\n",file,strerror(errno));
return -1;
}
/* open tmpfile */
tmpfile = malloc(strlen(file)+10);
sprintf(tmpfile,"%s.XXXXXX",file);
fd = mkstemp(tmpfile);
if (-1 == fd) {
fprintf(stderr,"mkstemp(%s): %s\n",tmpfile,strerror(errno));
goto oops;
}
out = fdopen(fd,"w");
/* copy owner and permissions */
if (-1 == fstat(fileno(in),&st)) {
fprintf(stderr,"fstat(%s): %s\n",file,strerror(errno));
// goto oops;
}
if (-1 == fchown(fileno(out),st.st_uid,st.st_gid)) {
fprintf(stderr,"fchown(%s): %s\n",tmpfile,strerror(errno));
// goto oops;
}
if (-1 == fchmod(fileno(out),st.st_mode)) {
fprintf(stderr,"fchmod(%s): %s\n",tmpfile,strerror(errno));
// goto oops;
}
/* transform */
if (0 != jpeg_transform_fp(in,out,transform,comment,thumbnail,tsize,flags))
goto oops;
/* worked ok -- commit */
fclose(in);
fclose(out);
if (flags & JFLAG_FILE_BACKUP) {
bakfile = malloc(strlen(file)+2);
sprintf(bakfile,"%s~",file);
rename(file,bakfile);
free(bakfile);
}
rename(tmpfile,file);
if (flags & JFLAG_FILE_KEEP_TIME) {
struct utimbuf u;
u.actime = st.st_atime;
u.modtime = st.st_mtime;
utime(file,&u);
}
/* cleanup & return */
free(tmpfile);
return 0;
oops:
/* something went wrong -- rollback */
if (in)
fclose(in);
if (out) {
fclose(out);
unlink(tmpfile);
}
return -1;
}