#include "toonz/studiopalettecmd.h" #include "toonz/tpalettehandle.h" #include "toonz/txsheethandle.h" #include "toonz/txshlevelhandle.h" #include "tundo.h" #include "tcolorstyles.h" #include "tsystem.h" #include "tconvert.h" #include "ttoonzimage.h" #include "timagecache.h" #include "toonz/studiopalette.h" #include "toonz/toonzscene.h" #include "toonz/levelset.h" #include "toonz/txshsimplelevel.h" #include "toonz/txshleveltypes.h" #include "toonz/sceneproperties.h" #include "toonz/toonzfolders.h" #include "toonz/txsheet.h" #include #include "historytypes.h" /*! \namespace StudioPaletteCmd \brief Provides a collection of methods to manage \b StudioPalette. */ //============================================================================= namespace { //----------------------------------------------------------------------------- //============================================================================= // PaletteAssignUndo : Undo for the "Load into Current Palette" command. class PaletteAssignUndo final : public TUndo { TPaletteP m_targetPalette, m_oldPalette, m_newPalette; TPaletteHandle *m_paletteHandle; public: PaletteAssignUndo(const TPaletteP &targetPalette, const TPaletteP &oldPalette, const TPaletteP &newPalette, TPaletteHandle *paletteHandle) : m_targetPalette(targetPalette) , m_oldPalette(oldPalette) , m_newPalette(newPalette) , m_paletteHandle(paletteHandle) {} void undo() const override { m_targetPalette->assign(m_oldPalette.getPointer()); m_paletteHandle->notifyPaletteChanged(); } void redo() const override { m_targetPalette->assign(m_newPalette.getPointer()); m_paletteHandle->notifyPaletteChanged(); } int getSize() const override { return sizeof(*this) + (m_targetPalette->getStyleCount() + m_oldPalette->getStyleCount() + m_newPalette->getStyleCount()) * 100; } QString getHistoryString() override { return QObject::tr("Load into Current Palette > %1") .arg(QString::fromStdWString(m_targetPalette->getPaletteName())); } int getHistoryType() override { return HistoryType::Palette; } }; //============================================================================= // StudioPaletteAssignUndo : Undo for the "Replace with Current Palette" // command. class StudioPaletteAssignUndo final : public TUndo { TPaletteP m_oldPalette, m_newPalette; TFilePath m_fp; TPaletteHandle *m_paletteHandle; public: StudioPaletteAssignUndo(const TFilePath &targetPath, const TPaletteP &oldPalette, const TPaletteP &newPalette, TPaletteHandle *paletteHandle) : m_fp(targetPath) , m_oldPalette(oldPalette) , m_newPalette(newPalette) , m_paletteHandle(paletteHandle) {} void undo() const override { StudioPalette *sp = StudioPalette::instance(); sp->setPalette(m_fp, m_oldPalette.getPointer(), true); m_paletteHandle->notifyPaletteChanged(); } void redo() const override { StudioPalette *sp = StudioPalette::instance(); sp->setPalette(m_fp, m_newPalette.getPointer(), true); m_paletteHandle->notifyPaletteChanged(); } int getSize() const override { return sizeof(*this) + (m_oldPalette->getStyleCount() + m_newPalette->getStyleCount()) * 100; } QString getHistoryString() override { return QObject::tr("Replace with Current Palette > %1") .arg(QString::fromStdString(m_fp.getLevelName())); } int getHistoryType() override { return HistoryType::Palette; } }; //============================================================================= // DeletePaletteUndo class DeletePaletteUndo final : public TUndo { TFilePath m_palettePath; TPaletteP m_palette; public: DeletePaletteUndo(const TFilePath &palettePath) : m_palettePath(palettePath) { m_palette = StudioPalette::instance()->getPalette(m_palettePath); } void undo() const override { StudioPalette::instance()->setPalette(m_palettePath, m_palette->clone(), true); } void redo() const override { StudioPalette::instance()->deletePalette(m_palettePath); } int getSize() const override { return sizeof(*this) + sizeof(TPalette); } QString getHistoryString() override { return QObject::tr("Delete Studio Palette : %1") .arg(QString::fromStdString(m_palettePath.getLevelName())); } int getHistoryType() override { return HistoryType::Palette; } }; //============================================================================= // CreatePaletteUndo class CreatePaletteUndo final : public TUndo { TFilePath m_palettePath; TPaletteP m_palette; public: CreatePaletteUndo(const TFilePath &palettePath) : m_palettePath(palettePath) { m_palette = StudioPalette::instance()->getPalette(m_palettePath); } void undo() const override { StudioPalette::instance()->deletePalette(m_palettePath); } void redo() const override { StudioPalette::instance()->setPalette(m_palettePath, m_palette->clone(), true); } int getSize() const override { return sizeof(*this) + sizeof(TPalette); } QString getHistoryString() override { return QObject::tr("Create Studio Palette : %1") .arg(QString::fromStdString(m_palettePath.getLevelName())); } int getHistoryType() override { return HistoryType::Palette; } }; //============================================================================= // DeleteFolderUndo class DeleteFolderUndo final : public TUndo { TFilePath m_path; TFilePathSet m_pathSet; QList m_paletteList; public: DeleteFolderUndo(const TFilePath &path, const TFilePathSet &pathSet) : m_path(path), m_pathSet(pathSet), m_paletteList() { for (TFilePathSet::const_iterator it = m_pathSet.begin(); it != m_pathSet.end(); it++) { TFilePath path = *it; if (path.getType() == "tpl") m_paletteList.push_back(StudioPalette::instance()->getPalette(path)); } } void undo() const override { StudioPalette::instance()->createFolder(m_path.getParentDir(), m_path.getWideName()); int paletteCount = -1; for (TFilePathSet::const_iterator it = m_pathSet.begin(); it != m_pathSet.end(); it++) { TFilePath path = *it; if (path.getType() == "tpl") // Is a palette StudioPalette::instance()->setPalette( path, m_paletteList.at(++paletteCount)->clone(), true); else // Is a folder StudioPalette::instance()->createFolder(path.getParentDir(), path.getWideName()); } } void redo() const override { StudioPalette::instance()->deleteFolder(m_path); } int getSize() const override { return sizeof(*this) + sizeof(TPalette); } QString getHistoryString() override { return QObject::tr("Delete Studio Palette Folder : %1") .arg(QString::fromStdString(m_path.getName())); } int getHistoryType() override { return HistoryType::Palette; } }; //============================================================================= // CreateFolderUndo class CreateFolderUndo final : public TUndo { TFilePath m_folderPath; public: CreateFolderUndo(const TFilePath &folderPath) : m_folderPath(folderPath) {} void undo() const override { StudioPalette::instance()->deleteFolder(m_folderPath); } void redo() const override { StudioPalette::instance()->createFolder(m_folderPath.getParentDir(), m_folderPath.getWideName()); } int getSize() const override { return sizeof(*this) + sizeof(TPalette); } QString getHistoryString() override { return QObject::tr("Create Studio Palette Folder : %1") .arg(QString::fromStdString(m_folderPath.getName())); } int getHistoryType() override { return HistoryType::Palette; } }; //============================================================================= // MovePaletteUndo class MovePaletteUndo final : public TUndo { TFilePath m_dstPath, m_srcPath; public: MovePaletteUndo(const TFilePath &dstPath, const TFilePath &srcPath) : m_dstPath(dstPath), m_srcPath(srcPath) {} void undo() const override { StudioPalette::instance()->movePalette(m_srcPath, m_dstPath); } void redo() const override { StudioPalette::instance()->movePalette(m_dstPath, m_srcPath); } int getSize() const override { return sizeof(*this); } QString getHistoryString() override { return QObject::tr("Move Studio Palette Folder : %1 : %2 > %3") .arg(QString::fromStdString(m_srcPath.getName())) .arg(QString::fromStdString(m_srcPath.getParentDir().getName())) .arg(QString::fromStdString(m_dstPath.getParentDir().getName())); } int getHistoryType() override { return HistoryType::Palette; } }; //------------------------------------------------------------- void adaptLevelToPalette(TXshLevelHandle *currentLevelHandle, TPaletteHandle *paletteHandle, TPalette *plt, int tolerance, bool noUndo); class AdjustIntoCurrentPaletteUndo final : public TUndo { TXshLevelHandle *m_currentLevelHandle; TPaletteHandle *m_paletteHandle; TPaletteP m_oldPalette, m_newPalette; TFrameId m_fid; std::string m_oldImageId; static int m_idCount; int m_undoSize; int m_tolerance; public: AdjustIntoCurrentPaletteUndo(TXshLevelHandle *currentLevelHandle, TPaletteHandle *paletteHandle, const TFrameId &fid, const TImageP &oldImage, const TPaletteP &oldPalette, const TPaletteP &newPalette, int tolerance) : TUndo() , m_currentLevelHandle(currentLevelHandle) , m_paletteHandle(paletteHandle) , m_fid(fid) , m_oldPalette(oldPalette) , m_newPalette(newPalette) , m_tolerance(tolerance) { m_oldImageId = "AdjustIntoCurrentPaletteUndo_oldImage_" + std::to_string(m_idCount++); TImageCache::instance()->add(m_oldImageId, (const TImageP)oldImage); m_undoSize = ((TToonzImageP)oldImage)->getRaster()->getLx() * ((TToonzImageP)oldImage)->getRaster()->getLy() * ((TToonzImageP)oldImage)->getRaster()->getPixelSize(); } ~AdjustIntoCurrentPaletteUndo() { TImageCache::instance()->remove(m_oldImageId); } void undo() const override { TImageP img = TImageCache::instance()->get(m_oldImageId, true); TXshSimpleLevel *level = m_currentLevelHandle->getSimpleLevel(); level->setPalette(m_oldPalette.getPointer()); // img->setPalette(m_oldPalette.getPointer()); level->setFrame(m_fid, img->cloneImage()); level->touchFrame(m_fid); if (level->getFirstFid() == m_fid) { m_currentLevelHandle->notifyLevelChange(); m_paletteHandle->setPalette(m_oldPalette.getPointer()); m_oldPalette->setDirtyFlag(true); m_paletteHandle->notifyPaletteChanged(); } } void redo() const override { adaptLevelToPalette(m_currentLevelHandle, m_paletteHandle, m_newPalette.getPointer(), m_tolerance, true); } int getSize() const override { return m_undoSize; } }; //----------------------------------------------------------------------------- int AdjustIntoCurrentPaletteUndo::m_idCount = 0; //----------------------------------------------------------------------------- } // namespace //============================================================================= void StudioPaletteCmd::loadIntoCurrentPalette(TPaletteHandle *paletteHandle, const TFilePath &fp) { TPalette *palette = StudioPalette::instance()->getPalette(fp, false); if (!palette) return; loadIntoCurrentPalette(paletteHandle, palette); } //----------------------------------------------------------------------------- namespace { std::map ToleranceMap; int findClosest(const TPixel &color, std::map &colorMap, int tolerance) { std::map::const_iterator it; it = ToleranceMap.find(color); if (it != ToleranceMap.end()) return it->second; tolerance *= tolerance; it = colorMap.begin(); int index = -1, minDist = 99999999; int dr, dg, db, dm; for (; it != colorMap.end(); ++it) { if ((dr = (color.r - it->first.r) * (color.r - it->first.r)) > tolerance) continue; if ((dg = (color.g - it->first.g) * (color.g - it->first.g)) > tolerance) continue; if ((db = (color.b - it->first.b) * (color.b - it->first.b)) > tolerance) continue; if ((dm = (color.m - it->first.m) * (color.m - it->first.m)) > tolerance) continue; int currDist = dr + dg + db + dm; if (currDist < minDist) minDist = currDist, index = it->second; } ToleranceMap[color] = index; return index; } //-------------------------------------------------------------------------------------- int getIndex(const TPixel &color, std::map &colorMap, TPalette *plt, int tolerance) { std::map::const_iterator it; it = colorMap.find(color); if (it != colorMap.end()) return it->second; if (tolerance > 0) { int index = findClosest(color, colorMap, tolerance); if (index != -1) return index; } int index = plt->addStyle(color); plt->getPage(0)->addStyle(index); colorMap[color] = index; return index; } //------------------------------------------- void adaptIndexes(TToonzImageP timg, std::map &colorMap, TPalette *plt, int tolerance) { TPalette *origPlt = timg->getPalette(); TRasterCM32P r = timg->getRaster(); TPixelCM32 oldPrev = TPixelCM32(), newPrev = TPixelCM32(); for (int i = 0; i < r->getLy(); i++) { TPixelCM32 *pix = r->pixels(i); for (int j = 0; j < r->getLx(); j++, pix++) { if (*pix == TPixelCM32()) continue; if (*pix == oldPrev) { *pix = newPrev; continue; } oldPrev = *pix; int ink = pix->getInk(); int paint = pix->getPaint(); if (ink > 0) { TPixel color = origPlt->getStyle(ink)->getMainColor(); int newIndex = getIndex(color, colorMap, plt, tolerance); pix->setInk(newIndex); } if (paint > 0) { TPixel color = origPlt->getStyle(paint)->getMainColor(); int newIndex = getIndex(color, colorMap, plt, tolerance); pix->setPaint(newIndex); } newPrev = *pix; } } } //----------------------------------------------- void adaptLevelToPalette(TXshLevelHandle *currentLevelHandle, TPaletteHandle *paletteHandle, TPalette *plt, int tolerance, bool noUndo) { TXshSimpleLevel *sl = currentLevelHandle->getSimpleLevel(); QApplication::setOverrideCursor(Qt::WaitCursor); TPalette *oldPalette = sl->getPalette(); ToleranceMap.clear(); std::map colorMap; for (int i = 0; i < plt->getStyleCount(); i++) { if (!plt->getStylePage(i)) continue; colorMap[plt->getStyle(i)->getMainColor()] = i; } std::vector fids; sl->getFids(fids); for (int i = 0; i < (int)fids.size(); i++) { TToonzImageP timg = (TToonzImageP)sl->getFrame(fids[i], true); if (!timg) continue; if (!noUndo) TUndoManager::manager()->add(new AdjustIntoCurrentPaletteUndo( currentLevelHandle, paletteHandle, fids[i], timg->cloneImage(), oldPalette->clone(), plt->clone(), tolerance)); adaptIndexes(timg, colorMap, plt, tolerance); timg->setPalette(plt); } QApplication::restoreOverrideCursor(); currentLevelHandle->getSimpleLevel()->setPalette(plt); paletteHandle->setPalette(plt); plt->setDirtyFlag(true); paletteHandle->notifyPaletteChanged(); currentLevelHandle->notifyLevelChange(); } } // namespaxce //----------------------------------------------------------------------------- void StudioPaletteCmd::loadIntoCurrentPalette( TPaletteHandle *paletteHandle, TPalette *plt, TXshLevelHandle *currentLevelHandle, int tolerance) { adaptLevelToPalette(currentLevelHandle, paletteHandle, plt, tolerance, false); } //--------------------------------------------------------------------------------------- void StudioPaletteCmd::loadIntoCurrentPalette(TPaletteHandle *paletteHandle, TPalette *palette) { assert(paletteHandle); TPalette *current = paletteHandle->getPalette(); if (!current) return; int styleId = paletteHandle->getStyleIndex(); TPalette *old = current->clone(); while (palette->getStyleCount() < current->getStyleCount()) { int index = palette->getStyleCount(); assert(index < current->getStyleCount()); TColorStyle *style = current->getStyle(index)->clone(); palette->addStyle(style); } // keep the color model path unchanged TFilePath oldRefImagePath = current->getRefImgPath(); std::wstring oldGlobalName = current->getGlobalName(); current->assign(palette, true); // keep global name unchanged current->setGlobalName(oldGlobalName); current->setDirtyFlag(true); current->setRefImgPath(oldRefImagePath); if (paletteHandle->getPalette() == current && styleId >= current->getStyleCount()) paletteHandle->setStyleIndex(1); TUndoManager::manager()->add( new PaletteAssignUndo(current, old, current->clone(), paletteHandle)); palette->setDirtyFlag(true); paletteHandle->notifyPaletteChanged(); } //----------------------------------------------------------------------------- void StudioPaletteCmd::mergeIntoCurrentPalette(TPaletteHandle *paletteHandle, const TFilePath &fp) { TPalette *palette = StudioPalette::instance()->getPalette(fp); if (!palette) return; mergeIntoCurrentPalette(paletteHandle, palette); } //----------------------------------------------------------------------------- void StudioPaletteCmd::mergeIntoCurrentPalette(TPaletteHandle *paletteHandle, TPalette *palette) { assert(paletteHandle); TPalette *current = paletteHandle->getPalette(); if (!current) return; if (current->isLocked()) return; TPalette *old = current->clone(); current->merge(palette, true); TUndoManager::manager()->add( new PaletteAssignUndo(current, old, current->clone(), paletteHandle)); palette->setDirtyFlag(true); paletteHandle->notifyPaletteChanged(); } //----------------------------------------------------------------------------- void StudioPaletteCmd::replaceWithCurrentPalette( TPaletteHandle *paletteHandle, TPaletteHandle *stdPaletteHandle, const TFilePath &fp) { StudioPalette *sp = StudioPalette::instance(); TPalette *palette = sp->getPalette(fp); if (!palette || palette->isLocked()) return; assert(paletteHandle); TPalette *current = paletteHandle->getPalette(); if (!current) return; // keep the studio palette's global name unchanged std::wstring oldGlobalName = palette->getGlobalName(); TPalette *old = palette->clone(); palette->assign(current); // put back the global name palette->setGlobalName(oldGlobalName); sp->setPalette(fp, current, true); TUndoManager::manager()->add( new StudioPaletteAssignUndo(fp, old, current->clone(), paletteHandle)); // Cambio la studioPalette correntette(palette); stdPaletteHandle->setPalette(palette); stdPaletteHandle->notifyPaletteSwitched(); } //----------------------------------------------------------------------------- void StudioPaletteCmd::updateAllLinkedStyles(TPaletteHandle *paletteHandle, TXsheetHandle *xsheetHandle) { if (!xsheetHandle) return; TXsheet *xsheet = xsheetHandle->getXsheet(); if (!xsheet) return; ToonzScene *scene = xsheet->getScene(); if (!scene) return; // emit signal only if something changed bool somethingChanged = false; StudioPalette *sp = StudioPalette::instance(); TLevelSet *levelSet = scene->getLevelSet(); for (int i = 0; i < levelSet->getLevelCount(); i++) { TXshLevel *xl = levelSet->getLevel(i); TXshSimpleLevel *sl = xl ? xl->getSimpleLevel() : 0; if (!sl) continue; TPalette *palette = sl->getPalette(); if (palette) { somethingChanged = somethingChanged | sp->updateLinkedColors(palette); if (sl->getType() == TZP_XSHLEVEL) { std::vector fids; sl->getFids(fids); std::vector::iterator it; for (it = fids.begin(); it != fids.end(); ++it) { TFrameId fid = *it; std::string id = sl->getImageId(fid); } } } } if (!paletteHandle || !paletteHandle->getPalette()) return; if (somethingChanged) paletteHandle->notifyColorStyleChanged(); } //----------------------------------------------------------------------------- /*! Delete palette identified by \b fp in \b StudioPalette. If there are any problems in loading send an error message. */ void StudioPaletteCmd::deletePalette(const TFilePath &fp) { TUndo *undo = new DeletePaletteUndo(fp); StudioPalette::instance()->deletePalette(fp); TUndoManager::manager()->add(undo); } //----------------------------------------------------------------------------- /*! Move palette from TFilePath \b srcPath to \b TFilePath \b dstPath. If there are any problems in moving send an error message. */ void StudioPaletteCmd::movePalette(const TFilePath &dstPath, const TFilePath &srcPath) { TSystem::touchParentDir(dstPath); StudioPalette::instance()->movePalette(dstPath, srcPath); TUndoManager::manager()->add(new MovePaletteUndo(dstPath, srcPath)); } //----------------------------------------------------------------------------- /*! Create palette \b palette in folder \b folderName with name \b paletteName. If there are any problems send an error message. */ TFilePath StudioPaletteCmd::createPalette(const TFilePath &folderName, std::string paletteName, const TPalette *palette) { TFilePath palettePath; TFileStatus status(folderName); if (!status.isDirectory()) throw TException("Select a folder."); if (!status.doesExist()) { TSystem::mkDir(folderName); FolderListenerManager::instance()->notifyFolderChanged( folderName.getParentDir()); } palettePath = StudioPalette::instance()->createPalette(folderName, paletteName); if (palette) StudioPalette::instance()->setPalette(palettePath, palette, true); TUndoManager::manager()->add(new CreatePaletteUndo(palettePath)); return palettePath; } //----------------------------------------------------------------------------- /*! Add a folder in StudioPalette TFilePath \b parentFolderPath. If there are any problems send an error message. */ TFilePath StudioPaletteCmd::addFolder(const TFilePath &parentFolderPath) { TFilePath folderPath; folderPath = StudioPalette::instance()->createFolder(parentFolderPath); if (!folderPath.isEmpty()) TUndoManager::manager()->add(new CreateFolderUndo(folderPath)); return folderPath; } //----------------------------------------------------------------------------- /*! Delete folder \b folderPath. If there are any problems in deleting send an error message. */ void StudioPaletteCmd::deleteFolder(const TFilePath &folderPath) { TFilePathSet folderPathSet; TSystem::readDirectoryTree(folderPathSet, folderPath, true, false); TUndo *undo = new DeleteFolderUndo(folderPath, folderPathSet); StudioPalette::instance()->deleteFolder(folderPath); TUndoManager::manager()->add(undo); } //----------------------------------------------------------------------------- void StudioPaletteCmd::scanPalettes(const TFilePath &folder, const TFilePath &sourcePath) { // error("uh oh"); }