summaryrefslogtreecommitdiff
path: root/noncore/graphics/opie-eye/slave
authorzecke <zecke>2004-03-22 23:32:41 (UTC)
committer zecke <zecke>2004-03-22 23:32:41 (UTC)
commit428b687982966dc2efabaf6dbcc55ad0ea30aa10 (patch) (side-by-side diff)
tree86da20abd2e4b97a59dc32e17996bde5ee74cc91 /noncore/graphics/opie-eye/slave
parent7ce623c6351646ce738a81e103632d73c5454ecc (diff)
downloadopie-428b687982966dc2efabaf6dbcc55ad0ea30aa10.zip
opie-428b687982966dc2efabaf6dbcc55ad0ea30aa10.tar.gz
opie-428b687982966dc2efabaf6dbcc55ad0ea30aa10.tar.bz2
Initial Check in of the Eye Of Zilla. This ImageViewer features
Image Infos, EXIF, Jpeg,Png,Gif support. It supports scaled loading of Jpegs. an smart image cache.... GUI needs some work and we need to find a bug in QCOP as well. TODO: Add Image Service for example Mailer Add ImageCanvas/Zoomer/Display
Diffstat (limited to 'noncore/graphics/opie-eye/slave') (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/graphics/opie-eye/slave/gif_slave.cpp305
-rw-r--r--noncore/graphics/opie-eye/slave/gif_slave.h22
-rw-r--r--noncore/graphics/opie-eye/slave/jpeg_slave.cpp1426
-rw-r--r--noncore/graphics/opie-eye/slave/jpeg_slave.h19
-rw-r--r--noncore/graphics/opie-eye/slave/main.cpp59
-rw-r--r--noncore/graphics/opie-eye/slave/png_slave.cpp210
-rw-r--r--noncore/graphics/opie-eye/slave/png_slave.h21
-rw-r--r--noncore/graphics/opie-eye/slave/slave.pro18
-rw-r--r--noncore/graphics/opie-eye/slave/slaveiface.cpp26
-rw-r--r--noncore/graphics/opie-eye/slave/slaveiface.h53
-rw-r--r--noncore/graphics/opie-eye/slave/slavereciever.cpp214
-rw-r--r--noncore/graphics/opie-eye/slave/slavereciever.h58
-rw-r--r--noncore/graphics/opie-eye/slave/thumbnailtool.cpp61
-rw-r--r--noncore/graphics/opie-eye/slave/thumbnailtool.h19
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