summaryrefslogtreecommitdiff
path: root/libopie2/opiemm
Unidiff
Diffstat (limited to 'libopie2/opiemm') (more/less context) (ignore whitespace changes)
-rw-r--r--libopie2/opiemm/oimagescrollview.cpp14
-rw-r--r--libopie2/opiemm/opieexif.cpp1195
-rw-r--r--libopie2/opiemm/opieexif.h139
-rw-r--r--libopie2/opiemm/opiemm.pro4
4 files changed, 1346 insertions, 6 deletions
diff --git a/libopie2/opiemm/oimagescrollview.cpp b/libopie2/opiemm/oimagescrollview.cpp
index 56be10b..37a1ad5 100644
--- a/libopie2/opiemm/oimagescrollview.cpp
+++ b/libopie2/opiemm/oimagescrollview.cpp
@@ -1,563 +1,569 @@
1#include "oimagescrollview.h" 1#include "oimagescrollview.h"
2 2
3#include <opie2/oimagezoomer.h> 3#include <opie2/oimagezoomer.h>
4#include <opie2/odebug.h> 4#include <opie2/odebug.h>
5#include <opie2/oapplication.h> 5#include <opie2/oapplication.h>
6#include <opie2/owait.h> 6#include <opie2/owait.h>
7#include <opie2/opieexif.h>
7 8
8#include <qimage.h> 9#include <qimage.h>
9#include <qlayout.h> 10#include <qlayout.h>
10 11
11/* for usage with the bitset */ 12/* for usage with the bitset */
12#define AUTO_SCALE 0 13#define AUTO_SCALE 0
13#define AUTO_ROTATE 1 14#define AUTO_ROTATE 1
14#define SHOW_ZOOMER 2 15#define SHOW_ZOOMER 2
15#define FIRST_RESIZE_DONE 3 16#define FIRST_RESIZE_DONE 3
16#define IMAGE_IS_JPEG 4 17#define IMAGE_IS_JPEG 4
17#define IMAGE_SCALED_LOADED 5 18#define IMAGE_SCALED_LOADED 5
18 19
19#define SCROLLVIEW_BITSET_SIZE 6 20#define SCROLLVIEW_BITSET_SIZE 6
20 21
21namespace Opie { 22namespace Opie {
22namespace MM { 23namespace MM {
23OImageScrollView::OImageScrollView( QWidget* parent, const char* name, WFlags f ) 24OImageScrollView::OImageScrollView( QWidget* parent, const char* name, WFlags f )
24 :QScrollView(parent,name,f|Qt::WRepaintNoErase ),_image_data(),_original_data(), 25 :QScrollView(parent,name,f|Qt::WRepaintNoErase ),_image_data(),_original_data(),
25 m_states(SCROLLVIEW_BITSET_SIZE),m_lastName("") 26 m_states(SCROLLVIEW_BITSET_SIZE),m_lastName("")
26{ 27{
27 _zoomer = 0; 28 _zoomer = 0;
28 m_states[AUTO_SCALE]=true; 29 m_states[AUTO_SCALE]=true;
29 m_states[AUTO_ROTATE]=true; 30 m_states[AUTO_ROTATE]=true;
30 m_states[FIRST_RESIZE_DONE]=false; 31 m_states[FIRST_RESIZE_DONE]=false;
31 m_states[IMAGE_IS_JPEG]=false; 32 m_states[IMAGE_IS_JPEG]=false;
32 m_states[IMAGE_SCALED_LOADED]=false; 33 m_states[IMAGE_SCALED_LOADED]=false;
33 m_states[SHOW_ZOOMER]=true; 34 m_states[SHOW_ZOOMER]=true;
34 init(); 35 init();
35} 36}
36 37
37OImageScrollView::OImageScrollView (const QImage&img, QWidget * parent, const char * name, WFlags f,bool always_scale,bool rfit) 38OImageScrollView::OImageScrollView (const QImage&img, QWidget * parent, const char * name, WFlags f,bool always_scale,bool rfit)
38 :QScrollView(parent,name,f|Qt::WRepaintNoErase),_image_data(),_original_data(img), 39 :QScrollView(parent,name,f|Qt::WRepaintNoErase),_image_data(),_original_data(img),
39 m_states(SCROLLVIEW_BITSET_SIZE),m_lastName("") 40 m_states(SCROLLVIEW_BITSET_SIZE),m_lastName("")
40{ 41{
41 _zoomer = 0; 42 _zoomer = 0;
42 m_states[AUTO_SCALE]=always_scale; 43 m_states[AUTO_SCALE]=always_scale;
43 m_states[AUTO_ROTATE]=rfit; 44 m_states[AUTO_ROTATE]=rfit;
44 m_states[FIRST_RESIZE_DONE]=false; 45 m_states[FIRST_RESIZE_DONE]=false;
45 m_states[IMAGE_IS_JPEG]=false; 46 m_states[IMAGE_IS_JPEG]=false;
46 m_states[IMAGE_SCALED_LOADED]=false; 47 m_states[IMAGE_SCALED_LOADED]=false;
47 m_states[SHOW_ZOOMER]=true; 48 m_states[SHOW_ZOOMER]=true;
48 _original_data.convertDepth(QPixmap::defaultDepth()); 49 _original_data.convertDepth(QPixmap::defaultDepth());
49 _original_data.setAlphaBuffer(false); 50 _original_data.setAlphaBuffer(false);
50 init(); 51 init();
51} 52}
52 53
53OImageScrollView::OImageScrollView (const QString&img, QWidget * parent, const char * name, WFlags f,bool always_scale,bool rfit) 54OImageScrollView::OImageScrollView (const QString&img, QWidget * parent, const char * name, WFlags f,bool always_scale,bool rfit)
54 :QScrollView(parent,name,f|Qt::WRepaintNoErase),_image_data(),_original_data(),m_states(SCROLLVIEW_BITSET_SIZE),m_lastName("") 55 :QScrollView(parent,name,f|Qt::WRepaintNoErase),_image_data(),_original_data(),m_states(SCROLLVIEW_BITSET_SIZE),m_lastName("")
55{ 56{
56 _zoomer = 0; 57 _zoomer = 0;
57 m_states.resize(SCROLLVIEW_BITSET_SIZE); 58 m_states.resize(SCROLLVIEW_BITSET_SIZE);
58 m_states[AUTO_SCALE]=always_scale; 59 m_states[AUTO_SCALE]=always_scale;
59 m_states[AUTO_ROTATE]=rfit; 60 m_states[AUTO_ROTATE]=rfit;
60 m_states[FIRST_RESIZE_DONE]=false; 61 m_states[FIRST_RESIZE_DONE]=false;
61 m_states[IMAGE_IS_JPEG]=false; 62 m_states[IMAGE_IS_JPEG]=false;
62 m_states[IMAGE_SCALED_LOADED]=false; 63 m_states[IMAGE_SCALED_LOADED]=false;
63 m_states[SHOW_ZOOMER]=true; 64 m_states[SHOW_ZOOMER]=true;
64 init(); 65 init();
65 setImage(img); 66 setImage(img);
66} 67}
67 68
68void OImageScrollView::setImage(const QImage&img) 69void OImageScrollView::setImage(const QImage&img)
69{ 70{
70 _image_data = QImage(); 71 _image_data = QImage();
71 _original_data=img; 72 _original_data=img;
72 _original_data.convertDepth(QPixmap::defaultDepth()); 73 _original_data.convertDepth(QPixmap::defaultDepth());
73 _original_data.setAlphaBuffer(false); 74 _original_data.setAlphaBuffer(false);
74 m_lastName = ""; 75 m_lastName = "";
75 setImageIsJpeg(false); 76 setImageIsJpeg(false);
76 setImageScaledLoaded(false); 77 setImageScaledLoaded(false);
77 if (FirstResizeDone()) { 78 if (FirstResizeDone()) {
78 generateImage(); 79 generateImage();
79 } 80 }
80} 81}
81 82
82void OImageScrollView::loadJpeg(bool interncall) 83void OImageScrollView::loadJpeg(bool interncall)
83{ 84{
84 if (m_lastName.isEmpty()) return; 85 if (m_lastName.isEmpty()) return;
85 QImageIO iio( m_lastName, 0l ); 86 QImageIO iio( m_lastName, 0l );
86 QString param; 87 QString param;
87 bool real_load = false; 88 bool real_load = false;
88 if (AutoScale()) { 89 if (AutoScale()) {
89 if (!interncall) { 90 if (!interncall) {
91 ExifData xf;
92 bool scanned = xf.scan(m_lastName);
90 int wid, hei; 93 int wid, hei;
91 wid = QApplication::desktop()->width(); 94 wid = QApplication::desktop()->width();
92 hei = QApplication::desktop()->height(); 95 hei = QApplication::desktop()->height();
93 if (hei>wid) { 96 if (hei>wid) {
94 wid = hei; 97 wid = hei;
95 } else { 98 } else {
96 hei = wid; 99 hei = wid;
97 } 100 }
98 param = QString( "Fast Shrink( 3 ) Scale( %1, %2, ScaleMin)" ).arg( wid ).arg( hei ); 101 if ( (scanned && (wid<xf.getWidth()||hei<xf.getHeight()))||!scanned ) {
99 odebug << "Load jpeg scaled \"" << param << "\"" << oendl; 102 param = QString( "Fast Shrink( 3 ) Scale( %1, %2, ScaleMin)" ).arg( wid ).arg( hei );
100 iio.setParameters(param.latin1()); 103 odebug << "Load jpeg scaled \"" << param << "\"" << oendl;
101 setImageScaledLoaded(true); 104 iio.setParameters(param.latin1());
105 setImageScaledLoaded(true);
106 }
107
102 real_load = true; 108 real_load = true;
103 } 109 }
104 } else { 110 } else {
105 if (ImageScaledLoaded()||!interncall) { 111 if (ImageScaledLoaded()||!interncall) {
106 odebug << "Load jpeg unscaled" << oendl; 112 odebug << "Load jpeg unscaled" << oendl;
107 real_load = true; 113 real_load = true;
108 } 114 }
109 setImageScaledLoaded(false); 115 setImageScaledLoaded(false);
110 } 116 }
111 if (real_load) { 117 if (real_load) {
112 _original_data = iio.read() ? iio.image() : QImage(); 118 _original_data = iio.read() ? iio.image() : QImage();
113 } 119 }
114} 120}
115 121
116void OImageScrollView::setImage( const QString& path ) { 122void OImageScrollView::setImage( const QString& path ) {
117 odebug << "load new image " << oendl; 123 odebug << "load new image " << oendl;
118 if (m_lastName == path) return; 124 if (m_lastName == path) return;
119 m_lastName = path; 125 m_lastName = path;
120 _original_data = QImage(); 126 _original_data = QImage();
121 QString itype = QImage::imageFormat(m_lastName); 127 QString itype = QImage::imageFormat(m_lastName);
122 odebug << "Image type = " << itype << oendl; 128 odebug << "Image type = " << itype << oendl;
123 if (itype == "JPEG") { 129 if (itype == "JPEG") {
124 setImageIsJpeg(true); 130 setImageIsJpeg(true);
125 loadJpeg(); 131 loadJpeg();
126 } else { 132 } else {
127 setImageIsJpeg(false); 133 setImageIsJpeg(false);
128 _original_data.load(path); 134 _original_data.load(path);
129 _original_data.convertDepth(QPixmap::defaultDepth()); 135 _original_data.convertDepth(QPixmap::defaultDepth());
130 _original_data.setAlphaBuffer(false); 136 _original_data.setAlphaBuffer(false);
131 } 137 }
132 _image_data = QImage(); 138 _image_data = QImage();
133 if (FirstResizeDone()) { 139 if (FirstResizeDone()) {
134 generateImage(); 140 generateImage();
135 if (isVisible()) viewport()->repaint(true); 141 if (isVisible()) viewport()->repaint(true);
136 } 142 }
137} 143}
138 144
139/* should be called every time the QImage changed it content */ 145/* should be called every time the QImage changed it content */
140void OImageScrollView::init() 146void OImageScrollView::init()
141{ 147{
142 odebug << "init " << oendl; 148 odebug << "init " << oendl;
143 149
144 /* 150 /*
145 * create the zoomer 151 * create the zoomer
146 * and connect ther various signals 152 * and connect ther various signals
147 */ 153 */
148 _zoomer = new Opie::MM::OImageZoomer( this, "The Zoomer" ); 154 _zoomer = new Opie::MM::OImageZoomer( this, "The Zoomer" );
149 connect(_zoomer, SIGNAL( zoomAreaRel(int,int)), 155 connect(_zoomer, SIGNAL( zoomAreaRel(int,int)),
150 this, SLOT(scrollBy(int,int)) ); 156 this, SLOT(scrollBy(int,int)) );
151 connect(_zoomer, SIGNAL( zoomArea(int,int)), 157 connect(_zoomer, SIGNAL( zoomArea(int,int)),
152 this, SLOT(center(int,int)) ); 158 this, SLOT(center(int,int)) );
153 connect(this,SIGNAL(contentsMoving(int,int)), 159 connect(this,SIGNAL(contentsMoving(int,int)),
154 _zoomer, (SLOT(setVisiblePoint(int,int))) ); 160 _zoomer, (SLOT(setVisiblePoint(int,int))) );
155 connect(this,SIGNAL(imageSizeChanged(const QSize&)), 161 connect(this,SIGNAL(imageSizeChanged(const QSize&)),
156 _zoomer, SLOT(setImageSize(const QSize&)) ); 162 _zoomer, SLOT(setImageSize(const QSize&)) );
157 connect(this,SIGNAL(viewportSizeChanged(const QSize&)), 163 connect(this,SIGNAL(viewportSizeChanged(const QSize&)),
158 _zoomer, SLOT(setViewPortSize(const QSize&)) ); 164 _zoomer, SLOT(setViewPortSize(const QSize&)) );
159 165
160 setBackgroundColor(white); 166 setBackgroundColor(white);
161 setFocusPolicy(QWidget::StrongFocus); 167 setFocusPolicy(QWidget::StrongFocus);
162 setImageScaledLoaded(false); 168 setImageScaledLoaded(false);
163 setImageIsJpeg(false); 169 setImageIsJpeg(false);
164 if (FirstResizeDone()) { 170 if (FirstResizeDone()) {
165 m_last_rot = Rotate0; 171 m_last_rot = Rotate0;
166 generateImage(); 172 generateImage();
167 } else if (_original_data.size().isValid()) { 173 } else if (_original_data.size().isValid()) {
168 if (image_fit_into(_original_data.size()) || !ShowZoomer()) _zoomer->hide(); 174 if (image_fit_into(_original_data.size()) || !ShowZoomer()) _zoomer->hide();
169 resizeContents(_original_data.width(),_original_data.height()); 175 resizeContents(_original_data.width(),_original_data.height());
170 } 176 }
171} 177}
172 178
173void OImageScrollView::setAutoRotate(bool how) 179void OImageScrollView::setAutoRotate(bool how)
174{ 180{
175 /* to avoid double repaints */ 181 /* to avoid double repaints */
176 if (AutoRotate() != how) { 182 if (AutoRotate() != how) {
177 m_states.setBit(AUTO_ROTATE,how); 183 m_states.setBit(AUTO_ROTATE,how);
178 _image_data = QImage(); 184 _image_data = QImage();
179 generateImage(); 185 generateImage();
180 } 186 }
181} 187}
182 188
183bool OImageScrollView::AutoRotate()const 189bool OImageScrollView::AutoRotate()const
184{ 190{
185 return m_states.testBit(AUTO_ROTATE); 191 return m_states.testBit(AUTO_ROTATE);
186} 192}
187 193
188void OImageScrollView::setAutoScaleRotate(bool scale, bool rotate) 194void OImageScrollView::setAutoScaleRotate(bool scale, bool rotate)
189{ 195{
190 m_states.setBit(AUTO_ROTATE,rotate); 196 m_states.setBit(AUTO_ROTATE,rotate);
191 setAutoScale(scale); 197 setAutoScale(scale);
192} 198}
193 199
194void OImageScrollView::setAutoScale(bool how) 200void OImageScrollView::setAutoScale(bool how)
195{ 201{
196 m_states.setBit(AUTO_SCALE,how); 202 m_states.setBit(AUTO_SCALE,how);
197 _image_data = QImage(); 203 _image_data = QImage();
198 if (ImageIsJpeg() && how == false && ImageScaledLoaded()==true) { 204 if (ImageIsJpeg() && how == false && ImageScaledLoaded()==true) {
199 loadJpeg(true); 205 loadJpeg(true);
200 } 206 }
201 generateImage(); 207 generateImage();
202} 208}
203 209
204bool OImageScrollView::AutoScale()const 210bool OImageScrollView::AutoScale()const
205{ 211{
206 return m_states.testBit(AUTO_SCALE); 212 return m_states.testBit(AUTO_SCALE);
207} 213}
208 214
209OImageScrollView::~OImageScrollView() 215OImageScrollView::~OImageScrollView()
210{ 216{
211} 217}
212 218
213void OImageScrollView::rescaleImage(int w, int h) 219void OImageScrollView::rescaleImage(int w, int h)
214{ 220{
215 if (_image_data.width()==w && _image_data.height()==h) { 221 if (_image_data.width()==w && _image_data.height()==h) {
216 return; 222 return;
217 } 223 }
218 double hs = (double)h / (double)_image_data.height() ; 224 double hs = (double)h / (double)_image_data.height() ;
219 double ws = (double)w / (double)_image_data.width() ; 225 double ws = (double)w / (double)_image_data.width() ;
220 double scaleFactor = (hs > ws) ? ws : hs; 226 double scaleFactor = (hs > ws) ? ws : hs;
221 int smoothW = (int)(scaleFactor * _image_data.width()); 227 int smoothW = (int)(scaleFactor * _image_data.width());
222 int smoothH = (int)(scaleFactor * _image_data.height()); 228 int smoothH = (int)(scaleFactor * _image_data.height());
223 _image_data = _image_data.smoothScale(smoothW,smoothH); 229 _image_data = _image_data.smoothScale(smoothW,smoothH);
224} 230}
225 231
226void OImageScrollView::rotate_into_data(Rotation r) 232void OImageScrollView::rotate_into_data(Rotation r)
227{ 233{
228 /* realy - we must do this that way, 'cause when acting direct on _image_data the app will 234 /* realy - we must do this that way, 'cause when acting direct on _image_data the app will
229 segfault :( */ 235 segfault :( */
230 QImage dest; 236 QImage dest;
231 int x, y; 237 int x, y;
232 if ( _original_data.depth() > 8 ) 238 if ( _original_data.depth() > 8 )
233 { 239 {
234 unsigned int *srcData, *destData; 240 unsigned int *srcData, *destData;
235 switch ( r ) 241 switch ( r )
236 { 242 {
237 case Rotate90: 243 case Rotate90:
238 dest.create(_original_data.height(), _original_data.width(), _original_data.depth()); 244 dest.create(_original_data.height(), _original_data.width(), _original_data.depth());
239 for ( y=0; y < _original_data.height(); ++y ) 245 for ( y=0; y < _original_data.height(); ++y )
240 { 246 {
241 srcData = (unsigned int *)_original_data.scanLine(y); 247 srcData = (unsigned int *)_original_data.scanLine(y);
242 for ( x=0; x < _original_data.width(); ++x ) 248 for ( x=0; x < _original_data.width(); ++x )
243 { 249 {
244 destData = (unsigned int *)dest.scanLine(x); 250 destData = (unsigned int *)dest.scanLine(x);
245 destData[_original_data.height()-y-1] = srcData[x]; 251 destData[_original_data.height()-y-1] = srcData[x];
246 } 252 }
247 } 253 }
248 break; 254 break;
249 case Rotate180: 255 case Rotate180:
250 dest.create(_original_data.width(), _original_data.height(), _original_data.depth()); 256 dest.create(_original_data.width(), _original_data.height(), _original_data.depth());
251 for ( y=0; y < _original_data.height(); ++y ) 257 for ( y=0; y < _original_data.height(); ++y )
252 { 258 {
253 srcData = (unsigned int *)_original_data.scanLine(y); 259 srcData = (unsigned int *)_original_data.scanLine(y);
254 destData = (unsigned int *)dest.scanLine(_original_data.height()-y-1); 260 destData = (unsigned int *)dest.scanLine(_original_data.height()-y-1);
255 for ( x=0; x < _original_data.width(); ++x ) 261 for ( x=0; x < _original_data.width(); ++x )
256 destData[_original_data.width()-x-1] = srcData[x]; 262 destData[_original_data.width()-x-1] = srcData[x];
257 } 263 }
258 break; 264 break;
259 case Rotate270: 265 case Rotate270:
260 dest.create(_original_data.height(), _original_data.width(), _original_data.depth()); 266 dest.create(_original_data.height(), _original_data.width(), _original_data.depth());
261 for ( y=0; y < _original_data.height(); ++y ) 267 for ( y=0; y < _original_data.height(); ++y )
262 { 268 {
263 srcData = (unsigned int *)_original_data.scanLine(y); 269 srcData = (unsigned int *)_original_data.scanLine(y);
264 for ( x=0; x < _original_data.width(); ++x ) 270 for ( x=0; x < _original_data.width(); ++x )
265 { 271 {
266 destData = (unsigned int *)dest.scanLine(_original_data.width()-x-1); 272 destData = (unsigned int *)dest.scanLine(_original_data.width()-x-1);
267 destData[y] = srcData[x]; 273 destData[y] = srcData[x];
268 } 274 }
269 } 275 }
270 break; 276 break;
271 default: 277 default:
272 dest = _original_data; 278 dest = _original_data;
273 break; 279 break;
274 } 280 }
275 } 281 }
276 else 282 else
277 { 283 {
278 unsigned char *srcData, *destData; 284 unsigned char *srcData, *destData;
279 unsigned int *srcTable, *destTable; 285 unsigned int *srcTable, *destTable;
280 switch ( r ) 286 switch ( r )
281 { 287 {
282 case Rotate90: 288 case Rotate90:
283 dest.create(_original_data.height(), _original_data.width(), _original_data.depth()); 289 dest.create(_original_data.height(), _original_data.width(), _original_data.depth());
284 dest.setNumColors(_original_data.numColors()); 290 dest.setNumColors(_original_data.numColors());
285 srcTable = (unsigned int *)_original_data.colorTable(); 291 srcTable = (unsigned int *)_original_data.colorTable();
286 destTable = (unsigned int *)dest.colorTable(); 292 destTable = (unsigned int *)dest.colorTable();
287 for ( x=0; x < _original_data.numColors(); ++x ) 293 for ( x=0; x < _original_data.numColors(); ++x )
288 destTable[x] = srcTable[x]; 294 destTable[x] = srcTable[x];
289 for ( y=0; y < _original_data.height(); ++y ) 295 for ( y=0; y < _original_data.height(); ++y )
290 { 296 {
291 srcData = (unsigned char *)_original_data.scanLine(y); 297 srcData = (unsigned char *)_original_data.scanLine(y);
292 for ( x=0; x < _original_data.width(); ++x ) 298 for ( x=0; x < _original_data.width(); ++x )
293 { 299 {
294 destData = (unsigned char *)dest.scanLine(x); 300 destData = (unsigned char *)dest.scanLine(x);
295 destData[_original_data.height()-y-1] = srcData[x]; 301 destData[_original_data.height()-y-1] = srcData[x];
296 } 302 }
297 } 303 }
298 break; 304 break;
299 case Rotate180: 305 case Rotate180:
300 dest.create(_original_data.width(), _original_data.height(), _original_data.depth()); 306 dest.create(_original_data.width(), _original_data.height(), _original_data.depth());
301 dest.setNumColors(_original_data.numColors()); 307 dest.setNumColors(_original_data.numColors());
302 srcTable = (unsigned int *)_original_data.colorTable(); 308 srcTable = (unsigned int *)_original_data.colorTable();
303 destTable = (unsigned int *)dest.colorTable(); 309 destTable = (unsigned int *)dest.colorTable();
304 for ( x=0; x < _original_data.numColors(); ++x ) 310 for ( x=0; x < _original_data.numColors(); ++x )
305 destTable[x] = srcTable[x]; 311 destTable[x] = srcTable[x];
306 for ( y=0; y < _original_data.height(); ++y ) 312 for ( y=0; y < _original_data.height(); ++y )
307 { 313 {
308 srcData = (unsigned char *)_original_data.scanLine(y); 314 srcData = (unsigned char *)_original_data.scanLine(y);
309 destData = (unsigned char *)dest.scanLine(_original_data.height()-y-1); 315 destData = (unsigned char *)dest.scanLine(_original_data.height()-y-1);
310 for ( x=0; x < _original_data.width(); ++x ) 316 for ( x=0; x < _original_data.width(); ++x )
311 destData[_original_data.width()-x-1] = srcData[x]; 317 destData[_original_data.width()-x-1] = srcData[x];
312 } 318 }
313 break; 319 break;
314 case Rotate270: 320 case Rotate270:
315 dest.create(_original_data.height(), _original_data.width(), _original_data.depth()); 321 dest.create(_original_data.height(), _original_data.width(), _original_data.depth());
316 dest.setNumColors(_original_data.numColors()); 322 dest.setNumColors(_original_data.numColors());
317 srcTable = (unsigned int *)_original_data.colorTable(); 323 srcTable = (unsigned int *)_original_data.colorTable();
318 destTable = (unsigned int *)dest.colorTable(); 324 destTable = (unsigned int *)dest.colorTable();
319 for ( x=0; x < _original_data.numColors(); ++x ) 325 for ( x=0; x < _original_data.numColors(); ++x )
320 destTable[x] = srcTable[x]; 326 destTable[x] = srcTable[x];
321 for ( y=0; y < _original_data.height(); ++y ) 327 for ( y=0; y < _original_data.height(); ++y )
322 { 328 {
323 srcData = (unsigned char *)_original_data.scanLine(y); 329 srcData = (unsigned char *)_original_data.scanLine(y);
324 for ( x=0; x < _original_data.width(); ++x ) 330 for ( x=0; x < _original_data.width(); ++x )
325 { 331 {
326 destData = (unsigned char *)dest.scanLine(_original_data.width()-x-1); 332 destData = (unsigned char *)dest.scanLine(_original_data.width()-x-1);
327 destData[y] = srcData[x]; 333 destData[y] = srcData[x];
328 } 334 }
329 } 335 }
330 break; 336 break;
331 default: 337 default:
332 dest = _original_data; 338 dest = _original_data;
333 break; 339 break;
334 } 340 }
335 341
336 } 342 }
337 _image_data = dest; 343 _image_data = dest;
338} 344}
339 345
340void OImageScrollView::generateImage() 346void OImageScrollView::generateImage()
341{ 347{
342 Rotation r = Rotate0; 348 Rotation r = Rotate0;
343 _pdata = QPixmap(); 349 _pdata = QPixmap();
344 if (_original_data.isNull()) { 350 if (_original_data.isNull()) {
345 emit imageSizeChanged( _image_data.size() ); 351 emit imageSizeChanged( _image_data.size() );
346 if (_zoomer) _zoomer->setImage( _image_data ); 352 if (_zoomer) _zoomer->setImage( _image_data );
347 return; 353 return;
348 } 354 }
349 if (width()>height()&&_original_data.width()<_original_data.height() || 355 if (width()>height()&&_original_data.width()<_original_data.height() ||
350 width()<height()&&_original_data.width()>_original_data.height()) { 356 width()<height()&&_original_data.width()>_original_data.height()) {
351 if (AutoRotate()) r = Rotate90; 357 if (AutoRotate()) r = Rotate90;
352 } 358 }
353 359
354 int twidth,theight; 360 int twidth,theight;
355 odebug << " r = " << r << oendl; 361 odebug << " r = " << r << oendl;
356 if (AutoScale() && (_original_data.width()>width() || _original_data.height() > height()) ) { 362 if (AutoScale() && (_original_data.width()>width() || _original_data.height() > height()) ) {
357 if (!_image_data.size().isValid()||width()>_image_data.width()||height()>_image_data.height()) { 363 if (!_image_data.size().isValid()||width()>_image_data.width()||height()>_image_data.height()) {
358 odebug << "Rescaling data" << oendl; 364 odebug << "Rescaling data" << oendl;
359 if (r==Rotate0) { 365 if (r==Rotate0) {
360 _image_data = _original_data; 366 _image_data = _original_data;
361 } else { 367 } else {
362 rotate_into_data(r); 368 rotate_into_data(r);
363 } 369 }
364 } 370 }
365 rescaleImage(width(),height()); 371 rescaleImage(width(),height());
366 } else if (!FirstResizeDone()||r!=m_last_rot||_image_data.width()==0) { 372 } else if (!FirstResizeDone()||r!=m_last_rot||_image_data.width()==0) {
367 if (r==Rotate0) { 373 if (r==Rotate0) {
368 _image_data = _original_data; 374 _image_data = _original_data;
369 } else { 375 } else {
370 rotate_into_data(r); 376 rotate_into_data(r);
371 } 377 }
372 m_last_rot = r; 378 m_last_rot = r;
373 } 379 }
374 _pdata.convertFromImage(_image_data); 380 _pdata.convertFromImage(_image_data);
375 twidth = _image_data.width(); 381 twidth = _image_data.width();
376 theight = _image_data.height(); 382 theight = _image_data.height();
377 383
378 /* 384 /*
379 * update the zoomer 385 * update the zoomer
380 */ 386 */
381 check_zoomer(); 387 check_zoomer();
382 emit imageSizeChanged( _image_data.size() ); 388 emit imageSizeChanged( _image_data.size() );
383 rescaleImage( 128, 128 ); 389 rescaleImage( 128, 128 );
384 resizeContents(twidth,theight); 390 resizeContents(twidth,theight);
385 /* 391 /*
386 * move scrollbar 392 * move scrollbar
387 */ 393 */
388 if (_zoomer) { 394 if (_zoomer) {
389 _zoomer->setGeometry( viewport()->width()-_image_data.width()/2, viewport()->height()-_image_data.height()/2, 395 _zoomer->setGeometry( viewport()->width()-_image_data.width()/2, viewport()->height()-_image_data.height()/2,
390 _image_data.width()/2, _image_data.height()/2 ); 396 _image_data.width()/2, _image_data.height()/2 );
391 _zoomer->setImage( _image_data ); 397 _zoomer->setImage( _image_data );
392 } 398 }
393 /* 399 /*
394 * invalidate 400 * invalidate
395 */ 401 */
396 _image_data=QImage(); 402 _image_data=QImage();
397 if (isVisible()) { 403 if (isVisible()) {
398 updateContents(contentsX(),contentsY(),width(),height()); 404 updateContents(contentsX(),contentsY(),width(),height());
399 } 405 }
400} 406}
401 407
402void OImageScrollView::resizeEvent(QResizeEvent * e) 408void OImageScrollView::resizeEvent(QResizeEvent * e)
403{ 409{
404 odebug << "OImageScrollView resizeEvent (" << e->size() << " - " << e->oldSize() << oendl; 410 odebug << "OImageScrollView resizeEvent (" << e->size() << " - " << e->oldSize() << oendl;
405 QScrollView::resizeEvent(e); 411 QScrollView::resizeEvent(e);
406 if (e->oldSize()==e->size()||!isUpdatesEnabled ()) return; 412 if (e->oldSize()==e->size()||!isUpdatesEnabled ()) return;
407 generateImage(); 413 generateImage();
408 setFirstResizeDone(true); 414 setFirstResizeDone(true);
409 emit viewportSizeChanged( viewport()->size() ); 415 emit viewportSizeChanged( viewport()->size() );
410 416
411} 417}
412 418
413void OImageScrollView::keyPressEvent(QKeyEvent * e) 419void OImageScrollView::keyPressEvent(QKeyEvent * e)
414{ 420{
415 if (!e) return; 421 if (!e) return;
416 int dx = horizontalScrollBar()->lineStep(); 422 int dx = horizontalScrollBar()->lineStep();
417 int dy = verticalScrollBar()->lineStep(); 423 int dy = verticalScrollBar()->lineStep();
418 if (e->key()==Qt::Key_Right) { 424 if (e->key()==Qt::Key_Right) {
419 scrollBy(dx,0); 425 scrollBy(dx,0);
420 e->accept(); 426 e->accept();
421 } else if (e->key()==Qt::Key_Left) { 427 } else if (e->key()==Qt::Key_Left) {
422 scrollBy(0-dx,0); 428 scrollBy(0-dx,0);
423 e->accept(); 429 e->accept();
424 } else if (e->key()==Qt::Key_Up) { 430 } else if (e->key()==Qt::Key_Up) {
425 scrollBy(0,0-dy); 431 scrollBy(0,0-dy);
426 e->accept(); 432 e->accept();
427 } else if (e->key()==Qt::Key_Down) { 433 } else if (e->key()==Qt::Key_Down) {
428 scrollBy(0,dy); 434 scrollBy(0,dy);
429 e->accept(); 435 e->accept();
430 } else { 436 } else {
431 e->ignore(); 437 e->ignore();
432 } 438 }
433 QScrollView::keyPressEvent(e); 439 QScrollView::keyPressEvent(e);
434} 440}
435 441
436void OImageScrollView::drawContents(QPainter * p, int clipx, int clipy, int clipw, int cliph) 442void OImageScrollView::drawContents(QPainter * p, int clipx, int clipy, int clipw, int cliph)
437{ 443{
438 if (!_pdata.size().isValid()) { 444 if (!_pdata.size().isValid()) {
439 p->fillRect(clipx,clipy,clipw,cliph, backgroundColor()); 445 p->fillRect(clipx,clipy,clipw,cliph, backgroundColor());
440 return; 446 return;
441 } 447 }
442 448
443 int w = clipw; 449 int w = clipw;
444 int h = cliph; 450 int h = cliph;
445 int x = clipx; 451 int x = clipx;
446 int y = clipy; 452 int y = clipy;
447 bool erase = false; 453 bool erase = false;
448 454
449 if (w>_pdata.width()) { 455 if (w>_pdata.width()) {
450 w = _pdata.width()-x; 456 w = _pdata.width()-x;
451 erase=true; 457 erase=true;
452 } 458 }
453 if (h>_pdata.height()) { 459 if (h>_pdata.height()) {
454 h = _pdata.height()-y; 460 h = _pdata.height()-y;
455 erase=true; 461 erase=true;
456 } 462 }
457 if (!erase && (clipy+cliph>_pdata.height()||clipx+clipw>_pdata.width())) { 463 if (!erase && (clipy+cliph>_pdata.height()||clipx+clipw>_pdata.width())) {
458 erase = true; 464 erase = true;
459 } 465 }
460 if (erase||_original_data.hasAlphaBuffer()) { 466 if (erase||_original_data.hasAlphaBuffer()) {
461 p->fillRect(clipx,clipy,clipw,cliph, backgroundColor()); 467 p->fillRect(clipx,clipy,clipw,cliph, backgroundColor());
462 } 468 }
463 if (w>0 && h>0&&x<_pdata.width()&&y<_pdata.height()) { 469 if (w>0 && h>0&&x<_pdata.width()&&y<_pdata.height()) {
464 p->drawPixmap(clipx,clipy,_pdata,x,y,w,h); 470 p->drawPixmap(clipx,clipy,_pdata,x,y,w,h);
465 } 471 }
466} 472}
467 473
468/* using the real geometry points and not the translated points is wanted! */ 474/* using the real geometry points and not the translated points is wanted! */
469void OImageScrollView::viewportMouseMoveEvent(QMouseEvent* e) 475void OImageScrollView::viewportMouseMoveEvent(QMouseEvent* e)
470{ 476{
471 int mx, my; 477 int mx, my;
472 mx = e->x(); 478 mx = e->x();
473 my = e->y(); 479 my = e->y();
474 if (_mouseStartPosX!=-1 && _mouseStartPosY!=-1) { 480 if (_mouseStartPosX!=-1 && _mouseStartPosY!=-1) {
475 int diffx = _mouseStartPosX-mx; 481 int diffx = _mouseStartPosX-mx;
476 int diffy = _mouseStartPosY-my; 482 int diffy = _mouseStartPosY-my;
477 scrollBy(diffx,diffy); 483 scrollBy(diffx,diffy);
478 } 484 }
479 _mouseStartPosX=mx; 485 _mouseStartPosX=mx;
480 _mouseStartPosY=my; 486 _mouseStartPosY=my;
481} 487}
482 488
483void OImageScrollView::contentsMousePressEvent ( QMouseEvent * e) 489void OImageScrollView::contentsMousePressEvent ( QMouseEvent * e)
484{ 490{
485 odebug << " X and Y " << e->x() << " " << e->y() << oendl; 491 odebug << " X and Y " << e->x() << " " << e->y() << oendl;
486 /* this marks the beginning of a possible mouse move. Due internal reasons of QT 492 /* this marks the beginning of a possible mouse move. Due internal reasons of QT
487 the geometry values here may real differ from that set in MoveEvent (I don't know 493 the geometry values here may real differ from that set in MoveEvent (I don't know
488 why). For getting them in real context, we use the first move-event to set the start 494 why). For getting them in real context, we use the first move-event to set the start
489 position ;) 495 position ;)
490 */ 496 */
491 _mouseStartPosX = -1; 497 _mouseStartPosX = -1;
492 _mouseStartPosY = -1; 498 _mouseStartPosY = -1;
493} 499}
494 500
495void OImageScrollView::setDestructiveClose() { 501void OImageScrollView::setDestructiveClose() {
496 WFlags fl = getWFlags(); 502 WFlags fl = getWFlags();
497 /* clear it just in case */ 503 /* clear it just in case */
498 fl &= ~WDestructiveClose; 504 fl &= ~WDestructiveClose;
499 fl |= WDestructiveClose; 505 fl |= WDestructiveClose;
500 setWFlags( fl ); 506 setWFlags( fl );
501} 507}
502 508
503bool OImageScrollView::image_fit_into(const QSize&s ) 509bool OImageScrollView::image_fit_into(const QSize&s )
504{ 510{
505 if (s.width()>width()||s.height()>height()) { 511 if (s.width()>width()||s.height()>height()) {
506 return false; 512 return false;
507 } 513 }
508 return true; 514 return true;
509} 515}
510 516
511void OImageScrollView::setShowZoomer(bool how) 517void OImageScrollView::setShowZoomer(bool how)
512{ 518{
513 m_states.setBit(SHOW_ZOOMER,how); 519 m_states.setBit(SHOW_ZOOMER,how);
514 check_zoomer(); 520 check_zoomer();
515} 521}
516 522
517bool OImageScrollView::ShowZoomer()const 523bool OImageScrollView::ShowZoomer()const
518{ 524{
519 return m_states.testBit(SHOW_ZOOMER); 525 return m_states.testBit(SHOW_ZOOMER);
520} 526}
521 527
522void OImageScrollView::check_zoomer() 528void OImageScrollView::check_zoomer()
523{ 529{
524 if (!_zoomer) return; 530 if (!_zoomer) return;
525 if ( (!ShowZoomer()||image_fit_into(_pdata.size()) ) && _zoomer->isVisible()) { 531 if ( (!ShowZoomer()||image_fit_into(_pdata.size()) ) && _zoomer->isVisible()) {
526 _zoomer->hide(); 532 _zoomer->hide();
527 } else if ( ShowZoomer() && !image_fit_into(_pdata.size()) && _zoomer->isHidden()){ 533 } else if ( ShowZoomer() && !image_fit_into(_pdata.size()) && _zoomer->isHidden()){
528 _zoomer->show(); 534 _zoomer->show();
529 } 535 }
530} 536}
531 537
532bool OImageScrollView::FirstResizeDone()const 538bool OImageScrollView::FirstResizeDone()const
533{ 539{
534 return m_states.testBit(FIRST_RESIZE_DONE); 540 return m_states.testBit(FIRST_RESIZE_DONE);
535} 541}
536 542
537void OImageScrollView::setFirstResizeDone(bool how) 543void OImageScrollView::setFirstResizeDone(bool how)
538{ 544{
539 m_states.setBit(FIRST_RESIZE_DONE,how); 545 m_states.setBit(FIRST_RESIZE_DONE,how);
540} 546}
541 547
542bool OImageScrollView::ImageIsJpeg()const 548bool OImageScrollView::ImageIsJpeg()const
543{ 549{
544 return m_states.testBit(IMAGE_IS_JPEG); 550 return m_states.testBit(IMAGE_IS_JPEG);
545} 551}
546 552
547void OImageScrollView::setImageIsJpeg(bool how) 553void OImageScrollView::setImageIsJpeg(bool how)
548{ 554{
549 m_states.setBit(IMAGE_IS_JPEG,how); 555 m_states.setBit(IMAGE_IS_JPEG,how);
550} 556}
551 557
552bool OImageScrollView::ImageScaledLoaded()const 558bool OImageScrollView::ImageScaledLoaded()const
553{ 559{
554 return m_states.testBit(IMAGE_SCALED_LOADED); 560 return m_states.testBit(IMAGE_SCALED_LOADED);
555} 561}
556 562
557void OImageScrollView::setImageScaledLoaded(bool how) 563void OImageScrollView::setImageScaledLoaded(bool how)
558{ 564{
559 m_states.setBit(IMAGE_SCALED_LOADED,how); 565 m_states.setBit(IMAGE_SCALED_LOADED,how);
560} 566}
561 567
562} // namespace MM 568} // namespace MM
563} // namespace Opie 569} // namespace Opie
diff --git a/libopie2/opiemm/opieexif.cpp b/libopie2/opiemm/opieexif.cpp
new file mode 100644
index 0000000..0860ea8
--- a/dev/null
+++ b/libopie2/opiemm/opieexif.cpp
@@ -0,0 +1,1195 @@
1#include "opieexif.h"
2
3/* OPIE */
4#include <opie2/odebug.h>
5#include <qpe/timestring.h>
6/* QT */
7#include <qobject.h>
8#include <qimage.h>
9
10/**
11 exif.h
12*/
13
14#include <stdio.h>
15#include <stdlib.h>
16#include <math.h>
17#include <time.h>
18
19#include <qstring.h>
20#include <qfile.h>
21#include <qimage.h>
22
23//static int HaveAll;
24
25//--------------------------------------------------------------------------
26// Table of Jpeg encoding process names
27
28#define M_SOF0 0xC0 // Start Of Frame N
29#define M_SOF1 0xC1 // N indicates which compression process
30#define M_SOF2 0xC2 // Only SOF0-SOF2 are now in common use
31#define M_SOF3 0xC3
32#define M_SOF5 0xC5 // NB: codes C4 and CC are NOT SOF markers
33#define M_SOF6 0xC6
34#define M_SOF7 0xC7
35#define M_SOF9 0xC9
36#define M_SOF10 0xCA
37#define M_SOF11 0xCB
38#define M_SOF13 0xCD
39#define M_SOF14 0xCE
40#define M_SOF15 0xCF
41#define M_SOI 0xD8 // Start Of Image (beginning of datastream)
42#define M_EOI 0xD9 // End Of Image (end of datastream)
43#define M_SOS 0xDA // Start Of Scan (begins compressed data)
44#define M_JFIF 0xE0 // Jfif marker
45#define M_EXIF 0xE1 // Exif marker
46#define M_COM 0xFE // COMment
47
48
49//--------------------------------------------------------------------------
50// Describes format descriptor
51static int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
52#define NUM_FORMATS 12
53
54#define FMT_BYTE 1
55#define FMT_STRING 2
56#define FMT_USHORT 3
57#define FMT_ULONG 4
58#define FMT_URATIONAL 5
59#define FMT_SBYTE 6
60#define FMT_UNDEFINED 7
61#define FMT_SSHORT 8
62#define FMT_SLONG 9
63#define FMT_SRATIONAL 10
64#define FMT_SINGLE 11
65#define FMT_DOUBLE 12
66
67//--------------------------------------------------------------------------
68// Describes tag values
69
70#define TAG_EXIF_OFFSET 0x8769
71#define TAG_INTEROP_OFFSET 0xa005
72
73#define TAG_MAKE 0x010F
74#define TAG_MODEL 0x0110
75#define TAG_ORIENTATION 0x0112
76
77#define TAG_EXPOSURETIME 0x829A
78#define TAG_FNUMBER 0x829D
79
80#define TAG_SHUTTERSPEED 0x9201
81#define TAG_APERTURE 0x9202
82#define TAG_MAXAPERTURE 0x9205
83#define TAG_FOCALLENGTH 0x920A
84
85#define TAG_DATETIME_ORIGINAL 0x9003
86#define TAG_USERCOMMENT 0x9286
87
88#define TAG_SUBJECT_DISTANCE 0x9206
89#define TAG_FLASH 0x9209
90
91#define TAG_FOCALPLANEXRES 0xa20E
92#define TAG_FOCALPLANEUNITS 0xa210
93#define TAG_EXIF_IMAGEWIDTH 0xA002
94#define TAG_EXIF_IMAGELENGTH 0xA003
95
96// the following is added 05-jan-2001 vcs
97#define TAG_EXPOSURE_BIAS 0x9204
98#define TAG_WHITEBALANCE 0x9208
99#define TAG_METERING_MODE 0x9207
100#define TAG_EXPOSURE_PROGRAM 0x8822
101#define TAG_ISO_EQUIVALENT 0x8827
102#define TAG_COMPRESSION_LEVEL 0x9102
103
104#define TAG_THUMBNAIL_OFFSET 0x0201
105#define TAG_THUMBNAIL_LENGTH 0x0202
106
107
108
109namespace Opie {
110
111namespace MM {
112
113class FatalError {
114 const char* ex;
115public:
116 FatalError(const char* s) { ex = s; }
117 void debug_print() const { owarn << "exception: " << ex << "" << oendl; }
118};
119
120ExifData::TagTable_t ProcessTable[] = {
121 { M_SOF0, "Baseline"},
122 { M_SOF1, "Extended sequential"},
123 { M_SOF2, "Progressive"},
124 { M_SOF3, "Lossless"},
125 { M_SOF5, "Differential sequential"},
126 { M_SOF6, "Differential progressive"},
127 { M_SOF7, "Differential lossless"},
128 { M_SOF9, "Extended sequential, arithmetic coding"},
129 { M_SOF10, "Progressive, arithmetic coding"},
130 { M_SOF11, "Lossless, arithmetic coding"},
131 { M_SOF13, "Differential sequential, arithmetic coding"},
132 { M_SOF14, "Differential progressive, arithmetic coding"},
133 { M_SOF15, "Differential lossless, arithmetic coding"},
134 { 0, "Unknown"}
135};
136
137//--------------------------------------------------------------------------
138// Parse the marker stream until SOS or EOI is seen;
139//--------------------------------------------------------------------------
140int ExifData::ReadJpegSections (QFile & infile, ReadMode_t ReadMode)
141{
142 int a;
143
144 a = infile.getch();
145
146 if (a != 0xff || infile.getch() != M_SOI) {
147 SectionsRead = 0;
148 return false;
149 }
150 for(SectionsRead = 0; SectionsRead < MAX_SECTIONS-1; ){
151 int marker = 0;
152 int got;
153 unsigned int ll,lh;
154 unsigned int itemlen;
155 uchar * Data;
156
157 for (a=0;a<7;a++){
158 marker = infile.getch();
159 if (marker != 0xff) break;
160
161 if (a >= 6){
162
163 owarn << "too many padding bytes" << oendl;
164 return false;
165
166 }
167 }
168
169 if (marker == 0xff){
170 // 0xff is legal padding, but if we get that many, something's wrong.
171 return false;
172 }
173
174 Sections[SectionsRead].Type = marker;
175
176 // Read the length of the section.
177 lh = (uchar) infile.getch();
178 ll = (uchar) infile.getch();
179
180 itemlen = (lh << 8) | ll;
181
182 if (itemlen < 2) {
183 return false;;
184 }
185
186 Sections[SectionsRead].Size = itemlen;
187
188 Data = (uchar *)malloc(itemlen+1); // Add 1 to allow sticking a 0 at the end.
189 Sections[SectionsRead].Data = Data;
190
191 // Store first two pre-read bytes.
192 Data[0] = (uchar)lh;
193 Data[1] = (uchar)ll;
194
195 got = infile.readBlock((char*)Data+2, itemlen-2); // Read the whole section.
196 if (( unsigned ) got != itemlen-2){
197 return false;
198 }
199 SectionsRead++;
200
201 switch(marker){
202
203 case M_SOS: // stop before hitting compressed data
204 // If reading entire image is requested, read the rest of the data.
205 if (ReadMode & READ_IMAGE){
206 unsigned long size;
207
208 size = QMAX( 0ul, infile.size()-infile.at() );
209 Data = (uchar *)malloc(size);
210 if (Data == NULL){
211 return false;
212 }
213
214 got = infile.readBlock((char*)Data, size);
215 if (( unsigned ) got != size){
216 return false;
217 }
218
219 Sections[SectionsRead].Data = Data;
220 Sections[SectionsRead].Size = size;
221 Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER;
222 SectionsRead ++;
223 //HaveAll = 1;
224 }
225 return true;
226
227 case M_EOI: // in case it's a tables-only JPEG stream
228 owarn << "No image in jpeg!" << oendl;
229 return false;
230
231 case M_COM: // Comment section
232 // pieczy 2002-02-12
233 // now the User comment goes to UserComment
234 // so we can store a Comment section also in READ_EXIF mode
235 process_COM(Data, itemlen);
236 break;
237
238 case M_JFIF:
239 // Regular jpegs always have this tag, exif images have the exif
240 // marker instead, althogh ACDsee will write images with both markers.
241 // this program will re-create this marker on absence of exif marker.
242 // hence no need to keep the copy from the file.
243 free(Sections[--SectionsRead].Data);
244 break;
245
246 case M_EXIF:
247 // Seen files from some 'U-lead' software with Vivitar scanner
248 // that uses marker 31 for non exif stuff. Thus make sure
249 // it says 'Exif' in the section before treating it as exif.
250 if ((ReadMode & READ_EXIF) && memcmp(Data+2, "Exif", 4) == 0){
251 process_EXIF((uchar *)Data, itemlen);
252 }else{
253 // Discard this section.
254 free(Sections[--SectionsRead].Data);
255 }
256 break;
257
258 case M_SOF0:
259 case M_SOF1:
260 case M_SOF2:
261 case M_SOF3:
262 case M_SOF5:
263 case M_SOF6:
264 case M_SOF7:
265 case M_SOF9:
266 case M_SOF10:
267 case M_SOF11:
268 case M_SOF13:
269 case M_SOF14:
270 case M_SOF15:
271 process_SOFn(Data, marker);
272 default:
273 break;
274 break;
275 }
276 }
277 return true;
278}
279
280
281//--------------------------------------------------------------------------
282// Discard read data.
283//--------------------------------------------------------------------------
284void ExifData::DiscardData(void)
285{
286 for (int a=0; a < SectionsRead; a++)
287 free(Sections[a].Data);
288 SectionsRead = 0;
289}
290
291//--------------------------------------------------------------------------
292// Convert a 16 bit unsigned value from file's native byte order
293//--------------------------------------------------------------------------
294int ExifData::Get16u(void * Short)
295{
296 if (MotorolaOrder){
297 return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
298 }else{
299 return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0];
300 }
301}
302
303//--------------------------------------------------------------------------
304// Convert a 32 bit signed value from file's native byte order
305//--------------------------------------------------------------------------
306int ExifData::Get32s(void * Long)
307{
308 if (MotorolaOrder){
309 return ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16)
310 | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 );
311 }else{
312 return ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16)
313 | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 );
314 }
315}
316
317//--------------------------------------------------------------------------
318// Convert a 32 bit unsigned value from file's native byte order
319//--------------------------------------------------------------------------
320unsigned ExifData::Get32u(void * Long)
321{
322 return (unsigned)Get32s(Long) & 0xffffffff;
323}
324
325//--------------------------------------------------------------------------
326// Evaluate number, be it int, rational, or float from directory.
327//--------------------------------------------------------------------------
328double ExifData::ConvertAnyFormat(void * ValuePtr, int Format)
329{
330 double Value;
331 Value = 0;
332
333 switch(Format){
334 case FMT_SBYTE: Value = *(signed char *)ValuePtr; break;
335 case FMT_BYTE: Value = *(uchar *)ValuePtr; break;
336
337 case FMT_USHORT: Value = Get16u(ValuePtr); break;
338
339 case FMT_ULONG: Value = Get32u(ValuePtr); break;
340
341 case FMT_URATIONAL:
342 case FMT_SRATIONAL:
343 {
344 int Num,Den;
345 Num = Get32s(ValuePtr);
346 Den = Get32s(4+(char *)ValuePtr);
347 if (Den == 0){
348 Value = 0;
349 }else{
350 Value = (double)Num/Den;
351 }
352 break;
353 }
354
355 case FMT_SSHORT: Value = (signed short)Get16u(ValuePtr); break;
356 case FMT_SLONG: Value = Get32s(ValuePtr); break;
357
358 // Not sure if this is correct (never seen float used in Exif format)
359 case FMT_SINGLE: Value = (double)*(float *)ValuePtr; break;
360 case FMT_DOUBLE: Value = *(double *)ValuePtr; break;
361 }
362 return Value;
363}
364
365//--------------------------------------------------------------------------
366// Process one of the nested EXIF directories.
367//--------------------------------------------------------------------------
368void ExifData::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength)
369{
370 int de;
371 int a;
372 int NumDirEntries;
373 unsigned ThumbnailOffset = 0;
374 unsigned ThumbnailSize = 0;
375
376 NumDirEntries = Get16u(DirStart);
377 #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
378
379 {
380 unsigned char * DirEnd;
381 DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
382 if (DirEnd+4 > (OffsetBase+ExifLength)){
383 if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength){
384 // Version 1.3 of jhead would truncate a bit too much.
385 // This also caught later on as well.
386 }else{
387 // Note: Files that had thumbnails trimmed with jhead 1.3 or earlier
388 // might trigger this.
389 return;
390 }
391 }
392 if (DirEnd < LastExifRefd) LastExifRefd = DirEnd;
393 }
394
395 for (de=0;de<NumDirEntries;de++){
396 int Tag, Format, Components;
397 unsigned char * ValuePtr;
398 int ByteCount;
399 char * DirEntry;
400 DirEntry = (char *)DIR_ENTRY_ADDR(DirStart, de);
401
402 Tag = Get16u(DirEntry);
403 Format = Get16u(DirEntry+2);
404 Components = Get32u(DirEntry+4);
405
406 if ((Format-1) >= NUM_FORMATS) {
407 // (-1) catches illegal zero case as unsigned underflows to positive large.
408 return;
409 }
410
411 ByteCount = Components * BytesPerFormat[Format];
412
413 if (ByteCount > 4){
414 unsigned OffsetVal;
415 OffsetVal = Get32u(DirEntry+8);
416 // If its bigger than 4 bytes, the dir entry contains an offset.
417 if (OffsetVal+ByteCount > ExifLength){
418 // Bogus pointer offset and / or bytecount value
419 //printf("Offset %d bytes %d ExifLen %d\n",OffsetVal, ByteCount, ExifLength);
420
421 return;
422 }
423 ValuePtr = OffsetBase+OffsetVal;
424 }else{
425 // 4 bytes or less and value is in the dir entry itself
426 ValuePtr = (unsigned char *)DirEntry+8;
427 }
428
429 if (LastExifRefd < ValuePtr+ByteCount){
430 // Keep track of last byte in the exif header that was actually referenced.
431 // That way, we know where the discardable thumbnail data begins.
432 LastExifRefd = ValuePtr+ByteCount;
433 }
434
435 // Extract useful components of tag
436 switch(Tag){
437
438 case TAG_MAKE:
439 ExifData::CameraMake = QString((char*)ValuePtr);
440 break;
441
442 case TAG_MODEL:
443 ExifData::CameraModel = QString((char*)ValuePtr);
444 break;
445
446 case TAG_ORIENTATION:
447 Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
448 break;
449
450 case TAG_DATETIME_ORIGINAL:
451 DateTime = QString((char*)ValuePtr);
452 break;
453
454 case TAG_USERCOMMENT:
455 // Olympus has this padded with trailing spaces. Remove these first.
456 for (a=ByteCount;;){
457 a--;
458 if ((ValuePtr)[a] == ' '){
459 (ValuePtr)[a] = '\0';
460 }else{
461 break;
462 }
463 if (a == 0) break;
464 }
465
466 // Copy the comment
467 if (memcmp(ValuePtr, "ASCII",5) == 0){
468 for (a=5;a<10;a++){
469 int c;
470 c = (ValuePtr)[a];
471 if (c != '\0' && c != ' '){
472 //strncpy(ImageInfo.Comments, (const char*)(a+ValuePtr), 199);
473 UserComment.sprintf("%s", (const char*)(a+ValuePtr));
474 break;
475 }
476 }
477 }else{
478 //strncpy(ImageInfo.Comments, (const char*)ValuePtr, 199);
479 UserComment.sprintf("%s", (const char*)ValuePtr);
480 }
481 break;
482
483 case TAG_FNUMBER:
484 // Simplest way of expressing aperture, so I trust it the most.
485 // (overwrite previously computd value if there is one)
486 ExifData::ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);
487 break;
488
489 case TAG_APERTURE:
490 case TAG_MAXAPERTURE:
491 // More relevant info always comes earlier, so only use this field if we don't
492 // have appropriate aperture information yet.
493 if (ExifData::ApertureFNumber == 0){
494 ExifData::ApertureFNumber
495 = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5);
496 }
497 break;
498
499 case TAG_FOCALLENGTH:
500 // Nice digital cameras actually save the focal length as a function
501 // of how farthey are zoomed in.
502 ExifData::FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);
503 break;
504
505 case TAG_SUBJECT_DISTANCE:
506 // Inidcates the distacne the autofocus camera is focused to.
507 // Tends to be less accurate as distance increases.
508 ExifData::Distance = (float)ConvertAnyFormat(ValuePtr, Format);
509 break;
510
511 case TAG_EXPOSURETIME:
512 // Simplest way of expressing exposure time, so I trust it most.
513 // (overwrite previously computd value if there is one)
514 ExifData::ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format);
515 break;
516
517 case TAG_SHUTTERSPEED:
518 // More complicated way of expressing exposure time, so only use
519 // this value if we don't already have it from somewhere else.
520 if (ExifData::ExposureTime == 0){
521 ExifData::ExposureTime
522 = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2)));
523 }
524 break;
525
526 case TAG_FLASH:
527 if (ConvertAnyFormat(ValuePtr, Format)){
528 ExifData::FlashUsed = 1;
529 }
530 break;
531
532 case TAG_EXIF_IMAGELENGTH:
533 ExifImageLength = (int)ConvertAnyFormat(ValuePtr, Format);
534 break;
535
536 case TAG_EXIF_IMAGEWIDTH:
537 ExifImageWidth = (int)ConvertAnyFormat(ValuePtr, Format);
538 break;
539
540 case TAG_FOCALPLANEXRES:
541 FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format);
542 break;
543
544 case TAG_FOCALPLANEUNITS:
545 switch((int)ConvertAnyFormat(ValuePtr, Format)){
546 case 1: FocalplaneUnits = 25.4; break; // inch
547 case 2:
548 // According to the information I was using, 2 means meters.
549 // But looking at the Cannon powershot's files, inches is the only
550 // sensible value.
551 FocalplaneUnits = 25.4;
552 break;
553
554 case 3: FocalplaneUnits = 10; break; // centimeter
555 case 4: FocalplaneUnits = 1; break; // milimeter
556 case 5: FocalplaneUnits = .001; break; // micrometer
557 }
558 break;
559
560 // Remaining cases contributed by: Volker C. Schoech (schoech@gmx.de)
561
562 case TAG_EXPOSURE_BIAS:
563 ExifData::ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format);
564 break;
565
566 case TAG_WHITEBALANCE:
567 ExifData::Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
568 break;
569
570 case TAG_METERING_MODE:
571 ExifData::MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
572 break;
573
574 case TAG_EXPOSURE_PROGRAM:
575 ExifData::ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
576 break;
577
578 case TAG_ISO_EQUIVALENT:
579 ExifData::ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
580 if ( ExifData::ISOequivalent < 50 ) ExifData::ISOequivalent *= 200;
581 break;
582
583 case TAG_COMPRESSION_LEVEL:
584 ExifData::CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format);
585 break;
586
587 case TAG_THUMBNAIL_OFFSET:
588 ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);
589 break;
590
591 case TAG_THUMBNAIL_LENGTH:
592 ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
593 break;
594
595 }
596
597 if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){
598 unsigned char * SubdirStart;
599 SubdirStart = OffsetBase + Get32u(ValuePtr);
600 if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){
601 return;
602 }
603 ProcessExifDir(SubdirStart, OffsetBase, ExifLength);
604 continue;
605 }
606 }
607
608 {
609 // In addition to linking to subdirectories via exif tags,
610 // there's also a potential link to another directory at the end of each
611 // directory. this has got to be the result of a comitee!
612 unsigned char * SubdirStart;
613 unsigned Offset;
614
615 if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength){
616 Offset = Get32u(DIR_ENTRY_ADDR(DirStart, NumDirEntries));
617 // There is at least one jpeg from an HP camera having an Offset of almost MAXUINT.
618 // Adding OffsetBase to it produces an overflow, so compare with ExifLength here.
619 // See http://bugs.kde.org/show_bug.cgi?id=54542
620 if (Offset && Offset < ExifLength){
621 SubdirStart = OffsetBase + Offset;
622 if (SubdirStart > OffsetBase+ExifLength){
623 if (SubdirStart < OffsetBase+ExifLength+20){
624 // Jhead 1.3 or earlier would crop the whole directory!
625 // As Jhead produces this form of format incorrectness,
626 // I'll just let it pass silently
627 owarn << "Thumbnail removed with Jhead 1.3 or earlier" << oendl;
628 }else{
629 return;
630 }
631 }else{
632 if (SubdirStart <= OffsetBase+ExifLength){
633 ProcessExifDir(SubdirStart, OffsetBase, ExifLength);
634 }
635 }
636 }
637 }else{
638 // The exif header ends before the last next directory pointer.
639 }
640 }
641
642 if (ThumbnailSize && ThumbnailOffset){
643 if (ThumbnailSize + ThumbnailOffset <= ExifLength){
644 // The thumbnail pointer appears to be valid. Store it.
645 Thumbnail.loadFromData(OffsetBase + ThumbnailOffset, ThumbnailSize, "JPEG");
646 }
647 }
648}
649
650//--------------------------------------------------------------------------
651// Process a COM marker. We want to leave the bytes unchanged. The
652// progam that displays this text may decide to remove blanks, convert
653// newlines, or otherwise modify the text. In particular we want to be
654// safe for passing utf-8 text.
655//--------------------------------------------------------------------------
656void ExifData::process_COM (const uchar * Data, int length)
657{
658 QChar ch;
659 int a;
660
661 for (a=2;a<length;a++){
662 ch = Data[a];
663 if (ch == '\000') continue; // Remove nulls
664 Comment.append(ch);
665 }
666}
667
668
669//--------------------------------------------------------------------------
670// Process a SOFn marker. This is useful for the image dimensions
671//--------------------------------------------------------------------------
672void ExifData::process_SOFn (const uchar * Data, int marker)
673{
674 int data_precision, num_components;
675
676 data_precision = Data[2];
677 ExifData::Height = Get16m(Data+3);
678 ExifData::Width = Get16m(Data+5);
679 num_components = Data[7];
680
681 if (num_components == 3){
682 ExifData::IsColor = 1;
683 }else{
684 ExifData::IsColor = 0;
685 }
686
687 ExifData::Process = marker;
688
689}
690
691//--------------------------------------------------------------------------
692// Get 16 bits motorola order (always) for jpeg header stuff.
693//--------------------------------------------------------------------------
694int ExifData::Get16m(const void * Short)
695{
696 return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
697}
698
699
700//--------------------------------------------------------------------------
701// Process a EXIF marker
702// Describes all the drivel that most digital cameras include...
703//--------------------------------------------------------------------------
704void ExifData::process_EXIF(unsigned char * CharBuf, unsigned int length)
705{
706 ExifData::FlashUsed = 0; // If it s from a digicam, and it used flash, it says so.
707
708 FocalplaneXRes = 0;
709 FocalplaneUnits = 0;
710 ExifImageWidth = 0;
711 ExifImageLength = 0;
712
713 { // Check the EXIF header component
714 static const uchar ExifHeader[] = "Exif\0\0";
715 if (memcmp(CharBuf+2, ExifHeader,6)){
716 return;
717 }
718 }
719
720 if (memcmp(CharBuf+8,"II",2) == 0){
721 // printf("Exif section in Intel order\n");
722 MotorolaOrder = 0;
723 }else{
724 if (memcmp(CharBuf+8,"MM",2) == 0){
725 // printf("Exif section in Motorola order\n");
726 MotorolaOrder = 1;
727 }else{
728 return;
729 }
730 }
731
732 // Check the next two values for correctness.
733 if (Get16u(CharBuf+10) != 0x2a
734 || Get32u(CharBuf+12) != 0x08){
735 return;
736 }
737
738 LastExifRefd = CharBuf;
739
740 // First directory starts 16 bytes in. Offsets start at 8 bytes in.
741 ProcessExifDir(CharBuf+16, CharBuf+8, length-6);
742
743 // This is how far the interesting (non thumbnail) part of the exif went.
744 ExifSettingsLength = LastExifRefd - CharBuf;
745
746 // Compute the CCD width, in milimeters.
747 if (FocalplaneXRes != 0){
748 ExifData::CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes);
749 }
750}
751
752//--------------------------------------------------------------------------
753// Convert exif time to Unix time structure
754//--------------------------------------------------------------------------
755int ExifData::Exif2tm(struct ::tm * timeptr, char * ExifTime)
756{
757 int a;
758
759 timeptr->tm_wday = -1;
760
761 // Check for format: YYYY:MM:DD HH:MM:SS format.
762 a = sscanf(ExifTime, "%d:%d:%d %d:%d:%d",
763 &timeptr->tm_year, &timeptr->tm_mon, &timeptr->tm_mday,
764 &timeptr->tm_hour, &timeptr->tm_min, &timeptr->tm_sec);
765
766 if (a == 6){
767 timeptr->tm_isdst = -1;
768 timeptr->tm_mon -= 1; // Adjust for unix zero-based months
769 timeptr->tm_year -= 1900; // Adjust for year starting at 1900
770 return true; // worked.
771 }
772
773 return false; // Wasn't in Exif date format.
774}
775
776//--------------------------------------------------------------------------
777// Contructor for initialising
778//--------------------------------------------------------------------------
779ExifData::ExifData()
780{
781 ExifData::Whitebalance = -1;
782 ExifData::MeteringMode = -1;
783 ExifData::FlashUsed = -1;
784 Orientation = 0;
785 Height = 0;
786 Width = 0;
787 IsColor = 0;
788 Process = 0;
789 FocalLength = 0;
790 ExposureTime = 0;
791 ApertureFNumber = 0;
792 Distance = 0;
793 CCDWidth = 0;
794 ExposureBias = 0;
795 ExposureProgram = 0;
796 ISOequivalent = 0;
797 CompressionLevel = 0;
798 MotorolaOrder = 0;
799}
800
801ExifData::~ExifData()
802{
803}
804
805//--------------------------------------------------------------------------
806// process a EXIF jpeg file
807//--------------------------------------------------------------------------
808bool ExifData::scan(const QString & path)
809{
810 int ret;
811
812 QFile f(path);
813 f.open(IO_ReadOnly);
814
815 // Scan the JPEG headers.
816 ret = ReadJpegSections(f, READ_EXIF);
817
818 if (ret == false){
819 owarn << "Not JPEG file!" << oendl;
820 DiscardData();
821 f.close();
822 return false;
823 }
824 f.close();
825 DiscardData();
826
827 //now make the strings clean,
828 // for exmaple my Casio is a "QV-4000 "
829 CameraMake = CameraMake.stripWhiteSpace();
830 CameraModel = CameraModel.stripWhiteSpace();
831 UserComment = UserComment.stripWhiteSpace();
832 Comment = Comment.stripWhiteSpace();
833 return true;
834}
835
836//--------------------------------------------------------------------------
837// Does the embedded thumbnail match the jpeg image?
838//--------------------------------------------------------------------------
839#ifndef JPEG_TOL
840#define JPEG_TOL 0.02
841#endif
842bool ExifData::isThumbnailSane() {
843 if (Thumbnail.isNull()) return false;
844
845 // check whether thumbnail dimensions match the image
846 // not foolproof, but catches some altered images (jpegtran -rotate)
847 if (ExifImageLength != 0 && ExifImageLength != Height) return false;
848 if (ExifImageWidth != 0 && ExifImageWidth != Width) return false;
849 if (Thumbnail.width() == 0 || Thumbnail.height() == 0) return false;
850 if (Height == 0 || Width == 0) return false;
851 double d = (double)Height/Width*Thumbnail.width()/Thumbnail.height();
852 return (1-JPEG_TOL < d) && (d < 1+JPEG_TOL);
853}
854
855
856
857static QImage flip_image( const QImage& img );
858static QImage rotate_90( const QImage& img );
859static QImage rotate_180( const QImage& );
860static QImage rotate_270( const QImage& );
861
862//--------------------------------------------------------------------------
863// return a thumbnail that respects the orientation flag
864// only if it seems sane
865//--------------------------------------------------------------------------
866QImage ExifData::getThumbnail() {
867 if (!isThumbnailSane()) return NULL;
868 if (!Orientation || Orientation == 1) return Thumbnail;
869
870 // now fix orientation
871
872 QImage dest = Thumbnail;
873 switch (Orientation) { // notice intentional fallthroughs
874 case 2: dest = flip_image( dest ); break;
875 case 4: dest = flip_image( dest );
876 case 3: dest =rotate_180( dest ); break;
877 case 5: dest = flip_image( dest );
878 case 6: dest = rotate_90( dest ); break;
879 case 7: dest = flip_image( dest );
880 case 8: dest = rotate_270( dest ); break;
881 default: break; // should never happen
882 }
883 return dest;
884}
885
886
887/*
888 *
889 */
890static QImage flip_image( const QImage& img ) {
891 return img.mirror( TRUE, FALSE );
892}
893
894
895static QImage dest;
896static int x, y;
897static unsigned int *srcData, *destData; // we're not threaded anyway
898static unsigned char *srcData8, *destData8; // 8 bit is char
899static unsigned int *srcTable, *destTable; // destination table
900
901
902static QImage rotate_90_8( const QImage &img ) {
903 dest.create(img.height(), img.width(), img.depth());
904 dest.setNumColors(img.numColors());
905 srcTable = (unsigned int *)img.colorTable();
906 destTable = (unsigned int *)dest.colorTable();
907 for ( x=0; x < img.numColors(); ++x )
908 destTable[x] = srcTable[x];
909 for ( y=0; y < img.height(); ++y ){
910 srcData8 = (unsigned char *)img.scanLine(y);
911 for ( x=0; x < img.width(); ++x ){
912 destData8 = (unsigned char *)dest.scanLine(x);
913 destData8[img.height()-y-1] = srcData8[x];
914 }
915 }
916 return dest;
917}
918
919static QImage rotate_90_all( const QImage& img ) {
920 dest.create(img.height(), img.width(), img.depth());
921 for ( y=0; y < img.height(); ++y ) {
922 srcData = (unsigned int *)img.scanLine(y);
923 for ( x=0; x < img.width(); ++x ) {
924 destData = (unsigned int *)dest.scanLine(x);
925 destData[img.height()-y-1] = srcData[x];
926 }
927 }
928
929 return dest;
930}
931
932
933static QImage rotate_90( const QImage & img ) {
934 if ( img.depth() > 8)
935 return rotate_90_all( img );
936 else
937 return rotate_90_8( img );
938}
939
940static QImage rotate_180_all( const QImage& img ) {
941 dest.create(img.width(), img.height(), img.depth());
942 for ( y=0; y < img.height(); ++y ){
943 srcData = (unsigned int *)img.scanLine(y);
944 destData = (unsigned int *)dest.scanLine(img.height()-y-1);
945 for ( x=0; x < img.width(); ++x )
946 destData[img.width()-x-1] = srcData[x];
947 }
948 return dest;
949}
950
951static QImage rotate_180_8( const QImage& img ) {
952 dest.create(img.width(), img.height(), img.depth());
953 dest.setNumColors(img.numColors());
954 srcTable = (unsigned int *)img.colorTable();
955 destTable = (unsigned int *)dest.colorTable();
956 for ( x=0; x < img.numColors(); ++x )
957 destTable[x] = srcTable[x];
958 for ( y=0; y < img.height(); ++y ){
959 srcData8 = (unsigned char *)img.scanLine(y);
960 destData8 = (unsigned char *)dest.scanLine(img.height()-y-1);
961 for ( x=0; x < img.width(); ++x )
962 destData8[img.width()-x-1] = srcData8[x];
963 }
964 return dest;
965}
966
967static QImage rotate_180( const QImage& img ) {
968 if ( img.depth() > 8 )
969 return rotate_180_all( img );
970 else
971 return rotate_180_8( img );
972}
973
974
975static QImage rotate_270_8( const QImage& img ) {
976 dest.create(img.height(), img.width(), img.depth());
977 dest.setNumColors(img.numColors());
978 srcTable = (unsigned int *)img.colorTable();
979 destTable = (unsigned int *)dest.colorTable();
980 for ( x=0; x < img.numColors(); ++x )
981 destTable[x] = srcTable[x];
982 for ( y=0; y < img.height(); ++y ){
983 srcData8 = (unsigned char *)img.scanLine(y);
984 for ( x=0; x < img.width(); ++x ){
985 destData8 = (unsigned char *)dest.scanLine(img.width()-x-1);
986 destData8[y] = srcData8[x];
987 }
988 }
989
990 return dest;
991}
992
993static QImage rotate_270_all( const QImage& img ) {
994 dest.create(img.height(), img.width(), img.depth());
995 for ( y=0; y < img.height(); ++y ){
996 srcData = (unsigned int *)img.scanLine(y);
997 for ( x=0; x < img.width(); ++x ){
998 destData = (unsigned int *)dest.scanLine(img.width()-x-1);
999 destData[y] = srcData[x];
1000 }
1001 }
1002 return dest;
1003}
1004
1005static QImage rotate_270( const QImage& img ) {
1006 if ( img.depth() > 8 )
1007 return rotate_270_all( img );
1008 else
1009 return rotate_270_8( img );
1010}
1011
1012
1013static QString color_mode_to_string( bool b ) {
1014 return b ? QObject::tr( "Colormode: Color\n" ) : QObject::tr( "Colormode: Black and white\n" );
1015}
1016
1017static QString compression_to_string( int level ) {
1018 QString str;
1019 switch( level ) {
1020 case 1:
1021 str = QObject::tr( "Basic" );
1022 break;
1023 case 2:
1024 str = QObject::tr( "Normal" );
1025 break;
1026 case 4:
1027 str = QObject::tr( "Fine" );
1028 break;
1029 default:
1030 str = QObject::tr( "Unknown" );
1031
1032 }
1033 return QObject::tr("Quality: %1\n").arg(str);
1034}
1035
1036
1037static QDateTime parseDateTime( const QString& string )
1038{
1039 QDateTime dt;
1040 if ( string.length() != 19 )
1041 return dt;
1042
1043 QString year = string.left( 4 );
1044 QString month = string.mid( 5, 2 );
1045 QString day = string.mid( 8, 2 );
1046 QString hour = string.mid( 11, 2 );
1047 QString minute = string.mid( 14, 2 );
1048 QString seconds = string.mid( 18, 2 );
1049
1050 bool ok;
1051 bool allOk = true;
1052 int y = year.toInt( &ok );
1053 allOk &= ok;
1054
1055 int mo = month.toInt( &ok );
1056 allOk &= ok;
1057
1058 int d = day.toInt( &ok );
1059 allOk &= ok;
1060
1061 int h = hour.toInt( &ok );
1062 allOk &= ok;
1063
1064 int mi = minute.toInt( &ok );
1065 allOk &= ok;
1066
1067 int s = seconds.toInt( &ok );
1068 allOk &= ok;
1069
1070 if ( allOk ) {
1071 dt.setDate( QDate( y, mo, d ) );
1072 dt.setTime( QTime( h, mi, s ) );
1073 }
1074
1075 return dt;
1076}
1077
1078static QString white_balance_string( int i ) {
1079 QString balance;
1080 switch ( i ) {
1081 case 0:
1082 balance = QObject::tr( "Unknown" );
1083 break;
1084 case 1:
1085 balance = QObject::tr( "Daylight" );
1086 break;
1087 case 2:
1088 balance = QObject::tr( "Fluorescent" );
1089 break;
1090 case 3:
1091 balance = QObject::tr( "Tungsten" );
1092 break;
1093 case 17:
1094 balance = QObject::tr( "Standard light A" );
1095 break;
1096 case 18:
1097 balance = QObject::tr( "Standard light B" );
1098 break;
1099 case 19:
1100 balance = QObject::tr( "Standard light C" );
1101 break;
1102 case 20:
1103 balance = QObject::tr( "D55" );
1104 break;
1105 case 21:
1106 balance = QObject::tr( "D65" );
1107 break;
1108 case 22:
1109 balance = QObject::tr( "D75" );
1110 break;
1111 case 255:
1112 balance = QObject::tr( "Other" );
1113 break;
1114 default:
1115 balance = QObject::tr( "Unknown" );
1116 }
1117 return QObject::tr( "White Balance: %1\n" ).arg( balance );
1118
1119}
1120
1121
1122static QString metering_mode( int i) {
1123 QString meter;
1124 switch( i ) {
1125 case 0:
1126 meter = QObject::tr( "Unknown" );
1127 break;
1128 case 1:
1129 meter = QObject::tr( "Average" );
1130 break;
1131 case 2:
1132 meter = QObject::tr( "Center weighted average" );
1133 break;
1134 case 3:
1135 meter = QObject::tr( "Spot" );
1136 break;
1137 case 4:
1138 meter = QObject::tr( "MultiSpot" );
1139 break;
1140 case 5:
1141 meter = QObject::tr( "Pattern" );
1142 break;
1143 case 6:
1144 meter = QObject::tr( "Partial" );
1145 break;
1146 case 255:
1147 meter = QObject::tr( "Other" );
1148 break;
1149 default:
1150 meter = QObject::tr( "Unknown" );
1151 }
1152
1153 return QObject::tr( "Metering Mode: %1\n" ).arg( meter );
1154}
1155
1156
1157static QString exposure_program( int i ) {
1158 QString exp;
1159 switch( i ) {
1160 case 0:
1161 exp = QObject::tr( "Not defined" );
1162 break;
1163 case 1:
1164 exp = QObject::tr( "Manual" );
1165 break;
1166 case 2:
1167 exp = QObject::tr( "Normal progam" );
1168 break;
1169 case 3:
1170 exp = QObject::tr( "Aperture priority" );
1171 break;
1172 case 4:
1173 exp = QObject::tr( "Shutter priority" );
1174 break;
1175 case 5:
1176 exp = QObject::tr( "Creative progam\n(biased toward fast shutter speed" );
1177 break;
1178 case 6:
1179 exp = QObject::tr( "Action progam\n(biased toward fast shutter speed)" );
1180 break;
1181 case 7:
1182 exp = QObject::tr( "Portrait mode\n(for closeup photos with the background out of focus)" );
1183 break;
1184 case 8:
1185 exp = QObject::tr( "Landscape mode\n(for landscape photos with the background in focus)" );
1186 break;
1187 default:
1188 exp = QObject::tr( "Unknown" );
1189 }
1190
1191 return QObject::tr( "Exposure Program: %1\n" ).arg( exp );
1192}
1193
1194} // namespace MM
1195} // namespace OPIE
diff --git a/libopie2/opiemm/opieexif.h b/libopie2/opiemm/opieexif.h
new file mode 100644
index 0000000..efaed71
--- a/dev/null
+++ b/libopie2/opiemm/opieexif.h
@@ -0,0 +1,139 @@
1#ifndef _OPIE_EXIF_H
2#define _OPIE_EXIF_H
3
4#include <qt.h>
5#include <qstring.h>
6#include <qimage.h>
7#include <qfile.h>
8
9#include <time.h>
10
11namespace Opie { namespace MM {
12
13#ifndef uchar
14typedef unsigned char uchar;
15#endif
16
17//#define MAX_SECTIONS 20
18//#define PSEUDO_IMAGE_MARKER 0x123; // Extra value.
19
20//! Class for reading exif data from images
21/*!
22 * This class is mostly used inside OImageScrollView for testing jpegs headers for a faster
23 * loading and scaling. It is taken from libexif and converted into an C++ structure.
24 *
25 * \see OImageScrollView
26 * \since 1.2
27 */
28class ExifData {
29public:
30 enum ReadMode_t {
31 READ_EXIF = 1,
32 READ_IMAGE = 2,
33 READ_ALL = 3
34 };
35
36 //--------------------------------------------------------------------------
37 // This structure is used to store jpeg file sections in memory.
38 struct Section_t {
39 uchar * Data;
40 int Type;
41 unsigned Size;
42 };
43
44 struct TagTable_t {
45 unsigned short Tag;
46 const char*const Desc;
47 };
48
49private:
50 static const int MAX_SECTIONS=20;
51 static const unsigned int PSEUDO_IMAGE_MARKER=0x123;
52 Section_t Sections[MAX_SECTIONS];
53
54 QString CameraMake;
55 QString CameraModel;
56 QString DateTime;
57 int Orientation;
58 int Height, Width;
59 int ExifImageLength, ExifImageWidth;
60 int IsColor;
61 int Process;
62 int FlashUsed;
63 float FocalLength;
64 float ExposureTime;
65 float ApertureFNumber;
66 float Distance;
67 int Whitebalance;
68 int MeteringMode;
69 float CCDWidth;
70 float ExposureBias;
71 int ExposureProgram;
72 int ISOequivalent;
73 int CompressionLevel;
74 QString UserComment;
75 QString Comment;
76 QImage Thumbnail;
77
78 unsigned char * LastExifRefd;
79 int ExifSettingsLength;
80 double FocalplaneXRes;
81 double FocalplaneUnits;
82 int MotorolaOrder;
83 int SectionsRead;
84
85 int ReadJpegSections (QFile & infile, ReadMode_t ReadMode);
86 void DiscardData(void);
87 int Get16u(void * Short);
88 int Get32s(void * Long);
89 unsigned Get32u(void * Long);
90 double ConvertAnyFormat(void * ValuePtr, int Format);
91 void ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength);
92 void process_COM (const uchar * Data, int length);
93 void process_SOFn (const uchar * Data, int marker);
94 int Get16m(const void * Short);
95 void process_EXIF(unsigned char * CharBuf, unsigned int length);
96 int Exif2tm(struct ::tm * timeptr, char * ExifTime);
97
98public:
99 //! Contructor for initialising
100 ExifData();
101 //! destructor
102 virtual ~ExifData();
103 //! scan a given file
104 /*!
105 * try to scan the EXIF data of a image file
106 * \param aFile the file to scan
107 * \return true if success, otherwise false
108 */
109 bool scan(const QString &aFile);
110 QString getCameraMake() { return CameraMake; }
111 QString getCameraModel() { return CameraModel; }
112 QString getDateTime() { return DateTime; }
113 int getOrientation() { return Orientation; }
114 int getHeight() { return Height; }
115 int getWidth() { return Width; }
116 int getIsColor() { return IsColor; }
117 int getProcess() { return Process; }
118 int getFlashUsed() { return FlashUsed; }
119 float getFocalLength() { return FocalLength; }
120 float getExposureTime() { return ExposureTime; }
121 float getApertureFNumber() { return ApertureFNumber; }
122 float getDistance() { return Distance; }
123 int getWhitebalance() { return Whitebalance; }
124 int getMeteringMode() { return MeteringMode; }
125 float getCCDWidth() { return CCDWidth; }
126 float getExposureBias() { return ExposureBias; }
127 int getExposureProgram() { return ExposureProgram; }
128 int getISOequivalent() { return ISOequivalent; }
129 int getCompressionLevel() { return CompressionLevel; }
130 QString getUserComment() { return UserComment; }
131 QString getComment() { return Comment; }
132 QImage getThumbnail();
133 bool isThumbnailSane();
134 bool isNullThumbnail() { return !isThumbnailSane(); }
135};
136
137}
138}
139#endif
diff --git a/libopie2/opiemm/opiemm.pro b/libopie2/opiemm/opiemm.pro
index 09b5ed4..32580c8 100644
--- a/libopie2/opiemm/opiemm.pro
+++ b/libopie2/opiemm/opiemm.pro
@@ -1,18 +1,18 @@
1TEMPLATE = lib 1TEMPLATE = lib
2CONFIG += qt warn_on 2CONFIG += qt warn_on
3DESTDIR = $(OPIEDIR)/lib 3DESTDIR = $(OPIEDIR)/lib
4HEADERS = osoundsystem.h oimagezoomer.h oimagescrollview.h 4HEADERS = osoundsystem.h oimagezoomer.h oimagescrollview.h opieexif.h
5SOURCES = osoundsystem.cpp oimagezoomer.cpp oimagescrollview.cpp 5SOURCES = osoundsystem.cpp oimagezoomer.cpp oimagescrollview.cpp opieexif.cpp
6INTERFACES = 6INTERFACES =
7TARGET = opiemm2 7TARGET = opiemm2
8VERSION = 1.9.0 8VERSION = 1.9.0
9INCLUDEPATH += $(OPIEDIR)/include 9INCLUDEPATH += $(OPIEDIR)/include
10DEPENDPATH += $(OPIEDIR)/include 10DEPENDPATH += $(OPIEDIR)/include
11 11
12!contains( platform, x11 ) { 12!contains( platform, x11 ) {
13 include ( $(OPIEDIR)/include.pro ) 13 include ( $(OPIEDIR)/include.pro )
14} 14}
15 15
16contains( platform, x11 ) { 16contains( platform, x11 ) {
17 LIBS = -L$(OPIEDIR)/lib -Wl,-rpath,$(OPIEDIR)/lib 17 LIBS = -L$(OPIEDIR)/lib -Wl,-rpath,$(OPIEDIR)/lib
18} 18}