AlbumShaper 1.0a3
photo.cpp
Go to the documentation of this file.
1//==============================================
2// copyright : (C) 2003-2005 by Will Stokes
3//==============================================
4// This program is free software; you can redistribute it
5// and/or modify it under the terms of the GNU General
6// Public License as published by the Free Software
7// Foundation; either version 2 of the License, or
8// (at your option) any later version.
9//==============================================
10//Systemwide includes
11#include <qpixmap.h>
12#include <qimage.h>
13#include <qstring.h>
14#include <q3textstream.h>
15#include <qdom.h>
16#include <qdir.h>
17#include <qfileinfo.h>
18#include <qregexp.h>
19#include <QDateTime>
20
21//Projectwide includes
22#include "photo.h"
23#include "subalbum.h"
24#include "album.h"
25#include "tools/fileTools.h"
26#include "tools/imageTools.h"
27#include "tools/xmlTools.h"
28#include "tools/md5.h"
29#include "../config.h"
30
31//==============================================
32Photo::Photo(Subalbum* subalbum, Photo* prev, int photoNumber)
33{
34 //set subalbum pointer
35 this->subalbum = subalbum;
36
37 //set prev pointer
38 this->prev = prev;
39
40 //set next pointer to NULL, new photos are always
41 //inserted at the end of collections
42 next = NULL;
43
44 //set initial photo and subalbum numbers
45 initialPhotoNumber = photoNumber;
47
48 //set default to the empty string
49 description = QString::null;
50
51 //set thumbnail image
52 thumbnailImage = NULL;
53
54 //set filenames and checksums to null until the actual photo data has been set
55 imageLocation = QString::null;
56 imageChecksum = QString::null;
57
58 slideshowLocation = QString::null;
59 slideshowChecksum = QString::null;
60
61 thumbnailLocation = QString::null;
62 thumbnailChecksum = QString::null;
63
64 //a default photo is not interesting. once the
65 //actual photo data or description files have
66 //been reset the photo will need to be saved.
67 needsSaving = false;
68
69 //by default a photos are assumed to be saved.
70 //new photos are setup with a uniqueID and we'll set this bool false there
71 everSaved = true;
72
73 //photo not recently reverted
74 recentlyReverted = false;
75}
76//==============================================
78{
79 //free the thumbnail image
80 delete thumbnailImage;
81}
82//==============================================
84//==============================================
86{
87 //construct and save slideshow and thumbnail images
88 QImage TslideshowImage, TthumbnailImage;
89 constructImages( imageLocation, TslideshowImage, TthumbnailImage );
90 TslideshowImage.save( slideshowLocation, "JPEG", 95 );
91 TthumbnailImage.save( thumbnailLocation, "JPEG", 95 );
92
93 //load up thumbnail image
94 delete thumbnailImage;
96
97 //image is being stored in temp location, needs saving!
98 needsSaving = true;
99
100 //set the subalbum as being modified and return
102 return true;
103}
104//==============================================
105bool Photo::setImage(QString imageName,
106 QString slideshowName,
107 QString thumbnailName)
108{
109 //set filenames, we'll lazily compute MD5 checksums for files when saving
110 imageLocation = imageName;
111 slideshowLocation = slideshowName;
112 thumbnailLocation = thumbnailName;
113
114 //load thumbnail image
115 delete thumbnailImage;
116 thumbnailImage = new QImage(thumbnailName);
117 if(thumbnailImage->isNull()) return false;
118
119 //image just loaded, no changes yet
120 needsSaving = false;
121 return true;
122}
123//==============================================
124bool Photo::setImage(QString imageName, int uniqueID)
125{
126 //this is a new photo, use a unique ID to construct temporary filenames
127 setEverSaved(false);
129 initialPhotoNumber = uniqueID;
130
131 QString tmpDir = subalbum->getAlbum()->getTmpDir();
132 imageLocation = QString("%1/%2_%3.jpg") .arg(tmpDir).arg(initialSubalbumNumber).arg(initialPhotoNumber);
133 slideshowLocation = QString("%1/%2_%3_slideshow.jpg").arg(tmpDir).arg(initialSubalbumNumber).arg(initialPhotoNumber);
134 thumbnailLocation = QString("%1/%2_%3_thumb.jpg") .arg(tmpDir).arg(initialSubalbumNumber).arg(initialPhotoNumber);
135
136 //if image in jpeg format simply copy file over
137 if( isJpeg(imageName) )
138 {
139 copyFile( imageName, imageLocation );
140 }
141 //otherwise we must load it up and save it out as jpeg
142 else
143 {
144 //if unable to open image at all using Qt then giveup
145 QImage tempImage(imageName);
146 if( tempImage.isNull() ) { return false; }
147
148 //save out as jpeg
149 tempImage.save( imageLocation, "JPEG", 95 );
150 }
151
152 //construct smaller iamges
153 return constructSmallerImages();
154}
155//==============================================
156bool Photo::setImage(QString editedImageFilename)
157{
158 //if the image has been saved then simply change the image,slideshow/thubnail
159 //filename handles to point to the temporary directory. We don't need to worry about backing up the
160 //image because the new version will be written to the temporary directory and the save location
161 if( getEverSaved() )
162 {
164 QString("/%1_%2.jpg").arg(initialSubalbumNumber).arg(initialPhotoNumber);
166 QString("/%1_%2_slideshow.jpg").arg(initialSubalbumNumber).arg(initialPhotoNumber);
168 QString("/%1_%2_thumb.jpg").arg(initialSubalbumNumber).arg(initialPhotoNumber);
169 }
170 //otherwise image has never been saved, make sure original form has been backed up!
171 else
172 {
173 QString tempOrigLocation = imageLocation;
174 tempOrigLocation.truncate( imageLocation.length() - 4 );
175 tempOrigLocation = tempOrigLocation + "_orig.jpg";
176
177 QDir tmpDir;
178 if(!tmpDir.exists( tempOrigLocation ) )
179 { copyFile( imageLocation, tempOrigLocation ); }
180 }
181
182 //copy over full size image
183 copyFile( editedImageFilename, imageLocation );
184
185 //reset revert flag since image has not been modified
186 recentlyReverted = false;
187
188 //construct smaller iamges
189 return constructSmallerImages();
190}
191//==============================================
195
196void Photo::setImageFilename(QString val) { imageLocation = val; }
199//==============================================
203
204void Photo::setImageChecksum(QString val) { imageChecksum = val; }
207//==============================================
208QString Photo::getDescription() { return QString(description); }
209//==============================================
210void Photo::setDescription(QString val)
211{
212 //set empty strings as null, takes up less space and necessary
213 //to check for string modification
214 if( val.isEmpty() )
215 val = QString::null;
216
217 if(description.compare(val) != 0)
218 {
219 description = val;
221 }
222}
223//==============================================
226//==============================================
228{
229 prev = val;
231}
232//==============================================
234{
235 next = val;
237}
238//==============================================
239QDateTime* Photo::importFromDisk(QDomNode* root)
240{
241 //create modified date/time object for returning
242 QDateTime* modified = new QDateTime[3];
243
244 QDomNode node = root->firstChild();
245 QDomText val;
246 while( !node.isNull() )
247 {
248 //------------------------------------------------------------
249 //photo description
250 if( node.isElement() && node.nodeName() == "description" )
251 {
252 val = node.firstChild().toText();
253 if(!val.isNull())
254 description = val.nodeValue();
255 description.replace("\\&quot;","\"");
256 }
257 //------------------------------------------------------------
258 //image information
259 else if( node.isElement() && node.nodeName() == "image" )
260 {
261 QDomNode childNode = node.firstChild();
262 while( !childNode.isNull() )
263 {
264 //------------------------------------------------------------
265 if( childNode.isElement() && childNode.nodeName() == "md5" )
266 {
267 val = childNode.firstChild().toText();
268 if(!val.isNull())
269 imageChecksum = val.nodeValue();
270 }
271 //------------------------------------------------------------
272 else if( childNode.isElement() && childNode.nodeName() == "modified" )
273 {
274 val = childNode.firstChild().toText();
275
276 //split value based on spaces, should be 7 fields
277 QStringList vals = QStringList::split( QRegExp(" "), val.nodeValue() );
278 int i=0;
279 int intVals[7];
280 QStringList::Iterator it;
281 for ( it = vals.begin(); it != vals.end(); ++it )
282 {
283 //sanity check incase more fields are provided than there should be
284 if(i >6)
285 break;
286
287 intVals[i] = QString(*it).toInt();
288 i++;
289 }
290 modified[0].setDate( QDate(intVals[0], intVals[1], intVals[2]) );
291 modified[0].setTime( QTime(intVals[3], intVals[4], intVals[5], intVals[6]) );
292 }
293 //------------------------------------------------------------
294 childNode = childNode.nextSibling();
295 }
296 }
297 //------------------------------------------------------------
298 //slideshow information
299 else if( node.isElement() && node.nodeName() == "slideshow" )
300 {
301 QDomNode childNode = node.firstChild();
302 while( !childNode.isNull() )
303 {
304 //------------------------------------------------------------
305 if( childNode.isElement() && childNode.nodeName() == "md5" )
306 {
307 val = childNode.firstChild().toText();
308 if(!val.isNull())
309 slideshowChecksum = val.nodeValue();
310 }
311 //------------------------------------------------------------
312 else if( childNode.isElement() && childNode.nodeName() == "modified" )
313 {
314 val = childNode.firstChild().toText();
315
316 //split value based on spaces, should be 6 fields
317 QStringList vals = QStringList::split( QRegExp(" "), val.nodeValue() );
318 int i=0;
319 int intVals[7];
320 QStringList::Iterator it;
321 for ( it = vals.begin(); it != vals.end(); ++it )
322 {
323 //sanity check incase more fields are provided than there should be
324 if(i >6)
325 break;
326
327 intVals[i] = QString(*it).toInt();
328 i++;
329 }
330 modified[1].setDate( QDate(intVals[0], intVals[1], intVals[2]) );
331 modified[1].setTime( QTime(intVals[3], intVals[4], intVals[5], intVals[6]) );
332 }
333 //------------------------------------------------------------
334 childNode = childNode.nextSibling();
335 }
336 }
337 //------------------------------------------------------------
338 //slideshow information
339 else if( node.isElement() && node.nodeName() == "thumb" )
340 {
341 QDomNode childNode = node.firstChild();
342 while( !childNode.isNull() )
343 {
344 //------------------------------------------------------------
345 if( childNode.isElement() && childNode.nodeName() == "md5" )
346 {
347 val = childNode.firstChild().toText();
348 if(!val.isNull())
349 thumbnailChecksum = val.nodeValue();
350 }
351 //------------------------------------------------------------
352 else if( childNode.isElement() && childNode.nodeName() == "modified" )
353 {
354 val = childNode.firstChild().toText();
355
356 //split value based on spaces, should be 6 fields
357 QStringList vals = QStringList::split( QRegExp(" "), val.nodeValue() );
358 int i=0;
359 int intVals[7];
360 QStringList::Iterator it;
361 for ( it = vals.begin(); it != vals.end(); ++it )
362 {
363 //sanity check incase more fields are provided than there should be
364 if(i >6)
365 break;
366
367 intVals[i] = QString(*it).toInt();
368 i++;
369 }
370 modified[2].setDate( QDate(intVals[0], intVals[1], intVals[2]) );
371 modified[2].setTime( QTime(intVals[3], intVals[4], intVals[5], intVals[6]) );
372 }
373 //------------------------------------------------------------
374 childNode = childNode.nextSibling();
375 }
376 }
377 //------------------------------------------------------------
378 //------------------------------------------------------------
379 //Handle md5 info as specified in 1.0a and 1.0a2 xml format
380 //image md5
381 else if( node.isElement() && node.nodeName() == "imageMD5" )
382 {
383 val = node.firstChild().toText();
384 if(!val.isNull())
385 imageChecksum = val.nodeValue();
386 }
387 //------------------------------------------------------------
388 //slideshow md5
389 else if( node.isElement() && node.nodeName() == "slideMD5" )
390 {
391 val = node.firstChild().toText();
392 if(!val.isNull())
393 slideshowChecksum = val.nodeValue();
394 }
395 //------------------------------------------------------------
396 //thumbnail md5
397 else if( node.isElement() && node.nodeName() == "thumbMD5" )
398 {
399 val = node.firstChild().toText();
400 if(!val.isNull())
401 thumbnailChecksum = val.nodeValue();
402 }
403 //------------------------------------------------------------
404 //------------------------------------------------------------
405 //advance to next node
406 node = node.nextSibling();
407 //------------------------------------------------------------
408 }
409
410 //return modification dates read in
411 return modified;
412}
413//==============================================
414void Photo::exportToXML(Q3TextStream& stream)
415{
416 QFileInfo info;
417
418 //write photo information
419 stream << " <photo>\n";
420 //-----
421 stream << " <description>" << fixXMLString(description) << "</description>\n";
422 //-----
423 //full image
424 info.setFile( getImageFilename() );
425 QDateTime modified = info.lastModified();
426 stream << " <image>\n";
427 stream << " <md5>" << fixXMLString(imageChecksum) << "</md5>\n";
428 stream << " <modified>";
429 stream << modified.date().year() << " ";
430 stream << modified.date().month() << " ";
431 stream << modified.date().day() << " ";
432 stream << modified.time().hour() << " ";
433 stream << modified.time().minute() << " ";
434 stream << modified.time().second() << " ";
435 stream << modified.time().msec() << "</modified>\n";
436 stream << " </image>\n";
437 //-----
438 //slidehow image
439 info.setFile( getSlideshowFilename() );
440 modified = info.lastModified();
441 stream << " <slideshow>\n";
442 stream << " <md5>" << fixXMLString(slideshowChecksum) << "</md5>\n";
443 stream << " <modified>";
444 stream << modified.date().year() << " ";
445 stream << modified.date().month() << " ";
446 stream << modified.date().day() << " ";
447 stream << modified.time().hour() << " ";
448 stream << modified.time().minute() << " ";
449 stream << modified.time().second() << " ";
450 stream << modified.time().msec() << "</modified>\n";
451 stream << " </slideshow>\n";
452 //-----
453 //thumbnail image
454 info.setFile( getThumbnailFilename() );
455 modified = info.lastModified();
456 stream << " <thumb>\n";
457 stream << " <md5>" << fixXMLString(thumbnailChecksum) << "</md5>\n";
458 stream << " <modified>";
459 stream << modified.date().year() << " ";
460 stream << modified.date().month() << " ";
461 stream << modified.date().day() << " ";
462 stream << modified.time().hour() << " ";
463 stream << modified.time().minute() << " ";
464 stream << modified.time().second() << " ";
465 stream << modified.time().msec() << "</modified>\n";
466 stream << " </thumb>\n";
467 //-----
468 stream << " </photo>\n";
469}
470//==============================================
475//==============================================
477{
478 //backup old filename
479 QString oldName = imageLocation;
480
481 //if the image did not previously need saving,
482 //reset filenames to point to temporary location and
483 //immediately perform transformation
484 if(!needsSaving)
485 {
486 imageLocation = subalbum->getAlbum()->getTmpDir() + QString("/%1_%2.jpg")
488 .arg(initialPhotoNumber);
489 slideshowLocation = subalbum->getAlbum()->getTmpDir() + QString("/%1_%2_slideshow.jpg")
491 .arg(initialPhotoNumber);
492 thumbnailLocation = subalbum->getAlbum()->getTmpDir() + QString("/%1_%2_thumb.jpg")
494 .arg(initialPhotoNumber);
495 transformImage( oldName, imageLocation, transformation );
496 }
497 else
498 {
499 //images that need saving already exist in the temporary directory
500 //this poses two problems:
501 //1.) fast jpeg transformations cannot be done in place, so we'll employ an
502 // intermediate image
503 QString intermediateName = QString("%1_intermdiate.jpg").arg(oldName);
504 transformImage( oldName, intermediateName, transformation );
505
506 //2.) If the photo has never been saved and an orig file in the temporary
507 // directory does not exist then the current file is the original version. we
508 // must make sure that this original photo is maintained using an orig file so
509 // in the future users can revert the photo to it's original form.
510 QString origName = subalbum->getAlbum()->getTmpDir() + QString("/0_%1_orig.jpg")
511 .arg(initialPhotoNumber);
512 QDir tmpDir;
513 if( !getEverSaved() && !tmpDir.exists(origName) )
514 {
515 moveFile( oldName, origName );
516 moveFile( intermediateName, imageLocation );
517 }
518 else
519 {
520 moveFile( intermediateName, imageLocation );
521 }
522 }
523
524 //image now modified from original form so orig file needs to be kept
525 recentlyReverted = false;
526
527 //construct smaller iamges
529}
530//==============================================
532void Photo::setNeedsSavingVal(bool val) { needsSaving = val; }
533//==============================================
535void Photo::setEverSaved(bool val) { everSaved = val; }
536//==============================================
538{
539 //if photo not recently reverted and orig and current filenames differ
540 QString newName = getImageFilename();
541 QString origName = originalImageFilename();
542
543 return ( !recentlyReverted &&
544 origName.compare( newName ) !=0 );
545}
546//==============================================
548{
549 return recentlyReverted;
550}
551//==============================================
553{
554 recentlyReverted = val;
555}
556//==============================================
558{
559 //ignore if revert is not possible
560 if(!revertPossible())
561 return;
562
563 //set image to reverted form
564 QString origName = originalImageFilename();
565 setImage( origName );
566
567 //recently reverted, orig file should be
568 //removed during next save since it's redundant
569 recentlyReverted = true;
570}
571//==============================================
573{
574 //determining the an images original filename is tricky
575 //if the photo has never been saved check for presence of an _orig file,
576 //otherwise use the current filename since the photo has not yet been modified
577 if( !getEverSaved() )
578 {
579 QString tempOrigLocation = imageLocation;
580 tempOrigLocation.truncate( imageLocation.length() - 4 );
581 tempOrigLocation = tempOrigLocation + "_orig.jpg";
582
583 QDir tmpDir;
584 if(tmpDir.exists( tempOrigLocation ) )
585 return tempOrigLocation;
586 else
587 return imageLocation;
588 }
589 //if the photo was previously saved, it's original form could either be:
590 //1.) the permanant storage location + _orig
591 //2.) the permanant storage location
592 else
593 {
594 QString storedOrigLocation = subalbum->getAlbum()->getSaveLocation() +
595 QString("/img/%1/%2_orig.jpg").arg(initialSubalbumNumber).arg(initialPhotoNumber);
596
597 QString lastSavedLocation = subalbum->getAlbum()->getSaveLocation() +
598 QString("/img/%1/%2.jpg").arg(initialSubalbumNumber).arg(initialPhotoNumber);
599
600 QDir tmpDir;
601 if(tmpDir.exists( storedOrigLocation ) )
602 return storedOrigLocation;
603 else
604 return lastSavedLocation;
605 }
606}
607//==============================================
610
613//==============================================
QString getTmpDir()
Returns the temporary directory for use when modifying and adding new images.
Definition album.cpp:142
QString getSaveLocation()
Returns the current save location of all images.
Definition album.cpp:141
A photo consists of a full size image, a smaller slide show image, a very small thumbnail image,...
Definition photo.h:45
void rotate270()
Rotates image clockwise 270 degrees.
Definition photo.cpp:472
bool getRecentlyReverted()
was the photo recently reverted? if so ignore the presence of orig files on disk
Definition photo.cpp:547
void setSlideshowChecksum(QString val)
Update slideshow checksum.
Definition photo.cpp:206
Subalbum * subalbum
Subalbum photo is in.
Definition photo.h:190
QDateTime * importFromDisk(QDomNode *root)
Builds photo from XML DOM node, returns date modified info from xml.
Definition photo.cpp:239
void setThumbnailChecksum(QString val)
Update thumbnail checksum.
Definition photo.cpp:205
bool getNeedsSavingVal()
Returns if the image needs to be saved to its permament location.
Definition photo.cpp:531
bool setImage(QString imageName, QString slideshowName, QString thumbnailName)
Setup photo using preexisting full size, slideshow, and thumbnail images.
Definition photo.cpp:105
void setRecentlyReverted(bool val)
reset the recently reverted value to val
Definition photo.cpp:552
Photo(Subalbum *subalbum, Photo *prev, int photoNumber)
Sets default information.
Definition photo.cpp:32
Photo * getNext()
Returns next photo pointer.
Definition photo.cpp:225
void applyTransformation(TRANSFORM_CODE transformation)
Apply transformation.
Definition photo.cpp:476
void rotate90()
Rotates image clockwise 90 degrees.
Definition photo.cpp:471
bool everSaved
Has the photo ever been saved?
Definition photo.h:224
QString thumbnailChecksum
Definition photo.h:218
QString getThumbnailChecksum()
Get thumbanil checksum.
Definition photo.cpp:201
void setInitialSubalbumNumber(int val)
Sets initial subalbum number.
Definition photo.cpp:612
QString getImageFilename()
Gets the image filename.
Definition photo.cpp:192
void setEverSaved(bool val)
sets everSaved
Definition photo.cpp:535
bool getEverSaved()
Returns if the image has ever been saved to a permanant location.
Definition photo.cpp:534
QString originalImageFilename()
orig filename
Definition photo.cpp:572
Photo * prev
Pointer to prev photo.
Definition photo.h:193
void setNext(Photo *val)
Sets next photo pointer.
Definition photo.cpp:233
QString description
Photo description.
Definition photo.h:205
void setPrev(Photo *val)
Sets prev photo pointer.
Definition photo.cpp:227
void setNeedsSavingVal(bool val)
Sets if the image needs to be saved to its permanent location.
Definition photo.cpp:532
QString thumbnailLocation
Definition photo.h:213
int getInitialSubalbumNumber()
Returns initial subalbum number.
Definition photo.cpp:611
void setThumbnailFilename(QString val)
Sets the thumbnail filename.
Definition photo.cpp:198
QString slideshowChecksum
Definition photo.h:217
void setInitialPhotoNumber(int val)
Sets initial photo number.
Definition photo.cpp:609
void flipVertically()
Flips image about vertical axis.
Definition photo.cpp:474
Photo * next
Pointer to next photo.
Definition photo.h:196
QString getThumbnailFilename()
Gets the thumbnail filename.
Definition photo.cpp:194
QImage * getThumbnailImage()
Gets thumbnail image.
Definition photo.cpp:83
Photo * getPrev()
Returns the previous photo pointer.
Definition photo.cpp:224
void exportToXML(Q3TextStream &stream)
Exports photo to xml.
Definition photo.cpp:414
int getInitialPhotoNumber()
Returns initial photo number.
Definition photo.cpp:608
QString imageChecksum
MD5 checksums, used to determine if image/thumbnail have been changed.
Definition photo.h:216
int initialSubalbumNumber
Definition photo.h:202
QString imageLocation
Filenames
Definition photo.h:211
bool recentlyReverted
Has the photo recently been reverted to it's original form? If so during the next save the orig file ...
Definition photo.h:228
QString getSlideshowChecksum()
Get thumbanil checksum.
Definition photo.cpp:202
void setSlideshowFilename(QString val)
Sets the slideshow filename.
Definition photo.cpp:197
void setDescription(QString val)
Sets the description.
Definition photo.cpp:210
void flipHorizontally()
Flips image about horizontal axis.
Definition photo.cpp:473
QString getImageChecksum()
Get image checksum.
Definition photo.cpp:200
int initialPhotoNumber
Definition photo.h:199
bool needsSaving
Unsaved modifications?
Definition photo.h:221
void setImageFilename(QString val)
Sets the image filename.
Definition photo.cpp:196
bool revertPossible()
can photo be reverted to a differnt original form
Definition photo.cpp:537
QString slideshowLocation
Definition photo.h:212
QString getSlideshowFilename()
Gets the slideshow filename.
Definition photo.cpp:193
QString getDescription()
Gets the description.
Definition photo.cpp:208
~Photo()
Definition photo.cpp:77
QImage * thumbnailImage
Thumbnail Image.
Definition photo.h:208
void revertPhoto()
revert photo to original form
Definition photo.cpp:557
void setImageChecksum(QString val)
Update image checksum.
Definition photo.cpp:204
bool constructSmallerImages()
Construct thumbnail and slideshow images, load up thumbnail image, and set photo has being modified.
Definition photo.cpp:85
A subalbum contains photos.
Definition subalbum.h:49
int getSubalbumNumber()
Returns subalbum number.
Definition subalbum.cpp:103
void setModified()
Definition subalbum.cpp:172
Album * getAlbum()
returns the album pointer
Definition subalbum.cpp:95
bool moveFile(QString oldName, QString newName)
Definition fileTools.cpp:40
bool copyFile(QString oldFilePath, QString newFilePath)
Copies a file from one location to another.
Definition fileTools.cpp:61
void constructImages(QString imageName, QImage &slideshowImage, QImage &thumbnailImage)
Constructs slideshow and thumbnail images for a full sized image.
bool transformImage(QString fileIn, QString fileOut, TRANSFORM_CODE transformation)
Apply image transformation on image
bool isJpeg(const char *filename)
Checks to see if an image is a valid jpg by seeing if the image dimensions can be read.
TRANSFORM_CODE
Definition imageTools.h:25
@ FLIP_V
Definition imageTools.h:29
@ ROTATE_270
Definition imageTools.h:27
@ ROTATE_90
Definition imageTools.h:26
@ FLIP_H
Definition imageTools.h:28
QString fixXMLString(QString text)
Fix strings before exporting to XML such that & becomes &, etc...
Definition xmlTools.cpp:36