comparison layer/ImageLayer.cpp @ 1608:6616e1899daa

Make ImageLayer able to report whether an image format can be opened
author Chris Cannam
date Mon, 11 May 2020 17:28:12 +0100
parents e6362cf5ff1d
children
comparison
equal deleted inserted replaced
1607:52d4bfba5b3d 1608:6616e1899daa
30 #include <QMouseEvent> 30 #include <QMouseEvent>
31 #include <QInputDialog> 31 #include <QInputDialog>
32 #include <QMutexLocker> 32 #include <QMutexLocker>
33 #include <QTextStream> 33 #include <QTextStream>
34 #include <QMessageBox> 34 #include <QMessageBox>
35 #include <QFileInfo>
36 #include <QImageReader>
35 37
36 #include <iostream> 38 #include <iostream>
37 #include <cmath> 39 #include <cmath>
38 40
39 ImageLayer::ImageMap 41 ImageLayer::ImageMap
40 ImageLayer::m_images; 42 ImageLayer::m_images;
41 43
44 ImageLayer::FileSourceMap
45 ImageLayer::m_fileSources;
46
42 QMutex 47 QMutex
43 ImageLayer::m_imageMapMutex; 48 ImageLayer::m_staticMutex;
44 49
45 ImageLayer::ImageLayer() : 50 ImageLayer::ImageLayer() :
46 m_editing(false), 51 m_editing(false),
47 m_editingCommand(nullptr) 52 m_editingCommand(nullptr)
48 { 53 {
462 { 467 {
463 if (dormant) { 468 if (dormant) {
464 // Delete the images named in the view's scaled map from the 469 // Delete the images named in the view's scaled map from the
465 // general image map as well. They can always be re-loaded 470 // general image map as well. They can always be re-loaded
466 // if it turns out another view still needs them. 471 // if it turns out another view still needs them.
467 QMutexLocker locker(&m_imageMapMutex); 472 QMutexLocker locker(&m_staticMutex);
468 for (ImageMap::iterator i = m_scaled[v].begin(); 473 for (ImageMap::iterator i = m_scaled[v].begin();
469 i != m_scaled[v].end(); ++i) { 474 i != m_scaled[v].end(); ++i) {
470 m_images.erase(i->first); 475 m_images.erase(i->first);
471 } 476 }
472 m_scaled.erase(v); 477 m_scaled.erase(v);
478 bool 483 bool
479 ImageLayer::getImageOriginalSize(QString name, QSize &size) const 484 ImageLayer::getImageOriginalSize(QString name, QSize &size) const
480 { 485 {
481 // cerr << "getImageOriginalSize: \"" << name << "\"" << endl; 486 // cerr << "getImageOriginalSize: \"" << name << "\"" << endl;
482 487
483 QMutexLocker locker(&m_imageMapMutex); 488 QMutexLocker locker(&m_staticMutex);
484 if (m_images.find(name) == m_images.end()) { 489 if (m_images.find(name) == m_images.end()) {
485 // cerr << "don't have, trying to open local" << endl; 490 // cerr << "don't have, trying to open local" << endl;
486 m_images[name] = QImage(getLocalFilename(name)); 491 m_images[name] = QImage(getLocalFilename(name));
487 } 492 }
488 if (m_images[name].isNull()) { 493 if (m_images[name].isNull()) {
507 m_scaled[v][name].height() == maxSize.height()))) { 512 m_scaled[v][name].height() == maxSize.height()))) {
508 // cerr << "cache hit" << endl; 513 // cerr << "cache hit" << endl;
509 return m_scaled[v][name]; 514 return m_scaled[v][name];
510 } 515 }
511 516
512 QMutexLocker locker(&m_imageMapMutex); 517 QMutexLocker locker(&m_staticMutex);
513 518
514 if (m_images.find(name) == m_images.end()) { 519 if (m_images.find(name) == m_images.end()) {
515 m_images[name] = QImage(getLocalFilename(name)); 520 m_images[name] = QImage(getLocalFilename(name));
516 } 521 }
517 522
585 590
586 m_editingCommand->remove(m_editingPoint); 591 m_editingCommand->remove(m_editingPoint);
587 592
588 if (dialog.exec() == QDialog::Accepted) { 593 if (dialog.exec() == QDialog::Accepted) {
589 594
590 checkAddSource(dialog.getImage()); 595 checkAddSourceAndConnect(dialog.getImage());
591 596
592 m_editingPoint = m_editingPoint 597 m_editingPoint = m_editingPoint
593 .withURI(dialog.getImage()) 598 .withURI(dialog.getImage())
594 .withLabel(dialog.getLabel()); 599 .withLabel(dialog.getLabel());
595 m_editingCommand->add(m_editingPoint); 600 m_editingCommand->add(m_editingPoint);
599 m_editingCommand = nullptr; 604 m_editingCommand = nullptr;
600 m_editing = false; 605 m_editing = false;
601 } 606 }
602 607
603 bool 608 bool
609 ImageLayer::isImageFileSupported(QString url)
610 {
611 QMutexLocker locker(&m_staticMutex);
612 QString filename = getLocalFilename(url);
613 QString ext = QFileInfo(filename).suffix().toLower();
614 auto formats = QImageReader::supportedImageFormats();
615 for (auto f: formats) {
616 if (QString::fromLatin1(f).toLower() == ext) {
617 return true;
618 }
619 }
620 return false;
621 }
622
623 bool
604 ImageLayer::addImage(sv_frame_t frame, QString url) 624 ImageLayer::addImage(sv_frame_t frame, QString url)
605 { 625 {
606 QImage image(getLocalFilename(url)); 626 {
607 if (image.isNull()) { 627 QMutexLocker locker(&m_staticMutex);
608 cerr << "Failed to open image from url \"" << url << "\" (local filename \"" << getLocalFilename(url) << "\"" << endl; 628 QImage image(getLocalFilename(url));
609 delete m_fileSources[url]; 629 if (image.isNull()) {
610 m_fileSources.erase(url); 630 SVCERR << "Failed to open image from url \"" << url << "\" (local filename \"" << getLocalFilename(url) << "\"" << endl;
611 return false; 631 delete m_fileSources[url];
632 m_fileSources.erase(url);
633 return false;
634 }
612 } 635 }
613 636
614 Event point = Event(frame).withURI(url); 637 Event point = Event(frame).withURI(url);
615 auto command = 638 auto command =
616 new ChangeEventsCommand(m_model.untyped, "Add Image"); 639 new ChangeEventsCommand(m_model.untyped, "Add Image");
695 image, 718 image,
696 label); 719 label);
697 720
698 if (dialog.exec() == QDialog::Accepted) { 721 if (dialog.exec() == QDialog::Accepted) {
699 722
700 checkAddSource(dialog.getImage()); 723 checkAddSourceAndConnect(dialog.getImage());
701 724
702 auto command = 725 auto command =
703 new ChangeEventsCommand(m_model.untyped, tr("Edit Image")); 726 new ChangeEventsCommand(m_model.untyped, tr("Edit Image"));
704 command->remove(*points.begin()); 727 command->remove(*points.begin());
705 command->add(points.begin()-> 728 command->add(points.begin()->
863 finish(command); 886 finish(command);
864 return true; 887 return true;
865 } 888 }
866 889
867 QString 890 QString
868 ImageLayer::getLocalFilename(QString img) const 891 ImageLayer::getLocalFilename(QString img)
869 { 892 {
893 // called with mutex held
894
870 if (m_fileSources.find(img) == m_fileSources.end()) { 895 if (m_fileSources.find(img) == m_fileSources.end()) {
871 checkAddSource(img); 896 checkAddSource(img, false);
872 if (m_fileSources.find(img) == m_fileSources.end()) { 897 if (m_fileSources.find(img) == m_fileSources.end()) {
873 return img; 898 return img;
874 } 899 }
875 } 900 }
901
876 return m_fileSources[img]->getLocalFilename(); 902 return m_fileSources[img]->getLocalFilename();
877 } 903 }
878 904
879 void 905 void
880 ImageLayer::checkAddSource(QString img) const 906 ImageLayer::checkAddSourceAndConnect(QString img)
907 {
908 checkAddSource(img, true);
909
910 QMutexLocker locker(&m_staticMutex);
911 if (m_fileSources.find(img) != m_fileSources.end()) {
912 connect(m_fileSources.at(img), SIGNAL(ready()),
913 this, SLOT(fileSourceReady()));
914 }
915 }
916
917 void
918 ImageLayer::checkAddSource(QString img, bool synchronise)
881 { 919 {
882 SVDEBUG << "ImageLayer::checkAddSource(" << img << "): yes, trying..." << endl; 920 SVDEBUG << "ImageLayer::checkAddSource(" << img << "): yes, trying..." << endl;
921
922 QMutexLocker locker(synchronise ? &m_staticMutex : nullptr);
883 923
884 if (m_fileSources.find(img) != m_fileSources.end()) { 924 if (m_fileSources.find(img) != m_fileSources.end()) {
885 return; 925 return;
886 } 926 }
887 927
888 ProgressDialog dialog(tr("Opening image URL..."), true, 2000); 928 ProgressDialog dialog(tr("Opening image URL..."), true, 2000);
889 FileSource *rf = new FileSource(img, &dialog); 929 FileSource *rf = new FileSource(img, &dialog);
890 if (rf->isOK()) { 930 if (rf->isOK()) {
891 cerr << "ok, adding it (local filename = " << rf->getLocalFilename() << ")" << endl; 931 SVDEBUG << "ok, adding it (local filename = " << rf->getLocalFilename() << ")" << endl;
892 m_fileSources[img] = rf; 932 m_fileSources[img] = rf;
893 connect(rf, SIGNAL(ready()), this, SLOT(fileSourceReady()));
894 } else { 933 } else {
895 delete rf; 934 delete rf;
896 } 935 }
897 } 936 }
898 937
903 const EventVector &points(model->getAllEvents()); 942 const EventVector &points(model->getAllEvents());
904 943
905 for (EventVector::const_iterator i = points.begin(); 944 for (EventVector::const_iterator i = points.begin();
906 i != points.end(); ++i) { 945 i != points.end(); ++i) {
907 946
908 checkAddSource((*i).getURI()); 947 checkAddSourceAndConnect(i->getURI());
909 } 948 }
910 } 949 }
911 950
912 void 951 void
913 ImageLayer::fileSourceReady() 952 ImageLayer::fileSourceReady()
915 // SVDEBUG << "ImageLayer::fileSourceReady" << endl; 954 // SVDEBUG << "ImageLayer::fileSourceReady" << endl;
916 955
917 FileSource *rf = dynamic_cast<FileSource *>(sender()); 956 FileSource *rf = dynamic_cast<FileSource *>(sender());
918 if (!rf) return; 957 if (!rf) return;
919 958
920 QString img; 959 bool shouldEmit = false;
921 for (FileSourceMap::const_iterator i = m_fileSources.begin(); 960
922 i != m_fileSources.end(); ++i) { 961 {
923 if (i->second == rf) { 962 QMutexLocker locker(&m_staticMutex);
924 img = i->first; 963
964 QString img;
965 for (FileSourceMap::const_iterator i = m_fileSources.begin();
966 i != m_fileSources.end(); ++i) {
967 if (i->second == rf) {
968 img = i->first;
925 // cerr << "it's image \"" << img << "\"" << endl; 969 // cerr << "it's image \"" << img << "\"" << endl;
926 break; 970 break;
927 } 971 }
928 } 972 }
929 if (img == "") return; 973 if (img == "") return;
930 974
931 QMutexLocker locker(&m_imageMapMutex); 975 m_images.erase(img);
932 m_images.erase(img); 976 for (ViewImageMap::iterator i = m_scaled.begin(); i != m_scaled.end(); ++i) {
933 for (ViewImageMap::iterator i = m_scaled.begin(); i != m_scaled.end(); ++i) { 977 i->second.erase(img);
934 i->second.erase(img); 978 shouldEmit = true;
979 }
980 }
981
982 if (shouldEmit) {
935 emit modelChanged(getModel()); 983 emit modelChanged(getModel());
936 } 984 }
937 } 985 }
938 986
939 void 987 void