Added reading of EXIF_Orientation tags when present in a jpeg file.

git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk@14817 16af8721-9629-0410-8352-f15c8da7e697
This commit is contained in:
Robert Osfield
2015-04-02 16:11:22 +00:00
parent 950d7bb60b
commit 8fb08496ae
4 changed files with 242 additions and 14 deletions

View File

@@ -1,6 +1,10 @@
INCLUDE_DIRECTORIES( ${JPEG_INCLUDE_DIR} )
SET(TARGET_SRC ReaderWriterJPEG.cpp )
SET(TARGET_SRC
EXIF_Orientation.cpp
ReaderWriterJPEG.cpp
)
SET(TARGET_LIBRARIES_VARS JPEG_LIBRARY )
#### end var setup ###
SETUP_PLUGIN(jpeg)

View File

@@ -0,0 +1,182 @@
#include "EXIF_Orientation.h"
#include <stdio.h>
#include <string.h>
#include <osg/Notify>
#include <osg/Endian>
#define EXIF_IDENT_STRING "Exif\000\000"
static unsigned short de_get16(void *ptr, bool byteSwap)
{
unsigned short val;
memcpy(&val, ptr, sizeof(val));
if (byteSwap) osg::swapBytes(val);
return val;
}
static unsigned int de_get32(void *ptr, bool byteSwap)
{
unsigned int val;
memcpy(&val, ptr, sizeof(val));
if (byteSwap) osg::swapBytes(val);
return val;
}
int EXIF_Orientation (j_decompress_ptr cinfo)
{
OSG_INFO<<"get_orientation()"<<std::endl;
jpeg_saved_marker_ptr exif_marker; /* Location of the Exif APP1 marker */
jpeg_saved_marker_ptr cmarker; /* Location to check for Exif APP1 marker */
/* check for Exif marker (also called the APP1 marker) */
exif_marker = NULL;
cmarker = cinfo->marker_list;
while (cmarker)
{
if (cmarker->marker == EXIF_JPEG_MARKER)
{
/* The Exif APP1 marker should contain a unique
identification string ("Exif\0\0"). Check for it. */
if (!memcmp (cmarker->data, EXIF_IDENT_STRING, 6))
{
exif_marker = cmarker;
}
}
cmarker = cmarker->next;
}
if (exif_marker==NULL)
{
OSG_INFO<<"exif_marker not found "<<std::endl;
return 0;
}
OSG_INFO<<"exif_marker found "<<exif_marker<<std::endl;
/* Do we have enough data? */
if (exif_marker->data_length < 32)
{
OSG_INFO<<"exif_marker too short : "<<exif_marker->data_length<<std::endl;
return 0;
}
osg::Endian tiffHeaderEndian = osg::LittleEndian;
const char leth[] = {0x49, 0x49, 0x2a, 0x00}; // Little endian TIFF header
const char beth[] = {0x4d, 0x4d, 0x00, 0x2a}; // Big endian TIFF header
/* Just skip data until TIFF header - it should be within 16 bytes from marker start.
Normal structure relative to APP1 marker -
0x0000: APP1 marker entry = 2 bytes
0x0002: APP1 length entry = 2 bytes
0x0004: Exif Identifier entry = 6 bytes
0x000A: Start of TIFF header (Byte order entry) - 4 bytes
- This is what we look for, to determine endianess.
0x000E: 0th IFD offset pointer - 4 bytes
exif_marker->data points to the first data after the APP1 marker
and length entries, which is the exif identification string.
The TIFF header should thus normally be found at i=6, below,
and the pointer to IFD0 will be at 6+4 = 10.
*/
/* Check for TIFF header and catch endianess */
unsigned int i = 0;
for(i=0; i < 16; ++i)
{
/* Little endian TIFF header */
if (memcmp (&exif_marker->data[i], leth, 4) == 0)
{
tiffHeaderEndian = osg::LittleEndian;
break;
}
/* Big endian TIFF header */
else if (memcmp (&exif_marker->data[i], beth, 4) == 0)
{
tiffHeaderEndian = osg::BigEndian;
break;
}
}
/* So did we find a TIFF header or did we just hit end of buffer? */
if (i >= 16)
{
OSG_INFO<<"Could not find TIFF header"<<std::endl;
return 0;
}
OSG_INFO<<"Found TIFF header = "<<i<<" endian = "<<((tiffHeaderEndian==osg::BigEndian)?"BigEndian":"LittleEndian")<< std::endl;
bool swapBytes = osg::getCpuByteOrder()!=tiffHeaderEndian;
OSG_INFO<<"swapBytes = "<<swapBytes<< std::endl;
/* Read out the offset pointer to IFD0 */
unsigned int offset = de_get32(&exif_marker->data[i] + 4, swapBytes);
i += offset;
OSG_INFO<<"offset = "<<offset<<std::endl;
/* Check that we still are within the buffer and can read the tag count */
if ((i + 2) > exif_marker->data_length)
return 0;
/* Find out how many tags we have in IFD0. As per the TIFF spec, the first
two bytes of the IFD contain a count of the number of tags. */
unsigned int tags = de_get16(&exif_marker->data[i], swapBytes);
i += 2;
OSG_INFO<<"tags = "<<tags<<std::endl;
/* Check that we still have enough data for all tags to check. The tags
are listed in consecutive 12-byte blocks. The tag ID, type, size, and
a pointer to the actual value, are packed into these 12 byte entries. */
if ((i + tags * 12) > exif_marker->data_length)
{
OSG_INFO<<"Not enough length for requied tags"<<std::endl;
return 0;
}
/* Endian the orientation tag ID, to locate it more easily */
unsigned int orient_tag_id = 0x112;
/* Check through IFD0 for tags of interest */
while (tags--)
{
unsigned int tag = de_get16(&exif_marker->data[i], swapBytes);
unsigned int type = de_get16(&exif_marker->data[i + 2], swapBytes);
unsigned int count = de_get32(&exif_marker->data[i + 4], swapBytes);
OSG_INFO<<" tag=0x"<<std::hex<<tag<<std::dec<<", type="<<type<<", count="<<count<<std::endl;
/* Is this the orientation tag? */
if (tag==orient_tag_id)
{
/* Check that type and count fields are OK. The orientation field
will consist of a single (count=1) 2-byte integer (type=3). */
if (type != 3 || count != 1) return 0;
/* Return the orientation value. Within the 12-byte block, the
pointer to the actual data is at offset 8. */
unsigned int ret = de_get16(&exif_marker->data[i + 8], swapBytes);
OSG_INFO<<"Found orientationTag, ret = "<<ret<<std::endl;
return ret <= 8 ? ret : 0;
}
/* move the pointer to the next 12-byte tag field. */
i = i + 12;
}
OSG_INFO<<"Could not find EXIF Orientation tag"<<std::endl;
return 0; /* No EXIF Orientation tag found */
}

