tahoma2d/toonz/sources/toonz/matchline.cpp
2020-06-06 18:49:41 -06:00

1121 lines
36 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "matchline.h"
// Tnz6 includes
#include "tapp.h"
// TnzTools includes
#include "tools/toolutils.h"
// TnzQt includes
#include "toonzqt/icongenerator.h"
#include "historytypes.h"
// TnzLib includes
#include "toonz/txsheet.h"
#include "toonz/toonzscene.h"
#include "toonz/levelset.h"
#include "toonz/txshsimplelevel.h"
#include "toonz/txshlevelcolumn.h"
#include "toonz/txshcell.h"
#include "toonz/scenefx.h"
#include "toonz/dpiscale.h"
#include "toonz/txsheethandle.h"
#include "toonz/palettecontroller.h"
#include "toonz/tpalettehandle.h"
#include "toonz/txshlevelhandle.h"
#include "toonz/txshleveltypes.h"
#include "toonz/tscenehandle.h"
#include "toonz/tframehandle.h"
#include "toonz/preferences.h"
// TnzCore includes
#include "tpalette.h"
#include "timagecache.h"
#include "tcolorstyles.h"
#include "tundo.h"
#include "tropcm.h"
#include "ttoonzimage.h"
#include "tenv.h"
// Qt includes
#include <QRadioButton>
#include <QPushButton>
#include <QApplication>
#include <QMainWindow>
#include <QProgressBar>
#include <QGroupBox>
#include <QLabel>
#include <QButtonGroup>
// STD includes
#include <map>
using namespace DVGui;
TEnv::IntVar MatchlineStyleIndex("MatchlineStyleIndex", -1);
TEnv::IntVar MatchlinePrevalence("MatchlinePrevalence", 0);
TEnv::IntVar MatchlineInkUsagePolicy("MatchlineInkUsagePolicy", -1);
namespace {
class MergeCmappedPair {
public:
const TXshCell *m_cell;
TAffine m_imgAff;
const TXshCell *m_mcell;
TAffine m_matchAff;
MergeCmappedPair(const TXshCell &cell, const TAffine &imgAff,
const TXshCell &mcell, const TAffine &matchAff)
: m_cell(&cell)
, m_imgAff(imgAff)
, m_mcell(&mcell)
, m_matchAff(matchAff){};
};
void doMatchlines(const std::vector<MergeCmappedPair> &matchingLevels,
int inkIndex, int inkPrevalence) {
if (matchingLevels.empty()) return;
TPalette *palette = matchingLevels[0].m_cell->getImage(false)->getPalette();
TPalette *matchPalette =
matchingLevels[0].m_mcell->getImage(false)->getPalette();
TPalette::Page *page;
// upInkId -> downInkId
std::map<int, int> usedInks;
int i = 0;
for (i = 0; i < (int)matchingLevels.size(); i++) {
TToonzImageP img = (TToonzImageP)matchingLevels[i].m_cell->getImage(true);
TToonzImageP match =
(TToonzImageP)matchingLevels[i].m_mcell->getImage(false);
if (!img || !match)
throw TRopException("Can do matchlines only on cmapped raster images!");
// img->lock();
TRasterCM32P ras = img->getRaster(); // img->getCMapped(false);
TRasterCM32P matchRas = match->getRaster(); // match->getCMapped(true);
if (!ras || !matchRas)
throw TRopException("Can do matchlines only on cmapped images!");
TAffine aff =
matchingLevels[i].m_imgAff.inv() * matchingLevels[i].m_matchAff;
int mlx = matchRas->getLx();
int mly = matchRas->getLy();
int rlx = ras->getLx();
int rly = ras->getLy();
TRectD in = convert(matchRas->getBounds()) - matchRas->getCenterD();
TRectD out = aff * in;
TPoint offs((rlx - mlx) / 2 + tround(out.getP00().x - in.getP00().x),
(rly - mly) / 2 + tround(out.getP00().y - in.getP00().y));
int lxout = tround(out.getLx()) + 1 + ((offs.x < 0) ? offs.x : 0);
int lyout = tround(out.getLy()) + 1 + ((offs.y < 0) ? offs.y : 0);
if (lxout <= 0 || lyout <= 0 || offs.x >= rlx || offs.y >= rly) {
// tmsg_error("no intersections between matchline and level");
continue;
}
aff = aff.place((double)(in.getLx() / 2.0), (double)(in.getLy() / 2.0),
(out.getLx()) / 2.0 + ((offs.x < 0) ? offs.x : 0),
(out.getLy()) / 2.0 + ((offs.y < 0) ? offs.y : 0));
if (offs.x < 0) offs.x = 0;
if (offs.y < 0) offs.y = 0;
if (lxout + offs.x > rlx || lyout + offs.y > rly) {
lxout = (lxout + offs.x > rlx) ? (rlx - offs.x) : lxout;
lyout = (lyout + offs.y > rly) ? (rly - offs.y) : lyout;
}
if (!aff.isIdentity(1e-4)) {
TRasterCM32P aux(lxout, lyout);
TRop::resample(aux, matchRas, aff);
matchRas = aux;
}
ras->lock();
matchRas->lock();
TRect raux = matchRas->getBounds() + offs;
TRasterP r = ras->extract(raux);
TRop::applyMatchLines(r, matchRas, palette, matchPalette, inkIndex,
inkPrevalence, usedInks);
ras->unlock();
matchRas->unlock();
img->setSavebox(img->getSavebox() + (matchRas->getBounds() + offs));
}
if (inkIndex == -2) {
std::map<int, int>::iterator it = usedInks.begin();
while (it != usedInks.end()) {
if (it->second < palette->getStyleCount())
usedInks.erase(it++);
else
++it;
}
}
/*- UseMatchlinedInkの場合はここでreturnする -*/
if (usedInks.empty()) return;
std::wstring pageName = L"match lines";
for (i = 0; i < palette->getPageCount(); i++)
if (palette->getPage(i)->getName() == pageName) {
page = palette->getPage(i);
break;
}
if (i == palette->getPageCount()) page = palette->addPage(pageName);
std::map<int, int>::iterator it = usedInks.begin(), it_e = usedInks.end();
int count = 0;
for (; it != it_e; ++it) {
while (palette->getStyleCount() <= it->second)
palette->addStyle(TPixel32::Red);
palette->setStyle(it->second, matchPalette->getStyle(it->first)->clone());
page->addStyle(it->second);
}
if (usedInks.size() > 0) {
palette->setDirtyFlag(true);
TApp::instance()
->getPaletteController()
->getCurrentPalette()
->notifyPaletteChanged();
}
}
/*------------------------------------------------------------------------*/
void applyDeleteMatchline(TXshSimpleLevel *sl,
const std::vector<TFrameId> &fids,
const std::vector<int> &_inkIndexes) {
TPalette::Page *page = 0;
int i, j, pageIndex = 0;
std::vector<int> inkIndexes = _inkIndexes;
if (fids.empty()) return;
TPalette *palette = 0;
if (inkIndexes.empty()) {
palette = sl->getFrame(fids[0], true)->getPalette();
for (i = 0; i < palette->getPageCount(); i++)
if (palette->getPage(i)->getName() == L"match lines") {
page = palette->getPage(i);
break;
}
if (!page) return;
pageIndex = i;
}
for (i = 0; i < (int)fids.size(); i++) {
TToonzImageP image = sl->getFrame(fids[i], true);
assert(image);
TRasterCM32P ras = image->getRaster(); // level[i]->getCMapped(false);
ras->lock();
if (inkIndexes.empty())
for (j = 0; j < page->getStyleCount(); j++)
inkIndexes.push_back(page->getStyleId(j));
TRop::eraseColors(ras, &inkIndexes, true);
ras->unlock();
TRect savebox;
TRop::computeBBox(ras, savebox);
image->setSavebox(savebox);
}
}
/*------------------------------------------------------------------------*/
} // namespace
//-----------------------------------------------------------------------------
void MatchlinesDialog::accept() {
QSettings settings;
bool ok;
int value = m_inkIndex->text().toInt(&ok);
MatchlineStyleIndex = (ok) ? value : -1;
MatchlinePrevalence = getInkPrevalence();
MatchlineInkUsagePolicy =
m_button1->isChecked() ? 0 : (m_button2->isChecked() ? 1 : 2);
QDialog::accept();
}
//----------------------------------------------------------------------------------
int MatchlinesDialog::exec(TPalette *plt) {
m_pltHandle->setPalette(plt);
int styleIndex = MatchlineStyleIndex;
if (styleIndex > plt->getStyleCount()) styleIndex = 1;
if (styleIndex != -1) m_inkIndex->setText(QString::number(styleIndex));
int prevalence = MatchlinePrevalence;
m_inkPrevalence->setValue(prevalence);
int inkUsagePolicy = MatchlineInkUsagePolicy;
switch (inkUsagePolicy) {
case 0:
m_button1->setChecked(true);
break;
case -1:
case 1:
m_button2->setChecked(true);
break;
case 2:
m_button3->setChecked(true);
break;
}
m_inkIndex->setEnabled(inkUsagePolicy == 1);
return QDialog::exec();
}
//-----------------------------------------------------------------------------
void MatchlinesDialog::onChooseInkClicked(bool value) {
m_inkIndex->setEnabled(value);
}
//-----------------------------------------------------------------------------
int MatchlinesDialog::getInkPrevalence() { return m_inkPrevalence->getValue(); }
//-----------------------------------------------------------------------------
int MatchlinesDialog::getInkIndex() {
if (m_button1->isChecked())
return -1;
else if (m_button3->isChecked())
return -2;
else {
if (QString("current").contains(m_inkIndex->text()))
return TApp::instance()
->getPaletteController()
->getCurrentLevelPalette()
->getStyleIndex();
else {
bool ok;
int value = m_inkIndex->text().toInt(&ok);
if (ok) return value;
}
return 0;
}
}
//-----------------------------------------------------------------------------
MatchlinesDialog::MatchlinesDialog()
: Dialog(TApp::instance()->getMainWindow(), true, true, "Matchlines")
, m_pltHandle(new TPaletteHandle())
, m_currentXsheet(0) {
setWindowTitle(tr("Apply Match Lines"));
m_button1 = new QRadioButton(tr("Add Match Line Inks"), this);
m_button2 = new QRadioButton(tr("Use Ink: "), this);
m_button3 = new QRadioButton(tr("Merge Inks"), this);
m_inkIndex = new StyleIndexLineEdit();
m_inkPrevalence = new IntField(this);
QPushButton *okBtn = new QPushButton(tr("Apply"), this);
QPushButton *cancelBtn = new QPushButton(tr("Cancel"), this);
QGroupBox *inkUsageGroupBox = new QGroupBox(tr("Ink Usage"), this);
QGroupBox *lineOrderGroupBox = new QGroupBox(tr("Line Stacking Order"), this);
m_lup_noGapButton = new QPushButton(this);
m_rup_noGapButton = new QPushButton(this);
m_lup_gapButton = new QPushButton(this);
m_rup_gapButton = new QPushButton(this);
m_button1->setCheckable(true);
m_button2->setCheckable(true);
m_button3->setCheckable(true);
m_button2->setChecked(true);
m_button3->setToolTip(
tr("Merge Inks : If the target level has the same style as the match "
"line ink\n"
"(i.e. with the same index and the same color), the existing style "
"will be used.\n"
"Otherwise, a new style will be added to \"match lines\" page."));
m_inkIndex->setPaletteHandle(m_pltHandle);
m_inkPrevalence->setRange(0, 100);
okBtn->setDefault(true);
QIcon lup_noGapIcon(
QPixmap(":Resources/match_lup_nogap.png").scaled(104, 104));
QIcon rup_noGapIcon(
QPixmap(":Resources/match_rup_nogap.png").scaled(104, 104));
QIcon lup_gapIcon(QPixmap(":Resources/match_lup_gap.png").scaled(104, 104));
QIcon rup_gapIcon(QPixmap(":Resources/match_rup_gap.png").scaled(104, 104));
m_lup_noGapButton->setObjectName("MatchLineButton");
m_rup_noGapButton->setObjectName("MatchLineButton");
m_lup_gapButton->setObjectName("MatchLineButton");
m_rup_gapButton->setObjectName("MatchLineButton");
m_lup_noGapButton->setCheckable(true);
m_rup_noGapButton->setCheckable(true);
m_lup_gapButton->setCheckable(true);
m_rup_gapButton->setCheckable(true);
m_lup_gapButton->setChecked(true);
m_lup_noGapButton->setIcon(lup_noGapIcon);
m_rup_noGapButton->setIcon(rup_noGapIcon);
m_lup_gapButton->setIcon(lup_gapIcon);
m_rup_gapButton->setIcon(rup_gapIcon);
m_lup_noGapButton->setIconSize(QSize(104, 104));
m_rup_noGapButton->setIconSize(QSize(104, 104));
m_lup_gapButton->setIconSize(QSize(104, 104));
m_rup_gapButton->setIconSize(QSize(104, 104));
QButtonGroup *lineStackButtonGroup = new QButtonGroup(this);
lineStackButtonGroup->addButton(m_lup_gapButton, 0);
lineStackButtonGroup->addButton(m_rup_gapButton, 100);
lineStackButtonGroup->addButton(m_lup_noGapButton, 30);
lineStackButtonGroup->addButton(m_rup_noGapButton, 70);
//----layout
m_topLayout->setMargin(5);
m_topLayout->setSpacing(5);
{
QGridLayout *inkUsageLay = new QGridLayout();
inkUsageLay->setMargin(5);
inkUsageLay->setSpacing(5);
{
inkUsageLay->addWidget(m_button1, 0, 0, 1, 2);
inkUsageLay->addWidget(m_button2, 1, 0);
inkUsageLay->addWidget(m_inkIndex, 1, 1, Qt::AlignLeft);
inkUsageLay->addWidget(m_button3, 2, 0, 1, 2);
}
inkUsageLay->setColumnStretch(0, 0);
inkUsageLay->setColumnStretch(1, 1);
inkUsageGroupBox->setLayout(inkUsageLay);
m_topLayout->addWidget(inkUsageGroupBox);
QVBoxLayout *lineOrderLay = new QVBoxLayout();
lineOrderLay->setMargin(5);
lineOrderLay->setSpacing(5);
{
QGridLayout *buttonsLay = new QGridLayout();
buttonsLay->setMargin(0);
buttonsLay->setSpacing(5);
{
buttonsLay->addWidget(new QLabel(tr("L-Up R-Down"), this), 0, 1);
buttonsLay->addWidget(new QLabel(tr("L-Down R-Up"), this), 0, 2);
buttonsLay->addWidget(new QLabel(tr("Keep\nHalftone"), this), 1, 0);
buttonsLay->addWidget(m_lup_gapButton, 1, 1);
buttonsLay->addWidget(m_rup_gapButton, 1, 2);
buttonsLay->addWidget(new QLabel(tr("Fill\nGaps"), this), 2, 0);
buttonsLay->addWidget(m_lup_noGapButton, 2, 1);
buttonsLay->addWidget(m_rup_noGapButton, 2, 2);
}
buttonsLay->setColumnStretch(0, 0);
buttonsLay->setColumnStretch(1, 1);
buttonsLay->setColumnStretch(2, 1);
buttonsLay->setRowStretch(0, 0);
buttonsLay->setRowStretch(1, 1);
buttonsLay->setRowStretch(2, 1);
lineOrderLay->addLayout(buttonsLay, 1);
QHBoxLayout *inkPrevalenceLay = new QHBoxLayout();
inkPrevalenceLay->setMargin(0);
inkPrevalenceLay->setSpacing(5);
{
inkPrevalenceLay->addWidget(new QLabel(tr("Line Prevalence"), this), 0);
inkPrevalenceLay->addWidget(m_inkPrevalence, 1);
}
lineOrderLay->addLayout(inkPrevalenceLay, 0);
}
lineOrderGroupBox->setLayout(lineOrderLay);
m_topLayout->addWidget(lineOrderGroupBox);
m_topLayout->addStretch();
}
m_buttonLayout->setMargin(0);
m_buttonLayout->setSpacing(10);
{
m_buttonLayout->addWidget(okBtn);
m_buttonLayout->addWidget(cancelBtn);
}
//----signal-slot connections
connect(m_button2, SIGNAL(toggled(bool)), this,
SLOT(onChooseInkClicked(bool)));
connect(okBtn, SIGNAL(clicked()), this, SLOT(accept()));
connect(cancelBtn, SIGNAL(clicked()), this, SLOT(reject()));
connect(lineStackButtonGroup, SIGNAL(buttonPressed(int)),
SLOT(onLineStackButtonPressed(int)));
connect(m_inkPrevalence, SIGNAL(valueChanged(bool)),
SLOT(onInkPrevalenceChanged(bool)));
}
//-----------------------------------------------------------------------------
void MatchlinesDialog::showEvent(QShowEvent *e) {
/*-- Xsheetが切り替わったらInkIndexをに戻す --*/
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
if (xsh != m_currentXsheet) {
m_inkIndex->setText("1");
m_currentXsheet = xsh;
}
}
//-----------------------------------------------------------------------------
/*-- ボタンを押したらPreveranceの値を変える。ボタンのIDが値に対応している --*/
void MatchlinesDialog::onLineStackButtonPressed(int id) {
m_inkPrevalence->setValue(id);
}
//-----------------------------------------------------------------------------
/*-
* スライダを動かしたらボタンを解除する。対応する値がある場合は対応するボタンを押される状態にする
* -*/
void MatchlinesDialog::onInkPrevalenceChanged(bool isDragging) {
if (isDragging) return;
m_lup_noGapButton->setChecked(false);
m_rup_noGapButton->setChecked(false);
m_lup_gapButton->setChecked(false);
m_rup_gapButton->setChecked(false);
if (m_inkPrevalence->getValue() == 0)
m_lup_gapButton->setChecked(true);
else if (m_inkPrevalence->getValue() == 100)
m_rup_gapButton->setChecked(true);
else if (m_inkPrevalence->getValue() < 50)
m_lup_noGapButton->setChecked(true);
else
m_rup_noGapButton->setChecked(true);
}
//-----------------------------------------------------------------------------
class DeleteMatchlineUndo final : public TUndo {
public:
TXshLevel *m_xl;
TXshSimpleLevel *m_sl;
std::vector<TFrameId> m_fids;
std::vector<int> m_indexes;
TPaletteP m_matchlinePalette;
DeleteMatchlineUndo(
TXshLevel *xl, TXshSimpleLevel *sl, const std::vector<TFrameId> &fids,
const std::vector<int> &indexes) //, TPalette*matchPalette)
: TUndo(),
m_xl(xl),
m_sl(sl),
m_fids(fids),
m_indexes(indexes) {
int i;
for (i = 0; i < fids.size(); i++) {
QString id = "DeleteMatchlineUndo" + QString::number((uintptr_t) this) +
"-" + QString::number(i);
TToonzImageP image = sl->getFrame(fids[i], false);
assert(image);
TImageCache::instance()->add(id, image->clone());
}
}
void undo() const override {
int i;
for (i = 0; i < m_fids.size(); i++) {
QString id = "DeleteMatchlineUndo" + QString::number((uintptr_t) this) +
"-" + QString::number(i);
TImageP img = TImageCache::instance()->get(id, false)->cloneImage();
m_sl->setFrame(m_fids[i], img);
ToolUtils::updateSaveBox(m_sl, m_fids[i]);
}
if (m_xl) invalidateIcons(m_xl, m_fids);
m_sl->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
void redo() const override {
int i;
applyDeleteMatchline(m_sl, m_fids, m_indexes);
for (i = 0; i < m_fids.size(); i++) {
ToolUtils::updateSaveBox(m_sl, m_fids[i]);
}
if (m_xl) invalidateIcons(m_xl, m_fids);
m_sl->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
int getSize() const override { return sizeof(*this); }
~DeleteMatchlineUndo() {
int i;
for (i = 0; i < m_fids.size(); i++)
TImageCache::instance()->remove("DeleteMatchlineUndo" +
QString::number((uintptr_t) this) + "-" +
QString::number(i));
}
QString getHistoryString() override {
return QObject::tr("Delete Matchline : Level %1")
.arg(QString::fromStdWString(m_sl->getName()));
}
int getHistoryType() override { return HistoryType::FilmStrip; }
};
//-----------------------------------------------------------------------------
class MatchlineUndo final : public TUndo {
TXshLevelP m_xl;
int m_mergeCmappedSessionId;
std::map<TFrameId, QString> m_images;
TXshSimpleLevel *m_level;
TPalette *m_palette;
int m_column, m_mColumn;
int m_index, m_prevalence;
public:
MatchlineUndo(TXshLevelP xl, int mergeCmappedSessionId, int index,
int prevalence, int column, TXshSimpleLevel *level,
const std::map<TFrameId, QString> &images, int mColumn,
TPalette *palette)
: TUndo()
, m_xl(xl)
, m_mergeCmappedSessionId(mergeCmappedSessionId)
, m_palette(palette->clone())
, m_level(level)
, m_column(column)
, m_mColumn(mColumn)
, m_index(index)
, m_prevalence(prevalence)
, m_images(images) {}
void undo() const override {
std::map<TFrameId, QString>::const_iterator it = m_images.begin();
m_level->getPalette()->assign(m_palette);
std::vector<TFrameId> fids;
for (; it != m_images.end(); ++it) //, ++mit)
{
QString id = "MatchlinesUndo" + QString::number(m_mergeCmappedSessionId) +
"-" + QString::number(it->first.getNumber());
TImageP img = TImageCache::instance()->get(id, false)->cloneImage();
img->setPalette(m_level->getPalette());
m_level->setFrame(it->first, img);
fids.push_back(it->first);
}
if (m_xl) invalidateIcons(m_xl.getPointer(), fids);
m_level->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
if (m_index == -1)
TApp::instance()
->getPaletteController()
->getCurrentPalette()
->notifyPaletteChanged();
}
void redo() const override {
doMatchlines(m_column, m_mColumn, m_index, m_prevalence,
m_mergeCmappedSessionId);
}
int getSize() const override { return sizeof(*this); }
~MatchlineUndo() {
std::map<TFrameId, QString>::const_iterator it = m_images.begin();
for (; it != m_images.end(); ++it) //, ++mit)
{
QString id = "MatchlineUndo" + QString::number(m_mergeCmappedSessionId) +
"-" + QString::number(it->first.getNumber());
TImageCache::instance()->remove(id);
}
delete m_palette;
}
QString getHistoryString() override {
return QObject::tr("Apply Matchline : Column%1 < Column%2")
.arg(QString::number(m_column + 1))
.arg(QString::number(m_mColumn + 1));
}
int getHistoryType() override { return HistoryType::FilmStrip; }
};
//-----------------------------------------------------------------------------
static int LastMatchlineIndex = -1;
void doMatchlines(int column, int mColumn, int index, int inkPrevalence,
int MergeCmappedSessionId) {
static int increment_MergeCmappedSessionId = 0;
if (MergeCmappedSessionId == 0) {
increment_MergeCmappedSessionId++;
MergeCmappedSessionId = increment_MergeCmappedSessionId;
}
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
int start, end;
xsh->getCellRange(column, start, end);
if (start > end) return;
std::vector<TXshCell> cell(end - start + 1);
std::vector<TXshCell> mCell(end - start + 1);
xsh->getCells(start, column, cell.size(), &(cell[0]));
if (mColumn != -1) xsh->getCells(start, mColumn, cell.size(), &(mCell[0]));
TXshColumn *col = xsh->getColumn(column);
TXshColumn *mcol = xsh->getColumn(mColumn);
std::vector<MergeCmappedPair> matchingLevels;
std::map<TFrameId, TFrameId> table;
TXshSimpleLevel *level = 0, *mLevel = 0;
TXshLevelP xl;
std::map<TFrameId, QString> images;
QProgressBar *getImageProgressBar = new QProgressBar(0);
getImageProgressBar->setAttribute(Qt::WA_DeleteOnClose);
getImageProgressBar->setWindowFlags(Qt::SubWindow | Qt::WindowStaysOnTopHint);
getImageProgressBar->setMinimum(0);
getImageProgressBar->setMaximum(cell.size() - 1);
getImageProgressBar->setValue(0);
getImageProgressBar->move(600, 500);
getImageProgressBar->setWindowTitle("Storing matchline pairs");
getImageProgressBar->show();
/*- 左カラムの各セルについてループ -*/
for (int i = 0; i < (int)cell.size(); i++) {
getImageProgressBar->setValue(i);
std::map<TFrameId, TFrameId>::iterator it;
/*- 両セルに中身が無い場合は次のセルへ -*/
if (cell[i].isEmpty() || mCell[i].isEmpty()) continue;
if (!level) {
level = cell[i].getSimpleLevel();
xl = cell[i].m_level;
}
/*-- 左カラムに複数のLevelが入っている場合、警告を出して抜ける --*/
else if (level != cell[i].getSimpleLevel()) {
getImageProgressBar->close();
DVGui::warning(
QObject::tr("It is not possible to apply match lines to a column "
"containing more than one level."));
/*-- 前に遡ってキャッシュを消去 --*/
i--;
for (; i >= 0; i--) {
TFrameId fid = cell[i].m_frameId;
QString id = "MatchlinesUndo" + QString::number(MergeCmappedSessionId) +
"-" + QString::number(fid.getNumber());
if (TImageCache::instance()->isCached(id.toStdString()))
TImageCache::instance()->remove(id);
}
return;
}
if (!mLevel)
mLevel = mCell[i].getSimpleLevel();
else if (mLevel != mCell[i].getSimpleLevel()) {
getImageProgressBar->close();
DVGui::warning(
QObject::tr("It is not possible to use a match lines column "
"containing more than one level."));
/*-- 前に遡ってキャッシュを消去 --*/
i--;
for (; i >= 0; i--) {
TFrameId fid = cell[i].m_frameId;
QString id = "MatchlinedUndo" + QString::number(MergeCmappedSessionId) +
"-" + QString::number(fid.getNumber());
if (TImageCache::instance()->isCached(id.toStdString()))
TImageCache::instance()->remove(id);
}
return;
}
TImageP img = cell[i].getImage(true);
TImageP match = mCell[i].getImage(true);
TFrameId fid = cell[i].m_frameId;
TFrameId mFid = mCell[i].m_frameId;
//画像が取得できなかったら次へ
if (!img || !match) continue;
if ((it = table.find(fid)) == table.end()) {
TToonzImageP timg = (TToonzImageP)img;
TToonzImageP tmatch = (TToonzImageP)match;
//ラスタLevelじゃないとき、エラーを返す
if (!timg || !tmatch) {
getImageProgressBar->close();
DVGui::warning(QObject::tr(
"Match lines can be applied to Smart Raster levels only."));
/*-- 前に遡ってキャッシュを消去 --*/
i--;
for (; i >= 0; i--) {
TFrameId fid = cell[i].m_frameId;
QString id = "MatchlinedUndo" +
QString::number(MergeCmappedSessionId) + "-" +
QString::number(fid.getNumber());
if (TImageCache::instance()->isCached(id.toStdString()))
TImageCache::instance()->remove(id);
}
return;
}
/*- Matchline前の画像をUndoに格納 -*/
QString id = "MatchlinesUndo" + QString::number(MergeCmappedSessionId) +
"-" + QString::number(fid.getNumber());
TImageCache::instance()->add(id, timg->clone(), false);
images[fid] = id;
TAffine imgAff, matchAff;
getColumnPlacement(imgAff, xsh, start + i, column, false);
getColumnPlacement(matchAff, xsh, start + i, mColumn, false);
TAffine dpiAff = getDpiAffine(level, fid);
TAffine mdpiAff = getDpiAffine(mLevel, mFid);
matchingLevels.push_back(MergeCmappedPair(cell[i], imgAff * dpiAff,
mCell[i], matchAff * mdpiAff));
table[fid] = mFid;
}
}
getImageProgressBar->close();
if (matchingLevels.empty()) {
DVGui::warning(
QObject::tr("Match lines can be applied to Smart Raster levels only."));
return;
}
if (inkPrevalence == -1) // we are not in the redo
{
TPalette *plt = level->getPalette();
if (!plt) {
DVGui::warning(
QObject::tr("The level you are using has not a valid palette."));
return;
}
static MatchlinesDialog *md = new MatchlinesDialog();
int styleCount = plt->getStyleCount();
index = styleCount;
while (index >= styleCount) {
if (md->exec(plt) != QDialog::Accepted) {
/*- キャッシュを消す -*/
for (int i = 0; i < (int)cell.size(); i++) {
TFrameId fid = cell[i].m_frameId;
TFrameId mFid = mCell[i].m_frameId;
QString id = "MatchlinedUndo" +
QString::number(MergeCmappedSessionId) + "-" +
QString::number(fid.getNumber());
QString mid = "MatchlinerUndo" +
QString::number(MergeCmappedSessionId) + "-" +
QString::number(mFid.getNumber());
if (TImageCache::instance()->isCached(id.toStdString()))
TImageCache::instance()->remove(id);
if (TImageCache::instance()->isCached(mid.toStdString()))
TImageCache::instance()->remove(mid);
}
return;
}
index = md->getInkIndex();
if (index >= styleCount)
DVGui::warning(
QObject::tr("The style index you specified is not available in the "
"palette of the destination level."));
if (index != -1) LastMatchlineIndex = index;
}
inkPrevalence = md ? md->getInkPrevalence() : 0;
TUndoManager::manager()->add(
new MatchlineUndo(xl, MergeCmappedSessionId, index, inkPrevalence,
column, level, images, mColumn, plt));
}
QApplication::setOverrideCursor(Qt::WaitCursor);
doMatchlines(matchingLevels, index, inkPrevalence);
QApplication::restoreOverrideCursor();
for (int i = 0; i < (int)cell.size(); i++) // the saveboxes must be updated
{
if (cell[i].isEmpty() || mCell[i].isEmpty()) continue;
if (!cell[i].getImage(false) || !mCell[i].getImage(false)) continue;
TXshSimpleLevel *sl = cell[i].getSimpleLevel();
const TFrameId &fid = cell[i].m_frameId;
ToolUtils::updateSaveBox(sl, fid);
IconGenerator::instance()->invalidate(sl, fid);
sl->setDirtyFlag(true);
}
level->setDirtyFlag(true);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}
namespace {
const TXshCell *findCell(int column, const TFrameId &fid) {
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
int i;
for (i = 0; i < xsh->getColumn(column)->getMaxFrame(); i++)
if (xsh->getCell(i, column).getFrameId() == fid)
return &(xsh->getCell(i, column));
return 0;
}
bool contains(const std::vector<TFrameId> &v, const TFrameId &val) {
int i;
for (i = 0; i < (int)v.size(); i++)
if (v[i] == val) return true;
return false;
}
//-----------------------------------------------------------------------------
QString indexes2string(const std::set<TFrameId> fids) {
if (fids.empty()) return "";
QString str;
std::set<TFrameId>::const_iterator it = fids.begin();
str = QString::number(it->getNumber());
while (it != fids.end()) {
std::set<TFrameId>::const_iterator it1 = it;
it1++;
int lastVal = it->getNumber();
while (it1 != fids.end() && it1->getNumber() == lastVal + 1) {
lastVal = it1->getNumber();
it1++;
}
if (lastVal != it->getNumber()) str += "-" + QString::number(lastVal);
if (it1 == fids.end()) return str;
str += ", " + QString::number(it1->getNumber());
it = it1;
}
return str;
}
} // namespace
//-----------------------------------------------------------------------------
std::vector<int> string2Indexes(const QString &values) {
std::vector<int> ret;
int i, j;
bool ok;
QStringList vals = values.split(',', QString::SkipEmptyParts);
for (i = 0; i < vals.size(); i++) {
if (vals.at(i).contains('-')) {
QStringList vals1 = vals.at(i).split('-', QString::SkipEmptyParts);
if (vals1.size() != 2) return std::vector<int>();
int from = vals1.at(0).toInt(&ok);
if (!ok) return std::vector<int>();
int to = vals1.at(1).toInt(&ok);
if (!ok) return std::vector<int>();
for (j = std::min(from, to); j <= std::max(from, to); j++)
ret.push_back(j);
} else {
int val = vals.at(i).toInt(&ok);
if (!ok) return std::vector<int>();
ret.push_back(val);
}
}
std::sort(ret.begin(), ret.end());
return ret;
}
//-----------------------------------------------------------------------------
std::vector<int> DeleteInkDialog::getInkIndexes() {
return string2Indexes(m_inkIndex->text());
}
std::vector<TFrameId> DeleteInkDialog::getFrames() {
std::vector<TFrameId> ret;
std::vector<int> ret1 = string2Indexes(m_frames->text());
int i;
for (i = 0; i < (int)ret1.size(); i++) ret.push_back(ret1[i]);
return ret;
}
DeleteInkDialog::DeleteInkDialog(const QString &str, int inkIndex)
: Dialog(TApp::instance()->getMainWindow(), true,
Preferences::instance()->getCurrentLanguage() == "English",
"DeleteInk") {
setWindowTitle(tr("Delete Lines"));
m_inkIndex = new LineEdit(QString::number(inkIndex));
m_frames = new LineEdit(str);
QPushButton *okBtn = new QPushButton(tr("Delete"), this);
QPushButton *cancelBtn = new QPushButton(tr("Cancel"), this);
okBtn->setDefault(true);
//--- layout
m_topLayout->setMargin(5);
m_topLayout->setSpacing(10);
{
QGridLayout *upperLay = new QGridLayout();
upperLay->setMargin(0);
upperLay->setSpacing(5);
{
upperLay->addWidget(new QLabel(tr("Style Index:"), this), 0, 0,
Qt::AlignRight | Qt::AlignVCenter);
upperLay->addWidget(m_inkIndex, 0, 1);
upperLay->addWidget(new QLabel(tr("Apply to Frames:"), this), 1, 0,
Qt::AlignRight | Qt::AlignVCenter);
upperLay->addWidget(m_frames, 1, 1);
}
m_topLayout->addLayout(upperLay);
}
m_buttonLayout->setMargin(0);
m_buttonLayout->setSpacing(10);
{
m_buttonLayout->addWidget(okBtn);
m_buttonLayout->addWidget(cancelBtn);
}
connect(okBtn, SIGNAL(clicked()), this, SLOT(accept()));
connect(cancelBtn, SIGNAL(clicked()), this, SLOT(reject()));
}
void DeleteInkDialog::setRange(const QString &str) { m_frames->setText(str); }
//-----------------------------------------------------------------------------
/*--
DeleteMatchLineコマンドから呼ばれる場合chooseInkがfalse
DeleteLinesコマンドから呼ばれる場合chooseInkがtrue
--*/
static void doDeleteMatchlines(TXshSimpleLevel *sl,
const std::set<TFrameId> &fids, bool chooseInk) {
std::vector<int> indexes;
// vector<TToonzImageP> images;
std::vector<TFrameId> frames;
std::vector<TFrameId> fidsToProcess;
int i;
if (chooseInk) {
TPaletteHandle *ph =
TApp::instance()->getPaletteController()->getCurrentLevelPalette();
if (LastMatchlineIndex == -1) LastMatchlineIndex = ph->getStyleIndex();
static DeleteInkDialog *md = 0;
if (!md)
md = new DeleteInkDialog(indexes2string(fids), LastMatchlineIndex);
else
md->setRange(indexes2string(fids));
bool ok = false;
while (!ok) {
if (md->exec() != QDialog::Accepted) return;
indexes = md->getInkIndexes();
if (indexes.empty()) {
DVGui::warning(QObject::tr(
"The style index range you specified is not valid: please separate "
"values with a comma (e.g. 1,2,5) or with a dash (e.g. 4-7 will "
"refer to indexes 4, 5, 6 and 7)."));
continue;
}
frames = md->getFrames();
if (frames.empty()) {
DVGui::warning(
QObject::tr("The frame range you specified is not valid: please "
"separate values with a comma (e.g. 1,2,5) or with a "
"dash (e.g. 4-7 will refer to frames 4, 5, 6 and 7)."));
continue;
}
for (i = 0; i < frames.size(); i++) {
if (!sl->isFid(frames[i])) continue;
// images.push_back(sl->getFrame(frames[i], true));
if (sl->getFrame(frames[i], false)) fidsToProcess.push_back(frames[i]);
}
break;
}
} else {
std::set<TFrameId>::const_iterator it = fids.begin();
for (; it != fids.end(); ++it) {
// images.push_back(sl->getFrame(*it, true));
if (sl->getFrame(*it, false)) fidsToProcess.push_back(*it);
}
}
if (fidsToProcess.empty()) {
DVGui::warning(QObject::tr(
"No drawing is available in the frame range you specified."));
return;
}
TXshLevel *xl = TApp::instance()->getCurrentLevel()->getLevel();
TUndoManager::manager()->add(new DeleteMatchlineUndo(
xl, sl, fidsToProcess, indexes)); //, images[0]->getPalette()));
applyDeleteMatchline(sl, fidsToProcess, indexes);
for (int i = 0; i < fidsToProcess.size();
i++) // the saveboxes must be updated
ToolUtils::updateSaveBox(sl, fidsToProcess[i]);
std::vector<TFrameId> fidsss;
xl->getFids(fidsss);
invalidateIcons(xl, fidsss);
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
sl->setDirtyFlag(true);
}
//-----------------------------------------------------------------------------
void deleteMatchlines(TXshSimpleLevel *sl, const std::set<TFrameId> &fids) {
doDeleteMatchlines(sl, fids, false);
}
void deleteInk(TXshSimpleLevel *sl, const std::set<TFrameId> &fids) {
doDeleteMatchlines(sl, fids, true);
}
//-----------------------------------------------------------------------------