From 2975e1912407c089c871e864a3a9f2067da033e9 Mon Sep 17 00:00:00 2001 From: justburner Date: Dec 28 2022 03:19:10 +0000 Subject: Paste external clipboard images T2D port with some changes Co-Authored-By: jeremybullock <79284068+jeremybullock@users.noreply.github.com> --- diff --git a/toonz/sources/include/tools/rasterselection.h b/toonz/sources/include/tools/rasterselection.h index 113b7e8..d015969 100644 --- a/toonz/sources/include/tools/rasterselection.h +++ b/toonz/sources/include/tools/rasterselection.h @@ -56,7 +56,7 @@ class DVAPI RasterSelection final : public TSelection { bool m_noAntialiasing; private: - void pasteSelection(const RasterImageData *data); + bool pasteSelection(const RasterImageData *data); public: RasterSelection(); diff --git a/toonz/sources/tnztools/rasterselection.cpp b/toonz/sources/tnztools/rasterselection.cpp index 4003a3e..a9502d7 100644 --- a/toonz/sources/tnztools/rasterselection.cpp +++ b/toonz/sources/tnztools/rasterselection.cpp @@ -28,6 +28,7 @@ #include "toonz/tframehandle.h" #include "toonz/txsheethandle.h" #include "toonz/tstageobject.h" +#include "toonzqt/gutil.h" #include #include @@ -1138,7 +1139,7 @@ void RasterSelection::cutSelection() { //----------------------------------------------------------------------------- -void RasterSelection::pasteSelection(const RasterImageData *riData) { +bool RasterSelection::pasteSelection(const RasterImageData *riData) { std::vector rect; double currentDpiX, currentDpiY; double dpiX, dpiY; @@ -1152,18 +1153,18 @@ void RasterSelection::pasteSelection(const RasterImageData *riData) { if (fullColorData) { DVGui::error(QObject::tr( "The copied selection cannot be pasted in the current drawing.")); - return; + return false; } riData->getData(cmRas, dpiX, dpiY, rect, m_strokes, m_originalStrokes, m_affine, m_currentImage->getPalette()); - if (!cmRas) return; + if (!cmRas) return false; m_floatingSelection = cmRas; } else if (TRasterImageP ri = (TRasterImageP)m_currentImage) { ri->getDpi(currentDpiX, currentDpiY); TRasterP ras; riData->getData(ras, dpiX, dpiY, rect, m_strokes, m_originalStrokes, m_affine, ri->getPalette()); - if (!ras) return; + if (!ras) return false; if (TRasterCM32P rasCM = ras) { TDimension dim = rasCM->getSize(); TRaster32P app = TRaster32P(dim.lx, dim.ly); @@ -1178,6 +1179,7 @@ void RasterSelection::pasteSelection(const RasterImageData *riData) { if (dpiX != 0 && dpiY != 0 && currentDpiX != 0 && currentDpiY != 0) sc = TScale(currentDpiX / dpiX, currentDpiY / dpiY); m_affine = m_affine * sc; + return true; } //----------------------------------------------------------------------------- @@ -1195,6 +1197,10 @@ void RasterSelection::pasteSelection() { return; } + TXshLevel *xl = app->getCurrentLevel()->getLevel(); + TXshSimpleLevel *sl = xl ? xl->getSimpleLevel() : nullptr; + int levelType = sl ? sl->getType() : NO_XSHLEVEL; + m_currentImage = image; m_fid = tool->getCurrentFid(); @@ -1203,11 +1209,26 @@ void RasterSelection::pasteSelection() { dynamic_cast(clipboard->mimeData()); const StrokesData *stData = dynamic_cast(clipboard->mimeData()); - if (!riData && !stData) return; + QImage clipImage = clipboard->image(); + if (!riData && !stData && clipImage.height() == 0) return; if (isFloating()) pasteFloatingSelection(); selectNone(); m_isPastedSelection = true; - m_oldPalette = m_currentImage->getPalette()->clone(); + if (!m_currentImageCell.getSimpleLevel()) { + const TXshCell &imageCell = TTool::getImageCell(); + + TImageP image = + imageCell.getImage(false, 1); // => See the onImageChanged() warning ! + + TToonzImageP ti = (TToonzImageP)image; + TRasterImageP ri = (TRasterImageP)image; + if (!ti && !ri) return; + + makeCurrent(); + setCurrentImage(image, imageCell); + } + if (m_currentImage->getPalette()) + m_oldPalette = m_currentImage->getPalette()->clone(); if (stData) { if (TToonzImageP ti = m_currentImage) riData = stData->toToonzImageData(ti); @@ -1227,8 +1248,48 @@ void RasterSelection::pasteSelection() { } } + if (clipImage.height() > 0 && (levelType == OVL_XSHLEVEL || + m_currentImage->getType() == OVL_XSHLEVEL)) { + // An image was pasted from outside OpenToonz + + // Set up variables + std::vector rects; + const std::vector strokes; + const std::vector originalStrokes; + TRasterImageP ri = m_currentImage; + TAffine aff; + TRasterP ras = rasterFromQImage(clipImage); + // center the image in the viewer + rects.push_back(TRectD(0.0 - clipImage.width() / 2, + 0.0 - clipImage.height() / 2, clipImage.width() / 2, + clipImage.height() / 2)); + + TRectD r = + TRectD(0.0 - (clipImage.width() / 2), 0.0 - (clipImage.height() / 2), + clipImage.width() / 2, clipImage.height() / 2); + TRect box = getRaster(m_currentImage)->getBounds(); + r *= convertRasterToWorld(box, m_currentImage); + if (!r.isEmpty()) { + TStroke stroke = getStrokeByRect(r); + if ((int)stroke.getControlPointCount() == 0) return; + m_strokes.push_back(stroke); + m_originalStrokes.push_back(stroke); + } + // pack up the data to send to the next pasteSelection + FullColorImageData *qimageData = new FullColorImageData(); + + qimageData->setData(ras, ri->getPalette(), 120.0, 120.0, + ri->getRaster()->getSize(), rects, m_strokes, + m_originalStrokes, aff); + setSelectionBbox(TRectD(0.0 - clipImage.width() / 2, + 0.0 - clipImage.height() / 2, clipImage.width() / 2, + clipImage.height() / 2)); + + riData = qimageData; + } + if (!riData) return; - pasteSelection(riData); + if (!pasteSelection(riData)) return; app->getPaletteController()->getCurrentLevelPalette()->notifyPaletteChanged(); notify(); diff --git a/toonz/sources/tnztools/toolutils.cpp b/toonz/sources/tnztools/toolutils.cpp index 45ebc40..fa7457f 100644 --- a/toonz/sources/tnztools/toolutils.cpp +++ b/toonz/sources/tnztools/toolutils.cpp @@ -533,13 +533,16 @@ void ToolUtils::TToolUndo::removeLevelAndFrameIfNeeded() const { m_level->eraseFrame(m_frameId); if (!m_isEditingLevel) { TXsheet *xsh = app->getCurrentXsheet()->getXsheet(); + TXshCell cell; for (const TTool::CellOps &cellOps : m_cellsData) { - TXshCell cell; if (cellOps.type == TTool::CellOps::ExistingToNew) cell = xsh->getCell(cellOps.r0 - 1, m_col); for (int r = cellOps.r0; r <= cellOps.r1; r++) xsh->setCell(r, m_col, cell); } + if (m_cellsData.size() < 1) { + xsh->setCell(m_row, m_col, cell); + } } if (m_createdLevel) { // butta il livello @@ -666,6 +669,9 @@ void ToolUtils::TRasterUndo::undo() const { removeLevelAndFrameIfNeeded(); + if (m_level) { + m_level->setDirtyFlag(true); + } app->getCurrentXsheet()->notifyXsheetChanged(); notifyImageChanged(); } diff --git a/toonz/sources/toonz/cellselection.cpp b/toonz/sources/toonz/cellselection.cpp index 64dc2f5..ccd4114 100644 --- a/toonz/sources/toonz/cellselection.cpp +++ b/toonz/sources/toonz/cellselection.cpp @@ -54,6 +54,8 @@ #include "toonz/tstageobjecttree.h" #include "toonz/stage.h" #include "vectorizerpopup.h" +#include "tools/rasterselection.h" +#include "tools/strokeselection.h" #include "toonz/sceneproperties.h" #include "toutputproperties.h" @@ -673,6 +675,16 @@ bool pasteRasterImageInCellWithoutUndo(int row, int col, } } if (img) { + // This seems redundant, but newly created levels were having an issue with + // pasting. + // Don't know why. + cell = xsh->getCell(row, col); + sl = cell.getSimpleLevel(); + fid = cell.getFrameId(); + img = cell.getImage(true); + if (!img->getPalette()) { + img->setPalette(sl->getPalette()); + } TRasterP ras; TRasterP outRas; double imgDpiX, imgDpiY; @@ -788,10 +800,12 @@ public: TTileSetFullColor *tiles, TXshSimpleLevel *level, const TFrameId &id, TPaletteP oldPalette, bool createdFrame, - bool isLevelCreated) + bool isLevelCreated, int col = -1) : ToolUtils::TFullColorRasterUndo(tiles, level, id, createdFrame, isLevelCreated, oldPalette) - , m_rasterImageData(data->clone()) {} + , m_rasterImageData(data->clone()) { + if (col > 0) m_col = col; + } ~PasteFullColorImageInCellsUndo() { delete m_rasterImageData; } @@ -801,6 +815,7 @@ public: bool isLevelCreated; pasteRasterImageInCellWithoutUndo(m_row, m_col, m_rasterImageData, &tiles, isLevelCreated); + m_level->setDirtyFlag(true); if (tiles) delete tiles; TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); } @@ -1643,24 +1658,38 @@ static void pasteStrokesInCell(int row, int col, //----------------------------------------------------------------------------- static void pasteRasterImageInCell(int row, int col, - const RasterImageData *rasterImageData) { + const RasterImageData *rasterImageData, + bool newLevel = false) { // to let the undo to know which frame is edited TTool::m_cellsData.clear(); TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); - TXshCell cell = xsh->getCell(row, col); bool createdFrame = false; bool isLevelCreated = false; TPaletteP oldPalette = 0; - if (!cell.getSimpleLevel()) { - createdFrame = true; - TXshSimpleLevel *sl = xsh->getCell(row - 1, col).getSimpleLevel(); - if (sl) oldPalette = sl->getPalette(); - } else { - TXshSimpleLevel *sl = cell.getSimpleLevel(); - if (sl->getType() == OVL_XSHLEVEL && sl->getPath().isUneditable()) - return; - oldPalette = sl->getPalette(); + + if (newLevel) { + while (!xsh->getCell(row, col).isEmpty()) { + col += 1; + } + createdFrame = true; + } + // get the current cell + TXshCell cell = xsh->getCell(row, col); + // if the cell doesn't have a level... + if (!newLevel) { + if (!cell.getSimpleLevel()) { + createdFrame = true; + // try the previous frame + TXshSimpleLevel *sl = xsh->getCell(row - 1, col).getSimpleLevel(); + if (sl) oldPalette = sl->getPalette(); + } else { + TXshSimpleLevel *sl = cell.getSimpleLevel(); + // don't do anything to ffmpeg level types + if (sl->getType() == OVL_XSHLEVEL && sl->getPath().isUneditable()) + return; + oldPalette = sl->getPalette(); + } } if (oldPalette) oldPalette = oldPalette->clone(); TTileSet *tiles = 0; @@ -1685,7 +1714,7 @@ static void pasteRasterImageInCell(int row, int col, } else if (fullColorTiles) { TUndoManager::manager()->add(new PasteFullColorImageInCellsUndo( rasterImageData, fullColorTiles, cell.getSimpleLevel(), - cell.getFrameId(), oldPalette, createdFrame, isLevelCreated)); + cell.getFrameId(), oldPalette, createdFrame, isLevelCreated, col)); } } @@ -1698,6 +1727,7 @@ void TCellSelection::pasteCells() { const QMimeData *mimeData = clipboard->mimeData(); TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet(); XsheetViewer *viewer = TApp::instance()->getCurrentXsheetViewer(); + ToolHandle *toolHandle = TApp::instance()->getCurrentTool(); bool initUndo = false; const TCellKeyframeData *cellKeyframeData = @@ -1876,13 +1906,20 @@ void TCellSelection::pasteCells() { } else pasteStrokesInCell(r0, c0, strokesData); } - if (const RasterImageData *rasterImageData = - dynamic_cast(mimeData)) { - if (isEmpty()) // Se la selezione delle celle e' vuota ritorno. + // Raster Time + // See if an image was copied from outside OpenToonz + QImage clipImage = clipboard->image(); + // See if the clipboard contains rasterData + const RasterImageData *rasterImageData = + dynamic_cast(mimeData); + if (rasterImageData || clipImage.height() > 0) { + if (isEmpty()) // Nothing selected. return; + // get the current image and find out the type TImageP img = xsh->getCell(r0, c0).getImage(false); if (!img && r0 > 0) { + // Try the previous cell. TXshCell cell = xsh->getCell(r0 - 1, c0); TXshLevel *xl = cell.m_level.getPointer(); if (xl && (xl->getType() != OVL_XSHLEVEL || @@ -1894,23 +1931,119 @@ void TCellSelection::pasteCells() { TToonzImageP ti(img); TVectorImageP vi(img); if (fullColData && (vi || ti)) { + // Bail out if the level is Smart Raster or Vector with normal raster + // data. DVGui::error(QObject::tr( "The copied selection cannot be pasted in the current drawing.")); return; } + // Convert non-plain raster data to strokes data if (!initUndo) { initUndo = true; TUndoManager::manager()->beginBlock(); } - if (vi) { + if (vi && clipImage.isNull()) { + // Vector stuff TXshSimpleLevel *sl = xsh->getCell(r0, c0).getSimpleLevel(); if (!sl) sl = xsh->getCell(r0 - 1, c0).getSimpleLevel(); assert(sl); StrokesData *strokesData = rasterImageData->toStrokesData(sl->getScene()); pasteStrokesInCell(r0, c0, strokesData); - } else - pasteRasterImageInCell(r0, c0, rasterImageData); - } + // end strokes stuff + } else { + TXshSimpleLevel *sl = xsh->getCell(r0, c0).getSimpleLevel(); + if (!sl && r0 > 0) sl = xsh->getCell(r0 - 1, c0).getSimpleLevel(); + bool newLevel = false; + TRasterImageP ri(img); + + if (clipImage.height() > 0) { + // This stuff is only if we have a pasted image from outside OpenToonz + bool cancel = false; + + if (sl && sl->getType() == OVL_XSHLEVEL) { + if (sl->getResolution().lx < clipImage.width() || + sl->getResolution().ly < clipImage.height()) { + clipImage = + clipImage.scaled(sl->getResolution().lx, sl->getResolution().ly, + Qt::KeepAspectRatio); + } + } else { + QString question = QObject::tr( + "Pasting external image from clipboard.\n\nWhat do you want to do?"); + int ret = DVGui::MsgBox(question, QObject::tr("New raster level"), + QObject::tr("Cancel"), 0); + if (ret == 1) { // New level chosen + newLevel = true; + } else { // Cancel or close + cancel = true; + } + } + + if (cancel) { + // Cancel or dialog closed + if (initUndo) TUndoManager::manager()->endBlock(); + return; + } + + if (newLevel) { + if (sl) { + // find the next empty column + while (!xsh->isColumnEmpty(c0)) { + c0 += 1; + } + } + TXshColumn *col = + TApp::instance()->getCurrentXsheet()->getXsheet()->getColumn(c0); + TApp::instance()->getCurrentColumn()->setColumnIndex(c0); + TApp::instance()->getCurrentColumn()->setColumn(col); + TApp::instance()->getCurrentFrame()->setFrame(r0); + } + + // create variables to go into the Full Color Raster Selection data + std::vector rects; + const std::vector strokes; + const std::vector originalStrokes; + TAffine aff; + TRasterP ras = rasterFromQImage(clipImage); + rects.push_back(TRectD(0.0 - clipImage.width() / 2, + 0.0 - clipImage.height() / 2, + clipImage.width() / 2, clipImage.height() / 2)); + FullColorImageData *qimageData = new FullColorImageData(); + TPalette *p; + if (!ri || !ri->getPalette() || newLevel) + p = new TPalette(); + else + p = ri->getPalette()->clone(); + TDimension dim; + if (ri && !newLevel) { + dim = ri->getRaster()->getSize(); + } else { + dim = TDimension(clipImage.width(), clipImage.height()); + } + qimageData->setData(ras, p, 120.0, 120.0, dim, rects, strokes, + originalStrokes, aff); + rasterImageData = qimageData; + // end of pasted from outside OpenToonz stuff + // rasterImageData holds all the info either way now. + } + + if (sl && sl->getType() == OVL_XSHLEVEL) { + // make selection always work on new raster cells + if (toolHandle->getTool()->getName() == "T_Selection") { + TSelection *ts = toolHandle->getTool()->getSelection(); + RasterSelection *rs = dynamic_cast(ts); + if (rs) { + toolHandle->getTool()->onDeactivate(); + toolHandle->getTool()->onActivate(); + rs->pasteSelection(); + return; + } + } + } + pasteRasterImageInCell(r0, c0, rasterImageData, newLevel); + + } // end of full raster stuff + } // end of raster stuff if (!initUndo) { DVGui::error(QObject::tr( "It is not possible to paste data: there is nothing to paste.")); diff --git a/toonz/sources/toonz/filmstripcommand.cpp b/toonz/sources/toonz/filmstripcommand.cpp index f7a7367..3c00e94 100644 --- a/toonz/sources/toonz/filmstripcommand.cpp +++ b/toonz/sources/toonz/filmstripcommand.cpp @@ -17,7 +17,9 @@ #include "toonzqt/strokesdata.h" #include "toonzqt/rasterimagedata.h" #include "timagecache.h" +#include "tools/toolhandle.h" #include "tools/toolutils.h" +#include "tools/rasterselection.h" #include "toonzqt/icongenerator.h" #include "tundo.h" @@ -1555,9 +1557,66 @@ void FilmstripCmd::paste(TXshSimpleLevel *sl, std::set &frames) { (frames.size() == 1) ? !sl->isFid((*frames.begin())) : false; TTileSet *tileSet = 0; std::map> indices; - TUndo *undo = 0; - TPaletteP plt = sl->getPalette()->clone(); - bool isPaste = pasteAreasWithoutUndo(data, sl, frames, &tileSet, indices); + TUndo *undo = 0; + TPaletteP plt = sl->getPalette()->clone(); + QImage clipImage = clipboard->image(); + + FullColorImageData *fullColorData = + dynamic_cast(data); + + if ((!clipImage.isNull() || fullColorData) && + sl->getType() != OVL_XSHLEVEL) { + DVGui::error(QObject::tr( + "Can't paste full raster data on a non full raster level.")); + return; + } + + if (sl->getType() == OVL_XSHLEVEL && !clipImage.isNull()) { + // This stuff is only if we have a pasted image from outside OpenToonz + + if (sl->getResolution().lx < clipImage.width() || + sl->getResolution().ly < clipImage.height()) { + clipImage = + clipImage.scaled(sl->getResolution().lx, sl->getResolution().ly, + Qt::KeepAspectRatio); + } + + // create variables to go into the Full Color Image data + std::vector rects; + const std::vector strokes; + const std::vector originalStrokes; + TAffine aff; + TRasterP ras = rasterFromQImage(clipImage); + rects.push_back(TRectD(0.0 - clipImage.width() / 2, + 0.0 - clipImage.height() / 2, + clipImage.width() / 2, clipImage.height() / 2)); + FullColorImageData *qimageData = new FullColorImageData(); + + TDimension dim = sl->getResolution(); + + qimageData->setData(ras, plt, 120.0, 120.0, dim, rects, strokes, + originalStrokes, aff); + data = qimageData; + // end of pasted from outside OpenToonz stuff + // data holds all the info either way now. + } + + if (sl && sl->getType() == OVL_XSHLEVEL) { + // make selection always work on new raster cells + ToolHandle *toolHandle = TApp::instance()->getCurrentTool(); + if (toolHandle->getTool()->getName() == "T_Selection") { + TSelection *ts = toolHandle->getTool()->getSelection(); + RasterSelection *rs = dynamic_cast(ts); + if (rs) { + toolHandle->getTool()->onDeactivate(); + toolHandle->getTool()->onActivate(); + rs->pasteSelection(); + return; + } + } + } + + bool isPaste = pasteAreasWithoutUndo(data, sl, frames, &tileSet, indices); RasterImageData *rasterImageData = dynamic_cast(data); StrokesData *strokesData = dynamic_cast(data); if (rasterImageData && tileSet)