Mercurial > hg > svgui
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 |