Merge pull request #5390 from Beep6581/filebrowser-catalog-speedups

Further speedups for filebrowser/catalog
This commit is contained in:
Ingo Weyrich
2019-08-07 16:06:03 +02:00
committed by GitHub
8 changed files with 176 additions and 108 deletions

View File

@@ -19,11 +19,11 @@
#include "browserfilter.h"
BrowserFilter::BrowserFilter () :
showTrash (true),
showNotTrash (true),
showOriginal (false),
multiselect (false),
exifFilterEnabled (false)
showTrash(true),
showNotTrash(true),
showOriginal(false),
exifFilterEnabled(false),
matchEqual(true)
{
for (int i = 0; i < 6; i++) {
showRanked[i] = true;

View File

@@ -33,13 +33,11 @@ public:
bool showOriginal;
bool showEdited[2];
bool showRecentlySaved[2];
bool multiselect;
Glib::ustring queryString;
Glib::ustring queryFileName;
bool exifFilterEnabled;
bool matchEqual;
ExifFilterSettings exifFilter;
std::vector<std::string> vFilterStrings;
BrowserFilter ();
};

View File

@@ -18,6 +18,7 @@
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <map>
#include <glibmm.h>
@@ -622,7 +623,7 @@ void FileBrowser::addEntry_ (FileBrowserEntry* entry)
initEntry(entry);
}
redraw(false);
redraw(entry);
}
FileBrowserEntry* FileBrowser::delEntry (const Glib::ustring& fname)
@@ -1469,12 +1470,12 @@ void FileBrowser::applyFilter (const BrowserFilter& filter)
redraw ();
}
bool FileBrowser::checkFilter (ThumbBrowserEntryBase* entryb) // true -> entry complies filter
bool FileBrowser::checkFilter (ThumbBrowserEntryBase* entryb) const // true -> entry complies filter
{
FileBrowserEntry* entry = static_cast<FileBrowserEntry*>(entryb);
if (filter.showOriginal && entry->getOriginal() != nullptr) {
if (filter.showOriginal && entry->getOriginal()) {
return false;
}
@@ -1493,44 +1494,22 @@ bool FileBrowser::checkFilter (ThumbBrowserEntryBase* entryb) // true -> entry
return false;
}
// return false is query is not satisfied
if (!filter.queryFileName.empty()) {
// return false if query is not satisfied
if (!filter.vFilterStrings.empty()) {
// check if image's FileName contains queryFileName (case insensitive)
// TODO should we provide case-sensitive search option via preferences?
Glib::ustring FileName;
FileName = Glib::path_get_basename (entry->thumbnail->getFileName());
FileName = FileName.uppercase();
//printf("FileBrowser::checkFilter FileName = '%s'; find() result= %i \n",FileName.c_str(), FileName.find(filter.queryFileName.uppercase()));
Glib::ustring decodedQueryFileName;
bool MatchEqual;
// Determine the match mode - check if the first 2 characters are equal to "!="
if (filter.queryFileName.find("!=") == 0) {
decodedQueryFileName = filter.queryFileName.substr (2, filter.queryFileName.length() - 2);
MatchEqual = false;
} else {
decodedQueryFileName = filter.queryFileName;
MatchEqual = true;
}
// Consider that queryFileName consist of comma separated values (FilterString)
// Evaluate if ANY of these FilterString are contained in the filename
// This will construct OR filter within the filter.queryFileName
std::string FileName = Glib::path_get_basename(entry->thumbnail->getFileName());
std::transform(FileName.begin(), FileName.end(), FileName.begin(), ::toupper);
int iFilenameMatch = 0;
std::vector<Glib::ustring> vFilterStrings = Glib::Regex::split_simple(",", decodedQueryFileName.uppercase());
for(size_t i = 0; i < vFilterStrings.size(); i++) {
// ignore empty vFilterStrings. Otherwise filter will always return true if
// e.g. filter.queryFileName ends on "," and will stop being a filter
if (!vFilterStrings.at(i).empty()) {
if (FileName.find(vFilterStrings.at(i)) != Glib::ustring::npos) {
iFilenameMatch++;
}
for (const auto& entry : filter.vFilterStrings) {
if (FileName.find(entry) != std::string::npos) {
++iFilenameMatch;
break;
}
}
if (MatchEqual) {
if (filter.matchEqual) {
if (iFilenameMatch == 0) { //none of the vFilterStrings found in FileName
return false;
}
@@ -1539,10 +1518,10 @@ bool FileBrowser::checkFilter (ThumbBrowserEntryBase* entryb) // true -> entry
return false;
}
}
}
/*experimental Regex support, this is unlikely to be useful to photographers*/
//bool matchfound=Glib::Regex::match_simple(filter.queryFileName.uppercase(),FileName);
//if (!matchfound) return false;
if (!filter.exifFilterEnabled) {
return true;
}
// check exif filter
@@ -1550,17 +1529,12 @@ bool FileBrowser::checkFilter (ThumbBrowserEntryBase* entryb) // true -> entry
double tol = 0.01;
double tol2 = 1e-8;
if (!filter.exifFilterEnabled) {
return true;
}
Glib::ustring camera(cfs->getCamera());
if (!cfs->exifValid)
return (!filter.exifFilter.filterCamera || filter.exifFilter.cameras.count(camera) > 0)
if (!cfs->exifValid) {
return (!filter.exifFilter.filterCamera || filter.exifFilter.cameras.count(cfs->getCamera()) > 0)
&& (!filter.exifFilter.filterLens || filter.exifFilter.lenses.count(cfs->lens) > 0)
&& (!filter.exifFilter.filterFiletype || filter.exifFilter.filetypes.count(cfs->filetype) > 0)
&& (!filter.exifFilter.filterExpComp || filter.exifFilter.expcomp.count(cfs->expcomp) > 0);
}
return
(!filter.exifFilter.filterShutter || (rtengine::FramesMetaData::shutterFromString(rtengine::FramesMetaData::shutterToString(cfs->shutter)) >= filter.exifFilter.shutterFrom - tol2 && rtengine::FramesMetaData::shutterFromString(rtengine::FramesMetaData::shutterToString(cfs->shutter)) <= filter.exifFilter.shutterTo + tol2))
@@ -1568,7 +1542,7 @@ bool FileBrowser::checkFilter (ThumbBrowserEntryBase* entryb) // true -> entry
&& (!filter.exifFilter.filterFocalLen || (cfs->focalLen >= filter.exifFilter.focalFrom - tol && cfs->focalLen <= filter.exifFilter.focalTo + tol))
&& (!filter.exifFilter.filterISO || (cfs->iso >= filter.exifFilter.isoFrom && cfs->iso <= filter.exifFilter.isoTo))
&& (!filter.exifFilter.filterExpComp || filter.exifFilter.expcomp.count(cfs->expcomp) > 0)
&& (!filter.exifFilter.filterCamera || filter.exifFilter.cameras.count(camera) > 0)
&& (!filter.exifFilter.filterCamera || filter.exifFilter.cameras.count(cfs->getCamera()) > 0)
&& (!filter.exifFilter.filterLens || filter.exifFilter.lenses.count(cfs->lens) > 0)
&& (!filter.exifFilter.filterFiletype || filter.exifFilter.filetypes.count(cfs->filetype) > 0);
}

View File

@@ -168,7 +168,7 @@ public:
void buttonPressed (LWButton* button, int actionCode, void* actionData) override;
void redrawNeeded (LWButton* button) override;
bool checkFilter (ThumbBrowserEntryBase* entry) override;
bool checkFilter (ThumbBrowserEntryBase* entry) const override;
void rightClicked (ThumbBrowserEntryBase* entry) override;
void doubleClicked (ThumbBrowserEntryBase* entry) override;
bool keyPressed (GdkEventKey* event) override;

View File

@@ -251,32 +251,32 @@ void FileBrowserEntry::_updateImage(rtengine::IImage8* img, double s, const rten
bool newLandscape = img->getWidth() > img->getHeight();
bool rotated = false;
if (preh == img->getHeight ()) {
if (preh == img->getHeight()) {
const bool resize = !preview || prew != img->getWidth();
prew = img->getWidth ();
GThreadLock lock;
// Check if image has been rotated since last time
rotated = preview != nullptr && newLandscape != landscape;
rotated = preview && newLandscape != landscape;
guint8* temp = preview;
preview = nullptr;
delete [] temp;
temp = new guint8 [prew * preh * 3];
memcpy (temp, img->getData(), prew * preh * 3);
preview = temp;
if (resize) {
delete [] preview;
preview = new guint8 [prew * preh * 3];
}
memcpy(preview, img->getData(), prew * preh * 3);
updateBackBuffer ();
}
landscape = newLandscape;
img->free ();
img->free();
if (parent != nullptr) {
if (parent) {
if (rotated) {
parent->thumbRearrangementNeeded();
} else if (redrawRequests == 0) {
parent->redrawNeeded (this);
parent->redrawNeeded(this);
}
}
}

View File

@@ -804,28 +804,28 @@ void FileCatalog::previewsFinishedUI ()
{
GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected
redrawAll ();
redrawAll();
previewsToLoad = 0;
if (filterPanel) {
filterPanel->set_sensitive (true);
filterPanel->set_sensitive(true);
if ( !hasValidCurrentEFS ) {
MyMutex::MyLock lock(dirEFSMutex);
if (!hasValidCurrentEFS) {
MyMutex::MyLock myLock(dirEFSMutex);
currentEFS = dirEFS;
filterPanel->setFilter ( dirEFS, true );
filterPanel->setFilter(dirEFS, true);
} else {
filterPanel->setFilter ( currentEFS, false );
filterPanel->setFilter(currentEFS, false);
}
}
if (exportPanel) {
exportPanel->set_sensitive (true);
exportPanel->set_sensitive(true);
}
// restart anything that might have been loaded low quality
fileBrowser->refreshQuickThumbImages();
fileBrowser->applyFilter (getFilter()); // refresh total image count
fileBrowser->applyFilter(getFilter()); // refresh total image count
_refreshProgressBar();
}
filepanel->loadingThumbs(M("PROGRESSBAR_READY"), 0);
@@ -1603,7 +1603,6 @@ BrowserFilter FileCatalog::getFilter ()
anyRankFilterActive || anyCLabelFilterActive || anyEditedFilterActive;
}
filter.multiselect = false;
/*
* Step 2
@@ -1619,7 +1618,6 @@ BrowserFilter FileCatalog::getFilter ()
(anyEditedFilterActive && anyRecentlySavedFilterActive) ||
(anySupplementaryActive && (anyRankFilterActive || anyCLabelFilterActive || anyEditedFilterActive || anyRecentlySavedFilterActive))) {
filter.multiselect = true;
filter.showRanked[0] = anyRankFilterActive ? bUnRanked->get_active () : true;
filter.showCLabeled[0] = anyCLabelFilterActive ? bUnCLabeled->get_active () : true;
@@ -1656,14 +1654,28 @@ BrowserFilter FileCatalog::getFilter ()
//TODO could use date:<value>;iso:<value> etc
// default will be filename
/* // this is for safe execution if getFilter is called before Query object is instantiated
Glib::ustring tempQuery;
tempQuery="";
if (Query) tempQuery = Query->get_text();
*/
filter.queryString = Query->get_text(); // full query string from Query Entry
filter.queryFileName = Query->get_text(); // for now Query is only by file name
Glib::ustring decodedQueryFileName = Query->get_text(); // for now Query is only by file name
// Determine the match mode - check if the first 2 characters are equal to "!="
if (decodedQueryFileName.find("!=") == 0) {
decodedQueryFileName = decodedQueryFileName.substr(2);
filter.matchEqual = false;
} else {
filter.matchEqual = true;
}
// Consider that queryFileName consist of comma separated values (FilterString)
// Evaluate if ANY of these FilterString are contained in the filename
// This will construct OR filter within the queryFileName
filter.vFilterStrings.clear();
const std::vector<Glib::ustring> filterStrings = Glib::Regex::split_simple(",", decodedQueryFileName.uppercase());
for (const auto& entry : filterStrings) {
// ignore empty filterStrings. Otherwise filter will always return true if
// e.g. queryFileName ends on "," and will stop being a filter
if (!entry.empty()) {
filter.vFilterStrings.push_back(entry);
}
}
return filter;
}

View File

@@ -28,7 +28,7 @@
using namespace std;
ThumbBrowserBase::ThumbBrowserBase ()
: location(THLOC_FILEBROWSER), inspector(nullptr), isInspectorActive(false), eventTime(0), lastClicked(nullptr), anchor(nullptr), previewHeight(options.thumbSize), numOfCols(1), arrangement(TB_Horizontal)
: location(THLOC_FILEBROWSER), inspector(nullptr), isInspectorActive(false), eventTime(0), lastClicked(nullptr), anchor(nullptr), previewHeight(options.thumbSize), numOfCols(1), lastRowHeight(0), arrangement(TB_Horizontal)
{
inW = -1;
inH = -1;
@@ -44,6 +44,8 @@ ThumbBrowserBase::ThumbBrowserBase ()
show_all ();
vscroll.get_adjustment()->set_lower(0);
hscroll.get_adjustment()->set_lower(0);
vscroll.signal_value_changed().connect( sigc::mem_fun(*this, &ThumbBrowserBase::scrollChanged) );
hscroll.signal_value_changed().connect( sigc::mem_fun(*this, &ThumbBrowserBase::scrollChanged) );
@@ -545,7 +547,6 @@ void ThumbBrowserBase::configScrollBars ()
auto ha = hscroll.get_adjustment();
int iw = internal.get_width();
ha->set_upper(inW);
ha->set_lower(0);
ha->set_step_increment(!fd.empty() ? fd[0]->getEffectiveWidth() : 0);
ha->set_page_increment(iw);
ha->set_page_size(iw);
@@ -560,7 +561,6 @@ void ThumbBrowserBase::configScrollBars ()
auto va = vscroll.get_adjustment();
va->set_upper(inH);
va->set_lower(0);
const auto height = !fd.empty() ? fd[0]->getEffectiveHeight() : 0;
va->set_step_increment(height);
va->set_page_increment(height == 0 ? ih : (ih / height) * height);
@@ -574,8 +574,22 @@ void ThumbBrowserBase::configScrollBars ()
}
}
void ThumbBrowserBase::arrangeFiles(bool checkfilter)
void ThumbBrowserBase::arrangeFiles(ThumbBrowserEntryBase* entry)
{
if (fd.empty()) {
// nothing to arrange
resizeThumbnailArea(0, 0);
return;
}
if(entry && entry->filtered) {
// a filtered entry was added, nothing to arrange, but has to be marked not drawable
MYREADERLOCK(l, entryRW);
entry->drawable = false;
MYREADERLOCK_RELEASE(l);
return;
}
MYREADERLOCK(l, entryRW);
// GUI already locked by ::redraw, the only caller of this method for now.
@@ -583,17 +597,22 @@ void ThumbBrowserBase::arrangeFiles(bool checkfilter)
//GThreadLock lock;
int rowHeight = 0;
for (const auto entry : fd) {
if (checkfilter) {
// apply filter
entry->filtered = !checkFilter(entry);
}
if (entry) {
// we got the reference to the added entry, makes calculation of rowHeight O(1)
lastRowHeight = rowHeight = std::max(lastRowHeight, entry->getMinimalHeight());
} else {
// compute size of the items
if (!entry->filtered) {
rowHeight = std::max(entry->getMinimalHeight(), rowHeight);
lastRowHeight = 0;
for (const auto thumb : fd) {
// apply filter
thumb->filtered = !checkFilter(thumb);
// compute max rowHeight
if (!thumb->filtered) {
rowHeight = std::max(thumb->getMinimalHeight(), rowHeight);
}
}
}
if (arrangement == TB_Horizontal) {
numOfCols = 1;
@@ -601,7 +620,6 @@ void ThumbBrowserBase::arrangeFiles(bool checkfilter)
for (unsigned int ct = 0; ct < fd.size(); ++ct) {
// arrange items in the column
int curry = 0;
for (; ct < fd.size() && fd[ct]->filtered; ++ct) {
fd[ct]->drawable = false;
@@ -610,10 +628,9 @@ void ThumbBrowserBase::arrangeFiles(bool checkfilter)
if (ct < fd.size()) {
const int maxw = fd[ct]->getMinimalWidth();
fd[ct]->setPosition(currx, curry, maxw, rowHeight);
fd[ct]->setPosition(currx, 0, maxw, rowHeight);
fd[ct]->drawable = true;
currx += maxw;
curry += rowHeight;
}
}
@@ -624,14 +641,16 @@ void ThumbBrowserBase::arrangeFiles(bool checkfilter)
const int availWidth = internal.get_width();
// initial number of columns
int oldNumOfCols = numOfCols;
numOfCols = 0;
int colsWidth = 0;
for (unsigned int i = 0; i < fd.size(); ++i) {
if (!fd[i]->filtered && colsWidth + fd[i]->getMinimalWidth() <= availWidth) {
colsWidth += fd[numOfCols]->getMinimalWidth();
colsWidth += fd[i]->getMinimalWidth();
++numOfCols;
if(colsWidth > availWidth) {
--numOfCols;
break;
}
}
@@ -667,8 +686,73 @@ void ThumbBrowserBase::arrangeFiles(bool checkfilter)
// arrange files
int curry = 0;
size_t ct = 0;
if (entry) {
std::vector<int> oldColWidths;
if (oldNumOfCols == numOfCols) {
for (; oldNumOfCols > 0; --oldNumOfCols) {
// compute old column widths
oldColWidths.assign(oldNumOfCols, 0);
for (unsigned int i = 0, j = 0; i < fd.size(); ++i) {
if (fd[i] != entry && !fd[i]->filtered && fd[i]->getMinimalWidth() > oldColWidths[j % oldNumOfCols]) {
oldColWidths[j % oldNumOfCols] = fd[i]->getMinimalWidth();
}
if (fd[i] != entry && !fd[i]->filtered) {
++j;
}
}
if (oldNumOfCols == 1 || std::accumulate(oldColWidths.begin(), oldColWidths.end(), 0) < availWidth) {
break;
}
}
}
bool arrangeAll = true;
if (oldNumOfCols == numOfCols) {
arrangeAll = false;
for (int i = 0; i < numOfCols; ++i) {
if(colWidths[i] != oldColWidths[i]) {
arrangeAll = true;
break;
}
}
}
if (!arrangeAll) {
int j = 0;
// Find currently added entry
for (; ct < fd.size() && fd[ct] != entry; j += !fd[ct]->filtered, ++ct) {
}
//Calculate the position of currently added entry
const int row = j / numOfCols;
const int col = j % numOfCols;
curry = row * rowHeight;
int currx = 0;
for (int c = 0; c < col; ++c) {
currx += colWidths[c];
}
// arrange all entries in the row beginning with the currently added one
for (int i = col; ct < fd.size() && i < numOfCols; ++i, ++ct) {
for (; ct < fd.size() && fd[ct]->filtered; ++ct) {
fd[ct]->drawable = false;
}
if (ct < fd.size()) {
fd[ct]->setPosition(currx, curry, colWidths[i], rowHeight);
fd[ct]->drawable = true;
currx += colWidths[i];
}
}
if (currx > 0) { // there were thumbnails placed in the row
curry += rowHeight;
}
}
}
// arrange remaining entries, if any, that's the most expensive part
for (; ct < fd.size();) {
for (unsigned int ct = 0; ct < fd.size();) {
// arrange items in the row
int currx = 0;
@@ -691,7 +775,7 @@ void ThumbBrowserBase::arrangeFiles(bool checkfilter)
MYREADERLOCK_RELEASE(l);
// This will require a Writer access
resizeThumbnailArea (colsWidth, curry);
resizeThumbnailArea(colsWidth, curry);
}
}
@@ -779,7 +863,6 @@ void ThumbBrowserBase::on_style_updated ()
ThumbBrowserBase::Internal::Internal () : ofsX(0), ofsY(0), parent(nullptr), dirty(true)
{
Glib::RefPtr<Gtk::StyleContext> style = get_style_context();
set_name("FileCatalog");
}
@@ -985,11 +1068,11 @@ bool ThumbBrowserBase::Internal::on_scroll_event (GdkEventScroll* event)
}
void ThumbBrowserBase::redraw (bool checkfilter)
void ThumbBrowserBase::redraw (ThumbBrowserEntryBase* entry)
{
GThreadLock lock;
arrangeFiles(checkfilter);
arrangeFiles(entry);
queue_draw();
}

View File

@@ -175,12 +175,13 @@ protected:
int previewHeight;
int numOfCols;
int lastRowHeight;
Arrangement arrangement;
std::set<Glib::ustring> editedFiles;
void arrangeFiles (bool checkfilter = true);
void arrangeFiles (ThumbBrowserEntryBase* entry = nullptr);
void zoomChanged (bool zoomIn);
public:
@@ -202,7 +203,7 @@ public:
return fd;
}
void on_style_updated () override;
void redraw (bool checkfilter = true); // arrange files and draw area
void redraw (ThumbBrowserEntryBase* entry = nullptr); // arrange files and draw area
void refreshThumbImages (); // refresh thumbnail sizes, re-generate thumbnail images, arrange and draw
void refreshQuickThumbImages (); // refresh thumbnail sizes, re-generate thumbnail images, arrange and draw
void refreshEditedState (const std::set<Glib::ustring>& efiles);
@@ -215,7 +216,7 @@ public:
void setArrangement (Arrangement a);
void enableTabMode(bool enable); // set both thumb sizes and arrangements
virtual bool checkFilter (ThumbBrowserEntryBase* entry)
virtual bool checkFilter (ThumbBrowserEntryBase* entry) const
{
return true;
}