#include "toonz/studiopalette.h"
#include "tfiletype.h"
#include "tstream.h"
#include "tconvert.h"
#include "tlevel_io.h"
#include "trasterimage.h"
#include "traster.h"
#include "tsystem.h"
#include "tcolorstyles.h"
#include "toonz/toonzfolders.h"
#include "toonz/tproject.h"
#include "toonz/toonzscene.h"
#include "tpalette.h"
#include <time.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <string.h>
//===================================================================
//-------------------------------------------------------------------
namespace
{
//-------------------------------------------------------------------
TFilePath makeUniqueName(TFilePath fp)
{
if (TFileStatus(fp).doesExist() == false)
return fp;
std::wstring name = fp.getWideName();
int index = 2;
int j = name.find_last_not_of(L"0123456789");
if (j != (int)std::wstring::npos && j + 1 < (int)name.length()) {
index = std::stoi(name.substr(j + 1)) + 1;
name = name.substr(0, j + 1);
}
for (;;) {
fp = fp.withName(name + std::to_wstring(index));
if (TFileStatus(fp).doesExist() == false)
return fp;
index++;
}
}
//-------------------------------------------------------------------
TPalette *loadPliPalette(const TFilePath &fp)
{
TLevelReaderP lr(fp);
TLevelP level = lr->loadInfo();
int frameCount = level->getFrameCount();
if (frameCount < 1)
return 0;
TPalette *palette = level->getPalette();
if (!palette)
return 0;
return palette->clone();
}
//-------------------------------------------------------------------
TPalette *loadTplPalette(const TFilePath &fp)
{
TPersist *p = 0;
TIStream is(fp);
is >> p;
TPalette *palette = dynamic_cast<TPalette *>(p);
return palette;
}
//-------------------------------------------------------------------
TPalette *loadToonz46Palette(const TFilePath &fp)
{
TImageP pltImg;
TImageReader::load(fp, pltImg);
if (!pltImg)
return 0;
TRasterImageP pltRasImg(pltImg);
if (!pltRasImg)
return 0;
TRaster32P rasPlt = pltRasImg->getRaster();
if (!rasPlt)
return 0;
TPalette *palette = new TPalette();
const int offset = 0; // FirstUserStyle-1;
assert(rasPlt->getLy() == 2);
rasPlt->lock();
TPixel32 *pixelRow = rasPlt->pixels(0);
int x;
for (x = 1; x < rasPlt->getLx(); ++x) {
TPixel32 color = pixelRow[x];
int styleId = offset + x;
if (styleId < palette->getStyleCount())
palette->setStyle(styleId, color);
else {
int j = palette->addStyle(color);
assert(j == styleId);
}
}
// aggiungo solo i colori usati (salvo il BG)
pixelRow = rasPlt->pixels(1);
TPalette::Page *page = palette->getPage(0);
for (x = 1; x < rasPlt->getLx(); ++x) {
if (pixelRow[x].r == 255)
page->addStyle(offset + x); // palette->addStyleToPage(offset+x, L"colors");
}
rasPlt->unlock();
return palette;
}
//-------------------------------------------------------------------
std::wstring readPaletteGlobalName(TFilePath path)
{
try {
TIStream is(path);
if (!is)
return L"";
std::string tagName;
if (!is.matchTag(tagName) || tagName != "palette")
return L"";
std::string name;
if (is.getTagParam("name", name))
return ::to_wstring(name);
} catch (...) {
}
return L"";
}
//-------------------------------------------------------------------
TFilePath searchPalette(TFilePath path, std::wstring paletteId)
{
TFilePathSet q;
try {
TSystem::readDirectory(q, path);
} catch (...) {
}
for (TFilePathSet::iterator i = q.begin(); i != q.end(); ++i) {
TFilePath fp = *i;
if (fp.getType() == "tpl") {
std::wstring gname = readPaletteGlobalName(fp);
if (gname == paletteId)
return fp;
} else if (TFileStatus(fp).isDirectory()) {
TFilePath palettePath = searchPalette(fp, paletteId);
if (palettePath != TFilePath())
return palettePath;
}
}
return TFilePath();
}
bool studioPaletteHasBeenReferred = false;
static std::map<std::wstring, TFilePath> table;
//-------------------------------------------------------------------
} // namespace
//-------------------------------------------------------------------
//===================================================================
//
// StudioPalette
//
//-------------------------------------------------------------------
StudioPalette::StudioPalette()
{
try {
m_root = ToonzFolder::getStudioPaletteFolder();
} catch (...) {
return;
}
if (!TFileStatus(m_root).doesExist()) {
try {
TSystem::mkDir(m_root);
FolderListenerManager::instance()->notifyFolderChanged(m_root.getParentDir());
} catch (...) {
}
try {
TSystem::mkDir(getLevelPalettesRoot());
FolderListenerManager::instance()->notifyFolderChanged(getLevelPalettesRoot().getParentDir());
} catch (...) {
}
}
}
//-------------------------------------------------------------------
StudioPalette::~StudioPalette()
{
}
//-------------------------------------------------------------------
bool StudioPalette::m_enabled = true;
//-------------------------------------------------------------------
void StudioPalette::enable(bool enabled)
{
assert(studioPaletteHasBeenReferred == false);
m_enabled = enabled;
}
//-------------------------------------------------------------------
StudioPalette *StudioPalette::instance()
{
static StudioPalette _instance;
studioPaletteHasBeenReferred = true;
assert(m_enabled);
return &_instance;
}
//-------------------------------------------------------------------
TFilePath StudioPalette::getLevelPalettesRoot()
{
return m_root + "Toonz Palettes";
}
//-------------------------------------------------------------------
TFilePath StudioPalette::getProjectPalettesRoot()
{
TProjectP p = TProjectManager::instance()->getCurrentProject();
TFilePath folderName = p->getFolder(TProject::Palettes);
if (folderName.isEmpty())
return TFilePath();
if (folderName.isAbsolute())
return folderName;
return p->getProjectFolder() + folderName;
}
//-------------------------------------------------------------------
bool loadRefImg(TPalette *palette, TFilePath dir)
{
assert(palette);
TFilePath fp = palette->getRefImgPath();
if (fp == TFilePath() || !TSystem::doesExistFileOrLevel(fp))
return false;
if (!fp.isAbsolute())
fp = dir + fp;
TLevelReaderP lr(fp);
if (!lr)
return false;
TLevelP level = lr->loadInfo();
if (!level || level->getFrameCount() == 0)
return false;
TLevel::Iterator it = level->begin();
TImageP img = lr->getFrameReader(it->first)->load();
if (!img)
return false;
img->setPalette(0);
palette->setRefImg(img);
return true;
}
//-------------------------------------------------------------------
TPalette *StudioPalette::getPalette(
const TFilePath &path,
bool loadRefImgFlag)
{
try {
if (path.getType() != "tpl")
return 0;
TPalette *palette = load(path);
if (!palette)
return 0;
if (loadRefImgFlag)
loadRefImg(palette, path.getParentDir());
// palette->addRef(); // ci va??
return palette;
} catch (...) {
return 0;
}
}
//-------------------------------------------------------------------
void StudioPalette::movePalette(const TFilePath &dstPath, const TFilePath &srcPath)
{
try {
TSystem::renameFile(dstPath, srcPath);
} catch (...) {
return;
}
std::wstring id = readPaletteGlobalName(dstPath);
table.erase(id);
FolderListenerManager::instance()->notifyFolderChanged(dstPath.getParentDir());
notifyMove(dstPath, srcPath);
}
//-------------------------------------------------------------------
int StudioPalette::getChildren(std::vector<TFilePath> &fps, const TFilePath &folderPath)
{
TFilePathSet q;
if (TFileStatus(folderPath).isDirectory()) {
try {
TSystem::readDirectory(q, folderPath, false, false);
} catch (...) {
}
}
for (TFilePathSet::iterator i = q.begin(); i != q.end(); ++i)
if (isFolder(*i) || isPalette(*i))
fps.push_back(*i);
// fps.push_back(m_root+"butta.tpl");
return fps.size();
}
//-------------------------------------------------------------------
int StudioPalette::getChildCount(const TFilePath &folderPath)
{
TFilePathSet q;
try {
TSystem::readDirectory(q, folderPath);
} catch (...) {
}
return q.size();
}
//-------------------------------------------------------------------
bool StudioPalette::isFolder(const TFilePath &path)
{
return TFileStatus(path).isDirectory();
}
//-------------------------------------------------------------------
bool StudioPalette::isReadOnly(const TFilePath &path)
{
return !TFileStatus(path).isWritable();
}
//-------------------------------------------------------------------
bool StudioPalette::isPalette(const TFilePath &path)
{
return path.getType() == "tpl";
}
//-------------------------------------------------------------------
/*! check if the palette is studio palette or level palette in order to separate icons in the StudioPaletteTree.
*/
bool StudioPalette::hasGlobalName(const TFilePath &path)
{
return (readPaletteGlobalName(path) != L"");
}
//-------------------------------------------------------------------
bool StudioPalette::isLevelPalette(const TFilePath &path)
{
TPalette *palette = getPalette(path);
if (!palette)
return false;
bool ret = !palette->isCleanupPalette();
delete palette;
return ret;
}
//-------------------------------------------------------------------
TFilePath StudioPalette::createFolder(const TFilePath &parentFolderPath)
{
TFilePath path = makeUniqueName(parentFolderPath + "new folder");
try {
TSystem::mkDir(path);
} catch (...) {
return TFilePath();
}
FolderListenerManager::instance()->notifyFolderChanged(parentFolderPath);
notifyTreeChange();
return path;
}
//-------------------------------------------------------------------
void StudioPalette::createFolder(const TFilePath &parentFolderPath, std::wstring name)
{
TFilePath fp = parentFolderPath + name;
if (TFileStatus(fp).doesExist())
return;
try {
TSystem::mkDir(fp);
} catch (...) {
return;
}
FolderListenerManager::instance()->notifyFolderChanged(parentFolderPath);
notifyTreeChange();
}
//-------------------------------------------------------------------
TFilePath StudioPalette::createPalette(const TFilePath &folderPath, std::string name)
{
TPalette *palette = 0;
if (name == "")
name = "new palette";
palette = new TPalette();
TFilePath fp = makeUniqueName(folderPath + (name + ".tpl"));
time_t ltime;
time(<ime);
std::wstring gname = std::to_wstring(ltime) + L"_" + std::to_wstring(rand());
palette->setGlobalName(gname);
setStylesGlobalNames(palette);
save(fp, palette);
delete palette;
notifyTreeChange();
return fp;
}
//-------------------------------------------------------------------
void StudioPalette::setPalette(const TFilePath &palettePath, const TPalette *plt, bool notifyPaletteChanged)
{
assert(palettePath.getType() == "tpl");
TPalette *palette = plt->clone();
palette->setIsLocked(plt->isLocked());
palette->addRef();
std::wstring pgn = palette->getGlobalName();
if (TFileStatus(palettePath).doesExist())
pgn = readPaletteGlobalName(palettePath);
palette->setGlobalName(pgn);
setStylesGlobalNames(palette);
save(palettePath, palette);
palette->release();
if (notifyPaletteChanged)
notifyPaletteChange(palettePath);
}
//-------------------------------------------------------------------
void StudioPalette::deletePalette(const TFilePath &palettePath)
{
assert(palettePath.getType() == "tpl");
try {
TSystem::deleteFile(palettePath);
} catch (...) {
return;
}
notifyTreeChange();
}
//-------------------------------------------------------------------
void StudioPalette::deleteFolder(const TFilePath &path)
{
try {
TSystem::rmDirTree(path);
} catch (...) {
}
notifyTreeChange();
}
//-------------------------------------------------------------------
TFilePath StudioPalette::importPalette(
const TFilePath &dstFolder,
const TFilePath &srcPath)
{
TPaletteP palette;
std::string ext = srcPath.getType();
try {
if (ext == "plt")
palette = loadToonz46Palette(srcPath);
else if (ext == "pli")
palette = loadPliPalette(srcPath);
else if (ext == "tpl")
palette = loadTplPalette(srcPath);
} catch (...) {
}
if (!palette)
return TFilePath();
std::wstring name = srcPath.getWideName();
assert(!palette->isCleanupPalette());
// convertToLevelPalette(palette.getPointer());
TFilePath fp = makeUniqueName(dstFolder + (name + L".tpl"));
time_t ltime;
time(<ime);
std::wstring gname = std::to_wstring(ltime) + L"_" + std::to_wstring(rand());
palette->setGlobalName(gname);
setStylesGlobalNames(palette.getPointer());
TSystem::touchParentDir(fp);
save(fp, palette.getPointer());
notifyTreeChange();
return fp;
}
//-------------------------------------------------------------------
//TFilePath StudioPalette::getRefImage(const TFilePath palette)
//{
// return palette.withType("pli");
//}
//-------------------------------------------------------------------
void foobar(std::wstring paletteId)
{
table.erase(paletteId);
}
TFilePath StudioPalette::getPalettePath(std::wstring paletteId)
{
std::map<std::wstring, TFilePath>::iterator it = table.find(paletteId);
if (it != table.end())
return it->second;
TFilePath fp = searchPalette(m_root, paletteId);
if (fp == TFilePath()) {
fp = searchPalette(getProjectPalettesRoot(), paletteId);
}
table[paletteId] = fp;
return fp;
}
//-------------------------------------------------------------------
TPalette *StudioPalette::getPalette(std::wstring paletteId)
{
TFilePath palettePath = getPalettePath(paletteId);
if (palettePath != TFilePath())
return getPalette(palettePath);
else
return 0;
}
//-------------------------------------------------------------------
TColorStyle *StudioPalette::getStyle(std::wstring styleId)
{
return 0;
}
//-------------------------------------------------------------------
std::pair<TFilePath, int> StudioPalette::getSourceStyle(TColorStyle *cs)
{
std::pair<TFilePath, int> ret(TFilePath(), -1);
if (!cs)
return ret;
std::wstring gname = cs->getGlobalName();
if (gname == L"")
return ret;
int k = gname.find_first_of(L'-', 1);
if (k == (int)std::wstring::npos)
return ret;
std::wstring paletteId = gname.substr(1, k - 1);
ret.first = getPalettePath(paletteId) - m_root;
ret.second = std::stoi(gname.substr(k + 1));
return ret;
}
//-------------------------------------------------------------------
/*! return if any style in the palette is changed
*/
bool StudioPalette::updateLinkedColors(TPalette *palette)
{
bool paletteIsChanged = false;
std::map<std::wstring, TPaletteP> table;
for (int i = 0; i < palette->getStyleCount(); i++) {
TColorStyle *cs = palette->getStyle(i);
std::wstring gname = cs->getGlobalName();
if (gname == L"" || gname[0] != L'+')
continue;
int k = gname.find_first_of(L'-', 1);
if (k == (int)std::wstring::npos)
continue;
std::wstring paletteId = gname.substr(1, k - 1);
std::map<std::wstring, TPaletteP>::iterator it;
it = table.find(paletteId);
TPalette *spPalette = 0;
if (it == table.end()) {
spPalette =
StudioPalette::instance()
->getPalette(paletteId);
if (!spPalette)
continue;
table[paletteId] = spPalette;
// spPalette->release();
assert(spPalette->getRefCount() == 1);
} else
spPalette = it->second.getPointer();
int j = std::stoi(gname.substr(k + 1));
if (spPalette && 0 <= j && j < spPalette->getStyleCount()) {
TColorStyle *spStyle = spPalette->getStyle(j);
assert(spStyle);
spStyle = spStyle->clone();
spStyle->setGlobalName(gname);
//put the style name in the studio palette into the original name
spStyle->setOriginalName(spStyle->getName());
//.. and keep the style name unchanged
spStyle->setName(cs->getName());
palette->setStyle(i, spStyle);
paletteIsChanged = true;
}
}
return paletteIsChanged;
}
//-------------------------------------------------------------------
void StudioPalette::setStylesGlobalNames(TPalette *palette)
{
for (int i = 0; i < palette->getStyleCount(); i++) {
TColorStyle *cs = palette->getStyle(i);
// set global name only to the styles of which the global name is empty
if (cs->getGlobalName() == L"") {
std::wstring gname = L"-" + palette->getGlobalName() + L"-" + std::to_wstring(i);
cs->setGlobalName(gname);
}
}
}
//-------------------------------------------------------------------
void StudioPalette::save(const TFilePath &path, TPalette *palette)
{
TOStream os(path);
std::map<std::string, std::string> attr;
attr["name"] = ::to_string(palette->getGlobalName());
os.openChild("palette", attr);
palette->saveData(os);
os.closeChild();
}
//-------------------------------------------------------------------
TPalette *StudioPalette::load(const TFilePath &path)
{
try {
TIStream is(path);
if (!is)
return 0;
std::string tagName;
if (!is.matchTag(tagName) || tagName != "palette")
return 0;
std::string gname;
is.getTagParam("name", gname);
TPalette *palette = new TPalette();
palette->loadData(is);
palette->setGlobalName(::to_wstring(gname));
is.matchEndTag();
palette->setPaletteName(path.getWideName());
return palette;
} catch (...) {
return 0;
}
}
//-------------------------------------------------------------------
void StudioPalette::addListener(Listener *listener)
{
if (std::find(m_listeners.begin(), m_listeners.end(), listener) == m_listeners.end())
m_listeners.push_back(listener);
}
//-------------------------------------------------------------------
void StudioPalette::removeListener(Listener *listener)
{
m_listeners.erase(
std::remove(m_listeners.begin(), m_listeners.end(), listener),
m_listeners.end());
}
//-------------------------------------------------------------------
void StudioPalette::notifyTreeChange()
{
for (std::vector<Listener *>::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
(*it)->onStudioPaletteTreeChange();
}
//-------------------------------------------------------------------
void StudioPalette::notifyMove(const TFilePath &dstPath, const TFilePath &srcPath)
{
for (std::vector<Listener *>::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
(*it)->onStudioPaletteMove(dstPath, srcPath);
}
//-------------------------------------------------------------------
void StudioPalette::notifyPaletteChange(const TFilePath &palette)
{
for (std::vector<Listener *>::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it)
(*it)->onStudioPaletteChange(palette);
}