tahoma2d/toonz/sources/toonzlib/palettecmd.cpp
2023-10-12 08:23:29 -04:00

1375 lines
47 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 "toonz/palettecmd.h"
// TnzLib includes
#include "toonz/tpalettehandle.h"
#include "toonz/txshlevelhandle.h"
#include "toonz/txsheethandle.h"
#include "toonz/studiopalette.h"
#include "toonz/txsheet.h"
#include "toonz/txshcolumn.h"
#include "toonz/txshcell.h"
#include "toonz/txshlevelcolumn.h"
#include "toonz/txshsimplelevel.h"
#include "toonz/cleanupcolorstyles.h"
#include "toonz/txshlevel.h"
#include "toonz/toonzscene.h"
#include "toonz/toonzimageutils.h"
#include "toonz/preferences.h"
// TnzCore includes
#include "tpalette.h"
#include "tcolorstyles.h"
#include "tundo.h"
#include "tvectorimage.h"
#include "ttoonzimage.h"
#include "trasterimage.h"
#include "tconvert.h"
#include "tcolorutils.h"
#include "tropcm.h"
#include "tstroke.h"
#include "tregion.h"
#include "tlevel_io.h"
#include "tpixelutils.h"
#include "historytypes.h"
// tcg includes
#include "tcg/boost/range_utility.h"
// boost includes
#include <boost/range/counting_range.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <QHash>
//===================================================================
void findPaletteLevels(std::set<TXshSimpleLevel *> &levels, int &rowIndex,
int &columnIndex, TPalette *palette, TXsheet *xsheet) {
rowIndex = columnIndex = -1;
int columnCount = xsheet->getColumnCount();
int c;
for (c = 0; c < columnCount; c++) {
TXshColumn *column = xsheet->getColumn(c);
if (!column || column->isEmpty()) continue;
TXshLevelColumn *levelColumn = column->getLevelColumn();
if (!levelColumn || levelColumn->isEmpty()) continue;
int r0, r1;
if (!column->getRange(r0, r1)) continue;
int r;
for (r = r0; r <= r1; r++) {
TXshCell cell = levelColumn->getCell(r);
if (cell.isEmpty()) continue;
TXshSimpleLevel *level = cell.getSimpleLevel();
if (!level || level->getPalette() != palette) continue;
levels.insert(level);
if (rowIndex < 0) {
rowIndex = r;
columnIndex = c;
}
}
}
}
//===================================================================
namespace {
bool isStyleUsed(const TVectorImageP vi, int styleId) {
int strokeCount = vi->getStrokeCount();
int i;
for (i = strokeCount - 1; i >= 0; i--) {
TStroke *stroke = vi->getStroke(i);
if (stroke && stroke->getStyle() == styleId) return true;
}
int regionCount = vi->getRegionCount();
for (i = 0; i < regionCount; i++) {
TRegion *region = vi->getRegion(i);
if (region && region->getStyle() == styleId) return true;
}
return false;
}
//-------------------------------------------------------------------
bool isStyleUsed(const TToonzImageP vi, int styleId) {
TRasterCM32P ras = vi->getRaster();
for (int y = 0; y < ras->getLy(); y++) {
TPixelCM32 *pix = ras->pixels(y), *endPix = pix + ras->getLx();
while (pix < endPix) {
if (pix->getPaint() == styleId) return true;
if (pix->getInk() == styleId) return true;
pix++;
}
}
return false;
}
//-------------------------------------------------------------------
/*! Return true if one style is used. */
bool areStylesUsed(const TImageP image, const std::vector<int> styleIds) {
int j;
for (j = 0; j < (int)styleIds.size(); j++)
if (isStyleUsed(image, styleIds[j])) return true;
return false;
}
} // namespace
//===================================================================
bool isStyleUsed(const TImageP image, int styleId) {
TVectorImageP vi = image;
TToonzImageP ti = image;
if (vi) return isStyleUsed(vi, styleId);
if (ti) return isStyleUsed(ti, styleId);
return false;
}
//===================================================================
/*! Return true if one style is used. */
bool areStylesUsed(const std::set<TXshSimpleLevel *> levels,
const std::vector<int> styleIds) {
for (auto const level : levels) {
std::vector<TFrameId> fids;
level->getFids(fids);
int i;
for (i = 0; i < (int)fids.size(); i++) {
TImageP image = level->getFrame(fids[i], true);
if (areStylesUsed(image, styleIds)) return true;
}
}
return false;
}
//===================================================================
//
// arrangeStyles
// srcPage : {a0,a1,...an } ==> dstPage : {b,b+1,...b+n-1}
//
//-------------------------------------------------------------------
/*! \namespace PaletteCmd
\brief Provides a collection of methods to manage \b TPalette.
*/
namespace {
class ArrangeStylesUndo final : public TUndo {
TPaletteHandle *m_paletteHandle;
TPaletteP m_palette;
int m_dstPageIndex;
int m_dstIndexInPage;
int m_srcPageIndex;
std::set<int> m_srcIndicesInPage;
public:
ArrangeStylesUndo(TPaletteHandle *paletteHandle, int dstPageIndex,
int dstIndexInPage, int srcPageIndex,
const std::set<int> &srcIndicesInPage)
: m_paletteHandle(paletteHandle)
, m_dstPageIndex(dstPageIndex)
, m_dstIndexInPage(dstIndexInPage)
, m_srcPageIndex(srcPageIndex)
, m_srcIndicesInPage(srcIndicesInPage) {
m_palette = m_paletteHandle->getPalette();
assert(m_palette);
assert(0 <= dstPageIndex && dstPageIndex < m_palette->getPageCount());
assert(0 <= srcPageIndex && srcPageIndex < m_palette->getPageCount());
TPalette::Page *dstPage = m_palette->getPage(dstPageIndex);
assert(dstPage);
assert(0 <= dstIndexInPage && dstIndexInPage <= dstPage->getStyleCount());
assert(!srcIndicesInPage.empty());
TPalette::Page *srcPage = m_palette->getPage(srcPageIndex);
assert(srcPage);
assert(0 <= *srcIndicesInPage.begin() &&
*srcIndicesInPage.rbegin() < srcPage->getStyleCount());
}
void undo() const override {
TPalette::Page *srcPage = m_palette->getPage(m_srcPageIndex);
assert(srcPage);
TPalette::Page *dstPage = m_palette->getPage(m_dstPageIndex);
assert(dstPage);
std::vector<int> styles;
int count = m_srcIndicesInPage.size();
int h = m_dstIndexInPage;
std::set<int>::const_iterator i;
for (i = m_srcIndicesInPage.begin(); i != m_srcIndicesInPage.end(); ++i) {
if (srcPage == dstPage && (*i) <= m_dstIndexInPage)
h--;
else
break;
}
assert(h + count - 1 <= dstPage->getStyleCount());
int k;
for (k = 0; k < count; k++) {
styles.push_back(dstPage->getStyleId(h));
dstPage->removeStyle(h, true);
}
k = 0;
for (i = m_srcIndicesInPage.begin(); i != m_srcIndicesInPage.end();
++i, ++k)
srcPage->insertStyle(*i, styles[k]);
m_paletteHandle->notifyPaletteChanged();
}
void redo() const override {
TPalette::Page *srcPage = m_palette->getPage(m_srcPageIndex);
assert(srcPage);
TPalette::Page *dstPage = m_palette->getPage(m_dstPageIndex);
assert(dstPage);
std::vector<int> styles;
std::set<int>::const_reverse_iterator i;
std::vector<int>::iterator j;
int k = m_dstIndexInPage;
for (i = m_srcIndicesInPage.rbegin(); i != m_srcIndicesInPage.rend(); ++i) {
int index = *i;
if (m_dstPageIndex == m_srcPageIndex && index < k) k--;
styles.push_back(srcPage->getStyleId(index));
srcPage->removeStyle(index, true);
}
for (j = styles.begin(); j != styles.end(); ++j)
dstPage->insertStyle(k, *j);
m_palette->setDirtyFlag(true);
m_paletteHandle->notifyPaletteChanged();
}
int getSize() const override {
return sizeof *this + m_srcIndicesInPage.size() * sizeof(int);
}
QString getHistoryString() override {
return QObject::tr("Arrange Styles in Palette %1")
.arg(QString::fromStdWString(m_palette->getPaletteName()));
}
int getHistoryType() override { return HistoryType::Palette; }
};
} // namespace
//-----------------------------------------------------------------------------
void PaletteCmd::arrangeStyles(TPaletteHandle *paletteHandle, int dstPageIndex,
int dstIndexInPage, int srcPageIndex,
const std::set<int> &srcIndicesInPage) {
if (dstPageIndex == srcPageIndex &&
dstIndexInPage == *srcIndicesInPage.begin())
return;
ArrangeStylesUndo *undo =
new ArrangeStylesUndo(paletteHandle, dstPageIndex, dstIndexInPage,
srcPageIndex, srcIndicesInPage);
undo->redo();
TUndoManager::manager()->add(undo);
}
//=============================================================================
// CreateStyle
//-----------------------------------------------------------------------------
namespace {
//-----------------------------------------------------------------------------
class CreateStyleUndo final : public TUndo {
TPaletteHandle *m_paletteHandle;
TPaletteP m_palette;
int m_pageIndex;
int m_styleId;
TColorStyle *m_style;
public:
CreateStyleUndo(TPaletteHandle *paletteHandle, int pageIndex, int styleId)
: m_paletteHandle(paletteHandle)
, m_pageIndex(pageIndex)
, m_styleId(styleId) {
m_palette = m_paletteHandle->getPalette();
m_style = m_palette->getStyle(m_styleId)->clone();
assert(m_palette);
assert(0 <= pageIndex && pageIndex < m_palette->getPageCount());
assert(0 <= styleId && styleId < m_palette->getStyleCount());
}
void undo() const override {
TPalette::Page *page = m_palette->getPage(m_pageIndex);
assert(page);
int indexInPage = page->search(m_styleId);
assert(indexInPage >= 0);
page->removeStyle(indexInPage, true);
m_paletteHandle->notifyPaletteChanged();
}
void redo() const override {
TPalette::Page *page = m_palette->getPage(m_pageIndex);
assert(page);
assert(m_palette->getStylePage(m_styleId) == 0);
int indexInPage = page->addStyle(m_styleId);
if (indexInPage == -1) {
indexInPage = page->getStyleCount();
page->insertStyle(indexInPage, m_style->getMainColor());
assert(m_styleId == page->getStyleId(indexInPage));
}
m_palette->getStyle(m_styleId)->setMainColor(m_style->getMainColor());
m_palette->getStyle(m_styleId)->setName(m_style->getName());
m_paletteHandle->notifyPaletteChanged();
}
int getSize() const override { return sizeof *this; }
QString getHistoryString() override {
return QObject::tr("Create Style#%1 in Palette %2")
.arg(QString::number(m_styleId))
.arg(QString::fromStdWString(m_palette->getPaletteName()));
}
int getHistoryType() override { return HistoryType::Palette; }
};
} // namespace
//-----------------------------------------------------------------------------
int PaletteCmd::createStyle(TPaletteHandle *paletteHandle,
TPalette::Page *page) {
int index = paletteHandle->getStyleIndex();
TPalette *palette = paletteHandle->getPalette();
int newIndex;
int UnpagedId = palette->getFirstUnpagedStyle();
if (UnpagedId != -1 && !palette->isCleanupPalette()) {
if (index == -1)
palette->getStyle(UnpagedId)->setMainColor(TPixel32::Black);
else
palette->getStyle(UnpagedId)->setMainColor(
palette->getStyle(index)->getMainColor());
newIndex = page->addStyle(UnpagedId);
} else if (!palette->isCleanupPalette()) {
if (index == -1)
newIndex = page->addStyle(TPixel32::Black);
else {
TColorStyle *style = palette->getStyle(index);
TCleanupStyle *cleanupStyle = dynamic_cast<TCleanupStyle *>(style);
if ((cleanupStyle || index == 0) && palette->isCleanupPalette()) {
TColorCleanupStyle *newCleanupStyle = new TColorCleanupStyle();
if (cleanupStyle) {
int i;
for (i = 0; i < cleanupStyle->getParamCount(); i++)
newCleanupStyle->setColorParamValue(
i, cleanupStyle->getColorParamValue(i));
}
newIndex = page->addStyle(newCleanupStyle);
} else
newIndex = page->addStyle(style->getMainColor());
}
} else /*- CleanupPaletteの場合 -*/
{
newIndex = page->addStyle(new TColorCleanupStyle(TPixel32::Red));
}
int newStyleId = page->getStyleId(newIndex);
/*- StudioPalette上でStyleを追加した場合、GlobalNameを自動で割り振る -*/
if (palette->getGlobalName() != L"") {
TColorStyle *cs = palette->getStyle(newStyleId);
std::wstring gname =
L"-" + palette->getGlobalName() + L"-" + std::to_wstring(newStyleId);
cs->setGlobalName(gname);
}
page->getStyle(newIndex)->setName(
QString("color_%1").arg(newStyleId).toStdWString());
paletteHandle->setStyleIndex(newStyleId);
palette->setDirtyFlag(true);
paletteHandle->notifyPaletteChanged();
TUndoManager::manager()->add(new CreateStyleUndo(
paletteHandle, page->getIndex(), page->getStyleId(newIndex)));
return newStyleId;
}
//=============================================================================
// addStyles
//-----------------------------------------------------------------------------
namespace {
//=============================================================================
// AddStylesUndo
//-----------------------------------------------------------------------------
class AddStylesUndo final : public TUndo {
TPaletteP m_palette;
int m_pageIndex;
int m_indexInPage;
std::vector<std::pair<TColorStyle *, int>> m_styles;
TPaletteHandle *m_paletteHandle;
public:
// creare DOPO l'inserimento
AddStylesUndo(const TPaletteP &palette, int pageIndex, int indexInPage,
int count, TPaletteHandle *paletteHandle)
: m_palette(palette)
, m_pageIndex(pageIndex)
, m_indexInPage(indexInPage)
, m_paletteHandle(paletteHandle) {
assert(m_palette);
assert(0 <= m_pageIndex && m_pageIndex < m_palette->getPageCount());
TPalette::Page *page = m_palette->getPage(m_pageIndex);
assert(page);
assert(0 <= indexInPage && indexInPage + count <= page->getStyleCount());
for (int i = 0; i < count; i++) {
std::pair<TColorStyle *, int> p;
p.second = page->getStyleId(m_indexInPage + i);
p.first = m_palette->getStyle(p.second)->clone();
m_styles.push_back(p);
}
}
~AddStylesUndo() {
for (int i = 0; i < (int)m_styles.size(); i++) delete m_styles[i].first;
}
void undo() const override {
TPalette::Page *page = m_palette->getPage(m_pageIndex);
assert(page);
int count = m_styles.size();
int i;
for (i = count - 1; i >= 0; i--) page->removeStyle(m_indexInPage + i);
m_paletteHandle->notifyPaletteChanged();
}
void redo() const override {
TPalette::Page *page = m_palette->getPage(m_pageIndex);
assert(page);
for (int i = 0; i < (int)m_styles.size(); i++) {
TColorStyle *cs = m_styles[i].first->clone();
int styleId = m_styles[i].second;
assert(m_palette->getStylePage(styleId) == 0);
m_palette->setStyle(styleId, cs);
page->insertStyle(m_indexInPage + i, styleId);
}
m_paletteHandle->notifyPaletteChanged();
}
int getSize() const override {
return sizeof *this + m_styles.size() * sizeof(TColorStyle);
}
QString getHistoryString() override {
return QObject::tr("Add Style to Palette %1")
.arg(QString::fromStdWString(m_palette->getPaletteName()));
}
int getHistoryType() override { return HistoryType::Palette; }
};
} // namespace
//-----------------------------------------------------------------------------
/*- ドラッグ&ドロップ時に呼ばれる -*/
void PaletteCmd::addStyles(TPaletteHandle *paletteHandle, int pageIndex,
int indexInPage,
const std::vector<TColorStyle *> &styles) {
TPalette *palette = paletteHandle->getPalette();
assert(0 <= pageIndex && pageIndex < palette->getPageCount());
TPalette::Page *page = palette->getPage(pageIndex);
assert(page);
assert(0 <= indexInPage && indexInPage <= page->getStyleCount());
int count = styles.size();
for (int i = 0; i < count; i++) {
page->insertStyle(indexInPage + i, styles[i]->clone());
/*-- StudioPaletteから持ち込んだ場合、その元の名前をoriginalNameに入れる
* --*/
if (styles[i]->getGlobalName() != L"") {
/*--
* もし元のStyleにOriginalNameが無ければコピー元がStudioPaletteからということ--*/
if (styles[i]->getOriginalName() == L"") {
/*-- 元のStyleの名前をペースト先のOriginalNameに入れる --*/
page->getStyle(indexInPage + i)->setOriginalName(styles[i]->getName());
}
}
/*--
* それ以外の場合は、clone()でそれぞれの名前をコピーしているので、そのままでOK
* --*/
}
TUndoManager::manager()->add(
new AddStylesUndo(palette, pageIndex, indexInPage, count, paletteHandle));
palette->setDirtyFlag(true);
}
//=============================================================================
// eraseStyles
//-----------------------------------------------------------------------------
namespace {
void eraseStylesInLevels(const std::set<TXshSimpleLevel *> &levels,
const std::vector<int> styleIds) {
for (auto const level : levels) {
std::vector<TFrameId> fids;
level->getFids(fids);
int i;
for (i = 0; i < (int)fids.size(); i++) {
TImageP image = level->getFrame(fids[i], true);
TVectorImageP vi = image;
TToonzImageP ti = image;
if (vi)
vi->eraseStyleIds(styleIds);
else if (ti)
TRop::eraseStyleIds(ti.getPointer(), styleIds);
}
}
}
} // namespace
//-----------------------------------------------------------------------------
void PaletteCmd::eraseStyles(const std::set<TXshSimpleLevel *> &levels,
const std::vector<int> &styleIds) {
typedef std::pair<const TXshSimpleLevelP, std::vector<TVectorImageP>>
LevelImages;
struct Undo final : public TUndo {
std::set<TXshSimpleLevel *> m_levels;
std::vector<int> m_styleIds;
mutable std::map<TXshSimpleLevelP, std::vector<TVectorImageP>>
m_imagesByLevel;
public:
Undo(const std::set<TXshSimpleLevel *> &levels,
const std::vector<int> &styleIds)
: m_levels(levels), m_styleIds(styleIds) {
tcg::substitute(m_imagesByLevel,
levels | boost::adaptors::filtered(isVector) |
boost::adaptors::transformed(toEmptyLevelImages));
}
bool isValid() const { return !m_levels.empty(); }
void redo() const override {
std::for_each(m_imagesByLevel.begin(), m_imagesByLevel.end(),
cloneImages);
eraseStylesInLevels(m_levels, m_styleIds);
}
void undo() const override {
std::for_each(m_imagesByLevel.begin(), m_imagesByLevel.end(),
restoreImages);
}
int getSize() const override { return 10 << 20; } // At max 10 per 100 MB
private:
static bool isVector(const TXshSimpleLevel *level) {
return (assert(level), level->getType() == PLI_XSHLEVEL);
}
static LevelImages toEmptyLevelImages(TXshSimpleLevel *level) {
return LevelImages(level, std::vector<TVectorImageP>());
}
static void copyStrokeIds(const TVectorImage &src, TVectorImage &dst) {
UINT s, sCount = src.getStrokeCount();
for (s = 0; s != sCount; ++s)
dst.getStroke(s)->setId(src.getStroke(s)->getId());
}
static TVectorImageP cloneImage(const TXshSimpleLevel &level, int f) {
TVectorImageP src = static_cast<TVectorImageP>(
level.getFrame(level.getFrameId(f), false));
TVectorImageP dst = src->clone();
copyStrokeIds(*src, *dst);
return dst;
}
static void cloneImages(LevelImages &levelImages) {
tcg::substitute(
levelImages.second,
boost::counting_range(0, levelImages.first->getFrameCount()) |
boost::adaptors::transformed([&levelImages](int f){ return cloneImage(*levelImages.first, f); }));
}
static void restoreImage(const TXshSimpleLevelP &level, int f,
const TVectorImageP &vi) {
level->setFrame(level->getFrameId(f), vi.getPointer());
}
static void restoreImages(LevelImages &levelImages) {
int f, fCount = std::min(levelImages.first->getFrameCount(),
int(levelImages.second.size()));
for (f = 0; f != fCount; ++f)
restoreImage(levelImages.first, f, levelImages.second[f]);
}
}; // Undo
if (levels.empty() || styleIds.empty()) return;
std::unique_ptr<TUndo> undo(new Undo(levels, styleIds));
if (static_cast<Undo &>(*undo).isValid()) {
undo->redo();
TUndoManager::manager()->add(undo.release());
}
}
//=============================================================================
// addPage
//-----------------------------------------------------------------------------
namespace {
class AddPageUndo final : public TUndo {
TPaletteHandle *m_paletteHandle;
TPaletteP m_palette;
int m_pageIndex;
std::wstring m_pageName;
std::vector<std::pair<TColorStyle *, int>> m_styles;
public:
// creare DOPO l'inserimento
AddPageUndo(TPaletteHandle *paletteHandle, int pageIndex,
std::wstring pageName)
: m_paletteHandle(paletteHandle)
, m_pageIndex(pageIndex)
, m_pageName(pageName) {
m_palette = m_paletteHandle->getPalette();
assert(m_palette);
assert(0 <= m_pageIndex && m_pageIndex < m_palette->getPageCount());
TPalette::Page *page = m_palette->getPage(m_pageIndex);
assert(page);
for (int i = 0; i < page->getStyleCount(); i++) {
std::pair<TColorStyle *, int> p;
p.first = page->getStyle(i)->clone();
p.second = page->getStyleId(i);
m_styles.push_back(p);
}
}
~AddPageUndo() {
for (int i = 0; i < (int)m_styles.size(); i++) delete m_styles[i].first;
}
void undo() const override {
m_palette->erasePage(m_pageIndex);
m_paletteHandle->notifyPaletteChanged();
}
void redo() const override {
TPalette::Page *page = m_palette->addPage(m_pageName);
assert(page);
assert(page->getIndex() == m_pageIndex);
for (int i = 0; i < (int)m_styles.size(); i++) {
TColorStyle *cs = m_styles[i].first->clone();
int styleId = m_styles[i].second;
assert(m_palette->getStylePage(styleId) == 0);
m_palette->setStyle(styleId, cs);
page->addStyle(styleId);
};
m_paletteHandle->notifyPaletteChanged();
}
int getSize() const override {
return sizeof *this + m_styles.size() * sizeof(TColorStyle);
}
QString getHistoryString() override {
return QObject::tr("Add Page %1 to Palette %2")
.arg(QString::fromStdWString(m_pageName))
.arg(QString::fromStdWString(m_palette->getPaletteName()));
}
int getHistoryType() override { return HistoryType::Palette; }
};
} // namespace
//-----------------------------------------------------------------------------
void PaletteCmd::addPage(TPaletteHandle *paletteHandle, std::wstring name,
bool withUndo) {
TPalette *palette = paletteHandle->getPalette();
if (name == L"")
name = L"page " + std::to_wstring(palette->getPageCount() + 1);
TPalette::Page *page = palette->addPage(name);
palette->setDirtyFlag(true);
paletteHandle->notifyPaletteChanged();
if (withUndo)
TUndoManager::manager()->add(
new AddPageUndo(paletteHandle, page->getIndex(), name));
}
//=============================================================================
// destroyPage
//-----------------------------------------------------------------------------
namespace {
class DestroyPageUndo final : public TUndo {
TPaletteHandle *m_paletteHandle;
TPaletteP m_palette;
int m_pageIndex;
std::wstring m_pageName;
std::vector<int> m_styles;
public:
DestroyPageUndo(TPaletteHandle *paletteHandle, int pageIndex)
: m_paletteHandle(paletteHandle), m_pageIndex(pageIndex) {
m_palette = m_paletteHandle->getPalette();
assert(m_palette);
assert(0 <= pageIndex && pageIndex < m_palette->getPageCount());
assert(m_palette->getPageCount() > 1);
TPalette::Page *page = m_palette->getPage(m_pageIndex);
assert(page);
m_pageName = page->getName();
m_styles.resize(page->getStyleCount());
for (int i = 0; i < page->getStyleCount(); i++)
m_styles[i] = page->getStyleId(i);
}
void undo() const override {
TPalette::Page *page = m_palette->addPage(m_pageName);
m_palette->movePage(page, m_pageIndex);
for (int i = 0; i < (int)m_styles.size(); i++) page->addStyle(m_styles[i]);
m_paletteHandle->notifyPaletteChanged();
}
void redo() const override {
m_palette->erasePage(m_pageIndex);
m_paletteHandle->notifyPaletteChanged();
}
int getSize() const override { return sizeof *this; }
QString getHistoryString() override {
return QObject::tr("Delete Page %1 from Palette %2")
.arg(QString::fromStdWString(m_pageName))
.arg(QString::fromStdWString(m_palette->getPaletteName()));
}
int getHistoryType() override { return HistoryType::Palette; }
};
} // namespace
//-----------------------------------------------------------------------------
void PaletteCmd::destroyPage(TPaletteHandle *paletteHandle, int pageIndex) {
TPalette *palette = paletteHandle->getPalette();
assert(0 <= pageIndex && pageIndex < palette->getPageCount());
TUndoManager::manager()->add(new DestroyPageUndo(paletteHandle, pageIndex));
palette->erasePage(pageIndex);
palette->setDirtyFlag(true);
paletteHandle->notifyPaletteChanged();
}
//===================================================================
// ReferenceImage
//-------------------------------------------------------------------
namespace {
//===================================================================
// SetReferenceImageUndo
//-------------------------------------------------------------------
class SetReferenceImageUndo final : public TUndo {
TPaletteP m_palette;
TPaletteP m_oldPalette, m_newPalette;
TPaletteHandle *m_paletteHandle;
public:
SetReferenceImageUndo(TPaletteP palette, TPaletteHandle *paletteHandle)
: m_palette(palette)
, m_oldPalette(palette->clone())
, m_paletteHandle(paletteHandle) {}
void onAdd() override { m_newPalette = m_palette->clone(); }
void undo() const override {
m_palette->assign(m_oldPalette.getPointer());
m_paletteHandle->notifyPaletteChanged();
}
void redo() const override {
m_palette->assign(m_newPalette.getPointer());
m_paletteHandle->notifyPaletteChanged();
}
int getSize() const override { return sizeof(*this) + sizeof(TPalette) * 2; }
QString getHistoryString() override {
return QObject::tr("Load Color Model %1 to Palette %2")
.arg(QString::fromStdString(
m_newPalette->getRefImgPath().getLevelName()))
.arg(QString::fromStdWString(m_palette->getPaletteName()));
}
int getHistoryType() override { return HistoryType::Palette; }
};
//===================================================================
// loadRefImage
//-------------------------------------------------------------------
int loadRefImage(TPaletteHandle *paletteHandle,
const PaletteCmd::ColorModelLoadingConfiguration &config,
TPaletteP levelPalette, const TFilePath &_fp,
ToonzScene *scene, const std::vector<int> &frames) {
TFilePath fp = scene->decodeFilePath(_fp);
// enable to store multiple frames in the level
QList<TImageP> imgs;
try {
TLevelReaderP lr(fp);
if (!lr) return 1;
TLevelP level = lr->loadInfo();
if (!level || level->getFrameCount() <= 0) return 1;
TLevel::Iterator it;
std::vector<TFrameId> fids;
for (it = level->begin(); it != level->end(); ++it) {
if (it->first == -1 || it->first == -2) {
assert(level->getFrameCount() == 1);
fids.push_back(it->first);
break;
}
// if the frame list is empty, store all fids of the level
if (frames.empty()) {
fids.push_back(it->first);
continue;
}
// if the frame list is specified, load only the frames matches with
// the list
else {
std::vector<int>::const_iterator framesIt;
for (framesIt = frames.begin(); framesIt != frames.end(); framesIt++) {
if (it->first.getNumber() == *framesIt) {
fids.push_back(it->first);
break;
}
}
}
}
levelPalette->setRefLevelFids(fids, !frames.empty());
const TLevel::Table *table = level->getTable();
for (int f = 0; f < fids.size(); f++) {
TFrameId fid = fids.at(f);
if (table->find(fid) != table->end()) {
TImageP img = lr->getFrameReader(fid)->load();
if (img) {
if (img->getPalette() == 0) {
if (level->getPalette() != 0)
img->setPalette(level->getPalette());
else if ((fp.getType() == "tzp" || fp.getType() == "tzu"))
img->setPalette(ToonzImageUtils::loadTzPalette(
fp.withType("plt").withNoFrame()));
}
imgs.push_back(img);
}
}
}
} catch (TException &e) {
std::wcout << L"error: " << e.getMessage() << std::endl;
} catch (...) {
std::cout << "error for other reasons" << std::endl;
}
if (imgs.empty()) return 1;
TUndo *undo = new SetReferenceImageUndo(levelPalette, paletteHandle);
bool isRasterLevel = false;
if (TRasterImageP ri = imgs.first()) isRasterLevel = true;
if (config.behavior != PaletteCmd::ReplaceColorModelPlt) // ret==1 or 3)
{
TPaletteP imagePalette;
// raster level case
if (isRasterLevel) {
int pageIndex = 0;
if (config.behavior == PaletteCmd::KeepColorModelPlt)
imagePalette = new TPalette();
else {
imagePalette = levelPalette->clone();
/*- Add new page and store color model's styles in it -*/
pageIndex =
imagePalette->addPage(QObject::tr("color model").toStdWString())
->getIndex();
}
static const int maxColorCount = 1024;
for (int i = 0; i < imgs.size(); i++) {
TRasterImageP ri = imgs.at(i);
if (!ri) continue;
TRaster32P raster = ri->getRaster();
if (!raster) continue;
int availableColorCount = maxColorCount - imagePalette->getStyleCount();
if (availableColorCount <= 0) break;
if (config.rasterPickType == PaletteCmd::PickColorChipGrid) {
// colors will be sorted according to config.colorChipOrder
QList<QPair<TPixel32, TPoint>> colors;
TColorUtils::buildColorChipPalette(
colors, raster, availableColorCount, config.gridColor,
config.gridLineWidth, config.colorChipOrder);
QList<QPair<TPixel32, TPoint>>::const_iterator it = colors.begin();
for (; it != colors.end(); ++it) {
int indexInPage =
imagePalette->getPage(pageIndex)->addStyle((*it).first);
imagePalette->getPage(pageIndex)
->getStyle(indexInPage)
->setPickedPosition((*it).second, i);
}
} else {
// colors will be automatically sorted by comparing (uint)TPixel32
// values
std::set<TPixel32> colors;
if (config.rasterPickType == PaletteCmd::PickEveryColors) {
// different colors will become separate styles
TColorUtils::buildPrecisePalette(colors, raster,
availableColorCount);
} else { // config.rasterPickType ==
// PaletteCmd::IntegrateSimilarColors
// relevant colors will united as one style
TColorUtils::buildPalette(colors, raster, availableColorCount);
}
colors.erase(TPixel::Black); // il nero viene messo dal costruttore
// della TPalette
std::set<TPixel32>::const_iterator it = colors.begin();
for (; it != colors.end(); ++it)
imagePalette->getPage(pageIndex)->addStyle(*it);
}
}
} else
imagePalette = imgs.first()->getPalette();
if (imagePalette) {
std::wstring gName = levelPalette->getGlobalName();
// Se sto caricando un reference image su una studio palette
if (!gName.empty()) {
imagePalette->setGlobalName(gName);
StudioPalette::instance()->setStylesGlobalNames(
imagePalette.getPointer());
}
// voglio evitare di sostituire una palette con pochi colori ad una con
// tanti colori
/*--
* ColorModelの色数が少ないのにOverwriteしようとした場合は、余分の分だけStyleが追加される
* --*/
while (imagePalette->getStyleCount() < levelPalette->getStyleCount()) {
int index = imagePalette->getStyleCount();
assert(index < levelPalette->getStyleCount());
TColorStyle *style = levelPalette->getStyle(index)->clone();
imagePalette->addStyle(style);
}
levelPalette->assign(imagePalette.getPointer());
}
}
// img->setPalette(0);
levelPalette->setRefImgPath(_fp);
TUndoManager::manager()->add(undo);
paletteHandle->notifyPaletteChanged();
return 0;
}
//-------------------------------------------------------------------
} // namespace
//-------------------------------------------------------------------
//===================================================================
// loadReferenceImage
// load frames specified in frames. if frames is empty, then load all frames of
// the level.
// return values -- 2: failed_to_get_palette, 1: failed_to_get_image, 0: OK
//-------------------------------------------------------------------
int PaletteCmd::loadReferenceImage(TPaletteHandle *paletteHandle,
const ColorModelLoadingConfiguration &config,
const TFilePath &_fp, ToonzScene *scene,
const std::vector<int> &frames) {
TPaletteP levelPalette = paletteHandle->getPalette();
if (!levelPalette) return 2;
int ret =
loadRefImage(paletteHandle, config, levelPalette, _fp, scene, frames);
if (ret != 0) return ret;
// when choosing replace(Keep the destination palette), dirty flag is
// unchanged
if (config.behavior != ReplaceColorModelPlt) {
levelPalette->setDirtyFlag(true);
paletteHandle->notifyPaletteDirtyFlagChanged();
}
return 0;
}
//===================================================================
// removeReferenceImage
//-------------------------------------------------------------------
void PaletteCmd::removeReferenceImage(TPaletteHandle *paletteHandle) {
TPaletteP levelPalette = paletteHandle->getPalette();
if (!levelPalette) return;
TUndo *undo = new SetReferenceImageUndo(levelPalette, paletteHandle);
levelPalette->setRefImg(TImageP());
levelPalette->setRefImgPath(TFilePath());
std::vector<TFrameId> fids;
levelPalette->setRefLevelFids(fids, false);
levelPalette->setDirtyFlag(true);
paletteHandle->notifyPaletteChanged();
TUndoManager::manager()->add(undo);
}
//=============================================================================
// MovePage
//-----------------------------------------------------------------------------
namespace {
class MovePageUndo final : public TUndo {
TPaletteHandle *m_paletteHandle;
TPaletteP m_palette;
int m_srcIndex;
int m_dstIndex;
public:
MovePageUndo(TPaletteHandle *paletteHandle, int srcIndex, int dstIndex)
: m_paletteHandle(paletteHandle)
, m_srcIndex(srcIndex)
, m_dstIndex(dstIndex) {
m_palette = m_paletteHandle->getPalette();
}
void undo() const override {
m_palette->movePage(m_palette->getPage(m_dstIndex), m_srcIndex);
m_paletteHandle->notifyPaletteChanged();
}
void redo() const override {
m_palette->movePage(m_palette->getPage(m_srcIndex), m_dstIndex);
m_paletteHandle->notifyPaletteChanged();
}
int getSize() const override { return sizeof *this; }
QString getHistoryString() override { return QObject::tr("Move Page"); }
int getHistoryType() override { return HistoryType::Palette; }
};
} // namespace
void PaletteCmd::movePalettePage(TPaletteHandle *paletteHandle, int srcIndex,
int dstIndex) {
TPaletteP palette = paletteHandle->getPalette();
palette->movePage(palette->getPage(srcIndex), dstIndex);
TUndoManager::manager()->add(
new MovePageUndo(paletteHandle, srcIndex, dstIndex));
paletteHandle->notifyPaletteChanged();
}
//=============================================================================
// RenamePage
//-----------------------------------------------------------------------------
namespace {
class RenamePageUndo final : public TUndo {
TPaletteHandle *m_paletteHandle;
TPaletteP m_palette;
int m_pageIndex;
std::wstring m_newName;
std::wstring m_oldName;
public:
RenamePageUndo(TPaletteHandle *paletteHandle, int pageIndex,
const std::wstring &newName)
: m_paletteHandle(paletteHandle)
, m_pageIndex(pageIndex)
, m_newName(newName) {
m_palette = m_paletteHandle->getPalette();
m_oldName = m_palette->getPage(pageIndex)->getName();
}
void undo() const override {
TPalette::Page *page = m_palette->getPage(m_pageIndex);
assert(page);
page->setName(m_oldName);
m_paletteHandle->notifyPaletteChanged();
}
void redo() const override {
TPalette::Page *page = m_palette->getPage(m_pageIndex);
assert(page);
page->setName(m_newName);
m_paletteHandle->notifyPaletteChanged();
}
int getSize() const override { return sizeof *this; }
QString getHistoryString() override {
return QObject::tr("Rename Page %1 > %2")
.arg(QString::fromStdWString(m_oldName))
.arg(QString::fromStdWString(m_newName));
}
int getHistoryType() override { return HistoryType::Palette; }
};
} // namespace
void PaletteCmd::renamePalettePage(TPaletteHandle *paletteHandle, int pageIndex,
const std::wstring &newName) {
if (!paletteHandle) return;
TPalette *palette = paletteHandle->getPalette();
if (!palette) return;
if (pageIndex < 0 || pageIndex >= palette->getPageCount()) return;
RenamePageUndo *undo = new RenamePageUndo(paletteHandle, pageIndex, newName);
paletteHandle->notifyPaletteChanged();
TPalette::Page *page = palette->getPage(pageIndex);
assert(page);
page->setName(newName);
palette->setDirtyFlag(true);
paletteHandle->notifyPaletteChanged();
TUndoManager::manager()->add(undo);
}
//=============================================================================
// RenamePaletteStyle
//-----------------------------------------------------------------------------
namespace {
class RenamePaletteStyleUndo final : public TUndo {
TPaletteHandle *m_paletteHandle; // Usato nell'undo e nel redo per lanciare
// la notifica di cambiamento
int m_styleId;
TPaletteP m_palette;
std::wstring m_newName;
std::wstring m_oldName;
public:
RenamePaletteStyleUndo(TPaletteHandle *paletteHandle,
const std::wstring &newName)
: m_paletteHandle(paletteHandle), m_newName(newName) {
m_palette = paletteHandle->getPalette();
assert(m_palette);
m_styleId = paletteHandle->getStyleIndex();
TColorStyle *style = m_palette->getStyle(m_styleId);
assert(style);
m_oldName = style->getName();
}
void undo() const override {
TColorStyle *style = m_palette->getStyle(m_styleId);
assert(style);
style->setName(m_oldName);
m_paletteHandle->notifyColorStyleChanged(false);
}
void redo() const override {
TColorStyle *style = m_palette->getStyle(m_styleId);
assert(style);
style->setName(m_newName);
m_paletteHandle->notifyColorStyleChanged(false);
}
int getSize() const override { return sizeof *this; }
QString getHistoryString() override {
return QObject::tr("Rename Style#%1 in Palette%2 : %3 > %4")
.arg(QString::number(m_styleId))
.arg(QString::fromStdWString(m_palette->getPaletteName()))
.arg(QString::fromStdWString(m_oldName))
.arg(QString::fromStdWString(m_newName));
}
int getHistoryType() override { return HistoryType::Palette; }
};
} // namespace
void PaletteCmd::renamePaletteStyle(TPaletteHandle *paletteHandle,
const std::wstring &newName) {
if (!paletteHandle) return;
TPalette *palette = paletteHandle->getPalette();
if (!palette) return;
TColorStyle *style = paletteHandle->getStyle();
if (!style) return;
if (style->getName() == newName) return;
RenamePaletteStyleUndo *undo =
new RenamePaletteStyleUndo(paletteHandle, newName);
style->setName(newName);
palette->setDirtyFlag(true);
paletteHandle->notifyColorStyleChanged(false);
TUndoManager::manager()->add(undo);
}
//=============================================================================
// organizePaletteStyle
// called in ColorModelViewer::pick() - move selected style to the first page
//-----------------------------------------------------------------------------
namespace {
class setStylePickedPositionUndo final : public TUndo {
TPaletteHandle *m_paletteHandle; // Used in undo and redo to notify change
int m_styleId;
TPaletteP m_palette;
TColorStyle::PickedPosition m_newPos;
TColorStyle::PickedPosition m_oldPos;
public:
setStylePickedPositionUndo(TPaletteHandle *paletteHandle, int styleId,
const TColorStyle::PickedPosition &newPos)
: m_paletteHandle(paletteHandle), m_styleId(styleId), m_newPos(newPos) {
m_palette = paletteHandle->getPalette();
assert(m_palette);
TColorStyle *style = m_palette->getStyle(m_styleId);
assert(style);
m_oldPos = style->getPickedPosition();
}
void undo() const override {
TColorStyle *style = m_palette->getStyle(m_styleId);
assert(style);
style->setPickedPosition(m_oldPos);
m_paletteHandle->notifyColorStyleChanged(false);
}
void redo() const override {
TColorStyle *style = m_palette->getStyle(m_styleId);
assert(style);
style->setPickedPosition(m_newPos);
m_paletteHandle->notifyColorStyleChanged(false);
}
int getSize() const override { return sizeof *this; }
QString getHistoryString() override {
return QObject::tr("Set Picked Position of Style#%1 in Palette%2 : %3,%4")
.arg(QString::number(m_styleId))
.arg(QString::fromStdWString(m_palette->getPaletteName()))
.arg(QString::number(m_newPos.pos.x))
.arg(QString::number(m_newPos.pos.y));
}
int getHistoryType() override { return HistoryType::Palette; }
};
} // namespace
void PaletteCmd::organizePaletteStyle(
TPaletteHandle *paletteHandle, int styleId,
const TColorStyle::PickedPosition &point) {
if (!paletteHandle) return;
TPalette *palette = paletteHandle->getPalette();
if (!palette) return;
// if the style is already in the first page, then do nothing
TPalette::Page *page = palette->getStylePage(styleId);
if (!page || page->getIndex() == 0) return;
int indexInPage = page->search(styleId);
TUndoManager::manager()->beginBlock();
// call arrangeStyles() to move style to the first page
arrangeStyles(paletteHandle, 0, palette->getPage(0)->getStyleCount(),
page->getIndex(), {indexInPage});
// then set the picked position
setStylePickedPositionUndo *undo =
new setStylePickedPositionUndo(paletteHandle, styleId, point);
undo->redo();
TUndoManager::manager()->add(undo);
TUndoManager::manager()->endBlock();
}
//=============================================================================
// called in ColorModelViewer::repickFromColorModel().
// Pick color from the img for all styles with "picked position" value.
//-----------------------------------------------------------------------------
namespace {
class pickColorByUsingPickedPositionUndo final : public TUndo {
TPaletteHandle *m_paletteHandle; // Used in undo and redo to notify change
TPaletteP m_palette;
QHash<int, QPair<TPixel32, TPixel32>> m_styleList;
public:
pickColorByUsingPickedPositionUndo(
TPaletteHandle *paletteHandle,
QHash<int, QPair<TPixel32, TPixel32>> styleList)
: m_paletteHandle(paletteHandle), m_styleList(styleList) {
m_palette = paletteHandle->getPalette();
assert(m_palette);
}
void undo() const override {
QHash<int, QPair<TPixel32, TPixel32>>::const_iterator i =
m_styleList.constBegin();
while (i != m_styleList.constEnd()) {
TColorStyle *style = m_palette->getStyle(i.key());
assert(style);
style->setMainColor(i.value().first);
++i;
}
m_paletteHandle->notifyColorStyleChanged(false);
}
void redo() const override {
QHash<int, QPair<TPixel32, TPixel32>>::const_iterator i =
m_styleList.constBegin();
while (i != m_styleList.constEnd()) {
TColorStyle *style = m_palette->getStyle(i.key());
assert(style);
style->setMainColor(i.value().second);
++i;
}
m_paletteHandle->notifyColorStyleChanged(false);
}
int getSize() const override { return sizeof *this; }
QString getHistoryString() override {
return QObject::tr("Update Colors by Using Picked Positions in Palette %1")
.arg(QString::fromStdWString(m_palette->getPaletteName()));
}
int getHistoryType() override { return HistoryType::Palette; }
};
TPixel32 pickColor(TRasterImageP ri, const TPoint &rasterPoint) {
TRasterP raster;
raster = ri->getRaster();
if (!TRect(raster->getSize()).contains(rasterPoint))
return TPixel32::Transparent;
TRaster32P raster32 = raster;
if (raster32) return raster32->pixels(rasterPoint.y)[rasterPoint.x];
TRasterGR8P rasterGR8 = raster;
if (rasterGR8)
return toPixel32(rasterGR8->pixels(rasterPoint.y)[rasterPoint.x]);
return TPixel32::Transparent;
}
} // namespace
void PaletteCmd::pickColorByUsingPickedPosition(TPaletteHandle *paletteHandle,
TImageP img, int frame) {
TRasterImageP ri = img;
if (!ri) return;
TPalette *currentPalette = paletteHandle->getPalette();
if (!currentPalette) return;
TDimension imgSize = ri->getRaster()->getSize();
QHash<int, QPair<TPixel32, TPixel32>> styleList;
// For all styles (starting from #1 as #0 is reserved for the transparent)
for (int sId = 1; sId < currentPalette->getStyleCount(); sId++) {
TColorStyle *style = currentPalette->getStyle(sId);
TPoint pp = style->getPickedPosition().pos;
int pickedFrame = style->getPickedPosition().frame;
// If style has a valid picked position
if (pp != TPoint() && frame == pickedFrame && pp.x >= 0 &&
pp.x < imgSize.lx && pp.y >= 0 && pp.y < imgSize.ly &&
style->hasMainColor()) {
TPixel32 beforeColor = style->getMainColor();
TPixel32 afterColor = pickColor(ri, pp);
style->setMainColor(afterColor);
//... store the style in the list for undo
styleList.insert(sId, QPair<TPixel32, TPixel32>(beforeColor, afterColor));
}
}
// if some style has been changed, then register undo and notify changes
if (!styleList.isEmpty()) {
pickColorByUsingPickedPositionUndo *undo =
new pickColorByUsingPickedPositionUndo(paletteHandle, styleList);
TUndoManager::manager()->add(undo);
paletteHandle->notifyColorStyleChanged(false, true); // set dirty flag here
}
}