2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TnzQt includes
|
|
|
|
|
#include "toonzqt/menubarcommand.h"
|
|
|
|
|
#include "toonzqt/viewcommandids.h"
|
|
|
|
|
#include "toonzqt/imageutils.h"
|
|
|
|
|
#include "toonzqt/dvdialog.h"
|
|
|
|
|
#include "toonzqt/gutil.h"
|
|
|
|
|
|
2021-02-24 06:12:28 +13:00
|
|
|
|
// Qt Includes:
|
|
|
|
|
#include <QScreen>
|
|
|
|
|
#include <QWindow>
|
|
|
|
|
|
2016-03-19 06:57:51 +13:00
|
|
|
|
// TnzLib includes
|
|
|
|
|
#include "toonz/preferences.h"
|
|
|
|
|
#include "toonz/namebuilder.h"
|
|
|
|
|
#include "toonz/toonzscene.h"
|
|
|
|
|
#include "toonz/tproject.h"
|
|
|
|
|
#include "toonz/Naa2TlvConverter.h"
|
2018-07-12 18:04:49 +12:00
|
|
|
|
#include "toonz/toonzimageutils.h"
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-04-15 17:11:23 +12:00
|
|
|
|
#ifdef _WIN32
|
2016-03-19 06:57:51 +13:00
|
|
|
|
#include "avicodecrestrictions.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// TnzCore includes
|
|
|
|
|
#include "tsystem.h"
|
|
|
|
|
#include "tfilepath_io.h"
|
|
|
|
|
#include "timage_io.h"
|
|
|
|
|
#include "tlevel.h"
|
|
|
|
|
#include "tlevel_io.h"
|
|
|
|
|
#include "tpalette.h"
|
|
|
|
|
#include "tropcm.h"
|
|
|
|
|
#include "trasterimage.h"
|
|
|
|
|
#include "tvectorrenderdata.h"
|
|
|
|
|
#include "tofflinegl.h"
|
|
|
|
|
#include "tconvert.h"
|
|
|
|
|
#include "trop.h"
|
|
|
|
|
#include "timageinfo.h"
|
|
|
|
|
#include "tfiletype.h"
|
|
|
|
|
#include "tfilepath.h"
|
|
|
|
|
#include "ttoonzimage.h"
|
|
|
|
|
#include "tvectorimage.h"
|
|
|
|
|
#include "tstroke.h"
|
|
|
|
|
|
|
|
|
|
// TnzQt includes
|
|
|
|
|
#include <QApplication>
|
2017-10-19 00:45:22 +13:00
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
#include <QtPlatformHeaders/QWindowsWindowFunctions>
|
|
|
|
|
#endif
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
|
|
// boost includes
|
|
|
|
|
#include <boost/iterator/transform_iterator.hpp>
|
|
|
|
|
|
|
|
|
|
//**********************************************************************************
|
|
|
|
|
// Local namespace stuff
|
|
|
|
|
//**********************************************************************************
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
namespace {
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
void addOverlappedRegions(TRegion *reg, std::vector<TFilledRegionInf> ®Inf) {
|
|
|
|
|
regInf.push_back(TFilledRegionInf(reg->getId(), reg->getStyle()));
|
|
|
|
|
UINT regNum = reg->getSubregionCount();
|
|
|
|
|
for (UINT i = 0; i < regNum; i++)
|
|
|
|
|
addOverlappedRegions(reg->getSubregion(i), regInf);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
void addRegionsInArea(TRegion *reg, std::vector<TFilledRegionInf> ®s,
|
|
|
|
|
const TRectD &area) {
|
|
|
|
|
if (area.contains(reg->getBBox()))
|
|
|
|
|
regs.push_back(TFilledRegionInf(reg->getId(), reg->getStyle()));
|
|
|
|
|
|
|
|
|
|
if (reg->getBBox().overlaps(area)) {
|
|
|
|
|
UINT regNum = reg->getSubregionCount();
|
|
|
|
|
for (UINT i = 0; i < regNum; i++)
|
|
|
|
|
addRegionsInArea(reg->getSubregion(i), regs, area);
|
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void getFrameIds(TFrameId from, TFrameId to, const TLevelP &level,
|
2016-06-15 18:43:10 +12:00
|
|
|
|
std::vector<TFrameId> &frames) {
|
|
|
|
|
struct locals {
|
|
|
|
|
static inline TFrameId getFrame(const TLevel::Table::value_type &pair) {
|
|
|
|
|
return pair.first;
|
|
|
|
|
}
|
|
|
|
|
}; // locals
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
if (from.isEmptyFrame()) from = TFrameId(-(std::numeric_limits<int>::max)());
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
if (to.isEmptyFrame()) to = TFrameId((std::numeric_limits<int>::max)());
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2018-08-30 20:18:43 +12:00
|
|
|
|
if (from > to) std::swap(from, to);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
const TLevel::Table &table = *level->getTable();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
TLevel::Table::const_iterator lBegin = table.lower_bound(from),
|
|
|
|
|
lEnd = table.upper_bound(to);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
assert(frames.empty());
|
|
|
|
|
frames.insert(frames.end(),
|
|
|
|
|
boost::make_transform_iterator(lBegin, locals::getFrame),
|
|
|
|
|
boost::make_transform_iterator(lEnd, locals::getFrame));
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
} // namespace
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
|
|
//**********************************************************************************
|
|
|
|
|
// ImageUtils namespace stuff
|
|
|
|
|
//**********************************************************************************
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
namespace ImageUtils {
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
// todo: spostare da qualche altra parte. rendere accessibile a tutti.
|
|
|
|
|
// utilizzare
|
2016-03-19 06:57:51 +13:00
|
|
|
|
// anche nella libreria dovunque si usi direttamente "_files"
|
2016-07-14 18:51:27 +12:00
|
|
|
|
static TFilePath getResourceFolder(const TFilePath &scenePath) {
|
2016-06-15 18:43:10 +12:00
|
|
|
|
return scenePath.getParentDir() + (scenePath.getName() + "_files");
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2016-07-14 18:51:27 +12:00
|
|
|
|
static void copyScene(const TFilePath &dst, const TFilePath &src) {
|
2016-06-15 18:43:10 +12:00
|
|
|
|
TSystem::copyFile(dst, src);
|
|
|
|
|
if (TProjectManager::instance()->isTabModeEnabled())
|
|
|
|
|
TSystem::copyDir(getResourceFolder(dst), getResourceFolder(src));
|
|
|
|
|
TFilePath srcIcon = ToonzScene::getIconPath(src);
|
|
|
|
|
if (TFileStatus(srcIcon).doesExist()) {
|
|
|
|
|
TFilePath dstIcon = ToonzScene::getIconPath(dst);
|
|
|
|
|
TSystem::copyFile(dstIcon, srcIcon);
|
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
TFilePath duplicate(const TFilePath &levelPath) {
|
|
|
|
|
if (levelPath == TFilePath()) return TFilePath();
|
|
|
|
|
|
|
|
|
|
if (!TSystem::doesExistFileOrLevel(levelPath)) {
|
|
|
|
|
DVGui::warning(
|
|
|
|
|
QObject::tr("It is not possible to find the %1 level.")
|
|
|
|
|
.arg(QString::fromStdWString(levelPath.getWideString())));
|
|
|
|
|
return TFilePath();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NameBuilder *nameBuilder =
|
|
|
|
|
NameBuilder::getBuilder(::to_wstring(levelPath.getName()));
|
|
|
|
|
std::wstring levelNameOut;
|
|
|
|
|
do
|
|
|
|
|
levelNameOut = nameBuilder->getNext();
|
|
|
|
|
while (TSystem::doesExistFileOrLevel(levelPath.withName(levelNameOut)));
|
|
|
|
|
|
|
|
|
|
TFilePath levelPathOut = levelPath.withName(levelNameOut);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
if (levelPath.getType() == "tnz")
|
|
|
|
|
copyScene(levelPathOut, levelPath);
|
|
|
|
|
else {
|
|
|
|
|
TSystem::copyFileOrLevel_throw(levelPathOut, levelPath);
|
|
|
|
|
if (levelPath.getType() == "tlv") {
|
|
|
|
|
TFilePath pltPath = levelPath.withType("tpl");
|
|
|
|
|
if (TSystem::doesExistFileOrLevel(pltPath))
|
|
|
|
|
TSystem::copyFileOrLevel_throw(levelPathOut.withType("tpl"), pltPath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (...) {
|
|
|
|
|
QString msg =
|
|
|
|
|
QObject::tr("There was an error copying %1").arg(toQString(levelPath));
|
|
|
|
|
DVGui::error(msg);
|
|
|
|
|
return TFilePath();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// QString msg = QString("Copied ") +
|
|
|
|
|
// QString::fromStdWString(levelPath.withoutParentDir().getWideString())
|
|
|
|
|
// +
|
|
|
|
|
// QString(" to " +
|
|
|
|
|
// QString::fromStdWString(levelPathOut.withoutParentDir().getWideString()));
|
|
|
|
|
// DVGui::info(msg);
|
|
|
|
|
|
|
|
|
|
return levelPathOut;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
void premultiply(const TFilePath &levelPath) {
|
|
|
|
|
if (levelPath == TFilePath()) return;
|
|
|
|
|
|
|
|
|
|
if (!TSystem::doesExistFileOrLevel(levelPath)) {
|
|
|
|
|
DVGui::warning(
|
|
|
|
|
QObject::tr("It is not possible to find the level %1")
|
|
|
|
|
.arg(QString::fromStdWString(levelPath.getWideString())));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TFileType::Type type = TFileType::getInfo(levelPath);
|
|
|
|
|
if (type == TFileType::CMAPPED_LEVEL) {
|
|
|
|
|
DVGui::warning(QObject::tr("Cannot premultiply the selected file."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (type == TFileType::VECTOR_LEVEL || type == TFileType::VECTOR_IMAGE) {
|
|
|
|
|
DVGui::warning(QObject::tr("Cannot premultiply a vector-based level."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (type != TFileType::RASTER_LEVEL && type != TFileType::RASTER_IMAGE) {
|
|
|
|
|
DVGui::info(QObject::tr("Cannot premultiply the selected file."));
|
2016-03-19 06:57:51 +13:00
|
|
|
|
return;
|
2016-06-15 18:43:10 +12:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
TLevelReaderP lr = TLevelReaderP(levelPath);
|
|
|
|
|
if (!lr) return;
|
|
|
|
|
TLevelP level = lr->loadInfo();
|
|
|
|
|
if (!level || level->getFrameCount() == 0) return;
|
|
|
|
|
|
|
|
|
|
bool isMovie = type == TFileType::RASTER_LEVEL;
|
|
|
|
|
/*
|
|
|
|
|
if (!isMovie && lr->getImageInfo()->m_samplePerPixel!=4)
|
|
|
|
|
{
|
2019-03-11 10:41:13 +13:00
|
|
|
|
QMessageBox::information(0, QString("ERROR"), QString("Only rgba images can be
|
2016-06-15 18:43:10 +12:00
|
|
|
|
premultiplied!"));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
TLevelWriterP lw;
|
|
|
|
|
if (!isMovie) {
|
|
|
|
|
lw = TLevelWriterP(levelPath);
|
|
|
|
|
if (!lw) return;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
2016-06-15 18:43:10 +12:00
|
|
|
|
|
|
|
|
|
qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
|
|
|
|
|
qApp->processEvents();
|
|
|
|
|
|
|
|
|
|
int count = 0;
|
|
|
|
|
TLevel::Iterator it = level->begin();
|
|
|
|
|
|
|
|
|
|
for (; it != level->end(); ++it) {
|
|
|
|
|
TImageReaderP ir = lr->getFrameReader(it->first);
|
|
|
|
|
|
|
|
|
|
TRasterImageP rimg = (TRasterImageP)ir->load();
|
|
|
|
|
if (!rimg) continue;
|
|
|
|
|
|
|
|
|
|
TRop::premultiply(rimg->getRaster());
|
|
|
|
|
ir = 0;
|
|
|
|
|
|
|
|
|
|
if (isMovie)
|
|
|
|
|
level->setFrame(it->first, rimg);
|
|
|
|
|
else {
|
|
|
|
|
TImageWriterP iw = lw->getFrameWriter(it->first);
|
|
|
|
|
iw->save(levelPath.withFrame(it->first), rimg);
|
|
|
|
|
iw = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lr = TLevelReaderP();
|
|
|
|
|
|
|
|
|
|
if (isMovie) {
|
|
|
|
|
TSystem::deleteFile(levelPath);
|
|
|
|
|
lw = TLevelWriterP(levelPath);
|
|
|
|
|
if (!lw) {
|
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
lw->save(level);
|
|
|
|
|
}
|
|
|
|
|
} catch (...) {
|
|
|
|
|
DVGui::warning(QObject::tr("Cannot premultiply the selected file."));
|
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
|
|
|
QString msg = QObject::tr("Level %1 premultiplied.")
|
|
|
|
|
.arg(QString::fromStdString(levelPath.getLevelName()));
|
|
|
|
|
DVGui::info(msg);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
void getFillingInformationOverlappingArea(const TVectorImageP &vi,
|
|
|
|
|
std::vector<TFilledRegionInf> ®Inf,
|
|
|
|
|
const TRectD &area1,
|
|
|
|
|
const TRectD &area2) {
|
|
|
|
|
if (!vi->isComputedRegionAlmostOnce()) return;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
assert(area1 != TRectD() || area2 != TRectD());
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
vi->findRegions();
|
|
|
|
|
UINT regNum = vi->getRegionCount();
|
|
|
|
|
TRegion *reg;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
for (UINT i = 0; i < regNum; i++) {
|
|
|
|
|
reg = vi->getRegion(i);
|
|
|
|
|
if (reg->getBBox().overlaps(area1) || reg->getBBox().overlaps(area2))
|
|
|
|
|
addOverlappedRegions(reg, regInf);
|
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
void getFillingInformationInArea(const TVectorImageP &vi,
|
|
|
|
|
std::vector<TFilledRegionInf> ®s,
|
|
|
|
|
const TRectD &area) {
|
|
|
|
|
if (!vi->isComputedRegionAlmostOnce()) return;
|
|
|
|
|
vi->findRegions();
|
|
|
|
|
UINT regNum = vi->getRegionCount();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
for (UINT i = 0; i < regNum; i++)
|
|
|
|
|
addRegionsInArea(vi->getRegion(i), regs, area);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
void assignFillingInformation(TVectorImage &vi,
|
|
|
|
|
const std::vector<TFilledRegionInf> ®s) {
|
|
|
|
|
vi.findRegions();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
UINT r, rCount = UINT(regs.size());
|
|
|
|
|
for (r = 0; r != rCount; ++r) {
|
|
|
|
|
const TFilledRegionInf &rInfo = regs[r];
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
if (TRegion *region = vi.getRegion(rInfo.m_regionId))
|
|
|
|
|
region->setStyle(rInfo.m_styleId);
|
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void getStrokeStyleInformationInArea(
|
2016-06-15 18:43:10 +12:00
|
|
|
|
const TVectorImageP &vi, std::vector<std::pair<int, int>> &strokesInfo,
|
|
|
|
|
const TRectD &area) {
|
|
|
|
|
if (!vi->isComputedRegionAlmostOnce()) return;
|
|
|
|
|
vi->findRegions();
|
|
|
|
|
UINT regNum = vi->getStrokeCount();
|
|
|
|
|
|
|
|
|
|
for (UINT i = 0; i < vi->getStrokeCount(); i++) {
|
|
|
|
|
if (!vi->inCurrentGroup(i)) continue;
|
|
|
|
|
if (area.contains(vi->getStroke(i)->getBBox()))
|
|
|
|
|
strokesInfo.push_back(
|
|
|
|
|
std::pair<int, int>(i, vi->getStroke(i)->getStyle()));
|
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------
|
2016-08-04 19:23:36 +12:00
|
|
|
|
static void convertFromCM(
|
|
|
|
|
const TLevelReaderP &lr, const TPaletteP &plt, const TLevelWriterP &lw,
|
|
|
|
|
const std::vector<TFrameId> &frames, const TAffine &aff,
|
|
|
|
|
const TRop::ResampleFilterType &resType, FrameTaskNotifier *frameNotifier,
|
|
|
|
|
const TPixel &bgColor, bool removeDotBeforeFrameNumber = false) {
|
2016-06-15 18:43:10 +12:00
|
|
|
|
for (int i = 0; i < (int)frames.size(); i++) {
|
|
|
|
|
if (frameNotifier->abortTask()) break;
|
|
|
|
|
try {
|
|
|
|
|
TImageReaderP ir = lr->getFrameReader(frames[i]);
|
|
|
|
|
TImageP img = ir->load();
|
|
|
|
|
TToonzImageP toonzImage(img);
|
|
|
|
|
double xdpi, ydpi;
|
|
|
|
|
toonzImage->getDpi(xdpi, ydpi);
|
|
|
|
|
assert(toonzImage);
|
|
|
|
|
if (toonzImage) {
|
|
|
|
|
TRasterCM32P rasCMImage = toonzImage->getRaster();
|
|
|
|
|
TRaster32P ras(
|
|
|
|
|
convert(aff * convert(rasCMImage->getBounds())).getSize());
|
|
|
|
|
if (!aff.isIdentity())
|
|
|
|
|
TRop::resample(ras, rasCMImage, plt, aff, resType);
|
|
|
|
|
else
|
|
|
|
|
TRop::convert(ras, rasCMImage, plt);
|
|
|
|
|
if (bgColor != TPixel::Transparent) TRop::addBackground(ras, bgColor);
|
|
|
|
|
|
|
|
|
|
TRasterImageP rasImage(ras);
|
|
|
|
|
rasImage->setDpi(xdpi, ydpi);
|
|
|
|
|
|
|
|
|
|
/*---
|
|
|
|
|
ConvertPopup
|
|
|
|
|
での指定に合わせて、[レベル名].[フレーム番号].[拡張子]
|
|
|
|
|
のうち、[レベル名]と[フレーム番号]の間のドットを消す。
|
|
|
|
|
例:A.0001.tga → A0001.tga になる。
|
|
|
|
|
---*/
|
|
|
|
|
if (removeDotBeforeFrameNumber) {
|
|
|
|
|
TFilePath outPath = lw->getFilePath().withFrame(frames[i]);
|
|
|
|
|
/*--- フレーム番号と拡張子の文字数の合計。大抵8 ---*/
|
|
|
|
|
int index = 4 + 1 + outPath.getType().length();
|
|
|
|
|
std::wstring renamedStr = outPath.getWideString();
|
|
|
|
|
if (renamedStr[renamedStr.length() - index - 1] == L'.')
|
|
|
|
|
renamedStr = renamedStr.substr(0, renamedStr.length() - index - 1) +
|
|
|
|
|
renamedStr.substr(renamedStr.length() - index, index);
|
|
|
|
|
|
|
|
|
|
const TFilePath fp(renamedStr);
|
|
|
|
|
TImageWriterP writer(fp);
|
|
|
|
|
writer->setProperties(lw->getProperties());
|
|
|
|
|
writer->save(rasImage);
|
|
|
|
|
} else {
|
|
|
|
|
TImageWriterP iw = lw->getFrameWriter(frames[i]);
|
|
|
|
|
iw->save(rasImage);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (...) {
|
|
|
|
|
// QString msg=QObject::tr("Frame %1 : conversion
|
|
|
|
|
// failed!").arg(QString::number(i+1));
|
|
|
|
|
// DVGui::info(msg);
|
|
|
|
|
}
|
|
|
|
|
/*--
|
|
|
|
|
* これはプログレスバーを進めるものなので、動画番号ではなく、完了したフレームの枚数を投げる
|
|
|
|
|
* --*/
|
|
|
|
|
frameNotifier->notifyFrameCompleted(100 * (i + 1) / frames.size());
|
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
|
|
2016-07-14 18:51:27 +12:00
|
|
|
|
static void convertFromVI(const TLevelReaderP &lr, const TPaletteP &plt,
|
2016-08-04 19:23:36 +12:00
|
|
|
|
const TLevelWriterP &lw,
|
|
|
|
|
const std::vector<TFrameId> &frames,
|
2016-07-14 18:51:27 +12:00
|
|
|
|
const TRop::ResampleFilterType &resType, int width,
|
|
|
|
|
FrameTaskNotifier *frameNotifier) {
|
2016-06-15 18:43:10 +12:00
|
|
|
|
QString msg;
|
|
|
|
|
int i;
|
|
|
|
|
std::vector<TVectorImageP> images;
|
|
|
|
|
TRectD maxBbox;
|
|
|
|
|
for (i = 0; i < (int)frames.size();
|
|
|
|
|
i++) { // trovo la bbox che possa contenere tutte le immagini
|
|
|
|
|
try {
|
|
|
|
|
TImageReaderP ir = lr->getFrameReader(frames[i]);
|
|
|
|
|
TVectorImageP img = ir->load();
|
|
|
|
|
images.push_back(img);
|
|
|
|
|
maxBbox += img->getBBox();
|
|
|
|
|
} catch (...) {
|
|
|
|
|
msg = QObject::tr("Frame %1 : conversion failed!")
|
|
|
|
|
.arg(QString::fromStdString(frames[i].expand()));
|
|
|
|
|
DVGui::info(msg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
maxBbox = maxBbox.enlarge(2);
|
|
|
|
|
TAffine aff;
|
|
|
|
|
if (width) // calcolo l'affine
|
2021-02-26 11:33:29 +13:00
|
|
|
|
aff = TScale((double)width / maxBbox.getLx());
|
2016-06-15 18:43:10 +12:00
|
|
|
|
maxBbox = aff * maxBbox;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < (int)images.size(); i++) {
|
|
|
|
|
if (frameNotifier->abortTask()) break;
|
|
|
|
|
try {
|
|
|
|
|
TVectorImageP vectorImage = images[i];
|
|
|
|
|
assert(vectorImage);
|
|
|
|
|
if (vectorImage) {
|
|
|
|
|
// faccio il render dell'immagine
|
|
|
|
|
vectorImage->transform(aff, true);
|
|
|
|
|
const TVectorRenderData rd(TTranslation(-maxBbox.getP00()), TRect(),
|
|
|
|
|
plt.getPointer(), 0, true, true);
|
|
|
|
|
TOfflineGL *glContext = new TOfflineGL(convert(maxBbox).getSize());
|
|
|
|
|
glContext->clear(TPixel32::Transparent);
|
|
|
|
|
glContext->draw(vectorImage, rd);
|
|
|
|
|
TRaster32P rasImage = (glContext->getRaster());
|
|
|
|
|
TImageWriterP iw = lw->getFrameWriter(TFrameId(i + 1));
|
|
|
|
|
iw->save(TRasterImageP(rasImage));
|
|
|
|
|
}
|
|
|
|
|
} catch (...) {
|
|
|
|
|
msg = QObject::tr("Frame %1 : conversion failed!")
|
|
|
|
|
.arg(QString::fromStdString(frames[i].expand()));
|
|
|
|
|
DVGui::info(msg);
|
|
|
|
|
}
|
|
|
|
|
frameNotifier->notifyFrameCompleted(100 * (i + 1) / frames.size());
|
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
|
2016-08-04 19:23:36 +12:00
|
|
|
|
static void convertFromFullRaster(
|
|
|
|
|
const TLevelReaderP &lr, const TLevelWriterP &lw,
|
|
|
|
|
const std::vector<TFrameId> &_frames, const TAffine &aff,
|
|
|
|
|
const TRop::ResampleFilterType &resType, FrameTaskNotifier *frameNotifier,
|
|
|
|
|
const TPixel &bgColor, bool removeDotBeforeFrameNumber = false) {
|
2016-06-15 18:43:10 +12:00
|
|
|
|
std::vector<TFrameId> frames = _frames;
|
2021-02-26 11:33:29 +13:00
|
|
|
|
if (frames.empty() && lr->loadInfo()->getFrameCount() ==
|
|
|
|
|
1) // e' una immagine singola, non un livello
|
2016-06-15 18:43:10 +12:00
|
|
|
|
frames.push_back(TFrameId());
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)frames.size(); i++) {
|
|
|
|
|
if (frameNotifier->abortTask()) break;
|
|
|
|
|
try {
|
|
|
|
|
TRasterImageP img;
|
|
|
|
|
if (frames[i] == TFrameId()) // immagine singola, non livello
|
|
|
|
|
{
|
|
|
|
|
assert(frames.size() == 1);
|
|
|
|
|
TImageP _img;
|
|
|
|
|
if (!TImageReader::load(lr->getFilePath(), _img)) continue;
|
|
|
|
|
img = _img;
|
|
|
|
|
} else {
|
|
|
|
|
TImageReaderP ir = lr->getFrameReader(frames[i]);
|
|
|
|
|
img = ir->load();
|
|
|
|
|
}
|
|
|
|
|
TRaster32P raster(convert(aff * img->getBBox()).getSize());
|
|
|
|
|
if (!aff.isIdentity())
|
|
|
|
|
TRop::resample(raster, img->getRaster(), aff, resType);
|
|
|
|
|
else {
|
|
|
|
|
if ((TRaster32P)img->getRaster())
|
|
|
|
|
raster = img->getRaster();
|
|
|
|
|
else
|
|
|
|
|
TRop::convert(raster, img->getRaster());
|
|
|
|
|
}
|
|
|
|
|
if (bgColor != TPixel::Transparent) TRop::addBackground(raster, bgColor);
|
|
|
|
|
|
|
|
|
|
TRasterImageP newImg(raster);
|
|
|
|
|
double xDpi, yDpi;
|
|
|
|
|
img->getDpi(xDpi, yDpi);
|
|
|
|
|
if (xDpi == 0 && yDpi == 0)
|
|
|
|
|
xDpi = yDpi = Preferences::instance()->getDefLevelDpi();
|
|
|
|
|
newImg->setDpi(xDpi, yDpi);
|
|
|
|
|
if (frames[i] == TFrameId())
|
|
|
|
|
TImageWriter::save(lw->getFilePath(), newImg);
|
|
|
|
|
else {
|
|
|
|
|
/*---
|
|
|
|
|
ConvertPopup での指定に合わせて、[レベル名].[フレーム番号].[拡張子]
|
|
|
|
|
のうち、[レベル名]と[フレーム番号]の間のドットを消す。
|
|
|
|
|
例:A.0001.tga → A0001.tga になる。
|
|
|
|
|
---*/
|
|
|
|
|
if (removeDotBeforeFrameNumber) {
|
|
|
|
|
TFilePath outPath = lw->getFilePath().withFrame(frames[i]);
|
|
|
|
|
/*--- フレーム番号と拡張子の文字数の合計。大抵8 ---*/
|
|
|
|
|
int index = 4 + 1 + outPath.getType().length();
|
|
|
|
|
std::wstring renamedStr = outPath.getWideString();
|
|
|
|
|
if (renamedStr[renamedStr.length() - index - 1] == L'.')
|
|
|
|
|
renamedStr = renamedStr.substr(0, renamedStr.length() - index - 1) +
|
|
|
|
|
renamedStr.substr(renamedStr.length() - index, index);
|
|
|
|
|
const TFilePath fp(renamedStr);
|
|
|
|
|
TImageWriterP writer(fp);
|
|
|
|
|
writer->setProperties(lw->getProperties());
|
|
|
|
|
writer->save(newImg);
|
|
|
|
|
} else {
|
|
|
|
|
TImageWriterP iw = lw->getFrameWriter(frames[i]);
|
|
|
|
|
iw->save(newImg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (...) {
|
|
|
|
|
// QString msg=QObject::tr("Frame %1 : conversion
|
|
|
|
|
// failed!").arg(QString::number(i+1));
|
|
|
|
|
// DVGui::info(msg);
|
|
|
|
|
}
|
|
|
|
|
/*--
|
|
|
|
|
* これはプログレスバーを進めるものなので、動画番号ではなく、完了したフレームの枚数を投げる
|
|
|
|
|
* --*/
|
|
|
|
|
frameNotifier->notifyFrameCompleted(100 * (i + 1) / frames.size());
|
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
|
2016-07-14 18:51:27 +12:00
|
|
|
|
static void convertFromVector(const TLevelReaderP &lr, const TLevelWriterP &lw,
|
|
|
|
|
const std::vector<TFrameId> &_frames,
|
|
|
|
|
FrameTaskNotifier *frameNotifier) {
|
2016-06-15 18:43:10 +12:00
|
|
|
|
std::vector<TFrameId> frames = _frames;
|
|
|
|
|
TLevelP lv = lr->loadInfo();
|
|
|
|
|
if (frames.empty() &&
|
|
|
|
|
lv->getFrameCount() == 1) // e' una immagine singola, non un livello
|
|
|
|
|
frames.push_back(TFrameId());
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)frames.size(); i++) {
|
|
|
|
|
if (frameNotifier->abortTask()) break;
|
|
|
|
|
try {
|
|
|
|
|
TVectorImageP img;
|
|
|
|
|
if (frames[i] == TFrameId()) // immagine singola, non livello
|
|
|
|
|
{
|
|
|
|
|
assert(frames.size() == 1);
|
|
|
|
|
TImageP _img;
|
|
|
|
|
if (!TImageReader::load(lr->getFilePath(), _img)) continue;
|
|
|
|
|
img = _img;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
TImageReaderP ir = lr->getFrameReader(frames[i]);
|
|
|
|
|
img = ir->load();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
img->setPalette(lv->getPalette());
|
|
|
|
|
|
|
|
|
|
if (frames[i] == TFrameId())
|
|
|
|
|
TImageWriter::save(lw->getFilePath(), img);
|
|
|
|
|
else {
|
|
|
|
|
TImageWriterP iw = lw->getFrameWriter(frames[i]);
|
|
|
|
|
iw->save(img);
|
|
|
|
|
}
|
|
|
|
|
} catch (...) {
|
|
|
|
|
// QString msg=QObject::tr("Frame %1 : conversion
|
|
|
|
|
// failed!").arg(QString::number(i+1));
|
|
|
|
|
// DVGui::info(msg);
|
|
|
|
|
}
|
|
|
|
|
frameNotifier->notifyFrameCompleted(100 * (i + 1) / frames.size());
|
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
void convert(const TFilePath &source, const TFilePath &dest,
|
2016-06-15 18:43:10 +12:00
|
|
|
|
const TFrameId &from, const TFrameId &to, double framerate,
|
|
|
|
|
TPropertyGroup *prop, FrameTaskNotifier *frameNotifier,
|
2022-02-02 16:54:35 +13:00
|
|
|
|
const TPixel &bgColor, bool removeDotBeforeFrameNumber,
|
|
|
|
|
const TFrameId &tmplFId) {
|
2016-06-15 18:43:10 +12:00
|
|
|
|
std::string dstExt = dest.getType(), srcExt = source.getType();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
// Load source level structure
|
|
|
|
|
TLevelReaderP lr(source);
|
|
|
|
|
TLevelP level = lr->loadInfo();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-04-15 17:11:23 +12:00
|
|
|
|
#ifdef _WIN32
|
2016-06-15 18:43:10 +12:00
|
|
|
|
if (dstExt == "avi") {
|
|
|
|
|
TDimension res(0, 0);
|
|
|
|
|
|
|
|
|
|
const TImageInfo *info = lr->getImageInfo(level->begin()->first);
|
|
|
|
|
res.lx = info->m_lx;
|
|
|
|
|
res.ly = info->m_ly;
|
|
|
|
|
|
|
|
|
|
std::string codecName = prop->getProperty(0)->getValueAsString();
|
|
|
|
|
if (!AviCodecRestrictions::canWriteMovie(::to_wstring(codecName), res)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-19 19:32:17 +12:00
|
|
|
|
#endif
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
// Get the frames available in level inside the [from, to] range
|
|
|
|
|
std::vector<TFrameId> frames;
|
|
|
|
|
getFrameIds(from, to, level, frames);
|
|
|
|
|
|
|
|
|
|
// Write the destination level
|
|
|
|
|
TLevelWriterP lw(dest, prop);
|
|
|
|
|
lw->setFrameRate(framerate);
|
2022-02-02 16:54:35 +13:00
|
|
|
|
lw->setFrameFormatTemplateFId(tmplFId);
|
2016-06-15 18:43:10 +12:00
|
|
|
|
|
|
|
|
|
if (srcExt == "tlv")
|
|
|
|
|
convertFromCM(lr, level->getPalette(), lw, frames, TAffine(),
|
|
|
|
|
TRop::Triangle, frameNotifier, bgColor,
|
|
|
|
|
removeDotBeforeFrameNumber);
|
|
|
|
|
else if (srcExt == "pli")
|
2021-07-08 13:39:28 +12:00
|
|
|
|
// assert(!"Conversion from pli files is currently disabled");
|
2016-06-15 18:43:10 +12:00
|
|
|
|
convertFromVector(lr, lw, frames, frameNotifier);
|
|
|
|
|
else
|
|
|
|
|
convertFromFullRaster(lr, lw, frames, TAffine(), TRop::Triangle,
|
|
|
|
|
frameNotifier, bgColor, removeDotBeforeFrameNumber);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
void convertNaa2Tlv(const TFilePath &source, const TFilePath &dest,
|
|
|
|
|
const TFrameId &from, const TFrameId &to,
|
2016-10-04 01:04:35 +13:00
|
|
|
|
FrameTaskNotifier *frameNotifier, TPalette *palette,
|
2017-05-19 21:01:43 +12:00
|
|
|
|
bool removeUnusedStyles, double dpi) {
|
2016-06-15 18:43:10 +12:00
|
|
|
|
std::string dstExt = dest.getType(), srcExt = source.getType();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
// Load source level structure
|
|
|
|
|
TLevelReaderP lr(source);
|
|
|
|
|
TLevelP level = lr->loadInfo();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
// Get the frames available in level inside the [from, to] range
|
|
|
|
|
std::vector<TFrameId> frames;
|
|
|
|
|
getFrameIds(from, to, level, frames);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
if (frames.empty()) return;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
// Write the destination level
|
|
|
|
|
TLevelWriterP lw; // Initialized just before a sure write
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
Naa2TlvConverter converter;
|
|
|
|
|
converter.setPalette(palette);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-11-09 17:52:45 +13:00
|
|
|
|
QList<int> usedStyleIds({0});
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
int f, fCount = int(frames.size());
|
|
|
|
|
for (f = 0; f != fCount; ++f) {
|
|
|
|
|
if (frameNotifier->abortTask()) break;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
try {
|
|
|
|
|
TImageReaderP ir = lr->getFrameReader(frames[f]);
|
|
|
|
|
TImageP img = ir->load();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
if (!img) continue;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
TRasterImageP ri = img;
|
|
|
|
|
if (!ri) continue;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
TRaster32P raster =
|
|
|
|
|
ri->getRaster(); // Converts only 32-bit images, it seems...
|
|
|
|
|
if (!raster) //
|
|
|
|
|
continue; //
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
converter.process(raster);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2017-05-19 21:01:43 +12:00
|
|
|
|
if (TToonzImageP dstImg = converter.makeTlv(
|
|
|
|
|
false, usedStyleIds, dpi)) // Opaque synthetic inks
|
2016-06-15 18:43:10 +12:00
|
|
|
|
{
|
|
|
|
|
if (converter.getPalette() == 0)
|
|
|
|
|
converter.setPalette(dstImg->getPalette());
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
if (!lw) // Initialize output file only on a sure write
|
|
|
|
|
lw = TLevelWriterP(dest); //
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
TImageWriterP iw = lw->getFrameWriter(frames[f]);
|
|
|
|
|
iw->save(dstImg);
|
|
|
|
|
} else {
|
|
|
|
|
DVGui::warning(QObject::tr(
|
|
|
|
|
"The source image seems not suitable for this kind of conversion"));
|
|
|
|
|
frameNotifier->notifyError();
|
|
|
|
|
}
|
|
|
|
|
} catch (...) {
|
|
|
|
|
}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
frameNotifier->notifyFrameCompleted(100 * (f + 1) / frames.size());
|
|
|
|
|
}
|
2016-11-09 17:52:45 +13:00
|
|
|
|
|
|
|
|
|
if (removeUnusedStyles) converter.removeUnusedStyles(usedStyleIds);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
2018-07-12 18:04:49 +12:00
|
|
|
|
void convertOldLevel2Tlv(const TFilePath &source, const TFilePath &dest,
|
|
|
|
|
const TFrameId &from, const TFrameId &to,
|
|
|
|
|
FrameTaskNotifier *frameNotifier) {
|
|
|
|
|
TFilePath destPltPath = TFilePath(dest.getParentDir().getWideString() +
|
|
|
|
|
L"\\" + dest.getWideName() + L".tpl");
|
|
|
|
|
if (TSystem::doesExistFileOrLevel(destPltPath))
|
|
|
|
|
TSystem::removeFileOrLevel(destPltPath);
|
|
|
|
|
|
|
|
|
|
TLevelWriterP lw(dest);
|
|
|
|
|
lw->setIconSize(Preferences::instance()->getIconSize());
|
|
|
|
|
TPaletteP palette =
|
|
|
|
|
ToonzImageUtils::loadTzPalette(source.withType("plt").withNoFrame());
|
|
|
|
|
TLevelReaderP lr(source);
|
|
|
|
|
if (!lr) {
|
|
|
|
|
DVGui::warning(QObject::tr(
|
|
|
|
|
"The source image seems not suitable for this kind of conversion"));
|
|
|
|
|
frameNotifier->notifyError();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
TLevelP inLevel = lr->loadInfo();
|
|
|
|
|
if (!inLevel || inLevel->getFrameCount() == 0) {
|
|
|
|
|
DVGui::warning(QObject::tr(
|
|
|
|
|
"The source image seems not suitable for this kind of conversion"));
|
|
|
|
|
frameNotifier->notifyError();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Get the frames available in level inside the [from, to] range
|
|
|
|
|
std::vector<TFrameId> frames;
|
|
|
|
|
getFrameIds(from, to, inLevel, frames);
|
|
|
|
|
if (frames.empty()) return;
|
|
|
|
|
|
|
|
|
|
TLevelP outLevel;
|
|
|
|
|
outLevel->setPalette(palette.getPointer());
|
|
|
|
|
try {
|
|
|
|
|
int f, fCount = int(frames.size());
|
|
|
|
|
for (f = 0; f != fCount; ++f) {
|
|
|
|
|
if (frameNotifier->abortTask()) break;
|
|
|
|
|
TToonzImageP img = lr->getFrameReader(frames[f])->load();
|
|
|
|
|
if (!img) continue;
|
|
|
|
|
img->setPalette(palette.getPointer());
|
|
|
|
|
lw->getFrameWriter(frames[f])->save(img);
|
|
|
|
|
|
|
|
|
|
frameNotifier->notifyFrameCompleted(100 * (f + 1) / frames.size());
|
|
|
|
|
}
|
|
|
|
|
} catch (TException &e) {
|
|
|
|
|
QString msg = QString::fromStdWString(e.getMessage());
|
|
|
|
|
DVGui::warning(msg);
|
|
|
|
|
lw = TLevelWriterP();
|
|
|
|
|
if (TSystem::doesExistFileOrLevel(dest)) TSystem::removeFileOrLevel(dest);
|
|
|
|
|
frameNotifier->notifyError();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
lw = TLevelWriterP();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
2022-01-11 12:36:08 +13:00
|
|
|
|
#define ZOOMLEVELS 17
|
2016-03-19 06:57:51 +13:00
|
|
|
|
#define NOZOOMINDEX 6
|
2016-06-15 18:43:10 +12:00
|
|
|
|
double ZoomFactors[ZOOMLEVELS] = {
|
2022-01-11 12:36:08 +13:00
|
|
|
|
0.015625, 0.03125, 0.0625, 0.125, 0.25, 0.375, 0.5, 0.625, 1, 1.5, 2, 2.5, 4, 8, 16, 32, 64};
|
2016-06-15 18:43:10 +12:00
|
|
|
|
|
|
|
|
|
double getQuantizedZoomFactor(double zf, bool forward) {
|
|
|
|
|
if (forward && (zf > ZoomFactors[ZOOMLEVELS - 1] ||
|
|
|
|
|
areAlmostEqual(zf, ZoomFactors[ZOOMLEVELS - 1], 1e-5)))
|
|
|
|
|
return zf;
|
|
|
|
|
else if (!forward &&
|
|
|
|
|
(zf < ZoomFactors[0] || areAlmostEqual(zf, ZoomFactors[0], 1e-5)))
|
|
|
|
|
return zf;
|
|
|
|
|
|
|
|
|
|
assert((!forward && zf > ZoomFactors[0]) ||
|
|
|
|
|
(forward && zf < ZoomFactors[ZOOMLEVELS - 1]));
|
|
|
|
|
int i = 0;
|
|
|
|
|
for (i = 0; i <= ZOOMLEVELS - 1; i++)
|
|
|
|
|
if (areAlmostEqual(zf, ZoomFactors[i], 1e-5)) zf = ZoomFactors[i];
|
|
|
|
|
|
|
|
|
|
if (forward && zf < ZoomFactors[0])
|
|
|
|
|
return ZoomFactors[0];
|
|
|
|
|
else if (!forward && zf > ZoomFactors[ZOOMLEVELS - 1])
|
|
|
|
|
return ZoomFactors[ZOOMLEVELS - 1];
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < ZOOMLEVELS - 1; i++)
|
|
|
|
|
if (ZoomFactors[i + 1] - zf >= 0 && zf - ZoomFactors[i] >= 0) {
|
|
|
|
|
if (forward && ZoomFactors[i + 1] == zf)
|
|
|
|
|
return ZoomFactors[i + 2];
|
|
|
|
|
else if (!forward && ZoomFactors[i] == zf)
|
|
|
|
|
return ZoomFactors[i - 1];
|
|
|
|
|
else
|
|
|
|
|
return forward ? ZoomFactors[i + 1] : ZoomFactors[i];
|
|
|
|
|
}
|
|
|
|
|
return ZoomFactors[NOZOOMINDEX];
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
} // namespace ImageUtils
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
2019-05-29 19:26:30 +12:00
|
|
|
|
void getViewerShortcuts(int &zoomIn, int &zoomOut, int &viewReset, int &zoomFit,
|
2017-11-20 19:55:27 +13:00
|
|
|
|
int &showHideFullScreen, int &actualPixelSize,
|
2019-05-29 19:26:30 +12:00
|
|
|
|
int &flipX, int &flipY, int &zoomReset,
|
|
|
|
|
int &rotateReset, int &positionReset) {
|
2016-06-15 18:43:10 +12:00
|
|
|
|
CommandManager *cManager = CommandManager::instance();
|
|
|
|
|
|
|
|
|
|
zoomIn = cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_ZoomIn));
|
|
|
|
|
zoomOut =
|
|
|
|
|
cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_ZoomOut));
|
2019-05-29 19:26:30 +12:00
|
|
|
|
viewReset =
|
|
|
|
|
cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_ViewReset));
|
2016-06-15 18:43:10 +12:00
|
|
|
|
zoomFit =
|
|
|
|
|
cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_ZoomFit));
|
|
|
|
|
showHideFullScreen = cManager->getKeyFromShortcut(
|
|
|
|
|
cManager->getShortcutFromId(V_ShowHideFullScreen));
|
|
|
|
|
actualPixelSize = cManager->getKeyFromShortcut(
|
|
|
|
|
cManager->getShortcutFromId(V_ActualPixelSize));
|
2017-11-20 19:55:27 +13:00
|
|
|
|
flipX = cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_FlipX));
|
|
|
|
|
flipY = cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_FlipY));
|
2019-05-29 19:26:30 +12:00
|
|
|
|
zoomReset =
|
|
|
|
|
cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_ZoomReset));
|
|
|
|
|
rotateReset =
|
|
|
|
|
cManager->getKeyFromShortcut(cManager->getShortcutFromId(V_RotateReset));
|
|
|
|
|
positionReset = cManager->getKeyFromShortcut(
|
|
|
|
|
cManager->getShortcutFromId(V_PositionReset));
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
} // namespace
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
namespace ImageUtils {
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
|
|
ShortcutZoomer::ShortcutZoomer(QWidget *zoomingWidget)
|
2016-06-15 18:43:10 +12:00
|
|
|
|
: m_widget(zoomingWidget) {}
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
bool ShortcutZoomer::exec(QKeyEvent *event) {
|
2019-05-29 19:26:30 +12:00
|
|
|
|
int zoomInKey, zoomOutKey, viewResetKey, zoomFitKey, showHideFullScreenKey,
|
|
|
|
|
actualPixelSize, flipX, flipY, zoomReset, rotateReset, positionReset;
|
|
|
|
|
getViewerShortcuts(zoomInKey, zoomOutKey, viewResetKey, zoomFitKey,
|
|
|
|
|
showHideFullScreenKey, actualPixelSize, flipX, flipY,
|
|
|
|
|
zoomReset, rotateReset, positionReset);
|
2016-06-15 18:43:10 +12:00
|
|
|
|
|
|
|
|
|
int key = event->key();
|
|
|
|
|
if (key == Qt::Key_Control || key == Qt::Key_Shift || key == Qt::Key_Alt)
|
|
|
|
|
return false;
|
|
|
|
|
|
2021-02-26 11:33:29 +13:00
|
|
|
|
key = key | event->modifiers() &
|
|
|
|
|
(~0xf0000000); // Ignore if the key is a numpad key
|
2016-06-15 18:43:10 +12:00
|
|
|
|
|
|
|
|
|
return (key == showHideFullScreenKey)
|
|
|
|
|
? toggleFullScreen()
|
|
|
|
|
: (key == Qt::Key_Escape)
|
|
|
|
|
? toggleFullScreen(true)
|
|
|
|
|
: (key == actualPixelSize)
|
|
|
|
|
? setActualPixelSize()
|
|
|
|
|
: (key == zoomFitKey)
|
|
|
|
|
? fit()
|
|
|
|
|
: (key == zoomInKey || key == zoomOutKey ||
|
2019-05-29 19:26:30 +12:00
|
|
|
|
key == viewResetKey)
|
2016-06-15 18:43:10 +12:00
|
|
|
|
? zoom(key == zoomInKey,
|
2019-05-29 19:26:30 +12:00
|
|
|
|
key == viewResetKey)
|
2017-11-20 19:55:27 +13:00
|
|
|
|
: (key == flipX)
|
|
|
|
|
? setFlipX()
|
2019-05-29 19:26:30 +12:00
|
|
|
|
: (key == flipY)
|
|
|
|
|
? setFlipY()
|
|
|
|
|
: (key == zoomReset)
|
|
|
|
|
? resetZoom()
|
|
|
|
|
: (key == rotateReset)
|
|
|
|
|
? resetRotation()
|
|
|
|
|
: (key ==
|
|
|
|
|
positionReset)
|
|
|
|
|
? resetPosition()
|
|
|
|
|
: false;
|
|
|
|
|
;
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//*********************************************************************************************
|
|
|
|
|
// FullScreenWidget implementation
|
|
|
|
|
//*********************************************************************************************
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
FullScreenWidget::FullScreenWidget(QWidget *parent) : QWidget(parent) {
|
|
|
|
|
QHBoxLayout *layout = new QHBoxLayout(this);
|
|
|
|
|
layout->setMargin(0);
|
|
|
|
|
layout->setSpacing(0);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
setLayout(layout);
|
2021-03-28 17:13:42 +13:00
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
this->winId();
|
|
|
|
|
#endif
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------------
|
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
void FullScreenWidget::setWidget(QWidget *widget) {
|
|
|
|
|
QLayout *layout = this->layout();
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2016-06-15 18:43:10 +12:00
|
|
|
|
delete layout->takeAt(0);
|
|
|
|
|
if ((m_widget = widget)) layout->addWidget(m_widget);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-24 23:13:20 +13:00
|
|
|
|
//=============================================================
|
2021-03-04 16:56:10 +13:00
|
|
|
|
bool FullScreenWidget::toggleFullScreen(
|
|
|
|
|
//=============================================================
|
2021-02-26 11:33:29 +13:00
|
|
|
|
|
2021-03-04 16:56:10 +13:00
|
|
|
|
const bool kfApplicationQuitInProgress) // Indicates whether the
|
2021-07-08 13:39:28 +12:00
|
|
|
|
// application is quitting.
|
2016-03-19 06:57:51 +13:00
|
|
|
|
|
2021-02-24 06:12:28 +13:00
|
|
|
|
/*
|
2021-03-04 16:56:10 +13:00
|
|
|
|
* DESCRIPTION:
|
2021-02-24 06:12:28 +13:00
|
|
|
|
*
|
2021-03-04 16:56:10 +13:00
|
|
|
|
* Entering full screen has to be done manually in order to avoid having the
|
|
|
|
|
* window placed on the wrong monitor on systems running X11 with multiple
|
|
|
|
|
* monitors.
|
2021-02-24 06:12:28 +13:00
|
|
|
|
*/
|
|
|
|
|
{
|
2021-03-04 16:56:10 +13:00
|
|
|
|
// Initialize the return value.
|
|
|
|
|
bool fFullScreenStateToggled = false;
|
|
|
|
|
|
|
|
|
|
// Define some constants for setting and clearing window flags.
|
|
|
|
|
const Qt::WindowFlags kwfFullScreenWidgetFlags =
|
2021-03-17 12:10:53 +13:00
|
|
|
|
Qt::Window | // <-- Make the widget become a window.
|
|
|
|
|
Qt::FramelessWindowHint; // <-- Full screen windows have no border.
|
2021-03-04 16:56:10 +13:00
|
|
|
|
|
|
|
|
|
const Qt::WindowFlags kwfFullScreenWidgetExcludedFlagsMask =
|
|
|
|
|
(Qt::WindowFlags)~Qt::WindowTitleHint; // <-- Full screen windows have no
|
|
|
|
|
// titlebar.
|
|
|
|
|
|
|
|
|
|
// Determine whether to enter or leave full screen mode
|
|
|
|
|
if (this->windowState() & Qt::WindowFullScreen) {
|
|
|
|
|
this->hide();
|
|
|
|
|
|
|
|
|
|
this->setWindowFlags(this->windowFlags() & ~kwfFullScreenWidgetFlags);
|
|
|
|
|
|
|
|
|
|
this->showNormal();
|
|
|
|
|
|
|
|
|
|
this->m_widget->setFocus();
|
|
|
|
|
|
|
|
|
|
// Set the return value to indicate that the full screen mode has been
|
|
|
|
|
// changed.
|
|
|
|
|
fFullScreenStateToggled = true;
|
|
|
|
|
} else {
|
|
|
|
|
// There's no point to switching into full screen if the
|
2021-07-08 13:39:28 +12:00
|
|
|
|
// application is in the process of quitting.
|
2021-03-04 16:56:10 +13:00
|
|
|
|
if (!kfApplicationQuitInProgress) {
|
|
|
|
|
//==============================================================
|
|
|
|
|
//
|
|
|
|
|
// NOTE:
|
|
|
|
|
//
|
|
|
|
|
// This new way of going into full screen mode does things manually in
|
|
|
|
|
// order to bypass Qt's incorrect policy of always relocating a widget
|
|
|
|
|
// to desktop coordinates (0,0) when the widget becomes a window via
|
|
|
|
|
// setting the Qt::Window flag.
|
|
|
|
|
//
|
|
|
|
|
// This makes full screen mode work MUCH better on X11 systems with
|
|
|
|
|
// multiple displays.
|
|
|
|
|
//
|
|
|
|
|
//==============================================================
|
|
|
|
|
//
|
|
|
|
|
// STRATEGY:
|
|
|
|
|
//
|
|
|
|
|
// 1. Obtain the rectangle of the screen that the widgets host
|
|
|
|
|
// window is on. This has to be done first, otherwise the CORRECT
|
|
|
|
|
// screen info can potentially become unavailable in the following
|
|
|
|
|
// steps.
|
|
|
|
|
//
|
|
|
|
|
// 2. Manually set all the necessary flags for the full screen
|
|
|
|
|
// window attributes and state. Qt WILL hide the widget/window when
|
|
|
|
|
// this is done.
|
|
|
|
|
//
|
|
|
|
|
// 3. Set the window geometry/rect to be the same as the screen from
|
|
|
|
|
// Step 1.
|
|
|
|
|
//
|
|
|
|
|
// 4. Make the window visible again.
|
|
|
|
|
//
|
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
// STEP 1:
|
|
|
|
|
|
|
|
|
|
// Get the window widget that contains this widget.
|
|
|
|
|
QWidget *ptrWindowWidget = this->window();
|
|
|
|
|
if (ptrWindowWidget) {
|
|
|
|
|
// Get the access to the QWindow object of the containing window.
|
|
|
|
|
QWindow *ptrContainingQWindow = ptrWindowWidget->windowHandle();
|
|
|
|
|
if (ptrContainingQWindow) {
|
|
|
|
|
// Get access to the screen the window is on.
|
|
|
|
|
QScreen *ptrScreenThisWindowIsOn = ptrContainingQWindow->screen();
|
|
|
|
|
if (ptrScreenThisWindowIsOn) {
|
|
|
|
|
#if !defined(_WIN32)
|
|
|
|
|
// Get the geometry rect for the correct screen.
|
|
|
|
|
QRect qrcScreen = ptrScreenThisWindowIsOn->geometry();
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
// STEP 2:
|
|
|
|
|
|
|
|
|
|
// Set the window flags to be frameless and with no titlebar.
|
|
|
|
|
//
|
|
|
|
|
// This call will turn the widget into a "window", and HIDE it. This
|
|
|
|
|
// is because Qt always hides a widget when it transforms a widget
|
|
|
|
|
// into a window, or turns a window into a widget.
|
|
|
|
|
//
|
|
|
|
|
this->setWindowFlags(
|
|
|
|
|
(this->windowFlags() & kwfFullScreenWidgetExcludedFlagsMask) |
|
|
|
|
|
kwfFullScreenWidgetFlags);
|
|
|
|
|
|
|
|
|
|
// Set the window state flag to indicate that it's now in fullscreen
|
|
|
|
|
// mode.
|
|
|
|
|
// If this state flag isn't set, the test for whether to enter or
|
|
|
|
|
// leave full screen mode won't work correctly.
|
|
|
|
|
this->setWindowState(Qt::WindowFullScreen);
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
// STEP 3:
|
|
|
|
|
|
|
|
|
|
// Set the window to the geometry rect of the correct screen.
|
|
|
|
|
this->setGeometry(qrcScreen);
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------
|
|
|
|
|
// STEP 4:
|
|
|
|
|
|
|
|
|
|
// Make the window visible. This also causes all the changes to the
|
|
|
|
|
// widget's flags, state and geometry that was just set to take
|
|
|
|
|
// effect.
|
|
|
|
|
this->show();
|
2021-02-27 17:42:22 +13:00
|
|
|
|
#else
|
2021-03-17 12:10:53 +13:00
|
|
|
|
this->setWindowFlags(this->windowFlags() | Qt::Window);
|
2021-03-04 16:56:10 +13:00
|
|
|
|
this->window()->windowHandle()->setScreen(ptrScreenThisWindowIsOn);
|
2021-02-27 17:42:22 +13:00
|
|
|
|
|
2021-03-04 16:56:10 +13:00
|
|
|
|
// http://doc.qt.io/qt-5/windows-issues.html#fullscreen-opengl-based-windows
|
|
|
|
|
QWindowsWindowFunctions::setHasBorderInFullScreen(
|
|
|
|
|
this->windowHandle(), true);
|
2021-02-27 17:42:22 +13:00
|
|
|
|
|
2021-03-04 16:56:10 +13:00
|
|
|
|
this->showFullScreen();
|
2021-02-27 17:42:22 +13:00
|
|
|
|
#endif
|
2021-03-04 16:56:10 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the return value to indicate that the full screen mode has been
|
|
|
|
|
// changed.
|
|
|
|
|
fFullScreenStateToggled = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (fFullScreenStateToggled);
|
2016-03-19 06:57:51 +13:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-04 16:56:10 +13:00
|
|
|
|
} // namespace ImageUtils
|