View File

@@ -0,0 +1,16 @@
#ifndef EXIF_Orientation_H
#define EXIF_Orientation_H
#include <stdio.h>
extern "C"
{
#include <jpeglib.h>
#include "jerror.h"
}
#define EXIF_JPEG_MARKER JPEG_APP0+1
extern int EXIF_Orientation (j_decompress_ptr cinfo);
#endif

View File

@@ -48,13 +48,7 @@
*
*/
#include <stdio.h>
extern "C"
{
#include <jpeglib.h>
#include "jerror.h"
}
#include "EXIF_Orientation.h"
#include <setjmp.h>
#include <string.h>
@@ -467,11 +461,11 @@ copyScanline(unsigned char *currPtr, unsigned char *from, int cnt)
return currPtr;
}
unsigned char *
simage_jpeg_load(std::istream& fin,
int *width_ret,
int *height_ret,
int *numComponents_ret)
unsigned char* simage_jpeg_load(std::istream& fin,
int *width_ret,
int *height_ret,
int *numComponents_ret,
unsigned int* exif_orientation)
{
int width;
int height;
@@ -535,8 +529,12 @@ int *numComponents_ret)
//jpeg_stdio_src(&cinfo, infile);
jpeg_istream_src(&cinfo,&fin);
/* Step 3: read file parameters with jpeg_read_header() */
jpeg_save_markers (&cinfo, EXIF_JPEG_MARKER, 0xffff);
(void) jpeg_read_header(&cinfo, TRUE);
/* We can ignore the return value from jpeg_read_header since
* (a) suspension is not possible with the stdio data source, and
@@ -544,6 +542,14 @@ int *numComponents_ret)
* See libjpeg.doc for more info.
*/
/* check for orientation tag */
*exif_orientation = EXIF_Orientation (&cinfo);
if (*exif_orientation!=0)
{
OSG_INFO<<"We have an EXIF_Orientation "<<exif_orientation<<std::endl;
}
/* Step 4: set parameters for decompression */
/* In this example, we don't need to change any of the defaults set by
* jpeg_read_header(), so we do nothing here.
@@ -825,8 +831,9 @@ class ReaderWriterJPEG : public osgDB::ReaderWriter
int width_ret;
int height_ret;
int numComponents_ret;
unsigned int exif_orientation=0;
imageData = osgDBJPEG::simage_jpeg_load(fin,&width_ret,&height_ret,&numComponents_ret);
imageData = osgDBJPEG::simage_jpeg_load(fin, &width_ret, &height_ret, &numComponents_ret, &exif_orientation);
if (imageData==NULL) return ReadResult::ERROR_IN_READING_FILE;
@@ -857,6 +864,23 @@ class ReaderWriterJPEG : public osgDB::ReaderWriter
imageData,
osg::Image::USE_NEW_DELETE);
if (exif_orientation>0)
{
// guide for meaning of exif_orientation provided by webpage: http://sylvana.net/jpegcrop/exif_orientation.html
switch(exif_orientation)
{
case(1): OSG_NOTICE<<"EXIF_Orientation 1 (top, left side), No need to rotate image. "<<std::endl; break; // do noting
case(2): OSG_NOTICE<<"EXIF_Orientation 2 (top, right side), flip x."<<std::endl; break;
case(3): OSG_NOTICE<<"EXIF_Orientation 3 (bottom, right side), rotate 180."<<std::endl; break;
case(4): OSG_NOTICE<<"EXIF_Orientation 4 (bottom, left side). flip y, rotate 180."<<std::endl; break;
case(5): OSG_NOTICE<<"EXIF_Orientation 5 (left side, top). flip y, rotate 90."<<std::endl; break;
case(6): OSG_NOTICE<<"EXIF_Orientation 6 (right side, top). rotate 90."<<std::endl; break;
case(7): OSG_NOTICE<<"EXIF_Orientation 7 (right side, bottom), flip Y, rotate 270."<<std::endl; break;
case(8): OSG_NOTICE<<"EXIF_Orientation 8 (left side, bottom). rotate 270."<<std::endl; break;
}
}
return pOsgImage;
}
@@ -883,6 +907,8 @@ class ReaderWriterJPEG : public osgDB::ReaderWriter
std::string fileName = osgDB::findDataFile( file, options );
if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
OSG_NOTICE<<std::endl<<"readImage("<<file<<")"<<std::endl;
osgDB::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary);
if(!istream) return ReadResult::ERROR_IN_READING_FILE;
ReadResult rr = readJPGStream(istream);