Diffstat (limited to 'noncore/graphics/opie-eye/slave/jpeg_slave.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | noncore/graphics/opie-eye/slave/jpeg_slave.cpp | 18 |
1 files changed, 11 insertions, 7 deletions
diff --git a/noncore/graphics/opie-eye/slave/jpeg_slave.cpp b/noncore/graphics/opie-eye/slave/jpeg_slave.cpp index 086b47f..fb7d5de 100644 --- a/noncore/graphics/opie-eye/slave/jpeg_slave.cpp +++ b/noncore/graphics/opie-eye/slave/jpeg_slave.cpp @@ -1,55 +1,59 @@ #include "jpeg_slave.h" - #include "thumbnailtool.h" PHUNK_VIEW_INTERFACE( "JPEG", JpegSlave ) -#include <qtopia/timestring.h> +/* OPIE */ +#include <opie2/odebug.h> +#include <qpe/timestring.h> +using namespace Opie::Core; + +/* QT */ #include <qobject.h> #include <qimage.h> /** exif.h */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #include <qstring.h> #include <qfile.h> #include <qimage.h> typedef enum { READ_EXIF = 1, READ_IMAGE = 2, READ_ALL = 3 }ReadMode_t; //-------------------------------------------------------------------------- // This structure is used to store jpeg file sections in memory. typedef struct { uchar * Data; int Type; unsigned Size; }Section_t; typedef unsigned char uchar; typedef struct { unsigned short Tag; const char*const Desc; }TagTable_t; #define MAX_SECTIONS 20 #define PSEUDO_IMAGE_MARKER 0x123; // Extra value. class ExifData { Section_t Sections[MAX_SECTIONS]; QString CameraMake; QString CameraModel; QString DateTime; int Orientation; int Height, Width; @@ -74,97 +78,97 @@ class ExifData { int ReadJpegSections (QFile & infile, ReadMode_t ReadMode); void DiscardData(void); int Get16u(void * Short); int Get32s(void * Long); unsigned Get32u(void * Long); double ConvertAnyFormat(void * ValuePtr, int Format); void ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength); void process_COM (const uchar * Data, int length); void process_SOFn (const uchar * Data, int marker); int Get16m(const void * Short); void process_EXIF(unsigned char * CharBuf, unsigned int length); int Exif2tm(struct tm * timeptr, char * ExifTime); public: ExifData(); bool scan(const QString &); QString getCameraMake() { return CameraMake; } QString getCameraModel() { return CameraModel; } QString getDateTime() { return DateTime; } int getOrientation() { return Orientation; } int getHeight() { return Height; } int getWidth() { return Width; } int getIsColor() { return IsColor; } int getProcess() { return Process; } int getFlashUsed() { return FlashUsed; } float getFocalLength() { return FocalLength; } float getExposureTime() { return ExposureTime; } float getApertureFNumber() { return ApertureFNumber; } float getDistance() { return Distance; } int getWhitebalance() { return Whitebalance; } int getMeteringMode() { return MeteringMode; } float getCCDWidth() { return CCDWidth; } float getExposureBias() { return ExposureBias; } int getExposureProgram() { return ExposureProgram; } int getISOequivalent() { return ISOequivalent; } int getCompressionLevel() { return CompressionLevel; } QString getUserComment() { return UserComment; } QString getComment() { return Comment; } QImage getThumbnail(); bool isThumbnailSane(); bool isNullThumbnail() { return !isThumbnailSane(); } }; class FatalError { const char* ex; public: FatalError(const char* s) { ex = s; } - void debug_print() const { qWarning("exception: %s", ex ); } + void debug_print() const { owarn << "exception: " << ex << "" << oendl; } }; static unsigned char * LastExifRefd; static int ExifSettingsLength; static double FocalplaneXRes; static double FocalplaneUnits; static int MotorolaOrder = 0; static int SectionsRead; //static int HaveAll; //-------------------------------------------------------------------------- // Table of Jpeg encoding process names #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 #define M_COM 0xFE // COMment TagTable_t ProcessTable[] = { { M_SOF0, "Baseline"}, { M_SOF1, "Extended sequential"}, { M_SOF2, "Progressive"}, { M_SOF3, "Lossless"}, { M_SOF5, "Differential sequential"}, { M_SOF6, "Differential progressive"}, { M_SOF7, "Differential lossless"}, { M_SOF9, "Extended sequential, arithmetic coding"}, { M_SOF10, "Progressive, arithmetic coding"}, { M_SOF11, "Lossless, arithmetic coding"}, { M_SOF13, "Differential sequential, arithmetic coding"}, @@ -217,162 +221,162 @@ static int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8}; #define TAG_SUBJECT_DISTANCE 0x9206 #define TAG_FLASH 0x9209 #define TAG_FOCALPLANEXRES 0xa20E #define TAG_FOCALPLANEUNITS 0xa210 #define TAG_EXIF_IMAGEWIDTH 0xA002 #define TAG_EXIF_IMAGELENGTH 0xA003 // the following is added 05-jan-2001 vcs #define TAG_EXPOSURE_BIAS 0x9204 #define TAG_WHITEBALANCE 0x9208 #define TAG_METERING_MODE 0x9207 #define TAG_EXPOSURE_PROGRAM 0x8822 #define TAG_ISO_EQUIVALENT 0x8827 #define TAG_COMPRESSION_LEVEL 0x9102 #define TAG_THUMBNAIL_OFFSET 0x0201 #define TAG_THUMBNAIL_LENGTH 0x0202 //-------------------------------------------------------------------------- // Parse the marker stream until SOS or EOI is seen; //-------------------------------------------------------------------------- int ExifData::ReadJpegSections (QFile & infile, ReadMode_t ReadMode) { int a; a = infile.getch(); if (a != 0xff || infile.getch() != M_SOI) { SectionsRead = 0; return false; } for(SectionsRead = 0; SectionsRead < MAX_SECTIONS-1; ){ int marker = 0; int got; unsigned int ll,lh; unsigned int itemlen; uchar * Data; for (a=0;a<7;a++){ marker = infile.getch(); if (marker != 0xff) break; if (a >= 6){ - qWarning( "too many padding bytes" ); + owarn << "too many padding bytes" << oendl; return false; } } if (marker == 0xff){ // 0xff is legal padding, but if we get that many, something's wrong. return false; } Sections[SectionsRead].Type = marker; // Read the length of the section. lh = (uchar) infile.getch(); ll = (uchar) infile.getch(); itemlen = (lh << 8) | ll; if (itemlen < 2) { return false;; } Sections[SectionsRead].Size = itemlen; Data = (uchar *)malloc(itemlen+1); // Add 1 to allow sticking a 0 at the end. Sections[SectionsRead].Data = Data; // Store first two pre-read bytes. Data[0] = (uchar)lh; Data[1] = (uchar)ll; got = infile.readBlock((char*)Data+2, itemlen-2); // Read the whole section. if (( unsigned ) got != itemlen-2){ return false; } SectionsRead++; 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){ unsigned long size; size = QMAX( 0ul, infile.size()-infile.at() ); Data = (uchar *)malloc(size); if (Data == NULL){ return false; } got = infile.readBlock((char*)Data, size); if (( unsigned ) got != size){ return false; } Sections[SectionsRead].Data = Data; 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 - qWarning( "No image in jpeg!" ); + owarn << "No image in jpeg!" << oendl; return false; case M_COM: // Comment section // pieczy 2002-02-12 // now the User comment goes to UserComment // so we can store a Comment section also in READ_EXIF mode process_COM(Data, itemlen); 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: // Seen files from some 'U-lead' software with Vivitar scanner // that uses marker 31 for non exif stuff. Thus make sure // it says 'Exif' in the section before treating it as exif. if ((ReadMode & READ_EXIF) && memcmp(Data+2, "Exif", 4) == 0){ process_EXIF((uchar *)Data, itemlen); }else{ // Discard this section. 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); default: break; break; } } @@ -681,97 +685,97 @@ void ExifData::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBa ExifData::ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); if ( ExifData::ISOequivalent < 50 ) ExifData::ISOequivalent *= 200; break; case TAG_COMPRESSION_LEVEL: ExifData::CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format); break; case TAG_THUMBNAIL_OFFSET: ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format); break; case TAG_THUMBNAIL_LENGTH: ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format); break; } if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){ unsigned char * SubdirStart; SubdirStart = OffsetBase + Get32u(ValuePtr); if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){ return; } ProcessExifDir(SubdirStart, OffsetBase, ExifLength); continue; } } { // 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 comitee! unsigned char * SubdirStart; unsigned Offset; if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength){ Offset = Get32u(DIR_ENTRY_ADDR(DirStart, NumDirEntries)); // There is at least one jpeg from an HP camera having an Offset of almost MAXUINT. // Adding OffsetBase to it produces an overflow, so compare with ExifLength here. // See http://bugs.kde.org/show_bug.cgi?id=54542 if (Offset && Offset < ExifLength){ SubdirStart = OffsetBase + Offset; if (SubdirStart > OffsetBase+ExifLength){ if (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 - qWarning( "Thumbnail removed with Jhead 1.3 or earlier" ); + owarn << "Thumbnail removed with Jhead 1.3 or earlier" << oendl; }else{ return; } }else{ if (SubdirStart <= OffsetBase+ExifLength){ ProcessExifDir(SubdirStart, OffsetBase, ExifLength); } } } }else{ // The exif header ends before the last next directory pointer. } } if (ThumbnailSize && ThumbnailOffset){ if (ThumbnailSize + ThumbnailOffset <= ExifLength){ // The thumbnail pointer appears to be valid. Store it. Thumbnail.loadFromData(OffsetBase + ThumbnailOffset, ThumbnailSize, "JPEG"); } } } //-------------------------------------------------------------------------- // Process a COM marker. We want to leave the bytes unchanged. The // progam that displays this text may decide to remove blanks, convert // newlines, or otherwise modify the text. In particular we want to be // safe for passing utf-8 text. //-------------------------------------------------------------------------- void ExifData::process_COM (const uchar * Data, int length) { QChar ch; int a; for (a=2;a<length;a++){ ch = Data[a]; if (ch == '\000') continue; // Remove nulls Comment.append(ch); } } //-------------------------------------------------------------------------- // Process a SOFn marker. This is useful for the image dimensions //-------------------------------------------------------------------------- void ExifData::process_SOFn (const uchar * Data, int marker) { int data_precision, num_components; @@ -868,97 +872,97 @@ int ExifData::Exif2tm(struct tm * timeptr, char * ExifTime) if (a == 6){ timeptr->tm_isdst = -1; timeptr->tm_mon -= 1; // Adjust for unix zero-based months timeptr->tm_year -= 1900; // Adjust for year starting at 1900 return true; // worked. } return false; // Wasn't in Exif date format. } //-------------------------------------------------------------------------- // Contructor for initialising //-------------------------------------------------------------------------- ExifData::ExifData() { ExifData::Whitebalance = -1; ExifData::MeteringMode = -1; ExifData::FlashUsed = -1; Orientation = 0; Height = 0; Width = 0; IsColor = 0; Process = 0; FocalLength = 0; ExposureTime = 0; ApertureFNumber = 0; Distance = 0; CCDWidth = 0; ExposureBias = 0; ExposureProgram = 0; ISOequivalent = 0; CompressionLevel = 0; } //-------------------------------------------------------------------------- // process a EXIF jpeg file //-------------------------------------------------------------------------- bool ExifData::scan(const QString & path) { int ret; QFile f(path); f.open(IO_ReadOnly); // Scan the JPEG headers. ret = ReadJpegSections(f, READ_EXIF); if (ret == false){ - qWarning( "Not JPEG file!" ); + owarn << "Not JPEG file!" << oendl; DiscardData(); f.close(); return false; } f.close(); DiscardData(); //now make the strings clean, // for exmaple my Casio is a "QV-4000 " CameraMake = CameraMake.stripWhiteSpace(); CameraModel = CameraModel.stripWhiteSpace(); UserComment = UserComment.stripWhiteSpace(); Comment = Comment.stripWhiteSpace(); return true; } //-------------------------------------------------------------------------- // Does the embedded thumbnail match the jpeg image? //-------------------------------------------------------------------------- #ifndef JPEG_TOL #define JPEG_TOL 0.02 #endif bool ExifData::isThumbnailSane() { if (Thumbnail.isNull()) return false; // check whether thumbnail dimensions match the image // not foolproof, but catches some altered images (jpegtran -rotate) if (ExifImageLength != 0 && ExifImageLength != Height) return false; if (ExifImageWidth != 0 && ExifImageWidth != Width) return false; if (Thumbnail.width() == 0 || Thumbnail.height() == 0) return false; if (Height == 0 || Width == 0) return false; double d = (double)Height/Width*Thumbnail.width()/Thumbnail.height(); return (1-JPEG_TOL < d) && (d < 1+JPEG_TOL); } static QImage flip_image( const QImage& img ); static QImage rotate_90( const QImage& img ); static QImage rotate_180( const QImage& ); static QImage rotate_270( const QImage& ); //-------------------------------------------------------------------------- // return a thumbnail that respects the orientation flag // only if it seems sane //-------------------------------------------------------------------------- QImage ExifData::getThumbnail() { if (!isThumbnailSane()) return NULL; |