android jpeg 이미지 해석 과정
1.
1.main.c
static int loadExifInfo(const char* FileName, int readJPG) {
#ifdef SUPERDEBUG
ALOGE("loadExifInfo");
#endif
int Modified = FALSE;
ReadMode_t ReadMode = READ_METADATA;
if (readJPG) {
// Must add READ_IMAGE else we can't write the JPG back out.
ReadMode |= READ_IMAGE;
}
#ifdef SUPERDEBUG
ALOGE("ResetJpgfile");
#endif
ResetJpgfile();// 0
// Start with an empty image information structure.
memset(&ImageInfo, 0, sizeof(ImageInfo));// ImageInfo
ImageInfo.FlashUsed = -1;
ImageInfo.MeteringMode = -1;
ImageInfo.Whitebalance = -1;
// Store file date/time.
{
struct stat st;
if (stat(FileName, &st) >= 0) {
ImageInfo.FileDateTime = st.st_mtime;
ImageInfo.FileSize = st.st_size;
}
}
strncpy(ImageInfo.FileName, FileName, PATH_MAX);
#ifdef SUPERDEBUG
ALOGE("ReadJpegFile");
#endif
return ReadJpegFile(FileName, ReadMode);
}
2.jpgfile.c 파일에서 ImageInfo 읽기
//Storage for simplified info extracted from file.
ImageInfo_t ImageInfo;
//--------------------------------------------------------------------------
// This structure stores Exif header image elements in a simple manner
// Used to store camera data as extracted from the various ways that it can be
// stored in an exif header
typedef struct {
char FileName [PATH_MAX+1];
time_t FileDateTime;
unsigned FileSize;
char CameraMake [32];
char CameraModel [40];
char DateTime [20];
char DigitizedTime[20];
// Fractions of seconds for DateTime tag, with milisecond precision.
char SubSecTime[SUB_SEC_SIZE];
// Fractions of seconds for DateTimeOriginal tag, with milisecond precision.
char SubSecTimeOrig[SUB_SEC_SIZE];
// Fractions of seconds for DateTimeDigitized tag, with milisecond precision.
char SubSecTimeDig[SUB_SEC_SIZE];
int Height, Width;
int Orientation;
int IsColor;
int Process;
int FlashUsed;
rat_t FocalLength;
float ExposureTime;
float ApertureFNumber;
float Distance;
float CCDWidth;
float ExposureBias;
float DigitalZoomRatio;
int FocalLength35mmEquiv; // Exif 2.2 tag - usually not present.
int Whitebalance;
int MeteringMode;
int ExposureProgram;
int ExposureMode;
int ISOequivalent;
int LightSource;
int DistanceRange;
char Comments[MAX_COMMENT_SIZE];
int CommentWidchars; // If nonzer, widechar comment, indicates number of chars.
unsigned ThumbnailOffset; // Exif offset to thumbnail
unsigned ThumbnailSize; // Size of thumbnail.
unsigned LargestExifOffset; // Last exif data referenced (to check if thumbnail is at end)
char ThumbnailAtEnd; // Exif header ends with the thumbnail
// (we can only modify the thumbnail if its at the end)
int ThumbnailSizeOffset;
int DateTimeOffsets[MAX_DATE_COPIES];
int numDateTimeTags;
int GpsInfoPresent;
char GpsLat[31];
char GpsLatRaw[MAX_BUF_SIZE];
char GpsLatRef[2];
char GpsLong[31];
char GpsLongRaw[MAX_BUF_SIZE];
char GpsLongRef[2];
char GpsAlt[20];
rat_t GpsAltRaw;
char GpsAltRef;
// gps-datestamp is 11 bytes ascii in EXIF 2.2
char GpsDateStamp[11];
char GpsTimeStamp[11];
char GpsProcessingMethod[GPS_PROCESSING_METHOD_LEN + 1];
}ImageInfo_t;
3. JPEG의 메이커를 해석한다. EXIF는 그 중의 한 메이커이다.다음에 exif에 대한 해석
//--------------------------------------------------------------------------
// JPEG markers consist of one or more 0xFF bytes, followed by a marker
// code byte (which is not an FF). Here are the marker codes of interest
// in this program. (See jdmarker.c for a more complete list.)
//--------------------------------------------------------------------------
#define M_SOF0 0xC0 // Start Of Frame N
#define M_SOF1 0xC1 // N indicates which compression process
#define M_SOF2 0xC2 // Only SOF0-SOF2 are now in common use
#define M_SOF3 0xC3
#define M_SOF5 0xC5 // NB: codes C4 and CC are NOT SOF markers
#define M_SOF6 0xC6
#define M_SOF7 0xC7
#define M_SOF9 0xC9
#define M_SOF10 0xCA
#define M_SOF11 0xCB
#define M_SOF13 0xCD
#define M_SOF14 0xCE
#define M_SOF15 0xCF
#define M_SOI 0xD8 // Start Of Image (beginning of datastream)
#define M_EOI 0xD9 // End Of Image (end of datastream)
#define M_SOS 0xDA // Start Of Scan (begins compressed data)
#define M_JFIF 0xE0 // Jfif marker
#define M_EXIF 0xE1 // Exif marker. Also used for XMP data!
#define M_XMP 0x10E1 // Not a real tag (same value in file as Exif!)
#define M_COM 0xFE // COMment
#define M_DQT 0xDB
#define M_DHT 0xC4
#define M_DRI 0xDD
#define M_IPTC 0xED // IPTC marker
4.ReadJpegFile, main.c
//--------------------------------------------------------------------------
// Read image data.
//--------------------------------------------------------------------------
int ReadJpegFile(const char * FileName, ReadMode_t ReadMode)
{
FILE * infile;
int ret;
infile = fopen(FileName, "rb"); // Unix ignores 'b', windows needs it.
if (infile == NULL) {
ALOGE("can't open '%s'", FileName);
fprintf(stderr, "can't open '%s'
", FileName);
return FALSE;
}
// Scan the JPEG headers.
printf("ReadJpegSections");
ret = ReadJpegSections(infile, ReadMode);//ReadJpegSections(infile, ReadMode)
if (!ret){
ALOGV("Cannot parse JPEG sections for file: %s", FileName);
fprintf(stderr,"Not JPEG: %s
",FileName);
}
fclose(infile);
if (ret == FALSE){
DiscardData();
}
return ret;
}
5.ReadJpegSections ,jpgfile.c, Parse the marker stream
//--------------------------------------------------------------------------
// Parse the marker stream until SOS or EOI is seen;
//--------------------------------------------------------------------------
int ReadJpegSections (FILE * infile, ReadMode_t ReadMode)
{
int a;
int HaveCom = FALSE;
a = fgetc(infile);
if (a != 0xff || fgetc(infile) != M_SOI){
return FALSE;
}
for(;;){
int itemlen;
int marker = 0;
int ll,lh, got;
uchar * Data;
CheckSectionsAllocated();
for (a=0;a<=16;a++){
marker = fgetc(infile);
if (marker != 0xff) break;
if (a >= 16){
fprintf(stderr,"too many padding bytes
");
return FALSE;
}
}
Sections[SectionsRead].Type = marker;
Sections[SectionsRead].Offset = ftell(infile);
// Read the length of the section.
lh = fgetc(infile);
ll = fgetc(infile);
itemlen = (lh << 8) | ll;
if (itemlen < 2){
// ErrFatal("invalid marker");
ALOGE("invalid marker");
return FALSE;
}
Sections[SectionsRead].Size = itemlen;
Data = (uchar *)malloc(itemlen);
if (Data == NULL){
// ErrFatal("Could not allocate memory");
ALOGE("Could not allocate memory");
return 0;
}
Sections[SectionsRead].Data = Data;
// Store first two pre-read bytes.
Data[0] = (uchar)lh;
Data[1] = (uchar)ll;
got = fread(Data+2, 1, itemlen-2, infile); // Read the whole section.
if (got != itemlen-2){
// ErrFatal("Premature end of file?");
ALOGE("Premature end of file?");
return FALSE;
}
SectionsRead += 1;
printf("reading marker %d", marker);
switch(marker){
case M_SOS: // stop before hitting compressed data
// If reading entire image is requested, read the rest of the data.
if (ReadMode & READ_IMAGE){
int cp, ep, size;
// Determine how much file is left.
cp = ftell(infile);
fseek(infile, 0, SEEK_END);
ep = ftell(infile);
fseek(infile, cp, SEEK_SET);
size = ep-cp;
Data = (uchar *)malloc(size);
if (Data == NULL){
// ErrFatal("could not allocate data for entire image");
ALOGE("could not allocate data for entire image");
return FALSE;
}
got = fread(Data, 1, size, infile);
if (got != size){
// ErrFatal("could not read the rest of the image");
ALOGE("could not read the rest of the image");
return FALSE;
}
CheckSectionsAllocated();
Sections[SectionsRead].Data = Data;
Sections[SectionsRead].Offset = cp;
Sections[SectionsRead].Size = size;
Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER;
SectionsRead ++;
HaveAll = 1;
}
return TRUE;
case M_EOI: // in case it's a tables-only JPEG stream
fprintf(stderr,"No image in jpeg!
");
return FALSE;
case M_COM: // Comment section
if (HaveCom || ((ReadMode & READ_METADATA) == 0)){
// Discard this section.
free(Sections[--SectionsRead].Data);
}else{
process_COM(Data, itemlen);
HaveCom = TRUE;
}
break;
case M_JFIF:
// Regular jpegs always have this tag, exif images have the exif
// marker instead, althogh ACDsee will write images with both markers.
// this program will re-create this marker on absence of exif marker.
// hence no need to keep the copy from the file.
free(Sections[--SectionsRead].Data);
break;
case M_EXIF:
// There can be different section using the same marker.
if (ReadMode & READ_METADATA){
if (memcmp(Data+2, "Exif", 4) == 0){
process_EXIF(Data, itemlen);// process_EXIF(Data, itemlen)
break;
}else if (memcmp(Data+2, "http:", 5) == 0){
Sections[SectionsRead-1].Type = M_XMP; // Change tag for internal purposes.
if (ShowTags){
printf("Image cotains XMP section, %d bytes long
", itemlen);
if (ShowTags){
ShowXmp(Sections[SectionsRead-1]);
}
}
break;
}
}
// Oterwise, discard this section.
free(Sections[--SectionsRead].Data);
break;
case M_IPTC:
if (ReadMode & READ_METADATA){
if (ShowTags){
printf("Image cotains IPTC section, %d bytes long
", itemlen);
}
// Note: We just store the IPTC section. Its relatively straightforward
// and we don't act on any part of it, so just display it at parse time.
}else{
free(Sections[--SectionsRead].Data);
}
break;
case M_SOF0:
case M_SOF1:
case M_SOF2:
case M_SOF3:
case M_SOF5:
case M_SOF6:
case M_SOF7:
case M_SOF9:
case M_SOF10:
case M_SOF11:
case M_SOF13:
case M_SOF14:
case M_SOF15:
process_SOFn(Data, marker);
break;
default:
// Skip any other sections.
if (ShowTags){
printf("Jpeg section marker 0x%02x size %d
",marker, itemlen);
}
break;
}
}
return TRUE;
}
6.Process a EXIF marker, exif.c
//--------------------------------------------------------------------------
// Process a EXIF marker
// Describes all the drivel that most digital cameras include...
//--------------------------------------------------------------------------
void process_EXIF (unsigned char * ExifSection, unsigned int length)
{
int FirstOffset;
FocalplaneXRes = 0;
FocalplaneUnits = 0;
ExifImageWidth = 0;
NumOrientations = 0;
if (ShowTags){
printf("Exif header %d bytes long
",length);
}
{ // Check the EXIF header component
static uchar ExifHeader[] = "Exif\0\0";
if (memcmp(ExifSection+2, ExifHeader,6)){
ErrNonfatal("Incorrect Exif header",0,0);
return;
}
}
if (memcmp(ExifSection+8,"II",2) == 0){
if (ShowTags) printf("Exif section in Intel order
");
MotorolaOrder = 0;
}else{
if (memcmp(ExifSection+8,"MM",2) == 0){
if (ShowTags) printf("Exif section in Motorola order
");
MotorolaOrder = 1;
}else{
ErrNonfatal("Invalid Exif alignment marker.",0,0);
return;
}
}
// Check the next value for correctness.
if (Get16u(ExifSection+10) != 0x2a){
ErrNonfatal("Invalid Exif start (1)",0,0);
return;
}
FirstOffset = Get32u(ExifSection+12);
if (FirstOffset < 8 || FirstOffset > 16){
// Usually set to 8, but other values valid too.
ErrNonfatal("Suspicious offset of first IFD value",0,0);
return;
}
DirWithThumbnailPtrs = NULL;
// First directory starts 16 bytes in. All offset are relative to 8 bytes in.
ProcessExifDir(ExifSection+8+FirstOffset, ExifSection+8, length-8, 0);
ImageInfo.ThumbnailAtEnd = ImageInfo.ThumbnailOffset >= ImageInfo.LargestExifOffset ? TRUE : FALSE;
#ifdef SUPERDEBUG
printf("Thumbnail %s end", (ImageInfo.ThumbnailAtEnd ? "at" : "NOT at"));
#endif
if (DumpExifMap){
unsigned a,b;
printf("Map: %05d- End of exif
",length-8);
// for (a=0;a
7.exif.c
//--------------------------------------------------------------------------
// Process one of the nested EXIF directories.
//--------------------------------------------------------------------------
static void ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase,
unsigned ExifLength, int NestingLevel)
{
int de;
int a;
int NumDirEntries;
unsigned ThumbnailOffset = 0;
unsigned ThumbnailSize = 0;
char IndentString[25];
printf("ProcessExifDir");
if (NestingLevel > 4){
ErrNonfatal("Maximum directory nesting exceeded (corrupt exif header)", 0,0);
return;
}
memset(IndentString, ' ', 25);
IndentString[NestingLevel * 4] = '\0';
NumDirEntries = Get16u(DirStart);
#define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
{
unsigned char * DirEnd;
DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
if (DirEnd+4 > (OffsetBase+ExifLength)){
if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength){
// Version 1.3 of jhead would truncate a bit too much.
// This also caught later on as well.
}else{
ErrNonfatal("Illegally sized exif subdirectory (%d entries)",NumDirEntries,0);
return;
}
}
if (DumpExifMap){
printf("Map: %05d-%05d: Directory
",(int)(DirStart-OffsetBase), (int)(DirEnd+4-OffsetBase));
}
}
if (ShowTags){
printf("(dir has %d entries)
",NumDirEntries);
}
for (de=0;de= NUM_FORMATS) {
// (-1) catches illegal zero case as unsigned underflows to positive large.
ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
continue;
}
if ((unsigned)Components > 0x10000){
ErrNonfatal("Illegal number of components %d for tag %04x", Components, Tag);
continue;
}
ByteCount = Components * BytesPerFormat[Format];
if (ByteCount > 4){
unsigned OffsetVal;
OffsetVal = Get32u(DirEntry+8);
// If its bigger than 4 bytes, the dir entry contains an offset.
if (OffsetVal+ByteCount > ExifLength){
// Bogus pointer offset and / or bytecount value
ErrNonfatal("Illegal value pointer for tag %04x", Tag,0);
continue;
}
ValuePtr = OffsetBase+OffsetVal;
if (OffsetVal > ImageInfo.LargestExifOffset){
ImageInfo.LargestExifOffset = OffsetVal;
}
if (DumpExifMap){
printf("Map: %05d-%05d: Data for tag %04x
",OffsetVal, OffsetVal+ByteCount, Tag);
}
}else{
// 4 bytes or less and value is in the dir entry itself
ValuePtr = DirEntry+8;
}
if (Tag == TAG_MAKER_NOTE){
if (ShowTags){
printf("%s Maker note: ",IndentString);
}
ProcessMakerNote(ValuePtr, ByteCount, OffsetBase, ExifLength);
continue;
}
if (ShowTags){
// Show tag name
for (a=0;;a++){
if (a >= (int)TAG_TABLE_SIZE){
printf("%s", IndentString);
printf(" Unknown Tag %04x Value = ", Tag);
break;
}
if (TagTable[a].Tag == Tag){
printf("%s", IndentString);
printf(" %s = ",TagTable[a].Desc);
break;
}
}
// Show tag value.
switch(Format){
case FMT_BYTE:
if(ByteCount>1){
printf("%.*ls
", ByteCount/2, (wchar_t *)ValuePtr);
}else{
PrintFormatNumber(ValuePtr, Format, ByteCount);
printf("
");
}
break;
case FMT_UNDEFINED:
// Undefined is typically an ascii string.
case FMT_STRING:
// String arrays printed without function call (different from int arrays)
{
printf("\"%s\"", ValuePtr);
// int NoPrint = 0;
// printf("\"");
// for (a=0;a= 32){
// putchar(ValuePtr[a]);
// NoPrint = 0;
// }else{
// // Avoiding indicating too many unprintable characters of proprietary
// // bits of binary information this program may not know how to parse.
// if (!NoPrint && a != ByteCount-1){
// putchar('?');
// NoPrint = 1;
// }
// }
// }
// printf("\"
");
}
break;
default:
// Handle arrays of numbers later (will there ever be?)
PrintFormatNumber(ValuePtr, Format, ByteCount);
printf("
");
}
}
// Extract useful components of tag
switch(Tag){
case TAG_MAKE:
strncpy(ImageInfo.CameraMake, (char *)ValuePtr, ByteCount < 31 ? ByteCount : 31);
break;
case TAG_MODEL:
strncpy(ImageInfo.CameraModel, (char *)ValuePtr, ByteCount < 39 ? ByteCount : 39);
break;
case TAG_SUBSEC_TIME:
strlcpy(ImageInfo.SubSecTime, (char *)ValuePtr, sizeof(ImageInfo.SubSecTime));
break;
case TAG_SUBSEC_TIME_ORIG:
strlcpy(ImageInfo.SubSecTimeOrig, (char *)ValuePtr,
sizeof(ImageInfo.SubSecTimeOrig));
break;
case TAG_SUBSEC_TIME_DIG:
strlcpy(ImageInfo.SubSecTimeDig, (char *)ValuePtr,
sizeof(ImageInfo.SubSecTimeDig));
break;
case TAG_DATETIME_DIGITIZED:
strlcpy(ImageInfo.DigitizedTime, (char *)ValuePtr,
sizeof(ImageInfo.DigitizedTime));
if (ImageInfo.numDateTimeTags >= MAX_DATE_COPIES){
ErrNonfatal("More than %d date fields! This is nuts", MAX_DATE_COPIES, 0);
break;
}
ImageInfo.DateTimeOffsets[ImageInfo.numDateTimeTags++] =
(char *)ValuePtr - (char *)OffsetBase;
break;
case TAG_DATETIME_ORIGINAL:
// If we get a DATETIME_ORIGINAL, we use that one.
strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19);
// Fallthru...
case TAG_DATETIME:
if (!isdigit(ImageInfo.DateTime[0])){
// If we don't already have a DATETIME_ORIGINAL, use whatever
// time fields we may have.
strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19);
}
if (ImageInfo.numDateTimeTags >= MAX_DATE_COPIES){
ErrNonfatal("More than %d date fields! This is nuts", MAX_DATE_COPIES, 0);
break;
}
ImageInfo.DateTimeOffsets[ImageInfo.numDateTimeTags++] =
(char *)ValuePtr - (char *)OffsetBase;
break;
case TAG_WINXP_COMMENT:
if (ImageInfo.Comments[0]){ // We already have a jpeg comment.
// Already have a comment (probably windows comment), skip this one.
if (ShowTags) printf("Windows XP commend and other comment in header
");
break; // Already have a windows comment, skip this one.
}
if (ByteCount > 1){
if (ByteCount > MAX_COMMENT_SIZE) ByteCount = MAX_COMMENT_SIZE;
memcpy(ImageInfo.Comments, ValuePtr, ByteCount);
ImageInfo.CommentWidchars = ByteCount/2;
}
break;
case TAG_USERCOMMENT:
if (ImageInfo.Comments[0]){ // We already have a jpeg comment.
// Already have a comment (probably windows comment), skip this one.
if (ShowTags) printf("Multiple comments in exif header
");
break; // Already have a windows comment, skip this one.
}
// Comment is often padded with trailing spaces. Remove these first.
for (a=ByteCount;;){
a--;
if ((ValuePtr)[a] == ' '){
(ValuePtr)[a] = '\0';
}else{
break;
}
if (a == 0) break;
}
// Copy the comment
{
// We want to set copied comment length (msize) to be the
// minimum of:
// (1) The space still available in Exif
// (2) The given comment length (ByteCount)
// (3) MAX_COMMENT_SIZE - 1
int msiz = ExifLength - (ValuePtr-OffsetBase);
if (msiz > ByteCount) msiz = ByteCount;
if (msiz > MAX_COMMENT_SIZE - 1) msiz = MAX_COMMENT_SIZE - 1;
if (msiz > 5 && memcmp(ValuePtr, "ASCII", 5) == 0) {
for (a = 5; a < 10 && a < msiz; a++) {
int c = (ValuePtr)[a];
if (c != '\0' && c != ' ') {
strncpy(ImageInfo.Comments,
(char *)ValuePtr + a, msiz - a);
break;
}
}
} else {
strncpy(ImageInfo.Comments, (char *)ValuePtr, msiz);
}
}
break;
case TAG_FNUMBER:
// Simplest way of expressing aperture, so I trust it the most.
// (overwrite previously computd value if there is one)
ImageInfo.ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_APERTURE:
case TAG_MAXAPERTURE:
// More relevant info always comes earlier, so only use this field if we don't
// have appropriate aperture information yet.
if (ImageInfo.ApertureFNumber == 0){
ImageInfo.ApertureFNumber
= (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5);
}
break;
case TAG_FOCALLENGTH:
// Nice digital cameras actually save the focal length as a function
// of how farthey are zoomed in.
ImageInfo.FocalLength.num = Get32u(ValuePtr);
ImageInfo.FocalLength.denom = Get32u(4+(char *)ValuePtr);
break;
case TAG_SUBJECT_DISTANCE:
// Inidcates the distacne the autofocus camera is focused to.
// Tends to be less accurate as distance increases.
ImageInfo.Distance = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_EXPOSURETIME:
// Simplest way of expressing exposure time, so I trust it most.
// (overwrite previously computd value if there is one)
ImageInfo.ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_SHUTTERSPEED:
// More complicated way of expressing exposure time, so only use
// this value if we don't already have it from somewhere else.
if (ImageInfo.ExposureTime == 0){
ImageInfo.ExposureTime
= (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2)));
}
break;
case TAG_FLASH:
ImageInfo.FlashUsed=(int)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_ORIENTATION:
if (NumOrientations >= 2){
// Can have another orientation tag for the thumbnail, but if there's
// a third one, things are stringae.
ErrNonfatal("More than two orientation tags!",0,0);
break;
}
OrientationPtr[NumOrientations] = ValuePtr;
OrientationNumFormat[NumOrientations] = Format;
if (NumOrientations == 0){
ImageInfo.Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
}
if (ImageInfo.Orientation < 0 || ImageInfo.Orientation > 8){
ErrNonfatal("Undefined rotation value %d", ImageInfo.Orientation, 0);
ImageInfo.Orientation = 0;
}
NumOrientations += 1;
break;
case TAG_EXIF_IMAGELENGTH:
case TAG_EXIF_IMAGEWIDTH:
// Use largest of height and width to deal with images that have been
// rotated to portrait format.
a = (int)ConvertAnyFormat(ValuePtr, Format);
if (ExifImageWidth < a) ExifImageWidth = a;
break;
case TAG_FOCAL_PLANE_XRES:
FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_FOCAL_PLANE_UNITS:
switch((int)ConvertAnyFormat(ValuePtr, Format)){
case 1: FocalplaneUnits = 25.4; break; // inch
case 2:
// According to the information I was using, 2 means meters.
// But looking at the Cannon powershot's files, inches is the only
// sensible value.
FocalplaneUnits = 25.4;
break;
case 3: FocalplaneUnits = 10; break; // centimeter
case 4: FocalplaneUnits = 1; break; // millimeter
case 5: FocalplaneUnits = .001; break; // micrometer
}
break;
case TAG_EXPOSURE_BIAS:
ImageInfo.ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_WHITEBALANCE:
ImageInfo.Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_LIGHT_SOURCE:
ImageInfo.LightSource = (int)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_METERING_MODE:
ImageInfo.MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_EXPOSURE_PROGRAM:
ImageInfo.ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_EXPOSURE_INDEX:
if (ImageInfo.ISOequivalent == 0){
// Exposure index and ISO equivalent are often used interchangeably,
// so we will do the same in jhead.
// http://photography.about.com/library/glossary/bldef_ei.htm
ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
}
break;
case TAG_EXPOSURE_MODE:
ImageInfo.ExposureMode = (int)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_ISO_EQUIVALENT:
ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
if ( ImageInfo.ISOequivalent < 50 ){
// Fixes strange encoding on some older digicams.
ImageInfo.ISOequivalent *= 200;
}
break;
case TAG_DIGITALZOOMRATIO:
ImageInfo.DigitalZoomRatio = (float)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_THUMBNAIL_OFFSET:
ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);
DirWithThumbnailPtrs = DirStart;
break;
case TAG_THUMBNAIL_LENGTH:
ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
ImageInfo.ThumbnailSizeOffset = ValuePtr-OffsetBase;
break;
case TAG_EXIF_OFFSET:
if (ShowTags) printf("%s Exif Dir:",IndentString);
case TAG_INTEROP_OFFSET:
if (Tag == TAG_INTEROP_OFFSET && ShowTags) printf("%s Interop Dir:",IndentString);
{
unsigned char * SubdirStart;
SubdirStart = OffsetBase + Get32u(ValuePtr);
if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){
ErrNonfatal("Illegal exif or interop ofset directory link",0,0);
}else{
ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
}
continue;
}
break;
case TAG_GPSINFO:
if (ShowTags) printf("%s GPS info dir:",IndentString);
{
unsigned char * SubdirStart;
SubdirStart = OffsetBase + Get32u(ValuePtr);
if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){
ErrNonfatal("Illegal GPS directory link",0,0);
}else{
ProcessGpsInfo(SubdirStart, ByteCount, OffsetBase, ExifLength);
}
continue;
}
break;
case TAG_FOCALLENGTH_35MM:
// The focal length equivalent 35 mm is a 2.2 tag (defined as of April 2002)
// if its present, use it to compute equivalent focal length instead of
// computing it from sensor geometry and actual focal length.
ImageInfo.FocalLength35mmEquiv = (unsigned)ConvertAnyFormat(ValuePtr, Format);
break;
case TAG_DISTANCE_RANGE:
// Three possible standard values:
// 1 = macro, 2 = close, 3 = distant
ImageInfo.DistanceRange = (int)ConvertAnyFormat(ValuePtr, Format);
break;
}
}
{
// In addition to linking to subdirectories via exif tags,
// there's also a potential link to another directory at the end of each
// directory. this has got to be the result of a committee!
unsigned char * SubdirStart;
unsigned Offset;
if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength){
printf("DirStart %p offset from dirstart %d", DirStart, 2+12*NumDirEntries);
Offset = Get32u(DirStart+2+12*NumDirEntries);
if (Offset){
SubdirStart = OffsetBase + Offset;
if (SubdirStart > OffsetBase+ExifLength || SubdirStart < OffsetBase){
printf("SubdirStart %p OffsetBase %p ExifLength %d Offset %d",
SubdirStart, OffsetBase, ExifLength, Offset);
if (SubdirStart > OffsetBase && SubdirStart < OffsetBase+ExifLength+20){
// Jhead 1.3 or earlier would crop the whole directory!
// As Jhead produces this form of format incorrectness,
// I'll just let it pass silently
if (ShowTags) printf("Thumbnail removed with Jhead 1.3 or earlier
");
}else{
ErrNonfatal("Illegal subdirectory link",0,0);
}
}else{
if (SubdirStart <= OffsetBase+ExifLength){
if (ShowTags) printf("%s Continued directory ",IndentString);
ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1);
}
}
if (Offset > ImageInfo.LargestExifOffset){
ImageInfo.LargestExifOffset = Offset;
}
}
}else{
// The exif header ends before the last next directory pointer.
}
}
if (ThumbnailOffset){
ImageInfo.ThumbnailAtEnd = FALSE;
if (DumpExifMap){
printf("Map: %05d-%05d: Thumbnail
",ThumbnailOffset, ThumbnailOffset+ThumbnailSize);
}
if (ThumbnailOffset <= ExifLength){
if (ThumbnailSize > ExifLength-ThumbnailOffset){
// If thumbnail extends past exif header, only save the part that
// actually exists. Canon's EOS viewer utility will do this - the
// thumbnail extracts ok with this hack.
ThumbnailSize = ExifLength-ThumbnailOffset;
if (ShowTags) printf("Thumbnail incorrectly placed in header
");
}
// The thumbnail pointer appears to be valid. Store it.
ImageInfo.ThumbnailOffset = ThumbnailOffset;
ImageInfo.ThumbnailSize = ThumbnailSize;
if (ShowTags){
printf("Thumbnail size: %d bytes
",ThumbnailSize);
}
}
}
printf("returning from ProcessExifDir");
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Skia의 기본 드로잉 명령 1텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.