Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
521
qt/screenshoter.cpp
Normal file
521
qt/screenshoter.cpp
Normal file
|
|
@ -0,0 +1,521 @@
|
|||
#include "qt/screenshoter.hpp"
|
||||
|
||||
#include "map/bookmark_helpers.hpp"
|
||||
#include "map/bookmark_manager.hpp"
|
||||
#include "map/framework.hpp"
|
||||
|
||||
#include "storage/country_info_getter.hpp"
|
||||
#include "storage/storage.hpp"
|
||||
|
||||
#include "platform/downloader_defines.hpp"
|
||||
#include "platform/preferred_languages.hpp"
|
||||
|
||||
#include "indexer/scales.hpp"
|
||||
|
||||
#include "base/file_name_utils.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/string_utils.hpp"
|
||||
|
||||
#include <QtGui/QPixmap>
|
||||
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
|
||||
namespace qt
|
||||
{
|
||||
namespace
|
||||
{
|
||||
bool ParsePoint(std::string_view s, char const * delim, m2::PointD & pt, uint8_t & zoom)
|
||||
{
|
||||
// Order in string is: lat, lon, zoom.
|
||||
strings::SimpleTokenizer iter(s, delim);
|
||||
if (!iter)
|
||||
return false;
|
||||
|
||||
double lat;
|
||||
double lon;
|
||||
uint8_t zoomLevel;
|
||||
if (strings::to_double(*iter, lat) && mercator::ValidLat(lat) && ++iter && strings::to_double(*iter, lon) &&
|
||||
mercator::ValidLon(lon) && ++iter && strings::to_uint(*iter, zoomLevel) && zoomLevel >= 1 &&
|
||||
zoomLevel <= scales::GetUpperStyleScale())
|
||||
{
|
||||
pt = mercator::FromLatLon(lat, lon);
|
||||
zoom = zoomLevel;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParseRect(std::string_view s, char const * delim, m2::RectD & rect)
|
||||
{
|
||||
// Order in string is: latLeftBottom, lonLeftBottom, latRigthTop, lonRigthTop.
|
||||
strings::SimpleTokenizer iter(s, delim);
|
||||
if (!iter)
|
||||
return false;
|
||||
|
||||
double latLeftBottom;
|
||||
double lonLeftBottom;
|
||||
double latRigthTop;
|
||||
double lonRigthTop;
|
||||
if (strings::to_double(*iter, latLeftBottom) && mercator::ValidLat(latLeftBottom) && ++iter &&
|
||||
strings::to_double(*iter, lonLeftBottom) && mercator::ValidLon(lonLeftBottom) && ++iter &&
|
||||
strings::to_double(*iter, latRigthTop) && mercator::ValidLat(latRigthTop) && ++iter &&
|
||||
strings::to_double(*iter, lonRigthTop) && mercator::ValidLon(lonRigthTop))
|
||||
{
|
||||
rect =
|
||||
m2::RectD(mercator::FromLatLon(latLeftBottom, lonLeftBottom), mercator::FromLatLon(latRigthTop, lonRigthTop));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParsePointsStr(std::string const & pointsStr, std::list<std::pair<m2::PointD, int>> & points)
|
||||
{
|
||||
strings::SimpleTokenizer tupleIter(pointsStr, ";");
|
||||
m2::PointD pt;
|
||||
uint8_t zoom;
|
||||
while (tupleIter)
|
||||
{
|
||||
if (ParsePoint(*tupleIter, ", \t", pt, zoom))
|
||||
{
|
||||
points.emplace_back(pt, zoom);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(LWARNING, ("Failed to parse point and zoom:", *tupleIter));
|
||||
return false;
|
||||
}
|
||||
++tupleIter;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseRectsStr(std::string const & rectsStr, std::list<m2::RectD> & rects)
|
||||
{
|
||||
strings::SimpleTokenizer tupleIter(rectsStr, ";");
|
||||
m2::RectD rect;
|
||||
while (tupleIter)
|
||||
{
|
||||
if (ParseRect(*tupleIter, ", \t", rect))
|
||||
{
|
||||
rects.push_back(rect);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(LWARNING, ("Failed to parse rect:", *tupleIter));
|
||||
return false;
|
||||
}
|
||||
++tupleIter;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Screenshoter::Screenshoter(ScreenshotParams const & screenshotParams, Framework & framework, QOpenGLWidget * widget)
|
||||
: m_screenshotParams(screenshotParams)
|
||||
, m_framework(framework)
|
||||
, m_widget(widget)
|
||||
{
|
||||
m_framework.GetStorage().Subscribe(std::bind(&Screenshoter::OnCountryChanged, this, std::placeholders::_1),
|
||||
[](storage::CountryId const &, downloader::Progress const &) {});
|
||||
}
|
||||
|
||||
void Screenshoter::Start()
|
||||
{
|
||||
if (m_state != State::NotStarted)
|
||||
return;
|
||||
|
||||
if (m_screenshotParams.m_dstPath.empty())
|
||||
m_screenshotParams.m_dstPath = base::JoinPath(m_screenshotParams.m_kmlPath, "screenshots");
|
||||
|
||||
LOG(LINFO, ("\nScreenshoter parameters:"
|
||||
"\n mode:",
|
||||
m_screenshotParams.m_mode, "\n kml_path:", m_screenshotParams.m_kmlPath,
|
||||
"\n points:", m_screenshotParams.m_points, "\n rects:", m_screenshotParams.m_rects,
|
||||
"\n dst_path:", m_screenshotParams.m_dstPath, "\n width:", m_screenshotParams.m_width,
|
||||
"\n height:", m_screenshotParams.m_height, "\n dpi_scale:", m_screenshotParams.m_dpiScale));
|
||||
|
||||
if (!Platform::MkDirChecked(m_screenshotParams.m_dstPath))
|
||||
{
|
||||
ChangeState(State::FileError);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PrepareItemsToProcess())
|
||||
return;
|
||||
|
||||
ChangeState(State::Ready);
|
||||
ProcessNextItem();
|
||||
}
|
||||
|
||||
void Screenshoter::ProcessNextItem()
|
||||
{
|
||||
CHECK_EQUAL(m_state, State::Ready, ());
|
||||
|
||||
switch (m_screenshotParams.m_mode)
|
||||
{
|
||||
case ScreenshotParams::Mode::KmlFiles: return PrepareToProcessKml();
|
||||
case ScreenshotParams::Mode::Points: return ProcessNextPoint();
|
||||
case ScreenshotParams::Mode::Rects: return ProcessNextRect();
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void Screenshoter::PrepareToProcessKml()
|
||||
{
|
||||
if (m_framework.GetBookmarkManager().AreSymbolSizesAcquired([this] { ProcessNextKml(); }))
|
||||
ProcessNextKml();
|
||||
}
|
||||
|
||||
void Screenshoter::ProcessNextKml()
|
||||
{
|
||||
std::string const postfix = "_" + languages::GetCurrentNorm();
|
||||
std::unique_ptr<kml::FileData> kmlData;
|
||||
while (kmlData == nullptr && !m_filesToProcess.empty())
|
||||
{
|
||||
auto const filePath = m_filesToProcess.front();
|
||||
m_filesToProcess.pop_front();
|
||||
m_nextScreenshotName = base::GetNameFromFullPathWithoutExt(filePath) + postfix;
|
||||
|
||||
ChangeState(State::LoadKml);
|
||||
kmlData = LoadKmlFile(filePath, KmlFileType::Text);
|
||||
if (kmlData != nullptr && kmlData->m_bookmarksData.empty() && kmlData->m_tracksData.empty())
|
||||
kmlData.reset();
|
||||
}
|
||||
|
||||
if (kmlData == nullptr)
|
||||
{
|
||||
ChangeState(State::Done);
|
||||
return;
|
||||
}
|
||||
|
||||
kmlData->m_categoryData.m_visible = true;
|
||||
|
||||
BookmarkManager::KMLDataCollection collection;
|
||||
collection.emplace_back("", std::move(kmlData));
|
||||
|
||||
auto & bookmarkManager = m_framework.GetBookmarkManager();
|
||||
auto es = bookmarkManager.GetEditSession();
|
||||
auto const idList = bookmarkManager.GetUnsortedBmGroupsIdList();
|
||||
for (auto catId : idList)
|
||||
es.DeleteBmCategory(catId, true);
|
||||
|
||||
bookmarkManager.CreateCategories(std::move(collection));
|
||||
|
||||
ChangeState(State::WaitPosition);
|
||||
auto const newCatId = bookmarkManager.GetUnsortedBmGroupsIdList().front();
|
||||
m_framework.ShowBookmarkCategory(newCatId, false);
|
||||
WaitPosition();
|
||||
}
|
||||
|
||||
void Screenshoter::ProcessNextRect()
|
||||
{
|
||||
if (m_rectsToProcess.empty())
|
||||
{
|
||||
ChangeState(State::Done);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string const postfix = "_" + languages::GetCurrentNorm();
|
||||
std::stringstream ss;
|
||||
ss << "rect_" << std::setfill('0') << std::setw(4) << m_itemsCount - m_rectsToProcess.size() << postfix;
|
||||
|
||||
m_nextScreenshotName = ss.str();
|
||||
|
||||
auto const rect = m_rectsToProcess.front();
|
||||
m_rectsToProcess.pop_front();
|
||||
|
||||
CHECK(rect.IsValid(), ());
|
||||
|
||||
ChangeState(State::WaitPosition);
|
||||
m_framework.ShowRect(rect, -1 /* maxScale */, false /* animation */, false /* useVisibleViewport */);
|
||||
WaitPosition();
|
||||
}
|
||||
|
||||
void Screenshoter::ProcessNextPoint()
|
||||
{
|
||||
if (m_pointsToProcess.empty())
|
||||
{
|
||||
ChangeState(State::Done);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string const postfix = "_" + languages::GetCurrentNorm();
|
||||
std::stringstream ss;
|
||||
ss << "point_" << std::setfill('0') << std::setw(4) << m_itemsCount - m_pointsToProcess.size() << postfix;
|
||||
|
||||
m_nextScreenshotName = ss.str();
|
||||
|
||||
auto const pointZoom = m_pointsToProcess.front();
|
||||
m_pointsToProcess.pop_front();
|
||||
|
||||
ChangeState(State::WaitPosition);
|
||||
m_framework.SetViewportCenter(pointZoom.first, pointZoom.second, false /* animation */);
|
||||
WaitPosition();
|
||||
}
|
||||
|
||||
void Screenshoter::PrepareCountries()
|
||||
{
|
||||
CHECK(m_countriesToDownload.empty(), ());
|
||||
|
||||
auto & storage = m_framework.GetStorage();
|
||||
|
||||
int const kMinCountryZoom = 10;
|
||||
if (m_framework.GetDrawScale() >= kMinCountryZoom)
|
||||
{
|
||||
auto countryIds = m_framework.GetCountryInfoGetter().GetRegionsCountryIdByRect(m_framework.GetCurrentViewport(),
|
||||
false /* rough */);
|
||||
for (auto const & countryId : countryIds)
|
||||
{
|
||||
if (storage.CountryStatusEx(countryId) == storage::Status::NotDownloaded)
|
||||
{
|
||||
ChangeState(State::WaitCountries);
|
||||
m_countriesToDownload.insert(countryId);
|
||||
storage.DownloadCountry(countryId, MapFileType::Map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_countriesToDownload.empty())
|
||||
{
|
||||
ChangeState(State::WaitGraphics);
|
||||
WaitGraphics();
|
||||
}
|
||||
}
|
||||
|
||||
void Screenshoter::OnCountryChanged(storage::CountryId countryId)
|
||||
{
|
||||
if (m_state != State::WaitCountries)
|
||||
return;
|
||||
|
||||
auto const status = m_framework.GetStorage().CountryStatusEx(countryId);
|
||||
if (status == storage::Status::OnDisk)
|
||||
{
|
||||
m_countriesToDownload.erase(countryId);
|
||||
if (m_countriesToDownload.empty())
|
||||
{
|
||||
ChangeState(State::WaitGraphics);
|
||||
auto const kDelay = std::chrono::seconds(3);
|
||||
GetPlatform().RunDelayedTask(Platform::Thread::File, kDelay, [this] { WaitGraphics(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Screenshoter::OnPositionReady()
|
||||
{
|
||||
if (m_state != State::WaitPosition)
|
||||
return;
|
||||
PrepareCountries();
|
||||
}
|
||||
|
||||
void Screenshoter::OnGraphicsReady()
|
||||
{
|
||||
if (m_state != State::WaitGraphics)
|
||||
return;
|
||||
|
||||
ChangeState(State::Ready);
|
||||
SaveScreenshot();
|
||||
}
|
||||
|
||||
void Screenshoter::WaitPosition()
|
||||
{
|
||||
m_framework.NotifyGraphicsReady([this]()
|
||||
{ GetPlatform().RunTask(Platform::Thread::Gui, [this] { OnPositionReady(); }); }, false /* needInvalidate */);
|
||||
}
|
||||
|
||||
void Screenshoter::WaitGraphics()
|
||||
{
|
||||
m_framework.NotifyGraphicsReady([this]()
|
||||
{ GetPlatform().RunTask(Platform::Thread::Gui, [this] { OnGraphicsReady(); }); }, true /* needInvalidate */);
|
||||
}
|
||||
|
||||
void Screenshoter::SaveScreenshot()
|
||||
{
|
||||
CHECK_EQUAL(m_state, State::Ready, ());
|
||||
|
||||
if (!Platform::MkDirChecked(m_screenshotParams.m_dstPath))
|
||||
{
|
||||
ChangeState(State::FileError);
|
||||
return;
|
||||
}
|
||||
|
||||
QSize screenshotSize(m_screenshotParams.m_width, m_screenshotParams.m_height);
|
||||
QImage framebuffer = m_widget->grabFramebuffer();
|
||||
ASSERT_EQUAL(framebuffer.width() % screenshotSize.width(), 0, ());
|
||||
ASSERT_EQUAL(framebuffer.height() % screenshotSize.height(), 0, ());
|
||||
QImage screenshot = framebuffer.scaled(screenshotSize);
|
||||
if (!screenshot.save(
|
||||
QString::fromStdString(base::JoinPath(m_screenshotParams.m_dstPath, m_nextScreenshotName + ".png")), "PNG",
|
||||
100))
|
||||
{
|
||||
ChangeState(State::FileError);
|
||||
return;
|
||||
}
|
||||
m_nextScreenshotName.clear();
|
||||
|
||||
ProcessNextItem();
|
||||
}
|
||||
|
||||
void Screenshoter::ChangeState(State newState, std::string const & msg)
|
||||
{
|
||||
m_state = newState;
|
||||
if (m_screenshotParams.m_statusChangedFn)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "[" << m_itemsCount - GetItemsToProcessCount() << "/" << m_itemsCount << "] file: " << m_nextScreenshotName
|
||||
<< " state: " << DebugPrint(newState);
|
||||
if (!msg.empty())
|
||||
ss << " msg: " << msg;
|
||||
LOG(LINFO, (ss.str()));
|
||||
m_screenshotParams.m_statusChangedFn(ss.str(), newState == Screenshoter::State::Done);
|
||||
}
|
||||
}
|
||||
|
||||
size_t Screenshoter::GetItemsToProcessCount()
|
||||
{
|
||||
switch (m_screenshotParams.m_mode)
|
||||
{
|
||||
case ScreenshotParams::Mode::KmlFiles: return m_filesToProcess.size();
|
||||
case ScreenshotParams::Mode::Points: return m_pointsToProcess.size();
|
||||
case ScreenshotParams::Mode::Rects: return m_rectsToProcess.size();
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
bool Screenshoter::PrepareItemsToProcess()
|
||||
{
|
||||
switch (m_screenshotParams.m_mode)
|
||||
{
|
||||
case ScreenshotParams::Mode::KmlFiles: return PrepareKmlFiles();
|
||||
case ScreenshotParams::Mode::Points: return PreparePoints();
|
||||
case ScreenshotParams::Mode::Rects: return PrepareRects();
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
bool Screenshoter::PrepareKmlFiles()
|
||||
{
|
||||
if (!Platform::IsDirectory(m_screenshotParams.m_kmlPath) &&
|
||||
!Platform::IsFileExistsByFullPath(m_screenshotParams.m_kmlPath))
|
||||
{
|
||||
ChangeState(State::FileError, "Invalid kml path.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Platform::IsDirectory(m_screenshotParams.m_kmlPath))
|
||||
{
|
||||
m_filesToProcess.push_back(m_screenshotParams.m_kmlPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
Platform::FilesList files;
|
||||
Platform::GetFilesByExt(m_screenshotParams.m_kmlPath, kKmlExtension, files);
|
||||
for (auto const & file : files)
|
||||
{
|
||||
auto const filePath = base::JoinPath(m_screenshotParams.m_kmlPath, file);
|
||||
m_filesToProcess.push_back(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
m_itemsCount = m_filesToProcess.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Screenshoter::PreparePoints()
|
||||
{
|
||||
if (!LoadPoints(m_screenshotParams.m_points))
|
||||
{
|
||||
ChangeState(State::ParamsError, "Invalid points.");
|
||||
return false;
|
||||
}
|
||||
m_itemsCount = m_pointsToProcess.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Screenshoter::PrepareRects()
|
||||
{
|
||||
if (!LoadRects(m_screenshotParams.m_rects))
|
||||
{
|
||||
ChangeState(State::ParamsError, "Invalid rects.");
|
||||
return false;
|
||||
}
|
||||
m_itemsCount = m_rectsToProcess.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Screenshoter::LoadRects(std::string const & rects)
|
||||
{
|
||||
if (Platform::IsFileExistsByFullPath(rects))
|
||||
{
|
||||
std::ifstream fin(rects);
|
||||
std::string line;
|
||||
while (std::getline(fin, line))
|
||||
{
|
||||
if (!ParseRectsStr(line, m_rectsToProcess))
|
||||
{
|
||||
m_rectsToProcess.clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!ParseRectsStr(rects, m_rectsToProcess))
|
||||
{
|
||||
m_rectsToProcess.clear();
|
||||
return false;
|
||||
}
|
||||
return !m_rectsToProcess.empty();
|
||||
}
|
||||
|
||||
bool Screenshoter::LoadPoints(std::string const & points)
|
||||
{
|
||||
if (Platform::IsFileExistsByFullPath(points))
|
||||
{
|
||||
std::ifstream fin(points);
|
||||
std::string line;
|
||||
while (std::getline(fin, line))
|
||||
{
|
||||
if (!ParsePointsStr(line, m_pointsToProcess))
|
||||
{
|
||||
m_pointsToProcess.clear();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!ParsePointsStr(points, m_pointsToProcess))
|
||||
{
|
||||
m_pointsToProcess.clear();
|
||||
return false;
|
||||
}
|
||||
return !m_pointsToProcess.empty();
|
||||
}
|
||||
|
||||
std::string DebugPrint(Screenshoter::State state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case Screenshoter::State::NotStarted: return "NotStarted";
|
||||
case Screenshoter::State::LoadKml: return "LoadKml";
|
||||
case Screenshoter::State::WaitPosition: return "WaitPosition";
|
||||
case Screenshoter::State::WaitCountries: return "WaitCountries";
|
||||
case Screenshoter::State::WaitGraphics: return "WaitGraphics";
|
||||
case Screenshoter::State::Ready: return "Ready";
|
||||
case Screenshoter::State::FileError: return "FileError";
|
||||
case Screenshoter::State::ParamsError: return "ParamsError";
|
||||
case Screenshoter::State::Done: return "Done";
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
std::string DebugPrint(ScreenshotParams::Mode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ScreenshotParams::Mode::KmlFiles: return "KmlFiles";
|
||||
case ScreenshotParams::Mode::Points: return "Points";
|
||||
case ScreenshotParams::Mode::Rects: return "Rects";
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
} // namespace qt
|
||||
Loading…
Add table
Add a link
Reference in a new issue