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:
@@ -1,6 +1,10 @@
|
|||||||
INCLUDE_DIRECTORIES( ${JPEG_INCLUDE_DIR} )
|
INCLUDE_DIRECTORIES( ${JPEG_INCLUDE_DIR} )
|
||||||
|
|
||||||
SET(TARGET_SRC ReaderWriterJPEG.cpp )
|
SET(TARGET_SRC
|
||||||
|
EXIF_Orientation.cpp
|
||||||
|
ReaderWriterJPEG.cpp
|
||||||
|
)
|
||||||
|
|
||||||
SET(TARGET_LIBRARIES_VARS JPEG_LIBRARY )
|
SET(TARGET_LIBRARIES_VARS JPEG_LIBRARY )
|
||||||
#### end var setup ###
|
#### end var setup ###
|
||||||
SETUP_PLUGIN(jpeg)
|
SETUP_PLUGIN(jpeg)
|
||||||
|
|||||||
182
src/osgPlugins/jpeg/EXIF_Orientation.cpp
Normal file
182
src/osgPlugins/jpeg/EXIF_Orientation.cpp
Normal 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 */
|
||||||
|
}
|
||||||
|
|
||||||
16
src/osgPlugins/jpeg/EXIF_Orientation.h
Normal file
16
src/osgPlugins/jpeg/EXIF_Orientation.h
Normal 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
|
||||||
@@ -48,13 +48,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include "EXIF_Orientation.h"
|
||||||
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
#include <jpeglib.h>
|
|
||||||
#include "jerror.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -467,11 +461,11 @@ copyScanline(unsigned char *currPtr, unsigned char *from, int cnt)
|
|||||||
return currPtr;
|
return currPtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char *
|
unsigned char* simage_jpeg_load(std::istream& fin,
|
||||||
simage_jpeg_load(std::istream& fin,
|
int *width_ret,
|
||||||
int *width_ret,
|
int *height_ret,
|
||||||
int *height_ret,
|
int *numComponents_ret,
|
||||||
int *numComponents_ret)
|
unsigned int* exif_orientation)
|
||||||
{
|
{
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
@@ -535,8 +529,12 @@ int *numComponents_ret)
|
|||||||
//jpeg_stdio_src(&cinfo, infile);
|
//jpeg_stdio_src(&cinfo, infile);
|
||||||
jpeg_istream_src(&cinfo,&fin);
|
jpeg_istream_src(&cinfo,&fin);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Step 3: read file parameters with jpeg_read_header() */
|
/* Step 3: read file parameters with jpeg_read_header() */
|
||||||
|
|
||||||
|
jpeg_save_markers (&cinfo, EXIF_JPEG_MARKER, 0xffff);
|
||||||
|
|
||||||
(void) jpeg_read_header(&cinfo, TRUE);
|
(void) jpeg_read_header(&cinfo, TRUE);
|
||||||
/* We can ignore the return value from jpeg_read_header since
|
/* We can ignore the return value from jpeg_read_header since
|
||||||
* (a) suspension is not possible with the stdio data source, and
|
* (a) suspension is not possible with the stdio data source, and
|
||||||
@@ -544,6 +542,14 @@ int *numComponents_ret)
|
|||||||
* See libjpeg.doc for more info.
|
* 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 */
|
/* Step 4: set parameters for decompression */
|
||||||
/* In this example, we don't need to change any of the defaults set by
|
/* In this example, we don't need to change any of the defaults set by
|
||||||
* jpeg_read_header(), so we do nothing here.
|
* jpeg_read_header(), so we do nothing here.
|
||||||
@@ -825,8 +831,9 @@ class ReaderWriterJPEG : public osgDB::ReaderWriter
|
|||||||
int width_ret;
|
int width_ret;
|
||||||
int height_ret;
|
int height_ret;
|
||||||
int numComponents_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;
|
if (imageData==NULL) return ReadResult::ERROR_IN_READING_FILE;
|
||||||
|
|
||||||
@@ -857,6 +864,23 @@ class ReaderWriterJPEG : public osgDB::ReaderWriter
|
|||||||
imageData,
|
imageData,
|
||||||
osg::Image::USE_NEW_DELETE);
|
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;
|
return pOsgImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -883,6 +907,8 @@ class ReaderWriterJPEG : public osgDB::ReaderWriter
|
|||||||
std::string fileName = osgDB::findDataFile( file, options );
|
std::string fileName = osgDB::findDataFile( file, options );
|
||||||
if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
|
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);
|
osgDB::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary);
|
||||||
if(!istream) return ReadResult::ERROR_IN_READING_FILE;
|
if(!istream) return ReadResult::ERROR_IN_READING_FILE;
|
||||||
ReadResult rr = readJPGStream(istream);
|
ReadResult rr = readJPGStream(istream);
|
||||||
|
|||||||
Reference in New Issue
Block a user