1 /*
2 * The following code is slightly based on the C# application
3 * SynthExport (http://synthexport.codeplex.com/) by Christoph Hausner
4 * and on the C++ application
5 * PhotoSynthToolkit (http://www.visual-experiments.com/) by Henri Astre
6 */
7
8 #include "synthData.h"
9 #include <math.h>
10 #include <QtSoapMessage>
11 #include <QNetworkAccessManager>
12 #include <QNetworkRequest>
13 #include <QNetworkReply>
14 #include <QScriptEngine>
15 #include <QScriptValue>
16 #include <QScriptValueIterator>
17 #include <QImage>
18 #include <QFile>
19
20 /********************
21 * CameraParameters *
22 ********************/
23
getTranslation()24 Point3m CameraParameters::getTranslation()
25 {
26 return Point3m(_fields[CameraParameters::POS_X], _fields[CameraParameters::POS_Z], -_fields[CameraParameters::POS_Y]);
27 }
28
getRotation()29 Matrix44m CameraParameters::getRotation()
30 {
31 qreal a = _fields[CameraParameters::ROT_X] * _fields[CameraParameters::ROT_X] +
32 _fields[CameraParameters::ROT_Y] * _fields[CameraParameters::ROT_Y] +
33 _fields[CameraParameters::ROT_Z] * _fields[CameraParameters::ROT_Z];
34 qreal w2 = 1 - a;
35 qreal w = sqrt(w2);
36 Quaternion<CMeshO::ScalarType> q(w,_fields[CameraParameters::ROT_X],_fields[CameraParameters::ROT_Y],_fields[CameraParameters::ROT_Z]);
37 Matrix44m rot;
38 q.ToMatrix(rot);
39 Matrix44m flip;
40 flip.SetRotateDeg(180,Point3m(1,0,0));
41 // (rot * flip)^T = flip^T * rot^T
42 Matrix44m transposedFlippedRot = flip.transpose() * rot.transpose();
43
44 Matrix44m rotate90;
45 rotate90.SetRotateDeg(90,Point3m(1,0,0));
46 Matrix44m rotation = transposedFlippedRot * rotate90;
47
48 return rotation;
49 }
50
51 /**************
52 * PointCloud *
53 **************/
54
PointCloud(int coordSysID,int binFileCount,QObject * parent)55 PointCloud::PointCloud(int coordSysID, int binFileCount, QObject *parent)
56 : QObject(parent)
57 {
58 _coordinateSystem = coordSysID;
59 _binFileCount = binFileCount;
60 }
61
62 /********************
63 * CoordinateSystem *
64 ********************/
65
CoordinateSystem(int id,QObject * parent)66 CoordinateSystem::CoordinateSystem(int id, QObject *parent)
67 : QObject(parent)
68 {
69 _id = id;
70 _shouldBeImported = false;
71 _pointCloud = 0;
72 }
73
74 /*************
75 * SynthData *
76 *************/
77
78 //contains the error descriptions relative to the errors that may occurr during the import process
79 //each position index in the array corresponds to the same value in the enum Errors of SynthData class.
80 //These strings are used by applyFilter in case of error.
81 const QString SynthData::errors[] =
82 {
83 "The provided URL is invalid",
84 "Save path is missing: specify one",
85 "The web service returned an error",
86 "The requested Synth is unavailable",
87 "Could not parse web service response: unexpected response",
88 "This filter is compatible with photosynths belonging to \"Synth\" category only",
89 "Error parsing collection data",
90 "This synth is empty",
91 "Error reading binary data, file may be corrupted",
92 "The point cloud is stored in an incompatible format and cannot be loaded",
93 "Error creating output directory for images"
94 "Error saving images to the filesystem"
95 };
96
97 const char *SynthData::steps[] =
98 {
99 "Contacting web service...",
100 "Downloading json data...",
101 "Parsing json data...",
102 "Downloading point cloud bin files...",
103 "Loading point cloud data...",
104 "Downloading images..."
105 };
106
checkAndSetState(bool condition,Error errorCode,QNetworkReply * httpResponse)107 bool SynthData::checkAndSetState(bool condition, Error errorCode, QNetworkReply *httpResponse)
108 {
109 if(condition)
110 setState(errorCode, httpResponse);
111 return condition;
112 }
113
setState(Error errorCode,QNetworkReply * httpResponse)114 void SynthData::setState(Error errorCode, QNetworkReply *httpResponse)
115 {
116 _state = errorCode;
117 _mutex.lock();
118 _dataReady = true;
119 _mutex.unlock();
120 if(httpResponse)
121 httpResponse->deleteLater();
122 }
123
SynthData(ImportSettings & settings,QObject * parent)124 SynthData::SynthData(ImportSettings &settings, QObject *parent)
125 : QObject(parent)
126 {
127 _coordinateSystems = new QList<CoordinateSystem*>();
128 _imageMap = new QHash<int,Image>();
129 _settings = settings;
130 _state = PENDING;
131 _step = WEB_SERVICE;
132 _progress = 0;
133 _mutex.lock();
134 _dataReady = false;
135 _mutex.unlock();
136 _semaphore = 0;
137 _imagesToDownloadCount = 0;
138 }
139
~SynthData()140 SynthData::~SynthData()
141 {
142 delete _coordinateSystems;
143 delete _imageMap;
144 }
145
146 /*
147 * Returns true if this SynthData represents a Synth downloaded from photosynth.net.
148 * Upon a successful download this function returns true if called on the download operation result.
149 * Use this function to determine if the data were successfully downloaded.
150 */
isValid()151 bool SynthData::isValid()
152 {
153 return (_state == SYNTH_NO_ERROR);
154 }
155
156 /*
157 * Useful for cb().
158 * Returns the progress for the current step, and sets info according to the step being executed
159 */
progressInfo()160 int SynthData::progressInfo()
161 {
162 _info = steps[_step];
163 return _progress;
164 }
165
166 /*
167 * Contacts the photosynth web service to retrieve informations about
168 * the synth whose identifier is contained within the given url.
169 */
downloadSynthInfo(vcg::CallBackPos * cb)170 void SynthData::downloadSynthInfo(vcg::CallBackPos *cb)
171 {
172 _cb = cb;
173 _step = WEB_SERVICE;
174 _progress = 0;
175 _cb(progressInfo(),_info.toStdString().data());
176 if(_settings._url.isNull() || _settings._url.isEmpty())
177 {
178 _state = WRONG_URL;
179 _mutex.lock();
180 _dataReady = true;
181 _mutex.unlock();
182 return;
183 }
184
185 if(_settings._imageSavePath.isNull())
186 {
187 _state = WRONG_PATH;
188 _mutex.lock();
189 _dataReady = true;
190 _mutex.unlock();
191 return ;
192 }
193 _savePath = _settings._imageSavePath;
194
195 //extracts the synth identifier
196 int i = _settings._url.indexOf("cid=",0,Qt::CaseInsensitive);
197 if(i < 0 || _settings._url.length() < i + 40)
198 {
199 _state = WRONG_URL;
200 _mutex.lock();
201 _dataReady = true;
202 _mutex.unlock();
203 return;
204 }
205
206 QString cid = _settings._url.mid(i + 4, 36);
207 _collectionID = cid;
208
209 QtSoapMessage message;
210 message.setMethod("GetCollectionData", "http://labs.live.com/");
211 message.addMethodArgument("collectionId", "", cid);
212 message.addMethodArgument("incrementEmbedCount", "", false, 0);
213
214 QtSoapHttpTransport *transport = new QtSoapHttpTransport(this);
215 connect(transport, SIGNAL(responseReady(const QtSoapMessage &)),this, SLOT(readWSresponse(const QtSoapMessage &)));
216 transport->setAction("http://labs.live.com/GetCollectionData");
217 transport->setHost("photosynth.net");
218
219 transport->submitRequest(message, "/photosynthws/PhotosynthService.asmx");
220 _state = PENDING;
221 _progress = 50;
222 _cb(progressInfo(),_info.toStdString().data());
223 }
224
225 /*
226 * Handles the photosynth web service response.
227 */
readWSresponse(const QtSoapMessage & response)228 void SynthData::readWSresponse(const QtSoapMessage &response)
229 {
230 if(checkAndSetState(response.isFault(), WEBSERVICE_ERROR)) return;
231 const QtSoapType &returnValue = response.returnValue();
232 if(returnValue["Result"].isValid())
233 {
234 //the requested synth was found
235 if(returnValue["Result"].toString() == "OK")
236 {
237 //we can only extract point clouds from synths
238 if(returnValue["CollectionType"].toString() == "Synth")
239 {
240 _collectionRoot = returnValue["CollectionRoot"].toString();
241 //the url of the json string containing data about the synth coordinate systems (different clusters of points)
242 //their camera parameters and the number of binary files containing the point clouds data.
243 QString jsonURL = returnValue["JsonUrl"].toString();
244 _progress = 100;
245 _cb(progressInfo(),_info.toStdString().data());
246 downloadJsonData(jsonURL);
247 }
248 else
249 setState(WRONG_COLLECTION_TYPE);
250 }
251 else
252 setState(NEGATIVE_RESPONSE);
253 }
254 else
255 setState(UNEXPECTED_RESPONSE);
256 }
257
258 /*
259 * Performs an HTTP request to download a json string containing data
260 * about the synth coordinate systems (different clusters of points)
261 * their camera parameters and the number of binary files containing
262 * the point clouds data.
263 */
downloadJsonData(QString jsonURL)264 void SynthData::downloadJsonData(QString jsonURL)
265 {
266 _step = DOWNLOAD_JSON;
267 _progress = 0;
268 _cb(progressInfo(),_info.toStdString().data());
269 QNetworkAccessManager *manager = new QNetworkAccessManager(this);
270 connect(manager, SIGNAL(finished(QNetworkReply*)),
271 this, SLOT(parseJsonString(QNetworkReply*)));
272
273 manager->get(QNetworkRequest(QUrl(jsonURL)));
274 _progress = 50;
275 _cb(progressInfo(),_info.toStdString().data());
276 }
277
278 /*
279 * Extracts from the given string informations about the coordinate
280 * systems and their camera parameters.
281 */
parseJsonString(QNetworkReply * httpResponse)282 void SynthData::parseJsonString(QNetworkReply *httpResponse)
283 {
284 _progress = 100;
285 _cb(progressInfo(),_info.toStdString().data());
286 _step = PARSE_JSON;
287 _progress = 0;
288 _cb(progressInfo(),_info.toStdString().data());
289 QByteArray payload = httpResponse->readAll();
290 QString json(payload);
291 QScriptEngine engine;
292 QScriptValue jsonData = engine.evaluate("(" + json + ")");
293 if(checkAndSetState(engine.hasUncaughtException(), JSON_PARSING, httpResponse)) return;
294 //the "l" property contains an object whose name is the synth cid
295 //and whose value is an array containing the coordinate systems
296 QScriptValue collections = jsonData.property("l");
297 if(checkAndSetState(!collections.isValid(), JSON_PARSING, httpResponse)) return;
298 //use an iterator to retrieve the first collection
299 QScriptValueIterator iterator(collections);
300 QScriptValue collection;
301 int coordSystemsCount = 0;
302 if(iterator.hasNext())
303 {
304 iterator.next();
305 collection = iterator.value();
306 coordSystemsCount = collection.property("_num_coord_systems").toInt32();
307 _numImages = collection.property("_num_images").toInt32();
308 }
309 else
310 {
311 setState(JSON_PARSING, httpResponse);
312 return;
313 }
314 if(coordSystemsCount > 0)
315 {
316 //the json object containing the images informations
317 QScriptValue map = collection.property("image_map");
318 parseImageMap(map);
319 CoordinateSystem *coordSys;
320 //the coordinate systems list
321 QScriptValue coordSystems = collection.property("x");
322 for(int i = 0; i <= coordSystemsCount; ++i)
323 {
324 if(_settings._clusterID == -1 || i == _settings._clusterID)
325 {
326 _progress = 50 + (i / (2*coordSystemsCount) * 100);
327 QScriptValue cs = coordSystems.property(QString::number(i));
328 QScriptValue pointCloud = cs.property("k");
329 if(pointCloud.isValid() && !pointCloud.isNull())
330 {
331 //NOTE: we are only interested in coordinate systems having a valid point cloud
332 //so we create one only under this condition, and discard all other ones
333 coordSys = new CoordinateSystem(i,this);
334 coordSys->_shouldBeImported = true;
335 _coordinateSystems->append(coordSys);
336 QScriptValueIterator it(pointCloud);
337 if(it.hasNext())
338 {
339 //pointCloud[0]
340 it.next();
341 QString str = it.value().toString();
342 if(!str.isNull() && !str.isEmpty())
343 {
344 //pointCloud[1]
345 it.next();
346 int binFileCount = it.value().toInt32();
347 PointCloud *p = new PointCloud(i,binFileCount,coordSys);
348 coordSys->_pointCloud = p;
349 }
350 }
351 //list of cameras
352 //NOTE: we are only interested in camera lists belonging to a coordinate system having a valid point cloud
353 //this is the reason why the code below is inside the previous if branch
354 QScriptValue cameras = cs.property("r");
355 if(cameras.isValid() && !cameras.isNull())
356 {
357 QScriptValueIterator it(cameras);
358 while(it.hasNext())
359 {
360 it.next();
361 int id = it.name().toInt();
362 QScriptValue camera = it.value();
363 //contains the camera extrinsics and some of intrinsics
364 QScriptValue parameters = camera.property("j");
365 CameraParameters params;
366 params._camID = id;
367 QScriptValueIterator paramIt(parameters);
368 paramIt.next();
369 params._imageID = paramIt.value().toInt32();
370 Image img = _imageMap->value(params._imageID);
371 img._shouldBeDownloaded = img._shouldBeDownloaded + 1;
372 _imageMap->insert(img._ID,img);
373 _imagesToDownloadCount++;
374 for(int i = CameraParameters::FIRST; i <= CameraParameters::LAST; ++i)
375 {
376 paramIt.next();
377 params[i] = paramIt.value().toNumber();
378 }
379 QScriptValue distortion = camera.property("f");
380 if(distortion.isValid() && !distortion.isNull())
381 {
382 QScriptValueIterator distortionIt(distortion);
383 distortionIt.next();
384 params._distortionRadius1 = distortionIt.value().toNumber();
385 distortionIt.next();
386 params._distortionRadius2 = distortionIt.value().toNumber();
387 }
388 else
389 params._distortionRadius1 = params._distortionRadius2 = 0;
390 coordSys->_cameraParametersList.append(params);
391 }
392 }
393 }
394 }
395 }
396 _progress = 100;
397 _cb(progressInfo(),_info.toStdString().data());
398 downloadBinFiles();
399 }
400 else
401 setState(EMPTY);
402 httpResponse->deleteLater();
403 }
404
405 /*
406 * Parse the image list and, for each element of the list, creates an Image and puts it in the dictionary
407 */
parseImageMap(QScriptValue & map)408 void SynthData::parseImageMap(QScriptValue &map)
409 {
410 QScriptValueIterator imageIt(map);
411 int i = 0;
412 while(imageIt.hasNext())
413 {
414 _progress = i / (2*_numImages) * 100;
415 _cb(progressInfo(),_info.toStdString().data());
416 imageIt.next();
417 Image image;
418 image._shouldBeDownloaded = 0;
419 image._ID = imageIt.name().toInt();
420 QScriptValue size = imageIt.value().property("d");
421 QScriptValueIterator sizeIt(size);
422 sizeIt.next();
423 image._width = sizeIt.value().toInt32();
424 sizeIt.next();
425 image._height = sizeIt.value().toInt32();
426 image._url = imageIt.value().property("u").toString();
427 _imageMap->insert(image._ID,image);
428 ++i;
429 }
430 }
431
432 /*
433 * Asks the server for bin files containing point clouds data
434 * sending an http request for each bin file composing the synth
435 */
downloadBinFiles()436 void SynthData::downloadBinFiles()
437 {
438 _step = DOWNLOAD_BIN;
439 _progress = 0;
440 _cb(progressInfo(),_info.toStdString().data());
441 QNetworkAccessManager *manager = new QNetworkAccessManager(this);
442 connect(manager, SIGNAL(finished(QNetworkReply*)),
443 this, SLOT(loadBinFile(QNetworkReply*)));
444 foreach(CoordinateSystem *sys, *_coordinateSystems)
445 {
446 if(sys->_shouldBeImported && sys->_pointCloud)
447 {
448 //As QNetworkAccessManager API is asynchronous, there is no mean, in the slot handling the response (loadBinFile),
449 //to understand if the response received is the last one, and thus to know if all data have been received.
450 //To let loadBinFile know when the processed response is the last one (allowing the _dataReady variable to be set to true)
451 //the counter _semaphore is used
452 _mutex.lock();
453 _semaphore += sys->_pointCloud->_binFileCount;
454 _mutex.unlock();
455 for(int i = 0; i < sys->_pointCloud->_binFileCount; ++i)
456 {
457 QString url = QString("%0points_%1_%2.bin").arg(_collectionRoot).arg(sys->_id).arg(i);
458 QNetworkRequest *request = new QNetworkRequest(QUrl(url));
459 PointCloud *p = (PointCloud *)sys->_pointCloud;
460 //the slot handling the response (loadBinFile) is able to know which pointCloud the received data belong to
461 //retrieving the originating object of the request whose response is being processed
462 request->setOriginatingObject(p);
463 manager->get(*request);
464 delete request;
465 }
466 }
467 }
468 _totalBinFilesCount = _semaphore;
469 if(_totalBinFilesCount == 0)
470 {
471 _state = SYNTH_NO_ERROR;
472 _mutex.lock();
473 _dataReady = true;
474 _mutex.unlock();
475 }
476 }
477
478 /*
479 * Reads point data from httpResponse payload and fills
480 * the pointCloud of the coordinateSystem relative to the http request
481 * originating httpResponse
482 */
loadBinFile(QNetworkReply * httpResponse)483 void SynthData::loadBinFile(QNetworkReply *httpResponse)
484 {
485 //if(_state == READING_BIN_DATA || _state == BIN_DATA_FORMAT)
486 bool ignore = false;
487 _mutex.lock();
488 ignore = _dataReady;
489 _mutex.unlock();
490 if(ignore)
491 {
492 httpResponse->deleteLater();
493 return;
494 }
495
496 _step = LOADING_BIN;
497 _progress = (_totalBinFilesCount - _semaphore) / _totalBinFilesCount * 100;
498 _cb(progressInfo(),_info.toStdString().data());
499 bool error = false;
500 unsigned short versionMajor = readBigEndianUInt16(httpResponse,error);
501 if(checkAndSetState(error,READING_BIN_DATA,httpResponse))
502 return;
503 unsigned short versionMinor = readBigEndianUInt16(httpResponse,error);
504 if(checkAndSetState(error,READING_BIN_DATA,httpResponse)) return;
505 if(checkAndSetState(versionMajor != 1 || versionMinor != 0, BIN_DATA_FORMAT, httpResponse)) return;
506 int n = readCompressedInt(httpResponse,error);
507 if(checkAndSetState(error,READING_BIN_DATA,httpResponse)) return;
508 //skip the header section
509 for (int i = 0; i < n; i++)
510 {
511 int m = readCompressedInt(httpResponse,error);
512 if(checkAndSetState(error,READING_BIN_DATA,httpResponse)) return;
513 for (int j = 0; j < m; j++)
514 {
515 readCompressedInt(httpResponse,error);
516 if(checkAndSetState(error,READING_BIN_DATA,httpResponse)) return;
517 readCompressedInt(httpResponse,error);
518 if(checkAndSetState(error,READING_BIN_DATA,httpResponse)) return;
519 }
520 }
521
522 int nPoints = readCompressedInt(httpResponse,error);
523 if(checkAndSetState(error,READING_BIN_DATA,httpResponse)) return;
524 for (int i = 0; i < nPoints; i++)
525 {
526 Point point;
527
528 point._x = readBigEndianSingle(httpResponse,error);
529 if(checkAndSetState(error,READING_BIN_DATA,httpResponse)) return;
530 point._y = readBigEndianSingle(httpResponse,error);
531 if(checkAndSetState(error,READING_BIN_DATA,httpResponse)) return;
532 point._z = readBigEndianSingle(httpResponse,error);
533 if(checkAndSetState(error,READING_BIN_DATA,httpResponse)) return;
534
535 ushort color = readBigEndianUInt16(httpResponse,error);
536 if(checkAndSetState(error,READING_BIN_DATA,httpResponse)) return;
537
538 point._r = (uchar)(((color >> 11) * 255) / 31);
539 point._g = (uchar)((((color >> 5) & 63) * 255) / 63);
540 point._b = (uchar)(((color & 31) * 255) / 31);
541 //the cloud whose received points belong to
542 PointCloud *cloud = (PointCloud *)httpResponse->request().originatingObject();
543 cloud->_points.append(point);
544 }
545
546 _mutex.lock();
547 --_semaphore;
548 _mutex.unlock();
549 if(_semaphore == 0)
550 {
551 if(!_savePath.isEmpty())
552 {
553 _progress = 100;
554 _cb(progressInfo(),_info.toStdString().data());
555 downloadImages();
556 }
557 else
558 setState(SYNTH_NO_ERROR);
559 }
560
561 httpResponse->deleteLater();
562 }
563
564 /*
565 * Asks the server for the images making up this synth.
566 * One http request is made for each image.
567 */
downloadImages()568 void SynthData::downloadImages()
569 {
570 _step = DOWNLOAD_IMG;
571 _progress = 0;
572 _cb(progressInfo(),_info.toStdString().data());
573 QDir dir(_savePath);
574 dir.mkdir(_collectionID);
575
576 QNetworkAccessManager *manager = new QNetworkAccessManager(this);
577 connect(manager, SIGNAL(finished(QNetworkReply*)),
578 this, SLOT(saveImages(QNetworkReply*)));
579 int requestCount = 0;
580 foreach(Image img, *_imageMap)
581 {
582 for(int i = 0; i < img._shouldBeDownloaded; ++i)
583 {
584 QNetworkRequest *request = new QNetworkRequest(QUrl(img._url));
585 request->setAttribute(QNetworkRequest::User,QVariant(img._ID));
586 manager->get(*request);
587 delete request;
588 ++requestCount;
589 }
590 }
591 if(requestCount == 0)
592 {
593 _state = SYNTH_NO_ERROR;
594 _mutex.lock();
595 _dataReady = true;
596 _mutex.unlock();
597 }
598 }
599
600 /*
601 * Reads the http response and save the image to the filesystem
602 */
saveImages(QNetworkReply * httpResponse)603 void SynthData::saveImages(QNetworkReply *httpResponse)
604 {
605 //if(_state == SAVE_IMG)
606 bool ignore = false;
607 _mutex.lock();
608 ignore = _dataReady;
609 _mutex.unlock();
610 if(ignore)
611 {
612 httpResponse->deleteLater();
613 return;
614 }
615
616 if(httpResponse->error() != QNetworkReply::NoError)
617 qDebug() << httpResponse->errorString();
618
619 _progress = (int)(_semaphore / _numImages) * 100;
620 _cb(progressInfo(),_info.toStdString().data());
621 QByteArray payload = httpResponse->readAll();
622 QDir dir(_savePath);
623 dir.cd(_collectionID);
624 int id = httpResponse->request().attribute(QNetworkRequest::User).toInt();
625 QString filename("IMG_%1.jpg");
626 QFile file(dir.filePath(filename.arg(QString::number(id))));
627 if(checkAndSetState(!file.open(QIODevice::WriteOnly), SAVE_IMG, httpResponse)) return;
628 if(checkAndSetState(file.write(payload) == -1, SAVE_IMG, httpResponse)) return;
629 file.close();
630
631 _mutex.lock();
632 ++_semaphore;
633 _mutex.unlock();
634 if(checkAndSetState(_semaphore == _imagesToDownloadCount, SYNTH_NO_ERROR, httpResponse)) return;
635
636 httpResponse->deleteLater();
637 }
638
639 /******************
640 * ImportSettings *
641 ******************/
642
ImportSettings(QString url,int clusterID,QString imageSavePath)643 ImportSettings::ImportSettings(QString url, int clusterID, QString imageSavePath)
644 {
645 _url = url;
646 _clusterID = clusterID;
647 _imageSavePath = imageSavePath;
648 }
649
650 /*********************
651 * Utility functions *
652 *********************/
653
654 /*
655 * based on C# code from Christoph Hausner's SynthExport
656 */
readCompressedInt(QIODevice * device,bool & error)657 int readCompressedInt(QIODevice *device, bool &error)
658 {
659 error = false;
660 int i = 0;
661 unsigned char byte;
662
663 do
664 {
665 error = device->read((char *)&byte, sizeof(char)) == -1 ? true : false;
666 if(error)
667 return i;
668 i = (i << 7) | (byte & 127);
669 } while (byte < 128);
670 return i;
671 }
672
673 /*
674 * based on C# code from Christoph Hausner's SynthExport
675 */
readBigEndianSingle(QIODevice * device,bool & error)676 float readBigEndianSingle(QIODevice *device, bool &error)
677 {
678 error = false;
679 unsigned char bytes[4];
680 for(int i = 0; i < 4; ++i)
681 {
682 error = device->read((char *)(bytes + i), sizeof(char)) == -1 ? true : false;
683 if(error)
684 return -1;
685 }
686 unsigned char reversed[] = { bytes[3],bytes[2],bytes[1],bytes[0] };
687
688 float *f = (float *)(& reversed[0]);
689 return*f;
690 }
691
692 /*
693 * based on C# code from Christoph Hausner's SynthExport
694 */
readBigEndianUInt16(QIODevice * device,bool & error)695 unsigned short readBigEndianUInt16(QIODevice *device, bool &error)
696 {
697 error = false;
698 unsigned short byte1=0;
699 error = device->read((char *)&byte1,sizeof(char)) == -1 ? true : false;
700 if(error) return 0;
701 unsigned short byte2=0;
702 error = device->read((char *)&byte2,sizeof(char)) == -1 ? true : false;
703 if(error)
704 return 0;
705
706 return byte2 | (byte1 << 8);
707 }
708
printPoint(Point * p)709 void printPoint(Point *p)
710 {
711 qDebug() << "x =" << p->_x << "; y =" << p->_y << "; z =" << p->_z << "R: " << p->_r << " G: " << p->_g << " B: " << p->_b;
712 }
713