Diffstat (limited to 'noncore/graphics/opie-eye/slave') (more/less context) (show whitespace changes)
-rw-r--r-- | noncore/graphics/opie-eye/slave/gif_slave.cpp | 305 | ||||
-rw-r--r-- | noncore/graphics/opie-eye/slave/gif_slave.h | 22 | ||||
-rw-r--r-- | noncore/graphics/opie-eye/slave/jpeg_slave.cpp | 1426 | ||||
-rw-r--r-- | noncore/graphics/opie-eye/slave/jpeg_slave.h | 19 | ||||
-rw-r--r-- | noncore/graphics/opie-eye/slave/main.cpp | 59 | ||||
-rw-r--r-- | noncore/graphics/opie-eye/slave/png_slave.cpp | 210 | ||||
-rw-r--r-- | noncore/graphics/opie-eye/slave/png_slave.h | 21 | ||||
-rw-r--r-- | noncore/graphics/opie-eye/slave/slave.pro | 18 | ||||
-rw-r--r-- | noncore/graphics/opie-eye/slave/slaveiface.cpp | 26 | ||||
-rw-r--r-- | noncore/graphics/opie-eye/slave/slaveiface.h | 53 | ||||
-rw-r--r-- | noncore/graphics/opie-eye/slave/slavereciever.cpp | 214 | ||||
-rw-r--r-- | noncore/graphics/opie-eye/slave/slavereciever.h | 58 | ||||
-rw-r--r-- | noncore/graphics/opie-eye/slave/thumbnailtool.cpp | 61 | ||||
-rw-r--r-- | noncore/graphics/opie-eye/slave/thumbnailtool.h | 19 |
14 files changed, 2511 insertions, 0 deletions
diff --git a/noncore/graphics/opie-eye/slave/gif_slave.cpp b/noncore/graphics/opie-eye/slave/gif_slave.cpp new file mode 100644 index 0000000..feb69b6 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/gif_slave.cpp @@ -0,0 +1,305 @@ +#include "gif_slave.h" + +#include "thumbnailtool.h" + +#include <qimage.h> +#include <qobject.h> +#include <qfile.h> +#include <qpixmap.h> + + +PHUNK_VIEW_INTERFACE( "Gif", GifSlave ); + + +namespace { +/* +** $Id$ +** +** Minimal GIF parser, for use in extracting and setting metadata. +** Modified for standalone & KDE calling by Bryce Nesbitt +** +** TODO: +** Support gif comments that span more than one comment block. +** Verify that Unicode utf-8 is fully unmolested by this code. +** Implement gif structure verifier. +** +** Based on: GIFtrans v1.12.2 +** Copyright (C) 24.2.94 by Andreas Ley <ley@rz.uni-karlsruhe.de> +** +******************************************************************************* +** +** Original distribution site is +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.c +** A man-page by knordlun@fltxa.helsinki.fi (Kai Nordlund) is at +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.1 +** An online version by taylor@intuitive.com (Dave Taylor) is at +** http://www.intuitive.com/coolweb/Addons/giftrans-doc.html +** To compile for MS-DOS or OS/2, you need getopt: +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/getopt.c +** MS-DOS executable can be found at +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.exe +** OS/2 executable can be found at +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.os2.exe +** A template rgb.txt for use with the MS-DOS version can be found at +** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/rgb.txt +** Additional info can be found on +** http://melmac.corp.harris.com/transparent_images.html +** +** The GIF file format is documented in +** ftp://ftp.uu.net/doc/literary/obi/Standards/Graphics/Formats/gif89a.doc.Z +** A good quick reference is at: +** http://www.goice.co.jp/member/mo/formats/gif.html +** +******************************************************************************* +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +*/ +#define SUCCESS (0) +#define FAILURE (1) + +#define READ_BINARY "r" +#define WRITE_BINARY "w" + + +static long int pos; +static char skipcomment,verbose; +char *global_comment; + +#define readword(buffer) ((buffer)[0]+256*(buffer)[1]) +#define readflag(buffer) ((buffer)?true:false) +#define hex(c) ('a'<=(c)&&(c)<='z'?(c)-'a'+10:'A'<=(c)&&(c)<='Z'?(c)-'A'+10:(c)-'0') + +static bool debug = false; +static bool output= false; + +void dump(long int, unsigned char *, size_t) { +} + +void skipdata(FILE *src) +{ + unsigned char size,buffer[256]; + + do { + pos=ftell(src); + (void)fread((void *)&size,1,1,src); + if (debug) + dump(pos,&size,1); + if (debug) { + pos=ftell(src); + (void)fread((void *)buffer,(size_t)size,1,src); + dump(pos,buffer,(size_t)size); + } + else + (void)fseek(src,(long int)size,SEEK_CUR); + } while (!feof(src)&&size>0); +} + + +void transdata(FILE *src, FILE *dest) +{ + unsigned char size,buffer[256]; + + do { + pos=ftell(src); + (void)fread((void *)&size,1,1,src); + pos=ftell(src); + (void)fread((void *)buffer,(size_t)size,1,src); + if (debug) + dump(pos,buffer,(size_t)size); + if (output) + (void)fwrite((void *)buffer,(size_t)size,1,dest); + } while (!feof(src)&&size>0); +} + +void transblock(FILE *src, FILE* dest) +{ + unsigned char size,buffer[256]; + + pos=ftell(src); + (void)fread((void *)&size,1,1,src); + if (debug) + dump(pos,&size,1); + if (output) + (void)fwrite((void *)&size,1,1,dest); + pos=ftell(src); + (void)fread((void *)buffer,(size_t)size,1,src); + if (debug) + dump(pos,buffer,(size_t)size); + if (output) + (void)fwrite((void *)buffer,(size_t)size,1,dest); +} + +void dumpcomment(FILE *src, QCString& str) +{ + unsigned char size; + + pos=ftell(src); + (void)fread((void *)&size,1,1,src); + if (debug) + dump(pos,&size,1); + str.resize( size+1 ); + (void)fread((void *)str.data(),size,1,src); + (void)fseek(src,(long int)pos,SEEK_SET); +} + + + + +int giftrans(FILE *src, FILE* dest, QString& str, bool full) +{ + unsigned char buffer[3*256],lsd[7],gct[3*256]; + unsigned int size,gct_size; + + /* Header */ + pos=ftell(src); + (void)fread((void *)buffer,6,1,src); + if (strncmp((char *)buffer,"GIF",3)) { + str = QObject::tr("Not a GIF file"); + (void)fprintf(stderr,"Not GIF file!\n"); + return(1); + } + + /* Logical Screen Descriptor */ + pos=ftell(src); + (void)fread((void *)lsd,7,1,src); + //(void)fprintf(stderr,"Logical Screen Descriptor:\n"); + str += QObject::tr("Dimensions: %1x%2\n").arg( readword(lsd) ).arg( readword(lsd+2 ) ); + //(void)fprintf(stderr,"Global Color Table Flag: %s\n",readflag(lsd[4]&0x80)); + str += QObject::tr("Depth: %1 bits\n").arg( (lsd[4]&0x70>>4 )+1); + //(void)fprintf(stderr,"Depth : %d bits\n",(lsd[4]&0x70>>4)+1); + if (lsd[4]&0x80 && full) { + str += QObject::tr("Sort Flag: %1\n" ).arg(readflag(lsd[4]&0x8) ); + str += QObject::tr("Size of Global Color Table: %1 colors\n" ).arg( 2<<(lsd[4]&0x7)); + str += QObject::tr("Background Color Index: %1\n" ).arg(lsd[5]); + } + if (lsd[6] && full) + str += QObject::tr("Pixel Aspect Ratio: %1 (Aspect Ratio %2)\n" ).arg( lsd[6] ). + arg( ((double)lsd[6]+15)/64 ); + + /* Global Color Table */ + if (lsd[4]&0x80) { + gct_size=2<<(lsd[4]&0x7); + pos=ftell(src); + (void)fread((void *)gct,gct_size,3,src); + } + + do { + pos=ftell(src); + (void)fread((void *)buffer,1,1,src); + switch (buffer[0]) { + case 0x2c: /* Image Descriptor */ + (void)fread((void *)(buffer+1),9,1,src); + /* Local Color Table */ + if (buffer[8]&0x80) { + size=2<<(buffer[8]&0x7); + pos=ftell(src); + (void)fread((void *)buffer,size,3,src); + } + /* Table Based Image Data */ + pos=ftell(src); + (void)fread((void *)buffer,1,1,src); + transdata(src,dest); + break; + case 0x21: /* Extension */ + (void)fread((void *)(buffer+1),1,1,src); + switch (buffer[1]) { + case 0xfe: /* Comment Extension */ + if (true) + { + QCString st; + dumpcomment(src, st); + str += QObject::tr("Comment: %1\n" ).arg( st ); + } + if (skipcomment) + skipdata(src); + else { + transdata(src,dest); + } + break; + case 0x01: /* Plain Text Extension */ + case 0xf9: /* Graphic Control Extension */ + case 0xff: /* Application Extension */ + default: + transblock(src,dest); + transdata(src,dest); + break; + } + break; + case 0x3b: /* Trailer (write comment just before here) */ + break; + default: + (void)fprintf(stderr,"0x%08lx: Error, unknown block 0x%02x!\n",ftell(src)-1,buffer[0]); + return(1); + } + } while (buffer[0]!=0x3b&&!feof(src)); + return(buffer[0]==0x3b?SUCCESS:FAILURE); +} + + +/****************************************************************************/ +extern void get_gif_info( const char * original_filename, QString& str, + bool full =false) +{ +FILE * infile; + + if ((infile = fopen(original_filename, READ_BINARY)) == NULL) { + fprintf(stderr, "can't open gif image '%s'\n", original_filename); + return ; + } + + output = FALSE; + verbose = TRUE; + debug = FALSE; + skipcomment = FALSE; + giftrans( infile, NULL, str, full ); + fclose( infile ); +} + +} + + +GifSlave::GifSlave() + : SlaveInterface(QStringList("gif")) +{} + +GifSlave::~GifSlave() { + +} + +QString GifSlave::iconViewName(const QString& str) { + QString st; + get_gif_info(QFile::encodeName( str ).data(), st ); + return st; +} + +QString GifSlave::fullImageInfo( const QString& str) { + QString st; + get_gif_info(QFile::encodeName( str ).data(), st, true ); + return st; +} + +QPixmap GifSlave::pixmap(const QString& path, int width, int height ) { + static QImage img; + img.load( path ); + if ( img.isNull() ) { + QPixmap pix; + return pix; + } + + return ThumbNailTool::scaleImage( img, width,height ); +} + + diff --git a/noncore/graphics/opie-eye/slave/gif_slave.h b/noncore/graphics/opie-eye/slave/gif_slave.h new file mode 100644 index 0000000..cb8522d --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/gif_slave.h @@ -0,0 +1,22 @@ +/* + * GPLv2 zecke@handhelds.org + */ + + +#ifndef SLAVE_GIF_IMPL_H +#define SLAVE_GIF_IMPL_H + +#include "slaveiface.h" + +class GifSlave : public SlaveInterface { +public: + GifSlave(); + ~GifSlave(); + + QString iconViewName( const QString& ); + QString fullImageInfo( const QString& ); + QPixmap pixmap( const QString&, int width, int height ); +}; + + +#endif diff --git a/noncore/graphics/opie-eye/slave/jpeg_slave.cpp b/noncore/graphics/opie-eye/slave/jpeg_slave.cpp new file mode 100644 index 0000000..95055fd --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/jpeg_slave.cpp @@ -0,0 +1,1426 @@ +#include "jpeg_slave.h" + +#include "thumbnailtool.h" + +PHUNK_VIEW_INTERFACE( "JPEG", JpegSlave ) + +#include <qtopia/timestring.h> +#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; + int ExifImageLength, ExifImageWidth; + int IsColor; + int Process; + int FlashUsed; + float FocalLength; + float ExposureTime; + float ApertureFNumber; + float Distance; + int Whitebalance; + int MeteringMode; + float CCDWidth; + float ExposureBias; + int ExposureProgram; + int ISOequivalent; + int CompressionLevel; + QString UserComment; + QString Comment; + QImage Thumbnail; + + 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 ); } +}; + + + +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"}, + { M_SOF14, "Differential progressive, arithmetic coding"}, + { M_SOF15, "Differential lossless, arithmetic coding"}, + { 0, "Unknown"} +}; + + + +//-------------------------------------------------------------------------- +// Describes format descriptor +static int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8}; +#define NUM_FORMATS 12 + +#define FMT_BYTE 1 +#define FMT_STRING 2 +#define FMT_USHORT 3 +#define FMT_ULONG 4 +#define FMT_URATIONAL 5 +#define FMT_SBYTE 6 +#define FMT_UNDEFINED 7 +#define FMT_SSHORT 8 +#define FMT_SLONG 9 +#define FMT_SRATIONAL 10 +#define FMT_SINGLE 11 +#define FMT_DOUBLE 12 + +//-------------------------------------------------------------------------- +// Describes tag values + +#define TAG_EXIF_OFFSET 0x8769 +#define TAG_INTEROP_OFFSET 0xa005 + +#define TAG_MAKE 0x010F +#define TAG_MODEL 0x0110 +#define TAG_ORIENTATION 0x0112 + +#define TAG_EXPOSURETIME 0x829A +#define TAG_FNUMBER 0x829D + +#define TAG_SHUTTERSPEED 0x9201 +#define TAG_APERTURE 0x9202 +#define TAG_MAXAPERTURE 0x9205 +#define TAG_FOCALLENGTH 0x920A + +#define TAG_DATETIME_ORIGINAL 0x9003 +#define TAG_USERCOMMENT 0x9286 + +#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" ); + 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!" ); + 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; + } + } + return true; +} + + +//-------------------------------------------------------------------------- +// Discard read data. +//-------------------------------------------------------------------------- +void ExifData::DiscardData(void) +{ + for (int a=0; a < SectionsRead; a++) + free(Sections[a].Data); + SectionsRead = 0; +} + +//-------------------------------------------------------------------------- +// Convert a 16 bit unsigned value from file's native byte order +//-------------------------------------------------------------------------- +int ExifData::Get16u(void * Short) +{ + if (MotorolaOrder){ + return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; + }else{ + return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0]; + } +} + +//-------------------------------------------------------------------------- +// Convert a 32 bit signed value from file's native byte order +//-------------------------------------------------------------------------- +int ExifData::Get32s(void * Long) +{ + if (MotorolaOrder){ + return ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16) + | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 ); + }else{ + return ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16) + | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 ); + } +} + +//-------------------------------------------------------------------------- +// Convert a 32 bit unsigned value from file's native byte order +//-------------------------------------------------------------------------- +unsigned ExifData::Get32u(void * Long) +{ + return (unsigned)Get32s(Long) & 0xffffffff; +} + +//-------------------------------------------------------------------------- +// Evaluate number, be it int, rational, or float from directory. +//-------------------------------------------------------------------------- +double ExifData::ConvertAnyFormat(void * ValuePtr, int Format) +{ + double Value; + Value = 0; + + switch(Format){ + case FMT_SBYTE: Value = *(signed char *)ValuePtr; break; + case FMT_BYTE: Value = *(uchar *)ValuePtr; break; + + case FMT_USHORT: Value = Get16u(ValuePtr); break; + + case FMT_ULONG: Value = Get32u(ValuePtr); break; + + case FMT_URATIONAL: + case FMT_SRATIONAL: + { + int Num,Den; + Num = Get32s(ValuePtr); + Den = Get32s(4+(char *)ValuePtr); + if (Den == 0){ + Value = 0; + }else{ + Value = (double)Num/Den; + } + break; + } + + case FMT_SSHORT: Value = (signed short)Get16u(ValuePtr); break; + case FMT_SLONG: Value = Get32s(ValuePtr); break; + + // Not sure if this is correct (never seen float used in Exif format) + case FMT_SINGLE: Value = (double)*(float *)ValuePtr; break; + case FMT_DOUBLE: Value = *(double *)ValuePtr; break; + } + return Value; +} + +//-------------------------------------------------------------------------- +// Process one of the nested EXIF directories. +//-------------------------------------------------------------------------- +void ExifData::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength) +{ + int de; + int a; + int NumDirEntries; + unsigned ThumbnailOffset = 0; + unsigned ThumbnailSize = 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{ + // Note: Files that had thumbnails trimmed with jhead 1.3 or earlier + // might trigger this. + return; + } + } + if (DirEnd < LastExifRefd) LastExifRefd = DirEnd; + } + + for (de=0;de<NumDirEntries;de++){ + int Tag, Format, Components; + unsigned char * ValuePtr; + int ByteCount; + char * DirEntry; + DirEntry = (char *)DIR_ENTRY_ADDR(DirStart, de); + + Tag = Get16u(DirEntry); + Format = Get16u(DirEntry+2); + Components = Get32u(DirEntry+4); + + if ((Format-1) >= NUM_FORMATS) { + // (-1) catches illegal zero case as unsigned underflows to positive large. + return; + } + + 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 + //printf("Offset %d bytes %d ExifLen %d\n",OffsetVal, ByteCount, ExifLength); + + return; + } + ValuePtr = OffsetBase+OffsetVal; + }else{ + // 4 bytes or less and value is in the dir entry itself + ValuePtr = (unsigned char *)DirEntry+8; + } + + if (LastExifRefd < ValuePtr+ByteCount){ + // Keep track of last byte in the exif header that was actually referenced. + // That way, we know where the discardable thumbnail data begins. + LastExifRefd = ValuePtr+ByteCount; + } + + // Extract useful components of tag + switch(Tag){ + + case TAG_MAKE: + ExifData::CameraMake = QString((char*)ValuePtr); + break; + + case TAG_MODEL: + ExifData::CameraModel = QString((char*)ValuePtr); + break; + + case TAG_ORIENTATION: + Orientation = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_DATETIME_ORIGINAL: + DateTime = QString((char*)ValuePtr); + break; + + case TAG_USERCOMMENT: + // Olympus has this 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 + if (memcmp(ValuePtr, "ASCII",5) == 0){ + for (a=5;a<10;a++){ + int c; + c = (ValuePtr)[a]; + if (c != '\0' && c != ' '){ + //strncpy(ImageInfo.Comments, (const char*)(a+ValuePtr), 199); + UserComment.sprintf("%s", (const char*)(a+ValuePtr)); + break; + } + } + }else{ + //strncpy(ImageInfo.Comments, (const char*)ValuePtr, 199); + UserComment.sprintf("%s", (const char*)ValuePtr); + } + break; + + case TAG_FNUMBER: + // Simplest way of expressing aperture, so I trust it the most. + // (overwrite previously computd value if there is one) + ExifData::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 (ExifData::ApertureFNumber == 0){ + ExifData::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. + ExifData::FocalLength = (float)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_SUBJECT_DISTANCE: + // Inidcates the distacne the autofocus camera is focused to. + // Tends to be less accurate as distance increases. + ExifData::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) + ExifData::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 (ExifData::ExposureTime == 0){ + ExifData::ExposureTime + = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2))); + } + break; + + case TAG_FLASH: + if (ConvertAnyFormat(ValuePtr, Format)){ + ExifData::FlashUsed = 1; + } + break; + + case TAG_EXIF_IMAGELENGTH: + ExifImageLength = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_EXIF_IMAGEWIDTH: + ExifImageWidth = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_FOCALPLANEXRES: + FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_FOCALPLANEUNITS: + 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; // milimeter + case 5: FocalplaneUnits = .001; break; // micrometer + } + break; + + // Remaining cases contributed by: Volker C. Schoech (schoech@gmx.de) + + case TAG_EXPOSURE_BIAS: + ExifData::ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_WHITEBALANCE: + ExifData::Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_METERING_MODE: + ExifData::MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_EXPOSURE_PROGRAM: + ExifData::ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format); + break; + + case TAG_ISO_EQUIVALENT: + 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" ); + }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; + + data_precision = Data[2]; + ExifData::Height = Get16m(Data+3); + ExifData::Width = Get16m(Data+5); + num_components = Data[7]; + + if (num_components == 3){ + ExifData::IsColor = 1; + }else{ + ExifData::IsColor = 0; + } + + ExifData::Process = marker; + +} + +//-------------------------------------------------------------------------- +// Get 16 bits motorola order (always) for jpeg header stuff. +//-------------------------------------------------------------------------- +int ExifData::Get16m(const void * Short) +{ + return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; +} + + +//-------------------------------------------------------------------------- +// Process a EXIF marker +// Describes all the drivel that most digital cameras include... +//-------------------------------------------------------------------------- +void ExifData::process_EXIF(unsigned char * CharBuf, unsigned int length) +{ + ExifData::FlashUsed = 0; // If it s from a digicam, and it used flash, it says so. + + FocalplaneXRes = 0; + FocalplaneUnits = 0; + ExifImageWidth = 0; + ExifImageLength = 0; + + { // Check the EXIF header component + static const uchar ExifHeader[] = "Exif\0\0"; + if (memcmp(CharBuf+2, ExifHeader,6)){ + return; + } + } + + if (memcmp(CharBuf+8,"II",2) == 0){ + // printf("Exif section in Intel order\n"); + MotorolaOrder = 0; + }else{ + if (memcmp(CharBuf+8,"MM",2) == 0){ + // printf("Exif section in Motorola order\n"); + MotorolaOrder = 1; + }else{ + return; + } + } + + // Check the next two values for correctness. + if (Get16u(CharBuf+10) != 0x2a + || Get32u(CharBuf+12) != 0x08){ + return; + } + + LastExifRefd = CharBuf; + + // First directory starts 16 bytes in. Offsets start at 8 bytes in. + ProcessExifDir(CharBuf+16, CharBuf+8, length-6); + + // This is how far the interesting (non thumbnail) part of the exif went. + ExifSettingsLength = LastExifRefd - CharBuf; + + // Compute the CCD width, in milimeters. + if (FocalplaneXRes != 0){ + ExifData::CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes); + } +} + +//-------------------------------------------------------------------------- +// Convert exif time to Unix time structure +//-------------------------------------------------------------------------- +int ExifData::Exif2tm(struct tm * timeptr, char * ExifTime) +{ + int a; + + timeptr->tm_wday = -1; + + // Check for format: YYYY:MM:DD HH:MM:SS format. + a = sscanf(ExifTime, "%d:%d:%d %d:%d:%d", + &timeptr->tm_year, &timeptr->tm_mon, &timeptr->tm_mday, + &timeptr->tm_hour, &timeptr->tm_min, &timeptr->tm_sec); + + 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!" ); + 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; + if (!Orientation || Orientation == 1) return Thumbnail; + + // now fix orientation + + QImage dest = Thumbnail; + switch (Orientation) { // notice intentional fallthroughs + case 2: dest = flip_image( dest ); break; + case 4: dest = flip_image( dest ); + case 3: dest =rotate_180( dest ); break; + case 5: dest = flip_image( dest ); + case 6: dest = rotate_90( dest ); break; + case 7: dest = flip_image( dest ); + case 8: dest = rotate_270( dest ); break; + default: break; // should never happen + } + return dest; +} + + +/* + * + */ +static QImage flip_image( const QImage& img ) { + return img.mirror( TRUE, FALSE ); +} + + +static QImage dest; +static int x, y; +static unsigned int *srcData, *destData; // we're not threaded anyway +static unsigned char *srcData8, *destData8; // 8 bit is char +static unsigned int *srcTable, *destTable; // destination table + + +static QImage rotate_90_8( const QImage &img ) { + dest.create(img.height(), img.width(), img.depth()); + dest.setNumColors(img.numColors()); + srcTable = (unsigned int *)img.colorTable(); + destTable = (unsigned int *)dest.colorTable(); + for ( x=0; x < img.numColors(); ++x ) + destTable[x] = srcTable[x]; + for ( y=0; y < img.height(); ++y ){ + srcData8 = (unsigned char *)img.scanLine(y); + for ( x=0; x < img.width(); ++x ){ + destData8 = (unsigned char *)dest.scanLine(x); + destData8[img.height()-y-1] = srcData8[x]; + } + } + return dest; +} + +static QImage rotate_90_all( const QImage& img ) { + dest.create(img.height(), img.width(), img.depth()); + for ( y=0; y < img.height(); ++y ) { + srcData = (unsigned int *)img.scanLine(y); + for ( x=0; x < img.width(); ++x ) { + destData = (unsigned int *)dest.scanLine(x); + destData[img.height()-y-1] = srcData[x]; + } + } + + return dest; +} + + +static QImage rotate_90( const QImage & img ) { + if ( img.depth() > 8) + return rotate_90_all( img ); + else + return rotate_90_8( img ); +} + +static QImage rotate_180_all( const QImage& img ) { + dest.create(img.width(), img.height(), img.depth()); + for ( y=0; y < img.height(); ++y ){ + srcData = (unsigned int *)img.scanLine(y); + destData = (unsigned int *)dest.scanLine(img.height()-y-1); + for ( x=0; x < img.width(); ++x ) + destData[img.width()-x-1] = srcData[x]; + } + return dest; +} + +static QImage rotate_180_8( const QImage& img ) { + dest.create(img.width(), img.height(), img.depth()); + dest.setNumColors(img.numColors()); + srcTable = (unsigned int *)img.colorTable(); + destTable = (unsigned int *)dest.colorTable(); + for ( x=0; x < img.numColors(); ++x ) + destTable[x] = srcTable[x]; + for ( y=0; y < img.height(); ++y ){ + srcData8 = (unsigned char *)img.scanLine(y); + destData8 = (unsigned char *)dest.scanLine(img.height()-y-1); + for ( x=0; x < img.width(); ++x ) + destData8[img.width()-x-1] = srcData8[x]; + } + return dest; +} + +static QImage rotate_180( const QImage& img ) { + if ( img.depth() > 8 ) + return rotate_180_all( img ); + else + return rotate_180_8( img ); +} + + +static QImage rotate_270_8( const QImage& img ) { + dest.create(img.height(), img.width(), img.depth()); + dest.setNumColors(img.numColors()); + srcTable = (unsigned int *)img.colorTable(); + destTable = (unsigned int *)dest.colorTable(); + for ( x=0; x < img.numColors(); ++x ) + destTable[x] = srcTable[x]; + for ( y=0; y < img.height(); ++y ){ + srcData8 = (unsigned char *)img.scanLine(y); + for ( x=0; x < img.width(); ++x ){ + destData8 = (unsigned char *)dest.scanLine(img.width()-x-1); + destData8[y] = srcData8[x]; + } + } + + return dest; +} + +static QImage rotate_270_all( const QImage& img ) { + dest.create(img.height(), img.width(), img.depth()); + for ( y=0; y < img.height(); ++y ){ + srcData = (unsigned int *)img.scanLine(y); + for ( x=0; x < img.width(); ++x ){ + destData = (unsigned int *)dest.scanLine(img.width()-x-1); + destData[y] = srcData[x]; + } + } + return dest; +} + +static QImage rotate_270( const QImage& img ) { + if ( img.depth() > 8 ) + return rotate_270_all( img ); + else + return rotate_270_8( img ); +} + + +static QString color_mode_to_string( bool b ) { + return b ? QObject::tr( "Colormode: Color\n" ) : QObject::tr( "Colormode: Black and white\n" ); +} + +static QString compression_to_string( int level ) { + QString str; + switch( level ) { + case 1: + str = QObject::tr( "Basic" ); + break; + case 2: + str = QObject::tr( "Normal" ); + break; + case 4: + str = QObject::tr( "Fine" ); + break; + default: + str = QObject::tr( "Unknown" ); + + } + return QObject::tr("Quality: %1\n").arg(str); +} + + +static QDateTime parseDateTime( const QString& string ) +{ + QDateTime dt; + if ( string.length() != 19 ) + return dt; + + QString year = string.left( 4 ); + QString month = string.mid( 5, 2 ); + QString day = string.mid( 8, 2 ); + QString hour = string.mid( 11, 2 ); + QString minute = string.mid( 14, 2 ); + QString seconds = string.mid( 18, 2 ); + + bool ok; + bool allOk = true; + int y = year.toInt( &ok ); + allOk &= ok; + + int mo = month.toInt( &ok ); + allOk &= ok; + + int d = day.toInt( &ok ); + allOk &= ok; + + int h = hour.toInt( &ok ); + allOk &= ok; + + int mi = minute.toInt( &ok ); + allOk &= ok; + + int s = seconds.toInt( &ok ); + allOk &= ok; + + if ( allOk ) { + dt.setDate( QDate( y, mo, d ) ); + dt.setTime( QTime( h, mi, s ) ); + } + + return dt; +} + +static QString white_balance_string( int i ) { + QString balance; + switch ( i ) { + case 0: + balance = QObject::tr( "Unknown" ); + break; + case 1: + balance = QObject::tr( "Daylight" ); + break; + case 2: + balance = QObject::tr( "Fluorescent" ); + break; + case 3: + balance = QObject::tr( "Tungsten" ); + break; + case 17: + balance = QObject::tr( "Standard light A" ); + break; + case 18: + balance = QObject::tr( "Standard light B" ); + break; + case 19: + balance = QObject::tr( "Standard light C" ); + break; + case 20: + balance = QObject::tr( "D55" ); + break; + case 21: + balance = QObject::tr( "D65" ); + break; + case 22: + balance = QObject::tr( "D75" ); + break; + case 255: + balance = QObject::tr( "Other" ); + break; + default: + balance = QObject::tr( "Unknown" ); + } + return QObject::tr( "White Balance: %1\n" ).arg( balance ); + +} + + +static QString metering_mode( int i) { + QString meter; + switch( i ) { + case 0: + meter = QObject::tr( "Unknown" ); + break; + case 1: + meter = QObject::tr( "Average" ); + break; + case 2: + meter = QObject::tr( "Center weighted average" ); + break; + case 3: + meter = QObject::tr( "Spot" ); + break; + case 4: + meter = QObject::tr( "MultiSpot" ); + break; + case 5: + meter = QObject::tr( "Pattern" ); + break; + case 6: + meter = QObject::tr( "Partial" ); + break; + case 255: + meter = QObject::tr( "Other" ); + break; + default: + meter = QObject::tr( "Unknown" ); + } + + return QObject::tr( "Metering Mode: %1\n" ).arg( meter ); +} + + +static QString exposure_program( int i ) { + QString exp; + switch( i ) { + case 0: + exp = QObject::tr( "Not defined" ); + break; + case 1: + exp = QObject::tr( "Manual" ); + break; + case 2: + exp = QObject::tr( "Normal progam" ); + break; + case 3: + exp = QObject::tr( "Aperture priority" ); + break; + case 4: + exp = QObject::tr( "Shutter priority" ); + break; + case 5: + exp = QObject::tr( "Creative progam\n(biased toward fast shutter speed" ); + break; + case 6: + exp = QObject::tr( "Action progam\n(biased toward fast shutter speed)" ); + break; + case 7: + exp = QObject::tr( "Portrait mode\n(for closeup photos with the background out of focus)" ); + break; + case 8: + exp = QObject::tr( "Landscape mode\n(for landscape photos with the background in focus)" ); + break; + default: + exp = QObject::tr( "Unknown" ); + } + + return QObject::tr( "Exposure Program: %1\n" ).arg( exp ); +} + +JpegSlave::JpegSlave() + : SlaveInterface( QStringList::split( " ", "jpeg jpg" ) ) +{} + +JpegSlave::~JpegSlave() {} + +QString JpegSlave::iconViewName( const QString& path) { + ExifData ImageInfo; + if ( !ImageInfo.scan( path ) ) + return QString::null; + + QString tag; + tag = QObject::tr( "<qt>Comment: %1\n" ).arg( ImageInfo.getComment() ); + { +// ODP fixme + QString timestring = TimeString::dateString( parseDateTime( ImageInfo.getDateTime() ), FALSE ); + tag += QObject::tr( "Date/Time: %1\n" ).arg( timestring ); + } + tag += QObject::tr( "Dimensions: %1x%2\n" ).arg(ImageInfo.getWidth()) + .arg(ImageInfo.getHeight() ); + + tag += color_mode_to_string( ImageInfo.getIsColor() ); + + tag += compression_to_string( ImageInfo.getCompressionLevel() ); + tag += QObject::tr( "</qt>" ); + + return tag; +} + + +/* + * messy messy string creation + */ +QString JpegSlave::fullImageInfo( const QString& path) { + ExifData ImageInfo; + if ( !ImageInfo.scan( path ) ) + return QString::null; + + QString tag, tmp; + tag = QObject::tr( "Comment: %1\n" ).arg( ImageInfo.getComment() ); + + tmp = ImageInfo.getCameraMake(); + if ( tmp.length() ) + tag += QObject::tr( "Manufacturer: %1\n" ).arg( tmp ); + tmp = ImageInfo.getCameraModel(); + if ( tmp.length() ) + tag += QObject::tr( "Model: %1\n" ).arg( tmp ); + { +// ODP fixme + tmp = TimeString::dateString( parseDateTime( ImageInfo.getDateTime() ), FALSE ); + tag += QObject::tr( "Date/Time: %1\n" ).arg( tmp ); + } + tag += QObject::tr( "Dimensions: %1x%2\n" ).arg(ImageInfo.getWidth()) + .arg(ImageInfo.getHeight() ); + + tag += color_mode_to_string( ImageInfo.getIsColor() ); + + tag += compression_to_string( ImageInfo.getCompressionLevel() ); + if ( ImageInfo.getOrientation() ) + tag += QObject::tr( "Orientation: %1\n" ).arg(ImageInfo.getOrientation() ); + + + { + int flash_used = ImageInfo.getFlashUsed(); + if ( flash_used >= 0 ) + tag += QObject::tr( "Flash used\n" ); + } + + if ( ImageInfo.getFocalLength() ) { + tag += QObject::tr( "Focal length: %1\n" ).arg( QString().sprintf( "%4.1f", ImageInfo.getFocalLength() ) ); + if ( ImageInfo.getCCDWidth() ) + tag += QObject::tr( "35mm equivalent: %1\n" ).arg( (int)(ImageInfo.getFocalLength()/ImageInfo.getCCDWidth()*35 + 0.5) ); + + } + + if ( ImageInfo.getCCDWidth() ) + tag += QObject::tr( "CCD width: %1" ).arg( ImageInfo.getCCDWidth() ); + if ( ImageInfo.getExposureTime() ) { + tmp = QString().sprintf("%4.2f", ImageInfo.getExposureTime() ); + float exposureTime = ImageInfo.getExposureTime(); + if ( exposureTime > 0 && exposureTime <= 0.5 ) + tmp += QString().sprintf(" (1/%d)", (int)(0.5 +1/exposureTime) ); + tag += QObject::tr( "Exposure time: %1\n" ).arg( tmp ); + } + + if ( ImageInfo.getApertureFNumber() ) + tag += QObject::tr( "Aperture: %1\n" ).arg( QString().sprintf("f/%3.1f", (double)ImageInfo.getApertureFNumber() ) ); + + if ( ImageInfo.getDistance() ) { + if ( ImageInfo.getDistance() < 0 ) + tag += QObject::tr( "Distance: %1\n" ).arg( QObject::tr( "Infinite" ) ); + else + tag += QObject::tr( "Distance: %1\n" ).arg( QString().sprintf( "%5.2fm", (double)ImageInfo.getDistance() ) ); + } + + if ( ImageInfo.getExposureBias() ) { + tag += QObject::tr( "Exposure bias: %1\n", QString().sprintf("%4.2f", (double)ImageInfo.getExposureBias() ) ); + } + + if ( ImageInfo.getWhitebalance() != -1 ) + tag += white_balance_string( ImageInfo.getWhitebalance() ); + + + if( ImageInfo.getMeteringMode() != -1 ) + tag += metering_mode( ImageInfo.getMeteringMode() ); + + if ( ImageInfo.getExposureProgram() ) + tag += exposure_program( ImageInfo.getExposureProgram() ); + if ( ImageInfo.getISOequivalent() ) + tag += QObject::tr( "ISO equivalent: %1\n" ).arg( QString().sprintf("%2d", ImageInfo.getISOequivalent() ) ); + + tmp = ImageInfo.getUserComment(); + if ( tmp.length() ) + tag += QObject::tr( "EXIF comment: %1" ).arg( tmp ); + + tag += QObject::tr( "</qt>" ); + + + + return tag; +} + +QPixmap JpegSlave::pixmap( const QString& path, int wid, int hei) { + ExifData ImageInfo; + if ( !ImageInfo.scan( path ) || ImageInfo.isNullThumbnail() ) { + QImage img; + QImageIO iio( path, 0l ); + QString str = QString( "Fast Shrink( 4 ) Scale( %1, %2, ScaleFree)" ).arg( wid ).arg( hei ); + iio.setParameters( str.latin1() );// will be strdupped anyway + img = iio.read() ? iio.image() : QImage(); + return ThumbNailTool::scaleImage( img, wid,hei ); + }else{ + QImage img = ImageInfo.getThumbnail(); + return ThumbNailTool::scaleImage( img, wid,hei ); + } +} diff --git a/noncore/graphics/opie-eye/slave/jpeg_slave.h b/noncore/graphics/opie-eye/slave/jpeg_slave.h new file mode 100644 index 0000000..e800dbd --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/jpeg_slave.h @@ -0,0 +1,19 @@ +/* + * GPLv2 zecke@handhelds.org + */ +#ifndef JPEG_SLAVE_IMPL_H +#define JPEG_SLAVE_IMPL_H + +#include "slaveiface.h" + +class JpegSlave : public SlaveInterface { +public: + JpegSlave(); + ~JpegSlave(); + + QString iconViewName( const QString& ); + QString fullImageInfo( const QString& ); + QPixmap pixmap( const QString&, int, int ); +}; + +#endif diff --git a/noncore/graphics/opie-eye/slave/main.cpp b/noncore/graphics/opie-eye/slave/main.cpp new file mode 100644 index 0000000..37020e6 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/main.cpp @@ -0,0 +1,59 @@ +/* + * GPLv2 Slave Main + */ + +#include "gif_slave.h" +#include "png_slave.h" +#include "jpeg_slave.h" +#include "thumbnailtool.h" +#include "slavereciever.h" + +#include <qpixmap.h> +#include <qcopchannel_qws.h> + +#include <qtopia/qpeapplication.h> + +int main( int argc, char* argv[] ) { + QPEApplication app( argc, argv ); + SlaveReciever rec( 0 ); + + QCopChannel chan( "QPE/opie-eye_slave" ); + QObject::connect(&chan,SIGNAL(received(const QCString&, const QByteArray&)), + &rec, SLOT(recieveAnswer(const QCString&,const QByteArray&))); + QObject::connect(qApp,SIGNAL(appMessage(const QCString&, const QByteArray&)), + &rec, SLOT(recieveAnswer(const QCString&,const QByteArray&))); + + return app.exec(); +} + +#ifdef DEBUG_IT +int main( int argc, char* argv[] ) { + QString str = QString::fromLatin1(argv[2] ); + QApplication app( argc, argv ); + GifSlave slave; + qWarning( str +" "+slave.iconViewName(str ) ); + qWarning( str+" "+slave.fullImageInfo( str ) ); + + PNGSlave pngslave; + qWarning( str + " " + pngslave.iconViewName(str) ); + qWarning( str + " " + pngslave.fullImageInfo(str)); + + + JpegSlave jpgslave; + qWarning( str + " " + jpgslave.iconViewName(str ) ); + qWarning( str + " " + jpgslave.fullImageInfo( str ) ); +//return app.exec(); + QPixmap pix = ThumbNailTool::getThumb( str, 24, 24 ); + if ( pix.isNull() ) { + qWarning( "No Thumbnail" ); + pix = slave.pixmap(str, 24, 24); + } + + if (!pix.isNull() ) { + qWarning( "Saving Thumbnail" ); + ThumbNailTool::putThumb( str, pix, 24, 24 ); + } + +} + +#endif diff --git a/noncore/graphics/opie-eye/slave/png_slave.cpp b/noncore/graphics/opie-eye/slave/png_slave.cpp new file mode 100644 index 0000000..72b93cc --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/png_slave.cpp @@ -0,0 +1,210 @@ +#include "png_slave.h" + +#include "thumbnailtool.h" + +#include <qobject.h> +#include <qfile.h> +#include <qimage.h> +#include <qpixmap.h> +#include <qstring.h> + +/* + * GPLv2 from kfile plugin + */ +PHUNK_VIEW_INTERFACE( "PNG", PNGSlave ); + +#define CHUNK_SIZE(data, index) ((data[index ]<<24) + (data[index+1]<<16) + \ + (data[index+2]<< 8) + data[index+3]) +#define CHUNK_TYPE(data, index) &data[index+4] +#define CHUNK_HEADER_SIZE 12 +#define CHUNK_DATA(data, index, offset) data[8+index+offset] + +/* TRANSLATOR QObject */ + +// known translations for common png keys +static const char* knownTranslations[] +#ifdef __GNUC__ +__attribute__((unused)) +#endif + = { + QT_TR_NOOP("Title"), + QT_TR_NOOP("Author"), + QT_TR_NOOP("Description"), + QT_TR_NOOP("Copyright"), + QT_TR_NOOP("Creation Time"), + QT_TR_NOOP("Software"), + QT_TR_NOOP("Disclaimer"), + QT_TR_NOOP("Warning"), + QT_TR_NOOP("Source"), + QT_TR_NOOP("Comment") +}; + +// and for the colors +static const char* colors[] = { + QT_TR_NOOP("Grayscale"), + QT_TR_NOOP("Unknown"), + QT_TR_NOOP("RGB"), + QT_TR_NOOP("Palette"), + QT_TR_NOOP("Grayscale/Alpha"), + QT_TR_NOOP("Unknown"), + QT_TR_NOOP("RGB/Alpha") +}; + + // and compressions +static const char* compressions[] = +{ + QT_TR_NOOP("Deflate") +}; + + // interlaced modes +static const char* interlaceModes[] = { + QT_TR_NOOP("None"), + QT_TR_NOOP("Adam7") +}; + + +static void read_comment( const QString& inf, + bool readComments, QString& str ) { + QFile f(inf); + f.open(IO_ReadOnly); + + if (f.size() < 26) return; + // the technical group will be read from the first 26 bytes. If the file + // is smaller, we can't even read this. + + uchar *data = new uchar[f.size()+1]; + f.readBlock(reinterpret_cast<char*>(data), f.size()); + data[f.size()]='\n'; + + // find the start + if (data[0] == 137 && data[1] == 80 && data[2] == 78 && data[3] == 71 && + data[4] == 13 && data[5] == 10 && data[6] == 26 && data[7] == 10 ) + { + // ok + // the IHDR chunk should be the first + if (!strncmp((char*)&data[12], "IHDR", 4)) + { + // we found it, get the dimensions + ulong x,y; + x = (data[16]<<24) + (data[17]<<16) + (data[18]<<8) + data[19]; + y = (data[20]<<24) + (data[21]<<16) + (data[22]<<8) + data[23]; + + uint type = data[25]; + uint bpp = data[24]; + + // the bpp are only per channel, so we need to multiply the with + // the channel count + switch (type) + { + case 0: break; // Grayscale + case 2: bpp *= 3; break; // RGB + case 3: break; // palette + case 4: bpp *= 2; break; // grayscale w. alpha + case 6: bpp *= 4; break; // RGBA + + default: // we don't get any sensible value here + bpp = 0; + } + + + str = QObject::tr("Dimensions: %1x%2\n" ).arg(x).arg(y); + str += QObject::tr("Depth: %1\n" ).arg(bpp); + str += QObject::tr("ColorMode: %1\n").arg( + (type < sizeof(colors)/sizeof(colors[0])) + ? QObject::tr(colors[data[25]]) : QObject::tr("Unknown") ); + + str += QObject::tr("Compression: %1\n").arg( + (data[26] < sizeof(compressions)/sizeof(compressions[0])) + ? QObject::tr(compressions[data[26]]) : QObject::tr("Unknown") ); + + str += QObject::tr("InterlaceMode: %1\n" ).arg( + (data[28] < sizeof(interlaceModes)/sizeof(interlaceModes[0])) + ? QObject::tr(interlaceModes[data[28]]) : QObject::tr("Unknown")); + } + + if ( readComments ) { + uint index = 8; + index += CHUNK_SIZE(data, index) + CHUNK_HEADER_SIZE; + + while(index<f.size()-12) + { + while (index < f.size() - 12 && + strncmp((char*)CHUNK_TYPE(data,index), "tEXt", 4)) + { + if (!strncmp((char*)CHUNK_TYPE(data,index), "IEND", 4)) + goto end; + + index += CHUNK_SIZE(data, index) + CHUNK_HEADER_SIZE; + } + + if (index < f.size() - 12) + { + // we found a tEXt field + // get the key, it's a null terminated string at the + // chunk start + + uchar* key = &CHUNK_DATA(data,index,0); + + int keysize=0; + for (;key[keysize]!=0; keysize++) + // look if we reached the end of the file + // (it might be corrupted) + if (8+index+keysize>=f.size()) + goto end; + + // the text comes after the key, but isn't null terminated + uchar* text = &CHUNK_DATA(data,index, keysize+1); + uint textsize = CHUNK_SIZE(data, index)-keysize-1; + + // security check, also considering overflow wraparound from the addition -- + // we may endup with a /smaller/ index if we wrap all the way around + uint firstIndex = (uint)(text - data); + uint onePastLastIndex = firstIndex + textsize; + + if ( onePastLastIndex > f.size() || onePastLastIndex <= firstIndex) + goto end; + + QByteArray arr(textsize); + arr = QByteArray(textsize).duplicate((const char*)text, + textsize); + str += QObject::tr( + QString(reinterpret_cast<char*>(key)), + QString(arr) ); + + index += CHUNK_SIZE(data, index) + CHUNK_HEADER_SIZE; + } + } + } + } +end: + delete[] data; + +} + + +PNGSlave::PNGSlave() + : SlaveInterface("png") +{ +} +PNGSlave::~PNGSlave() { +} +QString PNGSlave::iconViewName( const QString& path) { + QString str; + read_comment( path, false, str ); + return str; +} + +QString PNGSlave::fullImageInfo( const QString& path) { + QString str; + read_comment( path, true, str ); + return str; +} + + +QPixmap PNGSlave::pixmap( const QString& path, int width, int height) { + QImage img; img.load( path ); + if ( img.isNull() ) + return QPixmap(); + else + return ThumbNailTool::scaleImage( img, width,height ); +} diff --git a/noncore/graphics/opie-eye/slave/png_slave.h b/noncore/graphics/opie-eye/slave/png_slave.h new file mode 100644 index 0000000..408bfc4 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/png_slave.h @@ -0,0 +1,21 @@ +/* + * GPLv2 zecke@handhelds.org + */ +#ifndef PNG_SLAVE_IMPL_H +#define PNG_SLAVE_IMPL_H + +#include "slaveiface.h" + +class QString; +class QPixmap; +class PNGSlave : public SlaveInterface { +public: + PNGSlave(); + ~PNGSlave(); + + QString iconViewName( const QString& ); + QString fullImageInfo( const QString& ); + QPixmap pixmap( const QString&, int, int ); +}; + +#endif diff --git a/noncore/graphics/opie-eye/slave/slave.pro b/noncore/graphics/opie-eye/slave/slave.pro new file mode 100644 index 0000000..3f42495 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/slave.pro @@ -0,0 +1,18 @@ +CONFIG += qte +TEMPLATE = app +TARGET = opie-eye_slave +DESTDIR = $(OPIEDIR)/bin + +HEADERS = gif_slave.h slaveiface.h slavereciever.h \ + thumbnailtool.h png_slave.h jpeg_slave.h \ + ../iface/slaveiface.h +SOURCES = main.cpp gif_slave.cpp slavereciever.cpp \ + slaveiface.cpp thumbnailtool.cpp png_slave.cpp \ + jpeg_slave.cpp + +INCLUDEPATH += $(OPIEDIR)/include ../ +DEPENDSPATH += $(OPIEDIR)/include + +LIBS += -lqpe + +include ( $(OPIEDIR)/include.pro )
\ No newline at end of file diff --git a/noncore/graphics/opie-eye/slave/slaveiface.cpp b/noncore/graphics/opie-eye/slave/slaveiface.cpp new file mode 100644 index 0000000..170f7d5 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/slaveiface.cpp @@ -0,0 +1,26 @@ +/* + * GPLv2 zecke@handhelds.org + */ + +#include "slaveiface.h" + +static SlaveMap* _slaveMap = 0; +SlaveMap* slaveMap() { + if ( !_slaveMap ) + _slaveMap = new SlaveMap; + return _slaveMap; +} + +SlaveInterface::SlaveInterface( const QStringList& image ) + : m_list( image ) +{ + +} + +SlaveInterface::~SlaveInterface() { + +} + +QStringList SlaveInterface::imageFormats()const { + return m_list; +} diff --git a/noncore/graphics/opie-eye/slave/slaveiface.h b/noncore/graphics/opie-eye/slave/slaveiface.h new file mode 100644 index 0000000..18656c5 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/slaveiface.h @@ -0,0 +1,53 @@ +/* + * GPLv2 + */ + +#ifndef P_SLAVE_INTER_FACE_H +#define P_SLAVE_INTER_FACE_H + +#include <qfileinfo.h> +#include <qstringlist.h> +#include <qmap.h> + +/** + * @short The slave worker Interface for generating Preview + Image Info + Full + * IMage Info + */ +class QPixmap; +class SlaveInterface { +public: + SlaveInterface(const QStringList& imageformats); + virtual ~SlaveInterface(); + + QStringList imageFormats()const; + bool supports( const QString& )const; + virtual QString iconViewName(const QString&) = 0; + virtual QString fullImageInfo(const QString& )= 0; + virtual QPixmap pixmap( const QString&, int width, int height ) = 0; +private: + QStringList m_list; +}; + +inline bool SlaveInterface::supports( const QString& str)const { + return m_list.contains( QFileInfo( str ).extension(false) ); +} + +typedef SlaveInterface* (*phunkSlaveCreateFunc )(); +typedef QMap<QString,phunkSlaveCreateFunc> SlaveMap; + +typedef QMap<QString, SlaveInterface*> SlaveObjects; + +SlaveMap* slaveMap(); +SlaveObjects* slaveObjects(); + + + +#define PHUNK_VIEW_INTERFACE( NAME, IMPL ) \ + static SlaveInterface *create_ ## IMPL() { \ + return new IMPL(); \ + } \ + static SlaveMap::Iterator dummy_ ## IMPL = slaveMap()->insert( NAME, create_ ## IMPL ); + + + +#endif diff --git a/noncore/graphics/opie-eye/slave/slavereciever.cpp b/noncore/graphics/opie-eye/slave/slavereciever.cpp new file mode 100644 index 0000000..951f3df --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/slavereciever.cpp @@ -0,0 +1,214 @@ +/* + * GPLv2 zecke@handhelds.org + */ + +#include "slavereciever.h" +#include "slaveiface.h" + +#include <qpe/qcopenvelope_qws.h> +#include <qpe/qpeapplication.h> + +#include <qtimer.h> + +static SlaveObjects* _slaveObj = 0; + +QDataStream & operator << (QDataStream & str, bool b) +{ + str << Q_INT8(b); + return str; +} + +QDataStream & operator >> (QDataStream & str, bool & b) +{ + Q_INT8 l; + str >> l; + b = bool(l); + return str; +} + + + +QDataStream &operator<<( QDataStream& s, const PixmapInfo& inf) { + return s << inf.file << inf.pixmap << inf.width << inf.height; +} +QDataStream &operator>>( QDataStream& s, PixmapInfo& inf ) { + s >> inf.file >> inf.pixmap >> inf.width >> inf.height; + return s; +} +QDataStream &operator<<( QDataStream& s, const ImageInfo& i) { + return s << i.kind << i.file << i.info; +} +QDataStream &operator>>( QDataStream& s, ImageInfo& i ) { + s >> i.kind >> i.file >> i.info; + return s; +} + + + +SlaveObjects* slaveObjects() { + if ( !_slaveObj ) + _slaveObj = new SlaveObjects; + return _slaveObj; +} + +SlaveReciever::SlaveReciever( QObject* par) + : QObject( par ) +{ + m_inf = new QTimer(this); + connect(m_inf,SIGNAL(timeout()), + this, SLOT(slotImageInfo())); + m_pix = new QTimer(this); + connect(m_pix,SIGNAL(timeout()), + this, SLOT(slotThumbNail())); + + m_out = new QTimer(this); + connect(m_out,SIGNAL(timeout()), + this, SLOT(slotSend())); + + SlaveObjects *obj = slaveObjects(); // won't be changed + SlaveMap::Iterator it; + SlaveMap* map = slaveMap(); // SlaveMap won't be changed during execution!!! + for(it = map->begin(); it != map->end(); ++it ) { + obj->insert( it.key(), (*it.data())() ); + } +} + +SlaveReciever::~SlaveReciever() { +} + +void SlaveReciever::recieveAnswer( const QCString& string, const QByteArray& ar) { + qWarning( "String is %s", string.data() ); + QDataStream stream(ar, IO_ReadOnly ); + QStringList lst; + static ImageInfo inf; + static PixmapInfo pix; + + if ( string == "thumbInfo(QString)" ) { + stream >> inf.file; + m_inList.append(inf); + }else if ( string == "thumbInfos(QStringList)" ) { + stream >> lst; + for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) { + qWarning( "Adding thumbinfo for file "+ *it ); + inf.file = (*it); + m_inList.append(inf); + } + }else if ( string == "fullInfo(QString)" ) { + inf.kind = true; + stream >> inf.file; + m_inList.append(inf); + }else if ( string == "fullInfos(QStringList)" ) { + stream >> lst; + for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) { + qWarning( "Adding fullInfo for"+ *it ); + inf.file = (*it); + inf.kind = true; + m_inList.append(inf); + } + }else if ( string == "pixmapInfo(QString,int,int)" ) { + stream >> pix.file >> pix.width >> pix.height; + m_inPix.append(pix); + }else if ( string == "pixmapInfos(PixmapInfos)" ) { + PixmapList list; + stream >> list; + for(PixmapList::Iterator it = list.begin(); it != list.end(); ++it ) { + qWarning( "Got %d %d " + (*it).file, (*it).width , (*it).height ); + m_inPix.append(*it); + } + } + + if (!m_inf->isActive() && !m_inList.isEmpty() ) + m_inf->start(5); + + if (!m_pix->isActive() && !m_inPix.isEmpty() ) + m_pix->start(5); + + QPEApplication::setKeepRunning(); + +} + +PixmapList SlaveReciever::outPix()const { + return m_outPix; +} + +StringList SlaveReciever::outInf()const{ + return m_outList; +} + +void SlaveReciever::slotImageInfo() { + ImageInfo inf = m_inList.first(); + m_inList.remove( inf ); + + static SlaveObjects::Iterator it; + static SlaveObjects* map = slaveObjects(); // SlaveMap won't be changed during execution!!! + for(it = map->begin(); it != map->end(); ++it ) { + if( (*it)->supports(inf.file ) ) { + /* full image info */ + if (inf.kind ) + inf.info = (*it)->fullImageInfo( inf.file ); + else + inf.info = (*it)->iconViewName( inf.file ); + m_outList.append( inf ); + break; + } + } + + if (m_inList.isEmpty() ) + m_inf->stop(); + if (!m_out->isActive() && !m_outList.isEmpty() ) + m_out->start( 100 ); +} + +void SlaveReciever::slotThumbNail() { + PixmapInfo inf = m_inPix.first(); + m_inPix.remove( inf ); + + static SlaveObjects::Iterator it; + static SlaveObjects* map = slaveObjects(); // SlaveMap won't be changed during execution!!! + for(it = map->begin(); it != map->end(); ++it ) { + SlaveInterface* iface = it.data(); + if( iface->supports(inf.file ) ) { + /* pixmap */ + qWarning( "Asking for thumbNail in size %d %d for "+inf.file, inf.width, inf.height ); + inf.pixmap = iface->pixmap(inf.file, 64, 64); + m_outPix.append( inf ); + break; + } + } + + + + if(m_inPix.isEmpty() ) + m_pix->stop(); + if(!m_out->isActive() && !m_outPix.isEmpty() ) + m_out->start(100); +} + +void SlaveReciever::slotSend() { + + m_out->stop(); + + qWarning( "Sending %d %d", outPix().count(), outInf().count() ); + /* queue it and send */ + /* if this ever gets a service introduce request queues + * so we can differinatate between different clients + */ + if (! m_outPix.isEmpty() ) { + QCopEnvelope answer("QPE/opie-eye", "pixmapsHandled(PixmapList)" ); + answer << outPix(); + for ( PixmapList::Iterator it = m_outPix.begin();it!=m_outPix.end();++it ) { + qWarning( "Sending out %s %d %d", (*it).file.latin1(), (*it).width, (*it).height ); + } + } + if ( !m_outList.isEmpty() ) { + QCopEnvelope answer("QPE/opie-eye", "pixmapsHandled(StringList)" ); + answer << outInf(); + for ( StringList::Iterator it = m_outList.begin();it!=m_outList.end();++it ) { + qWarning( "Sending out2 " + (*it).file ); + } + } + + m_outList.clear(); + m_outPix.clear(); +} + diff --git a/noncore/graphics/opie-eye/slave/slavereciever.h b/noncore/graphics/opie-eye/slave/slavereciever.h new file mode 100644 index 0000000..214bfc6 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/slavereciever.h @@ -0,0 +1,58 @@ +/* + * GPLv2 + */ + + +#ifndef SLAVE_RECEIVER_H +#define SLAVE_RECEIVER_H + +/** + * Receive Requests + */ + +#include <iface/slaveiface.h> + +#include <qobject.h> +#include <qdatastream.h> +#include <qstringlist.h> +#include <qvaluelist.h> +#include <qpixmap.h> + + + +typedef QValueList<PixmapInfo> PixmapList; +typedef QValueList<ImageInfo> StringList; + +class QTimer; +class QSocket; +class SlaveReciever : public QObject { + Q_OBJECT + + friend QDataStream &operator<<( QDataStream&, const PixmapInfo& ); + friend QDataStream &operator>>( QDataStream&, PixmapInfo& ); + friend QDataStream &operator<<( QDataStream&, const ImageInfo& ); + friend QDataStream &operator>>( QDataStream&, ImageInfo ); +public: + + enum Job { ImageInfoJob, FullImageInfoJob, ThumbNailJob }; + SlaveReciever( QObject* parent ); + ~SlaveReciever(); + +public slots: + void recieveAnswer( const QCString&, const QByteArray& ); +public: + PixmapList outPix()const; + StringList outInf()const; + +private slots: + void slotSend(); + void slotImageInfo(); + void slotThumbNail(); +private: + QTimer *m_inf, *m_pix, *m_out; + StringList m_inList, m_outList; + PixmapList m_inPix, m_outPix; +}; + + +#endif diff --git a/noncore/graphics/opie-eye/slave/thumbnailtool.cpp b/noncore/graphics/opie-eye/slave/thumbnailtool.cpp new file mode 100644 index 0000000..a202457 --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/thumbnailtool.cpp @@ -0,0 +1,61 @@ +#include "thumbnailtool.h" + +#include <qfileinfo.h> +#include <qdir.h> +#include <qimage.h> +#include <qpixmap.h> +#include <qstring.h> + +static bool makeThumbDir( const QFileInfo& inf, bool make = false) { + QDir dir( inf.dirPath()+ "/.opie-eye" ); + if ( !dir.exists() ) + if ( make ) + return dir.mkdir(QString::null); + else + return false; + return true; +} + + +/* + * check if the Opie opie-eye dir exists + * check if a thumbnail exists + * load the thumbnail + * /foo/bar/imagefoo.gif + * check for a png in /foo/bar/.opie-eye/%dx%d-imagefoo.gif + */ +QPixmap ThumbNailTool::getThumb( const QString& path, int width, int height ) { + QFileInfo inf( path ); + qWarning( "Get Thumb" ); + if ( !makeThumbDir( inf ) ) { + QPixmap pix; + return pix; + } + QString str = QString( "/.opie-eye/%1x%2-%3" ).arg( width ).arg( height ).arg( inf.fileName() ); + qWarning( inf.dirPath()+str ); + return QPixmap( inf.dirPath()+str,"PNG" ); + +} + +void ThumbNailTool::putThumb( const QString& path, const QPixmap& pix, int width, int height ) { + QFileInfo inf( path ); + makeThumbDir( inf, true ); + QString str = QString( "/.opie-eye/%1x%2-%3" ).arg( width ).arg( height ).arg( inf.fileName() ); + qWarning( inf.dirPath()+str ); + pix.save( inf.dirPath()+str, "PNG" ); +} + + +QPixmap ThumbNailTool::scaleImage( QImage& img, int w, int h ) { + double hs = (double)h / (double)img.height() ; + double ws = (double)w / (double)img.width() ; + double scaleFactor = (hs > ws) ? ws : hs; + int smoothW = (int)(scaleFactor * img.width()); + int smoothH = (int)(scaleFactor * img.height()); + QPixmap pixmap; + if ( img.width() <= w && img.height() <= h ) + pixmap.convertFromImage( img ); + else + pixmap.convertFromImage( img.smoothScale( smoothW, smoothH) ); + return pixmap; +} diff --git a/noncore/graphics/opie-eye/slave/thumbnailtool.h b/noncore/graphics/opie-eye/slave/thumbnailtool.h new file mode 100644 index 0000000..4d0a30f --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/thumbnailtool.h @@ -0,0 +1,19 @@ +/* + * GPLv2 + */ + +#ifndef THUMB_NAIL_TOOL_H +#define THUMB_NAIL_TOOL_H +class QString; +class QPixmap; +class QImage; + +struct ThumbNailTool { + static QPixmap scaleImage( QImage&, int width, int height ); +/* get one isInvalid() if non found */ + static QPixmap getThumb( const QString&, int width, int height ); +/* put one */ + static void putThumb( const QString&, const QPixmap&, int width, int heigh ); +}; + +#endif |