summaryrefslogtreecommitdiff
path: root/noncore/graphics/opie-eye/slave
Unidiff
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 @@
1#include "gif_slave.h"
2
3#include "thumbnailtool.h"
4
5#include <qimage.h>
6#include <qobject.h>
7#include <qfile.h>
8#include <qpixmap.h>
9
10
11PHUNK_VIEW_INTERFACE( "Gif", GifSlave );
12
13
14namespace {
15/*
16** $Id$
17**
18** Minimal GIF parser, for use in extracting and setting metadata.
19** Modified for standalone & KDE calling by Bryce Nesbitt
20**
21** TODO:
22** Support gif comments that span more than one comment block.
23** Verify that Unicode utf-8 is fully unmolested by this code.
24** Implement gif structure verifier.
25**
26** Based on: GIFtrans v1.12.2
27** Copyright (C) 24.2.94 by Andreas Ley <ley@rz.uni-karlsruhe.de>
28**
29*******************************************************************************
30**
31** Original distribution site is
32** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.c
33** A man-page by knordlun@fltxa.helsinki.fi (Kai Nordlund) is at
34** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.1
35** An online version by taylor@intuitive.com (Dave Taylor) is at
36** http://www.intuitive.com/coolweb/Addons/giftrans-doc.html
37** To compile for MS-DOS or OS/2, you need getopt:
38** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/getopt.c
39** MS-DOS executable can be found at
40** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.exe
41** OS/2 executable can be found at
42** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/giftrans.os2.exe
43** A template rgb.txt for use with the MS-DOS version can be found at
44** ftp://ftp.rz.uni-karlsruhe.de/pub/net/www/tools/giftrans/rgb.txt
45** Additional info can be found on
46** http://melmac.corp.harris.com/transparent_images.html
47**
48** The GIF file format is documented in
49** ftp://ftp.uu.net/doc/literary/obi/Standards/Graphics/Formats/gif89a.doc.Z
50** A good quick reference is at:
51** http://www.goice.co.jp/member/mo/formats/gif.html
52**
53*******************************************************************************
54**
55** This program is free software; you can redistribute it and/or modify
56** it under the terms of the GNU General Public License as published by
57** the Free Software Foundation; either version 2 of the License, or
58** (at your option) any later version.
59**
60** This program is distributed in the hope that it will be useful,
61** but WITHOUT ANY WARRANTY; without even the implied warranty of
62** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
63** GNU General Public License for more details.
64**
65** You should have received a copy of the GNU General Public License
66** along with this program; if not, write to the Free Software
67** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
68**
69*/
70#define SUCCESS (0)
71#define FAILURE (1)
72
73#define READ_BINARY "r"
74#define WRITE_BINARY "w"
75
76
77static long int pos;
78static char skipcomment,verbose;
79char *global_comment;
80
81#define readword(buffer) ((buffer)[0]+256*(buffer)[1])
82#define readflag(buffer) ((buffer)?true:false)
83#define hex(c) ('a'<=(c)&&(c)<='z'?(c)-'a'+10:'A'<=(c)&&(c)<='Z'?(c)-'A'+10:(c)-'0')
84
85static bool debug = false;
86static bool output= false;
87
88void dump(long int, unsigned char *, size_t) {
89}
90
91void skipdata(FILE *src)
92{
93 unsigned char size,buffer[256];
94
95 do {
96 pos=ftell(src);
97 (void)fread((void *)&size,1,1,src);
98 if (debug)
99 dump(pos,&size,1);
100 if (debug) {
101 pos=ftell(src);
102 (void)fread((void *)buffer,(size_t)size,1,src);
103 dump(pos,buffer,(size_t)size);
104 }
105 else
106 (void)fseek(src,(long int)size,SEEK_CUR);
107 } while (!feof(src)&&size>0);
108}
109
110
111void transdata(FILE *src, FILE *dest)
112{
113 unsigned char size,buffer[256];
114
115 do {
116 pos=ftell(src);
117 (void)fread((void *)&size,1,1,src);
118 pos=ftell(src);
119 (void)fread((void *)buffer,(size_t)size,1,src);
120 if (debug)
121 dump(pos,buffer,(size_t)size);
122 if (output)
123 (void)fwrite((void *)buffer,(size_t)size,1,dest);
124 } while (!feof(src)&&size>0);
125}
126
127void transblock(FILE *src, FILE* dest)
128{
129 unsigned char size,buffer[256];
130
131 pos=ftell(src);
132 (void)fread((void *)&size,1,1,src);
133 if (debug)
134 dump(pos,&size,1);
135 if (output)
136 (void)fwrite((void *)&size,1,1,dest);
137 pos=ftell(src);
138 (void)fread((void *)buffer,(size_t)size,1,src);
139 if (debug)
140 dump(pos,buffer,(size_t)size);
141 if (output)
142 (void)fwrite((void *)buffer,(size_t)size,1,dest);
143}
144
145void dumpcomment(FILE *src, QCString& str)
146{
147 unsigned char size;
148
149 pos=ftell(src);
150 (void)fread((void *)&size,1,1,src);
151 if (debug)
152 dump(pos,&size,1);
153 str.resize( size+1 );
154 (void)fread((void *)str.data(),size,1,src);
155 (void)fseek(src,(long int)pos,SEEK_SET);
156}
157
158
159
160
161int giftrans(FILE *src, FILE* dest, QString& str, bool full)
162{
163 unsigned char buffer[3*256],lsd[7],gct[3*256];
164 unsigned int size,gct_size;
165
166 /* Header */
167 pos=ftell(src);
168 (void)fread((void *)buffer,6,1,src);
169 if (strncmp((char *)buffer,"GIF",3)) {
170 str = QObject::tr("Not a GIF file");
171 (void)fprintf(stderr,"Not GIF file!\n");
172 return(1);
173 }
174
175 /* Logical Screen Descriptor */
176 pos=ftell(src);
177 (void)fread((void *)lsd,7,1,src);
178 //(void)fprintf(stderr,"Logical Screen Descriptor:\n");
179 str += QObject::tr("Dimensions: %1x%2\n").arg( readword(lsd) ).arg( readword(lsd+2 ) );
180 //(void)fprintf(stderr,"Global Color Table Flag: %s\n",readflag(lsd[4]&0x80));
181 str += QObject::tr("Depth: %1 bits\n").arg( (lsd[4]&0x70>>4 )+1);
182 //(void)fprintf(stderr,"Depth : %d bits\n",(lsd[4]&0x70>>4)+1);
183 if (lsd[4]&0x80 && full) {
184 str += QObject::tr("Sort Flag: %1\n" ).arg(readflag(lsd[4]&0x8) );
185 str += QObject::tr("Size of Global Color Table: %1 colors\n" ).arg( 2<<(lsd[4]&0x7));
186 str += QObject::tr("Background Color Index: %1\n" ).arg(lsd[5]);
187 }
188 if (lsd[6] && full)
189 str += QObject::tr("Pixel Aspect Ratio: %1 (Aspect Ratio %2)\n" ).arg( lsd[6] ).
190 arg( ((double)lsd[6]+15)/64 );
191
192 /* Global Color Table */
193 if (lsd[4]&0x80) {
194 gct_size=2<<(lsd[4]&0x7);
195 pos=ftell(src);
196 (void)fread((void *)gct,gct_size,3,src);
197 }
198
199 do {
200 pos=ftell(src);
201 (void)fread((void *)buffer,1,1,src);
202 switch (buffer[0]) {
203 case 0x2c: /* Image Descriptor */
204 (void)fread((void *)(buffer+1),9,1,src);
205 /* Local Color Table */
206 if (buffer[8]&0x80) {
207 size=2<<(buffer[8]&0x7);
208 pos=ftell(src);
209 (void)fread((void *)buffer,size,3,src);
210 }
211 /* Table Based Image Data */
212 pos=ftell(src);
213 (void)fread((void *)buffer,1,1,src);
214 transdata(src,dest);
215 break;
216 case 0x21: /* Extension */
217 (void)fread((void *)(buffer+1),1,1,src);
218 switch (buffer[1]) {
219 case 0xfe: /* Comment Extension */
220 if (true)
221 {
222 QCString st;
223 dumpcomment(src, st);
224 str += QObject::tr("Comment: %1\n" ).arg( st );
225 }
226 if (skipcomment)
227 skipdata(src);
228 else {
229 transdata(src,dest);
230 }
231 break;
232 case 0x01: /* Plain Text Extension */
233 case 0xf9: /* Graphic Control Extension */
234 case 0xff: /* Application Extension */
235 default:
236 transblock(src,dest);
237 transdata(src,dest);
238 break;
239 }
240 break;
241 case 0x3b: /* Trailer (write comment just before here) */
242 break;
243 default:
244 (void)fprintf(stderr,"0x%08lx: Error, unknown block 0x%02x!\n",ftell(src)-1,buffer[0]);
245 return(1);
246 }
247 } while (buffer[0]!=0x3b&&!feof(src));
248 return(buffer[0]==0x3b?SUCCESS:FAILURE);
249}
250
251
252/****************************************************************************/
253extern void get_gif_info( const char * original_filename, QString& str,
254 bool full =false)
255{
256FILE * infile;
257
258 if ((infile = fopen(original_filename, READ_BINARY)) == NULL) {
259 fprintf(stderr, "can't open gif image '%s'\n", original_filename);
260 return ;
261 }
262
263 output = FALSE;
264 verbose = TRUE;
265 debug = FALSE;
266 skipcomment = FALSE;
267 giftrans( infile, NULL, str, full );
268 fclose( infile );
269}
270
271}
272
273
274GifSlave::GifSlave()
275 : SlaveInterface(QStringList("gif"))
276{}
277
278GifSlave::~GifSlave() {
279
280}
281
282QString GifSlave::iconViewName(const QString& str) {
283 QString st;
284 get_gif_info(QFile::encodeName( str ).data(), st );
285 return st;
286}
287
288QString GifSlave::fullImageInfo( const QString& str) {
289 QString st;
290 get_gif_info(QFile::encodeName( str ).data(), st, true );
291 return st;
292}
293
294QPixmap GifSlave::pixmap(const QString& path, int width, int height ) {
295 static QImage img;
296 img.load( path );
297 if ( img.isNull() ) {
298 QPixmap pix;
299 return pix;
300 }
301
302 return ThumbNailTool::scaleImage( img, width,height );
303}
304
305
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 @@
1/*
2 * GPLv2 zecke@handhelds.org
3 */
4
5
6#ifndef SLAVE_GIF_IMPL_H
7#define SLAVE_GIF_IMPL_H
8
9#include "slaveiface.h"
10
11class GifSlave : public SlaveInterface {
12public:
13 GifSlave();
14 ~GifSlave();
15
16 QString iconViewName( const QString& );
17 QString fullImageInfo( const QString& );
18 QPixmap pixmap( const QString&, int width, int height );
19};
20
21
22#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 @@
1#include "jpeg_slave.h"
2
3#include "thumbnailtool.h"
4
5PHUNK_VIEW_INTERFACE( "JPEG", JpegSlave )
6
7#include <qtopia/timestring.h>
8#include <qobject.h>
9#include <qimage.h>
10
11/**
12 exif.h
13*/
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <math.h>
18#include <time.h>
19
20#include <qstring.h>
21#include <qfile.h>
22#include <qimage.h>
23
24typedef enum {
25 READ_EXIF = 1,
26 READ_IMAGE = 2,
27 READ_ALL = 3
28}ReadMode_t;
29
30//--------------------------------------------------------------------------
31// This structure is used to store jpeg file sections in memory.
32typedef struct {
33 uchar * Data;
34 int Type;
35 unsigned Size;
36}Section_t;
37
38typedef unsigned char uchar;
39
40typedef struct {
41 unsigned short Tag;
42 const char*const Desc;
43}TagTable_t;
44
45#define MAX_SECTIONS 20
46#define PSEUDO_IMAGE_MARKER 0x123; // Extra value.
47
48class ExifData {
49 Section_t Sections[MAX_SECTIONS];
50
51 QString CameraMake;
52 QString CameraModel;
53 QString DateTime;
54 int Orientation;
55 int Height, Width;
56 int ExifImageLength, ExifImageWidth;
57 int IsColor;
58 int Process;
59 int FlashUsed;
60 float FocalLength;
61 float ExposureTime;
62 float ApertureFNumber;
63 float Distance;
64 int Whitebalance;
65 int MeteringMode;
66 float CCDWidth;
67 float ExposureBias;
68 int ExposureProgram;
69 int ISOequivalent;
70 int CompressionLevel;
71 QString UserComment;
72 QString Comment;
73 QImage Thumbnail;
74
75 int ReadJpegSections (QFile & infile, ReadMode_t ReadMode);
76 void DiscardData(void);
77 int Get16u(void * Short);
78 int Get32s(void * Long);
79 unsigned Get32u(void * Long);
80 double ConvertAnyFormat(void * ValuePtr, int Format);
81 void ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength);
82 void process_COM (const uchar * Data, int length);
83 void process_SOFn (const uchar * Data, int marker);
84 int Get16m(const void * Short);
85 void process_EXIF(unsigned char * CharBuf, unsigned int length);
86 int Exif2tm(struct tm * timeptr, char * ExifTime);
87
88public:
89 ExifData();
90 bool scan(const QString &);
91 QString getCameraMake() { return CameraMake; }
92 QString getCameraModel() { return CameraModel; }
93 QString getDateTime() { return DateTime; }
94 int getOrientation() { return Orientation; }
95 int getHeight() { return Height; }
96 int getWidth() { return Width; }
97 int getIsColor() { return IsColor; }
98 int getProcess() { return Process; }
99 int getFlashUsed() { return FlashUsed; }
100 float getFocalLength() { return FocalLength; }
101 float getExposureTime() { return ExposureTime; }
102 float getApertureFNumber() { return ApertureFNumber; }
103 float getDistance() { return Distance; }
104 int getWhitebalance() { return Whitebalance; }
105 int getMeteringMode() { return MeteringMode; }
106 float getCCDWidth() { return CCDWidth; }
107 float getExposureBias() { return ExposureBias; }
108 int getExposureProgram() { return ExposureProgram; }
109 int getISOequivalent() { return ISOequivalent; }
110 int getCompressionLevel() { return CompressionLevel; }
111 QString getUserComment() { return UserComment; }
112 QString getComment() { return Comment; }
113 QImage getThumbnail();
114 bool isThumbnailSane();
115 bool isNullThumbnail() { return !isThumbnailSane(); }
116};
117
118class FatalError {
119 const char* ex;
120public:
121 FatalError(const char* s) { ex = s; }
122 void debug_print() const { qWarning("exception: %s", ex ); }
123};
124
125
126
127static unsigned char * LastExifRefd;
128static int ExifSettingsLength;
129static double FocalplaneXRes;
130static double FocalplaneUnits;
131static int MotorolaOrder = 0;
132static int SectionsRead;
133//static int HaveAll;
134
135//--------------------------------------------------------------------------
136// Table of Jpeg encoding process names
137
138#define M_SOF0 0xC0 // Start Of Frame N
139#define M_SOF1 0xC1 // N indicates which compression process
140#define M_SOF2 0xC2 // Only SOF0-SOF2 are now in common use
141#define M_SOF3 0xC3
142#define M_SOF5 0xC5 // NB: codes C4 and CC are NOT SOF markers
143#define M_SOF6 0xC6
144#define M_SOF7 0xC7
145#define M_SOF9 0xC9
146#define M_SOF10 0xCA
147#define M_SOF11 0xCB
148#define M_SOF13 0xCD
149#define M_SOF14 0xCE
150#define M_SOF15 0xCF
151#define M_SOI 0xD8 // Start Of Image (beginning of datastream)
152#define M_EOI 0xD9 // End Of Image (end of datastream)
153#define M_SOS 0xDA // Start Of Scan (begins compressed data)
154#define M_JFIF 0xE0 // Jfif marker
155#define M_EXIF 0xE1 // Exif marker
156#define M_COM 0xFE // COMment
157
158
159TagTable_t ProcessTable[] = {
160 { M_SOF0, "Baseline"},
161 { M_SOF1, "Extended sequential"},
162 { M_SOF2, "Progressive"},
163 { M_SOF3, "Lossless"},
164 { M_SOF5, "Differential sequential"},
165 { M_SOF6, "Differential progressive"},
166 { M_SOF7, "Differential lossless"},
167 { M_SOF9, "Extended sequential, arithmetic coding"},
168 { M_SOF10, "Progressive, arithmetic coding"},
169 { M_SOF11, "Lossless, arithmetic coding"},
170 { M_SOF13, "Differential sequential, arithmetic coding"},
171 { M_SOF14, "Differential progressive, arithmetic coding"},
172 { M_SOF15, "Differential lossless, arithmetic coding"},
173 { 0, "Unknown"}
174};
175
176
177
178//--------------------------------------------------------------------------
179// Describes format descriptor
180static int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
181#define NUM_FORMATS 12
182
183#define FMT_BYTE 1
184#define FMT_STRING 2
185#define FMT_USHORT 3
186#define FMT_ULONG 4
187#define FMT_URATIONAL 5
188#define FMT_SBYTE 6
189#define FMT_UNDEFINED 7
190#define FMT_SSHORT 8
191#define FMT_SLONG 9
192#define FMT_SRATIONAL 10
193#define FMT_SINGLE 11
194#define FMT_DOUBLE 12
195
196//--------------------------------------------------------------------------
197// Describes tag values
198
199#define TAG_EXIF_OFFSET 0x8769
200#define TAG_INTEROP_OFFSET 0xa005
201
202#define TAG_MAKE 0x010F
203#define TAG_MODEL 0x0110
204#define TAG_ORIENTATION 0x0112
205
206#define TAG_EXPOSURETIME 0x829A
207#define TAG_FNUMBER 0x829D
208
209#define TAG_SHUTTERSPEED 0x9201
210#define TAG_APERTURE 0x9202
211#define TAG_MAXAPERTURE 0x9205
212#define TAG_FOCALLENGTH 0x920A
213
214#define TAG_DATETIME_ORIGINAL 0x9003
215#define TAG_USERCOMMENT 0x9286
216
217#define TAG_SUBJECT_DISTANCE 0x9206
218#define TAG_FLASH 0x9209
219
220#define TAG_FOCALPLANEXRES 0xa20E
221#define TAG_FOCALPLANEUNITS 0xa210
222#define TAG_EXIF_IMAGEWIDTH 0xA002
223#define TAG_EXIF_IMAGELENGTH 0xA003
224
225// the following is added 05-jan-2001 vcs
226#define TAG_EXPOSURE_BIAS 0x9204
227#define TAG_WHITEBALANCE 0x9208
228#define TAG_METERING_MODE 0x9207
229#define TAG_EXPOSURE_PROGRAM 0x8822
230#define TAG_ISO_EQUIVALENT 0x8827
231#define TAG_COMPRESSION_LEVEL 0x9102
232
233#define TAG_THUMBNAIL_OFFSET 0x0201
234#define TAG_THUMBNAIL_LENGTH 0x0202
235
236
237
238
239//--------------------------------------------------------------------------
240// Parse the marker stream until SOS or EOI is seen;
241//--------------------------------------------------------------------------
242int ExifData::ReadJpegSections (QFile & infile, ReadMode_t ReadMode)
243{
244 int a;
245
246 a = infile.getch();
247
248 if (a != 0xff || infile.getch() != M_SOI) {
249 SectionsRead = 0;
250 return false;
251 }
252 for(SectionsRead = 0; SectionsRead < MAX_SECTIONS-1; ){
253 int marker = 0;
254 int got;
255 unsigned int ll,lh;
256 unsigned int itemlen;
257 uchar * Data;
258
259 for (a=0;a<7;a++){
260 marker = infile.getch();
261 if (marker != 0xff) break;
262
263 if (a >= 6){
264
265 qWarning( "too many padding bytes" );
266 return false;
267
268 }
269 }
270
271 if (marker == 0xff){
272 // 0xff is legal padding, but if we get that many, something's wrong.
273 return false;
274 }
275
276 Sections[SectionsRead].Type = marker;
277
278 // Read the length of the section.
279 lh = (uchar) infile.getch();
280 ll = (uchar) infile.getch();
281
282 itemlen = (lh << 8) | ll;
283
284 if (itemlen < 2) {
285 return false;;
286 }
287
288 Sections[SectionsRead].Size = itemlen;
289
290 Data = (uchar *)malloc(itemlen+1); // Add 1 to allow sticking a 0 at the end.
291 Sections[SectionsRead].Data = Data;
292
293 // Store first two pre-read bytes.
294 Data[0] = (uchar)lh;
295 Data[1] = (uchar)ll;
296
297 got = infile.readBlock((char*)Data+2, itemlen-2); // Read the whole section.
298 if (( unsigned ) got != itemlen-2){
299 return false;
300 }
301 SectionsRead++;
302
303 switch(marker){
304
305 case M_SOS: // stop before hitting compressed data
306 // If reading entire image is requested, read the rest of the data.
307 if (ReadMode & READ_IMAGE){
308 unsigned long size;
309
310 size = QMAX( 0ul, infile.size()-infile.at() );
311 Data = (uchar *)malloc(size);
312 if (Data == NULL){
313 return false;
314 }
315
316 got = infile.readBlock((char*)Data, size);
317 if (( unsigned ) got != size){
318 return false;
319 }
320
321 Sections[SectionsRead].Data = Data;
322 Sections[SectionsRead].Size = size;
323 Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER;
324 SectionsRead ++;
325 //HaveAll = 1;
326 }
327 return true;
328
329 case M_EOI: // in case it's a tables-only JPEG stream
330 qWarning( "No image in jpeg!" );
331 return false;
332
333 case M_COM: // Comment section
334 // pieczy 2002-02-12
335 // now the User comment goes to UserComment
336 // so we can store a Comment section also in READ_EXIF mode
337 process_COM(Data, itemlen);
338 break;
339
340 case M_JFIF:
341 // Regular jpegs always have this tag, exif images have the exif
342 // marker instead, althogh ACDsee will write images with both markers.
343 // this program will re-create this marker on absence of exif marker.
344 // hence no need to keep the copy from the file.
345 free(Sections[--SectionsRead].Data);
346 break;
347
348 case M_EXIF:
349 // Seen files from some 'U-lead' software with Vivitar scanner
350 // that uses marker 31 for non exif stuff. Thus make sure
351 // it says 'Exif' in the section before treating it as exif.
352 if ((ReadMode & READ_EXIF) && memcmp(Data+2, "Exif", 4) == 0){
353 process_EXIF((uchar *)Data, itemlen);
354 }else{
355 // Discard this section.
356 free(Sections[--SectionsRead].Data);
357 }
358 break;
359
360 case M_SOF0:
361 case M_SOF1:
362 case M_SOF2:
363 case M_SOF3:
364 case M_SOF5:
365 case M_SOF6:
366 case M_SOF7:
367 case M_SOF9:
368 case M_SOF10:
369 case M_SOF11:
370 case M_SOF13:
371 case M_SOF14:
372 case M_SOF15:
373 process_SOFn(Data, marker);
374 default:
375 break;
376 break;
377 }
378 }
379 return true;
380}
381
382
383//--------------------------------------------------------------------------
384// Discard read data.
385//--------------------------------------------------------------------------
386void ExifData::DiscardData(void)
387{
388 for (int a=0; a < SectionsRead; a++)
389 free(Sections[a].Data);
390 SectionsRead = 0;
391}
392
393//--------------------------------------------------------------------------
394// Convert a 16 bit unsigned value from file's native byte order
395//--------------------------------------------------------------------------
396int ExifData::Get16u(void * Short)
397{
398 if (MotorolaOrder){
399 return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
400 }else{
401 return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0];
402 }
403}
404
405//--------------------------------------------------------------------------
406// Convert a 32 bit signed value from file's native byte order
407//--------------------------------------------------------------------------
408int ExifData::Get32s(void * Long)
409{
410 if (MotorolaOrder){
411 return ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16)
412 | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 );
413 }else{
414 return ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16)
415 | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 );
416 }
417}
418
419//--------------------------------------------------------------------------
420// Convert a 32 bit unsigned value from file's native byte order
421//--------------------------------------------------------------------------
422unsigned ExifData::Get32u(void * Long)
423{
424 return (unsigned)Get32s(Long) & 0xffffffff;
425}
426
427//--------------------------------------------------------------------------
428// Evaluate number, be it int, rational, or float from directory.
429//--------------------------------------------------------------------------
430double ExifData::ConvertAnyFormat(void * ValuePtr, int Format)
431{
432 double Value;
433 Value = 0;
434
435 switch(Format){
436 case FMT_SBYTE: Value = *(signed char *)ValuePtr; break;
437 case FMT_BYTE: Value = *(uchar *)ValuePtr; break;
438
439 case FMT_USHORT: Value = Get16u(ValuePtr); break;
440
441 case FMT_ULONG: Value = Get32u(ValuePtr); break;
442
443 case FMT_URATIONAL:
444 case FMT_SRATIONAL:
445 {
446 int Num,Den;
447 Num = Get32s(ValuePtr);
448 Den = Get32s(4+(char *)ValuePtr);
449 if (Den == 0){
450 Value = 0;
451 }else{
452 Value = (double)Num/Den;
453 }
454 break;
455 }
456
457 case FMT_SSHORT: Value = (signed short)Get16u(ValuePtr); break;
458 case FMT_SLONG: Value = Get32s(ValuePtr); break;
459
460 // Not sure if this is correct (never seen float used in Exif format)
461 case FMT_SINGLE: Value = (double)*(float *)ValuePtr; break;
462 case FMT_DOUBLE: Value = *(double *)ValuePtr; break;
463 }
464 return Value;
465}
466
467//--------------------------------------------------------------------------
468// Process one of the nested EXIF directories.
469//--------------------------------------------------------------------------
470void ExifData::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength)
471{
472 int de;
473 int a;
474 int NumDirEntries;
475 unsigned ThumbnailOffset = 0;
476 unsigned ThumbnailSize = 0;
477
478 NumDirEntries = Get16u(DirStart);
479 #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
480
481 {
482 unsigned char * DirEnd;
483 DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
484 if (DirEnd+4 > (OffsetBase+ExifLength)){
485 if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength){
486 // Version 1.3 of jhead would truncate a bit too much.
487 // This also caught later on as well.
488 }else{
489 // Note: Files that had thumbnails trimmed with jhead 1.3 or earlier
490 // might trigger this.
491 return;
492 }
493 }
494 if (DirEnd < LastExifRefd) LastExifRefd = DirEnd;
495 }
496
497 for (de=0;de<NumDirEntries;de++){
498 int Tag, Format, Components;
499 unsigned char * ValuePtr;
500 int ByteCount;
501 char * DirEntry;
502 DirEntry = (char *)DIR_ENTRY_ADDR(DirStart, de);
503
504 Tag = Get16u(DirEntry);
505 Format = Get16u(DirEntry+2);
506 Components = Get32u(DirEntry+4);
507
508 if ((Format-1) >= NUM_FORMATS) {
509 // (-1) catches illegal zero case as unsigned underflows to positive large.
510 return;
511 }
512
513 ByteCount = Components * BytesPerFormat[Format];
514
515 if (ByteCount > 4){
516 unsigned OffsetVal;
517 OffsetVal = Get32u(DirEntry+8);
518 // If its bigger than 4 bytes, the dir entry contains an offset.
519 if (OffsetVal+ByteCount > ExifLength){
520 // Bogus pointer offset and / or bytecount value
521 //printf("Offset %d bytes %d ExifLen %d\n",OffsetVal, ByteCount, ExifLength);
522
523 return;
524 }
525 ValuePtr = OffsetBase+OffsetVal;
526 }else{
527 // 4 bytes or less and value is in the dir entry itself
528 ValuePtr = (unsigned char *)DirEntry+8;
529 }
530
531 if (LastExifRefd < ValuePtr+ByteCount){
532 // Keep track of last byte in the exif header that was actually referenced.
533 // That way, we know where the discardable thumbnail data begins.
534 LastExifRefd = ValuePtr+ByteCount;
535 }
536
537 // Extract useful components of tag
538 switch(Tag){
539
540 case TAG_MAKE:
541 ExifData::CameraMake = QString((char*)ValuePtr);
542 break;
543
544 case TAG_MODEL:
545 ExifData::CameraModel = QString((char*)ValuePtr);
546 break;
547
548 case TAG_ORIENTATION:
549 Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
550 break;
551
552 case TAG_DATETIME_ORIGINAL:
553 DateTime = QString((char*)ValuePtr);
554 break;
555
556 case TAG_USERCOMMENT:
557 // Olympus has this padded with trailing spaces. Remove these first.
558 for (a=ByteCount;;){
559 a--;
560 if ((ValuePtr)[a] == ' '){
561 (ValuePtr)[a] = '\0';
562 }else{
563 break;
564 }
565 if (a == 0) break;
566 }
567
568 // Copy the comment
569 if (memcmp(ValuePtr, "ASCII",5) == 0){
570 for (a=5;a<10;a++){
571 int c;
572 c = (ValuePtr)[a];
573 if (c != '\0' && c != ' '){
574 //strncpy(ImageInfo.Comments, (const char*)(a+ValuePtr), 199);
575 UserComment.sprintf("%s", (const char*)(a+ValuePtr));
576 break;
577 }
578 }
579 }else{
580 //strncpy(ImageInfo.Comments, (const char*)ValuePtr, 199);
581 UserComment.sprintf("%s", (const char*)ValuePtr);
582 }
583 break;
584
585 case TAG_FNUMBER:
586 // Simplest way of expressing aperture, so I trust it the most.
587 // (overwrite previously computd value if there is one)
588 ExifData::ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);
589 break;
590
591 case TAG_APERTURE:
592 case TAG_MAXAPERTURE:
593 // More relevant info always comes earlier, so only use this field if we don't
594 // have appropriate aperture information yet.
595 if (ExifData::ApertureFNumber == 0){
596 ExifData::ApertureFNumber
597 = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5);
598 }
599 break;
600
601 case TAG_FOCALLENGTH:
602 // Nice digital cameras actually save the focal length as a function
603 // of how farthey are zoomed in.
604 ExifData::FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);
605 break;
606
607 case TAG_SUBJECT_DISTANCE:
608 // Inidcates the distacne the autofocus camera is focused to.
609 // Tends to be less accurate as distance increases.
610 ExifData::Distance = (float)ConvertAnyFormat(ValuePtr, Format);
611 break;
612
613 case TAG_EXPOSURETIME:
614 // Simplest way of expressing exposure time, so I trust it most.
615 // (overwrite previously computd value if there is one)
616 ExifData::ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format);
617 break;
618
619 case TAG_SHUTTERSPEED:
620 // More complicated way of expressing exposure time, so only use
621 // this value if we don't already have it from somewhere else.
622 if (ExifData::ExposureTime == 0){
623 ExifData::ExposureTime
624 = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2)));
625 }
626 break;
627
628 case TAG_FLASH:
629 if (ConvertAnyFormat(ValuePtr, Format)){
630 ExifData::FlashUsed = 1;
631 }
632 break;
633
634 case TAG_EXIF_IMAGELENGTH:
635 ExifImageLength = (int)ConvertAnyFormat(ValuePtr, Format);
636 break;
637
638 case TAG_EXIF_IMAGEWIDTH:
639 ExifImageWidth = (int)ConvertAnyFormat(ValuePtr, Format);
640 break;
641
642 case TAG_FOCALPLANEXRES:
643 FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format);
644 break;
645
646 case TAG_FOCALPLANEUNITS:
647 switch((int)ConvertAnyFormat(ValuePtr, Format)){
648 case 1: FocalplaneUnits = 25.4; break; // inch
649 case 2:
650 // According to the information I was using, 2 means meters.
651 // But looking at the Cannon powershot's files, inches is the only
652 // sensible value.
653 FocalplaneUnits = 25.4;
654 break;
655
656 case 3: FocalplaneUnits = 10; break; // centimeter
657 case 4: FocalplaneUnits = 1; break; // milimeter
658 case 5: FocalplaneUnits = .001; break; // micrometer
659 }
660 break;
661
662 // Remaining cases contributed by: Volker C. Schoech (schoech@gmx.de)
663
664 case TAG_EXPOSURE_BIAS:
665 ExifData::ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format);
666 break;
667
668 case TAG_WHITEBALANCE:
669 ExifData::Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
670 break;
671
672 case TAG_METERING_MODE:
673 ExifData::MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
674 break;
675
676 case TAG_EXPOSURE_PROGRAM:
677 ExifData::ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
678 break;
679
680 case TAG_ISO_EQUIVALENT:
681 ExifData::ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
682 if ( ExifData::ISOequivalent < 50 ) ExifData::ISOequivalent *= 200;
683 break;
684
685 case TAG_COMPRESSION_LEVEL:
686 ExifData::CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format);
687 break;
688
689 case TAG_THUMBNAIL_OFFSET:
690 ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);
691 break;
692
693 case TAG_THUMBNAIL_LENGTH:
694 ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
695 break;
696
697 }
698
699 if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){
700 unsigned char * SubdirStart;
701 SubdirStart = OffsetBase + Get32u(ValuePtr);
702 if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){
703 return;
704 }
705 ProcessExifDir(SubdirStart, OffsetBase, ExifLength);
706 continue;
707 }
708 }
709
710 {
711 // In addition to linking to subdirectories via exif tags,
712 // there's also a potential link to another directory at the end of each
713 // directory. this has got to be the result of a comitee!
714 unsigned char * SubdirStart;
715 unsigned Offset;
716
717 if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength){
718 Offset = Get32u(DIR_ENTRY_ADDR(DirStart, NumDirEntries));
719 // There is at least one jpeg from an HP camera having an Offset of almost MAXUINT.
720 // Adding OffsetBase to it produces an overflow, so compare with ExifLength here.
721 // See http://bugs.kde.org/show_bug.cgi?id=54542
722 if (Offset && Offset < ExifLength){
723 SubdirStart = OffsetBase + Offset;
724 if (SubdirStart > OffsetBase+ExifLength){
725 if (SubdirStart < OffsetBase+ExifLength+20){
726 // Jhead 1.3 or earlier would crop the whole directory!
727 // As Jhead produces this form of format incorrectness,
728 // I'll just let it pass silently
729 qWarning( "Thumbnail removed with Jhead 1.3 or earlier" );
730 }else{
731 return;
732 }
733 }else{
734 if (SubdirStart <= OffsetBase+ExifLength){
735 ProcessExifDir(SubdirStart, OffsetBase, ExifLength);
736 }
737 }
738 }
739 }else{
740 // The exif header ends before the last next directory pointer.
741 }
742 }
743
744 if (ThumbnailSize && ThumbnailOffset){
745 if (ThumbnailSize + ThumbnailOffset <= ExifLength){
746 // The thumbnail pointer appears to be valid. Store it.
747 Thumbnail.loadFromData(OffsetBase + ThumbnailOffset, ThumbnailSize, "JPEG");
748 }
749 }
750}
751
752//--------------------------------------------------------------------------
753// Process a COM marker. We want to leave the bytes unchanged. The
754// progam that displays this text may decide to remove blanks, convert
755// newlines, or otherwise modify the text. In particular we want to be
756// safe for passing utf-8 text.
757//--------------------------------------------------------------------------
758void ExifData::process_COM (const uchar * Data, int length)
759{
760 QChar ch;
761 int a;
762
763 for (a=2;a<length;a++){
764 ch = Data[a];
765 if (ch == '\000') continue; // Remove nulls
766 Comment.append(ch);
767 }
768}
769
770
771//--------------------------------------------------------------------------
772// Process a SOFn marker. This is useful for the image dimensions
773//--------------------------------------------------------------------------
774void ExifData::process_SOFn (const uchar * Data, int marker)
775{
776 int data_precision, num_components;
777
778 data_precision = Data[2];
779 ExifData::Height = Get16m(Data+3);
780 ExifData::Width = Get16m(Data+5);
781 num_components = Data[7];
782
783 if (num_components == 3){
784 ExifData::IsColor = 1;
785 }else{
786 ExifData::IsColor = 0;
787 }
788
789 ExifData::Process = marker;
790
791}
792
793//--------------------------------------------------------------------------
794// Get 16 bits motorola order (always) for jpeg header stuff.
795//--------------------------------------------------------------------------
796int ExifData::Get16m(const void * Short)
797{
798 return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
799}
800
801
802//--------------------------------------------------------------------------
803// Process a EXIF marker
804// Describes all the drivel that most digital cameras include...
805//--------------------------------------------------------------------------
806void ExifData::process_EXIF(unsigned char * CharBuf, unsigned int length)
807{
808 ExifData::FlashUsed = 0; // If it s from a digicam, and it used flash, it says so.
809
810 FocalplaneXRes = 0;
811 FocalplaneUnits = 0;
812 ExifImageWidth = 0;
813 ExifImageLength = 0;
814
815 { // Check the EXIF header component
816 static const uchar ExifHeader[] = "Exif\0\0";
817 if (memcmp(CharBuf+2, ExifHeader,6)){
818 return;
819 }
820 }
821
822 if (memcmp(CharBuf+8,"II",2) == 0){
823 // printf("Exif section in Intel order\n");
824 MotorolaOrder = 0;
825 }else{
826 if (memcmp(CharBuf+8,"MM",2) == 0){
827 // printf("Exif section in Motorola order\n");
828 MotorolaOrder = 1;
829 }else{
830 return;
831 }
832 }
833
834 // Check the next two values for correctness.
835 if (Get16u(CharBuf+10) != 0x2a
836 || Get32u(CharBuf+12) != 0x08){
837 return;
838 }
839
840 LastExifRefd = CharBuf;
841
842 // First directory starts 16 bytes in. Offsets start at 8 bytes in.
843 ProcessExifDir(CharBuf+16, CharBuf+8, length-6);
844
845 // This is how far the interesting (non thumbnail) part of the exif went.
846 ExifSettingsLength = LastExifRefd - CharBuf;
847
848 // Compute the CCD width, in milimeters.
849 if (FocalplaneXRes != 0){
850 ExifData::CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes);
851 }
852}
853
854//--------------------------------------------------------------------------
855// Convert exif time to Unix time structure
856//--------------------------------------------------------------------------
857int ExifData::Exif2tm(struct tm * timeptr, char * ExifTime)
858{
859 int a;
860
861 timeptr->tm_wday = -1;
862
863 // Check for format: YYYY:MM:DD HH:MM:SS format.
864 a = sscanf(ExifTime, "%d:%d:%d %d:%d:%d",
865 &timeptr->tm_year, &timeptr->tm_mon, &timeptr->tm_mday,
866 &timeptr->tm_hour, &timeptr->tm_min, &timeptr->tm_sec);
867
868 if (a == 6){
869 timeptr->tm_isdst = -1;
870 timeptr->tm_mon -= 1; // Adjust for unix zero-based months
871 timeptr->tm_year -= 1900; // Adjust for year starting at 1900
872 return true; // worked.
873 }
874
875 return false; // Wasn't in Exif date format.
876}
877
878//--------------------------------------------------------------------------
879// Contructor for initialising
880//--------------------------------------------------------------------------
881ExifData::ExifData()
882{
883 ExifData::Whitebalance = -1;
884 ExifData::MeteringMode = -1;
885 ExifData::FlashUsed = -1;
886 Orientation = 0;
887 Height = 0;
888 Width = 0;
889 IsColor = 0;
890 Process = 0;
891 FocalLength = 0;
892 ExposureTime = 0;
893 ApertureFNumber = 0;
894 Distance = 0;
895 CCDWidth = 0;
896 ExposureBias = 0;
897 ExposureProgram = 0;
898 ISOequivalent = 0;
899 CompressionLevel = 0;
900}
901
902//--------------------------------------------------------------------------
903// process a EXIF jpeg file
904//--------------------------------------------------------------------------
905bool ExifData::scan(const QString & path)
906{
907 int ret;
908
909 QFile f(path);
910 f.open(IO_ReadOnly);
911
912 // Scan the JPEG headers.
913 ret = ReadJpegSections(f, READ_EXIF);
914
915 if (ret == false){
916 qWarning( "Not JPEG file!" );
917 DiscardData();
918 f.close();
919 return false;
920 }
921 f.close();
922 DiscardData();
923
924 //now make the strings clean,
925 // for exmaple my Casio is a "QV-4000 "
926 CameraMake = CameraMake.stripWhiteSpace();
927 CameraModel = CameraModel.stripWhiteSpace();
928 UserComment = UserComment.stripWhiteSpace();
929 Comment = Comment.stripWhiteSpace();
930 return true;
931}
932
933//--------------------------------------------------------------------------
934// Does the embedded thumbnail match the jpeg image?
935//--------------------------------------------------------------------------
936#ifndef JPEG_TOL
937#define JPEG_TOL 0.02
938#endif
939bool ExifData::isThumbnailSane() {
940 if (Thumbnail.isNull()) return false;
941
942 // check whether thumbnail dimensions match the image
943 // not foolproof, but catches some altered images (jpegtran -rotate)
944 if (ExifImageLength != 0 && ExifImageLength != Height) return false;
945 if (ExifImageWidth != 0 && ExifImageWidth != Width) return false;
946 if (Thumbnail.width() == 0 || Thumbnail.height() == 0) return false;
947 if (Height == 0 || Width == 0) return false;
948 double d = (double)Height/Width*Thumbnail.width()/Thumbnail.height();
949 return (1-JPEG_TOL < d) && (d < 1+JPEG_TOL);
950}
951
952
953
954static QImage flip_image( const QImage& img );
955static QImage rotate_90( const QImage& img );
956static QImage rotate_180( const QImage& );
957static QImage rotate_270( const QImage& );
958
959//--------------------------------------------------------------------------
960// return a thumbnail that respects the orientation flag
961// only if it seems sane
962//--------------------------------------------------------------------------
963QImage ExifData::getThumbnail() {
964 if (!isThumbnailSane()) return NULL;
965 if (!Orientation || Orientation == 1) return Thumbnail;
966
967 // now fix orientation
968
969 QImage dest = Thumbnail;
970 switch (Orientation) { // notice intentional fallthroughs
971 case 2: dest = flip_image( dest ); break;
972 case 4: dest = flip_image( dest );
973 case 3: dest =rotate_180( dest ); break;
974 case 5: dest = flip_image( dest );
975 case 6: dest = rotate_90( dest ); break;
976 case 7: dest = flip_image( dest );
977 case 8: dest = rotate_270( dest ); break;
978 default: break; // should never happen
979 }
980 return dest;
981}
982
983
984/*
985 *
986 */
987static QImage flip_image( const QImage& img ) {
988 return img.mirror( TRUE, FALSE );
989}
990
991
992static QImage dest;
993static int x, y;
994static unsigned int *srcData, *destData; // we're not threaded anyway
995static unsigned char *srcData8, *destData8; // 8 bit is char
996static unsigned int *srcTable, *destTable; // destination table
997
998
999static QImage rotate_90_8( const QImage &img ) {
1000 dest.create(img.height(), img.width(), img.depth());
1001 dest.setNumColors(img.numColors());
1002 srcTable = (unsigned int *)img.colorTable();
1003 destTable = (unsigned int *)dest.colorTable();
1004 for ( x=0; x < img.numColors(); ++x )
1005 destTable[x] = srcTable[x];
1006 for ( y=0; y < img.height(); ++y ){
1007 srcData8 = (unsigned char *)img.scanLine(y);
1008 for ( x=0; x < img.width(); ++x ){
1009 destData8 = (unsigned char *)dest.scanLine(x);
1010 destData8[img.height()-y-1] = srcData8[x];
1011 }
1012 }
1013 return dest;
1014}
1015
1016static QImage rotate_90_all( const QImage& img ) {
1017 dest.create(img.height(), img.width(), img.depth());
1018 for ( y=0; y < img.height(); ++y ) {
1019 srcData = (unsigned int *)img.scanLine(y);
1020 for ( x=0; x < img.width(); ++x ) {
1021 destData = (unsigned int *)dest.scanLine(x);
1022 destData[img.height()-y-1] = srcData[x];
1023 }
1024 }
1025
1026 return dest;
1027}
1028
1029
1030static QImage rotate_90( const QImage & img ) {
1031 if ( img.depth() > 8)
1032 return rotate_90_all( img );
1033 else
1034 return rotate_90_8( img );
1035}
1036
1037static QImage rotate_180_all( const QImage& img ) {
1038 dest.create(img.width(), img.height(), img.depth());
1039 for ( y=0; y < img.height(); ++y ){
1040 srcData = (unsigned int *)img.scanLine(y);
1041 destData = (unsigned int *)dest.scanLine(img.height()-y-1);
1042 for ( x=0; x < img.width(); ++x )
1043 destData[img.width()-x-1] = srcData[x];
1044 }
1045 return dest;
1046}
1047
1048static QImage rotate_180_8( const QImage& img ) {
1049 dest.create(img.width(), img.height(), img.depth());
1050 dest.setNumColors(img.numColors());
1051 srcTable = (unsigned int *)img.colorTable();
1052 destTable = (unsigned int *)dest.colorTable();
1053 for ( x=0; x < img.numColors(); ++x )
1054 destTable[x] = srcTable[x];
1055 for ( y=0; y < img.height(); ++y ){
1056 srcData8 = (unsigned char *)img.scanLine(y);
1057 destData8 = (unsigned char *)dest.scanLine(img.height()-y-1);
1058 for ( x=0; x < img.width(); ++x )
1059 destData8[img.width()-x-1] = srcData8[x];
1060 }
1061 return dest;
1062}
1063
1064static QImage rotate_180( const QImage& img ) {
1065 if ( img.depth() > 8 )
1066 return rotate_180_all( img );
1067 else
1068 return rotate_180_8( img );
1069}
1070
1071
1072static QImage rotate_270_8( const QImage& img ) {
1073 dest.create(img.height(), img.width(), img.depth());
1074 dest.setNumColors(img.numColors());
1075 srcTable = (unsigned int *)img.colorTable();
1076 destTable = (unsigned int *)dest.colorTable();
1077 for ( x=0; x < img.numColors(); ++x )
1078 destTable[x] = srcTable[x];
1079 for ( y=0; y < img.height(); ++y ){
1080 srcData8 = (unsigned char *)img.scanLine(y);
1081 for ( x=0; x < img.width(); ++x ){
1082 destData8 = (unsigned char *)dest.scanLine(img.width()-x-1);
1083 destData8[y] = srcData8[x];
1084 }
1085 }
1086
1087 return dest;
1088}
1089
1090static QImage rotate_270_all( const QImage& img ) {
1091 dest.create(img.height(), img.width(), img.depth());
1092 for ( y=0; y < img.height(); ++y ){
1093 srcData = (unsigned int *)img.scanLine(y);
1094 for ( x=0; x < img.width(); ++x ){
1095 destData = (unsigned int *)dest.scanLine(img.width()-x-1);
1096 destData[y] = srcData[x];
1097 }
1098 }
1099 return dest;
1100}
1101
1102static QImage rotate_270( const QImage& img ) {
1103 if ( img.depth() > 8 )
1104 return rotate_270_all( img );
1105 else
1106 return rotate_270_8( img );
1107}
1108
1109
1110static QString color_mode_to_string( bool b ) {
1111 return b ? QObject::tr( "Colormode: Color\n" ) : QObject::tr( "Colormode: Black and white\n" );
1112}
1113
1114static QString compression_to_string( int level ) {
1115 QString str;
1116 switch( level ) {
1117 case 1:
1118 str = QObject::tr( "Basic" );
1119 break;
1120 case 2:
1121 str = QObject::tr( "Normal" );
1122 break;
1123 case 4:
1124 str = QObject::tr( "Fine" );
1125 break;
1126 default:
1127 str = QObject::tr( "Unknown" );
1128
1129 }
1130 return QObject::tr("Quality: %1\n").arg(str);
1131}
1132
1133
1134static QDateTime parseDateTime( const QString& string )
1135{
1136 QDateTime dt;
1137 if ( string.length() != 19 )
1138 return dt;
1139
1140 QString year = string.left( 4 );
1141 QString month = string.mid( 5, 2 );
1142 QString day = string.mid( 8, 2 );
1143 QString hour = string.mid( 11, 2 );
1144 QString minute = string.mid( 14, 2 );
1145 QString seconds = string.mid( 18, 2 );
1146
1147 bool ok;
1148 bool allOk = true;
1149 int y = year.toInt( &ok );
1150 allOk &= ok;
1151
1152 int mo = month.toInt( &ok );
1153 allOk &= ok;
1154
1155 int d = day.toInt( &ok );
1156 allOk &= ok;
1157
1158 int h = hour.toInt( &ok );
1159 allOk &= ok;
1160
1161 int mi = minute.toInt( &ok );
1162 allOk &= ok;
1163
1164 int s = seconds.toInt( &ok );
1165 allOk &= ok;
1166
1167 if ( allOk ) {
1168 dt.setDate( QDate( y, mo, d ) );
1169 dt.setTime( QTime( h, mi, s ) );
1170 }
1171
1172 return dt;
1173}
1174
1175static QString white_balance_string( int i ) {
1176 QString balance;
1177 switch ( i ) {
1178 case 0:
1179 balance = QObject::tr( "Unknown" );
1180 break;
1181 case 1:
1182 balance = QObject::tr( "Daylight" );
1183 break;
1184 case 2:
1185 balance = QObject::tr( "Fluorescent" );
1186 break;
1187 case 3:
1188 balance = QObject::tr( "Tungsten" );
1189 break;
1190 case 17:
1191 balance = QObject::tr( "Standard light A" );
1192 break;
1193 case 18:
1194 balance = QObject::tr( "Standard light B" );
1195 break;
1196 case 19:
1197 balance = QObject::tr( "Standard light C" );
1198 break;
1199 case 20:
1200 balance = QObject::tr( "D55" );
1201 break;
1202 case 21:
1203 balance = QObject::tr( "D65" );
1204 break;
1205 case 22:
1206 balance = QObject::tr( "D75" );
1207 break;
1208 case 255:
1209 balance = QObject::tr( "Other" );
1210 break;
1211 default:
1212 balance = QObject::tr( "Unknown" );
1213 }
1214 return QObject::tr( "White Balance: %1\n" ).arg( balance );
1215
1216}
1217
1218
1219static QString metering_mode( int i) {
1220 QString meter;
1221 switch( i ) {
1222 case 0:
1223 meter = QObject::tr( "Unknown" );
1224 break;
1225 case 1:
1226 meter = QObject::tr( "Average" );
1227 break;
1228 case 2:
1229 meter = QObject::tr( "Center weighted average" );
1230 break;
1231 case 3:
1232 meter = QObject::tr( "Spot" );
1233 break;
1234 case 4:
1235 meter = QObject::tr( "MultiSpot" );
1236 break;
1237 case 5:
1238 meter = QObject::tr( "Pattern" );
1239 break;
1240 case 6:
1241 meter = QObject::tr( "Partial" );
1242 break;
1243 case 255:
1244 meter = QObject::tr( "Other" );
1245 break;
1246 default:
1247 meter = QObject::tr( "Unknown" );
1248 }
1249
1250 return QObject::tr( "Metering Mode: %1\n" ).arg( meter );
1251}
1252
1253
1254static QString exposure_program( int i ) {
1255 QString exp;
1256 switch( i ) {
1257 case 0:
1258 exp = QObject::tr( "Not defined" );
1259 break;
1260 case 1:
1261 exp = QObject::tr( "Manual" );
1262 break;
1263 case 2:
1264 exp = QObject::tr( "Normal progam" );
1265 break;
1266 case 3:
1267 exp = QObject::tr( "Aperture priority" );
1268 break;
1269 case 4:
1270 exp = QObject::tr( "Shutter priority" );
1271 break;
1272 case 5:
1273 exp = QObject::tr( "Creative progam\n(biased toward fast shutter speed" );
1274 break;
1275 case 6:
1276 exp = QObject::tr( "Action progam\n(biased toward fast shutter speed)" );
1277 break;
1278 case 7:
1279 exp = QObject::tr( "Portrait mode\n(for closeup photos with the background out of focus)" );
1280 break;
1281 case 8:
1282 exp = QObject::tr( "Landscape mode\n(for landscape photos with the background in focus)" );
1283 break;
1284 default:
1285 exp = QObject::tr( "Unknown" );
1286 }
1287
1288 return QObject::tr( "Exposure Program: %1\n" ).arg( exp );
1289}
1290
1291JpegSlave::JpegSlave()
1292 : SlaveInterface( QStringList::split( " ", "jpeg jpg" ) )
1293{}
1294
1295JpegSlave::~JpegSlave() {}
1296
1297QString JpegSlave::iconViewName( const QString& path) {
1298 ExifData ImageInfo;
1299 if ( !ImageInfo.scan( path ) )
1300 return QString::null;
1301
1302 QString tag;
1303 tag = QObject::tr( "<qt>Comment: %1\n" ).arg( ImageInfo.getComment() );
1304 {
1305// ODP fixme
1306 QString timestring = TimeString::dateString( parseDateTime( ImageInfo.getDateTime() ), FALSE );
1307 tag += QObject::tr( "Date/Time: %1\n" ).arg( timestring );
1308 }
1309 tag += QObject::tr( "Dimensions: %1x%2\n" ).arg(ImageInfo.getWidth())
1310 .arg(ImageInfo.getHeight() );
1311
1312 tag += color_mode_to_string( ImageInfo.getIsColor() );
1313
1314 tag += compression_to_string( ImageInfo.getCompressionLevel() );
1315 tag += QObject::tr( "</qt>" );
1316
1317 return tag;
1318}
1319
1320
1321/*
1322 * messy messy string creation
1323 */
1324QString JpegSlave::fullImageInfo( const QString& path) {
1325 ExifData ImageInfo;
1326 if ( !ImageInfo.scan( path ) )
1327 return QString::null;
1328
1329 QString tag, tmp;
1330 tag = QObject::tr( "Comment: %1\n" ).arg( ImageInfo.getComment() );
1331
1332 tmp = ImageInfo.getCameraMake();
1333 if ( tmp.length() )
1334 tag += QObject::tr( "Manufacturer: %1\n" ).arg( tmp );
1335 tmp = ImageInfo.getCameraModel();
1336 if ( tmp.length() )
1337 tag += QObject::tr( "Model: %1\n" ).arg( tmp );
1338 {
1339// ODP fixme
1340 tmp = TimeString::dateString( parseDateTime( ImageInfo.getDateTime() ), FALSE );
1341 tag += QObject::tr( "Date/Time: %1\n" ).arg( tmp );
1342 }
1343 tag += QObject::tr( "Dimensions: %1x%2\n" ).arg(ImageInfo.getWidth())
1344 .arg(ImageInfo.getHeight() );
1345
1346 tag += color_mode_to_string( ImageInfo.getIsColor() );
1347
1348 tag += compression_to_string( ImageInfo.getCompressionLevel() );
1349 if ( ImageInfo.getOrientation() )
1350 tag += QObject::tr( "Orientation: %1\n" ).arg(ImageInfo.getOrientation() );
1351
1352
1353 {
1354 int flash_used = ImageInfo.getFlashUsed();
1355 if ( flash_used >= 0 )
1356 tag += QObject::tr( "Flash used\n" );
1357 }
1358
1359 if ( ImageInfo.getFocalLength() ) {
1360 tag += QObject::tr( "Focal length: %1\n" ).arg( QString().sprintf( "%4.1f", ImageInfo.getFocalLength() ) );
1361 if ( ImageInfo.getCCDWidth() )
1362 tag += QObject::tr( "35mm equivalent: %1\n" ).arg( (int)(ImageInfo.getFocalLength()/ImageInfo.getCCDWidth()*35 + 0.5) );
1363
1364 }
1365
1366 if ( ImageInfo.getCCDWidth() )
1367 tag += QObject::tr( "CCD width: %1" ).arg( ImageInfo.getCCDWidth() );
1368 if ( ImageInfo.getExposureTime() ) {
1369 tmp = QString().sprintf("%4.2f", ImageInfo.getExposureTime() );
1370 float exposureTime = ImageInfo.getExposureTime();
1371 if ( exposureTime > 0 && exposureTime <= 0.5 )
1372 tmp += QString().sprintf(" (1/%d)", (int)(0.5 +1/exposureTime) );
1373 tag += QObject::tr( "Exposure time: %1\n" ).arg( tmp );
1374 }
1375
1376 if ( ImageInfo.getApertureFNumber() )
1377 tag += QObject::tr( "Aperture: %1\n" ).arg( QString().sprintf("f/%3.1f", (double)ImageInfo.getApertureFNumber() ) );
1378
1379 if ( ImageInfo.getDistance() ) {
1380 if ( ImageInfo.getDistance() < 0 )
1381 tag += QObject::tr( "Distance: %1\n" ).arg( QObject::tr( "Infinite" ) );
1382 else
1383 tag += QObject::tr( "Distance: %1\n" ).arg( QString().sprintf( "%5.2fm", (double)ImageInfo.getDistance() ) );
1384 }
1385
1386 if ( ImageInfo.getExposureBias() ) {
1387 tag += QObject::tr( "Exposure bias: %1\n", QString().sprintf("%4.2f", (double)ImageInfo.getExposureBias() ) );
1388 }
1389
1390 if ( ImageInfo.getWhitebalance() != -1 )
1391 tag += white_balance_string( ImageInfo.getWhitebalance() );
1392
1393
1394 if( ImageInfo.getMeteringMode() != -1 )
1395 tag += metering_mode( ImageInfo.getMeteringMode() );
1396
1397 if ( ImageInfo.getExposureProgram() )
1398 tag += exposure_program( ImageInfo.getExposureProgram() );
1399 if ( ImageInfo.getISOequivalent() )
1400 tag += QObject::tr( "ISO equivalent: %1\n" ).arg( QString().sprintf("%2d", ImageInfo.getISOequivalent() ) );
1401
1402 tmp = ImageInfo.getUserComment();
1403 if ( tmp.length() )
1404 tag += QObject::tr( "EXIF comment: %1" ).arg( tmp );
1405
1406 tag += QObject::tr( "</qt>" );
1407
1408
1409
1410 return tag;
1411}
1412
1413QPixmap JpegSlave::pixmap( const QString& path, int wid, int hei) {
1414 ExifData ImageInfo;
1415 if ( !ImageInfo.scan( path ) || ImageInfo.isNullThumbnail() ) {
1416 QImage img;
1417 QImageIO iio( path, 0l );
1418 QString str = QString( "Fast Shrink( 4 ) Scale( %1, %2, ScaleFree)" ).arg( wid ).arg( hei );
1419 iio.setParameters( str.latin1() );// will be strdupped anyway
1420 img = iio.read() ? iio.image() : QImage();
1421 return ThumbNailTool::scaleImage( img, wid,hei );
1422 }else{
1423 QImage img = ImageInfo.getThumbnail();
1424 return ThumbNailTool::scaleImage( img, wid,hei );
1425 }
1426}
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 @@
1/*
2 * GPLv2 zecke@handhelds.org
3 */
4#ifndef JPEG_SLAVE_IMPL_H
5#define JPEG_SLAVE_IMPL_H
6
7#include "slaveiface.h"
8
9class JpegSlave : public SlaveInterface {
10public:
11 JpegSlave();
12 ~JpegSlave();
13
14 QString iconViewName( const QString& );
15 QString fullImageInfo( const QString& );
16 QPixmap pixmap( const QString&, int, int );
17};
18
19#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 @@
1/*
2 * GPLv2 Slave Main
3 */
4
5#include "gif_slave.h"
6#include "png_slave.h"
7#include "jpeg_slave.h"
8#include "thumbnailtool.h"
9#include "slavereciever.h"
10
11#include <qpixmap.h>
12#include <qcopchannel_qws.h>
13
14#include <qtopia/qpeapplication.h>
15
16int main( int argc, char* argv[] ) {
17 QPEApplication app( argc, argv );
18 SlaveReciever rec( 0 );
19
20 QCopChannel chan( "QPE/opie-eye_slave" );
21 QObject::connect(&chan,SIGNAL(received(const QCString&, const QByteArray&)),
22 &rec, SLOT(recieveAnswer(const QCString&,const QByteArray&)));
23 QObject::connect(qApp,SIGNAL(appMessage(const QCString&, const QByteArray&)),
24 &rec, SLOT(recieveAnswer(const QCString&,const QByteArray&)));
25
26 return app.exec();
27}
28
29#ifdef DEBUG_IT
30int main( int argc, char* argv[] ) {
31 QString str = QString::fromLatin1(argv[2] );
32 QApplication app( argc, argv );
33 GifSlave slave;
34 qWarning( str +" "+slave.iconViewName(str ) );
35 qWarning( str+" "+slave.fullImageInfo( str ) );
36
37 PNGSlave pngslave;
38 qWarning( str + " " + pngslave.iconViewName(str) );
39 qWarning( str + " " + pngslave.fullImageInfo(str));
40
41
42 JpegSlave jpgslave;
43 qWarning( str + " " + jpgslave.iconViewName(str ) );
44 qWarning( str + " " + jpgslave.fullImageInfo( str ) );
45//return app.exec();
46 QPixmap pix = ThumbNailTool::getThumb( str, 24, 24 );
47 if ( pix.isNull() ) {
48 qWarning( "No Thumbnail" );
49 pix = slave.pixmap(str, 24, 24);
50 }
51
52 if (!pix.isNull() ) {
53 qWarning( "Saving Thumbnail" );
54 ThumbNailTool::putThumb( str, pix, 24, 24 );
55 }
56
57}
58
59#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 @@
1#include "png_slave.h"
2
3#include "thumbnailtool.h"
4
5#include <qobject.h>
6#include <qfile.h>
7#include <qimage.h>
8#include <qpixmap.h>
9#include <qstring.h>
10
11/*
12 * GPLv2 from kfile plugin
13 */
14PHUNK_VIEW_INTERFACE( "PNG", PNGSlave );
15
16#define CHUNK_SIZE(data, index) ((data[index ]<<24) + (data[index+1]<<16) + \
17 (data[index+2]<< 8) + data[index+3])
18#define CHUNK_TYPE(data, index) &data[index+4]
19#define CHUNK_HEADER_SIZE 12
20#define CHUNK_DATA(data, index, offset) data[8+index+offset]
21
22/* TRANSLATOR QObject */
23
24// known translations for common png keys
25static const char* knownTranslations[]
26#ifdef __GNUC__
27__attribute__((unused))
28#endif
29 = {
30 QT_TR_NOOP("Title"),
31 QT_TR_NOOP("Author"),
32 QT_TR_NOOP("Description"),
33 QT_TR_NOOP("Copyright"),
34 QT_TR_NOOP("Creation Time"),
35 QT_TR_NOOP("Software"),
36 QT_TR_NOOP("Disclaimer"),
37 QT_TR_NOOP("Warning"),
38 QT_TR_NOOP("Source"),
39 QT_TR_NOOP("Comment")
40};
41
42// and for the colors
43static const char* colors[] = {
44 QT_TR_NOOP("Grayscale"),
45 QT_TR_NOOP("Unknown"),
46 QT_TR_NOOP("RGB"),
47 QT_TR_NOOP("Palette"),
48 QT_TR_NOOP("Grayscale/Alpha"),
49 QT_TR_NOOP("Unknown"),
50 QT_TR_NOOP("RGB/Alpha")
51};
52
53 // and compressions
54static const char* compressions[] =
55{
56 QT_TR_NOOP("Deflate")
57};
58
59 // interlaced modes
60static const char* interlaceModes[] = {
61 QT_TR_NOOP("None"),
62 QT_TR_NOOP("Adam7")
63};
64
65
66static void read_comment( const QString& inf,
67 bool readComments, QString& str ) {
68 QFile f(inf);
69 f.open(IO_ReadOnly);
70
71 if (f.size() < 26) return;
72 // the technical group will be read from the first 26 bytes. If the file
73 // is smaller, we can't even read this.
74
75 uchar *data = new uchar[f.size()+1];
76 f.readBlock(reinterpret_cast<char*>(data), f.size());
77 data[f.size()]='\n';
78
79 // find the start
80 if (data[0] == 137 && data[1] == 80 && data[2] == 78 && data[3] == 71 &&
81 data[4] == 13 && data[5] == 10 && data[6] == 26 && data[7] == 10 )
82 {
83 // ok
84 // the IHDR chunk should be the first
85 if (!strncmp((char*)&data[12], "IHDR", 4))
86 {
87 // we found it, get the dimensions
88 ulong x,y;
89 x = (data[16]<<24) + (data[17]<<16) + (data[18]<<8) + data[19];
90 y = (data[20]<<24) + (data[21]<<16) + (data[22]<<8) + data[23];
91
92 uint type = data[25];
93 uint bpp = data[24];
94
95 // the bpp are only per channel, so we need to multiply the with
96 // the channel count
97 switch (type)
98 {
99 case 0: break; // Grayscale
100 case 2: bpp *= 3; break; // RGB
101 case 3: break; // palette
102 case 4: bpp *= 2; break; // grayscale w. alpha
103 case 6: bpp *= 4; break; // RGBA
104
105 default: // we don't get any sensible value here
106 bpp = 0;
107 }
108
109
110 str = QObject::tr("Dimensions: %1x%2\n" ).arg(x).arg(y);
111 str += QObject::tr("Depth: %1\n" ).arg(bpp);
112 str += QObject::tr("ColorMode: %1\n").arg(
113 (type < sizeof(colors)/sizeof(colors[0]))
114 ? QObject::tr(colors[data[25]]) : QObject::tr("Unknown") );
115
116 str += QObject::tr("Compression: %1\n").arg(
117 (data[26] < sizeof(compressions)/sizeof(compressions[0]))
118 ? QObject::tr(compressions[data[26]]) : QObject::tr("Unknown") );
119
120 str += QObject::tr("InterlaceMode: %1\n" ).arg(
121 (data[28] < sizeof(interlaceModes)/sizeof(interlaceModes[0]))
122 ? QObject::tr(interlaceModes[data[28]]) : QObject::tr("Unknown"));
123 }
124
125 if ( readComments ) {
126 uint index = 8;
127 index += CHUNK_SIZE(data, index) + CHUNK_HEADER_SIZE;
128
129 while(index<f.size()-12)
130 {
131 while (index < f.size() - 12 &&
132 strncmp((char*)CHUNK_TYPE(data,index), "tEXt", 4))
133 {
134 if (!strncmp((char*)CHUNK_TYPE(data,index), "IEND", 4))
135 goto end;
136
137 index += CHUNK_SIZE(data, index) + CHUNK_HEADER_SIZE;
138 }
139
140 if (index < f.size() - 12)
141 {
142 // we found a tEXt field
143 // get the key, it's a null terminated string at the
144 // chunk start
145
146 uchar* key = &CHUNK_DATA(data,index,0);
147
148 int keysize=0;
149 for (;key[keysize]!=0; keysize++)
150 // look if we reached the end of the file
151 // (it might be corrupted)
152 if (8+index+keysize>=f.size())
153 goto end;
154
155 // the text comes after the key, but isn't null terminated
156 uchar* text = &CHUNK_DATA(data,index, keysize+1);
157 uint textsize = CHUNK_SIZE(data, index)-keysize-1;
158
159 // security check, also considering overflow wraparound from the addition --
160 // we may endup with a /smaller/ index if we wrap all the way around
161 uint firstIndex = (uint)(text - data);
162 uint onePastLastIndex = firstIndex + textsize;
163
164 if ( onePastLastIndex > f.size() || onePastLastIndex <= firstIndex)
165 goto end;
166
167 QByteArray arr(textsize);
168 arr = QByteArray(textsize).duplicate((const char*)text,
169 textsize);
170 str += QObject::tr(
171 QString(reinterpret_cast<char*>(key)),
172 QString(arr) );
173
174 index += CHUNK_SIZE(data, index) + CHUNK_HEADER_SIZE;
175 }
176 }
177 }
178 }
179end:
180 delete[] data;
181
182}
183
184
185PNGSlave::PNGSlave()
186 : SlaveInterface("png")
187{
188}
189PNGSlave::~PNGSlave() {
190}
191QString PNGSlave::iconViewName( const QString& path) {
192 QString str;
193 read_comment( path, false, str );
194 return str;
195}
196
197QString PNGSlave::fullImageInfo( const QString& path) {
198 QString str;
199 read_comment( path, true, str );
200 return str;
201}
202
203
204QPixmap PNGSlave::pixmap( const QString& path, int width, int height) {
205 QImage img; img.load( path );
206 if ( img.isNull() )
207 return QPixmap();
208 else
209 return ThumbNailTool::scaleImage( img, width,height );
210}
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 @@
1/*
2 * GPLv2 zecke@handhelds.org
3 */
4#ifndef PNG_SLAVE_IMPL_H
5#define PNG_SLAVE_IMPL_H
6
7#include "slaveiface.h"
8
9class QString;
10class QPixmap;
11class PNGSlave : public SlaveInterface {
12public:
13 PNGSlave();
14 ~PNGSlave();
15
16 QString iconViewName( const QString& );
17 QString fullImageInfo( const QString& );
18 QPixmap pixmap( const QString&, int, int );
19};
20
21#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 @@
1CONFIG += qte
2TEMPLATE = app
3TARGET = opie-eye_slave
4DESTDIR = $(OPIEDIR)/bin
5
6HEADERS = gif_slave.h slaveiface.h slavereciever.h \
7 thumbnailtool.h png_slave.h jpeg_slave.h \
8 ../iface/slaveiface.h
9SOURCES = main.cpp gif_slave.cpp slavereciever.cpp \
10 slaveiface.cpp thumbnailtool.cpp png_slave.cpp \
11 jpeg_slave.cpp
12
13INCLUDEPATH += $(OPIEDIR)/include ../
14DEPENDSPATH += $(OPIEDIR)/include
15
16LIBS += -lqpe
17
18include ( $(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 @@
1/*
2 * GPLv2 zecke@handhelds.org
3 */
4
5#include "slaveiface.h"
6
7static SlaveMap* _slaveMap = 0;
8SlaveMap* slaveMap() {
9 if ( !_slaveMap )
10 _slaveMap = new SlaveMap;
11 return _slaveMap;
12}
13
14SlaveInterface::SlaveInterface( const QStringList& image )
15 : m_list( image )
16{
17
18}
19
20SlaveInterface::~SlaveInterface() {
21
22}
23
24QStringList SlaveInterface::imageFormats()const {
25 return m_list;
26}
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 @@
1/*
2 * GPLv2
3 */
4
5#ifndef P_SLAVE_INTER_FACE_H
6#define P_SLAVE_INTER_FACE_H
7
8#include <qfileinfo.h>
9#include <qstringlist.h>
10#include <qmap.h>
11
12/**
13 * @short The slave worker Interface for generating Preview + Image Info + Full
14 * IMage Info
15 */
16class QPixmap;
17class SlaveInterface {
18public:
19 SlaveInterface(const QStringList& imageformats);
20 virtual ~SlaveInterface();
21
22 QStringList imageFormats()const;
23 bool supports( const QString& )const;
24 virtual QString iconViewName(const QString&) = 0;
25 virtual QString fullImageInfo(const QString& )= 0;
26 virtual QPixmap pixmap( const QString&, int width, int height ) = 0;
27private:
28 QStringList m_list;
29};
30
31inline bool SlaveInterface::supports( const QString& str)const {
32 return m_list.contains( QFileInfo( str ).extension(false) );
33}
34
35typedef SlaveInterface* (*phunkSlaveCreateFunc )();
36typedef QMap<QString,phunkSlaveCreateFunc> SlaveMap;
37
38typedef QMap<QString, SlaveInterface*> SlaveObjects;
39
40SlaveMap* slaveMap();
41SlaveObjects* slaveObjects();
42
43
44
45#define PHUNK_VIEW_INTERFACE( NAME, IMPL ) \
46 static SlaveInterface *create_ ## IMPL() { \
47 return new IMPL(); \
48 } \
49 static SlaveMap::Iterator dummy_ ## IMPL = slaveMap()->insert( NAME, create_ ## IMPL );
50
51
52
53#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 @@
1/*
2 * GPLv2 zecke@handhelds.org
3 */
4
5#include "slavereciever.h"
6#include "slaveiface.h"
7
8#include <qpe/qcopenvelope_qws.h>
9#include <qpe/qpeapplication.h>
10
11#include <qtimer.h>
12
13static SlaveObjects* _slaveObj = 0;
14
15QDataStream & operator << (QDataStream & str, bool b)
16{
17 str << Q_INT8(b);
18 return str;
19}
20
21QDataStream & operator >> (QDataStream & str, bool & b)
22{
23 Q_INT8 l;
24 str >> l;
25 b = bool(l);
26 return str;
27}
28
29
30
31QDataStream &operator<<( QDataStream& s, const PixmapInfo& inf) {
32 return s << inf.file << inf.pixmap << inf.width << inf.height;
33}
34QDataStream &operator>>( QDataStream& s, PixmapInfo& inf ) {
35 s >> inf.file >> inf.pixmap >> inf.width >> inf.height;
36 return s;
37}
38QDataStream &operator<<( QDataStream& s, const ImageInfo& i) {
39 return s << i.kind << i.file << i.info;
40}
41QDataStream &operator>>( QDataStream& s, ImageInfo& i ) {
42 s >> i.kind >> i.file >> i.info;
43 return s;
44}
45
46
47
48SlaveObjects* slaveObjects() {
49 if ( !_slaveObj )
50 _slaveObj = new SlaveObjects;
51 return _slaveObj;
52}
53
54SlaveReciever::SlaveReciever( QObject* par)
55 : QObject( par )
56{
57 m_inf = new QTimer(this);
58 connect(m_inf,SIGNAL(timeout()),
59 this, SLOT(slotImageInfo()));
60 m_pix = new QTimer(this);
61 connect(m_pix,SIGNAL(timeout()),
62 this, SLOT(slotThumbNail()));
63
64 m_out = new QTimer(this);
65 connect(m_out,SIGNAL(timeout()),
66 this, SLOT(slotSend()));
67
68 SlaveObjects *obj = slaveObjects(); // won't be changed
69 SlaveMap::Iterator it;
70 SlaveMap* map = slaveMap(); // SlaveMap won't be changed during execution!!!
71 for(it = map->begin(); it != map->end(); ++it ) {
72 obj->insert( it.key(), (*it.data())() );
73 }
74}
75
76SlaveReciever::~SlaveReciever() {
77}
78
79void SlaveReciever::recieveAnswer( const QCString& string, const QByteArray& ar) {
80 qWarning( "String is %s", string.data() );
81 QDataStream stream(ar, IO_ReadOnly );
82 QStringList lst;
83 static ImageInfo inf;
84 static PixmapInfo pix;
85
86 if ( string == "thumbInfo(QString)" ) {
87 stream >> inf.file;
88 m_inList.append(inf);
89 }else if ( string == "thumbInfos(QStringList)" ) {
90 stream >> lst;
91 for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) {
92 qWarning( "Adding thumbinfo for file "+ *it );
93 inf.file = (*it);
94 m_inList.append(inf);
95 }
96 }else if ( string == "fullInfo(QString)" ) {
97 inf.kind = true;
98 stream >> inf.file;
99 m_inList.append(inf);
100 }else if ( string == "fullInfos(QStringList)" ) {
101 stream >> lst;
102 for(QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) {
103 qWarning( "Adding fullInfo for"+ *it );
104 inf.file = (*it);
105 inf.kind = true;
106 m_inList.append(inf);
107 }
108 }else if ( string == "pixmapInfo(QString,int,int)" ) {
109 stream >> pix.file >> pix.width >> pix.height;
110 m_inPix.append(pix);
111 }else if ( string == "pixmapInfos(PixmapInfos)" ) {
112 PixmapList list;
113 stream >> list;
114 for(PixmapList::Iterator it = list.begin(); it != list.end(); ++it ) {
115 qWarning( "Got %d %d " + (*it).file, (*it).width , (*it).height );
116 m_inPix.append(*it);
117 }
118 }
119
120 if (!m_inf->isActive() && !m_inList.isEmpty() )
121 m_inf->start(5);
122
123 if (!m_pix->isActive() && !m_inPix.isEmpty() )
124 m_pix->start(5);
125
126 QPEApplication::setKeepRunning();
127
128}
129
130PixmapList SlaveReciever::outPix()const {
131 return m_outPix;
132}
133
134StringList SlaveReciever::outInf()const{
135 return m_outList;
136}
137
138void SlaveReciever::slotImageInfo() {
139 ImageInfo inf = m_inList.first();
140 m_inList.remove( inf );
141
142 static SlaveObjects::Iterator it;
143 static SlaveObjects* map = slaveObjects(); // SlaveMap won't be changed during execution!!!
144 for(it = map->begin(); it != map->end(); ++it ) {
145 if( (*it)->supports(inf.file ) ) {
146 /* full image info */
147 if (inf.kind )
148 inf.info = (*it)->fullImageInfo( inf.file );
149 else
150 inf.info = (*it)->iconViewName( inf.file );
151 m_outList.append( inf );
152 break;
153 }
154 }
155
156 if (m_inList.isEmpty() )
157 m_inf->stop();
158 if (!m_out->isActive() && !m_outList.isEmpty() )
159 m_out->start( 100 );
160}
161
162void SlaveReciever::slotThumbNail() {
163 PixmapInfo inf = m_inPix.first();
164 m_inPix.remove( inf );
165
166 static SlaveObjects::Iterator it;
167 static SlaveObjects* map = slaveObjects(); // SlaveMap won't be changed during execution!!!
168 for(it = map->begin(); it != map->end(); ++it ) {
169 SlaveInterface* iface = it.data();
170 if( iface->supports(inf.file ) ) {
171 /* pixmap */
172 qWarning( "Asking for thumbNail in size %d %d for "+inf.file, inf.width, inf.height );
173 inf.pixmap = iface->pixmap(inf.file, 64, 64);
174 m_outPix.append( inf );
175 break;
176 }
177 }
178
179
180
181 if(m_inPix.isEmpty() )
182 m_pix->stop();
183 if(!m_out->isActive() && !m_outPix.isEmpty() )
184 m_out->start(100);
185}
186
187void SlaveReciever::slotSend() {
188
189 m_out->stop();
190
191 qWarning( "Sending %d %d", outPix().count(), outInf().count() );
192 /* queue it and send */
193 /* if this ever gets a service introduce request queues
194 * so we can differinatate between different clients
195 */
196 if (! m_outPix.isEmpty() ) {
197 QCopEnvelope answer("QPE/opie-eye", "pixmapsHandled(PixmapList)" );
198 answer << outPix();
199 for ( PixmapList::Iterator it = m_outPix.begin();it!=m_outPix.end();++it ) {
200 qWarning( "Sending out %s %d %d", (*it).file.latin1(), (*it).width, (*it).height );
201 }
202 }
203 if ( !m_outList.isEmpty() ) {
204 QCopEnvelope answer("QPE/opie-eye", "pixmapsHandled(StringList)" );
205 answer << outInf();
206 for ( StringList::Iterator it = m_outList.begin();it!=m_outList.end();++it ) {
207 qWarning( "Sending out2 " + (*it).file );
208 }
209 }
210
211 m_outList.clear();
212 m_outPix.clear();
213}
214
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 @@
1/*
2 * GPLv2
3 */
4
5
6#ifndef SLAVE_RECEIVER_H
7#define SLAVE_RECEIVER_H
8
9/**
10 * Receive Requests
11 */
12
13#include <iface/slaveiface.h>
14
15#include <qobject.h>
16#include <qdatastream.h>
17#include <qstringlist.h>
18#include <qvaluelist.h>
19#include <qpixmap.h>
20
21
22
23typedef QValueList<PixmapInfo> PixmapList;
24typedef QValueList<ImageInfo> StringList;
25
26class QTimer;
27class QSocket;
28class SlaveReciever : public QObject {
29 Q_OBJECT
30
31 friend QDataStream &operator<<( QDataStream&, const PixmapInfo& );
32 friend QDataStream &operator>>( QDataStream&, PixmapInfo& );
33 friend QDataStream &operator<<( QDataStream&, const ImageInfo& );
34 friend QDataStream &operator>>( QDataStream&, ImageInfo );
35public:
36
37 enum Job { ImageInfoJob, FullImageInfoJob, ThumbNailJob };
38 SlaveReciever( QObject* parent );
39 ~SlaveReciever();
40
41public slots:
42 void recieveAnswer( const QCString&, const QByteArray& );
43public:
44 PixmapList outPix()const;
45 StringList outInf()const;
46
47private slots:
48 void slotSend();
49 void slotImageInfo();
50 void slotThumbNail();
51private:
52 QTimer *m_inf, *m_pix, *m_out;
53 StringList m_inList, m_outList;
54 PixmapList m_inPix, m_outPix;
55};
56
57
58#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 @@
1#include "thumbnailtool.h"
2
3#include <qfileinfo.h>
4#include <qdir.h>
5#include <qimage.h>
6#include <qpixmap.h>
7#include <qstring.h>
8
9static bool makeThumbDir( const QFileInfo& inf, bool make = false) {
10 QDir dir( inf.dirPath()+ "/.opie-eye" );
11 if ( !dir.exists() )
12 if ( make )
13 return dir.mkdir(QString::null);
14 else
15 return false;
16 return true;
17}
18
19
20/*
21 * check if the Opie opie-eye dir exists
22 * check if a thumbnail exists
23 * load the thumbnail
24 * /foo/bar/imagefoo.gif
25 * check for a png in /foo/bar/.opie-eye/%dx%d-imagefoo.gif
26 */
27QPixmap ThumbNailTool::getThumb( const QString& path, int width, int height ) {
28 QFileInfo inf( path );
29 qWarning( "Get Thumb" );
30 if ( !makeThumbDir( inf ) ) {
31 QPixmap pix;
32 return pix;
33 }
34 QString str = QString( "/.opie-eye/%1x%2-%3" ).arg( width ).arg( height ).arg( inf.fileName() );
35 qWarning( inf.dirPath()+str );
36 return QPixmap( inf.dirPath()+str,"PNG" );
37
38}
39
40void ThumbNailTool::putThumb( const QString& path, const QPixmap& pix, int width, int height ) {
41 QFileInfo inf( path );
42 makeThumbDir( inf, true );
43 QString str = QString( "/.opie-eye/%1x%2-%3" ).arg( width ).arg( height ).arg( inf.fileName() );
44 qWarning( inf.dirPath()+str );
45 pix.save( inf.dirPath()+str, "PNG" );
46}
47
48
49QPixmap ThumbNailTool::scaleImage( QImage& img, int w, int h ) {
50 double hs = (double)h / (double)img.height() ;
51 double ws = (double)w / (double)img.width() ;
52 double scaleFactor = (hs > ws) ? ws : hs;
53 int smoothW = (int)(scaleFactor * img.width());
54 int smoothH = (int)(scaleFactor * img.height());
55 QPixmap pixmap;
56 if ( img.width() <= w && img.height() <= h )
57 pixmap.convertFromImage( img );
58 else
59 pixmap.convertFromImage( img.smoothScale( smoothW, smoothH) );
60 return pixmap;
61}
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 @@
1/*
2 * GPLv2
3 */
4
5#ifndef THUMB_NAIL_TOOL_H
6#define THUMB_NAIL_TOOL_H
7class QString;
8class QPixmap;
9class QImage;
10
11struct ThumbNailTool {
12 static QPixmap scaleImage( QImage&, int width, int height );
13/* get one isInvalid() if non found */
14 static QPixmap getThumb( const QString&, int width, int height );
15/* put one */
16 static void putThumb( const QString&, const QPixmap&, int width, int heigh );
17};
18
19#endif