#include "tapp.h" #include "tpalette.h" #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 "tw/action.h" #include "tropcm.h" #include "ttoonzimage.h" #include "matchline.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" #include "toonzqt/icongenerator.h" #include #include #include #include #include "tundo.h" #include "tools/toolutils.h" #include "timagecache.h" #include "tcolorstyles.h" #include "toonz/levelproperties.h" #include "toonz/childstack.h" #include "toonz/toonzimageutils.h" #include "tpaletteutil.h" #include using namespace DVGui; 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 mergeCmapped(const std::vector &matchingLevels) { 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 usedColors; 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 merge only cmapped raster images!"); std::set usedStyles; ToonzImageUtils::getUsedStyles(usedStyles, match); std::map indexTable; mergePalette(palette, indexTable, matchPalette, usedStyles); ToonzImageUtils::scrambleStyles(match, indexTable); match->setPalette(palette); matchPalette = palette; // img->lock(); TRasterCM32P ras = img->getRaster(); // img->getCMapped(false); TRasterCM32P matchRas = match->getRaster(); // match->getCMapped(true); if (!ras || !matchRas) throw TRopException("Can merge only 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) { // PRINTF("TAGLIO L'IMMAGINE\n"); 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::overlayCmapped(r, matchRas, palette, matchPalette, usedColors); ras->unlock(); matchRas->unlock(); img->setSavebox(img->getSavebox() + (matchRas->getBounds() + offs)); } /* std::map::iterator it = usedColors.begin(); for (; it != usedColors.end(); ++it) if (it->first != it->second) break; if (it == usedColors.end()) // this means that the merged palette does not // differ from source palette.(all usedColors // are not new color ) return; std::wstring pageName = L"merged palettes"; 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); it = usedColors.begin(); int count = 0; for (; it != usedColors.end(); ++it) { if (it->first == it->second) continue; while (palette->getStyleCount() <= it->second) palette->addStyle(TPixel32::Red); palette->setStyle(it->second, matchPalette->getStyle(it->first)->clone()); page->addStyle(it->second); } if (usedColors.size() > 0) palette->setDirtyFlag(true); */ } /*------------------------------------------------------------------------*/ void applyDeleteMatchline(TXshSimpleLevel *sl, const std::vector &fids, const std::vector &_inkIndexes) { TPalette::Page *page = 0; int i, j, pageIndex = 0; std::vector 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++) { // level[i]->lock(); 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); // level[i]->unlock(); } // if (page) // { // while (page->getStyleCount()) // page->removeStyle(0); // palette->erasePage(pageIndex); // } } /*------------------------------------------------------------------------*/ } // namespace //----------------------------------------------------------------------------- class DeleteMatchlineUndo final : public TUndo { public: TXshLevel *m_xl; TXshSimpleLevel *m_sl; std::vector m_fids; std::vector m_indexes; TPaletteP m_matchlinePalette; DeleteMatchlineUndo( TXshLevel *xl, TXshSimpleLevel *sl, const std::vector &fids, const std::vector &indexes) //, TPalette*matchPalette) : TUndo(), m_xl(xl), m_sl(sl), m_fids(fids), m_indexes(indexes) //, m_matchlinePalette(matchPalette->clone()) { // assert(matchPalette); 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; // TPalette *palette = m_matchlinePalette->clone(); // m_sl->setPalette(palette); 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]); } // TApp::instance()->getPaletteController()->getCurrentLevelPalette()->setPalette(palette); if (m_xl) invalidateIcons(m_xl, m_fids); m_sl->setDirtyFlag(true); TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); } void redo() const override { int i; // for (i=0; igetFrame(m_fids[i], true)); 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)); } }; //----------------------------------------------------------------------------- /* namespace { class DeleteLevelUndo final : public TUndo { TXshLevelP m_xl; public: DeleteLevelUndo(TXshLevel *xl) : m_xl(xl) {} void undo() const { ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); scene->getLevelSet()->insertLevel(m_xl.getPointer()); TApp::instance()->getCurrentScene()->notifyCastChange(); } void redo() const { ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene(); scene->getLevelSet()->removeLevel(m_xl.getPointer()); TApp::instance()->getCurrentScene()->notifyCastChange(); } int getSize() const { return sizeof *this + 100; } }; } //namespace */ static bool removeLevel(TXshLevel *level) { TApp *app = TApp::instance(); ToonzScene *scene = app->getCurrentScene()->getScene(); // if(scene->getChildStack()->getTopXsheet()->isLevelUsed(level)) // DVGui::error(QObject::tr("It is not possible to delete the used level //%1.").arg(QString::fromStdWString(level->getName())));//"E_CantDeleteUsedLevel_%1" // else { // TUndoManager *um = TUndoManager::manager(); // um->add(new DeleteLevelUndo(level)); scene->getLevelSet()->removeLevel(level, false); TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); TApp::instance()->getCurrentScene()->notifyCastChange(); } return true; } class MergeCmappedUndo final : public TUndo { TXshLevel *m_xl; int m_mergeCmappedSessionId; std::map m_images; TXshSimpleLevel *m_level; TPalette *m_palette; int m_column, m_mColumn; std::wstring m_fullpath; public: MergeCmappedUndo(TXshLevel *xl, int mergeCmappedSessionId, int column, TXshSimpleLevel *level, const std::map &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_images(images) { m_fullpath = m_xl->getPath().getWideString(); } void undo() const override { std::map::const_iterator it = m_images.begin(); TPalette *palette = m_palette->clone(); m_level->setPalette(palette); std::vector fids; for (; it != m_images.end(); ++it) //, ++mit) { QString id = "MergeCmappedUndo" + QString::number(m_mergeCmappedSessionId) + "-" + QString::number(it->first.getNumber()); TImageP img = TImageCache::instance()->get(id, false)->cloneImage(); img->setPalette(palette); m_level->setFrame(it->first, img); fids.push_back(it->first); } removeLevel(m_xl); TApp::instance() ->getPaletteController() ->getCurrentLevelPalette() ->setPalette(palette); m_level->setDirtyFlag(true); TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); } void redo() const override { mergeCmapped(m_column, m_mColumn, QString::fromStdWString(m_fullpath), true); } int getSize() const override { return sizeof(*this); } ~MergeCmappedUndo() { std::map::const_iterator it = m_images.begin(); for (; it != m_images.end(); ++it) //, ++mit) { QString id = "MergeCmappedUndo" + QString::number((uintptr_t) this) + "-" + QString::number(it->first.getNumber()); TImageCache::instance()->remove(id); } delete m_palette; } }; //----------------------------------------------------------------------------- static int LastMatchlineIndex = -1; class MergedPair { public: TFrameId m_f0, m_f1; TAffine m_aff; MergedPair(const TFrameId &f0, const TFrameId &f1, const TAffine &aff) : m_f0(f0), m_f1(f1), m_aff(aff) {} bool operator<(const MergedPair &p) const { if (m_f0 != p.m_f0) return m_f0 < p.m_f0; if (m_f1 != p.m_f1) return m_f1 < p.m_f1; if (m_aff.a11 != p.m_aff.a11) return m_aff.a11 < p.m_aff.a11; if (m_aff.a12 != p.m_aff.a12) return m_aff.a12 < p.m_aff.a12; if (m_aff.a13 != p.m_aff.a13) return m_aff.a13 < p.m_aff.a13; if (m_aff.a21 != p.m_aff.a21) return m_aff.a21 < p.m_aff.a21; if (m_aff.a22 != p.m_aff.a22) return m_aff.a22 < p.m_aff.a22; if (m_aff.a23 != p.m_aff.a23) return m_aff.a23 < p.m_aff.a23; return false; } }; //-------------------------------------------------------------------- void mergeCmapped(int column, int mColumn, const QString &fullpath, bool isRedo) { static int MergeCmappedSessionId = 0; MergeCmappedSessionId++; TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); int start, end; int mStart, mEnd; xsh->getCellRange(column, start, end); xsh->getCellRange(mColumn, mStart, mEnd); if (start > end) return; std::vector cell(std::max(end, mEnd) - std::min(start, mStart) + 1); std::vector mCell(cell.size()); xsh->getCells(std::min(start, mStart), column, cell.size(), &(cell[0])); if (mColumn != -1) xsh->getCells(std::min(start, mStart), mColumn, cell.size(), &(mCell[0])); TXshColumn *col = xsh->getColumn(column); TXshColumn *mcol = xsh->getColumn(mColumn); std::vector matchingLevels; std::map computedMergedMap; TXshSimpleLevel *level = 0, *mLevel = 0; TXshLevel *xl; std::map images; double dpix = 0, dpiy = 0; for (int i = 0; i < (int)cell.size(); i++) { if (!cell[i].isEmpty() && dpix == 0) ((TToonzImageP)(cell[i].getImage(false)))->getDpi(dpix, dpiy); if (!level) { level = cell[i].getSimpleLevel(); xl = cell[i].m_level.getPointer(); } if (!mLevel) mLevel = mCell[i].getSimpleLevel(); } if (!level || !mLevel) return; TFilePath fp(fullpath.toStdString()); TXshLevel *txl = level->getScene()->createNewLevel( level->getType(), fp.getWideName(), level->getResolution()); TXshSimpleLevel *newLevel = txl->getSimpleLevel(); newLevel->setPath(fp); newLevel->setPalette(level->getPalette()); newLevel->clonePropertiesFrom(level); // newLevel->setPath(fp); TApp::instance()->getCurrentScene()->notifySceneChanged(); TApp::instance()->getCurrentScene()->notifyCastChange(); TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); int count = 0; for (int i = 0; i < (int)cell.size(); i++) { if (cell[i].isEmpty() && mCell[i].isEmpty()) continue; TAffine imgAff, matchAff; getColumnPlacement(imgAff, xsh, std::min(start, mStart) + i, column, false); getColumnPlacement(matchAff, xsh, std::min(start, mStart) + i, mColumn, false); // std::map::iterator it; MergedPair mp(cell[i].isEmpty() ? TFrameId() : cell[i].getFrameId(), mCell[i].isEmpty() ? TFrameId() : mCell[i].getFrameId(), imgAff.inv() * matchAff); std::map::iterator computedMergedIt = computedMergedMap.find(mp); if (computedMergedIt != computedMergedMap.end()) { TXshCell newCell(newLevel, computedMergedIt->second); xsh->setCell(i, column, newCell); cell[i] = newCell; continue; } TFrameId newFid(++count); // level->getLastFid().getNumber()+1); TDimension dim = level->getResolution(); TToonzImageP newImage; if (cell[i].isEmpty()) { newImage = TToonzImageP(TRasterCM32P(dim), TRect(0, 0, dim.lx - 1, dim.ly - 1)); newImage->setDpi(dpix, dpiy); } else newImage = (TToonzImageP)(cell[i].getImage(false)->cloneImage()); newImage->setPalette(level->getPalette()); newLevel->setFrame(newFid, newImage); TXshCell newCell(newLevel, newFid); xsh->setCell(i, column, newCell); computedMergedMap[mp] = newCell.getFrameId(); cell[i] = newCell; 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; TToonzImageP timg = (TToonzImageP)img; TToonzImageP tmatch = (TToonzImageP)match; QString id = "MergeCmappedUndo" + QString::number(MergeCmappedSessionId) + "-" + QString::number(fid.getNumber()); TImageCache::instance()->add(id, timg->clone()); images[fid] = id; TAffine dpiAff = getDpiAffine(level, fid); TAffine mdpiAff = getDpiAffine(mLevel, mFid); matchingLevels.push_back(MergeCmappedPair(cell[i], imgAff * dpiAff, mCell[i], matchAff * mdpiAff)); } if (!isRedo) { TPalette *plt = level->getPalette(); TPaletteHandle *pltHandle = new TPaletteHandle(); pltHandle->setPalette(plt); int styleCount = plt->getStyleCount(); TUndoManager::manager()->add(new MergeCmappedUndo( txl, MergeCmappedSessionId, column, level, images, mColumn, plt)); } removeLevel(xl); QApplication::setOverrideCursor(Qt::WaitCursor); mergeCmapped(matchingLevels); 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); } newLevel->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 &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 fids) { if (fids.empty()) return ""; QString str; std::set::const_iterator it = fids.begin(); str = QString::number(it->getNumber()); while (it != fids.end()) { std::set::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; } //----------------------------------------------------------------------------- std::vector string2Indexes(const QString &values) { std::vector 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 from = vals1.at(0).toInt(&ok); if (!ok) return std::vector(); int to = vals1.at(1).toInt(&ok); if (!ok) return std::vector(); 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(); ret.push_back(val); } } std::sort(ret.begin(), ret.end()); return ret; } } // namespace //-----------------------------------------------------------------------------